@gjsify/adwaita-web 0.64.13-next.283 → 0.64.14

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 (44) hide show
  1. package/lib/types/application.d.ts +3 -3
  2. package/lib/types/elements/adw-application-window.d.ts +4 -4
  3. package/lib/types/elements/adw-bin.d.ts +13 -0
  4. package/lib/types/elements/adw-split-button.d.ts +19 -28
  5. package/lib/types/elements/adw-tab-overview.d.ts +4 -4
  6. package/lib/types/elements/gtk-menu-button.d.ts +30 -42
  7. package/lib/types/elements/gtk-popover-bin.d.ts +18 -11
  8. package/lib/types/elements/gtk-popover-menu-bar.d.ts +15 -8
  9. package/lib/types/elements/gtk-popover-menu.d.ts +13 -15
  10. package/lib/types/elements/menu-slot.d.ts +49 -0
  11. package/lib/types/elements/popover-menu.d.ts +11 -7
  12. package/lib/types/index.d.ts +2 -1
  13. package/lib/types/namespace/gio.d.ts +1 -1
  14. package/lib/types/widget-element.d.ts +8 -2
  15. package/package.json +12 -13
  16. package/src/adw-application-window.spec.ts +5 -2
  17. package/src/adw-bin.spec.ts +20 -0
  18. package/src/application.ts +4 -4
  19. package/src/elements/adw-application-window.ts +17 -21
  20. package/src/elements/adw-bin.ts +20 -1
  21. package/src/elements/adw-split-button.ts +35 -59
  22. package/src/elements/adw-tab-overview.ts +18 -28
  23. package/src/elements/gtk-menu-button.ts +60 -82
  24. package/src/elements/gtk-popover-bin.ts +32 -38
  25. package/src/elements/gtk-popover-menu-bar.ts +41 -46
  26. package/src/elements/gtk-popover-menu.ts +35 -54
  27. package/src/elements/menu-slot.ts +139 -0
  28. package/src/elements/popover-menu.ts +26 -16
  29. package/src/gio-menu.spec.ts +173 -34
  30. package/src/gtk-image.spec.ts +3 -2
  31. package/src/gtk-menu-button.spec.ts +19 -16
  32. package/src/gtk-popover-bin.spec.ts +6 -5
  33. package/src/gtk-popover-menu-bar.spec.ts +11 -6
  34. package/src/gtk-popover-menu.spec.ts +22 -22
  35. package/src/index.ts +10 -2
  36. package/src/keyboard-operable.spec.ts +6 -5
  37. package/src/menu-actions.spec.ts +6 -1
  38. package/src/namespace/gio.ts +19 -2
  39. package/src/pango-markup.spec.ts +11 -0
  40. package/src/pango-markup.ts +5 -2
  41. package/src/shared-tree-builder.ts +6 -1
  42. package/src/split-button.spec.ts +25 -8
  43. package/src/widget-element.ts +28 -7
  44. package/src/widget-members.spec.ts +42 -3
@@ -1,11 +1,11 @@
1
- import { GioApplicationBase, type AdwMenuItem, type AdwMenuActions, type AdwMenuModel, type ApplicationHost } from '@gjsify/adwaita-core';
1
+ import { GioApplicationBase, type AdwMenuItem, type AdwMenuActions, type AdwMenuTree, type ApplicationHost } from '@gjsify/adwaita-core';
2
2
  export declare const webHost: ApplicationHost;
3
3
  /** An element's parent for action resolution; a popover is rooted at the widget that owns it. */
4
4
  export declare function parentOfElement(node: object): object | null;
5
5
  /** What a `Gtk.Actionable` does on activation: the action `prefix.name` found from `widget`. */
6
6
  export declare function activateWidgetAction(widget: Element, fullName: string, parameter?: unknown): boolean;
7
- /** What the items of `model` read from the actions registered above `el` (ADR 0097 § 3). */
8
- export declare function menuActionsOfElement(el: Element, model: AdwMenuModel): AdwMenuActions;
7
+ /** What the items of `tree` read from the actions registered above `el` (ADR 0097 § 3). */
8
+ export declare function menuActionsOfElement(el: Element, tree: AdwMenuTree): AdwMenuActions;
9
9
  /** What choosing `item` in a menu owned by `el` does: the action it names fires. */
10
10
  export declare function activateElementMenuItem(el: Element, item: AdwMenuItem): boolean;
