@yuneta/gobj-ui 7.26.31 → 7.26.32

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/README.md CHANGED
@@ -95,8 +95,8 @@ consumers. They are independent snapshots (no shared git ancestry):
95
95
 
96
96
  | Line | Branch | Latest tag | Layout | Consumed by | How | Status |
97
97
  |------|--------|------------|--------|-------------|-----|--------|
98
- | **v2** | `main` | `7.26.x` | `src/` subdir | **gui_agent**, **gui_treedb**, **wattyzer**, the **yunovatios** GUIs | published npm `@yuneta/gobj-ui` (dist-tag `latest`) | active development |
99
- | **v1** | `v1` | `1.0.4` | `src/` subdir | **estadodelaire**, **hidraulia** | published npm `@yuneta/gobj-ui@^1.0.4` (dist-tag `legacy`) | frozen, maintenance-only |
98
+ | **v2** | `main` | `7.26.x` | `src/` subdir | **gui_agent**, **gui_treedb**, **wattyzer**, the **yunovatios** GUIs, **estadodelaire** (since 2026-10-13) | published npm `@yuneta/gobj-ui` (dist-tag `latest`) | active development |
99
+ | **v1** | `v1` | `1.0.4` | `src/` subdir | **hidraulia** | published npm `@yuneta/gobj-ui@^1.0.4` (dist-tag `legacy`) | frozen, maintenance-only |
100
100
 
101
101
  - **v2 / `main`** is the active development line: the declarative shell
102
102
  (legacy-stack-free since `3.0.0`). It is embedded as a git submodule in
@@ -109,9 +109,10 @@ consumers. They are independent snapshots (no shared git ancestry):
109
109
  (`npm run deploy-round` does the round). `test-app/` is the one exception:
110
110
  it consumes the working tree (`file:..`), which is what it is for.
111
111
  - **v1 / `v1`** is the frozen legacy-only stack (the declarative shell is not on
112
- this line). It is **published to npm**; estadodelaire and hidraulia depend on
113
- `@yuneta/gobj-ui@^1.0.4` from the registry. Land only maintenance fixes here,
114
- then `npm publish` a new `1.x`.
112
+ this line). It is **published to npm**; hidraulia depends on
113
+ `@yuneta/gobj-ui@^1.0.4` from the registry (estadodelaire left it for v2 on
114
+ 2026-10-13, and hidraulia is next). Land only maintenance fixes here, then
115
+ `npm publish` a new `1.x`.
115
116
 
116
117
  All new feature work lands on `main`/v2.
117
118
 
@@ -3835,28 +3836,57 @@ text is a key, `"configuración"` included.
3835
3836
  dependency of this package since `7.26.29`: an app need not declare it). A
3836
3837
  LOCALE is not a source: it is parsed, and then imported, so its top-level code
3837
3838
  runs -- the app's own code, but code. Comments are not code. A key is read
