@adaas/are-html 0.0.2 → 0.0.4

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 (200) hide show
  1. package/README.md +4 -4
  2. package/dist/browser/index.d.mts +88 -5
  3. package/dist/browser/index.mjs +542 -176
  4. package/dist/browser/index.mjs.map +1 -1
  5. package/dist/node/attributes/AreBinding.attribute.js +17 -4
  6. package/dist/node/attributes/AreBinding.attribute.js.map +1 -1
  7. package/dist/node/attributes/AreBinding.attribute.mjs +10 -3
  8. package/dist/node/attributes/AreBinding.attribute.mjs.map +1 -1
  9. package/dist/node/attributes/AreDirective.attribute.js +17 -4
  10. package/dist/node/attributes/AreDirective.attribute.js.map +1 -1
  11. package/dist/node/attributes/AreDirective.attribute.mjs +10 -3
  12. package/dist/node/attributes/AreDirective.attribute.mjs.map +1 -1
  13. package/dist/node/attributes/AreEvent.attribute.js +17 -4
  14. package/dist/node/attributes/AreEvent.attribute.js.map +1 -1
  15. package/dist/node/attributes/AreEvent.attribute.mjs +10 -3
  16. package/dist/node/attributes/AreEvent.attribute.mjs.map +1 -1
  17. package/dist/node/attributes/AreStatic.attribute.js +17 -4
  18. package/dist/node/attributes/AreStatic.attribute.js.map +1 -1
  19. package/dist/node/attributes/AreStatic.attribute.mjs +10 -3
  20. package/dist/node/attributes/AreStatic.attribute.mjs.map +1 -1
  21. package/dist/node/directives/AreDirectiveFor.directive.d.mts +8 -0
  22. package/dist/node/directives/AreDirectiveFor.directive.d.ts +8 -0
  23. package/dist/node/directives/AreDirectiveFor.directive.js +78 -33
  24. package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
  25. package/dist/node/directives/AreDirectiveFor.directive.mjs +78 -33
  26. package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
  27. package/dist/node/directives/AreDirectiveIf.directive.d.mts +18 -0
  28. package/dist/node/directives/AreDirectiveIf.directive.d.ts +18 -0
  29. package/dist/node/directives/AreDirectiveIf.directive.js +10 -3
  30. package/dist/node/directives/AreDirectiveIf.directive.js.map +1 -1
  31. package/dist/node/directives/AreDirectiveIf.directive.mjs +10 -3
  32. package/dist/node/directives/AreDirectiveIf.directive.mjs.map +1 -1
  33. package/dist/node/engine/AreHTML.compiler.d.mts +2 -2
  34. package/dist/node/engine/AreHTML.compiler.d.ts +2 -2
  35. package/dist/node/engine/AreHTML.compiler.js +57 -29
  36. package/dist/node/engine/AreHTML.compiler.js.map +1 -1
  37. package/dist/node/engine/AreHTML.compiler.mjs +58 -30
  38. package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
  39. package/dist/node/engine/AreHTML.constants.d.mts +53 -1
  40. package/dist/node/engine/AreHTML.constants.d.ts +53 -1
  41. package/dist/node/engine/AreHTML.constants.js +100 -0
  42. package/dist/node/engine/AreHTML.constants.js.map +1 -1
  43. package/dist/node/engine/AreHTML.constants.mjs +93 -0
  44. package/dist/node/engine/AreHTML.constants.mjs.map +1 -1
  45. package/dist/node/engine/AreHTML.context.d.mts +6 -2
  46. package/dist/node/engine/AreHTML.context.d.ts +6 -2
  47. package/dist/node/engine/AreHTML.context.js +42 -7
  48. package/dist/node/engine/AreHTML.context.js.map +1 -1
  49. package/dist/node/engine/AreHTML.context.mjs +35 -6
  50. package/dist/node/engine/AreHTML.context.mjs.map +1 -1
  51. package/dist/node/engine/AreHTML.engine.js +10 -7
  52. package/dist/node/engine/AreHTML.engine.js.map +1 -1
  53. package/dist/node/engine/AreHTML.engine.mjs +10 -7
  54. package/dist/node/engine/AreHTML.engine.mjs.map +1 -1
  55. package/dist/node/engine/AreHTML.interpreter.js +155 -43
  56. package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
  57. package/dist/node/engine/AreHTML.interpreter.mjs +155 -43
  58. package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
  59. package/dist/node/engine/AreHTML.lifecycle.js +17 -12
  60. package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
  61. package/dist/node/engine/AreHTML.lifecycle.mjs +9 -2
  62. package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
  63. package/dist/node/engine/AreHTML.tokenizer.js +14 -9
  64. package/dist/node/engine/AreHTML.tokenizer.js.map +1 -1
  65. package/dist/node/engine/AreHTML.tokenizer.mjs +10 -3
  66. package/dist/node/engine/AreHTML.tokenizer.mjs.map +1 -1
  67. package/dist/node/engine/AreHTML.transformer.js +13 -8
  68. package/dist/node/engine/AreHTML.transformer.js.map +1 -1
  69. package/dist/node/engine/AreHTML.transformer.mjs +9 -2
  70. package/dist/node/engine/AreHTML.transformer.mjs.map +1 -1
  71. package/dist/node/index.d.mts +2 -1
  72. package/dist/node/index.d.ts +2 -1
  73. package/dist/node/index.js +3 -3
  74. package/dist/node/index.mjs +1 -1
  75. package/dist/node/instructions/AddAttribute.instruction.js +3 -4
  76. package/dist/node/instructions/AddAttribute.instruction.js.map +1 -1
  77. package/dist/node/instructions/AddAttribute.instruction.mjs +3 -4
  78. package/dist/node/instructions/AddAttribute.instruction.mjs.map +1 -1
  79. package/dist/node/instructions/AddComment.instruction.js +3 -4
  80. package/dist/node/instructions/AddComment.instruction.js.map +1 -1
  81. package/dist/node/instructions/AddComment.instruction.mjs +3 -4
  82. package/dist/node/instructions/AddComment.instruction.mjs.map +1 -1
  83. package/dist/node/instructions/AddElement.instruction.js +3 -4
  84. package/dist/node/instructions/AddElement.instruction.js.map +1 -1
  85. package/dist/node/instructions/AddElement.instruction.mjs +3 -4
  86. package/dist/node/instructions/AddElement.instruction.mjs.map +1 -1
  87. package/dist/node/instructions/AddInterpolation.instruction.js +3 -4
  88. package/dist/node/instructions/AddInterpolation.instruction.js.map +1 -1
  89. package/dist/node/instructions/AddInterpolation.instruction.mjs +3 -4
  90. package/dist/node/instructions/AddInterpolation.instruction.mjs.map +1 -1
  91. package/dist/node/instructions/AddListener.instruction.js +3 -4
  92. package/dist/node/instructions/AddListener.instruction.js.map +1 -1
  93. package/dist/node/instructions/AddListener.instruction.mjs +3 -4
  94. package/dist/node/instructions/AddListener.instruction.mjs.map +1 -1
  95. package/dist/node/instructions/AddStyle.instruction.js +3 -4
  96. package/dist/node/instructions/AddStyle.instruction.js.map +1 -1
  97. package/dist/node/instructions/AddStyle.instruction.mjs +3 -4
  98. package/dist/node/instructions/AddStyle.instruction.mjs.map +1 -1
  99. package/dist/node/instructions/AddText.instruction.js +3 -4
  100. package/dist/node/instructions/AddText.instruction.js.map +1 -1
  101. package/dist/node/instructions/AddText.instruction.mjs +3 -4
  102. package/dist/node/instructions/AddText.instruction.mjs.map +1 -1
  103. package/dist/node/lib/AreDirective/AreDirective.component.js +5 -0
  104. package/dist/node/lib/AreDirective/AreDirective.component.js.map +1 -1
  105. package/dist/node/lib/AreDirective/AreDirective.component.mjs +5 -0
  106. package/dist/node/lib/AreDirective/AreDirective.component.mjs.map +1 -1
  107. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.js +17 -4
  108. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.js.map +1 -1
  109. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.mjs +10 -3
  110. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.mjs.map +1 -1
  111. package/dist/node/lib/AreHTMLNode/AreHTMLNode.js +3 -4
  112. package/dist/node/lib/AreHTMLNode/AreHTMLNode.js.map +1 -1
  113. package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs +3 -4
  114. package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs.map +1 -1
  115. package/dist/node/lib/AreRoot/AreRoot.component.js +3 -4
  116. package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
  117. package/dist/node/lib/AreRoot/AreRoot.component.mjs +3 -4
  118. package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
  119. package/dist/node/lib/{AreWatcher/AreWatcher.component.d.mts → AreRouteWatcher/AreRouteWatcher.component.d.mts} +2 -2
  120. package/dist/node/lib/{AreWatcher/AreWatcher.component.d.ts → AreRouteWatcher/AreRouteWatcher.component.d.ts} +2 -2
  121. package/dist/node/lib/{AreWatcher/AreWatcher.component.js → AreRouteWatcher/AreRouteWatcher.component.js} +9 -10
  122. package/dist/node/lib/AreRouteWatcher/AreRouteWatcher.component.js.map +1 -0
  123. package/dist/node/lib/{AreWatcher/AreWatcher.component.mjs → AreRouteWatcher/AreRouteWatcher.component.mjs} +10 -11
  124. package/dist/node/lib/AreRouteWatcher/AreRouteWatcher.component.mjs.map +1 -0
  125. package/dist/node/lib/AreStyle/AreStyle.context.js +17 -4
  126. package/dist/node/lib/AreStyle/AreStyle.context.js.map +1 -1
  127. package/dist/node/lib/AreStyle/AreStyle.context.mjs +10 -3
  128. package/dist/node/lib/AreStyle/AreStyle.context.mjs.map +1 -1
  129. package/dist/node/nodes/AreComment.js +17 -4
  130. package/dist/node/nodes/AreComment.js.map +1 -1
  131. package/dist/node/nodes/AreComment.mjs +10 -3
  132. package/dist/node/nodes/AreComment.mjs.map +1 -1
  133. package/dist/node/nodes/AreComponent.js +3 -4
  134. package/dist/node/nodes/AreComponent.js.map +1 -1
  135. package/dist/node/nodes/AreComponent.mjs +3 -4
  136. package/dist/node/nodes/AreComponent.mjs.map +1 -1
  137. package/dist/node/nodes/AreInterpolation.js +17 -4
  138. package/dist/node/nodes/AreInterpolation.js.map +1 -1
  139. package/dist/node/nodes/AreInterpolation.mjs +10 -3
  140. package/dist/node/nodes/AreInterpolation.mjs.map +1 -1
  141. package/dist/node/nodes/AreRoot.js +3 -4
  142. package/dist/node/nodes/AreRoot.js.map +1 -1
  143. package/dist/node/nodes/AreRoot.mjs +3 -4
  144. package/dist/node/nodes/AreRoot.mjs.map +1 -1
  145. package/dist/node/nodes/AreText.js +17 -4
  146. package/dist/node/nodes/AreText.js.map +1 -1
  147. package/dist/node/nodes/AreText.mjs +10 -3
  148. package/dist/node/nodes/AreText.mjs.map +1 -1
  149. package/dist/node/signals/AreRoute.signal.js +18 -5
  150. package/dist/node/signals/AreRoute.signal.js.map +1 -1
  151. package/dist/node/signals/AreRoute.signal.mjs +10 -3
  152. package/dist/node/signals/AreRoute.signal.mjs.map +1 -1
  153. package/docs/SYNTAX.md +714 -0
  154. package/docs/arehtml.monaco.json +235 -0
  155. package/docs/arehtml.monaco.ts +119 -0
  156. package/examples/dashboard/dist/index.html +1 -1
  157. package/examples/dashboard/dist/mpioi5ab-8c3oa9.js +13674 -0
  158. package/examples/jumpstart/dist/index.html +1 -1
  159. package/examples/{dashboard/dist/mnzfypsd-6zjt7w.js → jumpstart/dist/mor90p6y-0plg7g.js} +1869 -1926
  160. package/examples/jumpstart/dist/{mnpl1g4i-nobz9g.js → mor90p7p-1898bz.js} +2797 -2282
  161. package/examples/jumpstart/src/components/List.component.ts +14 -13
  162. package/examples/jumpstart/src/concept.ts +5 -4
  163. package/jest.config.ts +1 -1
  164. package/package.json +10 -6
  165. package/src/attributes/AreBinding.attribute.ts +5 -0
  166. package/src/attributes/AreDirective.attribute.ts +5 -0
  167. package/src/attributes/AreEvent.attribute.ts +5 -0
  168. package/src/attributes/AreStatic.attribute.ts +5 -0
  169. package/src/directives/AreDirectiveFor.directive.ts +97 -60
  170. package/src/directives/AreDirectiveIf.directive.ts +37 -15
  171. package/src/engine/AreHTML.compiler.ts +64 -36
  172. package/src/engine/AreHTML.constants.ts +144 -0
  173. package/src/engine/AreHTML.context.ts +33 -4
  174. package/src/engine/AreHTML.engine.ts +12 -7
  175. package/src/engine/AreHTML.interpreter.ts +195 -68
  176. package/src/engine/AreHTML.lifecycle.ts +5 -0
  177. package/src/engine/AreHTML.tokenizer.ts +6 -1
  178. package/src/engine/AreHTML.transformer.ts +5 -0
  179. package/src/index.ts +2 -2
  180. package/src/instructions/AddAttribute.instruction.ts +3 -4
  181. package/src/instructions/AddComment.instruction.ts +3 -4
  182. package/src/instructions/AddElement.instruction.ts +3 -4
  183. package/src/instructions/AddInterpolation.instruction.ts +3 -4
  184. package/src/instructions/AddListener.instruction.ts +3 -4
  185. package/src/instructions/AddStyle.instruction.ts +3 -4
  186. package/src/instructions/AddText.instruction.ts +3 -4
  187. package/src/lib/AreDirective/AreDirective.component.ts +5 -0
  188. package/src/lib/AreHTMLAttribute/AreHTML.attribute.ts +5 -0
  189. package/src/lib/AreHTMLNode/AreHTMLNode.ts +3 -4
  190. package/src/lib/AreRoot/AreRoot.component.ts +3 -4
  191. package/src/lib/{AreWatcher/AreWatcher.component.ts → AreRouteWatcher/AreRouteWatcher.component.ts} +5 -6
  192. package/src/lib/AreStyle/AreStyle.context.ts +5 -0
  193. package/src/nodes/AreComment.ts +5 -0
  194. package/src/nodes/AreComponent.ts +3 -4
  195. package/src/nodes/AreInterpolation.ts +5 -0
  196. package/src/nodes/AreRoot.ts +3 -4
  197. package/src/nodes/AreText.ts +5 -0
  198. package/src/signals/AreRoute.signal.ts +5 -0
  199. package/dist/node/lib/AreWatcher/AreWatcher.component.js.map +0 -1
  200. package/dist/node/lib/AreWatcher/AreWatcher.component.mjs.map +0 -1
