@adea-ai/ui 0.71.0 → 0.71.2

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 (99) hide show
  1. package/dist/components/composites/appearance-editor/appearance-editor.js +1 -1
  2. package/dist/components/composites/appearance-editor/appearance-editor.js.map +1 -1
  3. package/dist/components/composites/stat/stat.d.ts.map +1 -1
  4. package/dist/components/composites/stat/stat.js +8 -8
  5. package/dist/components/composites/stat/stat.js.map +1 -1
  6. package/dist/components/composites/update-dialog/update-dialog.d.ts +7 -0
  7. package/dist/components/composites/update-dialog/update-dialog.d.ts.map +1 -1
  8. package/dist/components/composites/update-dialog/update-dialog.js +71 -26
  9. package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
  10. package/dist/components/conversation/internal/chat-composer-shell.js +1 -1
  11. package/dist/components/conversation/internal/chat-composer-shell.js.map +1 -1
  12. package/dist/components/conversation/message-composer.d.ts.map +1 -1
  13. package/dist/components/conversation/message-composer.js +1 -1
  14. package/dist/components/conversation/message-composer.js.map +1 -1
  15. package/dist/components/ui/alert/alert.d.ts +3 -1
  16. package/dist/components/ui/alert/alert.d.ts.map +1 -1
  17. package/dist/components/ui/alert/alert.js +18 -8
  18. package/dist/components/ui/alert/alert.js.map +1 -1
  19. package/dist/components/ui/badge/badge.js +4 -4
  20. package/dist/components/ui/badge/badge.js.map +1 -1
  21. package/dist/components/ui/context-menu/context-menu.d.ts.map +1 -1
  22. package/dist/components/ui/context-menu/context-menu.js +2 -2
  23. package/dist/components/ui/context-menu/context-menu.js.map +1 -1
  24. package/dist/components/ui/dialog/dialog.d.ts +17 -1
  25. package/dist/components/ui/dialog/dialog.d.ts.map +1 -1
  26. package/dist/components/ui/dialog/dialog.js +120 -2
  27. package/dist/components/ui/dialog/dialog.js.map +1 -1
  28. package/dist/components/ui/diff-block/diff-block.js +2 -2
  29. package/dist/components/ui/diff-block/diff-block.js.map +1 -1
  30. package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts +3 -2
  31. package/dist/components/ui/dropdown-menu/dropdown-menu.d.ts.map +1 -1
  32. package/dist/components/ui/dropdown-menu/dropdown-menu.js +5 -4
  33. package/dist/components/ui/dropdown-menu/dropdown-menu.js.map +1 -1
  34. package/dist/components/ui/entity-icon/entity-icon.d.ts +3 -3
  35. package/dist/components/ui/entity-icon/entity-icon.js +7 -7
  36. package/dist/components/ui/entity-icon/entity-icon.js.map +1 -1
  37. package/dist/components/ui/field/field.js +1 -1
  38. package/dist/components/ui/field/field.js.map +1 -1
  39. package/dist/components/ui/menubar/menubar.d.ts.map +1 -1
  40. package/dist/components/ui/menubar/menubar.js +2 -2
  41. package/dist/components/ui/menubar/menubar.js.map +1 -1
  42. package/dist/components/ui/modal-dialog/modal-dialog.d.ts +12 -2
  43. package/dist/components/ui/modal-dialog/modal-dialog.d.ts.map +1 -1
  44. package/dist/components/ui/modal-dialog/modal-dialog.js +44 -27
  45. package/dist/components/ui/modal-dialog/modal-dialog.js.map +1 -1
  46. package/dist/components/ui/status-chip/status-chip.js +4 -4
  47. package/dist/components/ui/status-chip/status-chip.js.map +1 -1
  48. package/dist/lib/overlay.d.ts +2 -0
  49. package/dist/lib/overlay.d.ts.map +1 -1
  50. package/dist/lib/overlay.js +3 -1
  51. package/dist/lib/overlay.js.map +1 -1
  52. package/dist/lib/themes.d.ts +7 -0
  53. package/dist/lib/themes.d.ts.map +1 -1
  54. package/dist/lib/themes.js +22 -1
  55. package/dist/lib/themes.js.map +1 -1
  56. package/dist/lib/tokens.d.ts.map +1 -1
  57. package/dist/lib/tokens.js +4 -4
  58. package/dist/lib/tokens.js.map +1 -1
  59. package/dist/r/src/components/composites/appearance-editor/appearance-editor.tsx +1 -1
  60. package/dist/r/src/components/composites/stat/stat.tsx +5 -8
  61. package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +98 -26
  62. package/dist/r/src/components/conversation/internal/chat-composer-shell.tsx +1 -1
  63. package/dist/r/src/components/conversation/message-composer.tsx +3 -1
  64. package/dist/r/src/components/ui/alert/alert.tsx +20 -7
  65. package/dist/r/src/components/ui/badge/badge.tsx +4 -4
  66. package/dist/r/src/components/ui/context-menu/context-menu.tsx +2 -2
  67. package/dist/r/src/components/ui/dialog/dialog.tsx +204 -2
  68. package/dist/r/src/components/ui/diff-block/diff-block.tsx +4 -4
  69. package/dist/r/src/components/ui/dropdown-menu/dropdown-menu.tsx +5 -4
  70. package/dist/r/src/components/ui/entity-icon/entity-icon.tsx +7 -7
  71. package/dist/r/src/components/ui/field/field.tsx +1 -1
  72. package/dist/r/src/components/ui/menubar/menubar.tsx +2 -2
  73. package/dist/r/src/components/ui/modal-dialog/modal-dialog.tsx +24 -2
  74. package/dist/r/src/components/ui/status-chip/status-chip.tsx +4 -4
  75. package/dist/r/src/lib/overlay.ts +4 -0
  76. package/dist/r/src/lib/themes.ts +29 -0
  77. package/dist/r/src/lib/tokens.ts +19 -4
  78. package/dist/r/src/styles/theme.css +10 -10
  79. package/package.json +1 -1
  80. package/src/components/composites/appearance-editor/appearance-editor.tsx +1 -1
  81. package/src/components/composites/stat/stat.tsx +5 -8
  82. package/src/components/composites/update-dialog/update-dialog.tsx +98 -26
  83. package/src/components/conversation/internal/chat-composer-shell.tsx +1 -1
  84. package/src/components/conversation/message-composer.tsx +3 -1
  85. package/src/components/ui/alert/alert.tsx +20 -7
  86. package/src/components/ui/badge/badge.tsx +4 -4
  87. package/src/components/ui/context-menu/context-menu.tsx +2 -2
  88. package/src/components/ui/dialog/dialog.tsx +204 -2
  89. package/src/components/ui/diff-block/diff-block.tsx +4 -4
  90. package/src/components/ui/dropdown-menu/dropdown-menu.tsx +5 -4
  91. package/src/components/ui/entity-icon/entity-icon.tsx +7 -7
  92. package/src/components/ui/field/field.tsx +1 -1
  93. package/src/components/ui/menubar/menubar.tsx +2 -2
  94. package/src/components/ui/modal-dialog/modal-dialog.tsx +24 -2
  95. package/src/components/ui/status-chip/status-chip.tsx +4 -4
  96. package/src/lib/overlay.ts +4 -0
  97. package/src/lib/themes.ts +29 -0
  98. package/src/lib/tokens.ts +19 -4
  99. package/src/styles/theme.css +10 -10
@@ -10,7 +10,7 @@ import { EyeOff, Palette, PanelsTopLeft, SlidersHorizontal } from "lucide-solid"
10
10
  import { Show, createUniqueId } from "solid-js";
11
11
  //#region src/components/composites/appearance-editor/appearance-editor.tsx
12
12
  var _tmpl$ = /*#__PURE__*/ template(`<p role=status class="px-4 py-2 text-sm text-muted-foreground">`);
13
- var _tmpl$2 = /*#__PURE__*/ template(`<p role=alert class="mt-1 text-xs text-destructive">`);
13
+ var _tmpl$2 = /*#__PURE__*/ template(`<p role=alert class="mt-1 text-xs text-foreground">`);
14
14
  var _tmpl$3 = /*#__PURE__*/ template(`<div class="px-4 py-3"><label class="mb-2 block text-xs font-medium">Custom accent`);
15
15
  var _tmpl$4 = /*#__PURE__*/ template(`<p role=status class="px-4 py-2 text-xs text-muted-foreground">`);
16
16
  var _tmpl$5 = /*#__PURE__*/ template(`<p role=status class="px-4 pt-3 text-xs text-muted-foreground">`);