3838
- where it is WRITTEN -- given to `t()`, as an `i18n` or `data-i18n*` value of
3839
- an object literal, by `setAttribute()`, or (since `7.26.29`) written as a
3840
- member, `attrs.i18n = "save"`, `attrs["data-i18n-title"] = "save"` -- and
3841
- FOLLOWED where the source writes it out:
3839
+ where it is WRITTEN -- given to a translator, as an `i18n` or `data-i18n*`
3840
+ value of an object literal, by `setAttribute()`, or (since `7.26.29`) written
3841
+ as a member, `attrs.i18n = "save"`, `attrs["data-i18n-title"] = "save"` -- and
3842
+ FOLLOWED where the source writes it out.
3843
+
3844
+ A **translator** is one notion, the same everywhere the reader asks "is this a
3845
+ call of t()?" (since `7.26.32`; up to `7.26.31` it was a call NAMED `t` or
3846
+ `tr`): a name `t` or `tr` (whatever binds it), a member `.t` / `.tr`
3847
+ (`i18next.t`), i18next's `t` imported under any name (`import {t as tt} from
3848
+ "i18next"`), or an alias of one -- a const (`const translate = t`), a
3849
+ `.bind()` (`i18next.t.bind(i18next)`), a re-export of a module the reader
3850
+ reads --, called as `t(k)`, `t?.(k)`, `t.call(x, k)` or `t.apply(x, [k])`. An
3851
+ ARRAY given to it is i18next's list of fallback keys, and each one is a key:
3852
+ `t([specific, "generic"])` asks for "generic". The places, since `7.26.32`, also
3853
+ include `el.dataset.i18nTitle = k` (`dataset.i18nX` IS the attribute
3854
+ `data-i18n-x`), and an attribute or property NAMED by a const:
3855
+ `el.setAttribute(ATTR, k)`, `{[ATTR]: k}`, `x[ATTR] = k`, with `const ATTR =
3856
+ "data-i18n-title"`. What is followed:
3842
3857
 
3843
3858
  - a string literal, a template with no `${}`, and every branch of `?:`, `||`
3844
3859
  and `??` (the right one of `&&`), so
3845
3860
  `"data-i18n": pending ? "loading" : "click to load"` asks for both, and
3846
3861
  `comment || "print-tranger failed"` for its fallback;
3847
3862
  - a `const`, to its value, in the scope that declares it (a parameter or a
3848
- `let` of the same name hides it);
3863
+ `let` of the same name hides it), and what a destructuring reads from it
3864
+ (since `7.26.32`: `const {title = "untitled"} = LABELS`);
3865
+ - an IMPORT of a const or of a function, into the module it names, when the
3866
+ reader reads that module -- a file of the app, a module of this library;
3867
+ re-exports are followed (since `7.26.32`). A package (gobj-js, i18next) is
3868
+ not read, and neither is the export of a `let`;
3849
3869
  - a property of a const object literal: `DEFAULT_LABEL.ok`, and for a
3850
- computed one, `NOTES[verdict]`, every value the object holds;
3870
+ computed one, `NOTES[verdict]`, every value the object holds; also of a
3871
+ COPY of one (since `7.26.32`): `{...BASE, title: "t"}`,
3872
+ `Object.assign({}, BASE)`;
3851
3873
  - the element of a const array literal that a `.map()`, `.forEach()`, ...
3852
3874
  callback or a `for...of` walks, and what a destructuring reads from it
3853
3875
  (`for(const {key} of ROWS)`);
3854
3876
  - `String(x)`, and a template that is only `${x}`, as `x` (since `7.26.29`);
3855
- - the default of a parameter, `function f(msg = "fallback")`, besides the
3856
- parameter (since `7.26.29`);
3857
- - a call of a function of the same module whose every `return` is one of
3858
- these (since `7.26.29`). One that returns anything else -- a parameter, a
3859
- variable, a concatenation -- is not followed.
3877
+ - a parameter, however it is read (since `7.26.32`): `opts.title`,
3878
+ `opts?.title`, `arguments[1]`, `rest[0]` of a rest parameter, a destructuring
3879
+ in the parameter or in the body; and its default, `function f(msg =
3880
+ "fallback")`, besides the parameter (since `7.26.29`);
3881
+ - a call of a function whose every `return` is one of these (since
3882
+ `7.26.29`): of the same module, or (since `7.26.32`) imported from a module
3883
+ the reader reads, called by `.call()` / `.apply()` too. One that returns
3884
+ anything else -- a parameter, a variable, a concatenation -- is not
3885
+ followed. What a function returns is read ONCE, and a RECURSION is not
3886
+ followed: a call of a function that is being read is a value the reader
3887
+ cannot follow. Up to `7.26.31` a function was read again at every call of
3888
+ it, so a helper that calls itself in two branches (`label_of(n)` walking
3889
+ up a tree) took more than two minutes, and in three it never ended.
3860
3890
 
3861
3891
  ```js
3862
3892
  const KEY = "save";
