@happyvertical/smrt-svelte 0.55.18 → 0.55.19

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 (70) hide show
  1. package/agents/cookbook-shell.md +26 -2
  2. package/agents/overview-surfaces.md +13 -3
  3. package/agents/workspace.md +46 -0
  4. package/dist/components/app/AppShell.svelte +29 -7
  5. package/dist/components/app/AppShell.svelte.d.ts +13 -5
  6. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  7. package/dist/components/app/DockToggles.svelte +89 -47
  8. package/dist/components/app/DockToggles.svelte.d.ts +1 -1
  9. package/dist/components/app/DockToggles.svelte.d.ts.map +1 -1
  10. package/dist/components/app/PhoneTabBar.svelte +37 -12
  11. package/dist/components/app/PhoneTabBar.svelte.d.ts +1 -1
  12. package/dist/components/app/PhoneTabBar.svelte.d.ts.map +1 -1
  13. package/dist/components/app/ShellSettingsPage.svelte +2 -2
  14. package/dist/components/app/ShellSettingsPage.svelte.d.ts +2 -2
  15. package/dist/components/app/ShellSettingsPage.svelte.d.ts.map +1 -1
  16. package/dist/components/app/dock-toggle.d.ts +45 -0
  17. package/dist/components/app/dock-toggle.d.ts.map +1 -1
  18. package/dist/components/app/dock-toggle.js +26 -1
  19. package/dist/components/app/index.d.ts +1 -0
  20. package/dist/components/app/index.d.ts.map +1 -1
  21. package/dist/components/app/shell-user.d.ts +14 -0
  22. package/dist/components/app/shell-user.d.ts.map +1 -0
  23. package/dist/components/app/shell-user.js +1 -0
  24. package/dist/components/cookbook/CookbookSectionOverview.svelte +22 -3
  25. package/dist/components/cookbook/CookbookSectionOverview.svelte.d.ts +11 -2
  26. package/dist/components/cookbook/CookbookSectionOverview.svelte.d.ts.map +1 -1
  27. package/dist/components/cookbook/CookbookSectionPage.svelte +27 -9
  28. package/dist/components/cookbook/CookbookSectionPage.svelte.d.ts.map +1 -1
  29. package/dist/components/cookbook/index.d.ts +3 -1
  30. package/dist/components/cookbook/index.d.ts.map +1 -1
  31. package/dist/components/cookbook/index.js +2 -1
  32. package/dist/components/cookbook/registry.d.ts +18 -4
  33. package/dist/components/cookbook/registry.d.ts.map +1 -1
  34. package/dist/components/cookbook/registry.js +40 -2
  35. package/dist/components/cookbook/types.d.ts +39 -4
  36. package/dist/components/cookbook/types.d.ts.map +1 -1
  37. package/dist/components/overview/OverviewGrid.svelte +1 -0
  38. package/dist/components/overview/index.d.ts +1 -1
  39. package/dist/components/overview/index.d.ts.map +1 -1
  40. package/dist/components/overview/types.d.ts +6 -0
  41. package/dist/components/overview/types.d.ts.map +1 -1
  42. package/dist/components/overview/widgets/ChartWidget.svelte +158 -18
  43. package/dist/components/overview/widgets/ChartWidget.svelte.d.ts.map +1 -1
  44. package/dist/components/overview/widgets/ShortcutsWidget.svelte +32 -3
  45. package/dist/components/overview/widgets/ShortcutsWidget.svelte.d.ts.map +1 -1
  46. package/dist/components/overview/widgets/core.d.ts +9 -1
  47. package/dist/components/overview/widgets/core.d.ts.map +1 -1
  48. package/dist/components/overview/widgets/core.js +20 -1
  49. package/dist/components/overview/widgets/data.d.ts +16 -6
  50. package/dist/components/overview/widgets/data.d.ts.map +1 -1
  51. package/dist/components/workspace/admin-shell/AdminShell.svelte +13 -0
  52. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  53. package/dist/components/workspace/admin-shell/ShellSettingsPanel.svelte +52 -7
  54. package/dist/components/workspace/admin-shell/ShellSettingsPanel.svelte.d.ts +6 -2
  55. package/dist/components/workspace/admin-shell/ShellSettingsPanel.svelte.d.ts.map +1 -1
  56. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte +54 -5
  57. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts +7 -2
  58. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts.map +1 -1
  59. package/dist/components/workspace/admin-shell/mobile-shell.d.ts +1 -1
  60. package/dist/components/workspace/admin-shell/mobile-shell.d.ts.map +1 -1
  61. package/dist/components/workspace/admin-shell/state.svelte.d.ts +9 -0
  62. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  63. package/dist/components/workspace/admin-shell/state.svelte.js +14 -0
  64. package/dist/i18n/strings.overview.d.ts +5 -0
  65. package/dist/i18n/strings.overview.d.ts.map +1 -1
  66. package/dist/i18n/strings.overview.js +5 -0
  67. package/dist/i18n/strings.workspace.d.ts +2 -0
  68. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  69. package/dist/i18n/strings.workspace.js +3 -0
  70. package/package.json +16 -16