@@ -1 +1 @@
1
- {"version":3,"file":"appearance-editor.js","names":["EyeOff","Palette","PanelsTopLeft","SlidersHorizontal","Show","createUniqueId","cn","Button","Input","Switch","Popover","PopoverContent","PopoverDescription","PopoverTitle","PopoverTrigger","AppearanceEditorProps","AppearancePopoverProps","ModeChoices","AccentChoices","GlassChoices","SettingsRow","ThemeRow","AppearanceEditor","props","errorId","saveReasonId","custom","draft","accent","accentOptions","some","option","id","accentDescription","name","find","label","glassDescription","surface","surfaceCapability","themeDefaultDescription","_el$","_tmpl$6","_el$3","firstChild","_el$9","nextSibling","_el$0","_$insert","_$createComponent","when","recoveryNotice","children","_el$2","_tmpl$","appearance","selectedId","lightThemeId","preview","lightTheme","themes","disabled","saving","onSelect","onChange","darkThemeId","darkTheme","stacked","title","icon","description","_el$4","_tmpl$3","_el$5","_$setAttribute","value","aria-invalid","customAccentError","aria-describedby","undefined","onInput","event","currentTarget","_el$6","_tmpl$2","_$memo","frosted","reason","_el$7","_tmpl$4","checked","reduceTransparency","onManageThemes","type","size","variant","onClick","saveDisabledReason","_el$8","_tmpl$5","onReset","onCancel","aria-busy","onSave","_$effect","_$className","class","AppearancePopover","open","modal","onOpenChange","onOpen","onDismiss","as","_$createComponent","_$memo","_$className"],"sources":["../../../../src/components/composites/appearance-editor/appearance-editor.tsx"],"sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2026 Wing\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n/*\n * Composition substantially translated from Zeron settings/appearance.rs and\n * settings/widgets.rs, pinned at 30a9a9537c5ec96226c87f4bf349b6f77c5dfb59,\n * preserving Adea's existing appearance-surface.tsx port: System/Light/Dark\n * miniatures, compact independent theme rows, accent swatches and helper copy,\n * glass chips, and theme library affordance. Kobalte replaces the port's manual\n * radio keyboard model. The controlled draft and explicit action ports replace\n * GPUI globals and app-specific storage/native authority. Save/Cancel/Reset and\n * custom accent/reduced-transparency are accepted Adea divergences (#425).\n */\nimport { EyeOff, Palette, PanelsTopLeft, SlidersHorizontal } from 'lucide-solid'\nimport { Show, createUniqueId } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Button } from '../../ui/button/button'\nimport { Input } from '../../ui/input/input'\n\nimport { Switch } from '../../ui/switch/switch'\nimport {\n Popover,\n PopoverContent,\n PopoverDescription,\n PopoverTitle,\n PopoverTrigger,\n} from '../../ui/popover/popover'\nimport type { AppearanceEditorProps, AppearancePopoverProps } from './appearance-types'\nimport { ModeChoices, AccentChoices, GlassChoices } from './appearance-choices'\nimport { SettingsRow, ThemeRow } from './appearance-rows'\n\n/**\n * A live editor whose persistence and preview authority belong to its host.\n * No ThemeProvider is required, and no catalogue, renderer engine or storage is\n * imported. Hosts normalize unknown IDs and custom colors, report recovery,\n * snapshot on open, preview on changes, commit on Save, and restore on dismissal.\n * Both theme rows remain mounted when mode changes. Typeface/density axes are\n * deliberately absent from Adea #425's accepted surface.\n */\nexport function AppearanceEditor(props: AppearanceEditorProps) {\n const errorId = createUniqueId()\n const saveReasonId = createUniqueId()\n const custom = () =>\n props.draft.accent !== 'theme' &&\n !props.accentOptions.some((option) => option.id === props.draft.accent)\n const accentDescription = () => {\n if (props.draft.accent === 'theme') return \"Theme default · Uses the palette's intended color.\"\n const name = props.accentOptions.find((option) => option.id === props.draft.accent)?.label\n return `${name ?? 'Custom'} · Controls, glyphs, selections, code, and activity.`\n }\n const glassDescription = () =>\n props.draft.surface === 'opaque'\n ? 'Solid surfaces for every theme.'\n : props.draft.surface === 'frosted'\n ? 'Theme-colored glass where supported.'\n : (props.surfaceCapability.themeDefaultDescription ?? \"Uses this theme's default surface.\")\n return (\n <div data-appearance-editor class={cn('min-w-0', props.class)}>\n <Show when={props.recoveryNotice}>\n <p role=\"status\" class=\"px-4 py-2 text-sm text-muted-foreground\">\n {props.recoveryNotice}\n </p>\n </Show>\n <ModeChoices {...props} />\n <div class=\"divide-y divide-border rounded-lg border\">\n <ThemeRow\n appearance=\"light\"\n selectedId={props.draft.lightThemeId}\n preview={props.lightTheme}\n themes={props.themes}\n disabled={props.saving}\n onSelect={(lightThemeId) => props.onChange({ lightThemeId })}\n />\n <ThemeRow\n appearance=\"dark\"\n selectedId={props.draft.darkThemeId}\n preview={props.darkTheme}\n themes={props.themes}\n disabled={props.saving}\n onSelect={(darkThemeId) => props.onChange({ darkThemeId })}\n />\n <SettingsRow\n stacked\n title=\"Accent\"\n icon={<SlidersHorizontal />}\n description={accentDescription()}\n >\n <AccentChoices {...props} />\n </SettingsRow>\n <Show when={custom()}>\n <div class=\"px-4 py-3\">\n <label class=\"mb-2 block text-xs font-medium\" for={`${errorId}-input`}>\n Custom accent\n </label>\n <Input\n id={`${errorId}-input`}\n value={props.draft.accent}\n disabled={props.saving}\n aria-invalid={!!props.customAccentError}\n aria-describedby={props.customAccentError ? errorId : undefined}\n onInput={(event) => props.onChange({ accent: event.currentTarget.value })}\n />\n <Show when={props.customAccentError}>\n <p id={errorId} role=\"alert\" class=\"mt-1 text-xs text-destructive\">\n {props.customAccentError}\n </p>\n </Show>\n </div>\n </Show>\n <SettingsRow title=\"Glass\" icon={<PanelsTopLeft />} description={glassDescription()}>\n <GlassChoices {...props} />\n </SettingsRow>\n <Show when={!props.surfaceCapability.frosted && props.surfaceCapability.reason}>\n <p role=\"status\" class=\"px-4 py-2 text-xs text-muted-foreground\">\n {props.surfaceCapability.reason}\n </p>\n </Show>\n <SettingsRow\n title=\"Reduce transparency\"\n icon={<EyeOff />}\n description=\"Prefer solid surfaces, including during live preview.\"\n >\n <Switch\n checked={props.draft.reduceTransparency}\n disabled={props.saving}\n onChange={(reduceTransparency: boolean) => props.onChange({ reduceTransparency })}\n aria-label=\"Reduce transparency\"\n />\n </SettingsRow>\n <SettingsRow\n title=\"Theme library\"\n icon={<Palette />}\n description={\n props.onManageThemes\n ? 'Manage themes through the application.'\n : 'Theme import is unavailable.'\n }\n >\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={!props.onManageThemes || props.saving}\n onClick={() => props.onManageThemes?.()}\n >\n Manage themes\n </Button>\n </SettingsRow>\n </div>\n <Show when={props.saveDisabledReason}>\n <p id={saveReasonId} role=\"status\" class=\"px-4 pt-3 text-xs text-muted-foreground\">\n {props.saveDisabledReason}\n </p>\n </Show>\n <div class=\"flex flex-wrap items-center gap-2 px-4 py-4\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n disabled={props.saving}\n onClick={() => props.onReset()}\n >\n Reset\n </Button>\n <div class=\"ml-auto flex gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={props.saving}\n onClick={() => props.onCancel()}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={props.saving || !!props.customAccentError || !!props.saveDisabledReason}\n aria-busy={props.saving}\n aria-describedby={props.saveDisabledReason ? saveReasonId : undefined}\n onClick={() => props.onSave()}\n >\n {props.saving ? 'Saving…' : 'Save'}\n </Button>\n </div>\n </div>\n </div>\n )\n}\n\n/** Modal anchoring/dismissal and nested-menu behavior come from Kobalte.\n * Its measured available height also bounds scrolling, keeping footer actions\n * reachable when a narrow popup begins below its trigger. */\nexport function AppearancePopover(props: AppearancePopoverProps) {\n return (\n <Popover\n open={props.open}\n modal\n onOpenChange={(open) => (open ? props.onOpen() : props.onDismiss())}\n >\n <PopoverTrigger\n as={Button}\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label=\"Appearance settings\"\n >\n <Palette />\n </PopoverTrigger>\n <PopoverContent class=\"w-lg max-w-[calc(100vw-2rem)] max-h-(--kb-popper-content-available-height) overflow-y-auto\">\n <PopoverTitle>Appearance</PopoverTitle>\n <PopoverDescription>\n Changes preview immediately. Save keeps them; Cancel restores the previous appearance.\n </PopoverDescription>\n <AppearanceEditor {...props} />\n </PopoverContent>\n </Popover>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"appearance-editor.js","names":["EyeOff","Palette","PanelsTopLeft","SlidersHorizontal","Show","createUniqueId","cn","Button","Input","Switch","Popover","PopoverContent","PopoverDescription","PopoverTitle","PopoverTrigger","AppearanceEditorProps","AppearancePopoverProps","ModeChoices","AccentChoices","GlassChoices","SettingsRow","ThemeRow","AppearanceEditor","props","errorId","saveReasonId","custom","draft","accent","accentOptions","some","option","id","accentDescription","name","find","label","glassDescription","surface","surfaceCapability","themeDefaultDescription","_el$","_tmpl$6","_el$3","firstChild","_el$9","nextSibling","_el$0","_$insert","_$createComponent","when","recoveryNotice","children","_el$2","_tmpl$","appearance","selectedId","lightThemeId","preview","lightTheme","themes","disabled","saving","onSelect","onChange","darkThemeId","darkTheme","stacked","title","icon","description","_el$4","_tmpl$3","_el$5","_$setAttribute","value","aria-invalid","customAccentError","aria-describedby","undefined","onInput","event","currentTarget","_el$6","_tmpl$2","_$memo","frosted","reason","_el$7","_tmpl$4","checked","reduceTransparency","onManageThemes","type","size","variant","onClick","saveDisabledReason","_el$8","_tmpl$5","onReset","onCancel","aria-busy","onSave","_$effect","_$className","class","AppearancePopover","open","modal","onOpenChange","onOpen","onDismiss","as","_$createComponent","_$memo","_$className"],"sources":["../../../../src/components/composites/appearance-editor/appearance-editor.tsx"],"sourcesContent":["/*\n * MIT License\n *\n * Copyright (c) 2026 Wing\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in all\n * copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n/*\n * Composition substantially translated from Zeron settings/appearance.rs and\n * settings/widgets.rs, pinned at 30a9a9537c5ec96226c87f4bf349b6f77c5dfb59,\n * preserving Adea's existing appearance-surface.tsx port: System/Light/Dark\n * miniatures, compact independent theme rows, accent swatches and helper copy,\n * glass chips, and theme library affordance. Kobalte replaces the port's manual\n * radio keyboard model. The controlled draft and explicit action ports replace\n * GPUI globals and app-specific storage/native authority. Save/Cancel/Reset and\n * custom accent/reduced-transparency are accepted Adea divergences (#425).\n */\nimport { EyeOff, Palette, PanelsTopLeft, SlidersHorizontal } from 'lucide-solid'\nimport { Show, createUniqueId } from 'solid-js'\nimport { cn } from '#lib/utils'\nimport { Button } from '../../ui/button/button'\nimport { Input } from '../../ui/input/input'\n\nimport { Switch } from '../../ui/switch/switch'\nimport {\n Popover,\n PopoverContent,\n PopoverDescription,\n PopoverTitle,\n PopoverTrigger,\n} from '../../ui/popover/popover'\nimport type { AppearanceEditorProps, AppearancePopoverProps } from './appearance-types'\nimport { ModeChoices, AccentChoices, GlassChoices } from './appearance-choices'\nimport { SettingsRow, ThemeRow } from './appearance-rows'\n\n/**\n * A live editor whose persistence and preview authority belong to its host.\n * No ThemeProvider is required, and no catalogue, renderer engine or storage is\n * imported. Hosts normalize unknown IDs and custom colors, report recovery,\n * snapshot on open, preview on changes, commit on Save, and restore on dismissal.\n * Both theme rows remain mounted when mode changes. Typeface/density axes are\n * deliberately absent from Adea #425's accepted surface.\n */\nexport function AppearanceEditor(props: AppearanceEditorProps) {\n const errorId = createUniqueId()\n const saveReasonId = createUniqueId()\n const custom = () =>\n props.draft.accent !== 'theme' &&\n !props.accentOptions.some((option) => option.id === props.draft.accent)\n const accentDescription = () => {\n if (props.draft.accent === 'theme') return \"Theme default · Uses the palette's intended color.\"\n const name = props.accentOptions.find((option) => option.id === props.draft.accent)?.label\n return `${name ?? 'Custom'} · Controls, glyphs, selections, code, and activity.`\n }\n const glassDescription = () =>\n props.draft.surface === 'opaque'\n ? 'Solid surfaces for every theme.'\n : props.draft.surface === 'frosted'\n ? 'Theme-colored glass where supported.'\n : (props.surfaceCapability.themeDefaultDescription ?? \"Uses this theme's default surface.\")\n return (\n <div data-appearance-editor class={cn('min-w-0', props.class)}>\n <Show when={props.recoveryNotice}>\n <p role=\"status\" class=\"px-4 py-2 text-sm text-muted-foreground\">\n {props.recoveryNotice}\n </p>\n </Show>\n <ModeChoices {...props} />\n <div class=\"divide-y divide-border rounded-lg border\">\n <ThemeRow\n appearance=\"light\"\n selectedId={props.draft.lightThemeId}\n preview={props.lightTheme}\n themes={props.themes}\n disabled={props.saving}\n onSelect={(lightThemeId) => props.onChange({ lightThemeId })}\n />\n <ThemeRow\n appearance=\"dark\"\n selectedId={props.draft.darkThemeId}\n preview={props.darkTheme}\n themes={props.themes}\n disabled={props.saving}\n onSelect={(darkThemeId) => props.onChange({ darkThemeId })}\n />\n <SettingsRow\n stacked\n title=\"Accent\"\n icon={<SlidersHorizontal />}\n description={accentDescription()}\n >\n <AccentChoices {...props} />\n </SettingsRow>\n <Show when={custom()}>\n <div class=\"px-4 py-3\">\n <label class=\"mb-2 block text-xs font-medium\" for={`${errorId}-input`}>\n Custom accent\n </label>\n <Input\n id={`${errorId}-input`}\n value={props.draft.accent}\n disabled={props.saving}\n aria-invalid={!!props.customAccentError}\n aria-describedby={props.customAccentError ? errorId : undefined}\n onInput={(event) => props.onChange({ accent: event.currentTarget.value })}\n />\n <Show when={props.customAccentError}>\n <p id={errorId} role=\"alert\" class=\"mt-1 text-xs text-foreground\">\n {props.customAccentError}\n </p>\n </Show>\n </div>\n </Show>\n <SettingsRow title=\"Glass\" icon={<PanelsTopLeft />} description={glassDescription()}>\n <GlassChoices {...props} />\n </SettingsRow>\n <Show when={!props.surfaceCapability.frosted && props.surfaceCapability.reason}>\n <p role=\"status\" class=\"px-4 py-2 text-xs text-muted-foreground\">\n {props.surfaceCapability.reason}\n </p>\n </Show>\n <SettingsRow\n title=\"Reduce transparency\"\n icon={<EyeOff />}\n description=\"Prefer solid surfaces, including during live preview.\"\n >\n <Switch\n checked={props.draft.reduceTransparency}\n disabled={props.saving}\n onChange={(reduceTransparency: boolean) => props.onChange({ reduceTransparency })}\n aria-label=\"Reduce transparency\"\n />\n </SettingsRow>\n <SettingsRow\n title=\"Theme library\"\n icon={<Palette />}\n description={\n props.onManageThemes\n ? 'Manage themes through the application.'\n : 'Theme import is unavailable.'\n }\n >\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={!props.onManageThemes || props.saving}\n onClick={() => props.onManageThemes?.()}\n >\n Manage themes\n </Button>\n </SettingsRow>\n </div>\n <Show when={props.saveDisabledReason}>\n <p id={saveReasonId} role=\"status\" class=\"px-4 pt-3 text-xs text-muted-foreground\">\n {props.saveDisabledReason}\n </p>\n </Show>\n <div class=\"flex flex-wrap items-center gap-2 px-4 py-4\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"ghost\"\n disabled={props.saving}\n onClick={() => props.onReset()}\n >\n Reset\n </Button>\n <div class=\"ml-auto flex gap-2\">\n <Button\n type=\"button\"\n size=\"sm\"\n variant=\"outline\"\n disabled={props.saving}\n onClick={() => props.onCancel()}\n >\n Cancel\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={props.saving || !!props.customAccentError || !!props.saveDisabledReason}\n aria-busy={props.saving}\n aria-describedby={props.saveDisabledReason ? saveReasonId : undefined}\n onClick={() => props.onSave()}\n >\n {props.saving ? 'Saving…' : 'Save'}\n </Button>\n </div>\n </div>\n </div>\n )\n}\n\n/** Modal anchoring/dismissal and nested-menu behavior come from Kobalte.\n * Its measured available height also bounds scrolling, keeping footer actions\n * reachable when a narrow popup begins below its trigger. */\nexport function AppearancePopover(props: AppearancePopoverProps) {\n return (\n <Popover\n open={props.open}\n modal\n onOpenChange={(open) => (open ? props.onOpen() : props.onDismiss())}\n >\n <PopoverTrigger\n as={Button}\n type=\"button\"\n variant=\"ghost\"\n size=\"icon-sm\"\n aria-label=\"Appearance settings\"\n >\n <Palette />\n </PopoverTrigger>\n <PopoverContent class=\"w-lg max-w-[calc(100vw-2rem)] max-h-(--kb-popper-content-available-height) overflow-y-auto\">\n <PopoverTitle>Appearance</PopoverTitle>\n <PopoverDescription>\n Changes preview immediately. Save keeps them; Cancel restores the previous appearance.\n </PopoverDescription>\n <AppearanceEditor {...props} />\n </PopoverContent>\n </Popover>\n )\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"stat.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/stat/stat.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAKnD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,GAAG,CAAC,OAAO,CAAA;IAClB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAA;IACrC,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,eAkDpC;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,eASvD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,eASvD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,eASxD;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,eAa1E"}
1
+ {"version":3,"file":"stat.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/stat/stat.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAKnD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,CAAC,GAAG;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,GAAG,CAAC,OAAO,CAAA;IAClB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,SAAS,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,SAAS,CAAA;IACrC,wEAAwE;IACxE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,eA+CpC;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,eASrD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,eASvD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,eASvD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,IAAI,CAAC,eASxD;AAED,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,eAa1E"}
@@ -1,10 +1,10 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
- import { Dynamic, className, createComponent, effect, insert, mergeProps, spread, template } from "solid-js/web";
2
+ import { Dynamic, createComponent, insert, mergeProps, spread, template } from "solid-js/web";
3
3
  import { ArrowDownRight, ArrowUpRight } from "lucide-solid";
4
4
  import { Show, splitProps } from "solid-js";
5
5
  //#region src/components/composites/stat/stat.tsx
6
6
  var _tmpl$ = /*#__PURE__*/ template(`<span class="text-muted-foreground text-xs">`);
7
- var _tmpl$2 = /*#__PURE__*/ template(`<div><span class=tabular-nums>`);
7
+ var _tmpl$2 = /*#__PURE__*/ template(`<div class="text-foreground flex items-center gap-1 text-xs"><span class=tabular-nums>`);
8
8
  var _tmpl$3 = /*#__PURE__*/ template(`<div data-slot=stat><div class="text-muted-foreground truncate text-xs font-medium"></div><div class="flex items-baseline gap-1"><span class="text-xl font-semibold tabular-nums tracking-tight">`);
9
9
  var _tmpl$4 = /*#__PURE__*/ template(`<div data-slot=stat-group>`);
10
10
  var _tmpl$5 = /*#__PURE__*/ template(`<dl data-slot=property-list>`);
@@ -55,16 +55,16 @@ function Stat(props) {
55
55
  return local.deltaTone === "down" ? ArrowDownRight : ArrowUpRight;
56
56
  },
57
57
  "aria-hidden": "true",
58
- "class": "size-3"
58
+ get ["class"]() {
59
+ return cn("size-3", {
60
+ "text-success": local.deltaTone === "up",
61
+ "text-destructive": local.deltaTone === "down"
62
+ });
63
+ }
59
64
  });
60
65
  }
