@gjsify/gtk-host 0.43.0 → 0.44.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 (40) hide show
  1. package/README.md +36 -9
  2. package/lib/esm/adapters/react.js +1 -1
  3. package/lib/esm/conformance/diagnostics.js +2 -2
  4. package/lib/esm/conformance/index.js +2 -2
  5. package/lib/esm/descriptors/adw.js +1 -1
  6. package/lib/esm/descriptors/gtk.js +1 -1
  7. package/lib/esm/host.js +1 -1
  8. package/lib/esm/probe.js +1 -1
  9. package/lib/esm/style/errors.js +1 -0
  10. package/lib/esm/style/gtk-css.js +1 -0
  11. package/lib/esm/style/gtk-props.js +1 -0
  12. package/lib/esm/style/index.js +1 -0
  13. package/lib/esm/style/layout.js +1 -0
  14. package/lib/esm/style/paint.js +1 -0
  15. package/lib/esm/style/resolve.js +1 -0
  16. package/lib/esm/style/sheet.js +3 -0
  17. package/lib/esm/style/tokens.js +1 -0
  18. package/lib/types/adapters/react.d.ts +59 -10
  19. package/lib/types/conformance/diagnostics.d.ts +12 -2
  20. package/lib/types/conformance/index.d.ts +1 -1
  21. package/lib/types/descriptors/index.d.ts +1 -1
  22. package/lib/types/generator/gir.d.mts +10 -2
  23. package/lib/types/probe.d.ts +31 -2
  24. package/lib/types/props.d.ts +7 -4
  25. package/lib/types/style/errors.d.ts +6 -0
  26. package/lib/types/style/gtk-css.d.ts +24 -0
  27. package/lib/types/style/gtk-css.spec.d.ts +2 -0
  28. package/lib/types/style/gtk-props.d.ts +27 -0
  29. package/lib/types/style/gtk-props.spec.d.ts +2 -0
  30. package/lib/types/style/index.d.ts +13 -0
  31. package/lib/types/style/layout.d.ts +163 -0
  32. package/lib/types/style/layout.spec.d.ts +2 -0
  33. package/lib/types/style/paint.d.ts +53 -0
  34. package/lib/types/style/paint.spec.d.ts +2 -0
  35. package/lib/types/style/resolve.d.ts +58 -0
  36. package/lib/types/style/sheet.d.ts +40 -0
  37. package/lib/types/style/sheet.spec.d.ts +2 -0
  38. package/lib/types/style/tokens.d.ts +58 -0
  39. package/lib/types/testing/gate.d.mts +23 -0
  40. package/package.json +18 -11
package/README.md CHANGED
@@ -103,6 +103,15 @@ exists there, so it uses `indexed` and reorders natively. Near-identical APIs wi
103
103
  opposite capabilities are exactly why the table is measured per widget rather than
104
104
  inherited.
105
105
 
106
+ `Adw.NavigationView` declares the same `remove-all` degradation and it is a HAZARD
107
+ there rather than only a cost: its `remove()` also takes the page out of the
108
+ **navigation stack**, which no other container in the table does, so a renderer that
109
+ reorders these children disturbs navigation and not just paint order. It is `keyed`
110
+ with `titled: false` — MEASURED, `add(page)` takes one argument — and the tag a page
111
+ is addressed by afterwards (`push_by_tag`, `pop_to_tag`, `replace_with_tags`) is a
112
+ property on the CHILD, not an argument to the add. That is what lets a route key be
113
+ the join between React's membership and GTK's ordering.
114
+
106
115
  `slot` and `layout` are props the CHILD declares: `slot="end"` picks a `slotted`
107
116
  attachment point, `layout={{ column, row, columnSpan, rowSpan }}` a `coords` cell,
108
117
  `layout={{ name, title }}` a `keyed` page. Both are read at placement time, so
@@ -377,14 +386,30 @@ is not in this table because `@vue/runtime-core` does not export it at all
377
386
  READ instead of restated. `react-reconciler` is `module.exports = function
378
387
  $$$reconciler($$$hostConfig) { … }` and its body opens by destructuring the whole
379
388
  config, so a recording `Proxy` over the config reports exactly which members the
380
- installed version asks for. Measured on **0.29.2**, production bundle: **76**
381
- members read, **37** answered here, **39** switched off by three flags the config
382
- does declare (`supportsPersistence: false`, `supportsHydration: false`,
383
- `supportsTestSelectors` absent) plus `supportsMicrotasks`, and 4 more declared for
384
- the development bundle, which reads 94. `react.spec.ts` asserts all three
389
+ installed version asks for. Measured on **0.33.0** (React 19.2), production bundle:
390
+ **160** members read, **58** answered here, **102** switched off — by the four
391
+ capability flags the config declares false (`supportsPersistence`,
392
+ `supportsHydration`, `supportsResources`, `supportsSingletons`), by two it declares
393
+ not at all (`supportsTestSelectors`, `supportsMicrotasks`), and by three families no
394
+ flag gates and nothing here can mean (view transitions and the gesture timeline,
395
+ fragment instances, DevTools metadata, and the suspended-commit reason React reads
396
+ only to name a commit in its own timeline). `react.spec.ts` asserts all three
385
397
  directions, so a version bump that starts asking for something new fails a test
386
398
  instead of reading `undefined` inside a commit.
387
399
 
