aberdeen 1.4.0 → 1.4.3

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
@@ -79,14 +79,10 @@ function drawMain() {
79
79
 
80
80
  // Add item and delete checked buttons.
81
81
  $('div.row', () => {
82
- $('button#+', {
83
- click: () => items.push(new TodoItem("")),
84
- });
85
- $('button.outline#Delete checked', {
86
- click: () => {
87
- for(let idx in items) {
88
- if (items[idx].done) delete items[idx];
89
- }
82
+ $('button text=+ click=', () => items.push(new TodoItem("")));
83
+ $('button.outline text="Delete checked" click=', () => {
84
+ for(let idx in items) {
85
+ if (items[idx].done) delete items[idx];
90
86
  }
91
87
  });
92
88
  });
@@ -99,37 +95,33 @@ function drawItem(item) {
99
95
  // create below, so that it will persist when that state reruns.
100
96
  let editing: {value: boolean} = proxy(item.label == '');
101
97
 
102
- $('div.row', todoItemStyle, {create:grow, destroy: shrink}, () => {
98
+ $('div.row', todoItemStyle, 'create=', grow, 'destroy=', shrink, () => {
103
99
  // Conditionally add a class to `div.row`, based on item.done
104
100
  $({".done": ref(item,'done')});
105
101
 
106
102
  // The checkmark is hidden using CSS
107
- $('div.checkmark#✅');
103
+ $('div.checkmark text=✅');
108
104
 
109
105
  if (editing.value) {
110
- // Label <input>. Save using enter or button.
106
+ // Proxied string to hold label while being edited.
107
+ const labelCopy = proxy(item.label);
111
108
  function save() {
112
109
  editing.value = false;
113
- item.label = inputElement.value;
110
+ item.label = labelCopy.value;
114
111
  }
115
- let inputElement = $('input', {
116
- placeholder: 'Label',
117
- value: item.label,
118
- keydown: e => e.key==='Enter' && save(),
119
- });
120
- $('button.outline#Cancel', {click: () => editing.value = false});
121
- $('button#Save', {click: save});
112
+ // Label <input>. Save using enter or button.
113
+ $('input placeholder=Label bind=', labelCopy, 'keydown=', e => e.key==='Enter' && save());
114
+ $('button.outline text=Cancel click=', () => editing.value = false);
115
+ $('button text=Save click=', save);
122
116
  } else {
123
117
  // Label as text.
124
- $('p#' + item.label);
118
+ $('p text=', item.label);
125
119
 
126
120
  // Edit icon, if not done.
127
121
  if (!item.done) {
128
- $('a#Edit', {
129
- click: e => {
130
- editing.value = true;
131
- e.stopPropagation(); // We don't want to toggle as well.
132
- },
122
+ $('a text=Edit click=', e => {
123
+ editing.value = true;
124
+ e.stopPropagation(); // We don't want to toggle as well.
133
125
  });
134
126
  }
135
127
 
@@ -165,6 +157,7 @@ Some further examples:
165
157
  - [Routing demo](https://aberdeenjs.org/examples/route/) - [Source](https://github.com/vanviegen/aberdeen/tree/master/examples/route)
166
158
  - [JS Framework Benchmark demo](https://aberdeenjs.org/examples/js-framework-benchmark/) - [Source](https://github.com/vanviegen/aberdeen/tree/master/examples/js-framework-benchmark)
167
159
 
160
+
168
161
  ## Learning Aberdeen
169
162
 
170
163
  - [Tutorial](https://aberdeenjs.org/Tutorial/)
@@ -172,81 +165,18 @@ Some further examples:
172
165
 
173
166
  And you may want to study the examples above, of course!
174
167
 
175
- ## Changelog
176
-
177
- ### 1.4.0 (2025-01-07)
178
-
179
- **Enhancements:**
180
- - Shortcuts for common CSS properties. For instance: `$('div mv:10px')` for setting vertical (top and bottom) margins.
181
- - Variables you can set and use in CSS styles, e.g. `$('div bg:@myColor')` after setting `cssVars.myColor = 'red'`.
182
- - Default CSS variables are defined for spacing: `@2` is `0.5rem`, `@3` is `1rem`, etc. For example: `$('r:@3')` sets border radius to (a dynamically configurable) `1rem`.
183
- - All CSS shortcuts and `@` variables are also supported in `insertCss` and `insertGlobalCss`.
184
- - Added `insertGlobalCss` for adding global styles. The `global` argument to `insertCss` is now deprecated.
185
-
186
- **Fixes:**
187
- - When doing `$('div #first', () => $('#second'))`, *second* now comes after *first*. It used to be the other way around.
188
-
189
- ### 1.3.2 (2025-01-07)
190
-
191
- **Enhancements:**
192
- - It's now okay to first define a SELECT binding and then add its OPTIONs right after, while still allowing the binding to set the initial value. This used to throw an async error.
193
-
194
- **Fixes:**
195
- - Turns out some examples were still using old text content syntax.
196
-
197
- ### 1.3.1 (2025-01-07)
198
- **Fixes:**
199
- - Argument types accepted by `$` were too restrictive, as something like `$('prop=', myVal)` should be able to accept any type for `myVal`.
200
168
 
201
- ### 1.3.0 (2025-12-03)
202
- **Breaking changes:**
203
- - The shortcut for setting inline CSS styles in now `$('div color:red')` instead of `$('div $color=red')`.
204
- - The shortcut for adding text content is now `$('p#Hello')` instead of `$('p:Hello')`. It now also works with dynamic content: `$('p#', myObservable)`.
169
+ ## AI Integration
205
170
 
206
- **Enhancements:**
207
- - New A() string parser, reducing complexity and line count.
171
+ If you use Claude Code, GitHub Copilot or another AI agents that supports Skills, Aberdeen includes a `skill/` directory that provides specialized knowledge to the AI about how to use the library effectively.
208
172
 
209
- ### 1.2.0 (2025-09-27)
173
+ To use this, it is recommended to symlink the skill into your project's `.claude/skills` directory:
210
174
 
211
- **Enhancements:**
212
- - The `$` function now supports a more concise syntax for setting attributes and properties. Instead of writing `$('p', 'button', {$color: 'red', click: () => ...})`, you can now write `$('p button $color=red click=', () => ...)`.
213
- - The `proxy()` function can now accept `Promise`s, which will return an observable object with properties for `busy` status, `error` (if any), and the resolved `value`. This makes it easier to call async functions from within UI code.
214
-
215
- **Breaking changes:**
216
- - When a UI render function returns a `Promise`, that will now be reported as an error. Async render functions are fundamentally incompatible with Aberdeen's reactive model, so it's helpful to point that out. Use the new `proxy()` async support instead.
217
-
218
- ### 1.1.0 (2025-09-12)
219
-
220
- This major release aims to reduce surprises in our API, aligning more closely with regular JavaScript semantics (for better or worse).
221
-
222
- **Breaking changes:**
223
-
224
- - Functions that iterate objects (like `onEach` and `map`) will now only work on *own* properties of the object, ignoring those in the prototype chain. The new behavior should be more consistent and faster.
225
- - These iteration function now properly distinguish between `undefined` and *empty*. Previously, object/array/map items with `undefined` values were considered non-existent. The new behavior (though arguably confusing) is more consistent with regular JavaScript semantics.
226
- - The `copy` function no longer ..
227
- - Supports `SHALLOW` and `MERGE` flags. The latter has been replaced by a dedicated `merge` function. The former turned out not to be particularly useful.
228
- - Has weird special cases that would allow copying objects into maps and merging objects into arrays.
229
- - Copies properties from the prototype chain of objects. Only *own* properties are copied now. As the prototype link itself *is* copied over, this should actually result in copies being *more* similar to the original.
230
- - The `observe` function has been renamed to `derive` to better reflect its purpose and match terminology used in other reactive programming libraries.
231
- - The `$({element: myElement})` syntax for inserting existing DOM elements has been removed. Use `$(myElement)` instead.
232
- - The `route` API brings some significant changes. Modifying the `route` observable (which should now be accessed as `route.current`) will now always result in changing the current browser history item (URL and state, using `replaceState`), instead of using a heuristic to figure out what you probably want. Dedicated functions have been added for navigating to a new URL (`go`), back to a previous URL (`back`), and for going up in the route hierarchy (`up`).
233
- - The concept of immediate observers (through the `immediateObserve` function) no longer exists. It caused unexpected behavior (for instance due to the fact that an array `pop()` in JavaScript is implemented as a delete followed by a length change, so happens in two steps that would each call immediate observers). The reason it existed was mostly to enable a pre-1.0 version of the `route` API. It turned out to be a mistake.
234
-
235
- **Enhancements:**
236
-
237
- - The `peek` function can no also accept an object and a key as argument (e.g. `peek(obj, 'myKey')`). It does the same as `peek(() => obj.myKey)`, but more concise and faster.
238
- - The `copy` and `merge` functions now ..
239
- - Accept an optional `dstKey` argument, allowing you to assign to a specific key with `copy` semantics, and without subscribing to the key.
240
- - Return a boolean indicating whether any changes were made.
241
- - Are faster.
242
- - A new `dispatcher` module has been added. It provides a simple and type-safe way to match URL paths to handler functions, and extract parameters from the path. You can still use your own routing solution if you prefer, of course.
243
- - The `route` module now also has tests, making the whole project now fully covered by tests.
244
-
245
- **Fixes:**
246
-
247
- - Browser-back behavior in the `route` module had some reliability issues after page reloads.
248
- - The `copy` and `clone` function created Maps and Arrays with the wrong internal type. So `instanceof Array` would say yes, while `Array.isArray` would say no. JavaScript is weird.
175
+ ```bash
176
+ mkdir -p .claude/skills
177
+ ln -s ../../node_modules/aberdeen/skill .claude/skills/aberdeen
178
+ ```
249
179
 
250
- ### 1.0.0 (2025-05-07)
180
+ ## Changelog
251
181
 
252
- After five years of working on this library on and off, I'm finally happy with its API and the developer experience it offers. I'm calling it 1.0! To celebrate, I've created some pretty fancy (if I may say so myself) interactive documentation and a tutorial.
182
+ See [CHANGELOG.md](CHANGELOG.md) for a full history of changes.
@@ -16,7 +16,7 @@
16
16
  * ```typescript
17
17
  * const data = proxy("before");
18
18
  *
19
- * $({text: data});
19
+ * $('#'+data);
20
20
  * console.log(1, document.body.innerHTML); // before
21
21
  *
22
22
  * // Make an update that should cause the DOM to change.
@@ -80,9 +80,9 @@ export declare function onEach<K extends string | number | symbol, T>(target: Re
80
80
  * // Reactively display a message if the items array is empty
81
81
  * $('div', () => {
82
82
  * if (isEmpty(items)) {
83
- * $('p', 'i#No items yet!');
83
+ * $('p i#No items yet!');
84
84
  * } else {
85
- * onEach(items, item=>$('p#'+item));
85
+ * onEach(items, item => $('p#'+item));
86
86
  * }
87
87
  * });
88
88
  *
@@ -110,7 +110,7 @@ export interface ValueRef<T> {
110
110
  * const cnt = count(items);
111
111
  *
112
112
  * // Create a DOM text node for the count:
113
- * $('div', {text: cnt});
113
+ * $('div text=', cnt);
114
114
  * // <div>2</div>
115
115
 
116
116
  * // Or we can use it in an {@link derive} function:
@@ -293,17 +293,10 @@ export declare function clone<T extends object>(src: T): T;
293
293
  * const formData = proxy({ color: 'orange', velocity: 42 });
294
294
  *
295
295
  * // Usage with `bind`
296
- * $('input', {
297
- * type: 'text',
298
- * // Creates a two-way binding between the input's value and formData.username
299
- * bind: ref(formData, 'color')
300
- * });
296
+ * $('input type=text bind=', ref(formData, 'color'));
301
297
  *
302
298
  * // Usage as a dynamic property, causes a TextNode with just the name to be created and live-updated
303
- * $('p#Selected color: ', {
304
- * text: ref(formData, 'color'),
305
- * $color: ref(formData, 'color')
306
- * });
299
+ * $('p text="Selected color: " text=', ref(formData, 'color'), 'color:', ref(formData, 'color'));
307
300
  *
308
301
  * // Changes are actually stored in formData - this causes logs like `{color: "Blue", velocity 42}`
309
302
  * $(() => console.log(formData))
@@ -352,16 +345,7 @@ export declare function ref<T extends TargetType, K extends keyof T>(target: T,
352
345
  *
353
346
  * @example Create Element
354
347
  * ```typescript
355
- * $('button.secondary.outline#Submit', {
356
- * disabled: false,
357
- * click: () => console.log('Clicked!'),
358
- * $color: 'red'
359
- * });
360
- * ```
361
- *
362
- * Which can also be written as:
363
- * ```typescript
364
- * $('button.secondary.outline text=Submit $color=red disabled=', false, 'click=', () => console.log('Clicked!'));
348
+ * $('button.secondary.outline text=Submit color:red disabled=', false, 'click=', () => console.log('Clicked!'));
365
349
  * ```
366
350
  *
367
351
  * We want to set `disabled` as a property instead of an attribute, so we must use the `key=` syntax in order to provide
@@ -369,7 +353,7 @@ export declare function ref<T extends TargetType, K extends keyof T>(target: T,
369
353
  *
370
354
  * @example Create Nested Elements
371
355
  * ```typescript
372
- * let inputElement: Element = $('label#Click me', 'input', {type: 'checkbox'});
356
+ * let inputElement: Element = $('label text="Click me" input type=checkbox');
373
357
  * // You should usually not touch raw DOM elements, unless when integrating
374
358
  * // with non-Aberdeen code.
375
359
  * console.log('DOM element:', inputElement);
@@ -381,14 +365,14 @@ export declare function ref<T extends TargetType, K extends keyof T>(target: T,
381
365
  * $('div', () => { // Outer element
382
366
  * // This scope re-renders when state.count changes
383
367
  * $(`p#Count is ${state.count}`);
384
- * $('button#Increment', { click: () => state.count++ });
368
+ * $('button text=Increment click=', () => state.count++);
385
369
  * });
386
370
  * ```
387
371
  *
388
372
  * @example Two-way Binding
389
373
  * ```typescript
390
374
  * const user = proxy({ name: '' });
391
- * $('input', { placeholder: 'Name', bind: ref(user, 'name') });
375
+ * $('input placeholder=Name bind=', ref(user, 'name'));
392
376
  * $('h3', () => { // Reactive scope
393
377
  * $(`#Hello ${user.name || 'stranger'}`);
394
378
  * });
@@ -397,7 +381,7 @@ export declare function ref<T extends TargetType, K extends keyof T>(target: T,
397
381
  * @example Conditional Rendering
398
382
  * ```typescript
399
383
  * const show = proxy(false);
400
- * $('button', { click: () => show.value = !show.value }, () => $(show.value ? '#Hide' : '#Show'));
384
+ * $('button click=', () => show.value = !show.value, () => $(show.value ? '#Hide' : '#Show'));
401
385
  * $(() => { // Reactive scope
402
386
  * if (show.value) {
403
387
  * $('p#Details are visible!');
@@ -418,7 +402,7 @@ export declare function $(...args: any[]): undefined | Element;
418
402
  * - In case a selector contains a `&`, that character will be replaced by the parent selector.
419
403
  * - Selectors will be split on `,` characters, each combining with the parent selector with *or* semantics.
420
404
  * - Selector starting with `'@'` define at-rules like media queries. They may be nested within regular selectors.
421
- * @param global - @deprecated Use {@link insertGlobalCss} instead.
405
+ * @param global - Deprecated! Use {@link insertGlobalCss} instead.
422
406
  * @returns The unique class name prefix used for scoping (e.g., `.AbdStl1`). Use this
423
407
  * prefix with {@link $} to apply the styles.
424
408
  *
@@ -575,7 +559,7 @@ export declare function getParentElement(): Element;
575
559
  * })
576
560
  *
577
561
  * // Show the sum
578
- * $('h1', {text: sum});
562
+ * $('h1 text=', sum);
579
563
  *
580
564
  * // Make random changes to the array
581
565
  * const rnd = () => 0|(Math.random()*20);
@@ -609,8 +593,8 @@ export declare function clean(cleaner: () => void): void;
609
593
  * // When data.notifications changes, only this inner scope reruns,
610
594
  * // leaving the `<p>Welcome, ..</p>` untouched.
611
595
  * console.log('Notifications');
612
- * $('code.notification-badge#' + data.notifications);
613
- * $('a#Notify!', {click: () => data.notifications++});
596
+ * $('code.notification-badge text=', data.notifications);
597
+ * $('a text=Notify! click=', () => data.notifications++);
614
598
  * });
615
599
  * });
616
600
  * ```
@@ -707,7 +691,7 @@ export declare function unmountAll(): void;
707
691
  * ```
708
692
  *
709
693
  */
710
- export declare function peek<T extends object>(target: T, key: keyof T): T[typeof key];
694
+ export declare function peek<T extends object, K extends keyof T>(target: T, key: K): T[K];
711
695
  export declare function peek<K, V>(target: Map<K, V>, key: K): V | undefined;
712
696
  export declare function peek<T>(target: T[], key: number): T | undefined;
713
697
  export declare function peek<T>(target: () => T): T;