@gjsify/adwaita-core 0.47.0 → 0.49.0

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 (37) hide show
  1. package/lib/esm/adjustment.js +1 -0
  2. package/lib/esm/avatar.js +1 -1
  3. package/lib/esm/conformance/adjustment.js +1 -0
  4. package/lib/esm/conformance/avatar.js +1 -1
  5. package/lib/esm/conformance/index.js +1 -1
  6. package/lib/esm/conformance/list.js +1 -0
  7. package/lib/esm/conformance/menu.js +1 -0
  8. package/lib/esm/conformance/rows.js +1 -1
  9. package/lib/esm/conformance/shared-trees.js +1 -0
  10. package/lib/esm/conformance/split-button.js +1 -1
  11. package/lib/esm/index.js +1 -1
  12. package/lib/esm/list.js +1 -0
  13. package/lib/esm/menu.js +2 -0
  14. package/lib/esm/rows.js +1 -1
  15. package/lib/esm/split-button.js +1 -1
  16. package/lib/esm/tab-view.js +1 -1
  17. package/lib/types/adjustment.d.ts +198 -0
  18. package/lib/types/adjustment.spec.d.ts +2 -0
  19. package/lib/types/avatar.d.ts +15 -0
  20. package/lib/types/conformance/adjustment.d.ts +33 -0
  21. package/lib/types/conformance/avatar.d.ts +16 -0
  22. package/lib/types/conformance/index.d.ts +12 -4
  23. package/lib/types/conformance/list.d.ts +171 -0
  24. package/lib/types/conformance/menu.d.ts +118 -0
  25. package/lib/types/conformance/rows.d.ts +3 -3
  26. package/lib/types/conformance/shared-trees.d.ts +93 -0
  27. package/lib/types/conformance/split-button.d.ts +9 -19
  28. package/lib/types/index.d.ts +12 -6
  29. package/lib/types/list.d.ts +109 -0
  30. package/lib/types/list.spec.d.ts +2 -0
  31. package/lib/types/menu.d.ts +366 -0
  32. package/lib/types/menu.spec.d.ts +2 -0
  33. package/lib/types/rows.d.ts +78 -109
  34. package/lib/types/shared-trees.spec.d.ts +2 -0
  35. package/lib/types/split-button.d.ts +21 -32
  36. package/lib/types/tab-view.d.ts +35 -2
  37. package/package.json +3 -3