@@ -1,8 +1,10 @@
1
1
  <script lang="ts">
2
2
  /**
3
- * Chart widget (#3727): bars or a line over labelled values, drawn from the
4
- * loaded data. Every chart carries its numbers as text (bar values inline, a
5
- * visually hidden table for the line) so it is readable without sight.
3
+ * Chart widget (#3727, #3888): bars, a line, a plain table, a donut or a single
4
+ * headline value over labelled values, drawn from the loaded data. Every chart
5
+ * carries its numbers as text (bar values inline, a visible table for `table`,
6
+ * a visually hidden table for the line and the donut, the value itself for
7
+ * `stat`) so it is readable without sight.
6
8
  */
7
9
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
8
10
  import { M } from '../../../i18n/strings.overview.js';
@@ -32,7 +34,10 @@ const points = $derived.by(() => {
32
34
  }
33
35
  return out;
34
36
  });
35
- const style = $derived(options.style === 'line' ? 'line' : 'bar');
37
+ const STYLES = ['bar', 'line', 'table', 'pie', 'stat'] as const;
38
+ const style = $derived(
39
+ STYLES.find((candidate) => candidate === options.style) ?? 'bar',
40
+ );
36
41
  const fmt = (value: number): string =>
37
42
  formatWidgetValue(value, {
38
43
  format: chart?.format ?? 'integer',
@@ -51,13 +56,132 @@ const polyline = $derived(
51
56
  })
52
57
  .join(' '),
53
58
  );
59
+
60
+ // Donut: slices are percentages of the positive total on a circle whose
61
+ // path length is 100, so a segment's dash length is its share.
62
+ const total = $derived(
63
+ points.reduce((sum, point) => sum + Math.max(0, point.value), 0),
64
+ );
65
+ const slices = $derived.by(() => {
66
+ let offset = 0;
67
+ return points.map((point, index) => {
68
+ const share = total > 0 ? (Math.max(0, point.value) / total) * 100 : 0;
69
+ const slice = { ...point, index, share, offset };
70
+ offset += share;
71
+ return slice;
72
+ });
73
+ });
74
+ const percent = (share: number): string =>
75
+ formatWidgetValue(share / 100, { format: 'percent', locale });
76
+
77
+ // Stat: the latest value, and its change against the one before.
78
+ const latest = $derived(points.at(-1));
79
+ const delta = $derived.by((): number | null => {
80
+ if (
81
+ chart &&
82
+ typeof chart.delta === 'number' &&
83
+ Number.isFinite(chart.delta)
84
+ ) {
85
+ return chart.delta;
86
+ }
87
+ const previous = points.at(-2);
88
+ if (!latest || !previous || previous.value === 0) return null;
89
+ return ((latest.value - previous.value) / Math.abs(previous.value)) * 100;
90
+ });
91
+ const deltaText = $derived(
92
+ delta === null || delta === 0
93
+ ? ''
94
+ : t(
95
+ M[
96
+ delta > 0
97
+ ? 'ui.overview.metric.change_up'
98
+ : 'ui.overview.metric.change_down'
99
+ ],
100
+ {
101
+ value: formatWidgetValue(Math.abs(delta) / 100, {
102
+ format: 'percent',
103
+ locale,
104
+ }),
105
+ },
106
+ ),
107
+ );
54
108
  const summary = $derived(
55
109
  `${title}. ${t(M['ui.overview.chart.summary'], { count: points.length })}`,
56
110
  );
57
111
  </script>
58
112
 