@@ -3873,12 +3903,12 @@ function pick(ok) {
3873
3903
  t(pick(answer)); // asks for both
3874
3904
  ```
3875
3905
 
3876
- What it cannot follow cannot be known: a `let`, a parameter, an import, a
3877
- call, a concatenation, data from a backend. A key that arrives that way is
3878
- PUBLISHED by its module (below), and the place it is read is MARKED; a marked
3879
- `let` -- or a marked member or element of one -- is the one exception to "not
3880
- followed" (below). A source file that cannot be parsed fails the build, because
3881
- its keys would go unchecked.
3906
+ What it cannot follow cannot be known: a `let`, a parameter of an app's
3907
+ function, a call it does not read, a concatenation, data from a backend. A key
3908
+ that arrives that way is PUBLISHED by its module (below), and the place it is
3909
+ read is MARKED; a marked `let` -- or a marked member or element of one -- is the
3910
+ one exception to "not followed" (below). A source file that cannot be parsed
3911
+ fails the build, because its keys would go unchecked.
3882
3912
 
3883
3913
  **A key handed to a FUNCTION is read where it is handed.**
3884
3914
  `yui_shell_show_error(shell, "some records were gone before the delete")` asks
@@ -3900,10 +3930,16 @@ A position is an argument (`"1"`), a property of the object given there
3900
3930
  (`"2.title"`, also through `Object.assign()` or a spread of a const object),
3901
3931
  or a property of each object of an array (`"1.actions[].label"`). What is
3902
3932
  given at those positions is read like any other key (literals, consts,
3903
- branches): in the app's own code, on a call bound to an import of that
3904
- function from gobj-ui (an app function of the same name is not mistaken for
3905
- it), and in every module the app mounts, where an entry with
3906
- `exported: false` is a helper of its own module. A position may also hold
3933
+ branches): in the app's own code, on a call that reaches that function of
3934
+ gobj-ui (an app function of the same name is not mistaken for it), and in
3935
+ every module the app mounts, where an entry with `exported: false` is a
3936
+ helper of its own module. A call reaches it however it names it (since
3937
+ `7.26.32`; up to `7.26.31` only an import by its own name, or a namespace
3938
+ member): an import under any name, a re-export from a file of the app
3939
+ (`export {yui_shell_show_info as say} from "@yuneta/gobj-ui/src/shell_modals.js"`),
3940
+ a const alias (`const show = yui_shell_show_info`), a map in a const
3941
+ (`const API = {info: yui_shell_show_info}; API.info(...)`, imported too),
3942
+ `.call(x, ...)` and `.apply(x, [...])`. A position may also hold
3907
3943
  DOM, which is no key (since `7.26.29`): an array (a `createElement2` spec), a
3908
3944
  call of `createElement2()`, `createOneHtml()` or `html_markup()` imported
3909
3945
  from gobj-js, of `text_node()` from gobj-ui, of `document.createElement()`
@@ -3924,12 +3960,14 @@ gclass name, which titles it).
3924
3960
  (since `7.26.29`: up to `7.26.28` the table lacked 39 of them, and 6 entries
3925
3961
  lacked positions, so a literal handed to one went unasked -- REV-CLOUD
3926
3962
  V21.4.1). `src/i18n_key_functions.test.js` reads every module and finds each
3927
- function that hands one of its PARAMETERS on to a place a key is read --
3928
- `t()`, an `i18n`, `code` or `data-i18n*` value, a key position of a function
3929
- of the table -- the parameter itself or what is read from it (`opts.title`, a
3930
- destructured `{label}`, each `items[].name`), directly or through a `let` it is
3931
- given to. The search runs to a fixpoint: a function that hands its parameter
3932
- to one it found is one too.
3963
+ function that hands one of its PARAMETERS on to a place a key is read -- a
3964
+ translator (any of the forms above), an `i18n`, `code` or `data-i18n*` value
3965
+ (`el.dataset.i18nTitle` included), a key position of a function of the table
3966
+ -- the parameter itself or what is read from it: `opts.title`, `opts?.title`,
3967
+ a destructured `{label}` in the parameter or in the body, each `items[].name`,
3968
+ `arguments[1]`, `rest[0]`, a copy (`{...opts}`, `Object.assign({}, opts)`),
3969
+ directly or through a `let` it is given to. The search runs to a fixpoint: a
3970
+ function that hands its parameter to one it found is one too.
3933
3971
 
3934
3972
  - A function called by NAME -- in its module, or exported -- must be in the
3935
3973
  table with that position, or the test fails, naming the entry to add:
@@ -3946,7 +3984,47 @@ to one it found is one too.
3946
3984
  writes `i18n: /* i18n: data */ title_key`: the window's title is a key its
3947
3985
  host chose.
3948
3986
  - What a gobj keeps in its `priv` is its own state, not what a caller hands:
3949
- a path through the `priv` of a parameter is no position.
3987
+ a path through the `priv` of a parameter is no position. So is what is
3988
+ read from a parameter `gobj` (the convention of every gclass function):
3989
+ its attrs, `gobj_read_attr(gobj, "title")`, were set by whoever created it.
3990
+ - A parameter handed on through what NO position can name fails too (since
3991
+ `7.26.32`), unless it is marked where it is handed: a key transformed
3992
+ (`t(k.toLowerCase())`, `"prefix " + k`, a template with `${}`, a call of a
3993
+ helper that does not return it as it is), an iteration of
3994
+ `Object.entries(opts)`, a rest parameter walked (`rest.forEach(...)`),
3995
+ `arguments[i]` by a computed index, a part of a parameter that an array
3996
+ pattern destructures. Its callers' keys reach no reader, so either the key
3997
+ is handed on as it arrives -- and the function goes in the table -- or the
3998
+ hand-off is marked `/* i18n: data */` (or a list). The test names the
3999
+ function, the parameter and the line:
4000
+
4001
+ ```js
4002
+ function caption(gobj, k) { return t(k.toLowerCase()); }
4003
+ ```
4004
+
4005
+ ```
4006
+ x.js:1 caption() hands its parameter [1] to a place a key is read through `k.toLowerCase()`,
4007
+ which no position can name: hand the key on as it arrives (the function then goes in
4008
+ the table), or mark it where it hands it
4009
+ ```
4010
+
4011
+ It found four in the library itself: the column labels of a form's
4012
+ template in `build_html_form()` and `template2columns()`
4013
+ (`Object.entries(template)`: the host's schema, marked data), a label's key
4014
+ read back from its DOM (`name_form_control()` of `c_g6_nodes_tree.js`,
4015
+ marked data), and the message of `mark_field_validity()`, which mixes a key
4016
+ with the json field's own error (marked with its lists).
4017
+ - **The table is CORRECT, not only complete** (since `7.26.32`): the same
4018
+ search, started from NO table, must find every entry, at every position,
4019
+ with the same `exported`. An entry nothing forwards to -- a wrong position,
4020
+ a function that no longer hands it on -- fails, and so does an `exported`
4021
+ that disagrees with the module (it hides the calls of other modules). Up to
4022
+ `7.26.31` an extra entry passed every test. The check found two: an
4023
+ `exported: false` on a function its module exports (`create_floating_icon`),
4024
+ and `yui_selection_bar`'s `1.actions[].label`, true but invisible to the
4025
+ search, because the label travelled through a DOM property
4026
+ (`$b._yui_label`); the function now paints each label from the actions
4027
+ themselves, where the reader follows it.
3950
4028
 
3951
4029
  Naming a function moves the marker to where the value ENTERS: its callers give
3952
4030
  it a literal, a const, a position of their own, or a marker. The nav's items
@@ -3982,11 +4060,11 @@ that way. The same code with a `let` asks for neither.
3982
4060
  export const MY_VIEW_I18N_KEYS = ['shape', 'radius', 'apply to'];
3983
4061
  ```
3984
4062
 
3985
- The list is read as DATA, never run: `export const` (or `let`, `var`),
3986
- `const X = [...]; export {X}` (or `export {x as X_I18N_KEYS}`), wrapped in
3987
- `Object.freeze()` or not, each element a string literal, a template with no
3988
- `${}`, or (since `7.26.29`) a SPREAD of another array of the same module read
3989
- the same way -- a top-level `const`, or a list the module publishes:
4063
+ The list is read as DATA, never run: `export const`, or `const X = [...];
4064
+ export {X}` (or `export {x as X_I18N_KEYS}`), wrapped in `Object.freeze()` or
4065
+ not, each element a string literal, a template with no `${}`, or (since
4066
+ `7.26.29`) a SPREAD of another array of the same module read the same way -- a
4067
+ top-level `const`, which a list the module publishes is:
3990
4068
 
3991
4069
  ```js
3992
4070
  const COMMON_KEYS = ['shape', 'radius'];
@@ -3998,16 +4076,31 @@ Anything named `*_I18N_KEYS` (the name in any letter case) that no consumer
3998
4076
  can read **fails the build**, naming the file and the list, in the app's own
3999
4077
  code too: an export that is not such an array (an element that is a variable,
4000
4078
  a call, a template with `${}`, a spread of an import or of a `let`), a list
4001
- that is not exported, one declared inside a function or a block, one declared
4002
- by destructuring (`export const {X_I18N_KEYS} = tables`), one exported under a
4003
- name that does not end in `_I18N_KEYS`. Up to `7.26.27` the regex that read
4004
- the lists dropped such a list in silence, and a key holding `]` dropped the
4005
- whole array; up to `7.26.28` a spread of a const of the module was refused
4006
- too.
4079
+ declared with `let` or `var` (since `7.26.32`: it can be given other keys,
4080
+ which nobody reads), a list that is not exported, one declared inside a
4081
+ function or a block, one declared by destructuring (`export const
4082
+ {X_I18N_KEYS} = tables`), one exported under a name that does not end in
4083
+ `_I18N_KEYS`. Up to `7.26.27` the regex that read the lists dropped such a
4084
+ list in silence, and a key holding `]` dropped the whole array; up to
4085
+ `7.26.28` a spread of a const of the module was refused too.
4086
+
4087
+ **A published list is never MUTATED** (since `7.26.32`): it is read as it is
4088
+ WRITTEN, so a key it is given later is asked for by nobody. `X.push(k)`,
4089
+ `X.splice(i, 0, k)`, `X.unshift(k)`, `X.fill(k)`, `X[i] = k`, `X.length = n`,
4090
+ `Object.assign(X, ...)` and `delete X[i]` fail the build, in the module that
4091
+ publishes the list and in every module that imports it (under any name). Up
4092
+ to `7.26.31` an `export let` list was read and could be pushed into, and the
4093
+ pushed keys went unasked:
4094
+
4095
+ ```js
4096
+ export let MY_VIEW_I18N_KEYS = ["shape"]; // fails: a published list is an export const
4097
+ MY_VIEW_I18N_KEYS.push("radius"); // fails: "radius" would be asked for by nobody
4098
+ ```
4007
4099
 
4008
4100
  **A MARKER says where the keys of a place come from.** It is a comment that
4009
- stands right before the node it marks (only whitespace between): an argument,
4010
- a branch of one, a property value, or the whole call. Two kinds:
4101
+ stands right before the node it marks (only whitespace between, and since
4102
+ `7.26.32` a parenthesis: `/* i18n: data */ (a || b)` marks `a || b`): an
4103
+ argument, a branch of one, a property value, or the whole call. Two kinds:
4011
4104
 
4012
4105
  ```js
