kopular 0.9.0 → 0.10.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.
- package/LLM.md +16 -0
- package/README.md +3 -1
- package/package.json +1 -1
- package/src/component.js +14 -2
- package/src/component.ks +22 -2
package/LLM.md
CHANGED
|
@@ -124,6 +124,22 @@ w.Bump(); // re-render
|
|
|
124
124
|
reconciliation. Compose independent components into stable slots (see `Router`'s own
|
|
125
125
|
pattern of keeping page instances alive) to avoid this rather than nesting components
|
|
126
126
|
that both re-render.
|
|
127
|
+
- `RenderError(string message)`: `virtual`, called by `Mount()`/`Update()` (via a private
|
|
128
|
+
`SafeRender()` wrapper) when `Render()` throws, instead of letting the exception
|
|
129
|
+
propagate uncaught and crash whatever triggered the render (a click handler, a `Router`
|
|
130
|
+
navigation). The base implementation just `throw`s `message` again — **purely additive,
|
|
131
|
+
opt-in error recovery**; a `Component` that never overrides `RenderError` behaves
|
|
132
|
+
exactly as before this existed. Override it to show a fallback UI instead:
|
|
133
|
+
```ks
|
|
134
|
+
protected override Element RenderError(string message) {
|
|
135
|
+
Element el = document.createElement("div");
|
|
136
|
+
el.textContent = "Something went wrong: " + message;
|
|
137
|
+
return el;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
A later successful `Update()` (e.g. from a "Retry" button in that fallback calling back
|
|
141
|
+
into the component) renders normally again — there's no separate "broken" state to
|
|
142
|
+
reset, `SafeRender()` just tries `Render()` again like any other `Update()`.
|
|
127
143
|
|
|
128
144
|
## `Router` (`router.ks`)
|
|
129
145
|
|
package/README.md
CHANGED
|
@@ -15,7 +15,9 @@ Generating Kopular code with an AI coding assistant? Point it at **[`LLM.md`](./
|
|
|
15
15
|
- **`Component`**: a base class with `virtual Render()` (builds a fresh DOM subtree from
|
|
16
16
|
current state) and `Update()` (swaps the old subtree for the new one). No template
|
|
17
17
|
language, no diffing — components build/update the DOM imperatively against plain DOM
|
|
18
|
-
bindings, the way you'd write careful vanilla-JS UI code.
|
|
18
|
+
bindings, the way you'd write careful vanilla-JS UI code. An optional `virtual
|
|
19
|
+
RenderError(message)` renders a fallback instead of a hard crash if `Render()` throws —
|
|
20
|
+
purely additive; not overriding it keeps today's exact (uncaught) behavior.
|
|
19
21
|
- **Reactive state, no RxJS**: components hold `state<number>`/`state<string>`/... (a
|
|
20
22
|
KopScript language feature — see the [Kop](https://dev.azure.com/koppinator/Koppindependence/_git/Kop)
|
|
21
23
|
repo) and subscribe once, in their constructor, to call `Update()` on change. No
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kopular",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Kopular: a small component framework for KopScript — components, reactive state, constructor-injected services, routing, structural directives, and HTTP, with no template DSL and no DI container",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/component.js
CHANGED
|
@@ -5,14 +5,26 @@ export class Component {
|
|
|
5
5
|
return document.createElement("div");
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
RenderError(message) {
|
|
9
|
+
throw message;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
SafeRender() {
|
|
13
|
+
try {
|
|
14
|
+
return this.Render();
|
|
15
|
+
} catch (message) {
|
|
16
|
+
return this.RenderError(message);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
8
20
|
Mount(parent) {
|
|
9
21
|
(this.ParentElement = parent);
|
|
10
|
-
(this.Root = this.
|
|
22
|
+
(this.Root = this.SafeRender());
|
|
11
23
|
parent.appendChild(this.Root);
|
|
12
24
|
}
|
|
13
25
|
|
|
14
26
|
Update() {
|
|
15
|
-
let newRoot = this.
|
|
27
|
+
let newRoot = this.SafeRender();
|
|
16
28
|
this.ParentElement.replaceChild(newRoot, this.Root);
|
|
17
29
|
(this.Root = newRoot);
|
|
18
30
|
}
|
package/src/component.ks
CHANGED
|
@@ -21,14 +21,34 @@ class Component {
|
|
|
21
21
|
return document.createElement("div");
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// Overridden to render a fallback UI when Render() throws — a page bug,
|
|
25
|
+
// an unhandled rejected Http call, anything — instead of leaving
|
|
26
|
+
// Mount()/Update() to propagate the exception uncaught, which would
|
|
27
|
+
// otherwise crash whatever triggered the render (a click handler, a
|
|
28
|
+
// Router navigation) with nothing shown to the user at all. Default just
|
|
29
|
+
// re-throws, so anything that doesn't override this keeps today's exact
|
|
30
|
+
// behavior — this is purely additive, opt-in error recovery, not a
|
|
31
|
+
// behavior change for existing components.
|
|
32
|
+
protected virtual Element RenderError(string message) {
|
|
33
|
+
throw message;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private Element SafeRender() {
|
|
37
|
+
try {
|
|
38
|
+
return this.Render();
|
|
39
|
+
} catch (string message) {
|
|
40
|
+
return this.RenderError(message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
public void Mount(Element parent) {
|
|
25
45
|
this.ParentElement = parent;
|
|
26
|
-
this.Root = this.
|
|
46
|
+
this.Root = this.SafeRender();
|
|
27
47
|
parent.appendChild(this.Root);
|
|
28
48
|
}
|
|
29
49
|
|
|
30
50
|
protected void Update() {
|
|
31
|
-
Element newRoot = this.
|
|
51
|
+
Element newRoot = this.SafeRender();
|
|
32
52
|
this.ParentElement.replaceChild(newRoot, this.Root);
|
|
33
53
|
this.Root = newRoot;
|
|
34
54
|
}
|