113
+ {#snippet dataTable(hidden: boolean)}
114
+ <table class={hidden ? 'smrt-chart__table' : 'smrt-chart__data'}>
115
+ <caption>{t(M['ui.overview.chart.table'])}</caption>
116
+ <thead>
117
+ <tr>
118
+ <th scope="col">{t(M['ui.overview.chart.label_column'])}</th>
119
+ <th scope="col">{t(M['ui.overview.chart.value_column'])}</th>
120
+ </tr>
121
+ </thead>
122
+ <tbody>
123
+ {#each points as point, index (index)}
124
+ <tr><th scope="row">{point.label}</th><td>{fmt(point.value)}</td></tr>
125
+ {/each}
126
+ </tbody>
127
+ </table>
128
+ {/snippet}
129
+
59
130
  {#if points.length === 0}
60
131
  <p class="smrt-chart__empty" data-smrt-component="ChartWidget">{t(M['ui.overview.no_data'])}</p>
132
+ {:else if style === 'table'}
133
+ <div class="smrt-chart smrt-chart--table" data-smrt-component="ChartWidget">
134
+ {@render dataTable(false)}
135
+ </div>
136
+ {:else if style === 'stat'}
137
+ {#if latest}
138
+ <div class="smrt-chart smrt-chart--stat" data-smrt-component="ChartWidget">
139
+ <p class="smrt-chart__headline">{fmt(latest.value)}</p>
140
+ <p class="smrt-chart__caption">{latest.label}</p>
141
+ {#if deltaText}
142
+ <p class="smrt-chart__delta" data-direction={(delta ?? 0) > 0 ? 'up' : 'down'}>
143
+ <span aria-hidden="true">{(delta ?? 0) > 0 ? '▲' : '▼'}</span>
144
+ {deltaText}
145
+ </p>
146
+ {/if}
147
+ </div>
148
+ {/if}
149
+ {:else if style === 'pie'}
150
+ {#if total > 0}
151
+ <div class="smrt-chart smrt-chart--pie" data-smrt-component="ChartWidget">
152
+ <svg class="smrt-chart__donut" viewBox="0 0 42 42" role="img" aria-label={summary}>
153
+ {#each slices as slice (slice.index)}
154
+ {#if slice.share > 0}
155
+ <circle
156
+ class="smrt-chart__slice"
157
+ data-slot={slice.index % 8}
158
+ cx="21"
159
+ cy="21"
160
+ r="15.9155"
161
+ fill="none"
162
+ stroke-width="6"
163
+ pathLength="100"
164
+ stroke-dasharray="{slice.share} {100 - slice.share}"
165
+ stroke-dashoffset={-slice.offset}
166
+ transform="rotate(-90 21 21)"
167
+ />
168
+ {/if}
169
+ {/each}
170
+ </svg>
171
+ <ul class="smrt-chart__legend" aria-hidden="true">
172
+ {#each slices as slice (slice.index)}
173
+ <li class="smrt-chart__legend-item">
174
+ <span class="smrt-chart__swatch" data-slot={slice.index % 8}></span>
175
+ <span class="smrt-chart__label">{slice.label}</span>
176
+ <span class="smrt-chart__value">{fmt(slice.value)} · {percent(slice.share)}</span>
177
+ </li>
178
+ {/each}
179
+ </ul>
180
+ {@render dataTable(true)}
181
+ </div>
182
+ {:else}
183
+ <p class="smrt-chart__empty" data-smrt-component="ChartWidget">{t(M['ui.overview.no_data'])}</p>
184
+ {/if}
61
185
  {:else if style === 'line'}
62
186
  <div class="smrt-chart" data-smrt-component="ChartWidget">
63
187
  <svg class="smrt-chart__line" viewBox="0 0 100 40" preserveAspectRatio="none" role="img" aria-label={summary}>
@@ -67,20 +191,7 @@ const summary = $derived(
67
191
  <span>{points[0].label}</span>
68
192
  <span>{points[points.length - 1].label}</span>
69
193
  </p>
70
- <table class="smrt-chart__table">
71
- <caption>{t(M['ui.overview.chart.table'])}</caption>
72
- <thead>
73
- <tr>
74
- <th scope="col">{t(M['ui.overview.chart.label_column'])}</th>
75
- <th scope="col">{t(M['ui.overview.chart.value_column'])}</th>
76
- </tr>
77
- </thead>
78
- <tbody>
79
- {#each points as point, index (index)}
80
- <tr><th scope="row">{point.label}</th><td>{fmt(point.value)}</td></tr>
81
- {/each}
82
- </tbody>
83
- </table>
194
+ {@render dataTable(true)}
84
195
  </div>
85
196
  {:else}
86
197
  <ul class="smrt-chart__bars" aria-label={summary} data-smrt-component="ChartWidget">
@@ -109,6 +220,35 @@ const summary = $derived(
109
220
  .smrt-chart__bar { display: block; block-size: 100%; border-radius: inherit; background: var(--smrt-color-primary); }
110
221
  .smrt-chart__value { font-variant-numeric: tabular-nums; color: var(--smrt-color-on-surface-variant); }
111
222
  .smrt-chart__empty { margin: 0; color: var(--smrt-color-on-surface-variant); }
223
+ .smrt-chart--table { color: var(--smrt-color-on-surface); }
224
+ .smrt-chart__data { inline-size: 100%; border-collapse: collapse; font-size: var(--smrt-typography-body-medium-size, 0.875rem); }
225
+ .smrt-chart__data caption { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
226
+ .smrt-chart__data th, .smrt-chart__data td { padding: var(--smrt-spacing-1) var(--smrt-spacing-2); border-block-end: var(--smrt-hairline); text-align: start; }
227
+ .smrt-chart__data thead th { background: var(--smrt-surface-sunken); color: var(--smrt-color-on-surface-variant); font-weight: var(--smrt-typography-weight-medium, 500); }
228
+ .smrt-chart__data tbody th { font-weight: inherit; overflow-wrap: anywhere; }
229
+ .smrt-chart__data td, .smrt-chart__data thead th:last-child { text-align: end; font-variant-numeric: tabular-nums; }
230
+ .smrt-chart--stat { gap: var(--smrt-spacing-1); color: var(--smrt-color-on-surface); }
231
+ .smrt-chart__headline { margin: 0; font-size: var(--smrt-typography-display-small-size, 2rem); font-weight: var(--smrt-typography-weight-medium, 500); line-height: 1.1; font-variant-numeric: tabular-nums; }
232
+ .smrt-chart__caption { margin: 0; color: var(--smrt-color-on-surface-variant); font-size: var(--smrt-typography-body-medium-size, 0.875rem); }
233
+ .smrt-chart__delta { margin: 0; color: var(--smrt-color-on-surface-variant); font-size: var(--smrt-typography-label-large-size, 0.875rem); }
234
+ .smrt-chart--pie { grid-template-columns: minmax(6rem, 10rem) minmax(0, 1fr); align-items: center; gap: var(--smrt-spacing-4); }
235
+ .smrt-chart__donut { inline-size: 100%; block-size: auto; }
236
+ .smrt-chart__slice[data-slot='0'], .smrt-chart__swatch[data-slot='0'] { stroke: var(--smrt-color-primary); background: var(--smrt-color-primary); }
237
+ .smrt-chart__slice[data-slot='1'], .smrt-chart__swatch[data-slot='1'] { stroke: var(--smrt-color-secondary); background: var(--smrt-color-secondary); }
238
+ .smrt-chart__slice[data-slot='2'], .smrt-chart__swatch[data-slot='2'] { stroke: var(--smrt-color-tertiary); background: var(--smrt-color-tertiary); }
239
+ .smrt-chart__slice[data-slot='3'], .smrt-chart__swatch[data-slot='3'] { stroke: var(--smrt-color-info); background: var(--smrt-color-info); }
240
+ .smrt-chart__slice[data-slot='4'], .smrt-chart__swatch[data-slot='4'] { stroke: var(--smrt-color-primary-container); background: var(--smrt-color-primary-container); }
241
+ .smrt-chart__slice[data-slot='5'], .smrt-chart__swatch[data-slot='5'] { stroke: var(--smrt-color-secondary-container); background: var(--smrt-color-secondary-container); }
242
+ .smrt-chart__slice[data-slot='6'], .smrt-chart__swatch[data-slot='6'] { stroke: var(--smrt-color-tertiary-container); background: var(--smrt-color-tertiary-container); }
243
+ .smrt-chart__slice[data-slot='7'], .smrt-chart__swatch[data-slot='7'] { stroke: var(--smrt-color-info-container); background: var(--smrt-color-info-container); }
244
+ .smrt-chart__legend { display: grid; gap: var(--smrt-spacing-1); margin: 0; padding: 0; list-style: none; color: var(--smrt-color-on-surface); font-size: var(--smrt-typography-body-medium-size, 0.875rem); }
245
+ .smrt-chart__legend-item { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--smrt-spacing-2); }
246
+ .smrt-chart__swatch { inline-size: 0.75rem; block-size: 0.75rem; border-radius: var(--smrt-radius-small); }
247
+ .smrt-chart__legend .smrt-chart__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
248
+ @media (prefers-reduced-motion: no-preference) {
249
+ .smrt-chart__slice { transition: stroke-dasharray var(--smrt-duration-normal) var(--smrt-easing-standard), stroke-dashoffset var(--smrt-duration-normal) var(--smrt-easing-standard); }
250
+ .smrt-chart__bar { transition: inline-size var(--smrt-duration-normal) var(--smrt-easing-standard); }
251
+ }
112
252
  .smrt-chart__table { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }
113
253
  }
114
254
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"ChartWidget.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/ChartWidget.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,KAAK,eAAe,EAAY,MAAM,WAAW,CAAC;AAoG3D,QAAA,MAAM,WAAW,2EAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"ChartWidget.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/ChartWidget.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,KAAK,eAAe,EAAY,MAAM,WAAW,CAAC;AAqM3D,QAAA,MAAM,WAAW,2EAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -13,8 +13,12 @@ import { safeHref } from '../markdown.js';
13
13
  import type { WidgetComponentProps } from '../types.js';
14
14
  import { isRecord, type ShortcutsWidgetData } from './data.js';
15
15
 
16
- let { data, iconComponent }: WidgetComponentProps<ShortcutsWidgetData> =
17
- $props();
16
+ let {
17
+ data,
18
+ iconComponent,
19
+ locale,
20
+ presentation = 'card',
21
+ }: WidgetComponentProps<ShortcutsWidgetData> = $props();
18
22
 
19
23
  const { t } = useI18n();
20
24
  const MAX_ITEMS = 48;
@@ -25,8 +29,21 @@ interface Card {
25
29
  href: string;
26
30
  icon: string;
27
31
  description?: string;
32
+ count?: number;
28
33
  }
29
34
 
35
+ const numberFormat = $derived(new Intl.NumberFormat(locale));
36
+ const plural = $derived(new Intl.PluralRules(locale));
37
+ const countLabel = (count: number): string =>
38
+ t(
39
+ M[
40
+ plural.select(count) === 'one'
41
+ ? 'ui.overview.shortcuts.records_one'
42
+ : 'ui.overview.shortcuts.records_other'
43
+ ],
44
+ { count: numberFormat.format(count) },
45
+ );
46
+
30
47
  const cards = $derived.by((): Card[] => {
31
48
  if (!isRecord(data) || !Array.isArray(data.items)) return [];
32
49
  const out: Card[] = [];
@@ -42,6 +59,12 @@ const cards = $derived.by((): Card[] => {
42
59
  typeof item.icon === 'string' ? item.icon : SHELL_DEFAULT_SECTION_ICON,
43
60
  description:
44
61
  typeof item.description === 'string' ? item.description : undefined,
62
+ count:
63
+ typeof item.count === 'number' &&
64
+ Number.isFinite(item.count) &&
65
+ item.count >= 0
66
+ ? item.count
67
+ : undefined,
45
68
  });
46
69
  }
47
70
  return out;
@@ -51,7 +74,7 @@ const cards = $derived.by((): Card[] => {
51
74
  {#if cards.length === 0}
52
75
  <p class="smrt-shortcuts__empty" data-smrt-component="ShortcutsWidget">{t(M['ui.overview.shortcuts.empty'])}</p>
53
76
  {:else}
54
- <ul class="smrt-shortcuts" data-smrt-component="ShortcutsWidget">
77
+ <ul class="smrt-shortcuts" data-presentation={presentation} data-smrt-component="ShortcutsWidget">
55
78
  {#each cards as card (card.id)}
56
79
  <li class="smrt-shortcuts__card">
57
80
  <a class="smrt-shortcuts__link" href={card.href}>
@@ -60,6 +83,7 @@ const cards = $derived.by((): Card[] => {
60
83
  <ShellSectionIcon name={card.icon} size={28} {iconComponent} />
61
84
  </span>
62
85
  <span class="smrt-shortcuts__title">{card.label}</span>
86
+ {#if card.count !== undefined}<span class="smrt-shortcuts__count">{countLabel(card.count)}</span>{/if}
63
87
  </span>
64
88
  {#if card.description}<span class="smrt-shortcuts__desc">{card.description}</span>{/if}
65
89
  </a>
@@ -81,6 +105,11 @@ const cards = $derived.by((): Card[] => {
81
105
  .smrt-shortcuts__icon :global(.smrt-shell-section-icon) { inline-size: 28px; block-size: 28px; }
82
106
  .smrt-shortcuts__title { font-weight: var(--smrt-typography-weight-medium, 500); overflow-wrap: anywhere; }
83
107
  .smrt-shortcuts__desc { display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; color: var(--smrt-color-on-surface-variant); font-size: var(--smrt-typography-body-medium-size, 0.875rem); }
108
+ .smrt-shortcuts__count { margin-inline-start: auto; flex: 0 0 auto; color: var(--smrt-color-on-surface-variant); font-size: var(--smrt-typography-label-large-size, 0.75rem); font-variant-numeric: tabular-nums; white-space: nowrap; }
109
+ /* Plain: entries sit directly on the panel, no frame per entry. */
110
+ .smrt-shortcuts[data-presentation='plain'] { gap: var(--smrt-spacing-1); }
111
+ .smrt-shortcuts[data-presentation='plain'] .smrt-shortcuts__card { border: 0; border-radius: var(--smrt-radius-medium); background: transparent; }
112
+ .smrt-shortcuts[data-presentation='plain'] .smrt-shortcuts__card:hover { background: color-mix(in srgb, var(--smrt-color-on-surface) calc(var(--smrt-state-hover) * 100%), transparent); }
84
113
  .smrt-shortcuts__empty { margin: 0; color: var(--smrt-color-on-surface-variant); }
85
114
  }
86
115
  </style>
@@ -1 +1 @@
1
- {"version":3,"file":"ShortcutsWidget.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/ShortcutsWidget.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAY,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAuE/D,QAAA,MAAM,eAAe,+EAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"ShortcutsWidget.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/ShortcutsWidget.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAY,KAAK,mBAAmB,EAAE,MAAM,WAAW,CAAC;AA+F/D,QAAA,MAAM,eAAe,+EAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
@@ -16,7 +16,15 @@ import type { WidgetDefinition, WidgetLoad } from '../types.js';
16
16
  import type { ChartWidgetData, MetricWidgetData, RecordListWidgetData, ShortcutsWidgetData } from './data.js';
17
17
  /** Metric: one number from a model. Options: model, measure, field, filter. */
18
18
  export declare function createMetricWidget(load: WidgetLoad<MetricWidgetData>): WidgetDefinition<MetricWidgetData>;
19
- /** Chart: values grouped by a field over a period. */
19
+ /** Chart option-schema version: 2 added the `table`, `pie` and `stat` styles. */
20
+ export declare const CHART_OPTIONS_VERSION = 2;
21
+ /** Chart options written at v1 (`style` bar or line) are valid at v2 unchanged. */
22
+ export declare function migrateChartOptions(options: Record<string, unknown>, fromVersion: number): Record<string, unknown> | null;
23
+ /**
24
+ * Chart: values grouped by a field over a period, drawn as `bar`, `line`,
25
+ * `table`, `pie` (donut) or `stat` (headline value with its change). This is
26
+ * the renderer half of the runtime-report chart hints (#3878).
27
+ */
20
28
  export declare function createChartWidget(load: WidgetLoad<ChartWidgetData>): WidgetDefinition<ChartWidgetData>;
21
29
  /** Record list: the latest or top records of a model. */
22
30
  export declare function createRecordListWidget(load: WidgetLoad<RecordListWidgetData>): WidgetDefinition<RecordListWidgetData>;
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EAEX,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,WAAW,CAAC;AA2CnB,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,GACjC,gBAAgB,CAAC,gBAAgB,CAAC,CAgBpC;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,CAAC,eAAe,CAAC,GAChC,gBAAgB,CAAC,eAAe,CAAC,CA0CnC;AAED,yDAAyD;AACzD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,CAAC,oBAAoB,CAAC,GACrC,gBAAgB,CAAC,oBAAoB,CAAC,CAkCxC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,CAAC,mBAAmB,CAAC,GACpC,gBAAgB,CAAC,mBAAmB,CAAC,CAmBvC;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,IAAI,gBAAgB,CAoBnD;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,GAAE,cAAsC,EAChD,OAAO,GAAE,iBAAsB,EAC/B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,MAAM,IAAI,CAyBZ"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EAEX,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,WAAW,CAAC;AA2CnB,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,GACjC,gBAAgB,CAAC,gBAAgB,CAAC,CAgBpC;AAED,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,mFAAmF;AACnF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAIhC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,CAAC,eAAe,CAAC,GAChC,gBAAgB,CAAC,eAAe,CAAC,CAiDnC;AAED,yDAAyD;AACzD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,CAAC,oBAAoB,CAAC,GACrC,gBAAgB,CAAC,oBAAoB,CAAC,CAkCxC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,CAAC,mBAAmB,CAAC,GACpC,gBAAgB,CAAC,mBAAmB,CAAC,CAmBvC;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,IAAI,gBAAgB,CAoBnD;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,GAAE,cAAsC,EAChD,OAAO,GAAE,iBAAsB,EAC/B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,MAAM,IAAI,CAyBZ"}
@@ -71,7 +71,19 @@ export function createMetricWidget(load) {
71
71
  load,
72
72
  };
73
73
  }
74
- /** Chart: values grouped by a field over a period. */
74
+ /** Chart option-schema version: 2 added the `table`, `pie` and `stat` styles. */
75
+ export const CHART_OPTIONS_VERSION = 2;
76
+ /** Chart options written at v1 (`style` bar or line) are valid at v2 unchanged. */
77
+ export function migrateChartOptions(options, fromVersion) {
78
+ return fromVersion >= 1 && fromVersion < CHART_OPTIONS_VERSION
79
+ ? { ...options }
80
+ : null;
81
+ }
82
+ /**
83
+ * Chart: values grouped by a field over a period, drawn as `bar`, `line`,
84
+ * `table`, `pie` (donut) or `stat` (headline value with its change). This is
85
+ * the renderer half of the runtime-report chart hints (#3878).
86
+ */
75
87
  export function createChartWidget(load) {
76
88
  return {
77
89
  type: 'chart',
@@ -106,12 +118,19 @@ export function createChartWidget(load) {
106
118
  choices: [
107
119
  { value: 'bar', label: 'ui.overview.opt.style_bar' },
108
120
  { value: 'line', label: 'ui.overview.opt.style_line' },
121
+ { value: 'table', label: 'ui.overview.opt.style_table' },
122
+ { value: 'pie', label: 'ui.overview.opt.style_pie' },
123
+ { value: 'stat', label: 'ui.overview.opt.style_stat' },
109
124
  ],
110
125
  },
111
126
  filterField,
112
127
  ],
113
128
  component: ChartWidget,
114
129
  load,
130
+ // v2 widens `style` from bar | line to bar | line | table | pie | stat
131
+ // (#3888). Every v1 option set is still valid, so migration is identity.
132
+ version: CHART_OPTIONS_VERSION,
133
+ migrate: migrateChartOptions,
115
134
  defaultSpan: 2,
116
135
  };
117
136
  }
@@ -26,6 +26,12 @@ export interface ChartWidgetData {
26
26
  format?: WidgetValueFormat;
27
27
  currency?: string;
28
28
  href?: string;
29
+ /**
30
+ * `stat` style: percent change of the headline (the last point) against the
31
+ * period before (12.5 means +12.5%). Absent, it is derived from the last two
32
+ * points.
33
+ */
34
+ delta?: number | null;
29
35
  }
30
36
  /** `records`: the rows to list. The loader chooses the title field. */
31
37
  export interface RecordListWidgetData {
@@ -41,15 +47,19 @@ export interface RecordListWidgetData {
41
47
  /** Link to the full list. */
42
48
  href?: string;
43
49
  }
50
+ /** One page a shortcut links to (#3888). `count` is the record count, when known. */
51
+ export interface ShortcutEntry {
52
+ href: string;
53
+ label: string;
54
+ icon?: string;
55
+ description?: string;
56
+ count?: number;
57
+ }
44
58
  /** `shortcuts`: cards that link to pages. */
45
59
  export interface ShortcutsWidgetData {
46
- items: {
60
+ items: (ShortcutEntry & {
47
61
  id: string;
48
- label: string;
49
- href: string;
50
- icon?: string;
51
- description?: string;
52
- }[];
62
+ })[];
53
63
  }
54
64
  /** The part of a shell nav model section `shortcutsFromNav` reads. */
55
65
  export interface ShortcutNavSection {
@@ -1 +1 @@
1
- {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/data.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE;QACL,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,EAAE,CAAC;CACL;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS;QACd,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC7D,EAAE,CAAC;CACL;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,SAAS,CAAC,EAAE,MAAM,GACjB,mBAAmB,CAoBrB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE"}
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/data.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,CAAC,aAAa,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;CAC3C;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS;QACd,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC7D,EAAE,CAAC;CACL;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,SAAS,CAAC,EAAE,MAAM,GACjB,mBAAmB,CAoBrB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE"}
@@ -1006,6 +1006,19 @@ function buildLayoutStyle(shell: ModuleShellState): string {
1006
1006
  return toggleOn(edge) || showToggle(edge);
1007
1007
  }
1008
1008
 
1009
+ $effect(() => {
1010
+ const next = {
1011
+ top: topRendered,
1012
+ left: shell.isEdgeShown('left'),
1013
+ right: rightRendered,
1014
+ bottom: bottomRendered,
1015
+ };
1016
+ const prev = untrack(() => shell.regionsRendered);
1017
+ if ((Object.keys(next) as PanelEdge[]).some((e) => next[e] !== prev[e])) {
1018
+ shell.regionsRendered = next;
1019
+ }
1020
+ });
1021
+
1009
1022
  $effect(() => {
1010
1023
  shell.hotkeysAvailable = (['top', 'left', 'right', 'bottom'] as const).some(
1011
1024
  hotkeyActive,
@@ -1 +1 @@
1
- {"version":3,"file":"AdminShell.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/AdminShell.svelte.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AdminShell.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/AdminShell.svelte.ts"],"names":[],"mappings":"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"}
@@ -22,8 +22,12 @@ interface Props {
22
22
  account?: boolean;
23
23
  /** Where the sign-out control goes; the control is hidden when neither this nor `onSignOut` is given. */
24
24
  signOutHref?: string;
25
- /** Called by the sign-out control instead of following `signOutHref`. */
26
- onSignOut?: () => void;
25
+ /**
26
+ * Called by the sign-out control instead of following `signOutHref`. May
27
+ * return a promise: the control is busy and disabled while it is pending;
28
+ * a rejection shows an inline error and leaves the section as it was.
29
+ */
30
+ onSignOut?: () => unknown;
27
31
  }
28
32
 
29
33
  const {
@@ -52,18 +56,49 @@ const REGION_OF_EDGE: Record<PanelEdge, ShellRegion> = {
52
56
  const regionLabel = (edge: PanelEdge) =>
53
57
  t(SHELL_REGION_MESSAGES[REGION_OF_EDGE[edge]]);
54
58
 
55
- /** The areas this shell offers; hidden ones stay listed when a layout can restore them. */
59
+ /**
60
+ * The areas this shell offers. A region the user hid stays listed so Show can
61
+ * restore it; a region that is shown but not rendered (nothing to put in it, or
62
+ * not presented at this width) is left out because its row would do nothing.
63
+ */
56
64
  const rows = $derived(
57
65
  layout
58
66
  ? layout.panels
59
- .filter((panel) => panel.available)
67
+ .filter(
68
+ (panel) =>
69
+ panel.available &&
70
+ (!panel.visible || shell.regionsRendered[panel.edge]),
71
+ )
60
72
  .map((panel) => ({ edge: panel.edge, visible: panel.visible }))
61
73
  : edges
62
- .filter((edge) => shell.panels[edge] !== 'hidden')
74
+ .filter(
75
+ (edge) =>
76
+ shell.panels[edge] !== 'hidden' && shell.regionsRendered[edge],
77
+ )
63
78
  .map((edge) => ({ edge, visible: true })),
64
79
  );
65
- const visibleCount = $derived(rows.filter((row) => row.visible).length);
80
+ /** Counts every shown region, rendered or not, so the last real one can't be hidden. */
81
+ const visibleCount = $derived(
82
+ layout
83
+ ? layout.panels.filter((panel) => panel.available && panel.visible).length
84
+ : rows.length,
85
+ );
66
86
  const shortcutRows = $derived(rows.filter((row) => row.visible));
87
+ let signingOut = $state(false);
88
+ let signOutFailed = $state(false);
89
+
90
+ async function runSignOut() {
91
+ if (signingOut || !onSignOut) return;
92
+ signingOut = true;
93
+ signOutFailed = false;
94
+ try {
95
+ await onSignOut();
96
+ } catch {
97
+ signOutFailed = true;
98
+ } finally {
99
+ signingOut = false;
100
+ }
101
+ }
67
102
  const canSignOut = $derived(Boolean(signOutHref || onSignOut));
68
103
  const hotkeysEnabled = $derived(shell.settings.hotkeysEnabled !== false);
69
104
 
@@ -118,9 +153,19 @@ function conflictFor(edge: PanelEdge): string | null {
118
153
  {/if}
119
154
  {#if canSignOut}
120
155
  {#if onSignOut}
121
- <Button variant="secondary" size="sm" onclick={onSignOut}>
156
+ <Button
157
+ variant="secondary"
158
+ size="sm"
159
+ loading={signingOut}
160
+ onclick={runSignOut}
161
+ >
122
162
  {t(M['ui.shell_settings.sign_out'])}
123
163
  </Button>
164
+ {#if signOutFailed}
165
+ <p role="alert" data-smrt-part="error">
166
+ {t(M['ui.shell_settings.sign_out_failed'])}
167
+ </p>
168
+ {/if}
124
169
  {:else}
125
170
  <Button variant="secondary" size="sm" href={signOutHref}>
126
171
  {t(M['ui.shell_settings.sign_out'])}
@@ -5,8 +5,12 @@ interface Props {
5
5
  account?: boolean;
6
6
  /** Where the sign-out control goes; the control is hidden when neither this nor `onSignOut` is given. */
7
7
  signOutHref?: string;
8
- /** Called by the sign-out control instead of following `signOutHref`. */
9
- onSignOut?: () => void;
8
+ /**
9
+ * Called by the sign-out control instead of following `signOutHref`. May
10
+ * return a promise: the control is busy and disabled while it is pending;
11
+ * a rejection shows an inline error and leaves the section as it was.
12
+ */
13
+ onSignOut?: () => unknown;
10
14
  }
11
15
  declare const ShellSettingsPanel: import("svelte").Component<Props, {}, "">;
12
16
  type ShellSettingsPanel = ReturnType<typeof ShellSettingsPanel>;
@@ -1 +1 @@
1
- {"version":3,"file":"ShellSettingsPanel.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/ShellSettingsPanel.svelte.ts"],"names":[],"mappings":"AAoBA,UAAU,KAAK;IACb,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yGAAyG;IACzG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AA+MD,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"ShellSettingsPanel.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/workspace/admin-shell/ShellSettingsPanel.svelte.ts"],"names":[],"mappings":"AAoBA,UAAU,KAAK;IACb,mEAAmE;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yGAAyG;IACzG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;CAC3B;AAmPD,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}