4013
4106
  yui_shell_show_error(shell, /* i18n: data */ comment || "print-tranger failed", {t: t});
@@ -4021,7 +4114,15 @@ confirm_then(gobj, /* i18n: C_YUI_FORM_I18N_KEYS */ kw.warning || "all changes w
4021
4114
  above) are still read.
4022
4115
  - `i18n: <NAME>_I18N_KEYS` (several by commas) -- the keys come from those
4023
4116
  published lists. The keys of the marked expression that the reader CAN
4024
- follow must be in them: a marker is a claim, and it is checked.
4117
+ follow must be in them: a marker is a claim, and it is checked. A key BUILT
4118
+ at run time is published that way and marked with its list -- gui_agent's
4119
+ monitor names a control's label `monitor ${control}`, and publishes the six
4120
+ keys that template builds in `C_AGENT_MONITOR_I18N_KEYS`. A template with
4121
+ `${}` under a list marker must have the SHAPE of some key of the lists
4122
+ (since `7.26.32`): `monitor ${control}` is checked against `^monitor .*$`,
4123
+ so a marker naming the wrong list fails. Every key of a published list is
4124
+ asked for, so a control whose key the list lacks is the one thing no check
4125
+ sees: the list sits next to the template, and a comment says so.
4025
4126
 