61
66
  }), _el$7);
62
67
  insert(_el$7, () => local.delta);
63
- effect(() => className(_el$6, cn("flex items-center gap-1 text-xs", {
64
- "text-success": local.deltaTone === "up",
65
- "text-destructive": local.deltaTone === "down",
66
- "text-muted-foreground": local.deltaTone === void 0 || local.deltaTone === "neutral"
67
- })));
68
68
  return _el$6;
69
69
  }
70
70
  }), null);
@@ -1 +1 @@
1
- {"version":3,"file":"stat.js","names":["ArrowDownRight","ArrowUpRight","ComponentProps","JSX","Show","splitProps","Dynamic","cn","StatProps","label","value","Element","unit","delta","deltaTone","accent","Stat","props","local","rest","_el$","_tmpl$3","_el$2","firstChild","_el$3","nextSibling","_el$4","_$spread","_$mergeProps","class","_$insert","_$createComponent","when","children","_el$5","_tmpl$","_el$6","_tmpl$2","_el$7","component","_$effect","_$className","undefined","StatGroup","_el$8","_tmpl$4","PropertyList","_el$9","_tmpl$5","PropertyTerm","_el$0","_tmpl$6","PropertyValue","_el$1","_tmpl$7","PropertyRow","term","_el$10","_tmpl$8","_el$11","_el$12","_$mergeProps","_$createComponent","_$className"],"sources":["../../../../src/components/composites/stat/stat.tsx"],"sourcesContent":["import { ArrowDownRight, ArrowUpRight } from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { Dynamic } from 'solid-js/web'\nimport { cn } from '#lib/utils'\n\n/**\n * Stat.\n *\n * A single measured number with its label and, optionally, its change. The\n * whole component is a decision about honesty: a value shown without its\n * comparison is not information, so `delta` carries the direction, the amount\n * and — through `deltaTone` — whether the direction is *good*. A rising number\n * is not always a rising outcome, and only the caller knows which way is which.\n *\n * `tabular-nums` on the value is what lets two stats in a row be compared\n * without the digits shifting under the reader.\n */\nexport type StatProps = ComponentProps<'div'> & {\n label: string\n value: JSX.Element\n /** A unit or qualifier after the value, e.g. \"ms\", \"of 10\". */\n unit?: string\n /** The change, e.g. \"+12% from last week\". */\n delta?: string\n /** Whether the change is an improvement. Decides the arrow and the colour. */\n deltaTone?: 'up' | 'down' | 'neutral'\n /** Draws the leading accent rule used for a row of headline numbers. */\n accent?: boolean\n}\n\nexport function Stat(props: StatProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'label',\n 'value',\n 'unit',\n 'delta',\n 'deltaTone',\n 'accent',\n 'children',\n ])\n\n return (\n <div\n data-slot=\"stat\"\n class={cn(\n 'flex min-w-0 flex-col gap-1 rounded-lg border border-border bg-card p-3',\n { 'border-t-2 border-t-primary': local.accent },\n local.class\n )}\n {...rest}\n >\n <div class=\"text-muted-foreground truncate text-xs font-medium\">{local.label}</div>\n <div class=\"flex items-baseline gap-1\">\n <span class=\"text-xl font-semibold tabular-nums tracking-tight\">{local.value}</span>\n <Show when={local.unit}>\n <span class=\"text-muted-foreground text-xs\">{local.unit}</span>\n </Show>\n </div>\n <Show when={local.delta}>\n <div\n class={cn('flex items-center gap-1 text-xs', {\n 'text-success': local.deltaTone === 'up',\n 'text-destructive': local.deltaTone === 'down',\n 'text-muted-foreground': local.deltaTone === undefined || local.deltaTone === 'neutral',\n })}\n >\n <Show when={local.deltaTone === 'up' || local.deltaTone === 'down'}>\n <Dynamic\n component={local.deltaTone === 'down' ? ArrowDownRight : ArrowUpRight}\n aria-hidden=\"true\"\n class=\"size-3\"\n />\n </Show>\n <span class=\"tabular-nums\">{local.delta}</span>\n </div>\n </Show>\n {local.children}\n </div>\n )\n}\n\n/** A row of stats that shares one gap and wraps as the window narrows. */\nexport function StatGroup(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"stat-group\"\n class={cn('grid gap-3 sm:grid-cols-2 lg:grid-cols-4', local.class)}\n {...rest}\n />\n )\n}\n\n/**\n * PropertyList.\n *\n * A definition list for read-only metadata: the details of a file, a session, a\n * build. This is a `<dl>`, not a table and not a grid of divs, because the\n * relationship being expressed is term-and-definition and the platform has an\n * element for exactly that.\n *\n * Values are selectable by default. Metadata is the thing users copy most and\n * get the least often: a commit hash behind a drag-to-select gesture is a small\n * daily failure.\n */\nexport function PropertyList(props: ComponentProps<'dl'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <dl\n data-slot=\"property-list\"\n class={cn('grid grid-cols-[auto_1fr] items-baseline gap-x-4 gap-y-2 text-sm', local.class)}\n {...rest}\n />\n )\n}\n\nexport function PropertyTerm(props: ComponentProps<'dt'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <dt\n data-slot=\"property-term\"\n class={cn('text-muted-foreground whitespace-nowrap', local.class)}\n {...rest}\n />\n )\n}\n\nexport function PropertyValue(props: ComponentProps<'dd'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <dd\n data-slot=\"property-value\"\n class={cn('min-w-0 break-words select-text', local.class)}\n {...rest}\n />\n )\n}\n\n/** A single term/value pair, for a list built one row at a time. */\nexport function PropertyRow(props: ComponentProps<'div'> & { term: string }) {\n const [local, rest] = splitProps(props, ['class', 'term', 'children'])\n\n return (\n <div\n data-slot=\"property-row\"\n class={cn('flex items-baseline justify-between gap-4 text-sm', local.class)}\n {...rest}\n >\n <span class=\"text-muted-foreground shrink-0\">{local.term}</span>\n <span class=\"min-w-0 truncate text-end select-text\">{local.children}</span>\n </div>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"stat.js","names":["ArrowDownRight","ArrowUpRight","ComponentProps","JSX","Show","splitProps","Dynamic","cn","StatProps","label","value","Element","unit","delta","deltaTone","accent","Stat","props","local","rest","_el$","_tmpl$3","_el$2","firstChild","_el$3","nextSibling","_el$4","_$spread","_$mergeProps","class","_$insert","_$createComponent","when","children","_el$5","_tmpl$","_el$6","_tmpl$2","_el$7","component","StatGroup","_el$8","_tmpl$4","PropertyList","_el$9","_tmpl$5","PropertyTerm","_el$0","_tmpl$6","PropertyValue","_el$1","_tmpl$7","PropertyRow","term","_el$10","_tmpl$8","_el$11","_el$12","_$mergeProps","_$createComponent"],"sources":["../../../../src/components/composites/stat/stat.tsx"],"sourcesContent":["import { ArrowDownRight, ArrowUpRight } from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { Dynamic } from 'solid-js/web'\nimport { cn } from '#lib/utils'\n\n/**\n * Stat.\n *\n * A single measured number with its label and, optionally, its change. The\n * whole component is a decision about honesty: a value shown without its\n * comparison is not information, so `delta` carries the direction, the amount\n * and — through `deltaTone` — whether the direction is *good*. A rising number\n * is not always a rising outcome, and only the caller knows which way is which.\n *\n * `tabular-nums` on the value is what lets two stats in a row be compared\n * without the digits shifting under the reader.\n */\nexport type StatProps = ComponentProps<'div'> & {\n label: string\n value: JSX.Element\n /** A unit or qualifier after the value, e.g. \"ms\", \"of 10\". */\n unit?: string\n /** The change, e.g. \"+12% from last week\". */\n delta?: string\n /** Whether the change is an improvement. Decides the arrow and the colour. */\n deltaTone?: 'up' | 'down' | 'neutral'\n /** Draws the leading accent rule used for a row of headline numbers. */\n accent?: boolean\n}\n\nexport function Stat(props: StatProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'label',\n 'value',\n 'unit',\n 'delta',\n 'deltaTone',\n 'accent',\n 'children',\n ])\n\n return (\n <div\n data-slot=\"stat\"\n class={cn(\n 'flex min-w-0 flex-col gap-1 rounded-lg border border-border bg-card p-3',\n { 'border-t-2 border-t-primary': local.accent },\n local.class\n )}\n {...rest}\n >\n <div class=\"text-muted-foreground truncate text-xs font-medium\">{local.label}</div>\n <div class=\"flex items-baseline gap-1\">\n <span class=\"text-xl font-semibold tabular-nums tracking-tight\">{local.value}</span>\n <Show when={local.unit}>\n <span class=\"text-muted-foreground text-xs\">{local.unit}</span>\n </Show>\n </div>\n <Show when={local.delta}>\n <div class=\"text-foreground flex items-center gap-1 text-xs\">\n <Show when={local.deltaTone === 'up' || local.deltaTone === 'down'}>\n <Dynamic\n component={local.deltaTone === 'down' ? ArrowDownRight : ArrowUpRight}\n aria-hidden=\"true\"\n class={cn('size-3', {\n 'text-success': local.deltaTone === 'up',\n 'text-destructive': local.deltaTone === 'down',\n })}\n />\n </Show>\n <span class=\"tabular-nums\">{local.delta}</span>\n </div>\n </Show>\n {local.children}\n </div>\n )\n}\n\n/** A row of stats that shares one gap and wraps as the window narrows. */\nexport function StatGroup(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"stat-group\"\n class={cn('grid gap-3 sm:grid-cols-2 lg:grid-cols-4', local.class)}\n {...rest}\n />\n )\n}\n\n/**\n * PropertyList.\n *\n * A definition list for read-only metadata: the details of a file, a session, a\n * build. This is a `<dl>`, not a table and not a grid of divs, because the\n * relationship being expressed is term-and-definition and the platform has an\n * element for exactly that.\n *\n * Values are selectable by default. Metadata is the thing users copy most and\n * get the least often: a commit hash behind a drag-to-select gesture is a small\n * daily failure.\n */\nexport function PropertyList(props: ComponentProps<'dl'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <dl\n data-slot=\"property-list\"\n class={cn('grid grid-cols-[auto_1fr] items-baseline gap-x-4 gap-y-2 text-sm', local.class)}\n {...rest}\n />\n )\n}\n\nexport function PropertyTerm(props: ComponentProps<'dt'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <dt\n data-slot=\"property-term\"\n class={cn('text-muted-foreground whitespace-nowrap', local.class)}\n {...rest}\n />\n )\n}\n\nexport function PropertyValue(props: ComponentProps<'dd'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <dd\n data-slot=\"property-value\"\n class={cn('min-w-0 break-words select-text', local.class)}\n {...rest}\n />\n )\n}\n\n/** A single term/value pair, for a list built one row at a time. */\nexport function PropertyRow(props: ComponentProps<'div'> & { term: string }) {\n const [local, rest] = splitProps(props, ['class', 'term', 'children'])\n\n return (\n <div\n data-slot=\"property-row\"\n class={cn('flex items-baseline justify-between gap-4 text-sm', local.class)}\n {...rest}\n >\n <span class=\"text-muted-foreground shrink-0\">{local.term}</span>\n <span class=\"min-w-0 truncate text-end select-text\">{local.children}</span>\n </div>\n )\n}\n"],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import type { Accessor } from 'solid-js';
1
2
  /**
2
3
  * UpdateDialog.
3
4
  *
@@ -101,6 +102,12 @@ export type UpdateDialogProps = {
101
102
  */
102
103
  defaultOpen?: boolean;
103
104
  onOpenChange?: (open: boolean) => void;
105
+ /**
106
+ * Supplies a stable external control to focus when a controlled dialog closes.
107
+ * Useful when a menu or other layer closes before this dialog opens; when omitted,
108
+ * the element focused immediately before the dialog opens is used when available.
109
+ */
110
+ restoreFocusRef?: Accessor<HTMLElement | undefined>;
104
111
  /** Render only the panel, for a caller that provides its own dialog. */
105
112
  class?: string;
106
113
  };