@@ -0,0 +1,366 @@
1
+ /**
2
+ * The values `hidden-when` is documented to take.
3
+ *
4
+ * A union rather than `string` because each one has its OWN visibility rule in
5
+ * {@link resolveMenuItemState}; an unrecognised value is ignored by C on purpose ("this
6
+ * code may be running in context of a desktop shell … and should not spew criticals"),
7
+ * which {@link normalizeMenuModel} reproduces by dropping it.
8
+ */
9
+ export type AdwMenuHiddenWhen = 'action-disabled' | 'action-missing' | 'macos-menubar';
10
+ /** A section's `display-hint`, which asks for a row of buttons instead of a list. */
11
+ export type AdwMenuDisplayHint = 'horizontal-buttons' | 'circular-buttons' | 'inline-buttons';
12
+ /** A section's `text-direction`, read only when {@link AdwMenuDisplayHint} is horizontal. */
13
+ export type AdwMenuTextDirection = 'ltr' | 'rtl' | 'none';
14
+ /**
15
+ * One menu item — the attributes `GtkPopoverMenu` documents, plus `id`.
16
+ *
17
+ * `id` is OURS and has no meaning to GTK. It survives the round trip anyway, because a
18
+ * `GMenuModel`'s attribute space is open (`g_menu_item_set_attribute` takes any name and
19
+ * GTK ignores what it does not know), so carrying it is not a divergence — it is an
20
+ * unread attribute, exactly like an application's own. It exists because the two
21
+ * renderers' activation events already reported one, falling back to the label.
22
+ */
23
+ export interface AdwMenuItem {
24
+ readonly kind: 'item';
25
+ /** The user-visible string. `''` for an item that has none — never `undefined`. */
26
+ readonly label: string;
27
+ /**
28
+ * The DETAILED action name — `app.save-as`, `app.view::list`, `win.zoom(2)`.
29
+ *
30
+ * One string rather than an action plus a target, because that is what
31
+ * `g_menu_item_set_detailed_action` takes and what `g_action_parse_detailed_name`
32
+ * can read back for every `GVariant` target type. {@link parseDetailedAction} splits
33
+ * it where a surface needs the halves.
34
+ */
35
+ readonly action?: string;
36
+ /** Symbolic icon name shown beside the label. */
37
+ readonly icon?: string;
38
+ /** Icon for the button form a `display-hint` section draws instead of a row. */
39
+ readonly verbIcon?: string;
40
+ /** Accelerator text, e.g. `<Control>s`. Display only — it binds nothing. */
41
+ readonly accel?: string;
42
+ /** When this item disappears rather than merely dimming. */
43
+ readonly hiddenWhen?: AdwMenuHiddenWhen;
44
+ /** Names an application widget the surface must host in place of a row. */
45
+ readonly custom?: string;
46
+ /** Whether {@link label} is Pango markup. */
47
+ readonly useMarkup?: boolean;
48
+ /** Stable identifier for activation reporting; defaults to {@link label}. */
49
+ readonly id?: string;
50
+ }
51
+ /**
52
+ * A `section` link: items drawn inline, separated from their neighbours.
53
+ *
54
+ * A section is NOT a container the user opens — it is a visual grouping, which is why a
55
+ * flat surface may inline it ({@link flattenMenu}) without losing an item.
56
+ */
57
+ export interface AdwMenuSection {
58
+ readonly kind: 'section';
59
+ /** Optional heading above the group. */
60
+ readonly label?: string;
61
+ readonly displayHint?: AdwMenuDisplayHint;
62
+ readonly textDirection?: AdwMenuTextDirection;
63
+ readonly items: readonly AdwMenuNode[];
64
+ }
65
+ /**
66
+ * A `submenu` link: a nested menu the user opens.
67
+ *
68
+ * `submenuAction` is documented under ITEMS (`gtkpopovermenu.c:106-107`) rather than
69
+ * under submenus, and it is here anyway: it names the action that tracks whether the
70
+ * submenu is OPEN, so the only item it can sit on is the one carrying the link — which
71
+ * in this model IS this node. `gtkmenutrackeritem.c:822` and `:1049` read it, and both
72
+ * paths are submenu paths.
73
+ */
74
+ export interface AdwMenuSubmenu {
75
+ readonly kind: 'submenu';
76
+ readonly label: string;
77
+ readonly icon?: string;
78
+ /** Action tracking whether this submenu is shown (`submenu-action`). */
79
+ readonly submenuAction?: string;
80
+ /**
81
+ * `gtk-macos-special` — macOS menu-bar meaning, ignored everywhere else
82
+ * (`gtkpopovermenu.c:130-131`, read at `gtkmenutrackeritem.c:761`).
83
+ *
84
+ * Carried and never acted on here: no renderer in this workspace exports a macOS
85
+ * menu bar. It is in the model so that a menu written for one survives the trip,
86
+ * which is the whole lossless claim.
87
+ */
88
+ readonly macosSpecial?: string;
89
+ readonly items: readonly AdwMenuNode[];
90
+ }
91
+ /** Any node of a normalised model. */
92
+ export type AdwMenuNode = AdwMenuItem | AdwMenuSection | AdwMenuSubmenu;
93
+ /**
94
+ * A normalised menu: the value a renderer walks and the GTK host converts.
95
+ *
96
+ * `readonly` throughout, because every renderer holds one across a render and a shared
97
+ * mutable tree is how two widgets showing the same menu would diverge.
98
+ */
99
+ export type AdwMenuModel = readonly AdwMenuNode[];
100
+ /** An item as authored — `kind` is implied by the absence of a link. */
101
+ export interface AdwMenuItemInput {
102
+ label?: unknown;
103
+ action?: unknown;
104
+ icon?: unknown;
105
+ verbIcon?: unknown;
106
+ accel?: unknown;
107
+ hiddenWhen?: unknown;
108
+ custom?: unknown;
109
+ useMarkup?: unknown;
110
+ id?: unknown;
111
+ }
112
+ /** A section as authored — the `section` key IS the link. */
113
+ export interface AdwMenuSectionInput {
114
+ section: readonly AdwMenuEntryInput[];
115
+ label?: unknown;
116
+ displayHint?: unknown;
117
+ textDirection?: unknown;
118
+ }
119
+ /** A submenu as authored — the `submenu` key IS the link. */
120
+ export interface AdwMenuSubmenuInput {
121
+ submenu: readonly AdwMenuEntryInput[];
122
+ label?: unknown;
123
+ icon?: unknown;
124
+ submenuAction?: unknown;
125
+ macosSpecial?: unknown;
126
+ }
127
+ /**
128
+ * One authored entry.
129
+ *
130
+ * A BARE STRING is a label-only item, and that form is not a NativeScript leftover: it
131
+ * is the model's own shorthand, the same one {@link normalizeComboOptions} gives an
132
+ * option list, and it is now accepted on every surface rather than on one. What it
133
+ * cannot carry — an action, an icon, a section — is exactly what made
134
+ * `button.menu = ['Save as…']` unable to grow, so the shorthand widens INTO this union
135
+ * instead of standing beside it.
136
+ */
137
+ export type AdwMenuEntryInput = string | AdwMenuItemInput | AdwMenuSectionInput | AdwMenuSubmenuInput;
138
+ /** A menu as authored, before {@link normalizeMenuModel} has seen it. */
139
+ export type AdwMenuInput = readonly AdwMenuEntryInput[];
140
+ /**
141
+ * Authored entries → the normalised model every renderer walks.
142
+ *
143
+ * TOTAL BY CONSTRUCTION. The input reaches this function from author-written markup (a
144
+ * JSON attribute, an XML attribute, a JSX expression), and a typo must not take the
145
+ * widget down with it — the same rule the `menu` attribute parser it replaces already
146
+ * had. So an unusable entry is DROPPED and an unusable attribute is omitted; nothing
147
+ * here throws. The loud half is {@link assertMenuRenderable}, which runs against a
148
+ * SURFACE and can therefore say something true about what will not be drawn.
149
+ *
150
+ * A link wins over the attributes beside it: `{ label: 'More', submenu: [...] }` is a
151
+ * submenu with a label, never an item that happens to carry a list, because that is how
152
+ * `g_menu_item_get_link` reads the same object.
153
+ */
154
+ export declare function normalizeMenuModel(input: AdwMenuInput | null | undefined): AdwMenuModel;
155
+ /**
156
+ * A `menu` ATTRIBUTE — a JSON array — as a model.
157
+ *
158
+ * Total for the same reason {@link normalizeMenuModel} is, and it adds the one failure
159
+ * markup can produce that a JS value cannot: `JSON.parse` genuinely throws on a typo,
160
+ * and the widget must degrade to "no menu" rather than fail to upgrade.
161
+ */
162
+ export declare function parseMenuModel(json: string | null | undefined): AdwMenuModel;
163
+ /**
164
+ * Where a node sits: the index chain from the model's root.
165
+ *
166
+ * A `GMenuModel` addresses its items BY POSITION and a link is a model of its own, so a
167
+ * position inside a submenu is only meaningful together with the positions above it.
168
+ * Resolving a choice by LABEL instead silently dispatches the first of two identically
169
+ * named entries — the defect the split button's activation vectors already pin down, one
170
+ * level deeper now that a menu can nest.
171
+ */
172
+ export type AdwMenuPath = readonly number[];
173
+ /** The node at a path, or `null` when the path names none. */
174
+ export declare function menuNodeAt(model: AdwMenuModel, path: AdwMenuPath): AdwMenuNode | null;
175
+ /** The item at a path — `null` for a path that names a section, a submenu or nothing. */
176
+ export declare function menuItemAt(model: AdwMenuModel, path: AdwMenuPath): AdwMenuItem | null;
177
+ /** One row of a {@link flattenMenu} walk. */
178
+ export interface AdwMenuFlatRow {
179
+ /** The node this row draws. Sections never appear — they become {@link separated}. */
180
+ readonly node: AdwMenuItem | AdwMenuSubmenu;
181
+ /**
182
+ * Where it lives, for {@link menuItemAt}.
183
+ *
184
+ * It is also the depth: `path.length` counts the links walked to reach the row.
185
+ * There is no separate `depth` field, because nothing would indent by it — GTK
186
+ * draws a section flush with its neighbours — and a field only its own vectors read
187
+ * is a claim nothing holds.
188
+ */
189
+ readonly path: AdwMenuPath;
190
+ /** Whether a separator belongs above this row — a section boundary. */
191
+ readonly separated: boolean;
192
+ }
193
+ /**
194
+ * ONE LEVEL of a model as the ordered rows a list draws.
195
+ *
196
+ * SECTIONS ARE INLINED, SUBMENUS ARE NOT, and that asymmetry is `GMenuModel`'s own: a
197
+ * section is a visual grouping that `GtkPopoverMenu` itself draws inline with a
198
+ * separator, so inlining it loses a rule and no item; a submenu is a menu the user
199
+ * OPENS, so inlining it would put its items where they were never offered. A submenu
200
+ * row therefore stays a row, and a surface shows its contents by flattening THAT node's
201
+ * `items` when the user asks — a second popover page on the web, a second sheet on
202
+ * NativeScript. {@link AdwMenuFlatRow.path} is what makes the second call addressable.
203
+ *
204
+ * `separated` marks the FIRST row after a boundary and is never set on the first row of
205
+ * all, because a separator above nothing is a line at the top of the menu.
206
+ */
207
+ export declare function flattenMenu(model: AdwMenuModel, path?: AdwMenuPath): readonly AdwMenuFlatRow[];
208
+ /** What a `GActionGroup` publishes about one action, in portable form. */
209
+ export interface AdwMenuAction {
210
+ /** `g_action_group_get_action_enabled`. Absent counts as enabled. */
211
+ readonly enabled?: boolean;
212
+ /**
213
+ * `g_action_group_get_action_state`, as its CONTENT in text: `true`/`false` for a
214
+ * toggle, `list` for a radio whose targets are strings.
215
+ *
216
+ * NOT the `GVariant` TEXT form, which would quote the string —
217
+ * `GLib.Variant.new_string('list').print(false)` is `'list'`, with the quotes. A
218
+ * consumer bridging a real `GActionGroup` writes `state.get_string()[0]` for a
219
+ * string and `String(state.get_boolean())` for a boolean; reading `print(false)`
220
+ * instead is what makes every radio row render OFF, because the target it is
221
+ * compared against ({@link AdwDetailedAction.target}) is unquoted too.
222
+ */
223
+ readonly state?: string;
224
+ }
225
+ /**
226
+ * The actions a surface knows about — the portable stand-in for a `GActionGroup`.
227
+ *
228
+ * Keyed by the action's own name, prefix included (`app.save-as`), which is the key a
229
+ * detailed action name yields. An action ABSENT from the map is missing, not disabled,
230
+ * and the two differ: `hidden-when="action-missing"` hides one and shows the other.
231
+ *
232
+ * NO MAP AT ALL IS A THIRD THING, and the distinction is load-bearing rather than
233
+ * pedantic: `{}` is a group that knows no actions, so every item in it is insensitive;
234
+ * `undefined` is a surface with no action group to consult, so nothing is known and
235
+ * nothing is dimmed. MEASURED — collapsing the two made every actioned item in the
236
+ * browser renderer arrive `disabled`, because the common case is a menu whose actions
237
+ * the host dispatches itself and never declares.
238
+ */
239
+ export type AdwMenuActions = Readonly<Record<string, AdwMenuAction>>;
240
+ /** `GtkMenuTrackerItemRole`. */
241
+ export type AdwMenuItemRole = 'normal' | 'check' | 'radio';
242
+ /** What an item's action makes of it. */
243
+ export interface AdwMenuItemState {
244
+ /** `GTK_STATE_FLAG_INSENSITIVE`'s inverse — the action's `enabled`. */
245
+ readonly sensitive: boolean;
246
+ /** Whether the check/radio decoration is drawn. */
247
+ readonly toggled: boolean;
248
+ readonly role: AdwMenuItemRole;
249
+ /** Whether the item is drawn at all — `hidden-when`, folded. */
250
+ readonly visible: boolean;
251
+ }
252
+ /** An action name and the target text beside it. */
253
+ export interface AdwDetailedAction {
254
+ readonly name: string;
255
+ /**
256
+ * The target's CONTENT — `list` for `app.view::list` AND for `app.view('list')`,
257
+ * `2` for `win.zoom(2)`.
258
+ *
259
+ * Never the `GVariant` TEXT, which would quote a string: the two spellings of the
260
+ * same target must compare equal, or a radio written one way and an action state
261
+ * read the other way disagree. MEASURED: without the unquoting,
262
+ * `parseDetailedAction("app.v('list')")` answered `'list'` WITH quotes and every
263
+ * such radio row rendered OFF against a state of `list`.
264
+ */
265
+ readonly target?: string;
266
+ }
267
+ /**
268
+ * `app.view::list` → `{ name: 'app.view', target: 'list' }`.
269
+ *
270
+ * The two forms `g_action_parse_detailed_name` accepts, and no third: `::` for a string
271
+ * target and `(…)` for a `GVariant` text one. A quoted `(…)` target is UNQUOTED, so
272
+ * `app.v::list` and `app.v('list')` — the same detailed action to GLib — yield the same
273
+ * target here; without that they did not, and every radio written the second way read
274
+ * OFF.
275
+ *
276
+ * WHAT THIS DELIBERATELY DOES NOT DO is parse the `GVariant`. The target is the text
277
+ * between the delimiters with its quotes removed, so comparing it against an action's
278
+ * `state` is a STRING comparison. That is exact for the string and boolean targets a
279
+ * menu actually uses, and it does not canonicalise WHITESPACE: `win.zoom(2)` and
280
+ * `win.zoom( 2 )` name the same action to GLib and different targets here. A portable
281
+ * model with no `GVariant` implementation cannot close that, and pretending otherwise
282
+ * would be a wrong answer instead of a stated one.
283
+ *
284
+ * IT IS ALSO NOT A VALIDATOR. It never throws and answers something for every string,
285
+ * because it runs on the display side of a menu that has already been accepted. GIO is
286
+ * the authority on whether a detailed action name is legal, and `@gjsify/gtk-host`'s
287
+ * `buildGioMenu` asks it — see {@link AdwMenuItem.action}.
288
+ */
289
+ export declare function parseDetailedAction(detailed: string | undefined): AdwDetailedAction | null;
290
+ /**
291
+ * `sensitive`, `toggled`, `role` and `visible` for one item — `gtk_menu_tracker_item_*`
292
+ * as a pure fold.
293
+ *
294
+ * This is the whole answer to "where does a menu carry enabled and checked", and the
295
+ * answer is that it does not. C reads all three off the ACTION: `sensitive` is the
296
+ * action's `enabled` flag (c:332), `role` is RADIO when the item has a target and the
297
+ * action has a state (c:336-340), CHECK when the state is a boolean and there is no
298
+ * target (c:342-346), and `toggled` compares the two. An item with no `action` attribute
299
+ * at all is SENSITIVE — C sets it explicitly in the else branch
300
+ * (`gtkmenutrackeritem.c:594`) rather than leaving the field false, because a label with
301
+ * no action is not a disabled command.
302
+ *
303
+ * TWO PLACES THIS IS NOT C, both because a portable action map carries no `GVariantType`:
304
+ *
305
+ * · `can_activate`'s TYPE check. C refuses to activate an item whose target type does
306
+ * not match the action's parameter type, and that refusal feeds
307
+ * `hidden-when="action-missing"`. Here "missing" means only that the action is
308
+ * absent from {@link AdwMenuActions} — a present action with a mistyped target reads
309
+ * as activatable where GTK would hide it.
310
+ * · A boolean state is the TEXT `'true'`/`'false'`, since the state is text. A radio
311
+ * whose targets are literally those two strings would therefore read as a check.
312
+ * GTK tells them apart by `GVariant` type; nothing here can.
313
+ *
314
+ * `macos-menubar` hides an item only in the macOS menubar export (C guards it with
315
+ * `mac_os_mode`, c:534-535), which no renderer here is, so it stays visible.
316
+ */
317
+ export declare function resolveMenuItemState(item: AdwMenuItem, actions?: AdwMenuActions): AdwMenuItemState;
318
+ /**
319
+ * What a surface can draw.
320
+ *
321
+ * ONE CAPABILITY, because one is what actually differs in a way a reader could not
322
+ * otherwise see. All three surfaces nest (GTK slides a page, the browser swaps the
323
+ * popover's page, NativeScript opens a second sheet) and all three draw every
324
+ * attribute they have a place for, so a field for those would be `true` everywhere and
325
+ * a refusal arm nothing could ever reach — the shape this repository pays most for. A
326
+ * second field lands with the first surface that needs it.
327
+ */
328
+ export interface AdwMenuSurface {
329
+ /** A name for the diagnostic — `adwaita-web`, `nativescript`. */
330
+ readonly name: string;
331
+ /** Can it host an application widget named by `custom`? Only GTK can. */
332
+ readonly custom: boolean;
333
+ }
334
+ /** The browser popover: it has no `add_child` for a `custom` item to name. */
335
+ export declare const ADW_MENU_SURFACE_WEB: AdwMenuSurface;
336
+ /** The NativeScript action sheet: its rows are STRINGS, so a `custom` widget has no home. */
337
+ export declare const ADW_MENU_SURFACE_NATIVESCRIPT: AdwMenuSurface;
338
+ /** One thing a surface will not draw, with where it is. */
339
+ export interface AdwMenuRefusal {
340
+ readonly path: AdwMenuPath;
341
+ /** The attribute that cannot be honoured. */
342
+ readonly what: 'custom';
343
+ readonly message: string;
344
+ }
345
+ /**
346
+ * Every part of a model the surface cannot render — empty when it can render all of it.
347
+ *
348
+ * IT LISTS WHAT WOULD VANISH, NOT WHAT WOULD LOOK DIFFERENT. An icon a sheet cannot show
349
+ * is a decoration and the row is still there; a `custom` item is a PLACEHOLDER for an
350
+ * application widget, so a surface that ignores it draws a blank row where a control
351
+ * belongs, and the reader has no way to tell. That is the distinction the three-way
352
+ * split in this file's header draws, and it is why refusing everything unsupported
353
+ * would be useless: NativeScript renders no icon and no accelerator on any menu, so a
354
+ * refusal per decoration would fire on every well-formed menu and be switched off.
355
+ */
356
+ export declare function menuRefusals(model: AdwMenuModel, surface: AdwMenuSurface, path?: AdwMenuPath): AdwMenuRefusal[];
357
+ /**
358
+ * Throw if the surface cannot render the model.
359
+ *
360
+ * Called by a renderer when a menu is SET, so the throw names the assignment rather than
361
+ * arriving as a missing row three interactions later. The message carries every refusal,
362
+ * not the first: a menu written for GTK typically has all of its `custom` items at once,
363
+ * and reporting them one run at a time is how a port becomes a series of identical bug
364
+ * reports.
365
+ */
366
+ export declare function assertMenuRenderable(model: AdwMenuModel, surface: AdwMenuSurface): void;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -1,3 +1,8 @@
1
+ import type { AdwComboOption, AdwListItemsChanged } from './list.js';
2
+ export { ADW_COMBO_NO_SELECTION, normalizeComboOptions } from './list.js';
3
+ export type { AdwComboOption, AdwComboOptionInput } from './list.js';
4
+ export { SpinState } from './adjustment.js';
5
+ export type { SpinStateChange, SpinStateListener, SpinStateRangeListener } from './adjustment.js';
1
6
  /** Subscriber for {@link ExpanderState} changes — receives the new expanded flag. */
