@hyperfixi/behaviors 4.0.1 → 4.1.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 (2) hide show
  1. package/CHANGELOG.md +148 -1
  2. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -7,6 +7,152 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.1.0] - 2026-10-06
11
+
12
+ Translations keep what the source says. `tell` blocks, swaps, view transitions, chains of
13
+ handlers without their `end`s, events with names of several words, and variables named like
14
+ events or keywords now survive `translate`, MCP `translate_code` and the adapter in every
15
+ language. The adapter's per-language bundles render English with semantic's renderer, and
16
+ `@lokascript/semantic/core` gains `registerHandcrafted`.
17
+
18
+ ### Added
19
+
20
+ - **`@lokascript/semantic` reads upstream's `start view transition … end`, in all 24 languages.**
21
+ `on click start view transition swap #a with #b end` parsed as a `transition` command whose
22
+ patient was `swap`, so `translate`, MCP `translate_code` and the corpus lost the block and its
23
+ body (`on click transition swap`), and validation reported the rest as unconsumed input. The
24
+ block is now a `viewTransition` command with its `body` (`ViewTransitionSemanticNode`;
25
+ `isViewTransitionBlock` from the package root), nested the way a loop's body is: a command after
26
+ the block's `end` stays after it, and the `end` no longer closes the handler, behavior or `def`
27
+ around it. `start [a] view transition [using "<type>"]` is written in English in every language,
28
+ as core's `using view transition` tail already is (a browser API's name, which no profile
29
+ translates). The corpus row `swap-view-transition` is now written this way and parses on both
30
+ engines.
31
+
32
+ ### Changed
33
+
34
+ - **Single-language bundles hold only their own language's hand-crafted patterns.** Each
35
+ command's pattern dispatcher in `@lokascript/semantic` named all 24 languages' hand-crafted
36
+ patterns, so every bundle that could build patterns held all of them (~200 KB minified). Each
37
+ language module now registers its own (`src/patterns/handcrafted/<lang>.ts`, through
38
+ `@lokascript/semantic/core`'s new `registerHandcrafted`), and bundlers drop the rest:
39
+ semantic's single-language bundles are 14–15 KB gzipped smaller (`browser-de` 116 KB instead
40
+ of 130), and a Vite or Rollup build that imports `@lokascript/semantic/core` and one
41
+ `languages/<lang>` gets the same saving. The patterns each language builds are unchanged
42
+ (hashed for all 24 before and after, from source and from the split `dist/`). The adapter's
43
+ bundles, which also changed renderer in this release, are measured in its entry under Fixed.
44
+
45
+ ### Fixed
46
+
47
+ - **`@lokascript/semantic`: a variable named like an event or a keyword keeps its name.** Upstream
48
+ and the engine run `set input to "a"` on a variable named `input`, but semantic's English parse
49
+ dropped the whole command (an event name alone took the value as the text "input", which a
50
+ `set` cannot write), so `translate`, MCP `translate_code` and the adapter lost it in every
51
+ language; `put "a" into input` was lost in ar, de, fr, id and zh, whose tokenizers read `input`
52
+ as the event, and `toggle .a on input` in six. An event name alone where a value stands is now
53
+ the variable it spells, as a structure word or a verb already was, except where an event stands
54
+ (`send`, `trigger`, `repeat until event`, `wait`). And the renderer no longer writes a variable
55
+ in the language's own word where that word reads back as another variable: `set when to 1`
56
+ wrote es `establecer cuando a 1`, read back as a variable named `cuando`, in all 23 languages,
57
+ and bn, ms, th and tl wrote `increment i by index` with their word for `index`. A variable keeps
58
+ its localized word only where that word alone is the same value (es `ello` for `it`), and the
59
+ verified render spells one whose neighbours change it (vi `đặt giá trị`, `set`, for `put value`).
60
+ The value matrix now runs ten such names (`input`, `click`, `when`, `index`, …) in every
61
+ position: all pass on both engines. No corpus row changes.
62
+ - **`@lokascript/semantic`: the event a command names reads back in every language, and a
63
+ `wait` keeps its source for any event.** The handler head read a native event name through the
64
+ event table, but `send`, `trigger` and `repeat until event` read it only when the language's
65
+ tokenizer knew the word: es `enviar dobleclic a #x` read back `send dobleclic to #x`, so the
66
+ event fired as `dobleclic` (55 of the 170 localized names per command: es, pt, fr, de, ko and zh
67
+ coinages, and id and sw `unload`). They now read it as its English name, as the head does. A `wait` kept the source of an
68
+ event only from a fixed list, in English first: `wait for myEvent from #b`,
69
+ `wait for unload from #b` and `wait for focusin from window` lost the source in every language, and
70
+ `wait for click or myEvent from #b` everything after `click`. Upstream reads every leg of
71
+ `wait for` that is not a number as an event, so English now does, and a name followed by a
72
+ source is an event in every language (a time wait has none); tr and tl no longer read `focusin`
73
+ as `focus`. bn writes the event `send` sends with `কে`, as `trigger`'s, since
74
+ `#x তে স্ক্রোল তে পাঠান` read as `scroll to #x`; the old `তে` still reads. And fr's `change`
75
+ event, `changement`, no longer reads as the reactive `when … changes` word, which turned
76
+ `repeat until event change` into a `when` feature. Four corpus rows change, the bn renders of
77
+ `send` (`কে` for `তে`).
78
+ - **`@lokascript/semantic`: a hand-written event name of several words is read whole.** The event
79
+ table reads 20 names of several words that the renderer never writes (ar `تمرير الماوس`, de
80
+ `taste runter`, id `lepas tombol`, pt `pressionar tecla`, qu `mana q'away`, sw `bonyeza chini`,
81
+ tr `fare_bas`, zh `鼠标进入`, and twelve more), but their tokenizers split them, and the reader took
82
+ the first word for the event: de `wenn taste runter protokollieren 1` read `on taste log 1`, id
83
+ `ketika lepas tombol …` `on lepas`, sw `unapo bonyeza chini …` `on click`, qu
84
+ `maykama mana q'away …` `on false`, and ar `على تمرير الماوس من #b …` `on scroll`. A `wait for`,
85
+ `send`, `trigger` or `repeat until event` lost the event the same way. The tokenizers now read
86
+ each name whole; tr reads an `_`-joined word as one token when it is a keyword, as sw already did
87
+ (its own `tuş_bas` had never been read), and splits any other at `_` as before. No corpus row
88
+ changes.
89
+ - **`@lokascript/semantic`: an event name of several words keeps what is written after it.** ar
90
+ writes keydown, keyup and resize as `ضغط المفتاح`, `رفع المفتاح` and `تغيير حجم`, id writes
91
+ keydown as `tekan tombol`, and vi writes resize as `đổi kích thước`, but each tokenizer read only
92
+ the first word as the event. So `on keyup from #b log 1` read back `on keyup log 1` in ar,
93
+ `on resize from window` read back `on change` in ar and vi, and `send keyup to #x`,
94
+ `trigger keydown on #x`, `repeat until event keyup from #b` and `wait for resize` lost their
95
+ `to`, `on` or `from`, or waited for `change`. `translate`, MCP `translate_code` and the adapter
96
+ carried the loss. The three tokenizers now read each name whole, as es, fr, pt and qu already
97
+ did. The corpus's ar and vi `window-resize` rows, which the parser's compound repair already
98
+ read right, now parse at full confidence (1.0, was 0.82); no other corpus row changes.
99
+ - **`@lokascript/semantic`: a behavior's handlers need no `end` of their own.** Upstream ends a
100
+ handler's commands at the next feature, so `behavior F on click add .a on keyup log 1 end end`
101
+ is two handlers. The behavior parser split its body only at `end`, read both handlers as one,
102
+ and English wrote `behavior F then add .a then log 1`, which the engine rejects: `translate`,
103
+ MCP `translate_code` and the adapter broke such a behavior in every language. Each piece now goes
104
+ through the splitter a top-level chain of handlers already used, and parses to the same node as
105
+ the form with every `end` written. No corpus row changes (every one writes its `end`s).
106
+ - **`@lokascript/semantic`: a chain of handlers written natively without their `end`s reads as
107
+ written, in every language.** In 9 languages such a chain merged into one handler, at top level
108
+ and inside a behavior, and the result was valid English that ran wrong: de, fr, id, zh, ko, qu
109
+ and tr dropped the second event and ran its commands on the first (`on click add .a then log
110
+ 1`), and hi and bn read it as a destination (`add .a to keyup`). The split knew a handler head
111
+ only as `on`'s forms and ja/ko's `を で` / `을 에`; it now reads each language's own: the `when`
112
+ word the renderer writes (de `wenn`, fr `quand`, id `ketika`, qu `maykama`), zh's `一 … 就`, and
113
+ the words after the event (ko `할 때`, tr `i üzerinde`). The same rule ends an `init` with no
114
+ `end` in SOV languages too, and he and vi no longer read a handler after `toggle .a on #x` as the
115
+ toggle's. hi `पर` and bn `তে` are also the destination marker, which almost every command there
116
+ may write first, so a split at one is never trusted: it reads as written, at a confidence below
117
+ the adapter's threshold, so the adapter leaves the script as written and the engine reports it.
118
+ No corpus row changes.
119
+ - **`@lokascript/semantic`: English writes core's view-transition tail as upstream's block.**
120
+ `swap #a with #b using view transition` (and `morph`'s tail) renders to English as
121
+ `start view transition swap #a with #b end`, which upstream _hyperscript and `@hyperfixi/engine`
122
+ both parse; the tail itself is rejected by both. Every language reads the tail, and
123
+ `@lokascript/hyperscript-adapter` hands the host the English render, so a page written in
124
+ another language with a view-transition swap failed to parse and ran nothing. Foreign renders
125
+ keep the tail.
126
+ - **`@lokascript/semantic`: a `swap` keeps both operands in every language.** Only a swap of two
127
+ selectors round-tripped. `swap #target with me`, `swap :x with :y` and a swap of properties
128
+ (`swap my textContent with #a`, `swap #a's textContent with #b's textContent`) lost an operand
129
+ or its property in up to 21 languages, so a translated page swapped the wrong thing or nothing.
130
+ Four causes: swap's roles took no reference or property path; a handler's repair re-parse was
131
+ vetoed when the swap's first operand moved to its real role; an optional slot that declined a
132
+ value by type still consumed it (any command's pattern could lose the next role that way); and
133
+ in pl/uk the with-word is also the of-word, so `swap el with #t` read as `#t's el`.
134
+ - **`@lokascript/semantic`: a `tell` block keeps its extent.** Semantic kept `tell` flat, its body
135
+ being every statement after it, so in English and every translation a command after the tell's
136
+ `end` was lost (`tell #modal show end then log 2` lost the `log`) or pulled inside it
137
+ (`… end log 2`), a nested tell took its parent's remaining commands, and in a behavior or `def`
138
+ the tell's `end` closed the enclosing block, dropping the next handler or command. The parser
139
+ now nests a tell's body as it nests a loop's (`BlockCommandSemanticNode`, `isBlockCommand`), and
140
+ reads an `end` as the tell's exactly when the engine does: before a new `on <event>`, `def`,
141
+ `init` or `behavior`, a tell needs none.
142
+ - **`@lokascript/hyperscript-adapter`'s per-language and regional bundles render English with
143
+ semantic's renderer.** They rendered with their own English writer, which saved English's
144
+ language data and had drifted far from semantic's: over the corpus's 3,772 translations, 1,242
145
+ rendered to valid English that did something else, and 345 to English the engine rejects (the
146
+ script then kept its author's text and did not run). Every `if` lost its branches,
147
+ `put … before`/`after` became `put … into`, `from window`, `from elsewhere`, `debounced` and
148
+ `or <event>` vanished, `<button/> in me` widened to the whole page, `repeat for x in …` lost
149
+ its binding, and every `swap #a with #b` was written `swap of #a with #b`. They now give exactly
150
+ what the full `hyperscript-i18n.global.js` gives. English's own module builds through
151
+ `@lokascript/semantic/core` (its pattern generator and repeat heads,
152
+ `getRepeatPatternsForLanguage` now exported there), so a bundle holds one copy. Against 4.0.1,
153
+ the single-language bundles are the same size (`-de` 116 KB gzipped) and `-en` is 109 KB
154
+ instead of 148; the regional bundles are 8–16 KB larger (`western` 141 KB instead of 125).
155
+
10
156
  ## [4.0.1] - 2026-10-05
11
157
 
12
158
  Fixes for 4.0.0: the per-language adapter bundles read every language again, and the MCP server's
@@ -1095,7 +1241,8 @@ _Synchronized version release. See git history for details._
1095
1241
  - npm access token stored in GitHub Secrets
1096
1242
  - 2FA recommended for npm organization
1097
1243
 
1098
- [Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v4.0.1...HEAD
1244
+ [Unreleased]: https://github.com/codetalcott/hyperfixi/compare/v4.1.0...HEAD
1245
+ [4.1.0]: https://github.com/codetalcott/hyperfixi/compare/v4.0.1...v4.1.0
1099
1246
  [4.0.1]: https://github.com/codetalcott/hyperfixi/compare/v4.0.0...v4.0.1
1100
1247
  [4.0.0]: https://github.com/codetalcott/hyperfixi/compare/v3.3.0...v4.0.0
1101
1248
  [3.3.0]: https://github.com/codetalcott/hyperfixi/compare/v3.2.0...v3.3.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hyperfixi/behaviors",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "Reusable hyperscript behaviors for @hyperfixi/engine (hyperfixi-hs.js) and upstream _hyperscript",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -108,16 +108,16 @@
108
108
  "build:watch": "tsup --watch",
109
109
  "pretypecheck": "npm run generate",
110
110
  "typecheck": "tsc --noEmit",
111
- "pretest": "npm run generate && bash ../../scripts/ensure-fresh.sh engine",
111
+ "pretest": "npm run generate && bash ../../scripts/ensure-fresh.sh ../engine",
112
112
  "test": "vitest run",
113
113
  "test:watch": "vitest",
114
114
  "test:check": "VITEST_QUIET=1 bash ../../scripts/vitest-run.sh --reporter=dot"
115
115
  },
116
116
  "peerDependencies": {
117
- "@hyperfixi/engine": "^4.0.1"
117
+ "@hyperfixi/engine": "^4.1.0"
118
118
  },
119
119
  "devDependencies": {
120
- "@hyperfixi/engine": "^4.0.1",
120
+ "@hyperfixi/engine": "^4.1.0",
121
121
  "tsup": "^8.0.0",
122
122
  "typescript": "^5.0.0",
123
123
  "vitest": "^5.0.2"