@@ -1 +1 @@
1
- {"version":3,"file":"update-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"names":[],"mappings":"AAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,iEAAiE;AACjE,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,UAAU,GACV,SAAS,GACT,WAAW,GACX,aAAa,GACb,YAAY,GACZ,WAAW,GACX,QAAQ,CAAA;AAEZ,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,KAAK,EAAE,WAAW,CAAA;IAClB,uCAAuC;IACvC,cAAc,EAAE,MAAM,CAAA;IACtB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,wDAAwD;IACxD,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,iEAAiE;IACjE,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;IACjC,8DAA8D;IAC9D,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACtD,sEAAsE;IACtE,gBAAgB,IAAI,OAAO,CAAA;CAC5B,CAAC,CAAA;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,aAAa,CAAA;IACtB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,uEAAuE;IACvE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAoBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCAqXpD"}
1
+ {"version":3,"file":"update-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAmBxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,iEAAiE;AACjE,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,UAAU,GACV,SAAS,GACT,WAAW,GACX,aAAa,GACb,YAAY,GACZ,WAAW,GACX,QAAQ,CAAA;AAEZ,mFAAmF;AACnF,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC;IACjC,KAAK,EAAE,WAAW,CAAA;IAClB,uCAAuC;IACvC,cAAc,EAAE,MAAM,CAAA;IACtB,uDAAuD;IACvD,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B,sDAAsD;IACtD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,qDAAqD;IACrD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,wDAAwD;IACxD,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAC,CAAA;AAEF;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,CAAC;IACnC,iEAAiE;IACjE,SAAS,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;IACjC,8DAA8D;IAC9D,KAAK,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;IAC7B;;;;;;OAMG;IACH,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAA;IACtD,sEAAsE;IACtE,gBAAgB,IAAI,OAAO,CAAA;CAC5B,CAAC,CAAA;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,aAAa,CAAA;IACtB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,uEAAuE;IACvE,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC;;;;OAIG;IACH,eAAe,CAAC,EAAE,QAAQ,CAAC,WAAW,GAAG,SAAS,CAAC,CAAA;IACnD,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAoBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCA6apD"}
@@ -1,19 +1,19 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
2
  import { Badge } from "../../ui/badge/badge.js";
3
3
  import { Button } from "../../ui/button/button.js";
4
- import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "../../ui/dialog/dialog.js";
4
+ import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, createDialogFocusRestoration } from "../../ui/dialog/dialog.js";
5
5
  import { Progress } from "../../ui/progress/progress.js";