400
+ **What the React 19 bump cost, because the numbers alone understate it.** 0.29.2 read
401
+ 76 members and the development bundle read 94, which made the count itself a guard
402
+ against a lost `NODE_ENV=production` define; on 0.33.0 both bundles read 160 and that
403
+ guard is gone — the "runs without a DOM at all" vector, which asserts `document`,
404
+ `HTMLCanvasElement` and `Path2D` are absent, is now the only thing holding the build
405
+ recipe. Three further changes were silent rather than typed: `flushSync(() =>
406
+ updateContainer(…))` on a ConcurrentRoot mounts NOTHING under 19 (the sync path is
407
+ `updateContainerSync` + `flushSyncWork`); `createContainer` grew two error callbacks
408
+ *before* `onRecoverableError`, so the React 18 argument list keeps working and wires
409
+ the wrong handler; and an error the host throws no longer propagates out of
410
+ `render()` but goes to `onUncaughtError`, which is why `createRoot` holds the first
411
+ one and rethrows it — a named refusal that only reaches a log has bought nothing.
412
+
388
413
  ```ts
389
414
  import { createRoot, flushSync } from '@gjsify/gtk-host/react';
390
415
 
@@ -396,11 +421,13 @@ root.render(createElement('GtkBox', null, createElement('GtkLabel', { label: 'hi
396
421
  `ConcurrentRoot`'s updates are DEFAULT-lane, so they are handed to `scheduler`,
397
422
  which under GJS is a GLib timer source — with no main loop running yet, an
398
423
  unflushed first render leaves the container empty and says nothing. `render()`
399
- therefore wraps `updateContainer` in `flushSync`, which sets the current update
400
- priority to `DiscreteEventPriority` and makes everything scheduled inside it a
401
- sync lane the same call then flushes.
424
+ therefore calls `updateContainerSync`, which puts the work on the sync lane, and
425
+ then `flushSyncWork`, which drains it. Under React 18 this was
426
+ `flushSync(() => updateContainer(…))`; under 19 that combination on a
427
+ `ConcurrentRoot` mounts nothing at all, silently, so the two calls are spelled
428
+ separately and the reason is written down where they are.
402
429
  - **A `setState` from a GTK signal handler is concurrent** and lands on the next
403
- main-loop iteration, because `getCurrentEventPriority` returns the default lane
430
+ main-loop iteration, because `resolveUpdatePriority` returns the default lane
404
431
  (there is no ambient DOM event to derive a priority from). `flushSync` is the
405
432
  escape hatch. A vector pumps `GLib.MainContext.default()` and proves the
406
433
  scheduled path works at all under GJS — it does, on gjs 1.88.1, with no
@@ -1 +1 @@
1
- import"../_virtual/_rolldown/runtime.js";import{withoutKeys as e}from"../props.js";import{adopt as t,createElement as n,createText as r,destroy as i,destroyChildren as a,insert as o,materialize as s,setElementText as c,setProp as l,setText as u,widgetOf as d}from"../host.js";import f from"react-reconciler";import{ConcurrentRoot as p,DefaultEventPriority as m}from"react-reconciler/constants.js";const h=new Set([`children`]),ownProps=t=>e(t,h);function diffProps(e,t){let n=null;for(let r of Object.keys(t))h.has(r)||t[r]!==e[r]&&(n??=[]).push([r,t[r],e[r]]);for(let r of Object.keys(e))h.has(r)||r in t||(n??=[]).push([r,void 0,e[r]]);return n}const visibilityTargetOf=e=>{let t=s(e);return e.wrapper??t},g={supportsMutation:!0,supportsPersistence:!1,supportsHydration:!1,isPrimaryRenderer:!0,noTimeout:-1,scheduleTimeout:setTimeout,cancelTimeout:clearTimeout,createInstance:(e,t)=>n(e,ownProps(t)),createTextInstance:e=>r(e),appendInitialChild:(e,t)=>{o(t,e,null)},finalizeInitialChildren:()=>!1,shouldSetTextContent:()=>!1,getRootHostContext:()=>null,getChildHostContext:e=>e,getPublicInstance:e=>s(e),prepareForCommit:()=>null,resetAfterCommit:()=>{},preparePortalMount:()=>{},getCurrentEventPriority:()=>m,appendChild:(e,t)=>{o(t,e,null)},appendChildToContainer:(e,t)=>{o(t,e,null)},insertBefore:(e,t,n)=>{o(t,e,n)},insertInContainerBefore:(e,t,n)=>{o(t,e,n)},removeChild:(e,t)=>{i(t)},removeChildFromContainer:(e,t)=>{i(t)},clearContainer:e=>{a(e)},prepareUpdate:(e,t,n,r)=>diffProps(n,r),commitUpdate:(e,t)=>{for(let[n,r,i]of t)l(e,n,r,i)},commitTextUpdate:(e,t,n)=>{u(e,n)},commitMount:()=>{throw Error(`@gjsify/gtk-host/react: commitMount was called, which means finalizeInitialChildren returned true. Nothing in this adapter asks for a post-mount callback yet.`)},resetTextContent:e=>{c(e,``)},hideInstance:e=>{visibilityTargetOf(e).set_visible(!1)},unhideInstance:e=>{visibilityTargetOf(e).set_visible(!0)},hideTextInstance:e=>{u(e,``)},unhideTextInstance:(e,t)=>{u(e,t)},getInstanceFromNode:()=>null,getInstanceFromScope:()=>null,prepareScopeUpdate:()=>{},beforeActiveInstanceBlur:()=>{},afterActiveInstanceBlur:()=>{},detachDeletedInstance:()=>{}},_=f(g),flushSync=e=>_.flushSync(e);function createRoot(e,n={}){let r=t(e),i=n.onRecoverableError??(e=>{console.error(`@gjsify/gtk-host/react: React recovered from an error`,e)}),a=_.createContainer(r,p,null,!1,null,``,i,null);return{container:r,render(e){_.flushSync(()=>{_.updateContainer(e,a,null,null)})},unmount(){_.flushSync(()=>{_.updateContainer(null,a,null,null)})}}}function mount(e,t,n={}){let r=createRoot(t,n);return r.render(e),r}export{t as adopt,createRoot,flushSync,g as gtkHostConfig,mount,d as widgetOf};
1
+ import"../_virtual/_rolldown/runtime.js";import{withoutKeys as e}from"../props.js";import{adopt as t,createElement as n,createText as r,destroy as i,destroyChildren as a,insert as o,materialize as s,setElementText as c,setProp as l,setText as u,widgetOf as d}from"../host.js";import f from"react-reconciler";import{ConcurrentRoot as p,DefaultEventPriority as m,NoEventPriority as h}from"react-reconciler/constants.js";import{createContext as g}from"react";const _=new Set([`children`,`ref`]),ownProps=t=>e(t,_);function diffProps(e,t){let n=null;for(let r of Object.keys(t))_.has(r)||t[r]!==e[r]&&(n??=[]).push([r,t[r],e[r]]);for(let r of Object.keys(e))_.has(r)||r in t||(n??=[]).push([r,void 0,e[r]]);return n}const visibilityTargetOf=e=>{let t=s(e);return e.wrapper??t},v=Object.freeze({});let y=h;const b={supportsMutation:!0,supportsPersistence:!1,supportsHydration:!1,isPrimaryRenderer:!0,supportsResources:!1,supportsSingletons:!1,noTimeout:-1,scheduleTimeout:setTimeout,cancelTimeout:clearTimeout,createInstance:(e,t)=>n(e,ownProps(t)),createTextInstance:e=>r(e),appendInitialChild:(e,t)=>{o(t,e,null)},finalizeInitialChildren:()=>!1,shouldSetTextContent:()=>!1,getRootHostContext:()=>v,getChildHostContext:e=>e,getPublicInstance:e=>s(e),prepareForCommit:()=>null,resetAfterCommit:()=>{},preparePortalMount:()=>{},getCurrentUpdatePriority:()=>y,setCurrentUpdatePriority:e=>{y=e},resolveUpdatePriority:()=>y===h?m:y,shouldAttemptEagerTransition:()=>!1,trackSchedulerEvent:()=>{},resolveEventType:()=>null,resolveEventTimeStamp:()=>-1.1,appendChild:(e,t)=>{o(t,e,null)},appendChildToContainer:(e,t)=>{o(t,e,null)},insertBefore:(e,t,n)=>{o(t,e,n)},insertInContainerBefore:(e,t,n)=>{o(t,e,n)},removeChild:(e,t)=>{i(t)},removeChildFromContainer:(e,t)=>{i(t)},clearContainer:e=>{a(e)},commitUpdate:(e,t,n,r)=>{let i=diffProps(n,r);if(i!==null)for(let[t,n,r]of i)l(e,t,n,r)},commitTextUpdate:(e,t,n)=>{u(e,n)},commitMount:()=>{throw Error(`@gjsify/gtk-host/react: commitMount was called, which means finalizeInitialChildren returned true. Nothing in this adapter asks for a post-mount callback yet.`)},resetTextContent:e=>{c(e,``)},hideInstance:e=>{visibilityTargetOf(e).set_visible(!1)},unhideInstance:e=>{visibilityTargetOf(e).set_visible(!0)},hideTextInstance:e=>{u(e,``)},unhideTextInstance:(e,t)=>{u(e,t)},getInstanceFromNode:()=>null,getInstanceFromScope:()=>null,prepareScopeUpdate:()=>{},beforeActiveInstanceBlur:()=>{},maySuspendCommit:()=>!1,maySuspendCommitOnUpdate:()=>!1,maySuspendCommitInSyncRender:()=>!1,preloadInstance:()=>!0,startSuspendingCommit:()=>{},suspendInstance:()=>{},waitForCommitToBeReady:()=>null,requestPostPaintCallback:()=>{},NotPendingTransition:null,HostTransitionContext:g(null),resetFormInstance:()=>{},detachDeletedInstance:()=>{}},x=f(b),flushSync=e=>x.flushSyncFromReconciler(e);function createRoot(e,n={}){let r=t(e),report=(e,t)=>n=>{if(t)return t(n);console.error(`@gjsify/gtk-host/react: ${e}`,n)},i=null,a=n.onUncaughtError?report(`React hit an error no boundary caught`,n.onUncaughtError):e=>{console.error(`@gjsify/gtk-host/react: React hit an error no boundary caught`,e),i??=e},o=x.createContainer(r,p,null,!1,null,``,a,report(`an error boundary caught an error`,n.onCaughtError),report(`React recovered from an error`,n.onRecoverableError),()=>{}),renderSync=e=>{i=null,x.updateContainerSync(e,o,null,null);let t=x.flushSyncWork();if(i!==null){let e=i;throw i=null,e}if(t===!0)throw Error(`@gjsify/gtk-host/react: render() could not flush, because React is already rendering or committing — this call is re-entrant (a ref callback, a useLayoutEffect, or a GTK signal handler that fired during a commit). The widgets do NOT exist yet, which is what render() promises, so this throws rather than returning an empty container. Render the second tree from a useEffect or an idle callback instead.`)};return{container:r,render:renderSync,unmount(){renderSync(null)}}}function mount(e,t,n={}){let r=createRoot(t,n);return r.render(e),r}export{t as adopt,createRoot,flushSync,b as gtkHostConfig,mount,d as widgetOf};
@@ -1,2 +1,2 @@
1
- import"../_virtual/_rolldown/runtime.js";import e from"gi://GLib";let t=null;function describeLogRecord(e,t=new TextDecoder){let text=e=>e instanceof Uint8Array?t.decode(e):String(e),n=e,r=n?.MESSAGE;if(r!=null){let e=text(r);if(e!==``)return e}let i=n?Object.keys(n).filter(e=>e!==`MESSAGE`&&n[e]!==void 0&&n[e]!==null).map(e=>`${e}=${text(n[e])}`):[];return i.length>0?`<no MESSAGE field> ${i.join(` `)}`:`<a log record with no MESSAGE and no other field>`}function installDiagnosticsGate(){if(t)return t;let n=[],r=new TextDecoder,i=e.getenv(`G_MESSAGES_DEBUG`)!==null;return e.log_set_writer_func((t,a)=>{try{let o=describeLogRecord(a,r),s=t&e.LogLevelFlags.LEVEL_MASK;s<=e.LogLevelFlags.LEVEL_WARNING&&n.push(o),(i||s<=e.LogLevelFlags.LEVEL_MESSAGE)&&printerr(o)}catch{printerr(`<gtk-host: a log message could not be decoded>`)}return e.LogWriterOutput.HANDLED}),t={seen:n,reset(){n.length=0},assertQuiet(e){if(n.length===0)return;let t=n.length,r=n.join(`
2
- `);throw n.length=0,Error(`${e?e+`: `:``}GTK reported ${t} diagnostic(s) that would have passed at exit 0:\n ${r}`)}},t}export{describeLogRecord,installDiagnosticsGate};
1
+ import"../_virtual/_rolldown/runtime.js";import e from"gi://GLib";const t=[`Vulkan: `];function isEnvironmentDiagnostic(e){return t.some(t=>e.startsWith(t))}let n=null;function describeLogRecord(e,t=new TextDecoder){let text=e=>e instanceof Uint8Array?t.decode(e):String(e),n=e,r=n?.MESSAGE;if(r!=null){let e=text(r);if(e!==``)return e}let i=n?Object.keys(n).filter(e=>e!==`MESSAGE`&&n[e]!==void 0&&n[e]!==null).map(e=>`${e}=${text(n[e])}`):[];return i.length>0?`<no MESSAGE field> ${i.join(` `)}`:`<a log record with no MESSAGE and no other field>`}function installDiagnosticsGate(){if(n)return n;let t=[],r=[],i=new TextDecoder,a=e.getenv(`G_MESSAGES_DEBUG`)!==null;return e.log_set_writer_func((n,o)=>{try{let s=describeLogRecord(o,i),c=n&e.LogLevelFlags.LEVEL_MASK;c<=e.LogLevelFlags.LEVEL_WARNING&&(isEnvironmentDiagnostic(s)?r:t).push(s),(a||c<=e.LogLevelFlags.LEVEL_MESSAGE)&&printerr(s)}catch{printerr(`<gtk-host: a log message could not be decoded>`)}return e.LogWriterOutput.HANDLED}),n={seen:t,environment:r,reset(){t.length=0,r.length=0},assertQuiet(e){let n=r.length;if(r.length=0,t.length===0)return;let i=t.length,a=t.join(`
2
+ `);t.length=0;let o=n>0?`\n (plus ${n} diagnostic(s) about the HOST's graphics stack, not the tree — see isEnvironmentDiagnostic)`:``;throw Error(`${e?e+`: `:``}GTK reported ${i} diagnostic(s) that would have passed at exit 0:\n ${a}${o}`)}},n}export{describeLogRecord,installDiagnosticsGate,isEnvironmentDiagnostic};
@@ -1,2 +1,2 @@
1
- import"../_virtual/_rolldown/runtime.js";import{addressOf as e,unhandledPolicy as t}from"../policies.js";import{describeLogRecord as n,installDiagnosticsGate as r}from"./diagnostics.js";import{BUILTIN_DESCRIPTORS as i}from"../descriptors/index.js";import a from"gi://GObject";function methodsOf(e){switch(e.kind){case`none`:case`uncurated`:return[];case`single`:return[e.set];case`ordered`:return e.after?[e.append,e.after,e.remove]:[e.append,e.remove];case`indexed`:return[e.insert,e.remove];case`slotted`:return[...Object.values(e.slots),e.remove];case`keyed`:return[e.add,e.remove];case`coords`:return[e.attach,e.remove];default:return t(e)}}function descriptorProblems(e=i){let t=[];for(let n of e){let e;try{e=n.ctor()}catch(e){t.push({gtype:n.gtype,problem:`ctor() threw: ${e.message}`});continue}let r=a.type_name(e.$gtype);r!==n.gtype&&t.push({gtype:n.gtype,problem:`ctor() is ${r}, not ${n.gtype}`});for(let i of methodsOf(n.children))typeof e.prototype[i]!=`function`&&t.push({gtype:n.gtype,problem:`declares children.${i}(), which ${r} does not have`});if(n.textSink){let i=e.list_properties().find(e=>e.get_name()===n.textSink);i?(i.flags&a.ParamFlags.WRITABLE)===0?t.push({gtype:n.gtype,problem:`declares a READ-ONLY textSink "${n.textSink}"`}):a.type_is_a(i.value_type,a.TYPE_STRING)||t.push({gtype:n.gtype,problem:`declares textSink "${n.textSink}", which is ${a.type_name(i.value_type)}, not a string`}):t.push({gtype:n.gtype,problem:`declares textSink "${n.textSink}", which ${r} does not have`})}t.push(...policyProblems(n,e,r))}return t}function policyProblems(e,t,n){let r=[],i=t.prototype,a=e.children,requireGetter=(t,a)=>{let o=t.replace(/^set_/,`get_`);typeof i[o]!=`function`&&r.push({gtype:e.gtype,problem:`${a} uses ${t}() but ${n} has no ${o}(), so removal cannot check whether this child is still the one in place`})};if(a.kind===`single`&&requireGetter(a.set,`children.set`),a.kind===`slotted`){for(let[e,t]of Object.entries(a.slots))t.startsWith(`set_`)&&requireGetter(t,`slot "${e}"`);a.defaultSlot in a.slots||r.push({gtype:e.gtype,problem:`defaultSlot "${a.defaultSlot}" is not one of ${Object.keys(a.slots).join(`, `)}`})}if(a.kind===`ordered`&&a.reorder===`native`&&!a.after&&r.push({gtype:e.gtype,problem:`claims reorder: 'native' with no "after" method — reorderMode() would tell an adapter the wrong thing`}),a.kind===`keyed`){let t=i[a.add],o=a.titled?3:1;typeof t==`function`&&t.length!==o&&r.push({gtype:e.gtype,problem:`titled: ${a.titled} implies ${a.add}() takes ${o} argument(s), but ${n}'s takes ${t.length}`})}return r}function gtkChildren(e){let t=[],n=e;if(typeof n.get_first_child!=`function`)return t;for(let e=n.get_first_child();e;e=e.get_next_sibling())t.push(e);return t}const gtkChildTypes=e=>gtkChildren(e).map(e=>a.type_name(e.constructor.$gtype));function addressesOf(t){let n=[];for(let r=t.first;r;r=r.next)r.kind===`element`&&r.widget&&n.push(e(r));return n}function findDescendant(e,t){let n=[e];for(;n.length>0;){let r=n.shift();if(r!==e&&t(r))return r;n.push(...gtkChildren(r))}return null}function descendants(e){let t=[e];for(let n of gtkChildren(e))t.push(...descendants(n));return t}function dumpTree(e,t=0){let n=a.type_name(e.constructor.$gtype),r=[`${` `.repeat(t)}${n}`];for(let n of gtkChildren(e))r.push(dumpTree(n,t+1));return r.join(`
2
- `)}export{addressesOf,descendants,n as describeLogRecord,descriptorProblems,dumpTree,findDescendant,gtkChildTypes,gtkChildren,r as installDiagnosticsGate,methodsOf};
1
+ import"../_virtual/_rolldown/runtime.js";import{addressOf as e,unhandledPolicy as t}from"../policies.js";import{describeLogRecord as n,installDiagnosticsGate as r,isEnvironmentDiagnostic as i}from"./diagnostics.js";import{BUILTIN_DESCRIPTORS as a}from"../descriptors/index.js";import o from"gi://GObject";function methodsOf(e){switch(e.kind){case`none`:case`uncurated`:return[];case`single`:return[e.set];case`ordered`:return e.after?[e.append,e.after,e.remove]:[e.append,e.remove];case`indexed`:return[e.insert,e.remove];case`slotted`:return[...Object.values(e.slots),e.remove];case`keyed`:return[e.add,e.remove];case`coords`:return[e.attach,e.remove];default:return t(e)}}function descriptorProblems(e=a){let t=[];for(let n of e){let e;try{e=n.ctor()}catch(e){t.push({gtype:n.gtype,problem:`ctor() threw: ${e.message}`});continue}let r=o.type_name(e.$gtype);r!==n.gtype&&t.push({gtype:n.gtype,problem:`ctor() is ${r}, not ${n.gtype}`});for(let i of methodsOf(n.children))typeof e.prototype[i]!=`function`&&t.push({gtype:n.gtype,problem:`declares children.${i}(), which ${r} does not have`});if(n.textSink){let i=e.list_properties().find(e=>e.get_name()===n.textSink);i?(i.flags&o.ParamFlags.WRITABLE)===0?t.push({gtype:n.gtype,problem:`declares a READ-ONLY textSink "${n.textSink}"`}):o.type_is_a(i.value_type,o.TYPE_STRING)||t.push({gtype:n.gtype,problem:`declares textSink "${n.textSink}", which is ${o.type_name(i.value_type)}, not a string`}):t.push({gtype:n.gtype,problem:`declares textSink "${n.textSink}", which ${r} does not have`})}t.push(...policyProblems(n,e,r))}return t}function policyProblems(e,t,n){let r=[],i=t.prototype,a=e.children,requireGetter=(t,a)=>{let o=t.replace(/^set_/,`get_`);typeof i[o]!=`function`&&r.push({gtype:e.gtype,problem:`${a} uses ${t}() but ${n} has no ${o}(), so removal cannot check whether this child is still the one in place`})};if(a.kind===`single`&&requireGetter(a.set,`children.set`),a.kind===`slotted`){for(let[e,t]of Object.entries(a.slots))t.startsWith(`set_`)&&requireGetter(t,`slot "${e}"`);a.defaultSlot in a.slots||r.push({gtype:e.gtype,problem:`defaultSlot "${a.defaultSlot}" is not one of ${Object.keys(a.slots).join(`, `)}`})}if(a.kind===`ordered`&&a.reorder===`native`&&!a.after&&r.push({gtype:e.gtype,problem:`claims reorder: 'native' with no "after" method — reorderMode() would tell an adapter the wrong thing`}),a.kind===`keyed`){let t=i[a.add],o=a.titled?3:1;typeof t==`function`&&t.length!==o&&r.push({gtype:e.gtype,problem:`titled: ${a.titled} implies ${a.add}() takes ${o} argument(s), but ${n}'s takes ${t.length}`})}return r}function gtkChildren(e){let t=[],n=e;if(typeof n.get_first_child!=`function`)return t;for(let e=n.get_first_child();e;e=e.get_next_sibling())t.push(e);return t}const gtkChildTypes=e=>gtkChildren(e).map(e=>o.type_name(e.constructor.$gtype));function addressesOf(t){let n=[];for(let r=t.first;r;r=r.next)r.kind===`element`&&r.widget&&n.push(e(r));return n}function findDescendant(e,t){let n=[e];for(;n.length>0;){let r=n.shift();if(r!==e&&t(r))return r;n.push(...gtkChildren(r))}return null}function descendants(e){let t=[e];for(let n of gtkChildren(e))t.push(...descendants(n));return t}function dumpTree(e,t=0){let n=o.type_name(e.constructor.$gtype),r=[`${` `.repeat(t)}${n}`];for(let n of gtkChildren(e))r.push(dumpTree(n,t+1));return r.join(`
2
+ `)}export{addressesOf,descendants,n as describeLogRecord,descriptorProblems,dumpTree,findDescendant,gtkChildTypes,gtkChildren,r as installDiagnosticsGate,i as isEnvironmentDiagnostic,methodsOf};
@@ -1 +1 @@
1
- import"../_virtual/_rolldown/runtime.js";import e from"gi://Adw?version=1";const t=[{gtype:`AdwApplicationWindow`,ctor:()=>e.ApplicationWindow,children:{kind:`single`,set:`set_content`}},{gtype:`AdwWindow`,ctor:()=>e.Window,children:{kind:`single`,set:`set_content`}},{gtype:`AdwBin`,ctor:()=>e.Bin,children:{kind:`single`,set:`set_child`}},{gtype:`AdwToolbarView`,ctor:()=>e.ToolbarView,children:{kind:`slotted`,slots:{top:`add_top_bar`,bottom:`add_bottom_bar`,content:`set_content`},defaultSlot:`content`,remove:`remove`}},{gtype:`AdwHeaderBar`,ctor:()=>e.HeaderBar,children:{kind:`slotted`,slots:{start:`pack_start`,end:`pack_end`,title:`set_title_widget`},defaultSlot:`start`,remove:`remove`}},{gtype:`AdwPreferencesGroup`,ctor:()=>e.PreferencesGroup,children:{kind:`ordered`,append:`add`,remove:`remove`,reorder:`remove-all`}},{gtype:`AdwPreferencesPage`,ctor:()=>e.PreferencesPage,children:{kind:`indexed`,insert:`insert`,remove:`remove`,wrap:null}},{gtype:`AdwActionRow`,ctor:()=>e.ActionRow,children:{kind:`slotted`,slots:{prefix:`add_prefix`,suffix:`add_suffix`},defaultSlot:`suffix`,remove:`remove`}},{gtype:`AdwStatusPage`,ctor:()=>e.StatusPage,children:{kind:`single`,set:`set_child`}}];export{t as ADW_DESCRIPTORS};
1
+ import"../_virtual/_rolldown/runtime.js";import e from"gi://Adw?version=1";const t=[{gtype:`AdwApplicationWindow`,ctor:()=>e.ApplicationWindow,children:{kind:`single`,set:`set_content`}},{gtype:`AdwWindow`,ctor:()=>e.Window,children:{kind:`single`,set:`set_content`}},{gtype:`AdwBin`,ctor:()=>e.Bin,children:{kind:`single`,set:`set_child`}},{gtype:`AdwToolbarView`,ctor:()=>e.ToolbarView,children:{kind:`slotted`,slots:{top:`add_top_bar`,bottom:`add_bottom_bar`,content:`set_content`},defaultSlot:`content`,remove:`remove`}},{gtype:`AdwHeaderBar`,ctor:()=>e.HeaderBar,children:{kind:`slotted`,slots:{start:`pack_start`,end:`pack_end`,title:`set_title_widget`},defaultSlot:`start`,remove:`remove`}},{gtype:`AdwPreferencesGroup`,ctor:()=>e.PreferencesGroup,children:{kind:`ordered`,append:`add`,remove:`remove`,reorder:`remove-all`}},{gtype:`AdwPreferencesPage`,ctor:()=>e.PreferencesPage,children:{kind:`indexed`,insert:`insert`,remove:`remove`,wrap:null}},{gtype:`AdwActionRow`,ctor:()=>e.ActionRow,children:{kind:`slotted`,slots:{prefix:`add_prefix`,suffix:`add_suffix`},defaultSlot:`suffix`,remove:`remove`}},{gtype:`AdwStatusPage`,ctor:()=>e.StatusPage,children:{kind:`single`,set:`set_child`}},{gtype:`AdwNavigationView`,ctor:()=>e.NavigationView,children:{kind:`keyed`,add:`add`,remove:`remove`,nameFrom:`tag`,titled:!1}},{gtype:`AdwNavigationPage`,ctor:()=>e.NavigationPage,children:{kind:`single`,set:`set_child`}},{gtype:`AdwViewStack`,ctor:()=>e.ViewStack,children:{kind:`keyed`,add:`add_titled`,remove:`remove`,nameFrom:`name`,titled:!0}}];export{t as ADW_DESCRIPTORS};
@@ -1 +1 @@
1
- import"../_virtual/_rolldown/runtime.js";import e from"gi://Gtk?version=4.0";const t=[{gtype:`GtkBox`,ctor:()=>e.Box,children:{kind:`ordered`,append:`append`,after:`insert_child_after`,remove:`remove`,reorder:`native`}},{gtype:`GtkWindow`,ctor:()=>e.Window,children:{kind:`single`,set:`set_child`}},{gtype:`GtkApplicationWindow`,ctor:()=>e.ApplicationWindow,children:{kind:`single`,set:`set_child`}},{gtype:`GtkScrolledWindow`,ctor:()=>e.ScrolledWindow,children:{kind:`single`,set:`set_child`}},{gtype:`GtkFrame`,ctor:()=>e.Frame,children:{kind:`single`,set:`set_child`}},{gtype:`GtkLabel`,ctor:()=>e.Label,children:{kind:`none`},textSink:`label`},{gtype:`GtkButton`,ctor:()=>e.Button,children:{kind:`single`,set:`set_child`},textSink:`label`},{gtype:`GtkToggleButton`,ctor:()=>e.ToggleButton,children:{kind:`single`,set:`set_child`},textSink:`label`},{gtype:`GtkImage`,ctor:()=>e.Image,children:{kind:`none`}},{gtype:`GtkSwitch`,ctor:()=>e.Switch,children:{kind:`none`}},{gtype:`GtkEntry`,ctor:()=>e.Entry,children:{kind:`none`},textSink:`text`},{gtype:`GtkListBox`,ctor:()=>e.ListBox,children:{kind:`indexed`,insert:`insert`,remove:`remove`,wrap:`list-box-row`}},{gtype:`GtkFlowBox`,ctor:()=>e.FlowBox,children:{kind:`indexed`,insert:`insert`,remove:`remove`,wrap:`flow-box-child`}},{gtype:`GtkListBoxRow`,ctor:()=>e.ListBoxRow,children:{kind:`single`,set:`set_child`}},{gtype:`GtkStack`,ctor:()=>e.Stack,children:{kind:`keyed`,add:`add_titled`,remove:`remove`,nameFrom:`name`,titled:!0}},{gtype:`GtkGrid`,ctor:()=>e.Grid,children:{kind:`coords`,attach:`attach`,remove:`remove`}},{gtype:`GtkHeaderBar`,ctor:()=>e.HeaderBar,children:{kind:`slotted`,slots:{start:`pack_start`,end:`pack_end`,title:`set_title_widget`},defaultSlot:`start`,remove:`remove`}}];export{t as GTK_DESCRIPTORS};
1
+ import"../_virtual/_rolldown/runtime.js";import e from"gi://Gtk?version=4.0";const t=[{gtype:`GtkBox`,ctor:()=>e.Box,children:{kind:`ordered`,append:`append`,after:`insert_child_after`,remove:`remove`,reorder:`native`}},{gtype:`GtkWindow`,ctor:()=>e.Window,children:{kind:`single`,set:`set_child`}},{gtype:`GtkApplicationWindow`,ctor:()=>e.ApplicationWindow,children:{kind:`single`,set:`set_child`}},{gtype:`GtkScrolledWindow`,ctor:()=>e.ScrolledWindow,children:{kind:`single`,set:`set_child`}},{gtype:`GtkFrame`,ctor:()=>e.Frame,children:{kind:`single`,set:`set_child`}},{gtype:`GtkOverlay`,ctor:()=>e.Overlay,children:{kind:`slotted`,slots:{child:`set_child`,overlay:`add_overlay`},defaultSlot:`child`,remove:`remove_overlay`}},{gtype:`GtkLabel`,ctor:()=>e.Label,children:{kind:`none`},textSink:`label`},{gtype:`GtkButton`,ctor:()=>e.Button,children:{kind:`single`,set:`set_child`},textSink:`label`},{gtype:`GtkToggleButton`,ctor:()=>e.ToggleButton,children:{kind:`single`,set:`set_child`},textSink:`label`},{gtype:`GtkImage`,ctor:()=>e.Image,children:{kind:`none`}},{gtype:`GtkSwitch`,ctor:()=>e.Switch,children:{kind:`none`}},{gtype:`GtkEntry`,ctor:()=>e.Entry,children:{kind:`none`},textSink:`text`},{gtype:`GtkListBox`,ctor:()=>e.ListBox,children:{kind:`indexed`,insert:`insert`,remove:`remove`,wrap:`list-box-row`}},{gtype:`GtkFlowBox`,ctor:()=>e.FlowBox,children:{kind:`indexed`,insert:`insert`,remove:`remove`,wrap:`flow-box-child`}},{gtype:`GtkListBoxRow`,ctor:()=>e.ListBoxRow,children:{kind:`single`,set:`set_child`}},{gtype:`GtkStack`,ctor:()=>e.Stack,children:{kind:`keyed`,add:`add_titled`,remove:`remove`,nameFrom:`name`,titled:!0}},{gtype:`GtkGrid`,ctor:()=>e.Grid,children:{kind:`coords`,attach:`attach`,remove:`remove`}},{gtype:`GtkHeaderBar`,ctor:()=>e.HeaderBar,children:{kind:`slotted`,slots:{start:`pack_start`,end:`pack_end`,title:`set_title_widget`},defaultSlot:`start`,remove:`remove`}}];export{t as GTK_DESCRIPTORS};
package/lib/esm/host.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{err as e}from"./errors.js";import{addressOf as t,insertChild as n,makeDetachedContainer as r,makeWrapper as i,removeChild as a,setterSlotOf as o,setterSlots as s,slotOccupant as c}from"./policies.js";import{beginHostWrite as l,clearHandlers as u,endHostWrite as d,isEventProp as f,setHandler as p,toSignalName as m}from"./signals.js";import{coerce as h,paramSpecs as g,removedValue as _,requireSpec as v,toPropertyName as y}from"./props.js";import{lookupWidget as b,nearestRegistered as x}from"./registry.js";import S from"gi://GObject";function createElement(e,t){let n={kind:`element`,descriptor:b(e),widget:null,wrapper:null,slot:null,parent:null,prev:null,next:null,first:null,last:null,handlers:new Map,listeners:new Map,props:{},layout:null,textFromChildren:!1,attached:!1,destroyed:!1,foreign:[]};if(t)for(let[e,r]of Object.entries(t))setProp(n,e,r);return n}const createText=e=>({kind:`text`,data:e,parent:null,prev:null,next:null}),createAnchor=(e=``)=>({kind:`anchor`,data:e,parent:null,prev:null,next:null}),isText=e=>e.kind===`text`;function materialize(t){if(t.widget)return t.widget;for(let n of t.descriptor.requiresProps??[])if(!(n in t.props))throw e.missingConstructProp(t.descriptor.gtype,n);let n=t.descriptor.ctor(),r=g(n,t.descriptor.gtype),i={};for(let[e,n]of Object.entries(t.props))i[e]=h(v(r,t.descriptor.gtype,e),n,t.descriptor.gtype);l();try{t.widget=new n(i)}finally{d()}try{replayInto(t)}catch(e){u(t);for(let e of childSnapshot(t))e.kind===`element`&&e.attached&&(a(t,e),e.attached=!1);throw t.widget=null,t.wrapper=null,e}return t.widget}function replayInto(e){for(let[t,n]of e.listeners)p(e,t,n);for(let t of siblingsFrom(e.first,e))t.kind===`element`&&attach(e,t);e.textFromChildren&&flushText(e)}function setProp(e,t,n,r){if(f(t))return setEventHandler(e,t,n);if(t===`slot`)return setSlot(e,n);if(t===`layout`){let t=n,r=e.parent;if(!r||!e.widget){e.layout=t;return}let i=e.layout;replaceAt(e,r,()=>{e.layout=t},()=>{e.layout=i});return}let i=y(t),a=v(g(e.descriptor.ctor(),e.descriptor.gtype),e.descriptor.gtype,i),o=n==null,s=o?_(e.descriptor,a):h(a,n,e.descriptor.gtype),c=i in e.props?e.props[i]:void 0;if(o?delete e.props[i]:e.props[i]=n,e.widget){if((a.flags&S.ParamFlags.CONSTRUCT_ONLY)!==0)return rebuild(e,i,c);l();try{e.widget.set_property(i,s)}finally{d()}}}function setEventHandler(e,t,n){e.widget?p(e,t,n):n&&assertSignalExists(e,t),n?e.listeners.set(t,n):e.listeners.delete(t)}function assertSignalExists(t,n){let r=m(n,t.descriptor.eventAliases).split(`::`)[0],i=t.descriptor.ctor().$gtype;if(S.signal_lookup(r,i)===0)throw e.unknownSignal(t.descriptor.gtype,n,r)}function setSlot(e,t){if(e.slot===t)return;let n=e.parent;if(!n||!e.widget){e.slot=t;return}let r=e.slot;replaceAt(e,n,()=>{e.slot=t},()=>{e.slot=r})}function replaceAt(e,t,n,r){a(t,e),e.attached=!1,n();try{attach(t,e)}catch(n){r();try{attach(t,e)}catch{}throw n}}function rebuild(e,t,n){let r=e.parent;if(e.widget){u(e);for(let t of childSnapshot(e))t.kind===`element`&&(a(e,t),t.attached=!1);r&&a(r,e)}e.widget=null,e.wrapper=null,e.attached=!1;try{materialize(e)}catch(i){t!==void 0&&(n===void 0?delete e.props[t]:e.props[t]=n);try{materialize(e),r&&attach(r,e)}catch{}throw i}r&&attach(r,e)}function setText(e,t){e.data=t,e.kind===`text`&&e.parent&&flushText(e.parent)}function setElementText(e,t){writeTextSink(e,t),e.textFromChildren=!1,destroyChildren(e)}function flushText(e){let t=``,n=!1;for(let r of childNodes(e))r.kind===`text`&&(t+=r.data,n=!0);if(t===``&&!e.textFromChildren)return;let r=e.descriptor.textSink;if(t===``&&r&&holdsElementInSetterSlot(e)){e.textFromChildren=!1,delete e.props[r];return}writeTextSink(e,t),e.textFromChildren=n}function*setterSlotChildren(e){for(let t of siblingsFrom(e.first,e))t.kind===`element`&&t.attached&&o(e,t)&&(yield t)}const holdsElementInSetterSlot=e=>!setterSlotChildren(e).next().done;function writeTextSink(t,n){let r=t.descriptor.textSink;if(!r)throw e.textNotAccepted(t.descriptor.gtype,n);materialize(t);let i=t.descriptor.children,a=i.kind===`single`?i.set:null;if(a&&!holdsOursInSlot(t,null)&&appOccupant(t.widget,t.descriptor,a))throw e.occupiedSlot(t.descriptor.gtype,`text`,a);let o=v(g(t.descriptor.ctor(),t.descriptor.gtype),t.descriptor.gtype,r);l();try{t.widget.set_property(r,h(o,n,t.descriptor.gtype))}finally{d()}t.props[r]=n;for(let e of setterSlotChildren(t))e.attached=!1}function*childNodes(e){yield*siblingsFrom(e.first,e)}function childSnapshot(e){let t=[];for(let n of siblingsFrom(e.first,e))t.push(n);return t}function*siblingsFrom(t,n){let r=0;for(let i=t;i;i=i.next){if(++r>1e5)throw e.siblingCycle(n.descriptor.gtype);yield i}}function link(e,t,n){t.parent=e,n?(t.prev=n.prev,t.next=n,n.prev?n.prev.next=t:e.first=t,n.prev=t):(t.prev=e.last,t.next=null,e.last?e.last.next=t:e.first=t,e.last=t)}function unlink(e){let t=e.parent;t&&(e.prev?e.prev.next=e.next:t.first=e.next,e.next?e.next.prev=e.prev:t.last=e.prev,e.prev=null,e.next=null,e.parent=null)}function attach(e,r){if(!e.widget)return;materialize(r),ensureWrapper(e,r),refuseOccupiedSlot(e,r);let i=e.foreign.length>0?e.foreign.filter(t=>t.get_parent()===e.widget):e.foreign,a=i.length>0?i[i.length-1]:null,o=i.length;for(let n of siblingsFrom(e.first,e)){if(n===r)break;n.kind!==`element`||!n.attached||(a=t(n),o+=1)}let s=[];for(let t of siblingsFrom(r.next,e))t.kind===`element`&&t.attached&&s.push(t);n({parent:e,child:r,prevWidget:a,index:o,following:s}),r.attached=!0}function refuseOccupiedSlot(t,n){let r=o(t,n);if(r&&!holdsOursInSlot(t,n.slot)&&appOccupant(t.widget,t.descriptor,r))throw e.occupiedSlot(t.descriptor.gtype,n.descriptor.gtype,r)}function holdsOursInSlot(e,t){for(let n of siblingsFrom(e.first,e))if(n.kind===`element`&&n.attached&&n.slot===t)return!0;return!1}function ensureWrapper(e,t){if(t.wrapper)return;let n=i(e.descriptor.children,t.widget);n&&(t.wrapper=n)}function insert(e,t,n=null){assertHostParent(t);let r=e.parent,i=e.next,a=n===e?i:n;e.parent&&remove(e),link(t,e,a);try{e.kind===`element`?attach(t,e):e.kind===`text`&&flushText(t)}catch(t){throw unlink(e),r&&restore(e,r,i),t}}function assertHostParent(t){if(!isHostElement(t))throw e.notAHostParent(describeValue(t))}function isHostElement(e){let t=e;return!!t&&t.kind===`element`&&typeof t.descriptor==`object`}function describeValue(e){if(e==null)return String(e);if(typeof e!=`object`)return`a ${typeof e}`;let t=e.tagName;if(typeof t==`string`)return`a DOM element <${t.toLowerCase()}>`;let n=e.constructor?.$gtype,r=n?S.type_name(n)??`an unnamed GType`:null;if(r!==null&&r!==`JSObject`)return r;let i=e.constructor?.name;return i&&i!==`Object`?`a ${i}`:`a plain object`}function restore(e,t,n){try{link(t,e,n),e.kind===`element`?attach(t,e):e.kind===`text`&&flushText(t)}catch{unlink(e)}}function remove(e){let t=e.parent;t&&e.kind===`element`&&(a(t,e),e.attached=!1,e.wrapper&&=(e.wrapper.set_child(null),null)),unlink(e),t&&e.kind===`text`&&flushText(t)}function destroyChildren(e){for(let t of childSnapshot(e))destroy(t)}function disconnectHandlers(e){u(e),e.listeners.clear()}function destroy(e){if(e.kind===`element`&&(destroyChildren(e),u(e),e.listeners.clear()),remove(e),e.kind===`element`){let t=e.widget;t&&typeof t.destroy==`function`&&typeof t.get_parent==`function`&&t.get_parent()===null&&t.destroy(),e.widget=null,e.wrapper=null,e.props={},e.layout=null,e.textFromChildren=!1,e.destroyed=!0}}function mountRoot(e,t){materialize(e),insert(e,adopt(t))}function widgetOf(t){if(t.kind!==`element`)throw e.notAnElement(t.kind);if(t.destroyed)throw e.destroyedNode(t.descriptor.gtype);return materialize(t)}const createDetachedContainer=()=>adopt(r());function adopt(t){let n=t.constructor.$gtype,r=x(n);if(!r)throw e.unknownTag(gtypeNameOf(t));return{kind:`element`,descriptor:r,widget:t,wrapper:null,slot:null,parent:null,prev:null,next:null,first:null,last:null,handlers:new Map,listeners:new Map,props:{},layout:null,textFromChildren:!1,attached:!0,destroyed:!1,foreign:adoptedChildren(t,r)}}function adoptedChildren(e,t){let n=s(t.children);if(n.length===0)return directChildren(e);let r=[];for(let i of n){let n=appOccupant(e,t,i);n&&r.push(n)}return r}function appOccupant(e,t,n){let r=c(e,n);if(!r)return null;let i=t.textSink;if(!i)return r;let a=e[i.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())];return typeof a==`string`&&a!==``?null:r}function directChildren(e){let t=[],n=e;if(typeof n.get_first_child!=`function`)return t;for(let e=n.get_first_child();e;e=e.get_next_sibling())t.push(e);return t}const gtypeNameOf=e=>S.type_name(e.constructor.$gtype),parentNode=e=>e.parent,firstChild=e=>e.first,nextSibling=e=>e.next,prevSibling=e=>e.prev;export{adopt,createAnchor,createDetachedContainer,createElement,createText,describeValue,destroy,destroyChildren,disconnectHandlers,firstChild,insert,isHostElement,isText,materialize,mountRoot,nextSibling,parentNode,prevSibling,remove,setElementText,setEventHandler,setProp,setSlot,setText,widgetOf};
1
+ import"./_virtual/_rolldown/runtime.js";import{err as e}from"./errors.js";import{addressOf as t,insertChild as n,makeDetachedContainer as r,makeWrapper as i,removeChild as a,setterSlotOf as o,setterSlots as s,slotOccupant as c}from"./policies.js";import{beginHostWrite as l,clearHandlers as u,endHostWrite as d,isEventProp as f,setHandler as p,toSignalName as m}from"./signals.js";import{coerce as h,paramSpecs as g,removedValue as _,requireSpec as v,toPropertyName as y}from"./props.js";import{lookupWidget as b,nearestRegistered as x}from"./registry.js";import S from"gi://GObject";function createElement(e,t){let n={kind:`element`,descriptor:b(e),widget:null,wrapper:null,slot:null,parent:null,prev:null,next:null,first:null,last:null,handlers:new Map,listeners:new Map,props:{},layout:null,textFromChildren:!1,attached:!1,destroyed:!1,foreign:[]};if(t)for(let[e,r]of Object.entries(t))setProp(n,e,r);return n}const createText=e=>({kind:`text`,data:e,parent:null,prev:null,next:null}),createAnchor=(e=``)=>({kind:`anchor`,data:e,parent:null,prev:null,next:null}),isText=e=>e.kind===`text`;function materialize(t){if(t.widget)return t.widget;for(let n of t.descriptor.requiresProps??[])if(!(n in t.props))throw e.missingConstructProp(t.descriptor.gtype,n);let n=t.descriptor.ctor(),r=g(n,t.descriptor.gtype),i={};for(let[e,n]of Object.entries(t.props))i[e]=h(v(r,t.descriptor.gtype,e),n,t.descriptor.gtype);l();try{t.widget=new n(i)}finally{d()}try{replayInto(t)}catch(e){u(t);for(let e of childSnapshot(t))e.kind===`element`&&e.attached&&(a(t,e),e.attached=!1);throw t.widget=null,t.wrapper=null,e}return t.widget}function replayInto(e){for(let[t,n]of e.listeners)p(e,t,n);for(let t of siblingsFrom(e.first,e))t.kind===`element`&&attach(e,t);e.textFromChildren&&flushText(e)}function setProp(e,t,n,r){if(f(t))return setEventHandler(e,t,n);if(t===`slot`)return setSlot(e,n);if(t===`layout`){let t=n,r=e.parent;if(!r||!e.widget){e.layout=t;return}let i=e.layout;replaceAt(e,r,()=>{e.layout=t},()=>{e.layout=i});return}let i=y(t),a=v(g(e.descriptor.ctor(),e.descriptor.gtype),e.descriptor.gtype,i),o=n==null,s=o?_(e.descriptor,a):h(a,n,e.descriptor.gtype),c=i in e.props?e.props[i]:void 0;if(o?delete e.props[i]:e.props[i]=n,e.widget){if((a.flags&S.ParamFlags.CONSTRUCT_ONLY)!==0)return rebuild(e,i,c);l();try{writeProperty(e.widget,i,s)}finally{d()}}}function writeProperty(e,t,n){if(!Array.isArray(n)){e.set_property(t,n);return}let r=t.replace(/-([a-z0-9])/g,(e,t)=>t.toUpperCase());e[r]=n}function setEventHandler(e,t,n){e.widget?p(e,t,n):n&&assertSignalExists(e,t),n?e.listeners.set(t,n):e.listeners.delete(t)}function assertSignalExists(t,n){let r=m(n,t.descriptor.eventAliases).split(`::`)[0],i=t.descriptor.ctor().$gtype;if(S.signal_lookup(r,i)===0)throw e.unknownSignal(t.descriptor.gtype,n,r)}function setSlot(e,t){if(e.slot===t)return;let n=e.parent;if(!n||!e.widget){e.slot=t;return}let r=e.slot;replaceAt(e,n,()=>{e.slot=t},()=>{e.slot=r})}function replaceAt(e,t,n,r){a(t,e),e.attached=!1,n();try{attach(t,e)}catch(n){r();try{attach(t,e)}catch{}throw n}}function rebuild(e,t,n){let r=e.parent;if(e.widget){u(e);for(let t of childSnapshot(e))t.kind===`element`&&(a(e,t),t.attached=!1);r&&a(r,e)}e.widget=null,e.wrapper=null,e.attached=!1;try{materialize(e)}catch(i){t!==void 0&&(n===void 0?delete e.props[t]:e.props[t]=n);try{materialize(e),r&&attach(r,e)}catch{}throw i}r&&attach(r,e)}function setText(e,t){e.data=t,e.kind===`text`&&e.parent&&flushText(e.parent)}function setElementText(e,t){writeTextSink(e,t),e.textFromChildren=!1,destroyChildren(e)}function flushText(e){let t=``,n=!1;for(let r of childNodes(e))r.kind===`text`&&(t+=r.data,n=!0);if(t===``&&!e.textFromChildren)return;let r=e.descriptor.textSink;if(t===``&&r&&holdsElementInSetterSlot(e)){e.textFromChildren=!1,delete e.props[r];return}writeTextSink(e,t),e.textFromChildren=n}function*setterSlotChildren(e){for(let t of siblingsFrom(e.first,e))t.kind===`element`&&t.attached&&o(e,t)&&(yield t)}const holdsElementInSetterSlot=e=>!setterSlotChildren(e).next().done;function writeTextSink(t,n){let r=t.descriptor.textSink;if(!r)throw e.textNotAccepted(t.descriptor.gtype,n);materialize(t);let i=t.descriptor.children,a=i.kind===`single`?i.set:null;if(a&&!holdsOursInSlot(t,null)&&appOccupant(t.widget,t.descriptor,a))throw e.occupiedSlot(t.descriptor.gtype,`text`,a);let o=v(g(t.descriptor.ctor(),t.descriptor.gtype),t.descriptor.gtype,r);l();try{t.widget.set_property(r,h(o,n,t.descriptor.gtype))}finally{d()}t.props[r]=n;for(let e of setterSlotChildren(t))e.attached=!1}function*childNodes(e){yield*siblingsFrom(e.first,e)}function childSnapshot(e){let t=[];for(let n of siblingsFrom(e.first,e))t.push(n);return t}function*siblingsFrom(t,n){let r=0;for(let i=t;i;i=i.next){if(++r>1e5)throw e.siblingCycle(n.descriptor.gtype);yield i}}function link(e,t,n){t.parent=e,n?(t.prev=n.prev,t.next=n,n.prev?n.prev.next=t:e.first=t,n.prev=t):(t.prev=e.last,t.next=null,e.last?e.last.next=t:e.first=t,e.last=t)}function unlink(e){let t=e.parent;t&&(e.prev?e.prev.next=e.next:t.first=e.next,e.next?e.next.prev=e.prev:t.last=e.prev,e.prev=null,e.next=null,e.parent=null)}function attach(e,r){if(!e.widget)return;materialize(r),ensureWrapper(e,r),refuseOccupiedSlot(e,r);let i=e.foreign.length>0?e.foreign.filter(t=>t.get_parent()===e.widget):e.foreign,a=i.length>0?i[i.length-1]:null,o=i.length;for(let n of siblingsFrom(e.first,e)){if(n===r)break;n.kind!==`element`||!n.attached||(a=t(n),o+=1)}let s=[];for(let t of siblingsFrom(r.next,e))t.kind===`element`&&t.attached&&s.push(t);n({parent:e,child:r,prevWidget:a,index:o,following:s}),r.attached=!0}function refuseOccupiedSlot(t,n){let r=o(t,n);if(r&&!holdsOursInSlot(t,n.slot)&&appOccupant(t.widget,t.descriptor,r))throw e.occupiedSlot(t.descriptor.gtype,n.descriptor.gtype,r)}function holdsOursInSlot(e,t){for(let n of siblingsFrom(e.first,e))if(n.kind===`element`&&n.attached&&n.slot===t)return!0;return!1}function ensureWrapper(e,t){if(t.wrapper)return;let n=i(e.descriptor.children,t.widget);n&&(t.wrapper=n)}function insert(e,t,n=null){assertHostParent(t);let r=e.parent,i=e.next,a=n===e?i:n;e.parent&&remove(e),link(t,e,a);try{e.kind===`element`?attach(t,e):e.kind===`text`&&flushText(t)}catch(t){throw unlink(e),r&&restore(e,r,i),t}}function assertHostParent(t){if(!isHostElement(t))throw e.notAHostParent(describeValue(t))}function isHostElement(e){let t=e;return!!t&&t.kind===`element`&&typeof t.descriptor==`object`}function describeValue(e){if(e==null)return String(e);if(typeof e!=`object`)return`a ${typeof e}`;let t=e.tagName;if(typeof t==`string`)return`a DOM element <${t.toLowerCase()}>`;let n=e.constructor?.$gtype,r=n?S.type_name(n)??`an unnamed GType`:null;if(r!==null&&r!==`JSObject`)return r;let i=e.constructor?.name;return i&&i!==`Object`?`a ${i}`:`a plain object`}function restore(e,t,n){try{link(t,e,n),e.kind===`element`?attach(t,e):e.kind===`text`&&flushText(t)}catch{unlink(e)}}function restoreTextSink(e){let t=e.descriptor.textSink;if(!t||e.destroyed||!e.widget)return;let n=e.descriptor.children;if(n.kind!==`single`||holdsOursInSlot(e,null)||c(e.widget,n.set))return;let r=t.replace(/-([a-z])/g,(e,t)=>t.toUpperCase()),i=e.props[t]??e.props[r];if(typeof i!=`string`||i===``)return;let a=v(g(e.descriptor.ctor(),e.descriptor.gtype),e.descriptor.gtype,t);l();try{e.widget.set_property(t,h(a,i,e.descriptor.gtype))}finally{d()}}function remove(e){let t=e.parent;t&&e.kind===`element`&&(a(t,e),e.attached=!1,e.wrapper&&=(e.wrapper.set_child(null),null),restoreTextSink(t)),unlink(e),t&&e.kind===`text`&&flushText(t)}function destroyChildren(e){for(let t of childSnapshot(e))destroy(t)}function disconnectHandlers(e){u(e),e.listeners.clear()}function destroy(e){if(e.kind===`element`&&(destroyChildren(e),u(e),e.listeners.clear()),remove(e),e.kind===`element`){let t=e.widget;t&&typeof t.destroy==`function`&&typeof t.get_parent==`function`&&t.get_parent()===null&&t.destroy(),e.widget=null,e.wrapper=null,e.props={},e.layout=null,e.textFromChildren=!1,e.destroyed=!0}}function mountRoot(e,t){materialize(e),insert(e,adopt(t))}function widgetOf(t){if(t.kind!==`element`)throw e.notAnElement(t.kind);if(t.destroyed)throw e.destroyedNode(t.descriptor.gtype);return materialize(t)}const createDetachedContainer=()=>adopt(r());function adopt(t){let n=t.constructor.$gtype,r=x(n);if(!r)throw e.unknownTag(gtypeNameOf(t));return{kind:`element`,descriptor:r,widget:t,wrapper:null,slot:null,parent:null,prev:null,next:null,first:null,last:null,handlers:new Map,listeners:new Map,props:{},layout:null,textFromChildren:!1,attached:!0,destroyed:!1,foreign:adoptedChildren(t,r)}}function adoptedChildren(e,t){let n=s(t.children);if(n.length===0)return directChildren(e);let r=[];for(let i of n){let n=appOccupant(e,t,i);n&&r.push(n)}return r}function appOccupant(e,t,n){let r=c(e,n);if(!r)return null;let i=t.textSink;if(!i)return r;let a=e[i.replace(/-([a-z])/g,(e,t)=>t.toUpperCase())];return typeof a==`string`&&a!==``?null:r}function directChildren(e){let t=[],n=e;if(typeof n.get_first_child!=`function`)return t;for(let e=n.get_first_child();e;e=e.get_next_sibling())t.push(e);return t}const gtypeNameOf=e=>S.type_name(e.constructor.$gtype),parentNode=e=>e.parent,firstChild=e=>e.first,nextSibling=e=>e.next,prevSibling=e=>e.prev;export{adopt,createAnchor,createDetachedContainer,createElement,createText,describeValue,destroy,destroyChildren,disconnectHandlers,firstChild,insert,isHostElement,isText,materialize,mountRoot,nextSibling,parentNode,prevSibling,remove,setElementText,setEventHandler,setProp,setSlot,setText,widgetOf};
package/lib/esm/probe.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{installDiagnosticsGate as e}from"./conformance/diagnostics.js";import t from"gi://Gtk?version=4.0";import n from"gi://Adw?version=1";import r from"gi://GLib";import i from"system";function probeEnabled(){let e=r.getenv(`GJSIFY_HOST_PROBE`);if(e===null)return!1;let t=e.toLowerCase();return t!==``&&t!==`0`&&t!==`false`}const a=`<gtk-host probe: recorder self-check>`;function recorderIsHonest(e,t){let n=t.length;e(a,!1);let r=t.length===n+1&&t[n]===a;t.length=n,e(a,!0);let i=t.length===n;return t.length=n,r&&i?null:r?`the probe recorder records a PASSING check as a failure, so every assertion below is a false alarm`:`the probe recorder did not record a failed check, so every assertion below is unverifiable`}function runHostProbe(t,n=null){let r=t.label??`PROBE`,i=e();i.reset();let a=[],check=(e,t)=>{t||a.push(e)},o=recorderIsHonest(check,a);o!==null&&a.push(o);let s={};try{s=t.assert(t.build(n),check)}catch(e){a.push(`threw: ${e.message}`)}let c=i.seen.length;check(`no GTK diagnostics (saw ${c})`,c===0);let l=JSON.stringify({...a.length>0?{failures:a}:{},diagnostics:c,...s});return print(`${r}: ${a.length>0?`FAIL`:`PASS`} ${l}`),+(a.length>0)}async function runHostProbeApp(e){if(probeEnabled())return t.init(),i.exit(runHostProbe(e,null));let r=new n.Application({application_id:e.applicationId});r.connect(`activate`,()=>{let t=runHostProbe(e,r);t!==0&&i.exit(t),e.present(e.build(r))}),await r.runAsync([])}export{probeEnabled,runHostProbe,runHostProbeApp};
1
+ import"./_virtual/_rolldown/runtime.js";import{installDiagnosticsGate as e}from"./conformance/diagnostics.js";import t from"gi://Gtk?version=4.0";import n from"gi://Adw?version=1";import r from"gi://GLib";import i from"system";function probeEnabled(){let e=r.getenv(`GJSIFY_HOST_PROBE`);if(e===null)return!1;let t=e.toLowerCase();return t!==``&&t!==`0`&&t!==`false`}const a=`<gtk-host probe: recorder self-check>`;function recorderIsHonest(e,t){let n=t.length;e(a,!1);let r=t.length===n+1&&t[n]===a;t.length=n,e(a,!0);let i=t.length===n;return t.length=n,r&&i?null:r?`the probe recorder records a PASSING check as a failure, so every assertion below is a false alarm`:`the probe recorder did not record a failed check, so every assertion below is unverifiable`}async function runHostProbe(t){let n=t.label??`PROBE`,r=e();r.reset();let i=[],check=(e,t)=>{t||i.push(e)},a=recorderIsHonest(check,i);a!==null&&i.push(a);let o={};try{let e=t.build(null);o=await t.assert(e,check),t.teardown?.(e)}catch(e){i.push(`threw: ${e.message}`)}let s=r.seen.length;check(`no GTK diagnostics (saw ${s})`,s===0);let c=JSON.stringify({...i.length>0?{failures:i}:{},diagnostics:s,...o});return print(`${n}: ${i.length>0?`FAIL`:`PASS`} ${c}`),+(i.length>0)}async function runHostProbeApp(e){if(probeEnabled())return t.init(),i.exit(await runHostProbe(e));let r=new n.Application({application_id:e.applicationId});r.connect(`activate`,()=>{r.hold(),(async()=>{try{let t=await runHostProbe(e);if(t!==0)return i.exit(t);e.present(e.build(r))}catch(t){return printerr(`JS ERROR: ${e.applicationId} probe threw: ${String(t)}`),i.exit(1)}finally{r.release()}})()}),await r.runAsync([])}export{probeEnabled,runHostProbe,runHostProbeApp};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";var UnknownUtilityError=class extends Error{constructor(e,t){super(`@gjsify/gtk-host/style: "${e}" — ${t}`),this.name=`UnknownUtilityError`,this.utility=e}};export{UnknownUtilityError};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";const e=[[`background-color`,`rgb(255 0 0)`],[`color`,`#112233`],[`opacity`,`0.7`],[`border-radius`,`8px`],[`border-top-left-radius`,`8px`],[`border-top-right-radius`,`8px`],[`border-bottom-left-radius`,`8px`],[`border-bottom-right-radius`,`8px`],[`border-width`,`1px`],[`border-top-width`,`1px`],[`border-right-width`,`1px`],[`border-bottom-width`,`1px`],[`border-left-width`,`1px`],[`border-style`,`solid`],[`border-color`,`#abcdef`],[`border`,`1px solid #abcdef`],[`border-bottom`,`1px solid #abcdef`],[`box-shadow`,`0 1px 2px rgba(0,0,0,0.2)`],[`font-size`,`14px`],[`font-weight`,`700`],[`font-family`,`Cantarell`],[`font-style`,`italic`],[`letter-spacing`,`1px`],[`line-height`,`1.4`],[`text-decoration-line`,`underline`],[`text-transform`,`uppercase`],[`margin`,`8px`],[`margin-top`,`8px`],[`margin-right`,`8px`],[`margin-bottom`,`8px`],[`margin-left`,`8px`],[`padding`,`8px`],[`padding-top`,`8px`],[`padding-right`,`8px`],[`padding-bottom`,`8px`],[`padding-left`,`8px`],[`min-width`,`10px`],[`min-height`,`10px`],[`transform`,`scale(1.1)`],[`transition`,`all 100ms`],[`caret-color`,`#ff0000`],[`outline-style`,`solid`]],t=[[`text-align`,`center`],[`display`,`flex`],[`flex-direction`,`row`],[`gap`,`8px`],[`position`,`absolute`],[`width`,`100px`],[`height`,`100px`],[`justify-content`,`center`],[`align-items`,`center`],[`overflow`,`hidden`],[`z-index`,`1`],[`top`,`0`],[`right`,`0`],[`bottom`,`0`],[`left`,`0`],[`margin-start`,`8px`],[`margin-end`,`8px`],[`padding-start`,`8px`],[`padding-end`,`8px`]],n=new Set(e.map(([e])=>e));export{e as GTK_CSS_PROBES,n as GTK_CSS_PROPERTIES,t as NOT_GTK_CSS};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";const e=[[`GtkWidget`,`margin-top`,`gint`],[`GtkWidget`,`margin-bottom`,`gint`],[`GtkWidget`,`margin-start`,`gint`],[`GtkWidget`,`margin-end`,`gint`],[`GtkWidget`,`halign`,`GtkAlign`],[`GtkWidget`,`valign`,`GtkAlign`],[`GtkWidget`,`hexpand`,`gboolean`],[`GtkWidget`,`vexpand`,`gboolean`],[`GtkWidget`,`width-request`,`gint`],[`GtkWidget`,`height-request`,`gint`],[`GtkWidget`,`overflow`,`GtkOverflow`],[`GtkWidget`,`visible`,`gboolean`],[`GtkBox`,`orientation`,`GtkOrientation`],[`GtkBox`,`spacing`,`gint`],[`GtkLabel`,`xalign`,`gfloat`],[`GtkLabel`,`justify`,`GtkJustification`]],t=[[`GtkWidget`,`margin-left`],[`GtkWidget`,`margin-right`],[`GtkWidget`,`padding`],[`GtkWidget`,`padding-top`],[`GtkBox`,`padding`],[`GtkBox`,`margin-left`],[`GtkWidget`,`gap`],[`GtkBox`,`gap`],[`GtkWidget`,`flex`],[`GtkWidget`,`orientation`],[`GtkWidget`,`spacing`],[`GtkLabel`,`orientation`],[`GtkLabel`,`spacing`],[`GtkCenterBox`,`spacing`],[`GtkFlowBox`,`spacing`],[`GtkWidget`,`xalign`],[`GtkBox`,`xalign`]],n=new Set(e.map(([,e])=>e));export{n as GTK_WIDGET_PROPERTIES,e as GTK_WIDGET_PROPERTY_PROBES,t as NOT_GTK_WIDGET_PROPERTIES};
@@ -0,0 +1 @@
1
+ import{UnknownUtilityError as e}from"./errors.js";import{GTK_CSS_PROBES as t,GTK_CSS_PROPERTIES as n,NOT_GTK_CSS as r}from"./gtk-css.js";import{GTK_WIDGET_PROPERTIES as i,GTK_WIDGET_PROPERTY_PROBES as a,NOT_GTK_WIDGET_PROPERTIES as o}from"./gtk-props.js";import{MINIMAL_TOKENS as s,lookupToken as c,requireToken as l}from"./tokens.js";import{PAINT_PROPERTIES as u,partitionPaint as d,resolvePaintUtility as f}from"./paint.js";import{LAYOUT_PROPERTIES as p,partitionLayout as m,resolveLayoutUtility as h}from"./layout.js";import{partition as g,resolveUtilities as _,resolveUtility as v}from"./resolve.js";import{StyleSheet as y,StyleSheetError as b,VARIANT_PSEUDO as x}from"./sheet.js";export{t as GTK_CSS_PROBES,n as GTK_CSS_PROPERTIES,i as GTK_WIDGET_PROPERTIES,a as GTK_WIDGET_PROPERTY_PROBES,p as LAYOUT_PROPERTIES,s as MINIMAL_TOKENS,r as NOT_GTK_CSS,o as NOT_GTK_WIDGET_PROPERTIES,u as PAINT_PROPERTIES,y as StyleSheet,b as StyleSheetError,e as UnknownUtilityError,x as VARIANT_PSEUDO,c as lookupToken,g as partition,m as partitionLayout,d as partitionPaint,l as requireToken,h as resolveLayoutUtility,f as resolvePaintUtility,_ as resolveUtilities,v as resolveUtility};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{UnknownUtilityError as e}from"./errors.js";import{GTK_CSS_PROPERTIES as t}from"./gtk-css.js";import{GTK_WIDGET_PROPERTIES as n}from"./gtk-props.js";import{requireToken as r}from"./tokens.js";const i="GTK expresses main-axis growth as the boolean `hexpand`/`vexpand`, so there is no growth factor, no shrink factor and no flex basis to carry. `flex-1` is the only spelling with a GTK meaning",a="GTK's box gives leftover main-axis space to the children that expand; it has no per-gap distribution mode, and `Gtk.CenterBox` has exactly three slots. Unlike `justify-between`, no child count makes this expressible, so it is refused here rather than deferred to the shadow tree",o="scrolling is a WIDGET on GTK, not an overflow mode: wrap the element in a `Gtk.ScrolledWindow` (React Native `ScrollView`). `Gtk.Overflow` has exactly VISIBLE and HIDDEN (measured)",s="GTK has no percentage size: a widget requests a MINIMUM in pixels (`width-request`) or expands to fill what it is given (`hexpand`). `w-full`/`h-full` are the only fractions with an exact GTK meaning",c={m:[`marginTop`,`marginRight`,`marginBottom`,`marginLeft`],mt:[`marginTop`],mr:[`marginRight`],mb:[`marginBottom`],ml:[`marginLeft`],mx:[`marginLeft`,`marginRight`],my:[`marginTop`,`marginBottom`],ms:[`marginStart`],me:[`marginEnd`]},l={p:[`paddingTop`,`paddingRight`,`paddingBottom`,`paddingLeft`],pt:[`paddingTop`],pr:[`paddingRight`],pb:[`paddingBottom`],pl:[`paddingLeft`],px:[`paddingLeft`,`paddingRight`],py:[`paddingTop`,`paddingBottom`]},u={inset:[`top`,`right`,`bottom`,`left`],top:[`top`],right:[`right`],bottom:[`bottom`],left:[`left`]},d={start:`flex-start`,end:`flex-end`,center:`center`,stretch:`stretch`,baseline:`baseline`},f={start:`flex-start`,end:`flex-end`,center:`center`,between:`space-between`,around:`space-around`,evenly:`space-evenly`},fill=(e,t)=>{let n={};for(let r of e)n[r]=t;return n};function requireSize(t,n,r,i){let a=t[n],o=a?.[r]??t.spacing?.[r];if(o!==void 0)return o;let s=[...new Set([...Object.keys(a??{}),...Object.keys(t.spacing??{})])].sort().join(`, `);throw new e(i,`"${r}" is in neither the ${n} nor the spacing scale. Known: ${s===``?`(neither scale is configured)`:s}`)}function resolveLayoutUtility(t,n){switch(t){case`flex-1`:case`grow`:return{flexGrow:`1`};case`flex-row`:return{flexDirection:`row`};case`flex-col`:return{flexDirection:`column`};case`flex-nowrap`:return{};case`flex-wrap`:case`flex-wrap-reverse`:throw new e(t,"a `Gtk.Box` cannot wrap. Wrapping is a different widget — `Gtk.FlowBox` — and swapping the widget under an element is L2 widget selection, not a style property");case`flex-row-reverse`:case`flex-col-reverse`:throw new e(t,"`Gtk.Box` has no reversed orientation (`GtkOrientation` is HORIZONTAL and VERTICAL, measured). Reverse the children instead — the shadow tree is the order");case`absolute`:return{position:`absolute`};case`relative`:return{position:`relative`};case`hidden`:return{display:`none`};case`overflow-hidden`:return{overflow:`hidden`};case`overflow-visible`:return{overflow:`visible`};case`overflow-scroll`:case`overflow-auto`:throw new e(t,o);case`w-full`:return{width:`100%`};case`h-full`:return{height:`100%`}}let p=t.indexOf(`-`),m=p===-1?t:t.slice(0,p),h=p===-1?``:t.slice(p+1),g=c[m];if(g!==void 0)return fill(g,r(n.spacing,h,t,`spacing`));let _=l[m];if(_!==void 0)return fill(_,r(n.spacing,h,t,`spacing`));let v=u[m];if(v!==void 0)return fill(v,r(n.spacing,h,t,`spacing`));switch(m){case`ps`:case`pe`:throw new e(t,"GTK has no logical padding in either mechanism — `padding-start` is not a CSS property (measured) and no GTK class installs a padding property at all — so it cannot be expressed even approximately. Use `pl-*`/`pr-*`");case`flex`:case`grow`:case`shrink`:case`basis`:throw new e(t,i);case`items`:{let e=d[h];if(e===void 0)throw unknownToken(t,h,d);return{alignItems:e}}case`self`:{let n=h===`baseline`?void 0:d[h];if(n===void 0)throw h===`baseline`?new e(t,"a baseline is shared by a whole row, so GTK carries it on the BOX as `baseline-position` (measured on `Gtk.Box`, absent from `Gtk.Widget`) rather than per child. Use `items-baseline` on the parent"):unknownToken(t,h,d);return{alignSelf:n}}case`justify`:{if(h===`around`||h===`evenly`)throw new e(t,a);let n=f[h];if(n===void 0)throw unknownToken(t,h,f);return{justifyContent:n}}case`gap`:return h.startsWith(`x-`)?{columnGap:r(n.spacing,h.slice(2),t,`spacing`)}:h.startsWith(`y-`)?{rowGap:r(n.spacing,h.slice(2),t,`spacing`)}:{gap:r(n.spacing,h,t,`spacing`)};case`w`:case`h`:{if(h.includes(`/`))throw new e(t,s);let r=m===`w`?`width`:`height`;return{[r]:requireSize(n,r,h,t)}}case`text`:if(h===`left`||h===`center`||h===`right`||h===`justify`)return{textAlign:h};if(h===`start`||h===`end`)throw new e(t,"GTK aligns text PHYSICALLY: `Gtk.Label:xalign` is 0 = left, and `GtkJustification` has exactly LEFT, RIGHT, CENTER and FILL (measured). There is no start/end spelling to map onto. Use `text-left`/`text-right`");return null}return null}const unknownToken=(t,n,r)=>new e(t,`"${n}" is not a value of this family. Known: ${Object.keys(r).join(`, `)}`);function emitCss(n,r,i){if(!t.has(r))throw new e(r,`is not a property GTK accepts in a stylesheet — see gtk-css.ts`);n.css.push(`${r}: ${i}`)}function emitProp(t,r,i){if(!n.has(r))throw new e(r,`is not a property any GTK class installs — see gtk-props.ts`);t.props[r]=i}const p=/^(-?\d+(?:\.\d+)?)(?:px)?$/;function pixels(t,n){let r=p.exec(t.trim());if(r===null)throw new e(n,`"${t}" is not a pixel length. GTK's "${n}" is a gint of device pixels (measured, gtk-props.ts) with no unit conversion behind it, so only "12px" and "12" can be stored`);return Math.round(Number(r[1]))}function oneOf(t,n,r){if(n.includes(t))return t;throw new e(r,`"${t}" is not a value this partition routes. Known: ${n.join(`, `)}`)}const edgeRoute=t=>(n,r)=>{let i=r.intent.overlay;if(i===void 0||i.role!==`child`)throw new e(t,"only offsets an element that is absolutely positioned, and this one is not. GTK has no relative offset: add `absolute` (which makes the parent a `Gtk.Overlay`), or express the distance as a margin");i.edges[t]=pixels(n,t)},m={marginTop:(e,t)=>emitProp(t,`margin-top`,pixels(e,`margin-top`)),marginBottom:(e,t)=>emitProp(t,`margin-bottom`,pixels(e,`margin-bottom`)),marginStart:(e,t)=>emitProp(t,`margin-start`,pixels(e,`margin-start`)),marginEnd:(e,t)=>emitProp(t,`margin-end`,pixels(e,`margin-end`)),marginLeft:(e,t)=>emitCss(t,`margin-left`,e),marginRight:(e,t)=>emitCss(t,`margin-right`,e),paddingTop:(e,t)=>emitCss(t,`padding-top`,e),paddingRight:(e,t)=>emitCss(t,`padding-right`,e),paddingBottom:(e,t)=>emitCss(t,`padding-bottom`,e),paddingLeft:(e,t)=>emitCss(t,`padding-left`,e),flexDirection:(e,t)=>emitProp(t,`orientation`,oneOf(e,[`row`,`column`],`flexDirection`)===`row`?`horizontal`:`vertical`),flexGrow:(t,n)=>{if(t!==`1`)throw new e(`flexGrow`,i);n.intent.expand=`main-axis`},alignItems:(e,t)=>{t.intent.alignChildren=oneOf(e,[`flex-start`,`flex-end`,`center`,`stretch`,`baseline`],`alignItems`)},justifyContent:(t,n)=>{if(t===`space-around`||t===`space-evenly`)throw new e(`justifyContent`,a);n.intent.distribute=oneOf(t,[`flex-start`,`flex-end`,`center`,`space-between`],`justifyContent`)},alignSelf:(e,t)=>{t.intent.alignSelf=oneOf(e,[`flex-start`,`flex-end`,`center`,`stretch`,`baseline`],`alignSelf`)},gap:(e,t)=>emitProp(t,`spacing`,pixels(e,`spacing`)),columnGap:(e,t)=>{t.intent.axisSpacing={axis:`horizontal`,pixels:pixels(e,`spacing`)}},rowGap:(e,t)=>{t.intent.axisSpacing={axis:`vertical`,pixels:pixels(e,`spacing`)}},width:(e,t)=>e===`100%`?emitProp(t,`hexpand`,!0):emitProp(t,`width-request`,pixels(percentGuard(e,`width`),`width-request`)),height:(e,t)=>e===`100%`?emitProp(t,`vexpand`,!0):emitProp(t,`height-request`,pixels(percentGuard(e,`height`),`height-request`)),overflow:(t,n)=>{if(t===`scroll`)throw new e(`overflow`,o);emitProp(n,`overflow`,oneOf(t,[`visible`,`hidden`],`overflow`))},display:(e,t)=>emitProp(t,`visible`,oneOf(e,[`flex`,`none`],`display`)===`flex`),position:(e,t)=>{t.intent.overlay=oneOf(e,[`relative`,`absolute`],`position`)===`absolute`?{role:`child`,edges:{}}:{role:`context`}},top:edgeRoute(`top`),right:edgeRoute(`right`),bottom:edgeRoute(`bottom`),left:edgeRoute(`left`),textAlign:(e,t)=>{t.intent.textAlign=oneOf(e,[`left`,`center`,`right`,`justify`],`textAlign`)}},h=new Set(Object.keys(m));function percentGuard(t,n){if(t.endsWith(`%`))throw new e(n,s);return t}function partitionLayout(t){let n=t.marginLeft!==void 0||t.marginRight!==void 0,r=t.marginStart!==void 0||t.marginEnd!==void 0;if(n&&r)throw new e(`margin`,"a physical horizontal margin (`ml-*`/`mr-*`/`mx-*`/`m-*`) becomes GTK CSS and a logical one (`ms-*`/`me-*`) becomes the widget property; GTK applies BOTH and they ADD, where CSS would have let one win. Spell the horizontal margin one way");let i=[`gap`,`columnGap`,`rowGap`].filter(e=>t[e]!==void 0);if(i.length>1)throw new e(i.join(` + `),"a `Gtk.Box` has ONE `spacing` (measured), so two gap spellings on one element ask for two spacings. Keep one");let a={css:[],props:{},intent:{}};for(let e of Object.keys(m)){let n=t[e];n!==void 0&&m[e](n,a)}return a}export{h as LAYOUT_PROPERTIES,partitionLayout,resolveLayoutUtility};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{UnknownUtilityError as e}from"./errors.js";import{GTK_CSS_PROPERTIES as t}from"./gtk-css.js";import{lookupToken as n,requireToken as r}from"./tokens.js";const i={backgroundColor:`background-color`,color:`color`,opacity:`opacity`,borderRadius:`border-radius`,borderTopLeftRadius:`border-top-left-radius`,borderTopRightRadius:`border-top-right-radius`,borderBottomLeftRadius:`border-bottom-left-radius`,borderBottomRightRadius:`border-bottom-right-radius`,borderWidth:`border-width`,borderTopWidth:`border-top-width`,borderRightWidth:`border-right-width`,borderBottomWidth:`border-bottom-width`,borderLeftWidth:`border-left-width`,borderColor:`border-color`,fontSize:`font-size`,fontWeight:`font-weight`,fontFamily:`font-family`,fontStyle:`font-style`,letterSpacing:`letter-spacing`,lineHeight:`line-height`,textDecorationLine:`text-decoration-line`,textTransform:`text-transform`},withAlpha=(t,n)=>{let r=Number(n);if(!Number.isFinite(r)||r<0||r>100)throw new e(`${t}/${n}`,`the alpha modifier must be 0–100, got "${n}"`);return`alpha(${t}, ${r/100})`};function resolvePaintUtility(t,i){if(t.includes(`:`))throw new e(t,`strip the variant prefix before resolving; this resolves the utility itself`);let c=t.lastIndexOf(`/`),l=c===-1?void 0:t.slice(c+1),u=c===-1?t:t.slice(0,c),colour=e=>l===void 0?e:withAlpha(e,l),noAlpha=()=>{if(l!==void 0)throw new e(t,`an alpha modifier only applies to a colour utility`)};switch(u){case`underline`:return noAlpha(),{textDecorationLine:`underline`};case`line-through`:return noAlpha(),{textDecorationLine:`line-through`};case`no-underline`:return noAlpha(),{textDecorationLine:`none`};case`uppercase`:return noAlpha(),{textTransform:`uppercase`};case`lowercase`:return noAlpha(),{textTransform:`lowercase`};case`capitalize`:return noAlpha(),{textTransform:`capitalize`};case`normal-case`:return noAlpha(),{textTransform:`none`};case`italic`:return noAlpha(),{fontStyle:`italic`};case`not-italic`:return noAlpha(),{fontStyle:`normal`};case`rounded`:return noAlpha(),{borderRadius:r(i.borderRadius,`DEFAULT`,t,`borderRadius`)};case`border`:return noAlpha(),{borderWidth:r(i.borderWidth,`DEFAULT`,t,`borderWidth`)}}let d=u.indexOf(`-`),f=d===-1?u:u.slice(0,d),p=d===-1?``:u.slice(d+1);switch(f){case`bg`:return{backgroundColor:colour(r(i.colors,p,t,`colors`))};case`opacity`:return noAlpha(),{opacity:r(i.opacity,p,t,`opacity`)};case`rounded`:{noAlpha();let e=a[p];return e===void 0?{borderRadius:r(i.borderRadius,p,t,`borderRadius`)}:{[e]:r(i.borderRadius,`DEFAULT`,t,`borderRadius`)}}case`border`:{let a=o[p];if(a!==void 0)return noAlpha(),{[a]:r(i.borderWidth,`DEFAULT`,t,`borderWidth`)};let s=n(i.borderWidth,p),c=n(i.colors,p);if(s!==void 0&&c!==void 0)throw new e(t,`"${p}" is in both the borderWidth and colors scales, so this utility is ambiguous`);if(s!==void 0)return noAlpha(),{borderWidth:s};if(c!==void 0)return{borderColor:colour(c)};throw new e(t,`"${p}" is in neither the borderWidth nor the colors scale`)}case`text`:{if(s.has(p))return null;let r=n(i.fontSize,p),a=n(i.colors,p);if(r!==void 0&&a!==void 0)throw new e(t,`"${p}" is in both the fontSize and colors scales, so this utility is ambiguous`);if(r!==void 0)return noAlpha(),{fontSize:r};if(a!==void 0)return{color:colour(a)};throw new e(t,`"${p}" is in neither the fontSize nor the colors scale`)}case`font`:{noAlpha();let r=n(i.fontWeight,p),a=n(i.fontFamily,p);if(r!==void 0&&a!==void 0)throw new e(t,`"${p}" is in both the fontWeight and fontFamily scales, so this utility is ambiguous`);if(r!==void 0)return{fontWeight:r};if(a!==void 0)return{fontFamily:a};throw new e(t,`"${p}" is in neither the fontWeight nor the fontFamily scale`)}case`tracking`:return noAlpha(),{letterSpacing:r(i.letterSpacing,p,t,`letterSpacing`)};case`leading`:return noAlpha(),{lineHeight:r(i.lineHeight,p,t,`lineHeight`)}}return null}const a={tl:`borderTopLeftRadius`,tr:`borderTopRightRadius`,bl:`borderBottomLeftRadius`,br:`borderBottomRightRadius`},o={t:`borderTopWidth`,r:`borderRightWidth`,b:`borderBottomWidth`,l:`borderLeftWidth`},s=new Set([`left`,`center`,`right`,`justify`,`start`,`end`]),c=new Set(Object.keys(i));function partitionPaint(n){let r=[];for(let[a,o]of Object.entries(n)){if(o===void 0)continue;let n=i[a];if(n===void 0)throw new e(a,`is not a property the paint partition routes`);if(!t.has(n))throw new e(a,`maps to "${n}", which GTK does not accept — see gtk-css.ts`);r.push(`${n}: ${o}`)}let a=r.some(e=>/^border(-(top|right|bottom|left))?-width:/.test(e)),o=r.some(e=>/^border(-(top|right|bottom|left))?-style:/.test(e));return a&&!o&&r.push(`border-style: solid`),r}export{i as CSS_NAME,c as PAINT_PROPERTIES,partitionPaint,resolvePaintUtility};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{UnknownUtilityError as e}from"./errors.js";import{PAINT_PROPERTIES as t,partitionPaint as n,resolvePaintUtility as r}from"./paint.js";import{LAYOUT_PROPERTIES as i,partitionLayout as a,resolveLayoutUtility as o}from"./layout.js";function resolveUtility(t,n){let i=r(t,n);if(i!==null)return i;let a=o(t,n);if(a!==null)return a;throw new e(t,`is not a utility this vocabulary declares`)}function resolveUtilities(e,t){let n={};for(let r of e)Object.assign(n,resolveUtility(r,t));return n}function partition(r){let o={},s={};for(let[n,a]of Object.entries(r)){if(a===void 0)continue;let r=t.has(n)?o:i.has(n)?s:null;if(r===null)throw new e(n,`is not a property the style partition routes`);r[n]=a}let c=a(s);return{css:[...n(o),...c.css],props:c.props,intent:c.intent}}export{partition,resolveUtilities,resolveUtility};
@@ -0,0 +1,3 @@
1
+ import"../_virtual/_rolldown/runtime.js";import e from"gi://Gtk?version=4.0";import t from"gi://Gdk?version=4.0";const n={active:`:active`,hover:`:hover`,focus:`:focus`,disabled:`:disabled`};var StyleSheetError=class extends Error{constructor(e){super(`@gjsify/gtk-host/style: ${e}`),this.name=`StyleSheetError`}};function hashName(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619)>>>0;return`gjsify-s${t.toString(36)}`}const r=`gjsify-probe-8f3a91c4`,i=`.${r} { color: rgb(1 2 3); }`;var StyleSheet=class{#e=new e.CssProvider;#t=new Map;#n=!1;#r=!1;#i;constructor(e={}){this.#i=e}classFor(e,t={}){if(e.length===0&&Object.keys(t).length===0)throw new StyleSheetError(`classFor was given no declarations — an empty class would name nothing`);let r=e.join(`; `),i=[],a=hashName(`${r}|${Object.entries(t).map(([e,t])=>`${e}{${t.join(`; `)}}`).sort().join(``)}`);if(this.#t.get(a)!==void 0)return a;e.length>0&&i.push(`.${a} { ${r}; }`);for(let[e,r]of Object.entries(t)){let t=n[e];if(t===void 0)throw new StyleSheetError(`"${e}:" is not a variant this sheet can express. Known: ${Object.keys(n).sort().join(`, `)}. A variant becomes a GTK CSS pseudo-class, so one without a pseudo-class equivalent needs a different mechanism.`);r.length!==0&&i.push(`.${a}${t} { ${r.join(`; `)}; }`)}let o=i.join(`
2
+ `);return this.#o(o),this.#t.set(a,o),this.#a(),a}toString(){return[...this.#t.values()].join(`
3
+ `)}get size(){return this.#t.size}flush(){if(this.#r=!1,this.#e.load_from_string(this.toString()),this.#n)return;let n=this.#i.display??t.Display.get_default();if(n===null)throw new StyleSheetError(`there is no Gdk display to install the stylesheet on. Construct the sheet after Gtk.init(), or pass one explicitly.`);e.StyleContext.add_provider_for_display(n,this.#e,this.#i.priority??e.STYLE_PROVIDER_PRIORITY_APPLICATION),this.#n=!0}#a(){this.#r||(this.#r=!0,queueMicrotask(()=>{this.#r&&this.flush()}))}#o(t){let n=new e.CssProvider,a=[],o=n.connect(`parsing-error`,(e,t,n)=>{a.push(n.message)});if(n.load_from_string(`${t}\n${i}`),n.disconnect(o),!n.to_string().includes(r))throw new StyleSheetError(`a generated rule would disable every rule after it in the document, so it is refused:\n ${t}\n`+(a.length>0?` GTK said: ${a.join(`; `)}`:` GTK reported no error, which is why this is checked by containment rather than by the error signal.`));if(a.length>0)throw new StyleSheetError(`GTK refused a generated declaration:\n ${t}\n ${a.join(`; `)}`)}};export{StyleSheet,StyleSheetError,n as VARIANT_PSEUDO};
@@ -0,0 +1 @@
1
+ import"../_virtual/_rolldown/runtime.js";import{UnknownUtilityError as e}from"./errors.js";const t={spacing:{0:`0px`,px:`1px`},colors:{transparent:`transparent`,black:`rgb(0 0 0)`,white:`rgb(255 255 255)`},borderRadius:{none:`0`,sm:`2px`,DEFAULT:`4px`,md:`6px`,lg:`8px`,full:`9999px`},borderWidth:{DEFAULT:`1px`,0:`0`,2:`2px`,4:`4px`},fontSize:{sm:`12px`,base:`14px`,lg:`18px`,xl:`24px`},fontWeight:{normal:`400`,medium:`500`,semibold:`600`,bold:`700`},opacity:{0:`0`,50:`0.5`,60:`0.6`,70:`0.7`,80:`0.8`,90:`0.9`,100:`1`}},lookupToken=(e,t)=>e?.[t];function requireToken(t,n,r,i){let a=lookupToken(t,n);if(a!==void 0)return a;throw new e(r,`"${n}" is not in the ${i} scale. Known: ${t===void 0?`(the scale is not configured)`:Object.keys(t).sort().join(`, `)}`)}export{t as MINIMAL_TOKENS,lookupToken,requireToken};
@@ -4,8 +4,6 @@ import { adopt, widgetOf } from '../host.js';
4
4
  import type { HostElement, HostNode, HostText } from '../types.js';
5
5
  /** What React hands `createInstance`: the vnode props, `children` included. */
6
6
  export type ReactProps = Record<string, unknown>;
7
- /** One authored property that changed: name, next value, previous value. */
8
- type PropChange = readonly [key: string, next: unknown, prev: unknown];
9
7
  /**
10
8
  * The HostConfig, exported because it IS the contract.
11
9
  *
@@ -21,6 +19,8 @@ export declare const gtkHostConfig: {
21
19
  supportsPersistence: boolean;
22
20
  supportsHydration: boolean;
23
21
  isPrimaryRenderer: boolean;
22
+ supportsResources: boolean;
23
+ supportsSingletons: boolean;
24
24
  noTimeout: -1;
25
25
  scheduleTimeout: typeof setTimeout;
26
26
  cancelTimeout: typeof clearTimeout;
@@ -42,8 +42,8 @@ export declare const gtkHostConfig: {
42
42
  * flag only when `shouldSetTextContent` was true for the PREVIOUS props.
43
43
  */
44
44
  shouldSetTextContent: () => boolean;
45
- getRootHostContext: () => null;
46
- getChildHostContext: (parentHostContext: null) => null;
45
+ getRootHostContext: () => object;
46
+ getChildHostContext: (parentHostContext: object) => object;
47
47
  /**
48
48
  * What a `ref` receives — the author's widget, never the host's wrapper.
49
49
  *
@@ -55,8 +55,10 @@ export declare const gtkHostConfig: {
55
55
  prepareForCommit: () => null;
56
56
  resetAfterCommit: () => void;
57
57
  preparePortalMount: () => void;
58
+ getCurrentUpdatePriority: () => number;
59
+ setCurrentUpdatePriority: (priority: number) => void;
58
60
  /**
59
- * The priority every update outside React's own event system gets.
61
+ * The priority an update gets when React is not already inside a lane.
60
62
  *
61
63
  * React DOM derives this from the DOM event being handled (a click is
62
64
  * discrete, a scroll continuous). A GTK signal handler is not a DOM event and
@@ -67,7 +69,11 @@ export declare const gtkHostConfig: {
67
69
  * iteration. `flushSync` is the escape hatch, and the spec pumps the main
68
70
  * context to prove the scheduled path works at all under GJS.
69
71
  */
70
- getCurrentEventPriority: () => number;
72
+ resolveUpdatePriority: () => number;
73
+ shouldAttemptEagerTransition: () => boolean;
74
+ trackSchedulerEvent: () => void;
75
+ resolveEventType: () => null;
76
+ resolveEventTimeStamp: () => number;
71
77
  appendChild: (parent: HostElement, child: HostNode) => void;
72
78
  appendChildToContainer: (container: HostElement, child: HostNode) => void;
73
79
  insertBefore: (parent: HostElement, child: HostNode, before: HostNode) => void;
@@ -90,8 +96,17 @@ export declare const gtkHostConfig: {
90
96
  * for the life of the process.
91
97
  */
92
98
  clearContainer: (container: HostElement) => void;
93
- prepareUpdate: (_instance: HostElement, _type: string, oldProps: ReactProps, newProps: ReactProps) => PropChange[] | null;
94
- commitUpdate: (instance: HostElement, updatePayload: PropChange[]) => void;
99
+ /**
100
+ * Diff AND apply — one phase, because React 19 deleted the other one.
101
+ *
102
+ * The argument list is the change with teeth: React 18 passed
103
+ * `(instance, updatePayload, type, prevProps, nextProps, handle)` and 19 passes
104
+ * `(instance, type, prevProps, nextProps, handle)`. The payload slot was
105
+ * removed from the FRONT, so an adapter that kept the old signature would read
106
+ * the type string as its payload and iterate a string — which is why this is
107
+ * spelled out rather than left to positional luck.
108
+ */
109
+ commitUpdate: (instance: HostElement, _type: string, prevProps: ReactProps, nextProps: ReactProps) => void;
95
110
  commitTextUpdate: (textInstance: HostText, _oldText: string, newText: string) => void;
96
111
  commitMount: () => void;
97
112
  resetTextContent: (instance: HostElement) => void;
@@ -111,17 +126,30 @@ export declare const gtkHostConfig: {
111
126
  getInstanceFromScope: () => null;
112
127
  prepareScopeUpdate: () => void;
113
128
  beforeActiveInstanceBlur: () => void;
114
- afterActiveInstanceBlur: () => void;
129
+ maySuspendCommit: () => boolean;
130
+ maySuspendCommitOnUpdate: () => boolean;
131
+ maySuspendCommitInSyncRender: () => boolean;
132
+ preloadInstance: () => boolean;
133
+ startSuspendingCommit: () => void;
134
+ suspendInstance: () => void;
135
+ waitForCommitToBeReady: () => null;
136
+ requestPostPaintCallback: () => void;
137
+ NotPendingTransition: any;
138
+ HostTransitionContext: import("react").Context<any>;
139
+ resetFormInstance: () => void;
115
140
  detachDeletedInstance: () => void;
116
141
  };
117
142
  export type GtkHostConfig = typeof gtkHostConfig;
118
143
  /**
119
144
  * Run `fn` and flush every update it schedules before returning.
120
145
  *
121
- * The default lane is concurrent (see `getCurrentEventPriority`), so a `setState`
146
+ * The default lane is concurrent (see `resolveUpdatePriority`), so a `setState`
122
147
  * from a GTK signal handler lands on a later main-loop iteration. In an
123
148
  * application that is right — GTK is running a main loop. Outside one, and in a
124
149
  * test, this is how a change becomes visible without pumping.
150
+ *
151
+ * `reconciler.flushSync` was RENAMED to `flushSyncFromReconciler` in React 19.
152
+ * The rename is the harmless half; see `createRoot` for the half that is not.
125
153
  */
126
154
  export declare const flushSync: <T>(fn: () => T) => T;
127
155
  export interface ReactRoot {
@@ -144,6 +172,27 @@ export interface ReactRootOptions {
144
172
  * and asserts nothing arrived.
145
173
  */
146
174
  onRecoverableError?(error: Error): void;
175
+ /**
176
+ * An error NO error boundary caught. React 19 split this out of the single
177
+ * callback React 18 had, and it is the one that means the tree is broken.
178
+ *
179
+ * SUPPLYING THIS TURNS OFF THE RETHROW. By default `render()` throws the first
180
+ * uncaught error rather than returning normally, because a host refusal that
181
+ * only reaches a log has bought nothing. An application with its own error
182
+ * surface should not also get a throw — but it then owns reporting entirely.
183
+ */
184
+ onUncaughtError?(error: Error): void;
185
+ /**
186
+ * An error an error boundary DID catch — reported, then handled by the boundary.
187
+ *
188
+ * The default reports through `console.error`, which under GJS routes into the
189
+ * GLib log system at CRITICAL — so `installDiagnosticsGate()` COUNTS it, and a
190
+ * spec that deliberately renders a failing boundary will be told GTK reported a
191
+ * diagnostic. Such a spec should pass its own recorder here. Left as
192
+ * `console.error` because that is the right channel for an application, and a
193
+ * quieter default would be the wrong trade for the common case.
194
+ */
195
+ onCaughtError?(error: Error): void;
147
196
  }
148
197
  /**
149
198
  * A React root over a widget the application owns.
@@ -1,10 +1,20 @@
1
1
  export interface DiagnosticsGate {
2
- /** Warning-or-worse messages seen since the last `reset()`. */
2
+ /** Warning-or-worse messages ABOUT THE TREE, seen since the last `reset()`. */
3
3
  readonly seen: readonly string[];
4
+ /**
5
+ * Warning-or-worse messages about the HOST's graphics stack, kept apart.
6
+ *
7
+ * Recorded rather than dropped: `assertQuiet` names the count so a run is
8
+ * never quietly forgiven, and a caller that genuinely wants to assert on the
9
+ * environment can read them.
10
+ */
11
+ readonly environment: readonly string[];
4
12
  reset(): void;
5
- /** Throw if anything was recorded, naming every message. */
13
+ /** Throw if anything about the tree was recorded, naming every message. */
6
14
  assertQuiet(context?: string): void;
7
15
  }
16
+ /** Whether `message` describes the host's graphics stack rather than the tree. */
17
+ export declare function isEnvironmentDiagnostic(message: string): boolean;
8
18
  /**
9
19
  * Install the writer func once per process and return the gate.
10
20
  *
@@ -1,4 +1,4 @@
1
- export { describeLogRecord, installDiagnosticsGate, type DiagnosticsGate } from './diagnostics.js';
1
+ export { describeLogRecord, installDiagnosticsGate, isEnvironmentDiagnostic, type DiagnosticsGate, } from './diagnostics.js';
2
2
  import type Gtk from '@girs/gtk-4.0';
3
3
  import type { ChildPolicy, HostElement, WidgetDescriptor } from '../types.js';
4
4
  /** Every method name a policy names, so the check does not have to know the shapes. */
@@ -21,7 +21,7 @@ export declare const CURATED_DESCRIPTORS: readonly WidgetDescriptor[];
21
21
  * `descriptorProblems()` cannot see it because it never instantiates anything.
22
22
  *
23
23
  * MEASURED bare-constructing all 164 generated rows on gjs 1.88.1 / GTK 4.22.4 /
24
- * Adw 1.10, resuming past each abort: **163 construct, one does not**, and not a
24
+ * libadwaita 1.9.3, resuming past each abort: **163 construct, one does not**, and not a
25
25
  * single row throws. So this map is one entry rather than a policy — and
26
26
  * `constructsEveryDescriptor` in `generated.spec.ts` is what keeps it one entry.
27
27
  *
@@ -56,8 +56,16 @@ export interface GirProperty {
56
56
  * GIR writes `baseline_fill`; the nick GObject registered is `baseline-fill`. This
57
57
  * is the FALLBACK for a `<member>` with no `glib:nick` — the attribute itself is
58
58
  * the answer wherever it exists, because the substitution is right for Gtk and Adw
59
- * by luck and not by construction: across every GIR in this workspace 40,940
60
- * members carry the attribute and 97 disagree with it.
59
+ * by luck and not by construction: some nicks keep an underscore it would have
60
+ * replaced, and nothing but the attribute knows which one survived.
61
+ *
62
+ * The count that used to sit here is now `ts-for-gir/scripts/check-nick-derivation.mjs`,
63
+ * which owns the corpus this measures over. It also settles what looked like a
64
+ * contradiction: this file said 97 and ts-for-gir said 889 over the SAME 705 GIRs,
65
+ * and both were right — the derivations differ. 889 counts a fallback that also
66
+ * lowercases, which adds the 792 members whose GIR name carries uppercase and whose
67
+ * nick preserves it. This one does not lowercase, so 97 is its number. Quote a
68
+ * derived-nick count without naming its derivation and you get that pair back.
61
69
  */
62
70
  export declare const nickOf: (member: string) => string;
63
71
  export interface GirParam {
@@ -13,8 +13,28 @@ export interface HostProbe<T> {
13
13
  * and the exact getters, never the host's own bookkeeping, which would agree
14
14
  * with itself while the window is wrong. Return whatever belongs in the result
15
15
  * line; the harness adds the failure list and the diagnostic count.
16
+ *
17
+ * MAY BE ASYNC, and one adapter forces it: Vue flushes render jobs on a
18
+ * microtask, so an assertion after a click has to `await nextTick()` or it reads
19
+ * the pre-patch tree. Without this the Vue showcase hand-rolled the whole
20
+ * harness, and the copy carried the pre-`describeLogRecord` collector bug with
21
+ * it — a log record with no `MESSAGE` counted as a diagnostic and then described
22
+ * as the empty string.
16
23
  */
17
- assert(ui: T, check: ProbeCheck): Record<string, unknown>;
24
+ assert(ui: T, check: ProbeCheck): Record<string, unknown> | Promise<Record<string, unknown>>;
25
+ /**
26
+ * Release what the assertions built, before the diagnostics are counted.
27
+ *
28
+ * ORDER IS THE POINT. Teardown is exactly where GTK reports a mis-parented tree
29
+ * — `Finalizing GtkLabel …, but it still has children left` arrives at finalize,
30
+ * at exit 0 (ADR 0027 § Context). A probe that counts diagnostics and THEN tears
31
+ * down cannot see the one class of defect that only surfaces there, which is
32
+ * what both hand-written probes did.
33
+ *
34
+ * Optional because a probe that owns nothing has nothing to release; the
35
+ * harness's own `build` result is reachable only from here.
36
+ */
37
+ teardown?(ui: T): void;
18
38
  /** Prefix of the machine-readable result line. Default `PROBE`. */
19
39
  label?: string;
20
40
  }
@@ -34,6 +54,15 @@ export declare function probeEnabled(): boolean;
34
54
  /**
35
55
  * Run the showcase's assertions once and return the process exit code.
36
56
  *
57
+ * **The probe always builds HEADLESS, in both paths.** It is handed `null` rather
58
+ * than the `GApplication`, for two reasons that point the same way. It makes the two
59
+ * paths return the same verdict about the same tree — a probe whose GUI run differs
60
+ * from its headless run is two probes. And it keeps the GUI path from constructing
61
+ * and (now that `teardown` exists) destroying an `Adw.ApplicationWindow` INSIDE
62
+ * `activate`, which is the exact neighbourhood of the segfault recorded on
63
+ * `runHostProbeApp` below. Nothing a probe asserts needs an application; `present`
64
+ * gets the real one.
65
+ *
37
66
  * The diagnostics gate is reset first and asserted last, and both halves are the
38
67
  * harness's rather than the showcase's. Reset, because in the GUI path this runs
39
68
  * from `activate` — AFTER Adw startup, where a session-bus, portal, theme or a11y
@@ -42,7 +71,7 @@ export declare function probeEnabled(): boolean;
42
71
  * failure mode is exit 0: a mis-parented widget floods `Gtk-WARNING` and the
43
72
  * process still succeeds.
44
73
  */
45
- export declare function runHostProbe<T>(probe: HostProbe<T>, app?: Adw.Application | null): number;
74
+ export declare function runHostProbe<T>(probe: HostProbe<T>): Promise<number>;
46
75
  /**
47
76
  * The whole `main` of a gtk-host showcase.
48
77
  *
@@ -53,10 +53,13 @@ export declare function coerce(spec: GObject.ParamSpec, value: unknown, tag: str
53
53
  * leaves behind, not what the ParamSpec declares.
54
54
  *
55
55
  * The two disagree far too often to treat the ParamSpec as an approximation.
56
- * Measured on gjs 1.88.1 / GTK 4.22.4 / Adw 1.10 across the 26 shipped
57
- * descriptors: 953 scalar properties are both readable and writable, and
58
- * **104 of them disagree** — 78 once construct-only `css-name` is set aside.
59
- * Twelve property names carry it, four of them behavioural:
56
+ * Measured on gjs 1.88.1 / GTK 4.22.4 / libadwaita 1.9.3, one probe per row, over
57
+ * the curated descriptors as this table ships them — 27 of them at the time of the
58
+ * measurement, so the totals move when a row joins: 981 scalar properties are both
59
+ * readable and writable, and **107 of them disagree** — 80 once construct-only
60
+ * `css-name` is set aside. Twelve property names carry it, four of them
61
+ * behavioural, and those four are pinned BY NAME in `props.spec.ts` because a name
62
+ * is what survives a re-count:
60
63
  *
61
64
  * GtkWindow.visible spec=true constructed=false
62
65
  * AdwActionRow.activatable spec=true constructed=false
@@ -0,0 +1,6 @@
1
+ /** A utility class, property or combination this vocabulary cannot answer for, and why. */
2
+ export declare class UnknownUtilityError extends Error {
3
+ readonly name = "UnknownUtilityError";
4
+ readonly utility: string;
5
+ constructor(utility: string, detail: string);
6
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Property names GTK4 accepts in a stylesheet, with a value that parses.
3
+ *
4
+ * The value matters: `border-radius: 8px` and `border-radius: 9999px` both parse,
5
+ * but a property is only provably present when some value for it is accepted.
6
+ */
7
+ export declare const GTK_CSS_PROBES: ReadonlyArray<readonly [property: string, value: string]>;
8
+ /**
9
+ * Property names GTK4 REFUSES, and the reason each one is here.
10
+ *
11
+ * Most of them are the layout model the web has and GTK does not — there is no
12
+ * `display: flex`, no absolute positioning, no intrinsic `width`. They must become
13
+ * widget selection and widget properties, which is the layout half.
14
+ *
15
+ * Two groups are not that, and both are here because a reader would assume the
16
+ * opposite. `text-align` is not about layout at all: it is the property most likely
17
+ * to be grouped with `color` and `font-size` by anyone reading a Tailwind `text-*`
18
+ * vocabulary, and GTK drops it in silence. The four logical spacings ARE spacing,
19
+ * whose physical spellings sit in the accepted table three lines up — the refusal
20
+ * is per NAME, not per concept, and that is the asymmetry `layout.ts` is built on.
21
+ */
22
+ export declare const NOT_GTK_CSS: ReadonlyArray<readonly [property: string, value: string]>;
23
+ /** The property names of {@link GTK_CSS_PROBES}, for a membership test. */
24
+ export declare const GTK_CSS_PROPERTIES: ReadonlySet<string>;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Properties GTK installs, with the GType of their value.
3
+ *
4
+ * `GtkWidget` rows are the universal set — every widget in the table inherits
5
+ * them, which is what lets `layout.ts` emit them without knowing the tag.
6
+ */
7
+ export declare const GTK_WIDGET_PROPERTY_PROBES: ReadonlyArray<readonly [gtype: string, property: string, valueType: string]>;
8
+ /**
9
+ * Properties the class does NOT install, and the claim each one carries.
10
+ *
11
+ * Without this direction the table above could name every property in GTK and
12
+ * still pass — and every routing decision in `layout.ts` rests on an absence
13
+ * rather than on a presence.
14
+ */
15
+ export declare const NOT_GTK_WIDGET_PROPERTIES: ReadonlyArray<readonly [gtype: string, property: string]>;
16
+ /**
17
+ * The property NAMES of {@link GTK_WIDGET_PROPERTY_PROBES}, for a membership test.
18
+ *
19
+ * NAMES ONLY, deliberately weaker than the table: L1 does not know which widget a
20
+ * class list will land on, so the strongest guard it can run is "GTK installs a
21
+ * property by this name SOMEWHERE in the vocabulary". `orientation` passes it on
22
+ * an element that turns out to be a `Gtk.Label`, and refusing that is L2's job,
23
+ * with the tag in hand. Reading this set as "every widget has these" is the one
24
+ * mistake it invites, and `GTK_WIDGET_PROPERTY_PROBES` is the table that answers
25
+ * the per-class question.
26
+ */
27
+ export declare const GTK_WIDGET_PROPERTIES: ReadonlySet<string>;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,13 @@
1
+ export { GTK_CSS_PROBES, GTK_CSS_PROPERTIES, NOT_GTK_CSS } from './gtk-css.js';
2
+ export { GTK_WIDGET_PROPERTIES, GTK_WIDGET_PROPERTY_PROBES, NOT_GTK_WIDGET_PROPERTIES } from './gtk-props.js';
3
+ export { MINIMAL_TOKENS, lookupToken, requireToken } from './tokens.js';
4
+ export type { Scale, StyleTokens } from './tokens.js';
5
+ export { UnknownUtilityError } from './errors.js';
6
+ export { PAINT_PROPERTIES, partitionPaint, resolvePaintUtility } from './paint.js';
7
+ export type { PaintProps } from './paint.js';
8
+ export { LAYOUT_PROPERTIES, partitionLayout, resolveLayoutUtility } from './layout.js';
9
+ export type { AlignValue, Edge, JustifyValue, LayoutIntent, LayoutPartition, LayoutProps, OverlayIntent, } from './layout.js';
10
+ export { partition, resolveUtilities, resolveUtility } from './resolve.js';
11
+ export type { Partitioned, StyleProps } from './resolve.js';
12
+ export { StyleSheet, StyleSheetError, VARIANT_PSEUDO } from './sheet.js';
13
+ export type { StyleSheetOptions } from './sheet.js';
@@ -0,0 +1,163 @@
1
+ import { type StyleTokens } from './tokens.js';
2
+ /** React Native's own `alignItems`/`alignSelf` vocabulary. */
3
+ export type AlignValue = 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline';
4
+ /** React Native's own `justifyContent` vocabulary. */
5
+ export type JustifyValue = 'flex-start' | 'flex-end' | 'center' | 'space-between' | 'space-around' | 'space-evenly';
6
+ /** The four physical edges an absolutely positioned element offsets from. */
7
+ export type Edge = 'top' | 'right' | 'bottom' | 'left';
8
+ /**
9
+ * The properties the layout half understands, in React Native's spelling.
10
+ *
11
+ * The VALUES are React Native's too — `alignItems: 'flex-start'`, not `'start'` —
12
+ * and that is the point of ADR 0032 § 4 rather than a detail. A class list and a
13
+ * `style={{…}}` object are one information set arriving by two routes, and 48 of
14
+ * the measured application's 57 style objects carry exactly these names and values.
15
+ * Normalising the classes into a third spelling would put a translation table
16
+ * between the two front ends and give the partition two truths to keep in step.
17
+ */
18
+ export interface LayoutProps {
19
+ marginTop?: string;
20
+ marginRight?: string;
21
+ marginBottom?: string;
22
+ marginLeft?: string;
23
+ marginStart?: string;
24
+ marginEnd?: string;
25
+ paddingTop?: string;
26
+ paddingRight?: string;
27
+ paddingBottom?: string;
28
+ paddingLeft?: string;
29
+ flexDirection?: 'row' | 'column';
30
+ flexGrow?: string;
31
+ alignItems?: AlignValue;
32
+ justifyContent?: JustifyValue;
33
+ alignSelf?: AlignValue;
34
+ gap?: string;
35
+ columnGap?: string;
36
+ rowGap?: string;
37
+ width?: string;
38
+ height?: string;
39
+ overflow?: 'visible' | 'hidden' | 'scroll';
40
+ display?: 'flex' | 'none';
41
+ position?: 'relative' | 'absolute';
42
+ top?: string;
43
+ right?: string;
44
+ bottom?: string;
45
+ left?: string;
46
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
47
+ }
48
+ /**
49
+ * What L1 could not answer, for L2 to resolve against the shadow tree.
50
+ *
51
+ * Every field here is a case where the same input compiles to DIFFERENT GTK
52
+ * depending on something no per-element function can see. Nothing is an intent for
53
+ * being hard: `overflow-hidden` needs the parent no more than `bg-red` does, and it
54
+ * is a widget property below rather than a field here.
55
+ */
56
+ export interface LayoutIntent {
57
+ /**
58
+ * `flex-1` — `hexpand` on a row, `vexpand` on a column.
59
+ *
60
+ * ADR 0032 § 6, and the reason the intent mechanism exists at all: the axis is
61
+ * the PARENT's orientation, which an element does not know about itself.
62
+ */
63
+ readonly expand?: 'main-axis';
64
+ /**
65
+ * `items-*` — the cross-axis alignment of every CHILD.
66
+ *
67
+ * Two things missing at once. GTK has no `align-items`: the alignment lives on
68
+ * each child as `halign`/`valign`, so L2 must walk children this function has
69
+ * never seen. And WHICH of the two it is depends on this element's own
70
+ * orientation, which is `Gtk.Box`'s default when no `flex-row`/`flex-col` says
71
+ * otherwise — and the defaults disagree (`Gtk.Box` is horizontal, a React
72
+ * Native `View` is a column), so there is not even a safe fallback to guess.
73
+ */
74
+ readonly alignChildren?: AlignValue;
75
+ /**
76
+ * `justify-*` — main-axis distribution.
77
+ *
78
+ * `space-between` is a WIDGET CHOICE, not a property: `Gtk.CenterBox` for two or
79
+ * three children and a named refusal beyond that (ADR 0032 § 6). The child count
80
+ * decides, and L1 has no children. The other three are the box's own
81
+ * `halign`/`valign`, which needs the orientation for the same reason
82
+ * {@link LayoutIntent.alignChildren} does.
83
+ */
84
+ readonly distribute?: JustifyValue;
85
+ /**
86
+ * `self-*` — this element's own cross-axis alignment.
87
+ *
88
+ * `halign` or `valign` by the PARENT's orientation. Same shape as
89
+ * {@link LayoutIntent.expand}, same reason, one axis over.
90
+ */
91
+ readonly alignSelf?: AlignValue;
92
+ /**
93
+ * `gap-x-*` / `gap-y-*` — a spacing that is only valid on one orientation.
94
+ *
95
+ * `Gtk.Box` has ONE `spacing` (measured), so an axis-qualified gap is either
96
+ * that spacing or nothing at all, and which it is depends on the orientation
97
+ * L1 cannot see. Refusing the spelling outright was the alternative and is
98
+ * worse: `gap-x-2` on a row is ordinary, unambiguous authoring, and L2 answers
99
+ * it exactly.
100
+ */
101
+ readonly axisSpacing?: {
102
+ readonly axis: 'horizontal' | 'vertical';
103
+ readonly pixels: number;
104
+ };
105
+ /**
106
+ * `absolute` / `relative` / `inset-*` / `top-*` — the parent must be a `Gtk.Overlay`.
107
+ *
108
+ * A widget cannot make its own parent a different class, so the child's
109
+ * `absolute` is a request L2 executes on the PARENT. The `context` role is
110
+ * `relative`'s: it sets nothing (an overlay establishes the context by being
111
+ * one) but it is carried rather than dropped, because it is the half of the
112
+ * pair L2 can hold the other half against — an `absolute` child under a parent
113
+ * that never declared `relative` is a real authoring bug, and a dropped
114
+ * `relative` would make it undetectable.
115
+ */
116
+ readonly overlay?: OverlayIntent;
117
+ /**
118
+ * `text-*` — only a text widget aligns text.
119
+ *
120
+ * `xalign` and `justify` are `Gtk.Label`'s; `Gtk.Box` has neither (measured).
121
+ * A `<View className="text-center">` wrapping a `<Text>` is ordinary React
122
+ * Native, and on GTK the property has to travel to the label, which is a tree
123
+ * walk and therefore L2's.
124
+ */
125
+ readonly textAlign?: 'left' | 'center' | 'right' | 'justify';
126
+ }
127
+ export type OverlayIntent = {
128
+ readonly role: 'context';
129
+ } | {
130
+ readonly role: 'child';
131
+ readonly edges: Readonly<Partial<Record<Edge, number>>>;
132
+ };
133
+ /** What {@link partitionLayout} contributes to the partition. */
134
+ export interface LayoutPartition {
135
+ readonly css: readonly string[];
136
+ readonly props: Readonly<Record<string, unknown>>;
137
+ readonly intent: LayoutIntent;
138
+ }
139
+ /**
140
+ * One utility class → the layout properties it sets, or `null` for "not mine".
141
+ *
142
+ * `null` rather than a throw is what lets `resolve.ts` try both halves without
143
+ * either of them knowing the other exists. A family this half DOES own with a bad
144
+ * token still throws, and that distinction is the whole point: `flex-2` is a
145
+ * refusal with a reason, `wibble-3` is an unknown name.
146
+ */
147
+ export declare function resolveLayoutUtility(utility: string, tokens: StyleTokens): LayoutProps | null;
148
+ /**
149
+ * The {@link LayoutProps} keys, for the dispatch's ownership test.
150
+ *
151
+ * DERIVED from the route table rather than listed: a second list is a second thing
152
+ * to keep true, and the one that drifts is the one that decides whether a property
153
+ * is routed at all.
154
+ */
155
+ export declare const LAYOUT_PROPERTIES: ReadonlySet<string>;
156
+ /**
157
+ * Layout properties → `{ css, props, intent }`.
158
+ *
159
+ * The two cross-key refusals run first, because both are cases where every
160
+ * individual property is fine and the COMBINATION is what GTK cannot express.
161
+ * They are the reason this is not a per-property `map`.
162
+ */
163
+ export declare function partitionLayout(props: LayoutProps): LayoutPartition;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,53 @@
1
+ import { type StyleTokens } from './tokens.js';
2
+ /** The properties the paint half understands, in React Native's spelling. */
3
+ export interface PaintProps {
4
+ backgroundColor?: string;
5
+ color?: string;
6
+ opacity?: string;
7
+ borderRadius?: string;
8
+ borderTopLeftRadius?: string;
9
+ borderTopRightRadius?: string;
10
+ borderBottomLeftRadius?: string;
11
+ borderBottomRightRadius?: string;
12
+ borderWidth?: string;
13
+ borderTopWidth?: string;
14
+ borderRightWidth?: string;
15
+ borderBottomWidth?: string;
16
+ borderLeftWidth?: string;
17
+ borderColor?: string;
18
+ fontSize?: string;
19
+ fontWeight?: string;
20
+ fontFamily?: string;
21
+ fontStyle?: string;
22
+ letterSpacing?: string;
23
+ lineHeight?: string;
24
+ textDecorationLine?: string;
25
+ textTransform?: string;
26
+ }
27
+ /**
28
+ * React Native property name → the GTK CSS property it becomes.
29
+ *
30
+ * Exported so `paint.spec.ts` can assert the invariant this table rests on — that
31
+ * every value is a property GTK measurably accepts — directly, rather than through
32
+ * `partition`'s per-call guard, which no input can reach while the table is right.
33
+ */
34
+ export declare const CSS_NAME: Readonly<Record<keyof PaintProps, string>>;
35
+ /**
36
+ * One utility class → the paint properties it sets, or `null` for "not mine".
37
+ *
38
+ * Returns a partial record rather than mutating, so a caller can see what a single
39
+ * class contributed — which is what makes the last-wins rule testable.
40
+ */
41
+ export declare function resolvePaintUtility(utility: string, tokens: StyleTokens): PaintProps | null;
42
+ /** The {@link PaintProps} keys, for the dispatch's ownership test. */
43
+ export declare const PAINT_PROPERTIES: ReadonlySet<string>;
44
+ /**
45
+ * Paint properties → GTK CSS declarations.
46
+ *
47
+ * Every emitted name is checked against the MEASURED accepted set rather than
48
+ * trusted: a property this table maps but GTK does not accept would be dropped by
49
+ * GTK's parser with no diagnostic, which is the exact silence the whole partition
50
+ * exists to avoid. The check is cheap and it has caught the one property that looks
51
+ * like paint and is not.
52
+ */
53
+ export declare function partitionPaint(props: PaintProps): readonly string[];
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,58 @@
1
+ import { type LayoutIntent, type LayoutProps } from './layout.js';
2
+ import { type PaintProps } from './paint.js';
3
+ import type { StyleTokens } from './tokens.js';
4
+ /** One normalised property set, in React Native's spelling (ADR 0032 § 4). */
5
+ export type StyleProps = PaintProps & LayoutProps;
6
+ /** What the partition hands back — GTK's three destinations, all of them. */
7
+ export interface Partitioned {
8
+ /** GTK CSS declarations, `property: value`, without the braces. */
9
+ readonly css: readonly string[];
10
+ /**
11
+ * Widget properties in GTK's own spelling, with values the host will apply.
12
+ *
13
+ * The values are what `applyProps` coerces through the ParamSpec — a nick for
14
+ * an enum, a boolean for a boolean, an integer for a `gint`. NOT what a raw
15
+ * GJS setter accepts: `box.orientation = 'vertical'` keeps HORIZONTAL with no
16
+ * diagnostic at all (measured, gjs 1.88.1), which is the whole reason the host
17
+ * has a coercion step.
18
+ */
19
+ readonly props: Readonly<Record<string, unknown>>;
20
+ /** What only the shadow tree can answer, for L2 at attach time. */
21
+ readonly intent: LayoutIntent;
22
+ }
23
+ /**
24
+ * One utility class → the properties it sets.
25
+ *
26
+ * Paint runs first, and the order is not arbitrary: the paint families are closed
27
+ * sets of scale names, so its `null` is a cheap "no such family here", while the
28
+ * layout half owns the open-ended spellings (`gap-x-4`, `w-1/2`) whose parsing is
29
+ * where a wrong claim would be expensive.
30
+ */
31
+ export declare function resolveUtility(utility: string, tokens: StyleTokens): StyleProps;
32
+ /**
33
+ * A class list → the properties it sets, later classes winning.
34
+ *
35
+ * Last-wins is CSS's own rule for equal specificity and the one authors expect from
36
+ * a utility vocabulary. It is done HERE, on the property record, rather than by
37
+ * emitting two declarations and letting GTK resolve them — GTK resolves equal
38
+ * specificity by SHEET ORDER, not by the order of names in `css-classes`, so
39
+ * "the later class wins" would be false the moment two generated classes met.
40
+ *
41
+ * It is also why `m-*` puts its horizontal half in the same KEYS as `mx-*`: two
42
+ * spellings of one edge only resolve by last-wins while they are one key.
43
+ */
44
+ export declare function resolveUtilities(utilities: readonly string[], tokens: StyleTokens): StyleProps;
45
+ /**
46
+ * Properties → `{ css, props, intent }`.
47
+ *
48
+ * Splits the record by ownership and hands each half to its own partition, because
49
+ * the layout half's refusals are CROSS-KEY (a physical and a logical margin
50
+ * together, two gap spellings at once) and it can only see them with its whole
51
+ * share in hand.
52
+ *
53
+ * A property NEITHER half routes throws rather than being skipped. That is the same
54
+ * rule as an unknown utility, one layer down, and it is the one that catches a
55
+ * `style={{…}}` object carrying a React Native property nobody has mapped yet —
56
+ * the front end with no class name to check.
57
+ */
58
+ export declare function partition(props: StyleProps): Partitioned;
@@ -0,0 +1,40 @@
1
+ import Gdk from 'gi://Gdk?version=4.0';
2
+ /** A variant this sheet knows how to express, and the pseudo-class it becomes. */
3
+ export declare const VARIANT_PSEUDO: Readonly<Record<string, string>>;
4
+ /** A rule GTK refused, or one that would have poisoned the document. */
5
+ export declare class StyleSheetError extends Error {
6
+ readonly name = "StyleSheetError";
7
+ constructor(message: string);
8
+ }
9
+ export interface StyleSheetOptions {
10
+ /**
11
+ * Where the provider is installed. Omitted means the default display — the
12
+ * right answer for an application and the wrong one for a test, which is why
13
+ * it is injectable at all.
14
+ */
15
+ readonly display?: Gdk.Display | null;
16
+ /** Provider priority. Defaults to `STYLE_PROVIDER_PRIORITY_APPLICATION`. */
17
+ readonly priority?: number;
18
+ }
19
+ export declare class StyleSheet {
20
+ #private;
21
+ constructor(options?: StyleSheetOptions);
22
+ /**
23
+ * Register `declarations` (and optional variants) and return their class name.
24
+ *
25
+ * Idempotent: the same declarations always yield the same name and the document
26
+ * gains nothing the second time.
27
+ */
28
+ classFor(declarations: readonly string[], variants?: Readonly<Record<string, readonly string[]>>): string;
29
+ /** The document as GTK sees it. Exposed because a test that cannot read it proves nothing. */
30
+ toString(): string;
31
+ /** How many distinct rules the document holds. */
32
+ get size(): number;
33
+ /**
34
+ * Load the document now, rather than on the next microtask.
35
+ *
36
+ * A test has no microtask checkpoint it can rely on, and an application that is
37
+ * about to present a window wants the styles already in place.
38
+ */
39
+ flush(): void;
40
+ }
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,58 @@
1
+ /** A scale: token name → the CSS value it stands for. */
2
+ export type Scale = Readonly<Record<string, string>>;
3
+ /**
4
+ * Every scale the partition consults.
5
+ *
6
+ * All optional: a project that has no `letterSpacing` scale simply has no
7
+ * `tracking-*` utilities, and asking for one is an error naming the empty scale
8
+ * rather than a silent no-op.
9
+ */
10
+ export interface StyleTokens {
11
+ /**
12
+ * The one scale `m-*`, `p-*`, `gap-*`, `inset-*` and the edge utilities read.
13
+ *
14
+ * ONE scale for all of them because that is what a token file actually holds —
15
+ * `mt-2xs` and `gap-2xs` name the same 2xs — and splitting it would ask a
16
+ * project to repeat itself once per family.
17
+ *
18
+ * A value only reaches the WIDGET-PROPERTY channel if it is a pixel length:
19
+ * `margin-top` is a `gint` of device pixels (measured, `gtk-props.ts`) and GTK
20
+ * exposes no unit conversion there. A scale spelled in `rem` therefore still
21
+ * works for padding, which is CSS-only, and is a named error on a margin rather
22
+ * than a silently rounded one.
23
+ */
24
+ readonly spacing?: Scale;
25
+ /** `w-<token>`, falling back to {@link StyleTokens.spacing} — Tailwind's own layering. */
26
+ readonly width?: Scale;
27
+ /** `h-<token>`, falling back to {@link StyleTokens.spacing}. */
28
+ readonly height?: Scale;
29
+ readonly colors?: Scale;
30
+ readonly borderRadius?: Scale;
31
+ readonly borderWidth?: Scale;
32
+ readonly fontSize?: Scale;
33
+ readonly fontWeight?: Scale;
34
+ readonly fontFamily?: Scale;
35
+ readonly letterSpacing?: Scale;
36
+ readonly lineHeight?: Scale;
37
+ readonly opacity?: Scale;
38
+ }
39
+ /**
40
+ * A minimal set, for tests and for a consumer who has no token file yet.
41
+ *
42
+ * Deliberately SMALL rather than a copy of Tailwind's defaults. A large default
43
+ * scale would let a project's typo resolve against a value nobody chose, which is
44
+ * the failure the declared-vocabulary rule exists to prevent — and it would make
45
+ * "the values come from the project" false in the common case.
46
+ */
47
+ export declare const MINIMAL_TOKENS: StyleTokens;
48
+ /** A token's value, or `undefined` when the scale is absent or does not carry it. */
49
+ export declare const lookupToken: (scale: Scale | undefined, token: string) => string | undefined;
50
+ /**
51
+ * A token's value, or a named error that lists what the scale DOES hold.
52
+ *
53
+ * Both halves of the partition read scales, so the reader lives with the scales
54
+ * rather than in whichever half needed it first. Listing the known keys is the
55
+ * whole value of the message: "not in the borderRadius scale" turns a typo into a
56
+ * search, "Known: full, lg, md, none, sm" turns it into a one-line fix.
57
+ */
58
+ export declare function requireToken(scale: Scale | undefined, token: string, utility: string, scaleName: string): string;
@@ -1,2 +1,25 @@
1
+ import { type Runtime } from '@gjsify/unit';
1
2
  import type { DiagnosticsGate } from '../conformance/diagnostics.js';
2
3
  export declare const gated: (gate: DiagnosticsGate, name: string, body: () => Promise<void>) => Promise<void>;
4
+ /**
5
+ * The runtimes that can host GTK — and an IDENTITY list on purpose.
6
+ *
7
+ * These suites need a reachable GTK, not a particular interpreter, so the
8
+ * expressive form would be a capability (`@gjsify/unit` splits `'Display'` from
9
+ * `'Gl'` for exactly that reason). It is not used here, and the reason is the
10
+ * failure mode rather than taste.
11
+ *
12
+ * A capability can only be PROBED — nothing about `(os, env)` answers "is the Gtk
13
+ * typelib reachable from this process", and `@gjsify/unit` is Tier 1 and must not
14
+ * import `gi://` to find out. A probe that answers "no" makes the whole suite STAND
15
+ * DOWN, and `requireAxes` cannot catch that: it only holds axes the host MATCHES.
16
+ * So a container that lost its GTK would run zero tests and report success — the
17
+ * green-that-checked-nothing shape, arriving through the very gate meant to widen
18
+ * coverage. Measured once already: gated on `'Gjs'` alone, this suite built for the
19
+ * node target exited 0 having run 0 tests from 0 gates, 9 stood down.
20
+ *
21
+ * Named identities instead mean the suite RUNS wherever it was built, and a missing
22
+ * GTK dies loudly at `Gtk.init()` in the first line of each suite. Louder beats
23
+ * more expressive when the quiet failure is a pass.
24
+ */
25
+ export declare const GTK_HOSTS: Runtime[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/gtk-host",
3
- "version": "0.43.0",
3
+ "version": "0.44.0",
4
4
  "description": "Framework-agnostic GTK4/Adwaita host: the element model UI-framework renderers bind to",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -10,6 +10,10 @@
10
10
  "types": "./lib/types/index.d.ts",
11
11
  "default": "./lib/esm/index.js"
12
12
  },
13
+ "./style": {
14
+ "types": "./lib/types/style/index.d.ts",
15
+ "default": "./lib/esm/style/index.js"
16
+ },
13
17
  "./vue": {
14
18
  "types": "./lib/types/adapters/vue.d.ts",
15
19
  "default": "./lib/esm/adapters/vue.js"
@@ -63,9 +67,11 @@
63
67
  "build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
64
68
  "build:types": "gjsify tsc",
65
69
  "build:test": "gjsify run build:test:gjs",
70
+ "build:test:node": "gjsify build src/test.mts --app node --outfile dist/test.node.mjs --define '__VUE_OPTIONS_API__=false' --define '__VUE_PROD_DEVTOOLS__=false' --define '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false' --define 'process.env.NODE_ENV=\"production\"' --exclude-globals navigator",
66
71
  "build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs --define '__VUE_OPTIONS_API__=false' --define '__VUE_PROD_DEVTOOLS__=false' --define '__VUE_PROD_HYDRATION_MISMATCH_DETAILS__=false' --define 'process.env.NODE_ENV=\"production\"' --exclude-globals navigator",
67
72
  "test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:gjs",
68
73
  "test:gjs": "gjsify run test.gjs.mjs",
74
+ "test:gjs-on-node": "gjsify run build:gjsify && gjsify run build:test:node && NODE_GI_NATIVE=${NODE_GI_NATIVE:-prebuild} node dist/test.node.mjs",
69
75
  "generate": "gjsify build src/generator/main.mts --app gjs --outfile tmp/generate.gjs.mjs && gjsify run tmp/generate.gjs.mjs -- --out src/generated && gjsify format src/generated"
70
76
  },
71
77
  "keywords": [
@@ -88,8 +94,8 @@
88
94
  "peerDependencies": {
89
95
  "@vue/runtime-core": "^3.5.0",
90
96
  "solid-js": "^1.9.0",
91
- "react": "^18.3.1",
92
- "react-reconciler": "^0.29.2"
97
+ "react": "^19.2.0",
98
+ "react-reconciler": "^0.33.0"
93
99
  },
94
100
  "peerDependenciesMeta": {
95
101
  "@vue/runtime-core": {
@@ -106,24 +112,25 @@
106
112
  }
107
113
  },
108
114
  "devDependencies": {
109
- "@gjsify/cli": "^0.43.0",
115
+ "@gjsify/cli": "^0.44.0",
110
116
  "@vue/runtime-core": "^3.5.0",
111
117
  "solid-js": "^1.9.0",
112
- "@gjsify/unit": "^0.43.0",
118
+ "@gjsify/unit": "^0.44.0",
113
119
  "@types/node": "^25.9.2",
114
120
  "typescript": "^6.0.3",
115
- "@gjsify/domparser": "^0.43.0",
121
+ "@gjsify/domparser": "^0.44.0",
122
+ "@gjsify/node-gi": "file:../../node-gi/node-gi",
116
123
  "vue": "^3.5.41",
117
124
  "vue-tsc": "^3.3.11",
118
- "react": "^18.3.1",
119
- "react-reconciler": "^0.29.2",
120
- "@types/react": "^18.3.12",
121
- "@types/react-reconciler": "^0.28.9"
125
+ "react": "^19.2.0",
126
+ "react-reconciler": "^0.33.0",
127
+ "@types/react": "^19.2.2",
128
+ "@types/react-reconciler": "^0.33.0"
122
129
  },
123
130
  "gjsify": {
124
131
  "runtimes": {
125
132
  "gjs": "polyfill",
126
- "node": "none",
133
+ "node": "polyfill",
127
134
  "browser": "none",
128
135
  "nativescript": "none"
129
136
  },