4026
4127
  **A `data` marker is a claim too** (since `7.26.29`). On what the reader
4027
4128
  follows to keys and nothing else -- a literal, a template with no `${}`, a
@@ -4042,6 +4143,31 @@ yui_shell_show_info(shell, /* i18n: data */ pick());
4042
4143
  `/* i18n: data */ comment || "print-tranger failed"` stays legal: `comment` is
4043
4144
  data, and the fallback is read anyway.
4044
4145
 
4146
+ Since `7.26.32` the claim is checked further:
4147
+
4148
+ - the call may be of a function IMPORTED from a module the reader reads (a
4149
+ file of the app, a module of the library), and the const may be imported:
4150
+ both are followed into their module, so `/* i18n: data */ pick()` fails
4151
+ there too;
4152
+ - a data marker on a call whose returns MIX keys with data fails: the reader
4153
+ does not read such a call (it reads a call only when it knows every
4154
+ return), so the marker would hide the key. Split the branches, so the key is
4155
+ handed where it is read and only the data is marked:
4156
+
4157
+ ```js
4158
+ function label(kw) { return kw.ok? "accepted" : kw.comment; }
4159
+ yui_shell_show_info(shell, /* i18n: data */ label(kw)); // fails
4160
+ yui_shell_show_info(shell, /* i18n: data */ kw.comment || "accepted"); // the same, split
4161
+ ```
4162
+
4163
+ The library had it twice: `refusal_text()` of the treedb views returned the
4164
+ backend's words or "print-tranger failed". It returns the words only now,
4165
+ and the call writes the fallback. Under a LIST marker such a call is
4166
+ checked, not refused: its keys are asked for and must be in the lists;
4167
+ - a data marker on an ARRAY OF STRING LITERALS fails: it is no DOM (a
4168
+ `createElement2` spec carries its attrs, an object, in its second slot), so
4169
+ the strings would be hidden.
4170
+
4045
4171
  **A marked `let` (or `var`) is held to its marker.** A marker that names a list
4046
4172
  used to let a `let` pass unread, whatever it held: a new literal branch on it
4047
4173
  was neither asked for nor checked (REV-CLOUD V20). Now, when what a marker
@@ -4102,10 +4228,19 @@ would not carry what is pushed into it to the reader.
4102
4228
 
4103
4229
  `validate-locales` checks every marker in what it scans, in an app's code too:
4104
4230
  a comment that starts with `i18n:` and is no marker, a marker that stands