6
6
  import { formatBytes, formatReleaseDate, plainTextFromMarkdown } from "../../../lib/version-notes.js";
7
7
  import { createComponent, insert, memo, template } from "solid-js/web";
8
8
  import { Check, Download, ExternalLink, LoaderCircle, RefreshCw, Sparkles } from "lucide-solid";
9
- import { Show, createEffect, createMemo, createSignal, onMount, splitProps } from "solid-js";
9
+ import { Show, createEffect, createMemo, createSignal, onCleanup, onMount, splitProps } from "solid-js";
10
10
  //#region src/components/composites/update-dialog/update-dialog.tsx
11
11
  var _tmpl$ = /*#__PURE__*/ template(`<div class="flex items-center gap-3"><span class="flex size-10 items-center justify-center rounded-xl bg-primary text-primary-foreground"></span><div>`);
12
12
  var _tmpl$2 = /*#__PURE__*/ template(`<section class="rounded-xl border border-primary/35 bg-primary-subtle p-4"aria-label="Available update"><div class="flex flex-wrap items-start justify-between gap-4"><div class="flex flex-col gap-1"><p class="flex items-center gap-2 text-sm font-semibold">Version <!> is ready</p><p class="text-sm text-muted-foreground">The signed installer is verified before <!> restarts.`);
