flexdesk 0.17.1 → 0.17.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.3 (2026-10-22)
4
+
5
+ A patch release: everything below is additive. Every entry is documented in a
6
+ README section headed "(0.17.3)"; each entry links to it.
7
+
8
+ ### Added and changed
9
+
10
+ - [One toast, one history entry, one unread](README.md#one-toast-one-history-entry-one-unread-0173): `NotificationCenter.history` holds one entry per toast however it was raised (`show()`, `warn()` / `error()` / `info()` / `success()`, `toast:show`, `notification:show`, `log:entry`), and a toast that leaves marks its own entry `dismissed: true` with the `reason` instead of adding a second one. `nc.subscribe(fn)` calls `fn(entry, center)` once per entry added and answers the unsubscribe. `NotificationHistory` follows the centre through `subscribe()`, so every toast counts once on the bell, also one raised through the API (BUG-0034). It no longer watches the bus for toasts: its `notificationCenter` is a `NotificationCenter` (or anything with `history` and `subscribe`).
11
+
12
+ ### Removed or renamed exports
13
+
14
+ None. Compared with 0.17.2, across every entry point (`core`, `widgets`, `wm`,
15
+ `host`, `canvas`, `flow`, `charts`, `tiles`, `editor`) and the package
16
+ `exports` map, no export was added, removed or renamed. Not an export but worth
17
+ knowing: `NotificationCenter` gained the `subscribe(fn)` method (see above).
18
+
19
+ ## 0.17.2 (2026-10-22)
20
+
21
+ A patch release: everything below is additive. Every entry is documented in a
22
+ README section headed "(0.17.2)"; each entry links to it.
23
+
24
+ ### Added and changed
25
+
26
+ - [An open single instance takes new props: `navigate(props)`](README.md#an-open-single-instance-takes-new-props-navigateprops-0172): opening a `single` kind that is already open with new props reveals it and calls its content's `navigate(props)` hook once; the props are recorded on the tab. An open that names no props only reveals.
27
+ - [A link to a kind nobody registered](README.md#a-link-to-a-kind-nobody-registered-0172): `wm.navigate` to a kind that is not registered logs one warning, changes nothing and returns `false`.
28
+ - [A palette row drawn disabled, with a reason](README.md#a-palette-row-drawn-disabled-with-a-reason-0172): a command with `enabled: () => false` is listed greyed (`aria-disabled`), does nothing when picked, and shows its `disabledReason` as a tooltip.
29
+ - [A catalog source only behind its prefix: `scopedOnly`](README.md#a-catalog-source-only-behind-its-prefix-scopedonly-0172): a catalog source with `scopedOnly: true` is searched only when its prefix is typed, in the flat and the grouped palette.
30
+ - [The settings categories outside the page: `SettingsCategories`](README.md#the-settings-categories-outside-the-page-settingscategories-0172): `SettingsView` can leave out its own category list and filter, and `SettingsCategories` (new export from `@flexdesk/widgets`) shows them anywhere, kept in step with the view.
31
+ - [Toast cards are styled and clickable](README.md#toast-cards-are-styled-and-clickable-0172): toast cards use `twm-notification` and `twm-notification--<severity>`, styled in flexdesk.css and base.css, with their entry animation defined there; cards take pointer events, so their buttons work (BUG-0033). A sheet that targeted `.notification` must target `.twm-notification`.
32
+
33
+ ### Removed or renamed exports
34
+
35
+ None. Compared with 0.17.1, across every entry point (`core`, `widgets`, `wm`,
36
+ `host`, `canvas`, `flow`, `charts`, `tiles`, `editor`) and the package
37
+ `exports` map, exports were only added: `SettingsCategories` in `@flexdesk/widgets`.
38
+ Not an export but worth knowing: toast cards changed class from `notification`
39
+ to `twm-notification` (see above).
40
+
3
41
  ## 0.17.1 (2026-10-21)
4
42
 
5
43
  A patch release: everything below is additive. Every entry is documented in a
package/README.md CHANGED
@@ -237,6 +237,65 @@ with it. The taxonomy answers `isSingle(kind)`, `instanceKey(kind, props)` and
237
237
  `singleReason(kind)`; the window manager answers `isSingleKind(kind)`,
238
238
  `findInstance(kind, props)` and `revealInstance(kind, props)`.
239
239
 
240
+ ### An open single instance takes new props: `navigate(props)` (0.17.2)
241
+
242
+ Revealing a `single` instance used to drop the props the open asked for: the
243
+ observatory kept showing the planet it was opened with. A content factory that
244
+ returns a `navigate(props)` hook from its mount is now handed them:
245
+
246
+ ```js
247
+ content: {
248
+ observatory: (el, props) => {
249
+ const view = mountObservatory(el, props);
250
+ return {
251
+ destroy: () => view.destroy(),
252
+ navigate: (next) => view.focusPlanet(next.planet), // already open: go there
253
+ };
254
+ },
255
+ },
256
+
257
+ wm.navigate('observatory', { planet: 'mars' }); // open: revealed, then navigate({ planet: 'mars' })
258
+ ```
259
+
260
+ - **Every open path that reveals the instance calls it once**, after the
261
+ reveal, with a copy of the new props — `navigate`, the top nav,
262
+ `openInPrimary`, `openInTab*`, `splitLeafWith`, Back. The same props twice
263
+ are two calls; nothing is deduplicated, and no second instance is mounted.
264
+ - **The props are recorded on the instance's tab** (or its window's), so a
265
+ top-nav page restore or a reload mounts it with the latest. The tile is not
266
+ rebuilt by the write.
267
+ - **An open that names no props only reveals** — the top-nav button,
268
+ `openInPrimary(kind)`, `navigate(kind)`: it asks to see the instance, not to
269
+ empty it, so the hook is not called and the tab keeps its props.
270
+ - **A content without the hook is revealed as before**: its tab keeps its props
271
+ and nothing is called. A hook that throws is a console warning.
272
+
273
+ ### A link to a kind nobody registered (0.17.2)
274
+
275
+ `wm.navigate(kind, props, opts)` returns `true` once it has routed the
276
+ navigation. A `kind` the shell's `content` map has no factory for is refused
277
+ instead, on every destination (`main`, `origin`, a new tab, a window, a split,
278
+ the legacy `target`):
279
+
280
+ ```js
281
+ if (!wm.navigate(link.kind, link.props)) {
282
+ // nothing opened: tell the user the link is dead
283
+ }
284
+ ```
285
+
286
+ - **One `console.warn` naming the kind**, per call, and `false` back.
287
+ - **Nothing changes**: no tile, tab, window or desktop, no per-tab history, no
288
+ `onChange` — and no "no factory registered yet" placeholder tile, which is
289
+ what such a link used to leave behind.
290
+ - **No exception reaches the caller**, whatever `kind` is (`undefined`, `null`
291
+ and `''` included).
292
+
293
+ A shell that only navigates to kinds it registered sees no change; the return
294
+ value is new, so code that ignored it is unaffected. A split destination used
295
+ to hand back the new leaf's id; it now returns `true` like the others — call
296
+ `wm.splitLeafWith(...)` for the id. A restored layout that names an
297
+ unregistered kind still draws its placeholder: only `navigate` refuses.
298
+
240
299
  ### A pane that names what it shows: `ctx.setTitle` (0.12.1)
241
300
 
242
301
  A content factory receives `(hostEl, props, ctx)`, and `ctx` carries, per pane,
@@ -450,6 +509,60 @@ topic.* The fallback comes from `HelpModal.open(id, { fallback })`
450
509
  (`@flexdesk/widgets`), and you can pass one yourself. A topic id that nobody
451
510
  provides now shows *Help topic not found.* where it used to raise a load error.
452
511
 
512
+ ### A palette row drawn disabled, with a reason (0.17.2)
513
+
514
+ A palette row (a command from `commands`, or any other row) may carry
515
+ `enabled: () => boolean` and `disabledReason`. While `enabled()` returns false
516
+ the row is still listed, so it can say why it does nothing:
517
+
518
+ ```js
519
+ palette: {
520
+ commands: () => [
521
+ { id: 'save', label: 'Save', action: save,
522
+ enabled: () => dirty, disabledReason: 'Nothing to save' },
523
+ ],
524
+ },
525
+ ```
526
+
527
+ - The row gets the class `twm-cmdpal__item--disabled` and `aria-disabled="true"`,
528
+ and `disabledReason`, when given, is its `title` (the tooltip). The stylesheet
529
+ greys it.
530
+ - Picking it with a click, Enter, Ctrl+Enter or Shift+Enter runs nothing: not
531
+ its `action`, not `onPick`. The palette stays open on it.
532
+ - The arrow keys still move onto it and past it.
533
+ - `enabled()` is called again on every draw and every pick, so the row follows
534
+ your state while the palette is open. An `enabled()` that throws counts as
535
+ false.
536
+ - A row without `enabled` is drawn and picked as before. Both layouts (flat and
537
+ `grouped`) behave the same way.
538
+
539
+ `tests/command_palette_disabled.test.mjs` drives it in jsdom.
540
+
541
+ ### A catalog source only behind its prefix: `scopedOnly` (0.17.2)
542
+
543
+ A source can be too broad to compete with everything else in the palette: help
544
+ topics, say, which would crowd every query. Mark it `scopedOnly` and its rows are
545
+ listed only once its prefix has made the type chip:
546
+
547
+ ```js
548
+ createEntityCatalog({
549
+ sources: [
550
+ { navKind: 'model', list: listModels, shape: toRow },
551
+ { navKind: 'topic', list: listTopics, shape: toRow, scopedOnly: true },
552
+ ],
553
+ aliases: { help: ['topic'] },
554
+ });
555
+ ```
556
+
557
+ A plain typed query (`solver`) lists no `topic` row, in the flat and the grouped
558
+ layout alike: no row, no group header, no *Show all*. `help: solver` or
559
+ `topic: solver` lists its rows as any source's, and `help:` alone lists them all.
560
+ The mark is the palette's business: `loadAll`, `loadGrouped` and the tile menu
561
+ still return every source, a picked row reaches `onPick` as its `shape` made it,
562
+ and commands and `empty()` rows of the same kind are not held back. A source
563
+ without the option, or with `scopedOnly: false`, is searched exactly as in
564
+ 0.17.1. A value that is not a boolean is refused with a `TypeError`.
565
+
453
566
  ### Your keys, not the window manager's: `keymap` (0.17.0)
454
567
 
455
568
  The window manager binds F1..F12, Backspace, Ctrl+K, Ctrl+Tab, Ctrl/Shift+Arrows,
@@ -3034,6 +3147,52 @@ title; default "Settings"). The markup is `.twm-settings` >
3034
3147
  `.twm-settings__row` (`--modified`); `flexdesk.css` styles it from tokens.
3035
3148
  `tests/settings_view.test.mjs` covers it in jsdom.
3036
3149
 
3150
+ ## The settings categories outside the page: `SettingsCategories` (0.17.2)
3151
+
3152
+ A `SettingsView` made with `sidebar: false` draws no sidebar: no heading, no
3153
+ filter box, no category list and no *Reset all*, only the chosen category's
3154
+ settings (or the filter's matches). `new SettingsCategories({ view })` (in
3155
+ `@flexdesk/widgets`) draws those parts for that view wherever it is mounted,
3156
+ for example in an app's left navigator:
3157
+
3158
+ ```js
3159
+ import { SettingsView, SettingsCategories } from '@flexdesk/widgets';
3160
+
3161
+ const view = new SettingsView({ store, eventBus: bus, sidebar: false });
3162
+ view.mount(pageHost);
3163
+ const cats = new SettingsCategories({ view });
3164
+ cats.mount(navigatorHost);
3165
+
3166
+ const off = view.subscribe(({ category, query }) => { /* e.g. a tab title */ });
3167
+ // …
3168
+ off();
3169
+ cats.dispose();
3170
+ view.dispose();
3171
+ ```
3172
+
3173
+ It keeps no state of its own. Typing in its filter box is `view.setQuery()`,
3174
+ clicking a category is `view.selectCategory()` and its *Reset all* is
3175
+ `view.resetAll()`, which asks the view's `confirm` first and changes nothing
3176
+ unless it resolves true. It redraws from the view whenever the view's category,
3177
+ filter or registered categories change, so the two agree whichever side moved
3178
+ them: `view.selectCategory('window')` marks that category in the list, and
3179
+ `view.setQuery('shadow')` puts the text in its filter box. As in the view's own
3180
+ sidebar, choosing a category clears the filter, and no category is marked while
3181
+ the filter is in effect. Its clicks and keystrokes act once the view is mounted.
3182
+
3183
+ Two methods on `SettingsView` serve it and anything else that follows the view:
3184
+ `subscribe(fn)` calls `fn({ category, query })` after each such change and
3185
+ answers the unsubscribe; `isFiltering()` is true while the filter's trimmed
3186
+ text is not empty. `settingsViewContent({ …, sidebar: false })` passes the
3187
+ option through. A view made without `sidebar` (or with `sidebar: true`) draws
3188
+ exactly as in 0.17.1.
3189
+
3190
+ The markup is `.twm-settings-categories` > `.twm-settings__search`,
3191
+ `.twm-settings__nav` (`button.twm-settings__cat`, `--active`) and
3192
+ `.twm-settings__reset-all`, the same parts and classes as the sidebar's;
3193
+ `flexdesk.css` styles it, leaving its width and padding to the host.
3194
+ `tests/settings_view.test.mjs` §11–§14 cover it.
3195
+
3037
3196
  ## A step wizard: `Wizard` (0.17.0)
3038
3197
 
3039
3198
  `new Wizard({ steps, data, onFinish, onCancel, labels })` (in `@flexdesk/widgets`)
@@ -3368,7 +3527,8 @@ again: its pane hears `onShow` and `onSelect` fires, and nothing is written back
3368
3527
  Toasts, from `toast:show` on the bus (or `toastInfo` / `toastWarn` /
3369
3528
  `toastError` / `toastSuccess`) or `show()`. A payload with `persistent: true`
3370
3529
  stays until its × is clicked. `history` keeps every notification shown, and
3371
- `NotificationHistory` puts it behind a bell in the bottom bar.
3530
+ `NotificationHistory` puts it behind a bell in the bottom bar (one entry per toast;
3531
+ `subscribe(fn)` hears each one, 0.17.3).
3372
3532
 
3373
3533
  *New in 0.11.0:* **`overflow: 'collapse'`.** By default, a toast past
3374
3534
  `maxVisible` (3) pushes the oldest off the screen. With `'collapse'`, the toasts
@@ -3429,6 +3589,51 @@ job.on('end', () => nc.update(id, { progress: 1, severity: 'success', message: '
3429
3589
 
3430
3590
  `tests/notification_progress.test.mjs` holds it in jsdom.
3431
3591
 
3592
+ ### Toast cards are styled and clickable (0.17.2)
3593
+
3594
+ A toast card is `.twm-notification` with `.twm-notification--<severity>`
3595
+ (`info`, `success`, `warn`, `error`, `fatal`), and the "+N more" summary is
3596
+ `.twm-notification.twm-notification--summary`. `flexdesk.css` gives the card its
3597
+ background, a left border in the severity's colour (`--color-success`,
3598
+ `--color-warning`, `--color-danger`; info keeps the blue) and the entry
3599
+ animation `twm-notification-in`, which the sheet now defines. The card sets
3600
+ `pointer-events: auto` over the root's `none`, so its buttons and its
3601
+ click-to-close work.
3602
+
3603
+ Until 0.17.2 the card carried the bare `notification` / `notification--<severity>`
3604
+ classes, which no FlexDesk sheet styled: the card had no background and no
3605
+ border, and inherited `pointer-events: none`, so its buttons could not be
3606
+ clicked. A consumer stylesheet that targeted `.notification` should target
3607
+ `.twm-notification` instead. The live region is `.twm-notification-center__live-region`.
3608
+ `tests/notification_toast_styles.test.mjs` holds the classes, the cascade and
3609
+ the sheets.
3610
+
3611
+ ### One toast, one history entry, one unread (0.17.3)
3612
+
3613
+ `history` holds **one entry per toast**, however it was raised: `show()`, the
3614
+ `warn()` / `error()` / `info()` / `success()` helpers, `toast:show`,
3615
+ `notification:show` or `log:entry`. A toast that leaves (timeout, ×, overflow,
3616
+ `clearAll`) marks its own entry `dismissed: true` with the `reason`, and adds
3617
+ no entry. **`nc.subscribe(fn)`** calls `fn(entry, center)` once for each entry
3618
+ added, after it is in `history` (not for a `skipHistory` toast, and not for a
3619
+ dismissal), and answers the unsubscribe; `dispose()` drops every listener.
3620
+
3621
+ `NotificationHistory` follows the centre through `subscribe()`: every entry
3622
+ recorded is one unread on the bell until the popover is opened, also past
3623
+ `historyLimit`. It no longer watches the bus for toasts, so its
3624
+ `notificationCenter` is a `NotificationCenter` (or anything with `history` and
3625
+ `subscribe`).
3626
+
3627
+ ```js
3628
+ const off = nc.subscribe((entry) => console.log('recorded', entry.severity, entry.message));
3629
+ nc.warn('Disk almost full'); // the bell says 1
3630
+ ```
3631
+
3632
+ Until 0.17.3 the bell counted only on bus events, so a toast raised through
3633
+ `show()` / `warn()` / `error()` / `info()` never raised its badge, and a dismissal
3634
+ was pushed as a second entry: a toast that timed out was in `history` twice
3635
+ and counted twice. `tests/notification_history_follows_center.test.mjs` holds it.
3636
+
3432
3637
  ### Alerts with history: a log, a pane, a tab host and a bell (0.14.0)
3433
3638
 
3434
3639
  A toast is the moment. The **alert log** keeps what came in until it is
package/css/base.css CHANGED
@@ -275,6 +275,28 @@
275
275
  flex-direction: column;
276
276
  gap: var(--space-lg);
277
277
  }
278
+ /* A toast card. The root takes no pointer events (it overlays the page), so the
279
+ * card takes them back: without this its buttons cannot be clicked (BUG-0033). */
280
+ .twm-notification {
281
+ pointer-events: auto;
282
+ background: #1f1f20;
283
+ border: 1px solid #2f2f31;
284
+ border-left: 4px solid #4fc1ff;
285
+ border-radius: var(--radius-lg);
286
+ padding: var(--space-lg) 14px;
287
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
288
+ animation: twm-notification-in 160ms ease-out;
289
+ min-width: 280px;
290
+ cursor: pointer;
291
+ }
292
+ .twm-notification--success { border-left-color: var(--color-success, #3fb950); }
293
+ .twm-notification--warn { border-left-color: var(--color-warning, #ff9800); }
294
+ .twm-notification--error,
295
+ .twm-notification--fatal { border-left-color: var(--color-danger, #c74e4e); }
296
+ @keyframes twm-notification-in {
297
+ from { opacity: 0; transform: translateY(6px); }
298
+ to { opacity: 1; transform: translateY(0); }
299
+ }
278
300
  .twm-notification__header {
279
301
  display: flex;
280
302
  align-items: center;
@@ -425,7 +447,7 @@
425
447
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
426
448
  display: flex;
427
449
  flex-direction: column;
428
- animation: notification-in 160ms ease-out;
450
+ animation: twm-notification-in 160ms ease-out;
429
451
  }
430
452
  .twm-notification-history__header {
431
453
  display: flex;
package/css/flexdesk.css CHANGED
@@ -283,6 +283,28 @@
283
283
  flex-direction: column;
284
284
  gap: var(--space-lg);
285
285
  }
286
+ /* A toast card. The root takes no pointer events (it overlays the page), so the
287
+ * card takes them back: without this its buttons cannot be clicked (BUG-0033). */
288
+ .twm-notification {
289
+ pointer-events: auto;
290
+ background: #1f1f20;
291
+ border: 1px solid #2f2f31;
292
+ border-left: 4px solid #4fc1ff;
293
+ border-radius: var(--radius-lg);
294
+ padding: var(--space-lg) 14px;
295
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
296
+ animation: twm-notification-in 160ms ease-out;
297
+ min-width: 280px;
298
+ cursor: pointer;
299
+ }
300
+ .twm-notification--success { border-left-color: var(--color-success, #3fb950); }
301
+ .twm-notification--warn { border-left-color: var(--color-warning, #ff9800); }
302
+ .twm-notification--error,
303
+ .twm-notification--fatal { border-left-color: var(--color-danger, #c74e4e); }
304
+ @keyframes twm-notification-in {
305
+ from { opacity: 0; transform: translateY(6px); }
306
+ to { opacity: 1; transform: translateY(0); }
307
+ }
286
308
 
287
309
  /* NotificationCenter `overflow: 'collapse'`: the toasts past maxVisible, as one. */
288
310
  .twm-notification--summary {
@@ -455,7 +477,7 @@
455
477
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
456
478
  display: flex;
457
479
  flex-direction: column;
458
- animation: notification-in 160ms ease-out;
480
+ animation: twm-notification-in 160ms ease-out;
459
481
  }
460
482
  .twm-notification-history__header {
461
483
  display: flex;
@@ -8322,6 +8344,9 @@ body.twm-dragging * { user-select: none; }
8322
8344
  .twm-cmdpal__item--active .twm-cmdpal__acts { display: flex; }
8323
8345
  /* A copy verb a single kind refuses: drawn, greyed, with the reason as its title. */
8324
8346
  .twm-cmdpal__act--disabled { opacity: 0.45; }
8347
+ /* 0.17.2. A row whose `enabled()` says no: listed, reachable by the arrows,
8348
+ * greyed, with its `disabledReason` as the title; picking it does nothing. */
8349
+ .twm-cmdpal__item--disabled { opacity: 0.45; cursor: default; }
8325
8350
  .twm-cmdpal__item--active .twm-cmdpal__kind,
8326
8351
  .twm-cmdpal__item--active .twm-cmdpal__shortcut { display: none; }
8327
8352
  .twm-cmdpal__item--more { color: var(--text-muted); font-size: var(--font-size-sm); }
@@ -10541,6 +10566,17 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
10541
10566
  padding: var(--space-xl) 0;
10542
10567
  color: var(--text-muted);
10543
10568
  }
10569
+ /* SettingsCategories (0.17.2): the sidebar's filter, categories and
10570
+ * Reset all, mounted outside the view; the host sets its width and padding. */
10571
+ .twm-settings-categories {
10572
+ display: flex;
10573
+ flex-direction: column;
10574
+ min-height: 0;
10575
+ height: 100%;
10576
+ gap: var(--space-sm);
10577
+ color: var(--text-body);
10578
+ font-size: var(--font-size-sm);
10579
+ }
10544
10580
  /* ══ end SETTINGS VIEW ══ */
10545
10581
 
10546
10582
  /* ══ DOCUMENT OUTLINE (0.17.0) ═══════════════════════════════════════
package/dist/flexdesk.css CHANGED
@@ -283,6 +283,28 @@
283
283
  flex-direction: column;
284
284
  gap: var(--space-lg);
285
285
  }
286
+ /* A toast card. The root takes no pointer events (it overlays the page), so the
287
+ * card takes them back: without this its buttons cannot be clicked (BUG-0033). */
288
+ .twm-notification {
289
+ pointer-events: auto;
290
+ background: #1f1f20;
291
+ border: 1px solid #2f2f31;
292
+ border-left: 4px solid #4fc1ff;
293
+ border-radius: var(--radius-lg);
294
+ padding: var(--space-lg) 14px;
295
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
296
+ animation: twm-notification-in 160ms ease-out;
297
+ min-width: 280px;
298
+ cursor: pointer;
299
+ }
300
+ .twm-notification--success { border-left-color: var(--color-success, #3fb950); }
301
+ .twm-notification--warn { border-left-color: var(--color-warning, #ff9800); }
302
+ .twm-notification--error,
303
+ .twm-notification--fatal { border-left-color: var(--color-danger, #c74e4e); }
304
+ @keyframes twm-notification-in {
305
+ from { opacity: 0; transform: translateY(6px); }
306
+ to { opacity: 1; transform: translateY(0); }
307
+ }
286
308
 
287
309
  /* NotificationCenter `overflow: 'collapse'`: the toasts past maxVisible, as one. */
288
310
  .twm-notification--summary {
@@ -455,7 +477,7 @@
455
477
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
456
478
  display: flex;
457
479
  flex-direction: column;
458
- animation: notification-in 160ms ease-out;
480
+ animation: twm-notification-in 160ms ease-out;
459
481
  }
460
482
  .twm-notification-history__header {
461
483
  display: flex;
@@ -8322,6 +8344,9 @@ body.twm-dragging * { user-select: none; }
8322
8344
  .twm-cmdpal__item--active .twm-cmdpal__acts { display: flex; }
8323
8345
  /* A copy verb a single kind refuses: drawn, greyed, with the reason as its title. */
8324
8346
  .twm-cmdpal__act--disabled { opacity: 0.45; }
8347
+ /* 0.17.2. A row whose `enabled()` says no: listed, reachable by the arrows,
8348
+ * greyed, with its `disabledReason` as the title; picking it does nothing. */
8349
+ .twm-cmdpal__item--disabled { opacity: 0.45; cursor: default; }
8325
8350
  .twm-cmdpal__item--active .twm-cmdpal__kind,
8326
8351
  .twm-cmdpal__item--active .twm-cmdpal__shortcut { display: none; }
8327
8352
  .twm-cmdpal__item--more { color: var(--text-muted); font-size: var(--font-size-sm); }
@@ -10541,6 +10566,17 @@ button.twm-alerts__ref:hover { text-decoration: underline; }
10541
10566
  padding: var(--space-xl) 0;
10542
10567
  color: var(--text-muted);
10543
10568
  }
10569
+ /* SettingsCategories (0.17.2): the sidebar's filter, categories and
10570
+ * Reset all, mounted outside the view; the host sets its width and padding. */
10571
+ .twm-settings-categories {
10572
+ display: flex;
10573
+ flex-direction: column;
10574
+ min-height: 0;
10575
+ height: 100%;
10576
+ gap: var(--space-sm);
10577
+ color: var(--text-body);
10578
+ font-size: var(--font-size-sm);
10579
+ }
10544
10580
  /* ══ end SETTINGS VIEW ══ */
10545
10581
 
10546
10582
  /* ══ DOCUMENT OUTLINE (0.17.0) ═══════════════════════════════════════