@constela/runtime 1.0.0 → 1.0.1

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/README.md +176 -0
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -313,6 +313,182 @@ Server-rendered HTML is hydrated on the client without DOM reconstruction:
313
313
  }
314
314
  ```
315
315
 
316
+ ## Theme Provider
317
+
318
+ Manage application theming with reactive CSS variables:
319
+
320
+ ```typescript
321
+ import { createThemeProvider } from '@constela/runtime';
322
+
323
+ const theme = createThemeProvider({
324
+ config: {
325
+ mode: 'system',
326
+ colors: {
327
+ primary: 'hsl(220 90% 56%)',
328
+ background: 'hsl(0 0% 100%)',
329
+ },
330
+ darkColors: {
331
+ background: 'hsl(222 47% 11%)',
332
+ },
333
+ },
334
+ storageKey: 'app-theme',
335
+ useCookies: true,
336
+ });
337
+
338
+ // Get current theme
339
+ const current = theme.getTheme();
340
+ console.log(current.resolvedMode); // 'light' or 'dark'
341
+
342
+ // Switch theme
343
+ theme.setMode('dark');
344
+
345
+ // Subscribe to changes
346
+ const unsubscribe = theme.subscribe((theme) => {
347
+ console.log('Theme changed:', theme.resolvedMode);
348
+ });
349
+
350
+ // Cleanup
351
+ theme.destroy();
352
+ ```
353
+
354
+ **Features:**
355
+
356
+ - System preference detection via `prefers-color-scheme`
357
+ - CSS variable application to `:root`
358
+ - Dark class management on `document.documentElement`
359
+ - Persistence via localStorage with optional cookies (for SSR)
360
+ - Subscription-based change notifications
361
+
362
+ ## Realtime Features
363
+
364
+ ### SSE Connections
365
+
366
+ Establish Server-Sent Events connections:
367
+
368
+ ```json
369
+ {
370
+ "actions": [
371
+ {
372
+ "name": "connectToNotifications",
373
+ "steps": [
374
+ {
375
+ "do": "sseConnect",
376
+ "connection": "notifications",
377
+ "url": { "expr": "lit", "value": "/api/events" },
378
+ "eventTypes": ["message", "update", "delete"],
379
+ "reconnect": {
380
+ "enabled": true,
381
+ "strategy": "exponential",
382
+ "maxRetries": 5,
383
+ "baseDelay": 1000,
384
+ "maxDelay": 30000
385
+ },
386
+ "onMessage": [
387
+ { "do": "update", "target": "messages", "operation": "push", "value": { "expr": "var", "name": "payload" } }
388
+ ]
389
+ }
390
+ ]
391
+ }
392
+ ]
393
+ }
394
+ ```
395
+
396
+ **Reconnection Strategies:**
397
+
398
+ | Strategy | Description |
399
+ |----------|-------------|
400
+ | `exponential` | Exponential backoff (1s, 2s, 4s, 8s...) |
401
+ | `linear` | Linear backoff (1s, 2s, 3s, 4s...) |
402
+ | `none` | No automatic reconnection |
403
+
404
+ ### Optimistic Updates
405
+
406
+ Apply UI changes immediately with automatic rollback on failure:
407
+
408
+ ```json
409
+ {
410
+ "actions": [
411
+ {
412
+ "name": "likePost",
413
+ "steps": [
414
+ {
415
+ "do": "optimistic",
416
+ "target": "posts",
417
+ "path": { "expr": "var", "name": "payload", "path": "index" },
418
+ "value": { "expr": "lit", "value": { "liked": true } },
419
+ "result": "updateId",
420
+ "timeout": 5000
421
+ },
422
+ {
423
+ "do": "fetch",
424
+ "url": { "expr": "concat", "items": [
425
+ { "expr": "lit", "value": "/api/posts/" },
426
+ { "expr": "var", "name": "payload", "path": "id" },
427
+ { "expr": "lit", "value": "/like" }
428
+ ]},
429
+ "method": "POST",
430
+ "onSuccess": [
431
+ { "do": "confirm", "id": { "expr": "var", "name": "updateId" } }
432
+ ],
433
+ "onError": [
434
+ { "do": "reject", "id": { "expr": "var", "name": "updateId" } }
435
+ ]
436
+ }
437
+ ]
438
+ }
439
+ ]
440
+ }
441
+ ```
442
+
443
+ ### State Binding
444
+
445
+ Bind SSE messages directly to state:
446
+
447
+ ```json
448
+ {
449
+ "actions": [
450
+ {
451
+ "name": "bindNotifications",
452
+ "steps": [
453
+ {
454
+ "do": "bind",
455
+ "connection": "notifications",
456
+ "eventType": "update",
457
+ "target": "items",
458
+ "transform": { "expr": "get", "base": { "expr": "var", "name": "payload" }, "path": "data" }
459
+ }
460
+ ]
461
+ }
462
+ ]
463
+ }
464
+ ```
465
+
466
+ ## Island Hydration
467
+
468
+ Hydrate interactive islands with the appropriate strategy:
469
+
470
+ ```typescript
471
+ import { hydrateIsland } from '@constela/runtime';
472
+
473
+ // Hydrate an island when it becomes visible
474
+ hydrateIsland({
475
+ id: 'interactive-chart',
476
+ strategy: 'visible',
477
+ strategyOptions: { threshold: 0.5 },
478
+ program: compiledIslandProgram,
479
+ mount: document.querySelector('[data-island="interactive-chart"]'),
480
+ });
481
+ ```
482
+
483
+ **Supported Strategies:**
484
+
485
+ - `load` - Hydrate immediately
486
+ - `idle` - Hydrate when browser is idle (requestIdleCallback)
487
+ - `visible` - Hydrate when element enters viewport (IntersectionObserver)
488
+ - `interaction` - Hydrate on first user interaction (click, focus, mouseover)
489
+ - `media` - Hydrate when media query matches (matchMedia)
490
+ - `never` - Never hydrate (static content only)
491
+
316
492
  ## Security
317
493
 
318
494
  The runtime includes security measures:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@constela/runtime",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Runtime DOM renderer for Constela UI framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,8 +18,8 @@
18
18
  "dompurify": "^3.3.1",
19
19
  "marked": "^17.0.1",
20
20
  "shiki": "^3.20.0",
21
- "@constela/compiler": "0.15.0",
22
- "@constela/core": "0.17.0"
21
+ "@constela/compiler": "0.15.1",
22
+ "@constela/core": "0.17.1"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/dompurify": "^3.2.0",
@@ -32,7 +32,7 @@
32
32
  "@constela/server": "13.0.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@constela/ai": "2.0.0"
35
+ "@constela/ai": "2.0.1"
36
36
  },
37
37
  "peerDependenciesMeta": {
38
38
  "@constela/ai": {