2
7
  export type ExpanderStateListener = (expanded: boolean) => void;
3
8
  /**
@@ -23,44 +28,6 @@ export declare class ExpanderState {
23
28
  /** Collapse the disclosure. Returns whether it changed. */
24
29
  collapse(): boolean;
25
30
  }
26
- /** One selectable option in a {@link ComboState}. */
27
- export interface AdwComboOption {
28
- /** Display label shown in the row + chooser. */
29
- label: string;
30
- /** Underlying value returned by {@link ComboState.selectedValue}. */
31
- value: string;
32
- }
33
- /**
34
- * What an author may write for one option before {@link normalizeComboOptions}
35
- * has seen it: a bare string (value === label, the `Gtk.StringList` case), or a
36
- * partial descriptor where either half may stand in for the other.
37
- */
38
- export type AdwComboOptionInput = string | {
39
- value?: unknown;
40
- label?: unknown;
41
- };
42
- /**
43
- * Raw authored options → the stable `{ value, label }` descriptors every combo surface
44
- * works with.
45
- *
46
- * A bare string is both value and label (`Gtk.StringList`'s model, which is what
47
- * `Adw.ComboRow` is fed in the common case). In a descriptor either half stands in for the
48
- * missing other, so `{ label: 'Apple' }` is addressable by the value `'Apple'` rather than
49
- * by `undefined`. ONE home for the rule, so `<adw-combo-row>` and `<gtk-drop-down>` cannot
50
- * accept two different option vocabularies.
51
- */
52
- export declare function normalizeComboOptions(raw: ReadonlyArray<AdwComboOptionInput> | null | undefined): AdwComboOption[];
53
- /**
54
- * The "no item is selected" index — the TS mirror of `GTK_INVALID_LIST_POSITION`
55
- * (`AdwComboRow:selected`'s default and empty value).
56
- *
57
- * libadwaita spells it `G_MAXUINT` because the property is a `guint`; `-1` is the idiomatic
58
- * TS sentinel and the spelling {@link ADW_SIDEBAR_NO_SELECTION} uses for the same GTK
59
- * constant. Without it, an EMPTY model and a model whose first item has an empty label are
60
- * indistinguishable — both report index 0 with `selectedValue === ''` — so a renderer that
61
- * wants to draw a placeholder has nothing to test.
62
- */
63
- export declare const ADW_COMBO_NO_SELECTION = -1;
64
31
  /** Payload of a {@link ComboState} change. */
