@gjsify/gtk-host 0.51.0 → 0.51.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/esm/fonts.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{isFontFace as e,resolveFontSources as t}from"./font-dir.js";import{describeFontFamilyMatch as n,matchFontFamilies as r,matchFontFamily as i}from"./font-families.js";import{ADWAITA_UI_FONT_FAMILY as a,GNOME_UI_FONT_POINT_SIZE as o,UI_FONT_POLICIES as s,planUiFont as c,planUiFontPolicy as l}from"./ui-font.js";import u from"gi://Gtk?version=4.0";import d from"gi://Gio?version=2.0";import f from"gi://GLib?version=2.0";import p from"gi://PangoCairo?version=1.0";function isUnsupportedByFontMap(e){return e instanceof f.Error&&e.matches(d.io_error_quark(),d.IOErrorEnum.NOT_SUPPORTED)}function initFonts(e={}){let i=t({...e,env:{GJSIFY_FONT_DIR:f.getenv(`GJSIFY_FONT_DIR`)??void 0,GJSIFY_GTK_RUNTIME_FONT_DIR:f.getenv(`GJSIFY_GTK_RUNTIME_FONT_DIR`)??void 0}}),a=i.find(e=>e.origin===`app`)?.dir,o=[],s=[],c=[],l=e.expectedFamilies??[];if(i.length===0&&l.length===0)return{dir:a,sources:i,uiFont:void 0,registered:o,declined:s,failed:c,families:[],matches:[]};let u=p.FontMap.get_default(),m=i.length===0?[]:familyNames(u);for(let e of i){let t=[];collectFaces(d.File.new_for_path(e.dir),t,c);for(let e of t.sort())try{u.add_font_file(e),o.push(e)}catch(t){if(isUnsupportedByFontMap(t)){s.push(e);continue}c.push({path:e,message:messageOf(t)})}}let h=familyNames(u),g=i.length===0?[]:h.filter(e=>!m.includes(e)).sort(),_=r(l,h);captureUiFontBaseline();let v=e.uiFont===void 0?void 0:applyUiFontPolicy(e.uiFont);for(let e of c)console.warn(`initFonts: ${e.path} could not be read as an application font (${e.message}). Text asking for a family this application ships will render in a substituted one.`);for(let e of _)e.kind!==`exact`&&console.warn(`initFonts: ${n(e)}`);return{dir:a,sources:i,uiFont:v,registered:o,declined:s,failed:c,families:g,matches:_}}let m=!1,h;function captureUiFontBaseline(){if(m)return h;let e=u.Settings.get_default();if(e!==null)return h=e.gtk_font_name??void 0,m=!0,h}function uiFontBaseline(){return captureUiFontBaseline()}function applyUiFontPolicy(e){let t=typeof e==`string`?{policy:e}:e,n=u.Settings.get_default();if(n===null)return{next:void 0,kind:`unparsed`,family:void 0,size:void 0};let r=captureUiFontBaseline(),i=l({...t,family:t.family??resolvedAdwaitaFamily(t.policy),current:n.gtk_font_name??void 0,baseline:r});return i.next===void 0||(n.gtk_font_name=i.next),i}function resolvedAdwaitaFamily(e){if(e!==`adwaita`)return;let t=adwaitaUiFontAvailability();if(t.match.family!==void 0)return t.match.family;console.warn(`applyUiFontPolicy: "${t.family}" is ${t.match.kind} on this font map, so asking for it will render in a substituted family. Register the runtime bundle's faces with initFonts() first, and check adwaitaUiFontAvailability() before offering this policy.`)}function adwaitaUiFontAvailability(e=a){let t=i(e,familyNames(p.FontMap.get_default()));return{family:e,match:t,available:t.kind!==`absent`}}function familyNames(e){return e.list_families().map(e=>e.get_name())}function collectFaces(t,n,r){try{let i=t.enumerate_children(`standard::name,standard::type`,d.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null);for(let t=i.next_file(null);t!==null;t=i.next_file(null)){let a=i.get_child(t),o=a.get_path();o!==null&&(t.get_file_type()===d.FileType.DIRECTORY?collectFaces(a,n,r):e(t.get_name())&&n.push(o))}}catch(e){r.push({path:t.get_path()??``,message:messageOf(e)})}}function messageOf(e){return e instanceof f.Error||e instanceof Error?e.message:String(e)}export{a as ADWAITA_UI_FONT_FAMILY,o as GNOME_UI_FONT_POINT_SIZE,s as UI_FONT_POLICIES,adwaitaUiFontAvailability,applyUiFontPolicy,n as describeFontFamilyMatch,initFonts,isUnsupportedByFontMap,r as matchFontFamilies,i as matchFontFamily,c as planUiFont,l as planUiFontPolicy,t as resolveFontSources,uiFontBaseline};
1
+ import"./_virtual/_rolldown/runtime.js";import{isFontFace as e,resolveFontSources as t}from"./font-dir.js";import{describeFontFamilyMatch as n,matchFontFamilies as r,matchFontFamily as i}from"./font-families.js";import{ADWAITA_UI_FONT_FAMILY as a,GNOME_UI_FONT_POINT_SIZE as o,UI_FONT_POLICIES as s,planUiFont as c,planUiFontPolicy as l}from"./ui-font.js";import u from"gi://Gtk?version=4.0";import d from"gi://Gio?version=2.0";import f from"gi://GLib?version=2.0";import p from"gi://PangoCairo?version=1.0";function isUnsupportedByFontMap(e){return e instanceof f.Error&&e.matches(d.io_error_quark(),d.IOErrorEnum.NOT_SUPPORTED)}function initFonts(e={}){let i=t({...e,env:{GJSIFY_FONT_DIR:f.getenv(`GJSIFY_FONT_DIR`)??void 0,GJSIFY_GTK_RUNTIME_FONT_DIR:f.getenv(`GJSIFY_GTK_RUNTIME_FONT_DIR`)??void 0}}),a=i.find(e=>e.origin===`app`)?.dir,o=[],s=[],c=[],l=[],u=e.expectedFamilies??[];if(i.length===0&&u.length===0)return{dir:a,sources:o,uiFont:void 0,registered:s,declined:c,failed:l,families:[],matches:[]};let m=p.FontMap.get_default(),h=i.length===0?[]:familyNames(m);for(let e of i){let t=[],n=[],r=[],i=[];collectFaces(d.File.new_for_path(e.dir),t,i);for(let e of t.sort())try{m.add_font_file(e),n.push(e)}catch(t){if(isUnsupportedByFontMap(t)){r.push(e);continue}i.push({path:e,message:messageOf(t)})}s.push(...n),c.push(...r),l.push(...i),o.push({...e,registered:n,declined:r,failed:i})}let g=familyNames(m),_=i.length===0?[]:g.filter(e=>!h.includes(e)).sort(),v=r(u,g);captureUiFontBaseline();let y=e.uiFont===void 0?void 0:applyUiFontPolicy(e.uiFont);for(let e of l)console.warn(`initFonts: ${e.path} could not be read as an application font (${e.message}). Text asking for a family this application ships will render in a substituted one.`);for(let e of v)e.kind!==`exact`&&console.warn(`initFonts: ${n(e)}`);return{dir:a,sources:o,uiFont:y,registered:s,declined:c,failed:l,families:_,matches:v}}let m=!1,h;function captureUiFontBaseline(){if(m)return h;let e=u.Settings.get_default();if(e!==null)return h=e.gtk_font_name??void 0,m=!0,h}function uiFontBaseline(){return captureUiFontBaseline()}function applyUiFontPolicy(e){let t=typeof e==`string`?{policy:e}:e,n=t.settings===void 0?u.Settings.get_default():t.settings;if(n===null)return console.warn("applyUiFontPolicy: no Gtk.Settings — the toolkit is not initialised yet, so the policy was NOT applied. Call this after Gtk.init() (or from an application's `startup`), which is still before any window is built."),{next:void 0,kind:`uninitialised`,family:void 0,size:void 0};let r=captureUiFontBaseline(),i=l({...t,family:t.family??resolvedAdwaitaFamily(t.policy),current:n.gtk_font_name??void 0,baseline:r});return i.next===void 0||(n.gtk_font_name=i.next),i}function resolvedAdwaitaFamily(e){if(e!==`adwaita`)return;let t=adwaitaUiFontAvailability();if(t.match.family!==void 0)return t.match.family;console.warn(`applyUiFontPolicy: "${t.family}" is ${t.match.kind} on this font map, so asking for it will render in a substituted family. Register the runtime bundle's faces with initFonts() first, and check adwaitaUiFontAvailability() before offering this policy.`)}function adwaitaUiFontAvailability(e=a){let t=i(e,familyNames(p.FontMap.get_default()));return{family:e,match:t,available:t.kind!==`absent`}}function familyNames(e){return e.list_families().map(e=>e.get_name())}function collectFaces(t,n,r){try{let i=t.enumerate_children(`standard::name,standard::type`,d.FileQueryInfoFlags.NOFOLLOW_SYMLINKS,null);for(let t=i.next_file(null);t!==null;t=i.next_file(null)){let a=i.get_child(t),o=a.get_path();o!==null&&(t.get_file_type()===d.FileType.DIRECTORY?collectFaces(a,n,r):e(t.get_name())&&n.push(o))}}catch(e){r.push({path:t.get_path()??``,message:messageOf(e)})}}function messageOf(e){return e instanceof f.Error||e instanceof Error?e.message:String(e)}export{a as ADWAITA_UI_FONT_FAMILY,o as GNOME_UI_FONT_POINT_SIZE,s as UI_FONT_POLICIES,adwaitaUiFontAvailability,applyUiFontPolicy,n as describeFontFamilyMatch,initFonts,isUnsupportedByFontMap,r as matchFontFamilies,i as matchFontFamily,c as planUiFont,l as planUiFontPolicy,t as resolveFontSources,uiFontBaseline};
@@ -1,3 +1,4 @@
1
+ import Gtk from 'gi://Gtk?version=4.0';
1
2
  import { type FontFamilyMatch } from './font-families.js';