13
13
  var _tmpl$3 = /*#__PURE__*/ template(`<section class="grid gap-2"aria-label="Update progress"><p class="text-xs text-muted-foreground"role=status>`);
14
- var _tmpl$4 = /*#__PURE__*/ template(`<p class="flex items-center gap-2 text-sm text-success"role=status> is up to date.`);
15
- var _tmpl$5 = /*#__PURE__*/ template(`<p class="flex items-center gap-2 text-sm text-success"role=status>`);
16
- var _tmpl$6 = /*#__PURE__*/ template(`<p class="rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm text-destructive"role=alert>`);
14
+ var _tmpl$4 = /*#__PURE__*/ template(`<p class="text-foreground flex items-center gap-2 text-sm"role=status> is up to date.`);
15
+ var _tmpl$5 = /*#__PURE__*/ template(`<p class="text-foreground flex items-center gap-2 text-sm"role=status>`);
16
+ var _tmpl$6 = /*#__PURE__*/ template(`<p class="text-foreground rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm"role=alert>`);
17
17
  var _tmpl$7 = /*#__PURE__*/ template(`<div class="flex flex-col gap-2"><div class="flex items-center gap-2"><h2 id=update-release-notes class="text-sm font-semibold">What changed in this release</h2></div><div class="max-h-52 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground"role=region aria-labelledby=update-release-notes tabindex=0>`);
18
18
  var _tmpl$8 = /*#__PURE__*/ template(`<div class="flex flex-col gap-2"><h2 id=update-changelog class="text-sm font-semibold">Installed changelog</h2><p class="text-xs text-muted-foreground">A plain-text history of the installed release channel.</p><div class="max-h-72 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground"role=region aria-labelledby=update-changelog tabindex=0>`);
19
19
  var _tmpl$9 = /*#__PURE__*/ template(`<div class="flex min-h-0 flex-col gap-5 overflow-y-auto p-6"><section class="rounded-xl border border-border bg-background/45 p-4"aria-label="Version status"><div class="flex flex-wrap items-start justify-between gap-4"><div class="flex flex-col gap-1"><p class="text-2xs font-semibold tracking-widest text-muted-foreground uppercase">Installed version</p><p class="text-xl font-semibold tracking-tight">v</p><p class="text-sm text-muted-foreground">`);
@@ -41,6 +41,7 @@ function UpdateDialog(props) {
41
41
  "open",
42
42
  "defaultOpen",
43
43
  "onOpenChange",
44
+ "restoreFocusRef",
44
45
  "class"
45
46
  ]);
46
47
  const [uncontrolledOpen, setUncontrolledOpen] = createSignal(local.defaultOpen ?? false);
@@ -49,36 +50,57 @@ function UpdateDialog(props) {
49
50
  if (local.open === void 0) setUncontrolledOpen(next);
50
51
  local.onOpenChange?.(next);
51
52
  };
53
+ const focusRestoration = createDialogFocusRestoration({
54
+ open,
55
+ restoreFocusRef: local.restoreFocusRef
56
+ });
52
57
  const appName = () => local.appName ?? "the application";
53
58
  const desktop = () => local.adapter.isDesktopRuntime();
54
59
  const [state, setState] = createSignal(null);
55
60
  const [busy, setBusy] = createSignal(false);
56
61
  const [error, setError] = createSignal("");