65
32
  export interface ComboStateChange {
66
33
  /** The (new) selected index, or {@link ADW_COMBO_NO_SELECTION}. */
@@ -72,41 +39,94 @@ export interface ComboStateChange {
72
39
  /** True for a user pick ({@link ComboState.select}); false for a programmatic set. */
73
40
  interactive: boolean;
74
41
  }
75
- /** Subscriber for {@link ComboState} changes. */
42
+ /** Subscriber for {@link ComboState} selection changes. */
76
43
  export type ComboStateListener = (change: ComboStateChange) => void;
44
+ /** Subscriber for {@link ComboState} MODEL changes — one `items-changed` per replacement. */
45
+ export type ComboItemsListener = (change: AdwListItemsChanged) => void;
77
46
  /**
78
- * The selection state of a combo row: an options list plus the selected index,
79
- * with a two-way index↔value mapping and empty/out-of-range guards. Mirrors
80
- * `Adw.ComboRow`: a programmatic set ({@link setSelectedIndex} / {@link setSelectedValue}
81
- * / {@link setOptions}) updates silently (`interactive: false` — the renderer refreshes
82
- * the inline value but re-emits no `notify::selected`), while {@link select} is the
83
- * user pick that notifies (`interactive: true`).
47
+ * The selection state of a combo row: a portable list model (`AdwListModel`) plus
48
+ * the selected index, with a two-way index↔value mapping and empty/out-of-range guards.
49
+ * Mirrors `Adw.ComboRow`: a programmatic set ({@link setSelectedIndex} /
50
+ * {@link setSelectedValue} / {@link setModel}) updates silently (`interactive: false` —
51
+ * the renderer refreshes the inline value but re-emits no `notify::selected`), while
52
+ * {@link select} is the user pick that notifies (`interactive: true`).
53
+ *
54
+ * TWO SIGNALS, because they answer different questions and a renderer needs both
55
+ * (ADR 0046). {@link subscribe} reports the SELECTION; {@link subscribeItems} reports WHERE
56
+ * the model changed, so a renderer splices its item views instead of rebuilding them.
57
+ * `Adw.ComboRow` gets both from GTK for free — the first is `notify::selected`, the second
58
+ * is `Gio.ListModel::items-changed` on the model it holds — and this class had only the
59
+ * first, which is why both browser selectors dropped every option node on every
60
+ * assignment.
84
61
  */
85
62
  export declare class ComboState {
86
63
  private _options;
87
64
  private _selected;
88
65
  private readonly _listeners;
66
+ private readonly _itemListeners;
89
67
  /** Subscribe to selection changes. Returns an unsubscribe function. */
90
68
  subscribe(listener: ComboStateListener): () => void;
69
+ /**
70
+ * Subscribe to MODEL changes — `Gio.ListModel::items-changed`, one splice per
71
+ * {@link setModel}. Returns an unsubscribe function.
72
+ *
73
+ * Fires BEFORE the selection change {@link setModel} also emits, which is the order
74
+ * that makes it usable: the selection subscriber writes the new index into the item
75
+ * views, so those views have to exist by then. The rebuild path had the inverse
76
+ * ordering hazard written down in `<adw-combo-row>` — "AFTER the rebuild, never during
77
+ * it" — and this is what removes it rather than documenting it again.
78
+ */
79
+ subscribeItems(listener: ComboItemsListener): () => void;
91
80
  private _emit;
92
81
  private _setIndex;
93
- /** The selectable options. */
94
- get options(): AdwComboOption[];
95
82
  /**
96
- * Replace the options, then notify (`interactive: false`) so the renderer re-syncs the
97
- * inline value label — which may change even at an unchanged index, because the label is
98
- * read out of the MODEL.
83
+ * The list model — `Adw.ComboRow:model` / `Gtk.DropDown:model`, in portable form.
84
+ *
85
+ * A COPY, items included, and that is load-bearing rather than defensive. The splice
86
+ * (ADR 0046) decides what changed by COMPARING the assigned model against the stored
87
+ * one, so anything that leaks a reference into `_options` makes the comparison read the
88
+ * new value on both sides and answer "nothing changed". Handing back `_options` itself
89
+ * did exactly that, and it turned a pattern that WORKED under the old full rebuild into
90
+ * a silent no-op:
91
+ *
92
+ * ```ts
93
+ * const m = row.model; m.push(item); row.model = m; // → no splice, DOM unchanged
94
+ * ```
95
+ *
96
+ * Fresh items and not just a fresh array, because the same failure sits one level down:
97
+ * `m[0].label = 'x'` through a shared descriptor mutates the stored one too, and the
98
+ * comparison is by BOTH halves. A frozen item would have made the mutation loud; a copy
99
+ * makes the round trip CORRECT, which is the difference between documenting the hazard
100
+ * and removing it.
101
+ *
102
+ * Cost, MEASURED rather than assumed (Node 24.19, `{value,label}` records): linear, about
103
+ * four nanoseconds per item — so a read is well under a microsecond at every size a combo
104
+ * row actually holds, and the assignment beside it already costs more. What the shape
105
+ * rules out is a per-item read: the three call sites that had one — `_applyItemsChanged`
106
+ * in each browser selector and `_filter` in `<gtk-drop-down>`, which runs per row per
107
+ * keystroke — hoist it out of their loop, so a read is O(n) once per operation instead of
108
+ * O(n²). Nothing reads it per frame. A model large enough for the linear term to matter is
109
+ * a `Gtk.ListView` and belongs to `ListController`'s virtualisation, which ADR 0046 § 7
110
+ * keeps out of this class on purpose.
111
+ */
112
+ get model(): AdwComboOption[];
113
+ /**
114
+ * Replace the model: emit the splice that turns the old one into the new
115
+ * ({@link subscribeItems}), then notify the SELECTION (`interactive: false`) so the
116
+ * renderer re-syncs the inline value label — which may change even at an unchanged
117
+ * index, because the label is read out of the model.
99
118
  *
100
- * Replacing the model re-runs autoselect, so an index the new one does not have falls
101
- * back to 0 — including the sentinel, which is how a row recovers a selection when its
102
- * model grows. An EMPTY model has no 0 to fall back to and lands on
103
- * {@link ADW_COMBO_NO_SELECTION}.
119
+ * Replacing the model re-runs autoselect ({@link clampListSelection}), so an index the
120
+ * new one does not have falls back to 0 — including the sentinel, which is how a row
121
+ * recovers a selection when its model grows. An EMPTY model has no 0 to fall back to
122
+ * and lands on {@link ADW_COMBO_NO_SELECTION}.
104
123
  *
105
- * The check is `hasIndex`, not `selected >= length`: the sentinel is BELOW the range, so
106
- * a one-sided comparison leaves a `-1` in place and a model that was emptied and
107
- * refilled comes back with nothing selected.
124
+ * The selection notify is UNCONDITIONAL while the splice is not: an assignment of an
125
+ * equal model emits no `items-changed` (GTK emits none either) but still re-runs the
126
+ * label sync, because that is the behaviour every renderer has depended on since this
127
+ * class existed and nothing measured says it is wrong.
108
128
  */
109
- setOptions(options: AdwComboOption[]): void;
129
+ setModel(model: AdwComboOption[]): void;
110
130
  /**
111
131
  * The selected option index, or {@link ADW_COMBO_NO_SELECTION}. May also sit PAST the
112
132
  * options length: {@link setSelectedIndex} is deliberately permissive, mirroring a
@@ -157,57 +177,6 @@ export declare class ComboState {
157
177
  /** Number of options. */
158
178
  get count(): number;
159
179
  }
160
- /** Payload of a {@link SpinState} change. */
161
- export interface SpinStateChange {
162
- /** The new value (already clamped to `[min, max]`). */
163
- value: number;
164
- /** True for a stepper press ({@link SpinState.increment}/{@link SpinState.decrement}); false for a programmatic set/re-clamp. */
165
- interactive: boolean;
166
- }
167
- /** Subscriber for {@link SpinState} changes. */
168
- export type SpinStateListener = (change: SpinStateChange) => void;
169
- /**
170
- * The numeric adjustment state of a spin row: `value` bounded by `[min, max]` and
171
- * stepped by `step`. Every mutation clamps into range. Mirrors `Adw.SpinRow`: a
172
- * programmatic {@link setValue} (or a re-clamp from a moved {@link setMin}/{@link setMax})
173
- * notifies `interactive: false` (the renderer refreshes the display but re-emits no
174
- * `notify::value`), while {@link increment}/{@link decrement} are the stepper presses
175
- * that notify `interactive: true`.
176
- */
177
- export declare class SpinState {
178
- private _value;
179
- private _min;
180
- private _max;
181
- private _step;
182
- private readonly _listeners;
183
- /** Subscribe to value changes. Returns an unsubscribe function. */
184
- subscribe(listener: SpinStateListener): () => void;
185
- private _emit;
186
- private _clamp;
187
- private _bump;
188
- /** Re-clamp the current value after a bound moved; notifies `interactive: false` on change. */
189
- private _reclamp;
190
- /** The current numeric value (always within `[min, max]`). */
191
- get value(): number;
192
- /** Lower bound. */
193
- get min(): number;
194
- /** Upper bound. */
195
- get max(): number;
196
- /** Increment/decrement step applied per stepper press. */
197
- get step(): number;
198
- /** Programmatic value set — clamp, notify `interactive: false` on change. Returns whether it changed. */
199
- setValue(value: number): boolean;
200
- /** Set the lower bound and re-clamp the value into range. Returns whether the value changed. */
201
- setMin(value: number): boolean;
202
- /** Set the upper bound and re-clamp the value into range. Returns whether the value changed. */
203
- setMax(value: number): boolean;
204
- /** Set the step. A non-positive or non-finite step falls back to `1`. */
205
- setStep(value: number): void;
206
- /** Step up by `step`, clamped to `max`. Notifies `interactive: true` on change. Returns whether it changed. */
207
- increment(): boolean;
208
- /** Step down by `step`, clamped to `min`. Notifies `interactive: true` on change. Returns whether it changed. */
209
- decrement(): boolean;
210
- }
211
180
  /** Payload of a {@link ToggleGroupState} change. */
212
181
  export interface ToggleGroupStateChange {
213
182
  /** The newly-selected segment index. */
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;