4105
- before no place a key is read (a key position, `t()`, an `i18n` or
4231
+ before no place a key is read (a key position, a translator, an `i18n` or
4106
4232
  `data-i18n*` value), a marker that names a list no module the app mounts
4107
- publishes, a marked key that is not in the lists it names, a marked variable
4108
- that only ever holds literals -- each one fails, with the file and the line.
4233
+ publishes, a marked key that is not in the lists it names, a marked template
4234
+ with no key of its shape in them, a marked variable that only ever holds
4235
+ literals -- each one fails, with the file and the line. A marker that stands
4236
+ before no place says what it stands before, and why that is none (since
4237
+ `7.26.32`; it used to say only *"stands before no place a key is read"*):
4238
+
4239
+ ```
4240
+ main.js:8: the i18n marker stands before `1;`, the value a variable is DECLARED with: mark it
4241
+ where the variable is handed to a place a key is read. A marker marks the value it stands
4242
+ right before, where a key is read: ...
4243
+ ```
4109
4244
 
4110
4245
  **The rule is per CALL, in the library and in the app.**
4111
4246
  `src/i18n_key_functions.test.js` reads every module of the library with the
@@ -4132,7 +4267,11 @@ src/main.js:12: yui_shell_confirm_yesno() [2.title] is given opts, which the rea
4132
4267
 
4133
4268
  A TRANSLATED text handed to a key position fails even when it is marked: the
4134
4269
  function translates it again and marks it `data-i18n` with the translation,
4135
- so it never changes language. Hand it the key:
4270
+ so it never changes language. Hand it the key. A translated text is the value
4271
+ of any TRANSLATOR (since `7.26.32`; up to `7.26.31` only a call named `t`, so
4272
+ `tr("save")`, an alias of t, `t?.()` or `t.call()` passed marked -- and an
4273
+ unmarked `tr()` failed with *"cannot follow"*, whose advice was the marker that
4274
+ then let it pass):
4136
4275
 
4137
4276
  ```js
4138
4277
  yui_install_ask_once(shell, {t: t, message: t("install this app")}); // fails
@@ -4142,7 +4281,7 @@ yui_install_ask_once(shell, {t: t, message: "install this app"}); // the ke
4142
4281
  ```
4143
4282
  src/c_app.js:734 (call at line 732): yui_install_ask_once() [1.message] is given t("install this app"):
4144
4283
  a TRANSLATED text where a KEY goes (translated again and marked data-i18n with the translation,
4145
- it never changes language): hand it the key
4284
+ it never changes language, and no marker excuses it): hand the key, not t(key)
4146
4285
  ```
4147
4286
 
4148
4287
  In an app, what a wrapper of its own hands on is marked where it hands it:
@@ -4163,10 +4302,11 @@ module. The same test fails when a marker is not one
4163
4302
  or stands nowhere; when a marker names a list that is not published by its
4164
4303
  module or by one it imports (a consumer would not read it); when a marked key
4165
4304
  the reader can follow is not in the list; when a marked variable only ever
4166
- holds literals; when a list is not readable, holds a key that is not
4167
- lower-case ASCII, holds one twice, or holds one written nowhere else in its
4168
- module (stale); when an entry of the table names a function its module does
4169
- not declare.
4305
+ holds literals; when a list is not readable, is mutated, holds a key that is
4306
+ not lower-case ASCII, holds one twice, or holds one written nowhere else in
4307
+ its module (stale); when an entry of the table names a function its module
4308
+ does not declare, or (since `7.26.32`) a position the search for forwarders
4309
+ does not find.
4170
4310
 
4171
4311
  **Every key of every list is PINNED**: dropping it from its list fails a test,
4172
4312
  or loses nothing. For each key, one of these holds, or the test fails with
@@ -4210,19 +4350,41 @@ the node graph's key-taking helpers from its syntax tree, and
4210
4350
  `yui_dev_controls.test.js` builds the Developer window and compares every key
4211
4351
  it asks for or carries with the list.
4212
4352
 
4213
- **What is NOT pinned.** `t()` is not a key function, and only the positions of
4214
- `I18N_KEY_FUNCTIONS` are held to the per-call rule. So a key handed to `t()`,
4215
- `i18n` or a `data-i18n*` value by an UNMARKED `let`, a call the reader does not
4216
- follow or a concatenation is invisible to the validator and fails no test --
4217
- in the library and in an app alike. (A PARAMETER is not, in the library: the
4218
- forwarder test puts its function in the table, or demands a marker.) So is
4219
- what is written INTO a `const` table after its declaration (`LIST.push("k")`,
4220
- `TABLE.k = "k"`): the reader follows a const to its value, and only a marked
4221
- `let` is followed into what is written into it. It is found only by reading
4222
- the page in another language. In an app, a key its callers hand a wrapper of
4223
- its own reaches the locales only through a list the module publishes: the
4224
- wrapper's hand-off must be marked (the rule per call), and nothing ties its
4225
- callers' literals to the list.
4353
+ **What stays INVISIBLE** -- read by no check, failing nothing; found only by
4354
+ reading the page in another language:
4355
+
4356
+ - a key handed to a translator, an `i18n` or a `data-i18n*` value by an
4357
+ UNMARKED `let`, a call the reader does not follow (a recursion, one whose
4358
+ returns mix keys and data, a function of a package) or a computed string:
4359
+ a translator is not a key function, and only the positions of
4360
+ `I18N_KEY_FUNCTIONS` are held to the per-call rule -- in the library and in
4361
+ an app alike. (A PARAMETER is not, in the library: the forwarder test puts
4362
+ its function in the table, or demands a marker, also when no position can
4363
+ name it.);
4364
+ - a translator the reader does not know as one: a name other than `t` / `tr`
4365
+ that no const or import makes an alias of -- a parameter `translate`, a
4366
+ `let` alias, `opts.translate(k)`, `i18next.getFixedT()`;
4367
+ - a key function reached through a parameter (`apply(fn)` calling
4368
+ `fn(shell, "k")`), a `let` given more than one function, a map in a `let`,
4369
+ a computed member (`API[name](...)`), or `.apply()` of an array the source
4370
+ does not write out;
4371
+ - a `data-i18n*` attribute written through an alias of `dataset`
4372
+ (`const ds = el.dataset; ds.i18nTitle = k`), by `Object.assign()` into it,
4373
+ or named by a `let`;
4374
+ - what is written INTO a `const` table after its declaration
4375
+ (`TABLE.k = "k"`): the reader follows a const to its value, and only a
4376
+ marked `let` is followed into what is written into it (a published list
4377
+ that is written into fails: above);
4378
+ - what a module of a package exports (gobj-js, i18next), and an exported
4379
+ `let`;
4380
+ - in the library's forwarder search, a key stored in a heap object or a DOM
4381
+ node and read back somewhere else (`$b._yui_label` was one), and what a
4382
+ caller puts in a gobj's attrs (a window's `title` given at `gobj_create()`);
4383
+ - in an app, a key its callers hand a wrapper of its own reaches the locales
4384
+ only through a list the module publishes: the wrapper's hand-off must be
4385
+ marked (the rule per call), and nothing ties its callers' literals to the
4386
+ list -- nor a key built at run time (a template) to the list its marker
4387
+ names, beyond the shape of the template.
4226
4388
 
4227
4389
  **What fails the build, besides a key no locale defines:**
4228
4390
 
@@ -4233,9 +4395,12 @@ callers' literals to the list.
4233
4395
  - a locale that looks like one and exports no `{<code>}` with a
4234
4396
  `translation` dict;
4235
4397
  - a source file that cannot be parsed;
4236
- - a published list no consumer can read (above);
4237
- - a marker that is wrong (above), a `data` marker on what the reader follows
4238
- to keys included;
4398
+ - a published list no consumer can read (above), declared with `let` or
4399
+ `var`, or mutated;
4400
+ - a marker that is wrong (above): a `data` marker on what the reader follows
4401
+ to keys, or on what would hide a key (a call mixing keys and data, an array
4402
+ of string literals), a list marker on a template whose shape no key of the
4403
+ list has;
4239
4404
  - a key position of a key function, in the app's own code, given what the
4240
4405
  reader cannot follow and unmarked, or given a translated text (the rule per
4241
4406
  call, above).
@@ -27190,12 +27190,14 @@ function answer_print_tranger_refused$1(gobj, path, answer) {
27190
27190
  }
27191
27191
  /************************************************************
27192
27192
  * The text of a transport's refusal: gobj_command() answers a string,
27193
- * a command response or a number.
27193
+ * a command response or a number. Data only ("" when it says
27194
+ * nothing): the caller writes its own key as the fallback, where
27195
+ * validate-locales reads it.
27194
27196
  ************************************************************/
27195
27197
  function refusal_text$1(ret) {
27196
27198
  if (typeof ret === "string" && ret) return ret;
27197
27199
  if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
27198
- return "print-tranger failed";
27200
+ return "";
27199
27201
  }
27200
27202
  /************************************************************
27201
27203
  * The whole tranger, for the raw-json viewer.
@@ -27223,7 +27225,7 @@ function request_print_tranger$1(gobj, path) {
27223
27225
  }, gobj);
27224
27226
  if (ret) {
27225
27227
  (0, _yuneta_gobj_js.log_error)(ret);
27226
- answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
27228
+ answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) || "print-tranger failed" });
27227
27229
  return;
27228
27230
  }
27229
27231
  if (path) priv.drills_in_flight.add(path);
@@ -28364,8 +28366,8 @@ function yui_clear_selection(tabulator) {
28364
28366
  * through `refresh_language()`. Call it on EV_LANGUAGE_CHANGED.
28365
28367
  ***************************************************************/
28366
28368
  function yui_selection_bar(t, opts) {
28367
- let o = opts || {};
28368
- let actions = Array.isArray(o.actions) ? o.actions : [];
28369
+ const o = opts || {};
28370
+ const actions = Array.isArray(o.actions) ? o.actions : [];
28369
28371
  let name = o.name || "TABLE";
28370
28372
  let count = 0;
28371
28373
  let $count = (0, _yuneta_gobj_js.createElement2)([
@@ -28392,7 +28394,6 @@ function yui_selection_bar(t, opts) {
28392
28394
  ]],
28393
28395
  { click: action.on_click }
28394
28396
  ]);
28395
- $b._yui_label = action.label || "";
28396
28397
  $buttons.push($b);
28397
28398
  }
28398
28399
  let $clear = (0, _yuneta_gobj_js.createElement2)([
@@ -28425,15 +28426,18 @@ function yui_selection_bar(t, opts) {
28425
28426
  function label_of($b) {
28426
28427
  return $b.querySelector("span.icon ~ span");
28427
28428
  }
28429
+ function paint_label($b, key) {
28430
+ let $label = label_of($b);
28431
+ if ($label && key) $label.textContent = t(key);
28432
+ $b.title = key ? t(key) : "";
28433
+ $b.setAttribute("aria-label", $b.title);
28434
+ }
28428
28435
  function refresh() {
28429
28436
  $count.textContent = t("{{n}} selected", { n: count });
28430
- for (let $b of $buttons.concat([$clear])) {
28431
- let $label = label_of($b);
28432
- const key = $b === $clear ? "clear selection" : $b._yui_label;
28433
- if ($label && key) $label.textContent = t(key);
28434
- $b.title = key ? t(key) : "";
28435
- $b.setAttribute("aria-label", $b.title);
28436
- }
28437
+ actions.forEach((action, i) => {
28438
+ paint_label($buttons[i], action.label || "");
28439
+ });
28440
+ paint_label($clear, "clear selection");
28437
28441
  $el.classList.toggle("is-hidden", count <= 0);
28438
28442
  }
28439
28443
  function set_count(n) {
@@ -71278,12 +71282,14 @@ function answer_print_tranger_refused(gobj, path, answer) {
71278
71282
  }
71279
71283
  /************************************************************
71280
71284
  * The text of a transport's refusal: gobj_command() answers a string,
71281
- * a command response or a number.
71285
+ * a command response or a number. Data only ("" when it says
71286
+ * nothing): the caller writes its own key as the fallback, where
71287
+ * validate-locales reads it.
71282
71288
  ************************************************************/
71283
71289
  function refusal_text(ret) {
71284
71290
  if (typeof ret === "string" && ret) return ret;
71285
71291
  if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
71286
- return "print-tranger failed";
71292
+ return "";
71287
71293
  }
71288
71294
  /************************************************************
71289
71295
  * Fetch the treedb's raw tranger (or one subtree when `path` is set) as
@@ -71312,7 +71318,7 @@ function request_print_tranger(gobj, path) {
71312
71318
  }, gobj);
71313
71319
  if (ret) {
71314
71320
  (0, _yuneta_gobj_js.log_error)(ret);
71315
- answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
71321
+ answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) || "print-tranger failed" });
71316
71322
  return;
71317
71323
  }
71318
71324
  if (path) priv.drills_in_flight.add(path);
@@ -27185,12 +27185,14 @@ function answer_print_tranger_refused$1(gobj, path, answer) {
27185
27185
  }
27186
27186
  /************************************************************
27187
27187
  * The text of a transport's refusal: gobj_command() answers a string,
27188
- * a command response or a number.
27188
+ * a command response or a number. Data only ("" when it says
27189
+ * nothing): the caller writes its own key as the fallback, where
27190
+ * validate-locales reads it.
27189
27191
  ************************************************************/
27190
27192
  function refusal_text$1(ret) {
27191
27193
  if (typeof ret === "string" && ret) return ret;
27192
27194
  if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
27193
- return "print-tranger failed";
27195
+ return "";
27194
27196
  }
27195
27197
  /************************************************************
27196
27198
  * The whole tranger, for the raw-json viewer.
@@ -27218,7 +27220,7 @@ function request_print_tranger$1(gobj, path) {
27218
27220
  }, gobj);
27219
27221
  if (ret) {
27220
27222
  log_error(ret);
27221
- answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) });
27223
+ answer_print_tranger_refused$1(gobj, path, { error: refusal_text$1(ret) || "print-tranger failed" });
27222
27224
  return;
27223
27225
  }
27224
27226
  if (path) priv.drills_in_flight.add(path);
@@ -28359,8 +28361,8 @@ function yui_clear_selection(tabulator) {
28359
28361
  * through `refresh_language()`. Call it on EV_LANGUAGE_CHANGED.
28360
28362
  ***************************************************************/
28361
28363
  function yui_selection_bar(t, opts) {
28362
- let o = opts || {};
28363
- let actions = Array.isArray(o.actions) ? o.actions : [];
28364
+ const o = opts || {};
28365
+ const actions = Array.isArray(o.actions) ? o.actions : [];
28364
28366
  let name = o.name || "TABLE";
28365
28367
  let count = 0;
28366
28368
  let $count = createElement2([
@@ -28387,7 +28389,6 @@ function yui_selection_bar(t, opts) {
28387
28389
  ]],
28388
28390
  { click: action.on_click }
28389
28391
  ]);
28390
- $b._yui_label = action.label || "";
28391
28392
  $buttons.push($b);
28392
28393
  }
28393
28394
  let $clear = createElement2([
@@ -28420,15 +28421,18 @@ function yui_selection_bar(t, opts) {
28420
28421
  function label_of($b) {
28421
28422
  return $b.querySelector("span.icon ~ span");
28422
28423
  }
28424
+ function paint_label($b, key) {
28425
+ let $label = label_of($b);
28426
+ if ($label && key) $label.textContent = t(key);
28427
+ $b.title = key ? t(key) : "";
28428
+ $b.setAttribute("aria-label", $b.title);
28429
+ }
28423
28430
  function refresh() {
28424
28431
  $count.textContent = t("{{n}} selected", { n: count });
28425
- for (let $b of $buttons.concat([$clear])) {
28426
- let $label = label_of($b);
28427
- const key = $b === $clear ? "clear selection" : $b._yui_label;
28428
- if ($label && key) $label.textContent = t(key);
28429
- $b.title = key ? t(key) : "";
28430
- $b.setAttribute("aria-label", $b.title);
28431
- }
28432
+ actions.forEach((action, i) => {
28433
+ paint_label($buttons[i], action.label || "");
28434
+ });
28435
+ paint_label($clear, "clear selection");
28432
28436
  $el.classList.toggle("is-hidden", count <= 0);
28433
28437
  }
28434
28438
  function set_count(n) {
@@ -71273,12 +71277,14 @@ function answer_print_tranger_refused(gobj, path, answer) {
71273
71277
  }
71274
71278
  /************************************************************
71275
71279
  * The text of a transport's refusal: gobj_command() answers a string,
71276
- * a command response or a number.
71280
+ * a command response or a number. Data only ("" when it says
71281
+ * nothing): the caller writes its own key as the fallback, where
71282
+ * validate-locales reads it.
71277
71283
  ************************************************************/
71278
71284
  function refusal_text(ret) {
71279
71285
  if (typeof ret === "string" && ret) return ret;
71280
71286
  if (ret && typeof ret === "object" && typeof ret.comment === "string" && ret.comment) return ret.comment;
71281
- return "print-tranger failed";
71287
+ return "";
71282
71288
  }
71283
71289
  /************************************************************
71284
71290
  * Fetch the treedb's raw tranger (or one subtree when `path` is set) as
@@ -71307,7 +71313,7 @@ function request_print_tranger(gobj, path) {
71307
71313
  }, gobj);
71308
71314
  if (ret) {
71309
71315
  log_error(ret);
71310
- answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) });
71316
+ answer_print_tranger_refused(gobj, path, { error: refusal_text(ret) || "print-tranger failed" });
71311
71317
  return;
71312
71318
  }
71313
71319
  if (path) priv.drills_in_flight.add(path);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yuneta/gobj-ui",
3
- "version": "7.26.31",
3
+ "version": "7.26.32",
4
4
  "type": "module",
5
5
  "main": "dist/gobj-ui.cjs.js",
6
6
  "module": "dist/gobj-ui.es.js",