2
3
  import { type FontSource, type ResolveFontSourcesOptions } from './font-dir.js';
3
4
  import { type PlanUiFontOptions, type UiFontPlan, type UiFontPolicy } from './ui-font.js';
@@ -40,6 +41,29 @@ export interface FontFaceFailure {
40
41
  readonly path: string;
41
42
  readonly message: string;
42
43
  }
44
+ /**
45
+ * A {@link FontSource} and the faces IT contributed.
46
+ *
47
+ * WHY THE ATTRIBUTION IS PART OF THE RESULT rather than something a caller re-derives. Since the
48
+ * GTK runtime bundle started carrying the GNOME UI typeface there are TWO sources, and the flat
49
+ * {@link InitFontsResult.registered}, {@link InitFontsResult.declined} and
50
+ * {@link InitFontsResult.failed} lists span both — so "did MY staged face arrive" stopped being
51
+ * answerable from them the day a published bundle gained a face, silently, with no caller
52
+ * changing a line. The loop that hands each file to the font map is the only place that knows
53
+ * which directory it came from; anything downstream is reduced to comparing path prefixes, which
54
+ * is a guess about filesystem layout rather than a measurement.
55
+ *
56
+ * The same argument {@link InitFontsResult.families} makes about the family diff, one field over:
57
+ * only the call that did the work is in a position to take the reading.
58
+ */
59
+ export interface FontSourceOutcome extends FontSource {
60
+ /** Faces from THIS directory now on the default font map. */
61
+ readonly registered: readonly string[];
62
+ /** Faces from THIS directory the font map declined — see {@link isUnsupportedByFontMap}. */
63
+ readonly declined: readonly string[];
64
+ /** Faces from THIS directory that failed otherwise, plus the directory itself if unreadable. */
65
+ readonly failed: readonly FontFaceFailure[];
66
+ }
43
67
  /** What {@link initFonts} did, so a caller that cares can assert on it. */