11
11
  export declare class GioApplication extends GioApplicationBase {
@@ -1,10 +1,10 @@
1
- import { type AdwMenuInput, type AdwMenuModel } from '@gjsify/adwaita-core';
1
+ import { type AdwMenuModel } from '@gjsify/adwaita-core';
2
2
  import type { AdwBreakpoint } from '@gjsify/adwaita-core';
3
3
  import type { AdwSlot } from '../slotted-children.js';
4
4
  import { GtkApplicationWindow } from './gtk-application-window.js';
5
5
  export declare class AdwApplicationWindow extends GtkApplicationWindow {
6
6
  private readonly _features;
7
- private _model;
7
+ private readonly _slot;
8
8
  private _menubarEl;
9
9
  static get observedAttributes(): string[];
10
10
  /** The content area — the Adw.Window:content property. */
@@ -20,8 +20,8 @@ export declare class AdwApplicationWindow extends GtkApplicationWindow {
20
20
  get showMenubar(): boolean;
21
21
  set showMenubar(value: boolean);
22
22
  /** The menubar's model — what a GtkApplication would own here. */
23
- get menuModel(): AdwMenuModel;
24
- set menuModel(value: AdwMenuInput);
23
+ get menuModel(): AdwMenuModel | null;
24
+ set menuModel(value: AdwMenuModel | string | null);
25
25
  /** The menubar strip, or null before the window is connected. */
26
26
  get menubar(): HTMLDivElement | null;
27
27
  connectedCallback(): void;
@@ -5,6 +5,19 @@ export declare class AdwBin extends WidgetElement {
5
5
  get child(): Element | null;
6
6
  /** `adw_bin_set_child`: replace the current child, unparenting it. */
7
7
  set child(value: Element | null);
8
+ /**
9
+ * `adw_bin_get_child` / `adw_bin_set_child` — the METHOD spelling of {@link child}.
10
+ *
11
+ * The property alone is not the whole door. `Adw.Bin:child` is reached two ways and only
12
+ * one of them is a property: markup writes `child:`, but code built against GI calls
13
+ * `bin.get_child()` / `bin.set_child(w)`, which is the spelling a `.blp`-driven widget
14
+ * shared across renderers uses — GJS and the NativeScript bridge both answer it
15
+ * (`AdwSingleChildBase`), and here it was missing, so the shared code died with
16
+ * `get_child is not a function` on the web and nowhere else.
17
+ */
18
+ get_child(): Element | null;
19
+ /** `adw_bin_set_child`. Refuses a child that already has a parent, as {@link setChild} does. */
20
+ set_child(child: Element | null): void;
8
21
  connectedCallback(): void;
9
22
  disconnectedCallback(): void;
10
23
  /**
@@ -1,8 +1,15 @@
1
- import type { AdwMenuActions, AdwMenuInput, AdwMenuModel, SplitButtonDirection } from '@gjsify/adwaita-core';
1
+ import type { AdwMenuActions, AdwMenuModel, SplitButtonDirection } from '@gjsify/adwaita-core';
2
2
  import './gtk-popover.js';
3
3
  import { WidgetElement } from '../widget-element.js';
4
4
  export declare class AdwSplitButton extends WidgetElement {
5
5
  private readonly _state;
6
+ /**
7
+ * The model's door, tree snapshot and `items-changed` subscription (ADR 0128). The
8
+ * STATE still owns the menu as a property — `dropdownEnabled` is derived from it and
9
+ * the notify order is libadwaita's — so every write here goes through the slot FIRST
10
+ * (which is what refuses it and snapshots it) and is then handed to the state.
11
+ */
12
+ private readonly _slot;
6
13
  private _actionEl;
7
14
  private _dropdownEl;
8
15
  private _arrowEl;
@@ -24,39 +31,23 @@ export declare class AdwSplitButton extends WidgetElement {
24
31
  /** The symbolic icon name, or `null` when the action half shows a label or nothing. */
25
32
  get iconName(): string | null;
26
33
  set iconName(value: string);
27
- /** The menu, normalised (ADR 0042). Empty when the dropdown has none. */
28
- get menuModel(): AdwMenuModel;
29
34
  /**
30
- * Set the menu from anything the portable model accepts — a bare `string[]`, item
31
- * descriptors, sections, submenus.
35
+ * The menu the dropdown pops up, by identity, or `null` when it has none (ADR 0128).
32
36
  *
33
- * The REFUSAL is here rather than at render time: a `custom` item names an
34
- * application widget this surface cannot host, and discovering that as a blank row
35
- * three interactions later is what {@link assertMenuRenderable} exists to prevent.
37
+ * `null` is the ONLY spelling of "no menu" now: an empty model is a menu, so an empty
38
+ * one leaves the dropdown enabled exactly as it does in GTK, where a non-null model
39
+ * still builds a popover (measured, `MENU_MODEL_DOOR_VECTORS`).
36
40
  */
37
- set menuModel(value: AdwMenuInput);
41
+ get menuModel(): AdwMenuModel | null;
38
42
  /**
39
- * Normalise, REFUSE, store — the one door every menu comes through.
40
- *
41
- * The property setter is not the only way a menu arrives: an attribute is, and the
42
- * first cut refused only the setter, so a `custom` item written in markup drew
43
- * exactly the row `assertMenuRenderable` exists to prevent — one that reads like a
44
- * command and does nothing.
45
- *
46
- * THE TWO DOORS REFUSE DIFFERENTLY, and the difference is which side can hear it.
47
- * A property assignment is a CALL, so it throws and the caller can catch. An
48
- * attribute is MARKUP, parsed by the browser: a throw from `connectedCallback` is
49
- * not delivered to whoever appended the element, it is reported as an uncaught
50
- * page error — measured, it broke `adwaita-upgrade-order.spec.ts`, which counts
51
- * exactly those. Nobody can handle it and everybody else pays for it. So the
52
- * attribute path REFUSES THE MENU (no menu, and a menu-less dropdown is
53
- * insensitive, which is visible) and says why on `console.error`.
43
+ * Take a real model, a JSON string in the portable spelling, or `null`.
54
44
  *
55
- * A REFUSAL AND A TYPO ARE STILL DIFFERENT THINGS. `parseMenuModel` is total
56
- * because malformed JSON is an author slip and must not stop the element
57
- * upgrading; `custom` is well-formed, deliberate, and unhonourable here.
45
+ * The REFUSAL is at the assignment rather than at render time: a `custom` item names
46
+ * an application widget this surface cannot host, and discovering that as a blank row
47
+ * three interactions later is what the refusal exists to prevent. {@link MenuSlot}
48
+ * owns the door; the state is handed only a menu this surface agreed to draw.
58
49
  */
59
- private _acceptMenu;
50
+ set menuModel(value: AdwMenuModel | string | null);
60
51
  /**
61
52
  * What the action group publishes about the actions this menu names — the portable
62
53
  * stand-in for a `GActionGroup`, and the ONLY place a menu's enabled/checked state
@@ -1,4 +1,4 @@
1
- import { type AdwMenuInput, type AdwMenuModel } from '@gjsify/adwaita-core';
1
+ import { type AdwMenuModel } from '@gjsify/adwaita-core';
2
2
  import type { AdwTabView } from './adw-tab-view.js';
3
3
  import { WidgetElement } from '../widget-element.js';
4
4
  export declare class AdwTabOverview extends WidgetElement {
@@ -20,7 +20,7 @@ export declare class AdwTabOverview extends WidgetElement {
20
20
  private _newTabBtn;
21
21
  private readonly _thumbs;
22
22
  private _view;
23
- private _model;
23
+ private readonly _slot;
24
24
  private _searchActive;
25
25
  private _initialized;
26
26
  /** The last visibility actually painted, so `notify::open` fires on a real change. */
@@ -66,8 +66,8 @@ export declare class AdwTabOverview extends WidgetElement {
66
66
  get enableNewTab(): boolean;
67
67
  set enableNewTab(value: boolean);
68
68
  /** `Adw.TabOverview:secondary-menu` — the portal menu of extra actions. */
69
- get secondaryMenu(): AdwMenuModel;
70
- set secondaryMenu(value: AdwMenuInput);
69
+ get secondaryMenu(): AdwMenuModel | null;
70
+ set secondaryMenu(value: AdwMenuModel | string | null);
71
71
  /** `Adw.TabOverview:show-start-title-buttons`, TRUE upstream, so `="false"` turns it off. */
72
72
  get showStartTitleButtons(): boolean;
73
73
  set showStartTitleButtons(value: boolean);
@@ -1,10 +1,11 @@
1
- import type { AdwMenuActions, AdwMenuInput, AdwMenuModel, SplitButtonDirection } from '@gjsify/adwaita-core';
1
+ import type { AdwMenuActions, AdwMenuModel, SplitButtonDirection } from '@gjsify/adwaita-core';
2
2
  import './gtk-popover.js';
3
3
  import { WidgetElement } from '../widget-element.js';
4
4
  export declare class GtkMenuButton extends WidgetElement {
5
5
  private _buttonEl;
6
6
  private _popoverEl;
7
- private _model;
7
+ /** The model, its tree snapshot and its `items-changed` subscription (ADR 0128). */
8
+ private readonly _slot;
8
9
  private _actions;
9
10
  private _menuTitle;
10
11
  private _initialized;
@@ -12,41 +13,26 @@ export declare class GtkMenuButton extends WidgetElement {
12
13
  static get observedAttributes(): string[];
13
14
  /** Whether the popover menu is currently open. */
14
15
  get active(): boolean;
15
- /** The menu, normalised (ADR 0042). Setting rebuilds the popover. */
16
- get menuModel(): AdwMenuModel;
17
16
  /**
18
- * Set the menu from anything the portable model accepts. Refused HERE, at the
19
- * assignment, for a `custom` item this surface cannot host — see the split button's
20
- * note on the same setter.
17
+ * The menu this button pops up, BY IDENTITY — the model itself, `null` when there is
18
+ * none, exactly as `gtk_menu_button_get_menu_model` answers (ADR 0128).
19
+ *
20
+ * It no longer answers an array, and `[]` is no longer how "no menu" is spelled: an
21
+ * EMPTY model is a menu that GTK still builds a popover for, and `null` is the one
22
+ * spelling of its absence (measured, `MENU_MODEL_DOOR_VECTORS`).
21
23
  */
22
- set menuModel(value: AdwMenuInput);
23
- /** `Gtk.MenuButton:menu-model` — the GIR spelling of {@link menuModel}. */
24
- get menu_model(): AdwMenuModel;
25
- set menu_model(value: AdwMenuInput);
26
- get_menu_model(): AdwMenuModel;
27
- set_menu_model(value: AdwMenuInput): void;
24
+ get menuModel(): AdwMenuModel | null;
28
25
  /**
29
- * Normalise, REFUSE, store — the one door every menu comes through.
30
- *
31
- * The property setter is not the only way a menu arrives: an attribute is, and the
32
- * first cut refused only the setter, so a `custom` item written in markup drew
33
- * exactly the row `assertMenuRenderable` exists to prevent — one that reads like a
34
- * command and does nothing.
35
- *
36
- * THE TWO DOORS REFUSE DIFFERENTLY, and the difference is which side can hear it.
37
- * A property assignment is a CALL, so it throws and the caller can catch. An
38
- * attribute is MARKUP, parsed by the browser: a throw from `connectedCallback` is
39
- * not delivered to whoever appended the element, it is reported as an uncaught
40
- * page error — measured, it broke `adwaita-upgrade-order.spec.ts`, which counts
41
- * exactly those. Nobody can handle it and everybody else pays for it. So the
42
- * attribute path REFUSES THE MENU (no menu, and a menu-less dropdown is
43
- * insensitive, which is visible) and says why on `console.error`.
44
- *
45
- * A REFUSAL AND A TYPO ARE STILL DIFFERENT THINGS. `parseMenuModel` is total
46
- * because malformed JSON is an author slip and must not stop the element
47
- * upgrading; `custom` is well-formed, deliberate, and unhonourable here.
26
+ * Take a real model, a JSON string in the portable spelling, or `null`. Refused HERE,
27
+ * at the assignment, for a `custom` item this surface cannot host — {@link MenuSlot}
28
+ * owns both doors and the note on why they refuse differently.
48
29
  */
49
- private _acceptMenu;
30
+ set menuModel(value: AdwMenuModel | string | null);
31
+ /** `Gtk.MenuButton:menu-model` — the GIR spelling of {@link menuModel}. */
32
+ get menu_model(): AdwMenuModel | null;
33
+ set menu_model(value: AdwMenuModel | string | null);
34
+ get_menu_model(): AdwMenuModel | null;
35
+ set_menu_model(value: AdwMenuModel | string | null): void;
50
36
  /**
51
37
  * What the action group publishes about the actions this menu names — the portable
52
38
  * stand-in for a `GActionGroup`, and the only source of a menu's enabled/checked
@@ -62,18 +48,20 @@ export declare class GtkMenuButton extends WidgetElement {
62
48
  set direction(value: SplitButtonDirection);
63
49
  connectedCallback(): void;
64
50
  attributeChangedCallback(name: string): void;
65
- /**
66
- * The `menu-model` attribute, through the core parser both elements share.
67
- *
68
- * The copy this replaces was weaker in a way markup can reach: it kept `id` and
69
- * `icon` WHATEVER their runtime type, so `{"label":"Open","id":7}` produced an
70
- * entry whose `id` is a number in a field typed `string` — and `id` is what the
71
- * activation event reports. The core parser keeps only string-typed attributes.
72
- */
73
- private _parseMenuAttr;
74
51
  private _onPopoverToggled;
75
52
  private _render;
76
53
  /** The registry's answer, unless the application wrote `actions` itself (ADR 0097 § 2). */
77
54
  private _refreshActions;
55
+ /**
56
+ * `gtk_menu_button_set_menu_model` notifies UNCONDITIONALLY — no identity guard, so the
57
+ * same model written twice notifies twice and `null` over `null` notifies as well
58
+ * (measured, `MENU_MODEL_NOTIFY_VECTORS`). Its `Adw.SplitButton` sibling guards on
59
+ * pointer equality, and the two disagree on purpose: a port with one rule for both is
60
+ * wrong about one of them.
61
+ *
62
+ * A MUTATION of the held model is not a write and notifies nothing — it arrives over
63
+ * `items-changed`, where it redraws the rows and stops.
64
+ */
65
+ private _notifyModel;
78
66
  private _renderMenu;
79
67
  }
@@ -1,22 +1,29 @@
1
- import type { AdwMenuActions, AdwMenuInput, AdwMenuModel } from '@gjsify/adwaita-core';
1
+ import type { AdwMenuActions, AdwMenuModel } from '@gjsify/adwaita-core';
2
2
  import './gtk-popover-menu.js';
3
3
  import type { GtkPopoverMenu } from './gtk-popover-menu.js';
4
4
  import { WidgetElement } from '../widget-element.js';
5
5
  export declare class GtkPopoverBin extends WidgetElement {
6
- private _model;
6
+ /**
7
+ * The model's door and `items-changed` subscription (ADR 0128), with NOTHING to do
8
+ * when the menu it holds mutates: this bin draws no rows. The `<gtk-popover-menu>` it
9
+ * built holds the SAME model and watches it through a slot of its own, so the rows
10
+ * follow one level down — and GTK sends no `notify::menu-model` for a mutation either
11
+ * (measured, `MENU_MODEL_NOTIFY_VECTORS`).
12
+ */
13
+ private readonly _slot;
7
14
  private _popover;
8
15
  private _actions;
9
16
  private _initialized;
10
17
  private _longPressTimer;
11
18
  static get observedAttributes(): string[];
12
- /** The menu model, normalised (ADR 0042). */
13
- get menuModel(): AdwMenuModel;
19
+ /** The menu this bin pops up, by identity, or `null` (ADR 0128). */
20
+ get menuModel(): AdwMenuModel | null;
14
21
  /**
15
22
  * The model the popover is built from — ONE slot with `popover`, because GTK's two
16
23
  * setters clear each other (gtkpopoverbin.c:474-500, :543-551). A model REPLACES the
17
24
  * popover; a popover DROPS the model, and both notify for both properties.
18
25
  */
19
- set menuModel(value: AdwMenuInput | AdwMenuModel);
26
+ set menuModel(value: AdwMenuModel | string | null);
20
27
  /**
21
28
  * The popover this bin presents, or `null`.
22
29
  *
@@ -61,6 +68,12 @@ export declare class GtkPopoverBin extends WidgetElement {
61
68
  * un-modified key beside Shift is the whole binding here.
62
69
  */
63
70
  private _onKeyDown;
71
+ /**
72
+ * The popover follows the PRESENCE of a model, not its length: an empty non-null
73
+ * `Gio.Menu` at `menu-model` still builds a `GtkPopoverMenu` in GTK and still
74
+ * dissociates an application-set popover (measured, `MENU_MODEL_DOOR_VECTORS`), and
75
+ * `null` is the one spelling of "no menu".
76
+ */
64
77
  private _buildPopover;
65
78
  /**
66
79
  * `set_popover` (gtkpopoverbin.c:543-587): hide and unparent the old one, parent the
@@ -74,10 +87,4 @@ export declare class GtkPopoverBin extends WidgetElement {
74
87
  * of its own to report.
75
88
  */
76
89
  private _notifyModel;
77
- /**
78
- * The `menu-model` ATTRIBUTE through the core parser, refusing a `custom` item the way
79
- * every menu-bearing surface here does: markup is parsed by the browser, so a throw from
80
- * `connectedCallback` would reach nobody and the menu would be silently empty.
81
- */
82
- private _parseMenuAttr;
83
90
  }
@@ -1,16 +1,24 @@
1
- import type { AdwMenuActions, AdwMenuInput, AdwMenuModel } from '@gjsify/adwaita-core';
1
+ import type { AdwMenuActions, AdwMenuModel } from '@gjsify/adwaita-core';
2
2
  import './gtk-popover-menu.js';
3
3
  import { WidgetElement } from '../widget-element.js';
4
4
  export declare class GtkPopoverMenuBar extends WidgetElement {
5
- private _model;
5
+ /** The model, its tree snapshot and its `items-changed` subscription (ADR 0128). */
6
+ private readonly _slot;
6
7
  private _actions;
7
8
  private _items;
8
9
  private _active;
9
10
  private _initialized;
10
11
  static get observedAttributes(): string[];
11
- /** The menu model, normalised (ADR 0042). */
12
- get menuModel(): AdwMenuModel;
13
- set menuModel(value: AdwMenuInput);
12
+ /** The menu this bar draws, by identity, or `null` (ADR 0128). */
13
+ get menuModel(): AdwMenuModel | null;
14
+ set menuModel(value: AdwMenuModel | string | null);
15
+ /**
16
+ * The one place a changed menu is answered — from either door AND from a mutation of
17
+ * the menu the bar is holding, which is why the `console.warn` below is here rather
18
+ * than beside the attribute parse it used to live in: a toplevel entry that is not a
19
+ * submenu can arrive by `append_item` just as easily as by markup.
20
+ */
21
+ private _menuChanged;
14
22
  /** What the action group publishes about the actions these menus name (ADR 0042). */
15
23
  get actions(): AdwMenuActions | null;
16
24
  set actions(value: AdwMenuActions | null);
@@ -51,8 +59,8 @@ export declare class GtkPopoverMenuBar extends WidgetElement {
51
59
  /**
52
60
  * One `menubar > item > popover` (the node tree in gtkpopovermenubar.c:38-46).
53
61
  *
54
- * A toplevel node that is not a submenu is refused by `_parseMenuAttr` with GTK's own
55
- * wording rather than drawn: `tracker_insert` builds nothing for it
62
+ * A toplevel node that is not a submenu is WARNED about by {@link _menuChanged} with
63
+ * GTK's own wording rather than drawn: `tracker_insert` builds nothing for it
56
64
  * (gtkpopovermenubar.c:466), and a row that opens nothing is the same lie in another
57
65
  * shape.
58
66
  */
@@ -63,5 +71,4 @@ export declare class GtkPopoverMenuBar extends WidgetElement {
63
71
  * row is not the row that opened a submenu (gtkpopovermenu.c:257-259).
64
72
  */
65
73
  private _activate;
66
- private _parseMenuAttr;
67
74
  }
@@ -1,27 +1,26 @@
1
- import type { AdwMenuActions, AdwMenuInput, AdwMenuModel } from '@gjsify/adwaita-core';
1
+ import type { AdwMenuActions, AdwMenuModel } from '@gjsify/adwaita-core';
2
2
  import { GtkPopover } from './gtk-popover.js';
3
3
  export declare class GtkPopoverMenu extends GtkPopover {
4
- private _model;
4
+ /** The model, its tree snapshot and its `items-changed` subscription (ADR 0128). */
5
+ private readonly _slot;
5
6
  private _actions;
6
7
  private _menuView;
7
8
  /** Named apart from `GtkPopover`'s own: two `private` fields of one name are two fields. */
8
9
  private _menuInitialized;
9
10
  static get observedAttributes(): string[];
10
- /** The menu, normalised (ADR 0042). */
11
- get menuModel(): AdwMenuModel;
11
+ /** The menu this popover draws, by identity, or `null` (ADR 0128). */
12
+ get menuModel(): AdwMenuModel | null;
12
13
  /**
13
- * Set the menu from anything the portable model accepts, refusing a `custom` item
14
- * HERE — at the assignment, where it is a call the caller can catch. An ATTRIBUTE is
15
- * markup the browser parses, so the attribute path refuses the whole menu instead and
16
- * says why on `console.error`, exactly as `<gtk-menu-button>` does.
14
+ * Take a real model, a JSON string in the portable spelling, or `null`, refusing a
15
+ * `custom` item HERE — at the assignment, where it is a call the caller can catch.
16
+ * An ATTRIBUTE is markup the browser parses, so that path refuses the whole menu and
17
+ * says why on `console.error`; {@link MenuSlot} owns both doors.
17
18
  *
18
- * `AdwMenuModel` is in the union because a menu bar hands over a submenu's own `items`
19
- * untouched, and `normalizeMenuModel` is documented to be IDEMPOTENT over an
20
- * already-normalised node — that is what lets a renderer hand a model it is holding
21
- * straight back. The one cast is where the core's parameter type (`AdwMenuInput`, whose
22
- * entries are the AUTHORED shapes) is narrower than the behaviour it documents.
19
+ * A REAL MODEL IS WHAT A MENU BAR HANDS OVER for one of its submenus, and the model
20
+ * it hands over is `buildMenu(node.items)` — a menu of its own rather than this
21
+ * element's authored one, so the two cannot overwrite each other.
23
22
  */
24
- set menuModel(value: AdwMenuInput | AdwMenuModel);
23
+ set menuModel(value: AdwMenuModel | string | null);
25
24
  /** What the action group publishes about the actions this menu names (ADR 0042). */
26
25
  get actions(): AdwMenuActions | null;
27
26
  set actions(value: AdwMenuActions | null);
@@ -81,5 +80,4 @@ export declare class GtkPopoverMenu extends GtkPopover {
81
80
  */
82
81
  private _activate;
83
82
  private _renderMenu;
84
- private _parseMenuAttr;
85
83
  }
@@ -0,0 +1,49 @@
1
+ import type { AdwMenuTree, GioMenuModel } from '@gjsify/adwaita-core';
2
+ export declare class MenuSlot {
3
+ private readonly onItemsChanged;
4
+ /**
5
+ * The menu this element OWNS, so an edit to the same `menu-model="[…]"` attribute is a
6
+ * content change rather than a new menu — the popover already open follows it instead of
7
+ * being rebuilt (ADR 0128 § 5, the same rule `GjsifyOptionList` has for a list).
8
+ */
9
+ private readonly _authored;
10
+ private _model;
11
+ private _tree;
12
+ private _unwatch;
13
+ /** @param onItemsChanged what to do when the HELD menu mutates under the element. */
14
+ constructor(onItemsChanged: () => void);
15
+ /** The model itself, by identity — what `menuModel` answers and what a child is handed. */
16
+ get model(): GioMenuModel | null;
17
+ /** The snapshot every walker reads. Re-taken on each `items-changed`, never aliased. */
18
+ get tree(): AdwMenuTree;
19
+ /**
20
+ * Whether there is anything to show — NOT whether there is a menu.
21
+ *
22
+ * `null` and an empty `Gio.Menu` are different menus and GTK treats them differently: an
23
+ * empty non-null model still builds a `GtkPopoverMenu` and still dissociates an
24
+ * application-set popover (measured, `MENU_MODEL_DOOR_VECTORS`). This answers the
25
+ * question the ROWS ask — "is there a row to draw" — and the elements ask
26
+ * `model === null` where GTK asks about the model.
27
+ */
28
+ get isEmpty(): boolean;
29
+ /**
30
+ * A PROPERTY write: a real model by identity, a JSON string into the authored menu,
31
+ * `null` for no menu — and a `TypeError` for an array, which this door no longer takes
32
+ * (`buildMenu` is how an array becomes a model now).
33
+ *
34
+ * A REFUSAL PUTS THE AUTHORED MENU BACK, because the door has already reconciled it by
35
+ * the time the tree can be refused: without the restore a refused string would leave the
36
+ * element holding a menu it threw over, and the next render would draw it.
37
+ */
38
+ write(value: unknown): void;
39
+ /**
40
+ * The `menu-model` ATTRIBUTE, through the same door. A `null` attribute is no menu.
41
+ *
42
+ * A REFUSAL AND A TYPO ARE STILL DIFFERENT THINGS: `parseMenuModel` is total because
43
+ * malformed JSON is an author slip that must not stop the element upgrading, while a
44
+ * `custom` item is well-formed, deliberate, and unhonourable on this surface.
45
+ */
46
+ writeMarkup(value: string | null): void;
47
+ /** Hold `next`, snapshot it, and listen to it — the three that always happen together. */
48
+ private _adopt;
49
+ }
@@ -1,4 +1,4 @@
1
- import type { AdwMenuActions, AdwMenuModel, AdwMenuPath } from '@gjsify/adwaita-core';
1
+ import type { AdwMenuActions, AdwMenuPath, AdwMenuTree } from '@gjsify/adwaita-core';
2
2
  import type { GtkPopover } from './gtk-popover.js';
3
3
  /**
4
4
  * Called when the reader chooses an item, with the path that addresses it in the WHOLE
@@ -19,7 +19,7 @@ export declare class PopoverMenuView {
19
19
  /** Class prefix for the element's own hooks — `adw-menu-button`, `adw-split-button-menu`. */
20
20
  private readonly prefix;
21
21
  private readonly onActivate;
22
- private _model;
22
+ private _tree;
23
23
  private _actions;
24
24
  private _title;
25
25
  /** The submenu whose items are showing; `[]` is the root page. */
@@ -28,7 +28,7 @@ export declare class PopoverMenuView {
28
28
  private _openers;
29
29
  /** The row that LEAVES a page — what ArrowLeft acts on. */
30
30
  private _backRow;
31
- /** The canonical text of the model showing, so an unchanged re-render keeps the page. */
31
+ /** The canonical text of the tree showing, so an unchanged re-render keeps the page. */
32
32
  private _key;
33
33
  private _rows;
34
34
  constructor(surface: GtkPopover,
@@ -90,13 +90,17 @@ export declare class PopoverMenuView {
90
90
  */
91
91
  private focusPage;
92
92
  /**
93
- * Named for the value it takes, and NOT `setModel` — the portable LIST model gave
93
+ * Named for the value it takes, which since ADR 0128 is a TREE and not a model: this
94
+ * view never holds a `Gio.MenuModel`, it walks the snapshot its element took, and a
95
+ * `setMenuModel` here would name a type it has no way to read. The element is the one
96
+ * place that owns the model.
97
+ *
98
+ * It was `setMenuModel` for the reason the rename keeps: the portable LIST model gave
94
99
  * `ComboState` a `setModel` (ADR 0046), and two methods called `setModel` over two
95
100
  * unrelated values on one surface is the collision the vocabulary work exists to
96
- * remove. It also un-blinds `check-adwaita-collection-reactivity`, which resolves a
97
- * collection call by the setter's name.
101
+ * remove.
98
102
  */
99
- setMenuModel(model: AdwMenuModel): void;
103
+ setMenuTree(tree: AdwMenuTree): void;
100
104
  setActions(actions: AdwMenuActions | null): void;
101
105
  setTitle(title: string): void;
102
106
  /** Return to the root page — called when the popover closes. */
@@ -32,6 +32,7 @@ export { AdwBottomSheetBottomBar, AdwBottomSheetContent, AdwBottomSheetSheet } f
32
32
  export { AdwAlertResponse } from './elements/adw-alert-dialog.js';
33
33
  export type { GtkPopoverAlign, GtkPopoverPosition, GtkPopoverRole } from './elements/gtk-popover.js';
34
34
  export type { AdwViewStackPageInfo } from './elements/adw-view-stack.js';
35
- export type { AdwMenuActions, AdwMenuEntryInput, AdwMenuInput, AdwMenuItem, AdwMenuModel, AdwMenuNode, AdwMenuPath, AdwMenuSection, AdwMenuSubmenu, } from '@gjsify/adwaita-core';
35
+ export { buildMenu, menuTree, watchMenuModel } from '@gjsify/adwaita-core';
36
+ export type { AdwMenuActions, AdwMenuEntryInput, AdwMenuInput, AdwMenuItem, AdwMenuModel, AdwMenuNode, AdwMenuPath, AdwMenuSection, AdwMenuSubmenu, AdwMenuTree, } from '@gjsify/adwaita-core';
36
37
  export type { GtkDropDownOption } from './elements/gtk-drop-down.js';
37
38
  export type { AdwDialogPresentationMode } from './elements/adw-dialog.js';
@@ -1,5 +1,5 @@
1
1
  import '../file-chooser-host.js';
2
- export { GioMenu as Menu, GioMenuItem as MenuItem } from '@gjsify/adwaita-core';
2
+ export { GioMenu as Menu, GioMenuItem as MenuItem, GioMenuModel as MenuModel, GioMenuAttributeIter as MenuAttributeIter, GioMenuLinkIter as MenuLinkIter, MENU_ATTRIBUTE_ACTION, MENU_ATTRIBUTE_ACTION_NAMESPACE, MENU_ATTRIBUTE_ICON, MENU_ATTRIBUTE_LABEL, MENU_ATTRIBUTE_TARGET, MENU_EXPORTER_MAX_SECTION_SIZE, MENU_LINK_SECTION, MENU_LINK_SUBMENU, } from '@gjsify/adwaita-core';
3
3
  export { ListModel } from '@gjsify/adwaita-core';
4
4
  export { GioFile as File, GioListStore as ListStore, GioOutputStream as OutputStream, GioFileOutputStream as FileOutputStream, FileCreateFlags, IOErrorEnum, promisify as _promisify, } from '@gjsify/adwaita-core';
5
5
  export { SimpleAction, SimpleActionGroup, ApplicationFlags } from '@gjsify/adwaita-core';
@@ -9,7 +9,13 @@ export declare class WidgetElement extends HTMLElement {
9
9
  get_visible(): boolean;
10
10
  /** `gtk_widget_set_visible`. */
11
11
  set_visible(visible: boolean): void;
12
- /** `gtk_widget_get_first_child`: the first child element, `null` for a leaf. */
12
+ /**
13
+ * `gtk_widget_get_first_child`: the first child WIDGET, `null` for a leaf.
14
+ *
15
+ * The five walkers skip the elements a port builds for its own insides — `isWidgetNode`
16
+ * in `@gjsify/adwaita-core` carries the rule and the incident behind it, and the
17
+ * registered-class copy in `widgetTreeMembers()` answers from the same function.
18
+ */
13
19
  get_first_child(): Element | null;
14
20
  /** `gtk_widget_get_last_child`. */
15
21
  get_last_child(): Element | null;
@@ -17,7 +23,7 @@ export declare class WidgetElement extends HTMLElement {
17
23
  get_next_sibling(): Element | null;
18
24
  /** `gtk_widget_get_prev_sibling`. */
19
25
  get_prev_sibling(): Element | null;
20
- /** `gtk_widget_get_parent`. */
26
+ /** `gtk_widget_get_parent`: the nearest ancestor that is a widget, not a port's own wrapper. */
21
27
  get_parent(): Element | null;
22
28
  /** `gtk_widget_get_width`: the layout box in CSS px; 0 until the widget is rendered. */
23
29
  get_width(): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/adwaita-web",
3
- "version": "0.64.13-next.283",
3
+ "version": "0.64.14",
4
4
  "description": "Adwaita/Libadwaita web components for browser targets",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -58,14 +58,14 @@
58
58
  "@codemirror/language": "^6.12.4",
59
59
  "@codemirror/state": "^6.7.0",
60
60
  "@codemirror/view": "^6.43.4",
61
- "@gjsify/adwaita-core": "0.64.13-next.283",
62
- "@gjsify/adwaita-fonts": "0.64.13-next.283",
63
- "@gjsify/adwaita-icons": "0.64.13-next.283",
64
- "@gjsify/gtksource-core": "0.64.13-next.283",
61
+ "@gjsify/adwaita-core": "^0.64.14",
62
+ "@gjsify/adwaita-fonts": "^0.64.14",
63
+ "@gjsify/adwaita-icons": "^0.64.14",
64
+ "@gjsify/gtksource-core": "^0.64.14",
65
65
  "@lezer/highlight": "^1.2.3"
66
66
  },
67
67
  "peerDependencies": {
68
- "@gjsify/app-settings": "0.64.13-next.283"
68
+ "@gjsify/app-settings": "^0.64.14"
69
69
  },
70
70
  "peerDependenciesMeta": {
71
71
  "@gjsify/app-settings": {
@@ -73,11 +73,11 @@
73
73
  }
74
74
  },
75
75
  "devDependencies": {
76
- "@gjsify/app-settings": "0.64.13-next.283",
77
- "@gjsify/blueprint": "0.64.13-next.283",
78
- "@gjsify/cli": "0.64.13-next.283",
79
- "@gjsify/unit": "0.64.13-next.283",
80
- "@gjsify/vite-plugin-blueprint": "0.64.13-next.283",
76
+ "@gjsify/app-settings": "^0.64.14",
77
+ "@gjsify/blueprint": "^0.64.14",
78
+ "@gjsify/cli": "^0.64.14",
79
+ "@gjsify/unit": "^0.64.14",
80
+ "@gjsify/vite-plugin-blueprint": "^0.64.14",
81
81
  "sass": "^1.101.0",
82
82
  "typescript": "^6.0.3"
83
83
  },
@@ -114,6 +114,5 @@
114
114
  "bugs": {
115
115
  "url": "https://github.com/gjsify/gjsify/issues"
116
116
  },
117
- "homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/adwaita-web#readme",
118
- "gitHead": "05fae97125a374e84bf57c2d2d086fb53523f205"
117
+ "homepage": "https://github.com/gjsify/gjsify/tree/main/packages/web/adwaita-web#readme"
119
118
  }
@@ -8,6 +8,7 @@
8
8
  // <adw-window>'s spec and only the delta is asserted here.
9
9
  import { describe, it, expect } from '@gjsify/unit';
10
10
  import { driveWindowVectors, WINDOW_VECTORS } from '@gjsify/adwaita-core/conformance';
11
+ import { menuTree } from '@gjsify/adwaita-core';
11
12
 
12
13
  import { webWindowSubject } from './gtk-window.spec.js';
13
14
 
@@ -114,12 +115,14 @@ export const AdwApplicationWindowTest = async () => {
114
115
 
115
116
  await it('a broken model is dropped rather than thrown', async () => {
116
117
  // `parseMenuModel` is total by construction: a typo in markup must not take
117
- // the window down with it.
118
+ // the window down with it. Since ADR 0128 the window still HOLDS its authored
119
+ // menu — the typo empties it rather than removing it, and `null` is the only
120
+ // spelling of "no menu".
118
121
  const win = document.createElement('adw-application-window') as AdwApplicationWindow;
119
122
  win.setAttribute('menu-model', '{not json');
120
123
  win.setAttribute('show-menubar', '');
121
124
  document.body.appendChild(win);
122
- expect(win.menuModel).toStrictEqual([]);
125
+ expect(menuTree(win.menuModel!)).toStrictEqual([]);
123
126
  expect(win.menubar?.hidden).toBe(true);
124
127
  win.remove();
125
128
  });