package/docs/SYNTAX.md ADDED
@@ -0,0 +1,714 @@
1
+ # `@adaas/are-html` — Template Syntax Reference
2
+
3
+ A complete reference for the template syntax understood by the **ARE‑HTML**
4
+ engine. This document is the source of truth for building syntax highlighting,
5
+ linting, and editor tooling (Monaco / VS Code TextMate / Tree-sitter / etc.).
6
+
7
+ > Conventions in this document
8
+ >
9
+ > - `<x>` denotes a placeholder (you write something there).
10
+ > - Code blocks tagged ` ```html ` are template fragments.
11
+ > - Code blocks tagged ` ```ts ` are companion JavaScript/TypeScript code.
12
+
13
+ ---
14
+
15
+ ## 1. Big picture
16
+
17
+ ARE‑HTML templates look like HTML, but element attributes can carry one of
18
+ **four sigils** that alter their meaning:
19
+
20
+ | Sigil | Attribute kind | Example | Purpose |
21
+ | :---: | ---------------- | --------------------------- | ------------------------------------ |
22
+ | _none_ | **Static** | `class="menu-item"` | Plain HTML attribute |
23
+ | `:` | **Binding** | `:value="user.name"` | Reactive expression → attribute/prop |
24
+ | `@` | **Event** | `@click="$save"` | DOM event listener |
25
+ | `$` | **Directive** | `$if="visible"` | Structural / behavioral directive |
26
+
27
+ Inside text, `{{ … }}` is a **reactive interpolation**.
28
+
29
+ ### 1.1 Lexical grammar (per-attribute)
30
+
31
+ Tokenizer regex used by the engine:
32
+
33
+ ```
34
+ ATTR ::= ([$:@]?[\w.-]+) ( = "..." | = '...' | = bare )?
35
+ ```
36
+
37
+ Effectively:
38
+
39
+ | Pattern | Token kind |
40
+ | --------------------------- | ----------- |
41
+ | `$<name>` | Directive |
42
+ | `:<name>` | Binding |
43
+ | `@<name>(.<modifier>)*` | Event |
44
+ | `<name>` (`[\w.-]+`) | Static attr |
45
+
46
+ Attribute **values** are always JS-like expressions when carried by `:`, `@`,
47
+ `$` or `{{ … }}`. Expressions are evaluated in a sandbox over the component's
48
+ `AreStore`, so any property you `store.set(…)` is in scope as a top-level
49
+ identifier.
50
+
51
+ ### 1.2 File structure of a component
52
+
53
+ A component is a TypeScript class extending `Are`. Templates live in the
54
+ decorated `template()` method:
55
+
56
+ ```ts
57
+ import { A_Caller, A_Inject } from "@adaas/a-concept";
58
+ import { Are, AreNode, AreStore } from "@adaas/are";
59
+
60
+ export class HelloComponent extends Are {
61
+
62
+ @Are.Template
63
+ async template(@A_Inject(A_Caller) node: AreNode) {
64
+ node.setContent(`
65
+ <div class="hello">
66
+ Hello, {{name}}!
67
+ <button @click="$greet">Greet</button>
68
+ </div>
69
+ `);
70
+ }
71
+
72
+ @Are.Data
73
+ async data(@A_Inject(AreStore) store: AreStore) {
74
+ store.set({ name: "World" });
75
+ }
76
+
77
+ @Are.EventHandler
78
+ async greet(@A_Inject(AreStore) store: AreStore) {
79
+ store.set("name", "ARE");
80
+ }
81
+ }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## 2. Interpolations — `{{ expression }}`
87
+
88
+ Reactive text expressions inside text content (NOT inside attribute values).
89
+
90
+ ```html
91
+ <span>Hello, {{ user.name }}!</span>
92
+ <span>Total: {{ items.length }}</span>
93
+ <span>{{ count > 0 ? 'has' : 'none' }}</span>
94
+ <span>{{ a + b * 2 }}</span>
95
+ ```
96
+
97
+ Rules:
98
+
99
+ 1. Whitespace inside `{{ }}` is ignored.
100
+ 2. The expression is JS-like; any property in the component's store is a
101
+ top-level identifier.
102
+ 3. Result is coerced to a DOM string:
103
+ - `null` / `undefined` → `""` (empty string, not the literal text).
104
+ - `object` → `JSON.stringify(value)`.
105
+ - everything else → `String(value)`.
106
+ 4. Re-evaluated automatically when any dependency in the store changes.
107
+ 5. **Not allowed in attributes.** Use `:attr="expr"` instead.
108
+
109
+ ---
110
+
111
+ ## 3. Static attributes (no sigil)
112
+
113
+ Plain HTML attributes. Value is a literal string.
114
+
115
+ ```html
116
+ <div class="card primary" id="main" data-role="container"></div>
117
+ ```
118
+
119
+ ---
120
+
121
+ ## 4. Bindings — `:<attribute>`
122
+
123
+ Reactive attribute / property bindings. The right-hand side is an **expression**.
124
+
125
+ ```html
126
+ <input :value="user.email" :disabled="loading">
127
+ <a :href="'/users/' + user.id">Profile</a>
128
+ <img :src="avatar || '/default.png'" :alt="user.name">
129
+ ```
130
+
131
+ ### 4.1 Property name matching
132
+
133
+ The framework looks up the right destination key on the child component
134
+ using both **kebab-case** and **camelCase**:
135
+
136
+ ```html
137
+ <my-card :user-name="user.name"></my-card>
138
+ <!-- writes either `user-name` or `userName` into the child store, whichever exists -->
139
+ ```
140
+
141
+ ### 4.2 Boolean attributes
142
+
143
+ For boolean HTML attributes (`disabled`, `checked`, `readonly`, `required`,
144
+ `hidden`, `multiple`, `selected`, `open`, `autofocus`, `autoplay`, `controls`,
145
+ `loop`, `muted`, `default`, `defer`, `async`, `nomodule`, `novalidate`,
146
+ `reversed`, `ismap`, `formnovalidate`, `playsinline`, `itemscope`, `truespeed`,
147
+ `typemustmatch`, `inert`, `allowfullscreen`):
148
+
149
+ ```html
150
+ <button :disabled="loading"></button>
151
+ <input :readonly="locked">
152
+ <input :checked="agreed" type="checkbox">
153
+ ```
154
+
155
+ - Truthy → attribute is present (and corresponding IDL property set to `true`).
156
+ - Falsy → attribute is removed (IDL property `false`).
157
+
158
+ ### 4.3 IDL form properties
159
+
160
+ For form inputs, the engine uses the **IDL property** (not `setAttribute`)
161
+ when binding:
162
+
163
+ | Element / attribute | Set as IDL property |
164
+ | ------------------------------------- | ------------------- |
165
+ | `<input :value="…">` | `input.value` |
166
+ | `<input :checked="…">` | `input.checked` |
167
+ | `<select :value="…">` | `select.value` |
168
+ | `<textarea :value="…">` | `textarea.value` |
169
+ | `<option :selected="…">` | `option.selected` |
170
+
171
+ This means `:value` round-trips correctly even after the user types into the
172
+ input.
173
+
174
+ ### 4.4 `:class` (string, array, or object)
175
+
176
+ ```html
177
+ <!-- string -->
178
+ <div :class="active ? 'on' : 'off'">…</div>
179
+
180
+ <!-- array -->
181
+ <div :class="['btn', kind, isActive && 'btn-active']">…</div>
182
+
183
+ <!-- object: keys whose value is truthy are added -->
184
+ <div :class="{ 'btn': true, 'btn-primary': primary, 'btn-disabled': loading }">…</div>
185
+ ```
186
+
187
+ `:class` is **merged** with any static `class="…"` on the same element — both
188
+ are preserved.
189
+
190
+ ### 4.5 `:style` (string or object)
191
+
192
+ ```html
193
+ <!-- string (raw CSS) -->
194
+ <div :style="'color:' + color + ';font-size:14px'">…</div>
195
+
196
+ <!-- object: camelCase or kebab-case keys; numbers get 'px' for length-like keys -->
197
+ <div :style="{ color: textColor, fontSize: '14px', marginTop: 8 }">…</div>
198
+ ```
199
+
200
+ ### 4.6 Other attributes
201
+
202
+ For everything else, the value is set with `setAttribute`. If the expression
203
+ evaluates to `false`, `null`, or `undefined`, the attribute is **removed**.
204
+
205
+ ```html
206
+ <a :title="hover">…</a>
207
+ <img :alt="caption">
208
+ <div :data-id="row.id">…</div>
209
+ ```
210
+
211
+ ---
212
+
213
+ ## 5. Events — `@<event>[.<modifier>…]`
214
+
215
+ Bind a DOM event to an `@Are.EventHandler` method on the component, or to an
216
+ inline expression.
217
+
218
+ ### 5.1 Reference an event-handler method
219
+
220
+ When the value starts with `$<name>` it refers to an `@Are.EventHandler`
221
+ method named `<name>`:
222
+
223
+ ```html
224
+ <button @click="$save">Save</button>
225
+ <button @click="$save(item, $event)">Save with args</button>
226
+ <form @submit="$onSubmit($event)">…</form>
227
+ ```
228
+
229
+ - Always available in the handler argument scope: `$event` (the DOM event).
230
+ - If no arguments are passed, the DOM event is appended automatically.
231
+ - Multiple handlers per call are allowed: `@click="$a(); $b($event)"`.
232
+
233
+ ### 5.2 Inline expressions
234
+
235
+ Any expression is allowed; it runs each time the event fires:
236
+
237
+ ```html
238
+ <button @click="count = count + 1">+1</button>
239
+ <a @click="open = !open">Toggle</a>
240
+ ```
241
+
242
+ ### 5.3 Event modifiers
243
+
244
+ Append one or more `.<modifier>` to the event name:
245
+
246
+ | Modifier | Effect |
247
+ | --------- | ----------------------------------------------------------- |
248
+ | `.stop` | `event.stopPropagation()` before handler runs |
249
+ | `.prevent`| `event.preventDefault()` before handler runs |
250
+ | `.self` | Only fire when `event.target === element` |
251
+ | `.capture`| `addEventListener(…, { capture: true })` |
252
+ | `.once` | `addEventListener(…, { once: true })` |
253
+ | `.passive`| `addEventListener(…, { passive: true })` |
254
+
255
+ Modifiers can be combined in any order:
256
+
257
+ ```html
258
+ <a @click.stop.prevent="$open">Open</a>
259
+ <form @submit.prevent="$submit">…</form>
260
+ <div @scroll.passive="$onScroll">…</div>
261
+ <div @click.self="$onSelfOnly">…</div>
262
+ ```
263
+
264
+ ### 5.4 Key modifiers (keyboard events)
265
+
266
+ For `keydown` / `keyup` / `keypress`, the following keys can be used as
267
+ modifiers — handler only fires when the matching key is pressed:
268
+
269
+ | Modifier | Matches `event.key` |
270
+ | -------------------- | ------------------------------------ |
271
+ | `.enter` | `Enter` |
272
+ | `.esc` / `.escape` | `Escape` |
273
+ | `.tab` | `Tab` |
274
+ | `.space` | `' '` (Space) |
275
+ | `.delete` / `.backspace` | `Delete` / `Backspace` |
276
+ | `.up` | `ArrowUp` |
277
+ | `.down` | `ArrowDown` |
278
+ | `.left` | `ArrowLeft` |
279
+ | `.right` | `ArrowRight` |
280
+
281
+ Combined with system-key modifiers:
282
+
283
+ | Modifier | Matches |
284
+ | --------- | ------- |
285
+ | `.ctrl` | `event.ctrlKey` must be true |
286
+ | `.alt` | `event.altKey` must be true |
287
+ | `.shift` | `event.shiftKey` must be true |
288
+ | `.meta` | `event.metaKey` must be true |
289
+
290
+ Examples:
291
+
292
+ ```html
293
+ <input @keydown.enter="$submit">
294
+ <input @keydown.esc="$cancel">
295
+ <input @keydown.ctrl.enter="$saveDraft">
296
+ <div @keydown.up.prevent="$moveUp">
297
+ ```
298
+
299
+ ### 5.5 The `$event` argument
300
+
301
+ Always available as the variable `$event` inside the handler call:
302
+
303
+ ```html
304
+ <button @click="$save($event, row)">Save</button>
305
+ <input @input="$onInput($event.target.value)">
306
+ ```
307
+
308
+ ---
309
+
310
+ ## 6. Directives — `$<name>`
311
+
312
+ Structural / behavioral attributes. They have **higher priority** than other
313
+ attributes on the same element.
314
+
315
+ ### 6.1 `$if="<expression>"`
316
+
317
+ Conditionally render an element subtree.
318
+
319
+ ```html
320
+ <div $if="user.isAdmin">Admin only content</div>
321
+ <span $if="badge > 0" class="menu-badge">{{badge}}</span>
322
+ <p $if="!loading && error">{{error}}</p>
323
+ ```
324
+
325
+ - Re-evaluated reactively.
326
+ - Truthy ↔ falsy transitions mount / unmount the subtree.
327
+ - Identical truthiness across updates is a **no-op** (no remount).
328
+
329
+ > ⚠️ Do **not** combine `$if` and `$for` on the **same** element. Wrap one in a
330
+ > parent:
331
+ >
332
+ > ```html
333
+ > <!-- ✅ Right -->
334
+ > <div $if="active === 'list'">
335
+ > <li $for="item in items">{{item.name}}</li>
336
+ > </div>
337
+ > ```
338
+
339
+ ### 6.2 `$for="<binding> in <source> [track <key>]"`
340
+
341
+ Render a node once per item. The binding is one of:
342
+
343
+ ```html
344
+ <li $for="item in items">…</li>
345
+ <li $for="item, i in items">…</li>
346
+ <li $for="(item, i) in items">…</li>
347
+ ```
348
+
349
+ - `item` — current value
350
+ - `i` — current index (defaults to `index` when omitted)
351
+ - `items` — any expression returning an array
352
+
353
+ #### Stable identity: `track <expr>`
354
+
355
+ For correct reuse across reorders / removals, supply a `track` clause whose
356
+ expression yields a unique key per item. Dotted paths are supported:
357
+
358
+ ```html
359
+ <li $for="row in rows track row.id">{{row.name}}</li>
360
+ <li $for="(item, i) in items track item.uid">{{item.title}}</li>
361
+ ```
362
+
363
+ When `track` is omitted the engine falls back to identity by **reference**
364
+ (`===`). Use `track` whenever items can be re-created (e.g. after a
365
+ `store.set('items', [...next])`).
366
+
367
+ #### Filtering helper
368
+
369
+ Inline filtering is supported via a `filter(<source>, <key>)` helper:
370
+
371
+ ```html
372
+ <li $for="item in filter(items, 'active') track item.id">{{item.name}}</li>
373
+ ```
374
+
375
+ Inside `filter`, items are kept where the named property is truthy.
376
+
377
+ ### 6.3 `$<custom>`
378
+
379
+ Any custom directive registered with the engine appears with the `$` prefix:
380
+
381
+ ```html
382
+ <input $autofocus>
383
+ <div $tooltip="'Click to edit'">…</div>
384
+ ```
385
+
386
+ Custom directives implement `transform`, `compile`, and `update` lifecycles
387
+ on a `AreDirective` subclass. See `src/directives/AreDirectiveFor.directive.ts`
388
+ for a reference implementation.
389
+
390
+ ---
391
+
392
+ ## 7. Built-in scope keywords
393
+
394
+ These identifiers always exist when an expression is evaluated:
395
+
396
+ | Identifier | Where | Meaning |
397
+ | ----------- | ------------- | ---------------------------------------- |
398
+ | `$event` | `@event=…` | The DOM event object |
399
+ | `item` / `<your-key>` | `$for=…` | The current iteration value |
400
+ | `index` / `<your-index>` | `$for=…` | The current iteration index |
401
+ | store keys | everywhere | Anything `store.set(<key>, …)` registers |
402
+
403
+ Component instance methods decorated with `@Are.EventHandler` are exposed
404
+ under the `$` prefix in event expressions: `@click="$save"` calls `save()`.
405
+
406
+ ---
407
+
408
+ ## 8. Putting it together — full example
409
+
410
+ ```html
411
+ <div class="dashboard" :class="{ loading: loading }">
412
+
413
+ <h1>Hello, {{ user.name }}!</h1>
414
+
415
+ <button :disabled="loading"
416
+ @click.prevent="$refresh">
417
+ Refresh
418
+ </button>
419
+
420
+ <input :value="filter"
421
+ @input="$onFilter($event.target.value)"
422
+ @keydown.enter="$apply"
423
+ @keydown.esc="$reset">
424
+
425
+ <div $if="error" class="error">{{ error }}</div>
426
+
427
+ <ul $if="!loading">
428
+ <li $for="user in filter(users, 'active') track user.id"
429
+ :class="{ selected: user.id === selectedId }"
430
+ @click="$select(user)">
431
+
432
+ <span class="name">{{ user.name }}</span>
433
+ <span $if="user.unread > 0" class="badge">{{ user.unread }}</span>
434
+ <button @click.stop="$remove(user)">×</button>
435
+ </li>
436
+ </ul>
437
+ </div>
438
+ ```
439
+
440
+ ---
441
+
442
+ ## 9. Cheat-sheet (one-liner per case)
443
+
444
+ ```html
445
+ <!-- 1. Static attribute -->
446
+ <div class="card" id="x" data-x="1"></div>
447
+
448
+ <!-- 2. Interpolation in text -->
449
+ <span>{{ value }}</span>
450
+
451
+ <!-- 3. Reactive attribute binding -->
452
+ <a :href="url"></a>
453
+
454
+ <!-- 4. Boolean attribute -->
455
+ <button :disabled="loading"></button>
456
+
457
+ <!-- 5. IDL form property -->
458
+ <input :value="email" :checked="agreed" type="checkbox">
459
+
460
+ <!-- 6. :class merge — string / array / object -->
461
+ <div :class="active ? 'on' : 'off'"></div>
462
+ <div :class="['btn', kind]"></div>
463
+ <div :class="{ on: active, big: large }"></div>
464
+
465
+ <!-- 7. :style — string or object -->
466
+ <div :style="'color:' + color"></div>
467
+ <div :style="{ color, fontSize: 14 }"></div>
468
+
469
+ <!-- 8. Custom prop binding (kebab/camel both work) -->
470
+ <my-card :user-name="user.name"></my-card>
471
+
472
+ <!-- 9. Event — handler reference -->
473
+ <button @click="$save"></button>
474
+
475
+ <!-- 10. Event — handler call with args -->
476
+ <button @click="$save(item, $event)"></button>
477
+
478
+ <!-- 11. Event — inline expression -->
479
+ <button @click="count = count + 1"></button>
480
+
481
+ <!-- 12. Event modifiers -->
482
+ <a @click.stop.prevent="$open"></a>
483
+ <form @submit.prevent="$submit"></form>
484
+ <div @scroll.passive="$onScroll"></div>
485
+ <div @click.self="$onSelf"></div>
486
+ <a @click.once="$track"></a>
487
+ <div @click.capture="$capture"></div>
488
+
489
+ <!-- 13. Key modifiers -->
490
+ <input @keydown.enter="$submit">
491
+ <input @keydown.esc="$cancel">
492
+ <input @keydown.ctrl.enter="$saveDraft">
493
+
494
+ <!-- 14. $if -->
495
+ <div $if="visible"></div>
496
+
497
+ <!-- 15. $for, with index, with parens -->
498
+ <li $for="item in items">{{item}}</li>
499
+ <li $for="item, i in items">{{i}}: {{item}}</li>
500
+ <li $for="(item, i) in items">{{i}}: {{item}}</li>
501
+
502
+ <!-- 16. $for with track -->
503
+ <li $for="row in rows track row.id">{{row.name}}</li>
504
+
505
+ <!-- 17. $for with inline filter -->
506
+ <li $for="u in filter(users, 'active') track u.id">{{u.name}}</li>
507
+
508
+ <!-- 18. $if + $for (must be on different elements!) -->
509
+ <div $if="show">
510
+ <li $for="x in xs track x.id">{{x.name}}</li>
511
+ </div>
512
+ ```
513
+
514
+ ---
515
+
516
+ ## 10. Tooling reference (for Monaco / TextMate / Tree-sitter)
517
+
518
+ This section enumerates **every token kind** in a machine-friendly form so you
519
+ can wire up syntax highlighting.
520
+
521
+ ### 10.1 Token kinds
522
+
523
+ | Kind | Pattern (regex, JS flavor) | Notes |
524
+ | ------------------- | --------------------------------------------------------------- | ----- |
525
+ | `tag.open` | `<[a-zA-Z][\w-]*` | Opening tag start |
526
+ | `tag.close` | `</[a-zA-Z][\w-]*\s*>` | Closing tag |
527
+ | `tag.selfClose` | `/>` | End of self-closing tag |
528
+ | `tag.end` | `>` | End of opening tag |
529
+ | `attr.static` | `\b[\w-]+(?=\s*=\|\s\|>\|/)` | Plain attribute name |
530
+ | `attr.binding` | `:[\w-]+` | `:value`, `:class`, `:user-name`, … |
531
+ | `attr.event` | `@[\w-]+(\.[\w-]+)*` | `@click`, `@keydown.enter`, `@click.stop.prevent` |
532
+ | `attr.directive` | `\$[\w-]+` | `$if`, `$for`, `$tooltip`, … |
533
+ | `attr.assign` | `=` | Attribute assignment |
534
+ | `attr.value.dq` | `"[^"]*"` | Double-quoted value (expression for `:`,`@`,`$`) |
535
+ | `attr.value.sq` | `'[^']*'` | Single-quoted value |
536
+ | `attr.value.bare` | `[^\s>/"'=]+` | Unquoted value |
537
+ | `interp.delimiter` | `\{\{` … `\}\}` | Interpolation start/end |
538
+ | `interp.expression` | content between `{{` and `}}` | JS-like expression |
539
+ | `comment.html` | `<!--` … `-->` | HTML comment |
540
+ | `text` | anything else | Plain text |
541
+
542
+ ### 10.2 Reserved attribute names
543
+
544
+ Highlight these as **directive keywords** when prefixed with `$`:
545
+
546
+ ```
547
+ if for else else-if show hide model ref slot key
548
+ ```
549
+
550
+ > Currently shipped: `$if`, `$for`. The rest are reserved for future built-ins.
551
+
552
+ ### 10.3 Reserved event modifiers
553
+
554
+ Highlight these as **modifier keywords** when following a `.` after an event
555
+ name:
556
+
557
+ ```
558
+ stop prevent self capture once passive
559
+ enter esc escape tab space delete backspace
560
+ up down left right
561
+ ctrl alt shift meta
562
+ ```
563
+
564
+ ### 10.4 Inside expression values
565
+
566
+ Inside the value of `:`, `@`, `$`, and `{{ … }}`, the content is a
567
+ **JS-like expression**:
568
+
569
+ - Identifiers: `[A-Za-z_$][\w$]*`
570
+ - Member access: `\.`, `?.`
571
+ - Numbers: `\d+(\.\d+)?`
572
+ - Strings: `'…'` and `"…"` with escapes
573
+ - Operators: `+ - * / % == != === !== < > <= >= && || ?? ! ?:`
574
+ - Punctuation: `( ) [ ] , ; { }`
575
+ - Reserved literals: `true`, `false`, `null`, `undefined`
576
+
577
+ Special identifiers worth highlighting:
578
+
579
+ | Identifier | Where | As |
580
+ | ---------- | -------------------- | ------------------------------- |
581
+ | `$event` | event expressions | `variable.special` |
582
+ | `$<name>` | event expressions | `function.handler` (calls method) |
583
+ | `track` | `$for` value | `keyword.control` (only after `in <expr>`) |
584
+ | `in` | `$for` value | `keyword.control` |
585
+ | `filter` | `$for` value | `support.function` |
586
+
587
+ ### 10.5 Suggested Monaco scope mapping (Monarch)
588
+
589
+ Drop-in starter for a Monaco Monarch language definition:
590
+
591
+ ```js
592
+ export const areHtmlLanguage = {
593
+ defaultToken: '',
594
+ tokenPostfix: '.arehtml',
595
+
596
+ keywords: ['if', 'for', 'else', 'else-if', 'show', 'hide', 'model', 'ref', 'slot', 'key'],
597
+
598
+ eventModifiers: [
599
+ 'stop','prevent','self','capture','once','passive',
600
+ 'enter','esc','escape','tab','space','delete','backspace',
601
+ 'up','down','left','right',
602
+ 'ctrl','alt','shift','meta'
603
+ ],
604
+
605
+ tokenizer: {
606
+ root: [
607
+ [/<!--/, 'comment', '@comment'],
608
+ [/\{\{/, { token: 'delimiter.interpolation', next: '@interp' }],
609
+ [/<\/[a-zA-Z][\w-]*\s*>/, 'tag'],
610
+ [/<[a-zA-Z][\w-]*/, { token: 'tag', next: '@tagAttrs' }],
611
+ [/[^<{]+/, ''],
612
+ ],
613
+
614
+ comment: [
615
+ [/-->/, 'comment', '@pop'],
616
+ [/[^-]+/, 'comment'],
617
+ [/./, 'comment'],
618
+ ],
619
+
620
+ interp: [
621
+ [/\}\}/, { token: 'delimiter.interpolation', next: '@pop' }],
622
+ { include: '@expr' },
623
+ ],
624
+
625
+ tagAttrs: [
626
+ [/\s+/, ''],
627
+ [/\/?>/, { token: 'tag', next: '@pop' }],
628
+
629
+ // directive
630
+ [/\$[\w-]+/, {
631
+ cases: {
632
+ '$if|$for': 'keyword.directive.builtin',
633
+ '@default': 'keyword.directive'
634
+ }
635
+ }],
636
+
637
+ // binding
638
+ [/:[\w-]+/, 'attribute.name.binding'],
639
+
640
+ // event with optional modifiers
641
+ [/@[\w-]+(?:\.[\w-]+)*/, {
642
+ cases: {
643
+ '@default': 'attribute.name.event'
644
+ }
645
+ }],
646
+
647
+ // static attribute
648
+ [/[\w-]+/, 'attribute.name'],
649
+
650
+ [/=/, 'delimiter'],
651
+
652
+ // expression-bearing values (anything quoted after :, @, $)
653
+ [/"/, { token: 'string.quote', next: '@dqValue' }],
654
+ [/'/, { token: 'string.quote', next: '@sqValue' }],
655
+ [/[^\s>/"'=]+/, 'string'],
656
+ ],
657
+
658
+ dqValue: [
659
+ [/"/, { token: 'string.quote', next: '@pop' }],
660
+ { include: '@expr' },
661
+ ],
662
+ sqValue: [
663
+ [/'/, { token: 'string.quote', next: '@pop' }],
664
+ { include: '@expr' },
665
+ ],
666
+
667
+ expr: [
668
+ [/\$event\b/, 'variable.predefined'],
669
+ [/\$[A-Za-z_]\w*/, 'function.handler'],
670
+ [/\b(true|false|null|undefined)\b/, 'constant.language'],
671
+ [/\b(in|track|filter)\b/, 'keyword.control'],
672
+ [/[A-Za-z_]\w*/, 'identifier'],
673
+ [/\d+(\.\d+)?/, 'number'],
674
+ [/[+\-*/%<>=!&|?:.,;()\[\]{}]+/, 'operator'],
675
+ [/\s+/, ''],
676
+ ],
677
+ }
678
+ };
679
+ ```
680
+
681
+ ### 10.6 Suggested TextMate-style scope names
682
+
683
+ For VS Code grammars, map ARE‑HTML tokens to these scopes:
684
+
685
+ | Token | TextMate scope |
686
+ | -------------------- | -------------------------------------------------- |
687
+ | `attr.binding` | `entity.other.attribute-name.binding.arehtml` |
688
+ | `attr.event` | `entity.other.attribute-name.event.arehtml` |
689
+ | `attr.directive` | `entity.other.attribute-name.directive.arehtml` |
690
+ | `event.modifier` | `keyword.other.modifier.event.arehtml` |
691
+ | `directive.builtin` | `keyword.control.directive.arehtml` |
692
+ | `interp.delimiter` | `punctuation.section.embedded.begin/end.arehtml` |
693
+ | `interp.expression` | `meta.embedded.expression.arehtml` |
694
+ | `$event` | `variable.language.event.arehtml` |
695
+ | `$<handler>` | `entity.name.function.handler.arehtml` |
696
+ | `track` / `in` / `filter` | `keyword.control.loop.arehtml` |
697
+
698
+ ---
699
+
700
+ ## 11. Notes for tooling authors
701
+
702
+ 1. Treat any attribute starting with `:`, `@`, or `$` as **expression-bearing**:
703
+ the value should be tokenized as JS, not as a literal string.
704
+ 2. The set of valid event modifiers is closed (see §5.3, §5.4). Anything else
705
+ after `@event.` is a **key name** (the framework matches `event.key === '<mod>'`).
706
+ 3. The set of currently-shipped directives is `$if`, `$for`. All other `$<name>`
707
+ should still be highlighted (custom directives), but not flagged as errors.
708
+ 4. Within `$for` values, the keywords `in`, `track`, and the helper `filter`
709
+ are reserved. The grammar is:
710
+ `(<key> | (<key>) | <key>, <index> | (<key>, <index>)) in <expr> ( track <expr> )?`.
711
+ 5. `:class` and `:style` accept string / array / object — the grammar inside is
712
+ plain JS; only the host attribute name is special.
713
+ 6. Boolean attributes and IDL form properties (see §4.2 and §4.3) are
714
+ **runtime** behaviors only — there is no syntactic difference for the editor.