44
68
  export interface InitFontsResult {
45
69
  /**
@@ -50,8 +74,16 @@ export interface InitFontsResult {
50
74
  * answering `true` because the platform's were. {@link sources} is the full list.
51
75
  */
52
76
  readonly dir: string | undefined;
53
- /** Every directory registered, runtime first. See {@link FontSource}. */
54
- readonly sources: readonly FontSource[];
77
+ /**
78
+ * Every directory registered, runtime first, and what each one contributed.
79
+ *
80
+ * THE ONLY PLACE A FACE IS ATTRIBUTED TO A DIRECTORY. Read this, not the flat lists below,
81
+ * whenever the question is about ONE source — "did the face I staged arrive", "did the
82
+ * runtime bundle bring its own" — because the flat lists answer for all of them at once and
83
+ * their counts move whenever a published bundle changes what it carries. See
84
+ * {@link FontSourceOutcome}.
85
+ */
86
+ readonly sources: readonly FontSourceOutcome[];
55
87
  /**
56
88
  * What the UI-font-size policy did, or `undefined` when it did not run.
57
89
  *
@@ -59,11 +91,17 @@ export interface InitFontsResult {
59
91
  * there, so the default leaves `gtk-font-name` alone entirely.
60
92
  */
61
93
  readonly uiFont: UiFontPlan | undefined;
62
- /** Faces now on the default font map. */
94
+ /** Faces now on the default font map, ACROSS EVERY {@link sources} entry. */
63
95
  readonly registered: readonly string[];
64
- /** Faces the font map declined as unsupported — see {@link isUnsupportedByFontMap}. */
96
+ /**
97
+ * Faces the font map declined as unsupported, across every {@link sources} entry — see
98
+ * {@link isUnsupportedByFontMap}.
99
+ */
65
100
  readonly declined: readonly string[];
66
- /** Faces that failed for any other reason. Each was warned about; none threw. */
101
+ /**
102
+ * Faces that failed for any other reason, across every {@link sources} entry. Each was warned
103
+ * about; none threw.
104
+ */
67
105
  readonly failed: readonly FontFaceFailure[];
68
106
  /**
69
107
  * The family names the default font map GAINED across this call, sorted.
@@ -144,6 +182,14 @@ export declare function initFonts(options?: InitFontsOptions): InitFontsResult;
144
182
  /** Everything {@link planUiFontPolicy} takes except what this module supplies itself. */
145
183
  export interface ApplyUiFontPolicyOptions extends PlanUiFontOptions {
146
184
  readonly policy: UiFontPolicy;
185
+ /**
186
+ * The settings object to act on. Defaults to `Gtk.Settings.get_default()`.
187
+ *
188
+ * A seam, not a knob: the null arm below is the one a real consumer hit, and on a host with a
189
+ * display there is no way to reach it through the default — `get_default()` never answers null
190
+ * once GTK is up. Passing `null` explicitly is how the test for it exists at all.
191
+ */
192
+ readonly settings?: Gtk.Settings | null;
147
193
  }
148
194
  /**
149
195
  * `gtk-font-name` as this process first found it — what the `system` policy restores.
@@ -165,10 +211,15 @@ export declare function uiFontBaseline(): string | undefined;
165
211
  * captured before the first write, which is why switching `adwaita` → `system` at runtime
166
212
  * returns the host's own `Segoe UI 9` rather than an approximation of it.
167
213
  *
168
- * `Gtk.Settings.get_default()` answers null before `Gtk.init()`, and that is a legitimate state
169
- * rather than an error: a program may register its faces before it initialises the toolkit. It
170
- * reports `unparsed` — the arm that already means "nothing to act on, so nothing changed" —
171
- * instead of throwing a caller out of a font call over a setting.
214
+ * `Gtk.Settings.get_default()` answers null before `Gtk.init()`. That is not an error to throw
215
+ * over — a program may register its faces before it initialises the toolkit — but it is NOT the
216
+ * same answer as "the policy ran and had nothing to do", and reporting it as `unparsed` said it
217
+ * was. A consumer that called this at module scope got a plan indistinguishable from a host that
218
+ * needed no correction, so its setting silently did nothing: measured in Learn6502 0.8.0, where
219
+ * `ui-font: policy=size -> unparsed (unchanged)` was printed on macOS and on Windows — the one
220
+ * platform whose 16 px against GNOME's 19 is the reason the policy exists. It now reports
221
+ * `uninitialised` and says so once, because a caller that is too early can only find out from
222
+ * here.
172
223
  */
173
224
  export declare function applyUiFontPolicy(request: UiFontPolicy | ApplyUiFontPolicyOptions): UiFontPlan;
174
225
  /**
@@ -21,7 +21,7 @@ export type UiFontPolicy = 'system' | 'size' | 'adwaita';
21
21
  */
22
22
  export declare const UI_FONT_POLICIES: readonly UiFontPolicy[];
23
23
  /** The reasons a plan gives. See {@link UiFontPlan.kind}. */
24
- export type UiFontPlanKind = 'raised' | 'family' | 'restored' | 'kept' | 'unparsed';
24
+ export type UiFontPlanKind = 'raised' | 'family' | 'restored' | 'kept' | 'unparsed' | 'uninitialised';
25
25
  /** What {@link planUiFont} or {@link planUiFontPolicy} decided, and why. */
26
26
  export interface UiFontPlan {
27
27
  /** The `gtk-font-name` to set, or `undefined` when nothing should change. */
@@ -31,7 +31,17 @@ export interface UiFontPlan {
31
31
  * `family` — a family override was asked for and applied;
32
32
  * `restored` — the host's ORIGINAL value is being put back (the `system` policy);
33
33
  * `kept` — nothing to do: the setting already says what the policy wants;
34
- * `unparsed` — the current value carries no point size this can reason about.
34
+ * `unparsed` — the current value carries no point size this can reason about;
35
+ * `uninitialised` — there was no `Gtk.Settings` to act on, so the policy did NOT run.
36
+ *
37
+ * THE LAST TWO ARE NOT THE SAME ANSWER and used to be reported as one. Both leave
38
+ * `gtk-font-name` alone, which is why collapsing them looked harmless — but `unparsed` means
39
+ * the policy ran and correctly declined, while `uninitialised` means it never ran at all. A
40
+ * consumer that calls this before `Gtk.init()` gets a plan that reads exactly like a host
41
+ * that needed nothing, so the setting it built is dead and its own log line says so in words
42
+ * that look fine. Learn6502 shipped that: `ui-font: policy=size -> unparsed (unchanged)` on
43
+ * macOS AND Windows, from a call at module scope, with the size correction never applied on
44
+ * the one platform it exists for.
35
45
  */
36
46
  readonly kind: UiFontPlanKind;
37
47
  /** The family in effect after the plan, for reporting. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/gtk-host",
3
- "version": "0.51.0",
3
+ "version": "0.51.1",
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",
@@ -101,7 +101,7 @@
101
101
  "@girs/gtk-4.0": "5.0.0",
102
102
  "@girs/pango-1.0": "5.0.0",
103
103
  "@girs/pangocairo-1.0": "5.0.0",
104
- "@gjsify/adwaita-core": "^0.51.0"
104
+ "@gjsify/adwaita-core": "^0.51.1"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "@vue/runtime-core": "^3.5.0",
@@ -124,10 +124,10 @@
124
124
  }
125
125
  },
126
126
  "devDependencies": {
127
- "@gjsify/cli": "^0.51.0",
127
+ "@gjsify/cli": "^0.51.1",
128
128
  "@vue/runtime-core": "^3.5.0",
129
129
  "solid-js": "^1.9.0",
130
- "@gjsify/unit": "^0.51.0",
130
+ "@gjsify/unit": "^0.51.1",
131
131
  "@types/node": "^25.9.2",
132
132
  "typescript": "^6.0.3",
133
133
  "@gjsify/node-gi": "file:../../node-gi/node-gi",