57
- const loadStatus = async () => {
58
- if (!desktop()) return;
62
+ let lifecycleEpoch = 0;
63
+ let activeOpenEpoch;
64
+ let disposed = false;
65
+ onCleanup(() => {
66
+ disposed = true;
67
+ activeOpenEpoch = void 0;
68
+ lifecycleEpoch += 1;
69
+ });
70
+ const loadStatus = async (isCurrent = () => !disposed) => {
71
+ if (!desktop() || !isCurrent()) return;
59
72
  try {
60
- setState(await local.adapter.getStatus());
73
+ const status = await local.adapter.getStatus();
74
+ if (isCurrent()) setState(status);
61
75
  } catch (caught) {
62
- setError(errorMessage(caught, "Version status is unavailable"));
76
+ if (isCurrent()) setError(errorMessage(caught, "Version status is unavailable"));
63
77
  }
64
78
  };
65
79
  onMount(() => {
66
- if (desktop()) loadStatus();
80
+ if (desktop() && !open()) {
81
+ const epoch = lifecycleEpoch;
82
+ loadStatus(() => !disposed && lifecycleEpoch === epoch);
83
+ }
67
84
  });
68
85
  const check = async () => {
69
86
  if (!desktop()) {
70
87
  setError(local.desktopOnlyMessage ?? `Update checks are available from the desktop app.`);
71
88
  return;
72
89
  }
90
+ const epoch = activeOpenEpoch;
91
+ const isCurrent = () => !disposed && epoch !== void 0 && activeOpenEpoch === epoch && lifecycleEpoch === epoch && open();
92
+ if (!isCurrent()) return;
73
93
  setBusy(true);
74
94
  setError("");
75
95
  try {
76
- setState(await local.adapter.check());
96
+ const checked = await local.adapter.check();
97
+ if (isCurrent()) setState(checked);
77
98
  } catch (caught) {
99
+ if (!isCurrent()) return;
78
100
  setError(errorMessage(caught, "Could not check for updates"));
79
- await loadStatus();
101
+ await loadStatus(isCurrent);
80
102
  } finally {
81
- setBusy(false);
103
+ if (isCurrent()) setBusy(false);
82
104
  }
83
105
  };
84
106
  /**
@@ -87,41 +109,58 @@ function UpdateDialog(props) {
87
109
  * disposed scope.
88
110
  */
89
111
  createEffect(() => {
90
- if (!open() || !desktop()) return;
112
+ if (!open() || !desktop()) {
113
+ if (activeOpenEpoch !== void 0) {
114
+ activeOpenEpoch = void 0;
115
+ lifecycleEpoch += 1;
116
+ }
117
+ return;
118
+ }
119
+ const epoch = ++lifecycleEpoch;
120
+ activeOpenEpoch = epoch;
91
121
  let active = true;
122
+ const isCurrent = () => active && !disposed && activeOpenEpoch === epoch && lifecycleEpoch === epoch && open();
123
+ onCleanup(() => {
124
+ active = false;
125
+ if (activeOpenEpoch === epoch) {
126
+ activeOpenEpoch = void 0;
127
+ lifecycleEpoch += 1;
128
+ }
129
+ });
92
130
  setError("");
93
131
  setBusy(true);
94
132
  (async () => {
95
133
  try {
96
134
  const current = await local.adapter.getStatus();
97
- if (!active) return;
135
+ if (!isCurrent()) return;
98
136
  setState(current);
99
137
  const checked = await local.adapter.check();
100
- if (active) setState(checked);
138
+ if (isCurrent()) setState(checked);
101
139
  } catch (caught) {
102
- if (active) setError(errorMessage(caught, "Could not check for updates"));
140
+ if (isCurrent()) setError(errorMessage(caught, "Could not check for updates"));
103
141
  } finally {
104
- if (active) setBusy(false);
142
+ if (isCurrent()) setBusy(false);
105
143
  }
106
144
  })();
107
- return () => {
108
- active = false;
109
- };
110
145
  });
111
146
  const install = async () => {
112
147
  const version = state()?.availableVersion;
113
- if (!version) return;
148
+ const epoch = activeOpenEpoch;
149
+ const isCurrent = () => !disposed && epoch !== void 0 && activeOpenEpoch === epoch && lifecycleEpoch === epoch && open();
150
+ if (!version || !isCurrent()) return;
114
151
  setBusy(true);
115
152
  setError("");
116
153
  try {
117
154
  const next = await local.adapter.install(version);
155
+ if (!isCurrent()) return;
118
156
  setState(next);
119
157
  if (next.phase === "failed") setError(errorMessage(next.error, "Update installation failed"));
120
158
  } catch (caught) {
159
+ if (!isCurrent()) return;
121
160
  setError(errorMessage(caught, "Update installation failed"));
122
- await loadStatus();
161
+ await loadStatus(isCurrent);
123
162
  } finally {
124
- setBusy(false);
163
+ if (isCurrent()) setBusy(false);
125
164
  }
126
165
  };
127
166
  const changelog = createMemo(() => plainTextFromMarkdown(state()?.changelog ?? ""));
@@ -184,6 +223,12 @@ function UpdateDialog(props) {
184
223
  get ["class"]() {
185
224
  return cn("max-w-3xl", local.class);
186
225
  },
226
+ get onOpenAutoFocus() {
227
+ return focusRestoration.onOpenAutoFocus;
228
+ },
229
+ get onCloseAutoFocus() {
230
+ return focusRestoration.onCloseAutoFocus;
231
+ },
187
232
  get children() {
188
233
  return [
189
234
  createComponent(DialogHeader, { get children() {
@@ -345,7 +390,7 @@ function UpdateDialog(props) {
345
390
  get children() {
346
391
  var _el$23 = _tmpl$4(), _el$24 = _el$23.firstChild;
347
392
  insert(_el$23, createComponent(Check, {
348
- "class": "size-4",
393
+ "class": "size-4 text-success",
349
394
  "aria-hidden": "true"
350
395
  }), _el$24);
351
396
  insert(_el$23, appName, _el$24);
@@ -359,7 +404,7 @@ function UpdateDialog(props) {
359
404
  get children() {
360
405
  var _el$25 = _tmpl$5();
361
406
  insert(_el$25, createComponent(Check, {
362
- "class": "size-4",
407
+ "class": "size-4 text-success",
363
408
  "aria-hidden": "true"
364
409
  }), null);
365
410
  insert(_el$25, createComponent(Show, {
@@ -1 +1 @@
1
- {"version":3,"file":"update-dialog.js","names":["createEffect","createMemo","createSignal","onMount","Show","splitProps","Check","Download","ExternalLink","LoaderCircle","RefreshCw","Sparkles","Badge","Button","Dialog","DialogClose","DialogContent","DialogDescription","DialogFooter","DialogHeader","DialogTitle","DialogTrigger","Progress","cn","formatBytes","formatReleaseDate","plainTextFromMarkdown","UpdatePhase","UpdateState","Readonly","phase","currentVersion","availableVersion","changelog","releaseNotes","releaseDate","downloadedBytes","totalBytes","error","releaseUrl","restartRequired","UpdateAdapter","getStatus","Promise","check","install","expectedVersion","isDesktopRuntime","UpdateDialogProps","adapter","appName","fallbackVersion","desktopOnlyMessage","open","defaultOpen","onOpenChange","class","errorMessage","caught","fallback","Error","message","isBusy","state","UpdateDialog","props","local","uncontrolledOpen","setUncontrolledOpen","setOpen","next","undefined","desktop","setState","busy","setBusy","setError","loadStatus","active","current","checked","version","notes","value","working","triggerLabel","progressValue","Math","min","round","_$createComponent","children","when","as","variant","size","_$memo","_el$","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_$insert","_el$4","_tmpl$9","_el$5","_el$6","_el$7","_el$8","_el$9","_el$0","_el$1","type","disabled","onClick","_el$10","_tmpl$2","_el$11","_el$12","_el$13","_el$14","_el$16","_el$15","_el$17","_el$18","_el$20","_el$19","released","_el$35","_tmpl$0","_el$36","_el$21","_tmpl$3","_el$22","aria-label","_el$23","_tmpl$4","_el$24","_el$25","_tmpl$5","_el$26","_tmpl$6","_el$27","_tmpl$7","_el$28","_el$29","_el$30","_el$31","_tmpl$8","_el$32","_el$33","_el$34","url","window","_$createComponent","_$memo"],"sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, onMount, Show, splitProps } from 'solid-js'\nimport { Check, Download, ExternalLink, LoaderCircle, RefreshCw, Sparkles } from 'lucide-solid'\nimport { Badge } from '../../ui/badge'\nimport { Button } from '../../ui/button'\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '../../ui/dialog'\nimport { Progress } from '../../ui/progress'\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 * **Errors are not always `Error`s.** An adapter can reject with a string, and a\n * shell can answer with an error field. Both are read, and a fallback is shown\n * rather than an empty alert.\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 | '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 /** False in a browser build, where there is no installer to drive. */\n isDesktopRuntime(): boolean\n}>\n\nexport type UpdateDialogProps = {\n adapter: UpdateAdapter\n /** The application's name, used in the title and the copy. */\n appName?: 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 /** 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 /** Render only the panel, for a caller that provides its own dialog. */\n class?: string\n}\n\nfunction errorMessage(caught: unknown, fallback: string): string {\n if (caught instanceof Error && caught.message) return caught.message\n if (typeof caught === 'string' && caught) return caught\n return 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' || state?.phase === 'downloading' || state?.phase === 'installing'\n )\n}\n\nexport function UpdateDialog(props: UpdateDialogProps) {\n const [local] = splitProps(props, [\n 'adapter',\n 'appName',\n 'fallbackVersion',\n 'desktopOnlyMessage',\n 'open',\n 'defaultOpen',\n 'onOpenChange',\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 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\n const loadStatus = async () => {\n if (!desktop()) return\n try {\n setState(await local.adapter.getStatus())\n } catch (caught) {\n setError(errorMessage(caught, 'Version status is unavailable'))\n }\n }\n\n onMount(() => {\n if (desktop()) void loadStatus()\n })\n\n const check = async () => {\n if (!desktop()) {\n setError(local.desktopOnlyMessage ?? `Update checks are available from the desktop app.`)\n return\n }\n setBusy(true)\n setError('')\n try {\n setState(await local.adapter.check())\n } catch (caught) {\n setError(errorMessage(caught, 'Could not check for updates'))\n await loadStatus()\n } finally {\n 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()) return\n let active = true\n setError('')\n setBusy(true)\n void (async () => {\n try {\n const current = await local.adapter.getStatus()\n if (!active) return\n setState(current)\n const checked = await local.adapter.check()\n if (active) setState(checked)\n } catch (caught) {\n if (active) setError(errorMessage(caught, 'Could not check for updates'))\n } finally {\n if (active) setBusy(false)\n }\n })()\n return () => {\n active = false\n }\n })\n\n const install = async () => {\n const version = state()?.availableVersion\n if (!version) return\n setBusy(true)\n setError('')\n try {\n const next = await local.adapter.install(version)\n setState(next)\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 }\n } catch (caught) {\n setError(errorMessage(caught, 'Update installation failed'))\n await loadStatus()\n } finally {\n setBusy(false)\n }\n }\n\n const changelog = createMemo(() => plainTextFromMarkdown(state()?.changelog ?? ''))\n const notes = () => {\n const value = state()?.releaseNotes\n return value ? plainTextFromMarkdown(value) : ''\n }\n const working = () => busy() || isBusy(state())\n\n const triggerLabel = () => {\n const current = state()\n const fallback = `v${local.fallbackVersion ?? '0.1.0'}`\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 class={cn('max-w-3xl', local.class)}>\n <DialogHeader>\n <div class=\"flex items-center gap-3\">\n <span class=\"flex size-10 items-center justify-center rounded-xl bg-primary text-primary-foreground\">\n <Sparkles class=\"size-5\" aria-hidden=\"true\" />\n </span>\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 class=\"flex min-h-0 flex-col gap-5 overflow-y-auto p-6\">\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 <p class=\"text-xl font-semibold tracking-tight\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </p>\n <p class=\"text-sm text-muted-foreground\">\n <Show\n when={state()?.phase === 'current'}\n fallback={\n <Show\n when={state()?.phase === 'available' && state()?.availableVersion}\n fallback={\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 <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\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 Check latest version\n </Button>\n </div>\n </section>\n\n <Show when={state()?.phase === 'available' && state()?.availableVersion}>\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} 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 <Button type=\"button\" size=\"sm\" disabled={working()} onClick={() => void install()}>\n <Show when={working()} fallback={<Download aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Install and restart\n </Button>\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 when={state()?.phase === 'downloading' || state()?.phase === 'installing'}>\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 </section>\n </Show>\n\n <Show when={state()?.phase === 'current'}>\n <p class=\"flex items-center gap-2 text-sm text-success\" role=\"status\">\n <Check class=\"size-4\" aria-hidden=\"true\" />\n {appName()} is up to date.\n </p>\n </Show>\n\n <Show when={state()?.phase === 'installed'}>\n <p class=\"flex items-center gap-2 text-sm text-success\" role=\"status\">\n <Check class=\"size-4\" 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=\"rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm text-destructive\"\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\">\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-mono text-xs 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 <h2 id=\"update-changelog\" class=\"text-sm font-semibold\">\n Installed changelog\n </h2>\n <p class=\"text-xs text-muted-foreground\">\n A plain-text history of the installed release channel.\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-mono text-xs 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 <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n // `noopener,noreferrer` because the release page is a different\n // origin and must not get a handle on this window.\n onClick={() => window.open(url(), '_blank', 'noopener,noreferrer')}\n >\n <ExternalLink aria-hidden=\"true\" />\n View releases\n </Button>\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","Check","Download","ExternalLink","LoaderCircle","RefreshCw","Sparkles","Badge","Button","Dialog","DialogClose","DialogContent","DialogDescription","DialogFooter","DialogHeader","DialogTitle","DialogTrigger","createDialogFocusRestoration","Progress","cn","formatBytes","formatReleaseDate","plainTextFromMarkdown","UpdatePhase","UpdateState","Readonly","phase","currentVersion","availableVersion","changelog","releaseNotes","releaseDate","downloadedBytes","totalBytes","error","releaseUrl","restartRequired","UpdateAdapter","getStatus","Promise","check","install","expectedVersion","isDesktopRuntime","UpdateDialogProps","adapter","appName","fallbackVersion","desktopOnlyMessage","open","defaultOpen","onOpenChange","restoreFocusRef","HTMLElement","class","errorMessage","caught","fallback","Error","message","isBusy","state","UpdateDialog","props","local","uncontrolledOpen","setUncontrolledOpen","setOpen","next","undefined","focusRestoration","desktop","setState","busy","setBusy","setError","lifecycleEpoch","activeOpenEpoch","disposed","loadStatus","isCurrent","status","epoch","checked","active","current","version","notes","value","working","triggerLabel","progressValue","Math","min","round","_$createComponent","children","when","as","variant","size","_$memo","onOpenAutoFocus","onCloseAutoFocus","_el$","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_$insert","_el$4","_tmpl$9","_el$5","_el$6","_el$7","_el$8","_el$9","_el$0","_el$1","type","disabled","onClick","_el$10","_tmpl$2","_el$11","_el$12","_el$13","_el$14","_el$16","_el$15","_el$17","_el$18","_el$20","_el$19","released","_el$35","_tmpl$0","_el$36","_el$21","_tmpl$3","_el$22","aria-label","_el$23","_tmpl$4","_el$24","_el$25","_tmpl$5","_el$26","_tmpl$6","_el$27","_tmpl$7","_el$28","_el$29","_el$30","_el$31","_tmpl$8","_el$32","_el$33","_el$34","url","window","_$createComponent","_$memo"],"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 } from 'solid-js'\nimport { Check, Download, ExternalLink, LoaderCircle, RefreshCw, Sparkles } from 'lucide-solid'\nimport { Badge } from '../../ui/badge'\nimport { Button } from '../../ui/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 { 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 * **Errors are not always `Error`s.** An adapter can reject with a string, and a\n * shell can answer with an error field. Both are read, and a fallback is shown\n * rather than an empty alert.\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 | '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 /** False in a browser build, where there is no installer to drive. */\n isDesktopRuntime(): boolean\n}>\n\nexport type UpdateDialogProps = {\n adapter: UpdateAdapter\n /** The application's name, used in the title and the copy. */\n appName?: 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 /** 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 errorMessage(caught: unknown, fallback: string): string {\n if (caught instanceof Error && caught.message) return caught.message\n if (typeof caught === 'string' && caught) return caught\n return 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' || state?.phase === 'downloading' || state?.phase === 'installing'\n )\n}\n\nexport function UpdateDialog(props: UpdateDialogProps) {\n const [local] = splitProps(props, [\n 'adapter',\n 'appName',\n 'fallbackVersion',\n 'desktopOnlyMessage',\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 let lifecycleEpoch = 0\n let activeOpenEpoch: number | undefined\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 if (isCurrent()) setState(status)\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 check = async () => {\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 setBusy(true)\n setError('')\n try {\n const checked = await local.adapter.check()\n if (isCurrent()) setState(checked)\n } catch (caught) {\n if (!isCurrent()) return\n setError(errorMessage(caught, 'Could not check for updates'))\n await loadStatus(isCurrent)\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 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 setBusy(true)\n void (async () => {\n try {\n const current = await local.adapter.getStatus()\n if (!isCurrent()) return\n setState(current)\n const checked = await local.adapter.check()\n if (isCurrent()) setState(checked)\n } catch (caught) {\n if (isCurrent()) setError(errorMessage(caught, 'Could not check for updates'))\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 try {\n const next = await local.adapter.install(version)\n if (!isCurrent()) return\n setState(next)\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 }\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 const changelog = createMemo(() => plainTextFromMarkdown(state()?.changelog ?? ''))\n const notes = () => {\n const value = state()?.releaseNotes\n return value ? plainTextFromMarkdown(value) : ''\n }\n const working = () => busy() || isBusy(state())\n\n const triggerLabel = () => {\n const current = state()\n const fallback = `v${local.fallbackVersion ?? '0.1.0'}`\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 class={cn('max-w-3xl', local.class)}\n onOpenAutoFocus={focusRestoration.onOpenAutoFocus}\n onCloseAutoFocus={focusRestoration.onCloseAutoFocus}\n >\n <DialogHeader>\n <div class=\"flex items-center gap-3\">\n <span class=\"flex size-10 items-center justify-center rounded-xl bg-primary text-primary-foreground\">\n <Sparkles class=\"size-5\" aria-hidden=\"true\" />\n </span>\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 class=\"flex min-h-0 flex-col gap-5 overflow-y-auto p-6\">\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 <p class=\"text-xl font-semibold tracking-tight\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </p>\n <p class=\"text-sm text-muted-foreground\">\n <Show\n when={state()?.phase === 'current'}\n fallback={\n <Show\n when={state()?.phase === 'available' && state()?.availableVersion}\n fallback={\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 <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\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 Check latest version\n </Button>\n </div>\n </section>\n\n <Show when={state()?.phase === 'available' && state()?.availableVersion}>\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} 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 <Button type=\"button\" size=\"sm\" disabled={working()} onClick={() => void install()}>\n <Show when={working()} fallback={<Download aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Install and restart\n </Button>\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 when={state()?.phase === 'downloading' || state()?.phase === 'installing'}>\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 </section>\n </Show>\n\n <Show when={state()?.phase === 'current'}>\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\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\">\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-mono text-xs 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 <h2 id=\"update-changelog\" class=\"text-sm font-semibold\">\n Installed changelog\n </h2>\n <p class=\"text-xs text-muted-foreground\">\n A plain-text history of the installed release channel.\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-mono text-xs 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 <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n // `noopener,noreferrer` because the release page is a different\n // origin and must not get a handle on this window.\n onClick={() => window.open(url(), '_blank', 'noopener,noreferrer')}\n >\n <ExternalLink aria-hidden=\"true\" />\n View releases\n </Button>\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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"}