@adukiorg/anza 0.2.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/CHANGELOG.md +137 -0
- package/README.md +215 -0
- package/bin/anza.js +63 -0
- package/bin/create.js +150 -0
- package/importmap.json +72 -0
- package/package.json +100 -0
- package/src/core/animations/index.js +55 -0
- package/src/core/animations/play.js +111 -0
- package/src/core/animations/registry.js +54 -0
- package/src/core/animations/scroll.js +50 -0
- package/src/core/animations/tokens.js +58 -0
- package/src/core/animations/usage.md +301 -0
- package/src/core/animations/waapi.js +86 -0
- package/src/core/api/cache.js +120 -0
- package/src/core/api/caches/glob.js +24 -0
- package/src/core/api/caches/index.js +118 -0
- package/src/core/api/events/index.js +75 -0
- package/src/core/api/fetch.js +99 -0
- package/src/core/api/index.js +158 -0
- package/src/core/api/pipeline.js +98 -0
- package/src/core/api/plan.md +209 -0
- package/src/core/api/prefixes/index.js +66 -0
- package/src/core/api/retry.js +69 -0
- package/src/core/api/stream.js +127 -0
- package/src/core/api/upload.js +180 -0
- package/src/core/api/usage.md +206 -0
- package/src/core/events/bus.js +38 -0
- package/src/core/events/delegate.js +79 -0
- package/src/core/events/index.js +26 -0
- package/src/core/events/listen.js +50 -0
- package/src/core/events/missing.md +103 -0
- package/src/core/events/once.js +49 -0
- package/src/core/events/plan.md +177 -0
- package/src/core/events/types/index.js +34 -0
- package/src/core/events/usage.md +107 -0
- package/src/core/offline/bridge.js +51 -0
- package/src/core/offline/clock.js +100 -0
- package/src/core/offline/connectivity.js +116 -0
- package/src/core/offline/index.js +41 -0
- package/src/core/offline/missing.md +89 -0
- package/src/core/offline/plan.md +143 -0
- package/src/core/offline/queue.js +168 -0
- package/src/core/offline/state.js +18 -0
- package/src/core/offline/sync.js +106 -0
- package/src/core/offline/usage.md +273 -0
- package/src/core/platform/guard.js +104 -0
- package/src/core/platform/index.js +42 -0
- package/src/core/platform/missing.md +119 -0
- package/src/core/platform/platform.d.ts +88 -0
- package/src/core/platform/polyfills/anchor.js +79 -0
- package/src/core/platform/polyfills/navigation.js +142 -0
- package/src/core/platform/polyfills/popover.js +142 -0
- package/src/core/platform/polyfills/scheduler.js +194 -0
- package/src/core/platform/polyfills/shadow.js +35 -0
- package/src/core/platform/polyfills/urlpattern.js +119 -0
- package/src/core/platform/supports.js +186 -0
- package/src/core/platform/usage.md +287 -0
- package/src/core/router/cache.js +95 -0
- package/src/core/router/container.js +146 -0
- package/src/core/router/handler.js +52 -0
- package/src/core/router/history.js +120 -0
- package/src/core/router/index.js +158 -0
- package/src/core/router/intercept.js +376 -0
- package/src/core/router/match.js +145 -0
- package/src/core/router/missing.md +716 -0
- package/src/core/router/outlet.js +139 -0
- package/src/core/router/plan.md +370 -0
- package/src/core/router/sync/index.js +16 -0
- package/src/core/router/sync/tab.js +115 -0
- package/src/core/router/sync/transport.js +139 -0
- package/src/core/router/transitions.js +59 -0
- package/src/core/router/usage.md +773 -0
- package/src/core/security/crypto.js +159 -0
- package/src/core/security/index.js +49 -0
- package/src/core/security/missing.md +97 -0
- package/src/core/security/permissions.js +64 -0
- package/src/core/security/sanitize.js +100 -0
- package/src/core/security/usage.md +283 -0
- package/src/core/state/derived.js +117 -0
- package/src/core/state/index.js +23 -0
- package/src/core/state/missing.md +165 -0
- package/src/core/state/persist.js +284 -0
- package/src/core/state/store.js +308 -0
- package/src/core/state/sync.js +46 -0
- package/src/core/state/usage.md +440 -0
- package/src/core/storage/cache.js +83 -0
- package/src/core/storage/idb.js +196 -0
- package/src/core/storage/index.js +373 -0
- package/src/core/storage/lru.js +107 -0
- package/src/core/storage/missing.md +165 -0
- package/src/core/storage/opfs.js +190 -0
- package/src/core/storage/plan.md +69 -0
- package/src/core/storage/quota.js +69 -0
- package/src/core/storage/usage.md +226 -0
- package/src/core/ui/base.js +50 -0
- package/src/core/ui/define/container.js +82 -0
- package/src/core/ui/define/define.js +12 -0
- package/src/core/ui/define/element.js +390 -0
- package/src/core/ui/define/index.js +9 -0
- package/src/core/ui/define/orchestrator.js +105 -0
- package/src/core/ui/define/proxy.js +644 -0
- package/src/core/ui/define/state.js +6 -0
- package/src/core/ui/define/utils.js +134 -0
- package/src/core/ui/implementation.md +170 -0
- package/src/core/ui/index.js +41 -0
- package/src/core/ui/observe.js +117 -0
- package/src/core/ui/plan.md +510 -0
- package/src/core/ui/schedule.js +60 -0
- package/src/core/ui/template.js +37 -0
- package/src/core/ui/transitions.js +37 -0
- package/src/core/ui/ui.types.md +890 -0
- package/src/core/ui/usage.md +1124 -0
- package/src/core/ui/watch.md +346 -0
- package/src/core/workers/broadcast.js +138 -0
- package/src/core/workers/dedicated.js +153 -0
- package/src/core/workers/index.js +169 -0
- package/src/core/workers/locks.js +160 -0
- package/src/core/workers/offscreen.js +166 -0
- package/src/core/workers/plan.md +381 -0
- package/src/core/workers/pool.js +267 -0
- package/src/core/workers/shared.js +137 -0
- package/src/core/workers/usage.md +622 -0
- package/src/elements/base.js +12 -0
- package/src/elements/data/card/index.html +9 -0
- package/src/elements/data/card/index.js +19 -0
- package/src/elements/data/card/index.tags.json +1 -0
- package/src/elements/data/card/style.css +46 -0
- package/src/elements/data/chart/index.html +1 -0
- package/src/elements/data/chart/index.js +143 -0
- package/src/elements/data/chart/index.tags.json +1 -0
- package/src/elements/data/chart/style.css +13 -0
- package/src/elements/data/list/index.html +3 -0
- package/src/elements/data/list/index.js +19 -0
- package/src/elements/data/list/index.tags.json +1 -0
- package/src/elements/data/list/style.css +39 -0
- package/src/elements/data/stat/index.html +9 -0
- package/src/elements/data/stat/index.js +19 -0
- package/src/elements/data/stat/index.tags.json +1 -0
- package/src/elements/data/stat/style.css +50 -0
- package/src/elements/data/table/index.html +1 -0
- package/src/elements/data/table/index.js +16 -0
- package/src/elements/data/table/index.tags.json +1 -0
- package/src/elements/data/table/style.css +50 -0
- package/src/elements/feedback/alert/index.html +11 -0
- package/src/elements/feedback/alert/index.js +28 -0
- package/src/elements/feedback/alert/index.tags.json +1 -0
- package/src/elements/feedback/alert/style.css +75 -0
- package/src/elements/feedback/empty/index.html +13 -0
- package/src/elements/feedback/empty/index.js +34 -0
- package/src/elements/feedback/empty/index.tags.json +1 -0
- package/src/elements/feedback/empty/style.css +45 -0
- package/src/elements/feedback/progress/index.html +7 -0
- package/src/elements/feedback/progress/index.js +46 -0
- package/src/elements/feedback/progress/index.tags.json +1 -0
- package/src/elements/feedback/progress/style.css +36 -0
- package/src/elements/feedback/skeleton/index.html +1 -0
- package/src/elements/feedback/skeleton/index.js +78 -0
- package/src/elements/feedback/skeleton/index.tags.json +1 -0
- package/src/elements/feedback/skeleton/style.css +28 -0
- package/src/elements/feedback/toast/index.html +3 -0
- package/src/elements/feedback/toast/index.js +65 -0
- package/src/elements/feedback/toast/index.tags.json +1 -0
- package/src/elements/feedback/toast/style.css +36 -0
- package/src/elements/forms/checkbox/index.html +7 -0
- package/src/elements/forms/checkbox/index.js +104 -0
- package/src/elements/forms/checkbox/index.tags.json +1 -0
- package/src/elements/forms/checkbox/style.css +86 -0
- package/src/elements/forms/field/index.html +13 -0
- package/src/elements/forms/field/index.js +42 -0
- package/src/elements/forms/field/index.tags.json +1 -0
- package/src/elements/forms/field/style.css +42 -0
- package/src/elements/forms/form/index.html +3 -0
- package/src/elements/forms/form/index.js +122 -0
- package/src/elements/forms/form/index.tags.json +1 -0
- package/src/elements/forms/form/style.css +11 -0
- package/src/elements/forms/input/index.html +4 -0
- package/src/elements/forms/input/index.js +103 -0
- package/src/elements/forms/input/index.tags.json +1 -0
- package/src/elements/forms/input/style.css +39 -0
- package/src/elements/forms/radio/index.html +4 -0
- package/src/elements/forms/radio/index.js +109 -0
- package/src/elements/forms/radio/index.tags.json +1 -0
- package/src/elements/forms/radio/style.css +65 -0
- package/src/elements/forms/select/index.html +9 -0
- package/src/elements/forms/select/index.js +114 -0
- package/src/elements/forms/select/index.tags.json +1 -0
- package/src/elements/forms/select/style.css +95 -0
- package/src/elements/forms/textarea/index.html +4 -0
- package/src/elements/forms/textarea/index.js +115 -0
- package/src/elements/forms/textarea/index.tags.json +1 -0
- package/src/elements/forms/textarea/style.css +46 -0
- package/src/elements/forms/toggle/index.html +4 -0
- package/src/elements/forms/toggle/index.js +89 -0
- package/src/elements/forms/toggle/index.tags.json +1 -0
- package/src/elements/forms/toggle/style.css +63 -0
- package/src/elements/forms/upload/index.html +13 -0
- package/src/elements/forms/upload/index.js +120 -0
- package/src/elements/forms/upload/index.tags.json +1 -0
- package/src/elements/forms/upload/style.css +61 -0
- package/src/elements/index.js +71 -0
- package/src/elements/layout/app/index.html +7 -0
- package/src/elements/layout/app/index.js +16 -0
- package/src/elements/layout/app/index.tags.json +1 -0
- package/src/elements/layout/app/style.css +41 -0
- package/src/elements/layout/grid/index.html +3 -0
- package/src/elements/layout/grid/index.js +41 -0
- package/src/elements/layout/grid/index.tags.json +1 -0
- package/src/elements/layout/grid/style.css +12 -0
- package/src/elements/layout/header/index.html +8 -0
- package/src/elements/layout/header/index.js +16 -0
- package/src/elements/layout/header/index.tags.json +1 -0
- package/src/elements/layout/header/style.css +28 -0
- package/src/elements/layout/scroll/index.html +3 -0
- package/src/elements/layout/scroll/index.js +19 -0
- package/src/elements/layout/scroll/index.tags.json +1 -0
- package/src/elements/layout/scroll/style.css +24 -0
- package/src/elements/layout/sidebar/index.html +3 -0
- package/src/elements/layout/sidebar/index.js +24 -0
- package/src/elements/layout/sidebar/index.tags.json +1 -0
- package/src/elements/layout/sidebar/style.css +30 -0
- package/src/elements/layout/split/index.html +3 -0
- package/src/elements/layout/split/index.js +18 -0
- package/src/elements/layout/split/index.tags.json +1 -0
- package/src/elements/layout/split/style.css +28 -0
- package/src/elements/layout/stack/index.html +3 -0
- package/src/elements/layout/stack/index.js +31 -0
- package/src/elements/layout/stack/index.tags.json +1 -0
- package/src/elements/layout/stack/style.css +15 -0
- package/src/elements/layout/surface/index.html +3 -0
- package/src/elements/layout/surface/index.js +19 -0
- package/src/elements/layout/surface/index.tags.json +1 -0
- package/src/elements/layout/surface/style.css +29 -0
- package/src/elements/navigation/breadcrumb/index.html +5 -0
- package/src/elements/navigation/breadcrumb/index.js +16 -0
- package/src/elements/navigation/breadcrumb/index.tags.json +1 -0
- package/src/elements/navigation/breadcrumb/style.css +36 -0
- package/src/elements/navigation/nav/index.html +3 -0
- package/src/elements/navigation/nav/index.js +24 -0
- package/src/elements/navigation/nav/index.tags.json +1 -0
- package/src/elements/navigation/nav/style.css +38 -0
- package/src/elements/navigation/pagination/index.html +3 -0
- package/src/elements/navigation/pagination/index.js +94 -0
- package/src/elements/navigation/pagination/index.tags.json +1 -0
- package/src/elements/navigation/pagination/style.css +39 -0
- package/src/elements/navigation/steps/index.html +6 -0
- package/src/elements/navigation/steps/index.js +64 -0
- package/src/elements/navigation/steps/index.tags.json +1 -0
- package/src/elements/navigation/steps/style.css +78 -0
- package/src/elements/navigation/tabs/index.html +6 -0
- package/src/elements/navigation/tabs/index.js +132 -0
- package/src/elements/navigation/tabs/index.tags.json +1 -0
- package/src/elements/navigation/tabs/style.css +52 -0
- package/src/elements/overlay/dialog/index.html +5 -0
- package/src/elements/overlay/dialog/index.js +57 -0
- package/src/elements/overlay/dialog/index.tags.json +1 -0
- package/src/elements/overlay/dialog/style.css +31 -0
- package/src/elements/overlay/drawer/index.html +3 -0
- package/src/elements/overlay/drawer/index.js +56 -0
- package/src/elements/overlay/drawer/index.tags.json +1 -0
- package/src/elements/overlay/drawer/style.css +48 -0
- package/src/elements/overlay/menu/index.html +3 -0
- package/src/elements/overlay/menu/index.js +107 -0
- package/src/elements/overlay/menu/index.tags.json +1 -0
- package/src/elements/overlay/menu/style.css +43 -0
- package/src/elements/overlay/popover/index.html +3 -0
- package/src/elements/overlay/popover/index.js +44 -0
- package/src/elements/overlay/popover/index.tags.json +1 -0
- package/src/elements/overlay/popover/style.css +21 -0
- package/src/elements/overlay/sheet/index.html +8 -0
- package/src/elements/overlay/sheet/index.js +105 -0
- package/src/elements/overlay/sheet/index.tags.json +1 -0
- package/src/elements/overlay/sheet/style.css +64 -0
- package/src/elements/overlay/tooltip/index.html +6 -0
- package/src/elements/overlay/tooltip/index.js +16 -0
- package/src/elements/overlay/tooltip/index.tags.json +1 -0
- package/src/elements/overlay/tooltip/style.css +41 -0
- package/src/elements/primitives/avatar/index.html +2 -0
- package/src/elements/primitives/avatar/index.js +79 -0
- package/src/elements/primitives/avatar/index.tags.json +1 -0
- package/src/elements/primitives/avatar/style.css +36 -0
- package/src/elements/primitives/badge/index.html +3 -0
- package/src/elements/primitives/badge/index.js +20 -0
- package/src/elements/primitives/badge/index.tags.json +1 -0
- package/src/elements/primitives/badge/style.css +67 -0
- package/src/elements/primitives/button/index.html +3 -0
- package/src/elements/primitives/button/index.js +61 -0
- package/src/elements/primitives/button/index.tags.json +1 -0
- package/src/elements/primitives/button/style.css +66 -0
- package/src/elements/primitives/divider/index.html +1 -0
- package/src/elements/primitives/divider/index.js +43 -0
- package/src/elements/primitives/divider/index.tags.json +1 -0
- package/src/elements/primitives/divider/style.css +39 -0
- package/src/elements/primitives/icon/index.html +3 -0
- package/src/elements/primitives/icon/index.js +66 -0
- package/src/elements/primitives/icon/index.tags.json +1 -0
- package/src/elements/primitives/icon/style.css +20 -0
- package/src/elements/primitives/link/index.html +3 -0
- package/src/elements/primitives/link/index.js +129 -0
- package/src/elements/primitives/link/index.tags.json +1 -0
- package/src/elements/primitives/link/style.css +40 -0
- package/src/elements/primitives/spinner/index.html +1 -0
- package/src/elements/primitives/spinner/index.js +62 -0
- package/src/elements/primitives/spinner/index.tags.json +1 -0
- package/src/elements/primitives/spinner/style.css +20 -0
- package/src/elements/primitives/text/index.html +1 -0
- package/src/elements/primitives/text/index.js +79 -0
- package/src/elements/primitives/text/index.tags.json +1 -0
- package/src/elements/primitives/text/style.css +25 -0
- package/src/index.js +23 -0
- package/src/styles/base.css +66 -0
- package/src/styles/index.css +10 -0
- package/src/styles/layers.css +9 -0
- package/src/styles/reset.css +66 -0
- package/src/sw/activate.js +51 -0
- package/src/sw/expire.js +47 -0
- package/src/sw/index.js +28 -0
- package/src/sw/install.js +35 -0
- package/src/sw/push.js +58 -0
- package/src/sw/queue.js +60 -0
- package/src/sw/routes.js +71 -0
- package/src/sw/strategies.js +247 -0
- package/src/sw/sync.js +80 -0
- package/src/tokens/index.css +26 -0
- package/src/tokens/primitives/colors.css +54 -0
- package/src/tokens/primitives/motion.css +34 -0
- package/src/tokens/primitives/radius.css +16 -0
- package/src/tokens/primitives/shadow.css +34 -0
- package/src/tokens/primitives/spacing.css +27 -0
- package/src/tokens/primitives/typography.css +46 -0
- package/src/tokens/primitives/zindex.css +18 -0
- package/src/tokens/registered/colors.css +133 -0
- package/src/tokens/registered/dimensions.css +31 -0
- package/src/tokens/semantic/components.css +125 -0
- package/src/tokens/semantic/contrast.css +33 -0
- package/src/tokens/semantic/dark.css +61 -0
- package/src/tokens/semantic/light.css +64 -0
- package/types/core/animations/index.d.ts +52 -0
- package/types/core/api/index.d.ts +68 -0
- package/types/core/events/index.d.ts +50 -0
- package/types/core/offline/index.d.ts +68 -0
- package/types/core/platform/index.d.ts +60 -0
- package/types/core/router/index.d.ts +203 -0
- package/types/core/security/index.d.ts +33 -0
- package/types/core/state/index.d.ts +68 -0
- package/types/core/storage/index.d.ts +40 -0
- package/types/core/ui/index.d.ts +446 -0
- package/types/core/workers/index.d.ts +221 -0
- package/types/elements/index.d.ts +150 -0
- package/types/index.d.ts +18 -0
|
@@ -0,0 +1,622 @@
|
|
|
1
|
+
# Native Workers Usage Guide
|
|
2
|
+
|
|
3
|
+
The Native Workers layer provides a small browser-native concurrency facade for Dedicated Workers, pooled task execution, SharedWorker connections, BroadcastChannel events, Web Locks, and OffscreenCanvas rendering.
|
|
4
|
+
|
|
5
|
+
Status: this guide documents the implemented public Workers contract: `workers.run`, `workers.dedicated`, `workers.shared`, `workers.lock`, `workers.broadcast`, `workers.subscribe`, `workers.offscreen`, `workers.close`, `workers.clear`, feature detection, request/response messages, transferables, and lifecycle cleanup.
|
|
6
|
+
|
|
7
|
+
Import from the workers entry point:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
import { workers, has } from '@adukiorg/anza/workers';
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 1. Feature Detection
|
|
14
|
+
|
|
15
|
+
Use `has` when behavior depends on a browser primitive.
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
if (!has.worker) {
|
|
19
|
+
throw new Error('This feature needs Web Worker support');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (has.isolated) {
|
|
23
|
+
// SharedArrayBuffer paths may be enabled here.
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Flags:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
has.worker
|
|
31
|
+
has.shared
|
|
32
|
+
has.channel
|
|
33
|
+
has.locks
|
|
34
|
+
has.offscreen
|
|
35
|
+
has.isolated
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Rules:
|
|
39
|
+
|
|
40
|
+
- `has.worker` gates Dedicated Workers and pools.
|
|
41
|
+
- `has.shared` only tells whether native SharedWorker is available; `workers.shared` can still fall back to a Dedicated Worker.
|
|
42
|
+
- `has.channel` gates BroadcastChannel messaging.
|
|
43
|
+
- `has.locks` gates native cross-context Web Locks; `workers.lock` has a same-tab fallback.
|
|
44
|
+
- `has.offscreen` gates OffscreenCanvas.
|
|
45
|
+
- `has.isolated` is required before using SharedArrayBuffer.
|
|
46
|
+
|
|
47
|
+
## 2. Message Contract
|
|
48
|
+
|
|
49
|
+
Worker request/response messages use structured-clone-safe plain data.
|
|
50
|
+
|
|
51
|
+
Request sent to a task worker:
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
{
|
|
55
|
+
task,
|
|
56
|
+
payload,
|
|
57
|
+
meta,
|
|
58
|
+
port
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Response sent back through `port`:
|
|
63
|
+
|
|
64
|
+
```javascript
|
|
65
|
+
{ ok: true, value }
|
|
66
|
+
{ ok: false, error }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Worker script:
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
const tasks = {
|
|
73
|
+
async double(payload) {
|
|
74
|
+
return payload * 2;
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
async sum(payload) {
|
|
78
|
+
return payload.reduce((total, value) => total + value, 0);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
self.onmessage = async ({ data }) => {
|
|
83
|
+
const { task, payload, port } = data;
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const run = tasks[task];
|
|
87
|
+
if (!run) throw new Error(`Unknown task: ${task}`);
|
|
88
|
+
|
|
89
|
+
const value = await run(payload);
|
|
90
|
+
port.postMessage({ ok: true, value });
|
|
91
|
+
} catch (err) {
|
|
92
|
+
port.postMessage({ ok: false, error: err.message });
|
|
93
|
+
} finally {
|
|
94
|
+
port.close();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Rules:
|
|
100
|
+
|
|
101
|
+
- Pass plain objects, arrays, Maps, Sets, typed arrays, primitives, Blobs, Files, and other structured-clone-safe values.
|
|
102
|
+
- Do not pass DOM nodes, functions, class instances with methods, symbols, WeakMaps, WeakSets, or WeakRefs.
|
|
103
|
+
- Validate `task` names inside worker scripts.
|
|
104
|
+
- Treat worker messages as untrusted input.
|
|
105
|
+
- Keep worker scripts as module workers.
|
|
106
|
+
|
|
107
|
+
## 3. Transferables
|
|
108
|
+
|
|
109
|
+
Use transferables for large binary data so ownership moves instead of bytes being copied.
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
const buffer = new ArrayBuffer(1024 * 1024);
|
|
113
|
+
|
|
114
|
+
const result = await workers.run('/workers/bytes.js', 'hash', {
|
|
115
|
+
payload: buffer,
|
|
116
|
+
transferables: [buffer]
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
After transfer, the sender loses access to the transferred object.
|
|
121
|
+
|
|
122
|
+
Common transferables:
|
|
123
|
+
|
|
124
|
+
- `ArrayBuffer`
|
|
125
|
+
- `MessagePort`
|
|
126
|
+
- `ReadableStream`
|
|
127
|
+
- `WritableStream`
|
|
128
|
+
- `TransformStream`
|
|
129
|
+
- `ImageBitmap`
|
|
130
|
+
- `OffscreenCanvas`
|
|
131
|
+
- `AudioData`
|
|
132
|
+
- `VideoFrame`
|
|
133
|
+
|
|
134
|
+
## 4. `workers.run`
|
|
135
|
+
|
|
136
|
+
Use `workers.run` for CPU-bound tasks that should execute in a lazily allocated pool.
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
const value = await workers.run('/workers/math.js', 'sum', {
|
|
140
|
+
payload: [1, 2, 3],
|
|
141
|
+
priority: 'user-visible',
|
|
142
|
+
timeout: 3000
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
With cancellation:
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
const ctrl = new AbortController();
|
|
150
|
+
|
|
151
|
+
const task = workers.run('/workers/search.js', 'index', {
|
|
152
|
+
payload: records,
|
|
153
|
+
signal: ctrl.signal,
|
|
154
|
+
timeout: 5000,
|
|
155
|
+
priority: 'background'
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
ctrl.abort();
|
|
159
|
+
|
|
160
|
+
await task;
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Priority values:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
'user-blocking'
|
|
167
|
+
'user-visible'
|
|
168
|
+
'background'
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use priorities like this:
|
|
172
|
+
|
|
173
|
+
- `user-blocking`: input-triggered work needed before the next visible response.
|
|
174
|
+
- `user-visible`: normal work the user is waiting on.
|
|
175
|
+
- `background`: indexing, warming, cleanup, sync, and telemetry work.
|
|
176
|
+
|
|
177
|
+
Pool options are read when the pool for a script is first created:
|
|
178
|
+
|
|
179
|
+
```javascript
|
|
180
|
+
await workers.run('/workers/image.js', 'resize', {
|
|
181
|
+
payload: image,
|
|
182
|
+
size: 2,
|
|
183
|
+
max: 4,
|
|
184
|
+
idle: 15000
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Use `idempotent` only when retrying the task is safe:
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
await workers.run('/workers/sync.js', 'replay', {
|
|
192
|
+
payload: item,
|
|
193
|
+
idempotent: true
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## 5. `workers.dedicated`
|
|
198
|
+
|
|
199
|
+
Use `workers.dedicated` when one caller needs direct control of a single long-lived Dedicated Worker.
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
const worker = workers.dedicated('/workers/parser.js');
|
|
203
|
+
|
|
204
|
+
const parsed = await worker.run('json', {
|
|
205
|
+
payload: text,
|
|
206
|
+
timeout: 2000
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
worker.terminate();
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Direct Dedicated Workers are useful for:
|
|
213
|
+
|
|
214
|
+
- A long-lived parser.
|
|
215
|
+
- A persistent crypto worker.
|
|
216
|
+
- A custom protocol worker.
|
|
217
|
+
- A workflow that should not share a pool with other work.
|
|
218
|
+
|
|
219
|
+
## 6. `Pool`
|
|
220
|
+
|
|
221
|
+
Use `Pool` directly when you need a reusable pool instance with explicit lifetime.
|
|
222
|
+
|
|
223
|
+
```javascript
|
|
224
|
+
import { Pool } from '@adukiorg/anza/workers';
|
|
225
|
+
|
|
226
|
+
const pool = new Pool('/workers/image.js', {
|
|
227
|
+
max: 3,
|
|
228
|
+
idle: 30000
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const thumbnail = await pool.run('thumbnail', {
|
|
232
|
+
payload: file,
|
|
233
|
+
priority: 'user-visible'
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
pool.terminate();
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
Use the facade for most app code. Use `Pool` directly when a module owns its own worker lifetime.
|
|
240
|
+
|
|
241
|
+
## 7. `Shared`
|
|
242
|
+
|
|
243
|
+
Use `workers.shared` when multiple same-origin tabs need one shared background resource.
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
const socket = workers.shared('/workers/socket.js', 'socket');
|
|
247
|
+
|
|
248
|
+
const stop = socket.subscribe((message) => {
|
|
249
|
+
console.log(message);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
socket.send({
|
|
253
|
+
type: 'subscribe',
|
|
254
|
+
topic: 'prices'
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
stop();
|
|
258
|
+
socket.close();
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Shared workers are best for:
|
|
262
|
+
|
|
263
|
+
- One WebSocket shared across tabs.
|
|
264
|
+
- One auth refresh coordinator.
|
|
265
|
+
- One same-origin rate limiter.
|
|
266
|
+
- One shared state coordinator.
|
|
267
|
+
|
|
268
|
+
When native SharedWorker is unavailable, the implementation falls back to a Dedicated Worker with the same `send` and `subscribe` shape.
|
|
269
|
+
|
|
270
|
+
Compatibility aliases:
|
|
271
|
+
|
|
272
|
+
```javascript
|
|
273
|
+
socket.postMessage(message);
|
|
274
|
+
socket.onMessage(handler);
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Prefer:
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
socket.send(message);
|
|
281
|
+
socket.subscribe(handler);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## 8. Shared Worker Script
|
|
285
|
+
|
|
286
|
+
A SharedWorker script receives ports through `onconnect`.
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
const ports = new Set();
|
|
290
|
+
|
|
291
|
+
self.onconnect = (event) => {
|
|
292
|
+
const port = event.ports[0];
|
|
293
|
+
ports.add(port);
|
|
294
|
+
|
|
295
|
+
port.onmessage = ({ data }) => {
|
|
296
|
+
if (data.type === 'subscribe') {
|
|
297
|
+
port.postMessage({ type: 'subscribed', topic: data.topic });
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
port.start();
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
function publish(message) {
|
|
305
|
+
for (const port of ports) {
|
|
306
|
+
port.postMessage(message);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
For the Dedicated Worker fallback, support normal `self.onmessage` too if the script needs to run in both modes.
|
|
312
|
+
|
|
313
|
+
```javascript
|
|
314
|
+
self.onmessage = ({ data }) => {
|
|
315
|
+
self.postMessage({ type: 'received', value: data });
|
|
316
|
+
};
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## 9. `workers.broadcast`
|
|
320
|
+
|
|
321
|
+
Use BroadcastChannel for stateless cross-tab events.
|
|
322
|
+
|
|
323
|
+
```javascript
|
|
324
|
+
const stop = workers.subscribe('sync:done', (payload) => {
|
|
325
|
+
console.log('synced at', payload.at);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
workers.broadcast('sync:done', {
|
|
329
|
+
at: Date.now()
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
stop();
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
With `AbortSignal` cleanup:
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
const ctrl = new AbortController();
|
|
339
|
+
|
|
340
|
+
workers.subscribe('profile:changed', refresh, ctrl.signal);
|
|
341
|
+
|
|
342
|
+
ctrl.abort();
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Use BroadcastChannel for:
|
|
346
|
+
|
|
347
|
+
- Cache invalidation.
|
|
348
|
+
- Sync completion.
|
|
349
|
+
- State refresh notices.
|
|
350
|
+
- Lightweight tab-to-tab notifications.
|
|
351
|
+
|
|
352
|
+
Use SharedWorker instead when state must persist or when only one connection should exist.
|
|
353
|
+
|
|
354
|
+
## 10. Broadcast Manager
|
|
355
|
+
|
|
356
|
+
The named `broadcast` export exposes direct channel controls.
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
import { broadcast } from '@adukiorg/anza/workers';
|
|
360
|
+
|
|
361
|
+
const stop = broadcast.subscribe('cache:avatar', clearAvatar);
|
|
362
|
+
|
|
363
|
+
broadcast.broadcast('cache:avatar', { user: '123' });
|
|
364
|
+
|
|
365
|
+
stop();
|
|
366
|
+
broadcast.close('cache:avatar');
|
|
367
|
+
broadcast.clear();
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
`broadcast.clear()` closes every managed channel.
|
|
371
|
+
|
|
372
|
+
## 11. `workers.lock`
|
|
373
|
+
|
|
374
|
+
Use Web Locks for cross-tab mutual exclusion.
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
await workers.lock('idb:records', async () => {
|
|
378
|
+
await saveRecord(record);
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
With options:
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
await workers.lock('auth:refresh', refreshToken, {
|
|
386
|
+
mode: 'exclusive',
|
|
387
|
+
timeout: 5000,
|
|
388
|
+
signal: ctrl.signal,
|
|
389
|
+
ifAvailable: false,
|
|
390
|
+
steal: false
|
|
391
|
+
});
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
Shared mode:
|
|
395
|
+
|
|
396
|
+
```javascript
|
|
397
|
+
await workers.lock('opfs:report', readReport, {
|
|
398
|
+
mode: 'shared'
|
|
399
|
+
});
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Try-lock behavior:
|
|
403
|
+
|
|
404
|
+
```javascript
|
|
405
|
+
try {
|
|
406
|
+
await workers.lock('sync:leader', lead, {
|
|
407
|
+
ifAvailable: true
|
|
408
|
+
});
|
|
409
|
+
} catch (err) {
|
|
410
|
+
// Another tab is already leading.
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
Lock naming:
|
|
415
|
+
|
|
416
|
+
| Pattern | Use |
|
|
417
|
+
|---|---|
|
|
418
|
+
| `idb:<store>` | IndexedDB store access |
|
|
419
|
+
| `opfs:<file>` | OPFS file access |
|
|
420
|
+
| `auth:<op>` | Auth operations |
|
|
421
|
+
| `sync:<role>` | Sync leader or sync phase |
|
|
422
|
+
| `cache:<name>` | Cache invalidation |
|
|
423
|
+
|
|
424
|
+
Rules:
|
|
425
|
+
|
|
426
|
+
- Use exclusive locks for writes.
|
|
427
|
+
- Use shared locks for reads when supported.
|
|
428
|
+
- Keep lock callbacks short.
|
|
429
|
+
- Do not wait on user input inside a lock.
|
|
430
|
+
- Use `timeout` for locks that should never wait forever.
|
|
431
|
+
|
|
432
|
+
## 12. `workers.offscreen`
|
|
433
|
+
|
|
434
|
+
Use OffscreenCanvas for render loops that should not block UI work.
|
|
435
|
+
|
|
436
|
+
```javascript
|
|
437
|
+
const handle = await workers.offscreen(canvas, '/workers/render.js', {
|
|
438
|
+
onError(err) {
|
|
439
|
+
console.error('render failed', err);
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
onResize(size) {
|
|
443
|
+
console.log(size.width, size.height, size.dpr);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
handle.send({
|
|
448
|
+
type: 'frame',
|
|
449
|
+
scene
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
handle.resize();
|
|
453
|
+
handle.close();
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
The returned promise resolves after the worker acknowledges readiness.
|
|
457
|
+
|
|
458
|
+
## 13. Offscreen Worker Script
|
|
459
|
+
|
|
460
|
+
The render worker receives the canvas and a control port.
|
|
461
|
+
|
|
462
|
+
```javascript
|
|
463
|
+
let ctx;
|
|
464
|
+
let port;
|
|
465
|
+
|
|
466
|
+
self.onmessage = ({ data }) => {
|
|
467
|
+
if (data.canvas && data.port) {
|
|
468
|
+
ctx = data.canvas.getContext('2d');
|
|
469
|
+
port = data.port;
|
|
470
|
+
port.onmessage = oncommand;
|
|
471
|
+
port.postMessage({ ok: true });
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
function oncommand({ data }) {
|
|
476
|
+
if (data.type === 'resize') {
|
|
477
|
+
resize(data);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (data.type === 'frame') {
|
|
482
|
+
draw(data.scene);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function resize({ width, height, dpr }) {
|
|
487
|
+
ctx.canvas.width = Math.round(width * dpr);
|
|
488
|
+
ctx.canvas.height = Math.round(height * dpr);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function draw(scene) {
|
|
492
|
+
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
493
|
+
// draw scene
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
Keep the control port open if the main thread will call `handle.send()` or `handle.resize()`.
|
|
498
|
+
|
|
499
|
+
## 14. Lifecycle
|
|
500
|
+
|
|
501
|
+
Close what you open.
|
|
502
|
+
|
|
503
|
+
```javascript
|
|
504
|
+
const worker = workers.dedicated('/workers/job.js');
|
|
505
|
+
worker.terminate();
|
|
506
|
+
|
|
507
|
+
const shared = workers.shared('/workers/socket.js');
|
|
508
|
+
shared.close();
|
|
509
|
+
|
|
510
|
+
const stop = workers.subscribe('event', handler);
|
|
511
|
+
stop();
|
|
512
|
+
|
|
513
|
+
const canvas = await workers.offscreen(el, '/workers/render.js');
|
|
514
|
+
canvas.close();
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Pool lifecycle:
|
|
518
|
+
|
|
519
|
+
```javascript
|
|
520
|
+
workers.close('/workers/image.js');
|
|
521
|
+
workers.clear();
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
`workers.close(script)` terminates one pool.
|
|
525
|
+
|
|
526
|
+
`workers.clear()` terminates all pools and clears managed broadcast channels.
|
|
527
|
+
|
|
528
|
+
## 15. Error Handling
|
|
529
|
+
|
|
530
|
+
Handle task errors like normal promise errors.
|
|
531
|
+
|
|
532
|
+
```javascript
|
|
533
|
+
try {
|
|
534
|
+
await workers.run('/workers/job.js', 'import', {
|
|
535
|
+
payload: file,
|
|
536
|
+
timeout: 10000
|
|
537
|
+
});
|
|
538
|
+
} catch (err) {
|
|
539
|
+
console.error(err);
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Worker-side task failures should return an error response.
|
|
544
|
+
|
|
545
|
+
```javascript
|
|
546
|
+
port.postMessage({
|
|
547
|
+
ok: false,
|
|
548
|
+
error: err.message
|
|
549
|
+
});
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
Unhandled worker errors reject pending Dedicated Worker requests.
|
|
553
|
+
|
|
554
|
+
## 16. Testing Workers
|
|
555
|
+
|
|
556
|
+
Mock `Worker` when unit testing callers.
|
|
557
|
+
|
|
558
|
+
```javascript
|
|
559
|
+
const original = globalThis.Worker;
|
|
560
|
+
|
|
561
|
+
globalThis.Worker = class MockWorker {
|
|
562
|
+
addEventListener() {}
|
|
563
|
+
|
|
564
|
+
postMessage({ task, payload, port }) {
|
|
565
|
+
port.postMessage({
|
|
566
|
+
ok: true,
|
|
567
|
+
value: `${task}:${payload}`
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
terminate() {}
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
try {
|
|
575
|
+
const value = await workers.run('/mock.js', 'ping', {
|
|
576
|
+
payload: 'pong'
|
|
577
|
+
});
|
|
578
|
+
} finally {
|
|
579
|
+
globalThis.Worker = original;
|
|
580
|
+
}
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Test these paths:
|
|
584
|
+
|
|
585
|
+
- Success response.
|
|
586
|
+
- `{ ok: false }` response.
|
|
587
|
+
- Abort before start.
|
|
588
|
+
- Abort while queued.
|
|
589
|
+
- Timeout.
|
|
590
|
+
- Worker error.
|
|
591
|
+
- `messageerror`.
|
|
592
|
+
- Transferables.
|
|
593
|
+
- Cleanup after `close`, `terminate`, and `clear`.
|
|
594
|
+
|
|
595
|
+
## 17. Choosing a Primitive
|
|
596
|
+
|
|
597
|
+
| Need | Use |
|
|
598
|
+
|---|---|
|
|
599
|
+
| CPU task with scheduling | `workers.run` |
|
|
600
|
+
| Single tab-owned worker | `workers.dedicated` |
|
|
601
|
+
| Owned pool instance | `new Pool()` |
|
|
602
|
+
| Shared socket | `workers.shared` |
|
|
603
|
+
| Cross-tab event | `workers.broadcast` |
|
|
604
|
+
| Cross-tab subscription | `workers.subscribe` |
|
|
605
|
+
| Cross-tab mutex | `workers.lock` |
|
|
606
|
+
| Canvas render loop | `workers.offscreen` |
|
|
607
|
+
|
|
608
|
+
## 18. Checklist
|
|
609
|
+
|
|
610
|
+
- Use `workers.run` for normal CPU work.
|
|
611
|
+
- Use `workers.dedicated` only when direct worker ownership matters.
|
|
612
|
+
- Use `workers.shared` for persistent same-origin coordination.
|
|
613
|
+
- Use `workers.broadcast` for stateless events.
|
|
614
|
+
- Use `workers.lock` for shared resources and leader election.
|
|
615
|
+
- Use `workers.offscreen` for expensive canvas rendering.
|
|
616
|
+
- Pass `AbortSignal` to cancellable tasks.
|
|
617
|
+
- Use `timeout` when a task or lock should not wait forever.
|
|
618
|
+
- Pass transferables for large binary values.
|
|
619
|
+
- Do not pass DOM nodes, functions, or class instances to workers.
|
|
620
|
+
- Validate task names inside worker scripts.
|
|
621
|
+
- Close direct workers, shared connections, broadcasts, and offscreen handles.
|
|
622
|
+
- Call `workers.clear()` during app-level teardown.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/elements/base.js
|
|
3
|
+
*
|
|
4
|
+
* Elements Base boundary.
|
|
5
|
+
* Re-exports the unified lifecycle BaseElement class as `Base` to maintain
|
|
6
|
+
* clean architectural limits for custom components authoring.
|
|
7
|
+
*
|
|
8
|
+
* Source: doc 04 — Web Components §1
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { BaseElement } from '../core/ui/base.js';
|
|
12
|
+
export { BaseElement as Base };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* src/elements/data/card/index.js
|
|
3
|
+
*
|
|
4
|
+
* Data Element: <ui-card>
|
|
5
|
+
* Premium card containment container including header, body, footer slots,
|
|
6
|
+
* and subtle micro-animation interactive hover lifts.
|
|
7
|
+
*
|
|
8
|
+
* Source: doc 04 — Web Components §3, doc 05 — Native UI Primitives §3
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { ui } from '../../../core/ui/index.js';
|
|
12
|
+
|
|
13
|
+
ui.element('ui-card', {
|
|
14
|
+
style: './style.css',
|
|
15
|
+
template: './index.html',
|
|
16
|
+
props: {
|
|
17
|
+
interactive: { type: Boolean, reflect: true }
|
|
18
|
+
}
|
|
19
|
+
}, import.meta.url);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":1,"refs":[],"ids":[],"classes":["body","footer","header"],"tags":["div","slot"],"compound":["div.body","div.footer","div.header"],"attrs":["class","name","part"],"refTypes":{}}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
background: var(--color-surface-elevated);
|
|
5
|
+
border: var(--space-px) solid var(--color-border-default);
|
|
6
|
+
border-radius: var(--radius-lg);
|
|
7
|
+
box-shadow: var(--shadow-sm);
|
|
8
|
+
overflow: hidden;
|
|
9
|
+
font-family: var(--font-family-sans);
|
|
10
|
+
transition:
|
|
11
|
+
transform var(--duration-fast) var(--ease-out),
|
|
12
|
+
box-shadow var(--duration-fast) var(--ease-out),
|
|
13
|
+
border-color var(--duration-fast) var(--ease-out);
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
width: 100%;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.header {
|
|
19
|
+
padding: var(--space-4) var(--space-6);
|
|
20
|
+
border-bottom: var(--space-px) solid var(--color-border-default);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.body {
|
|
24
|
+
padding: var(--space-6);
|
|
25
|
+
flex: 1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.footer {
|
|
29
|
+
padding: var(--space-4) var(--space-6);
|
|
30
|
+
background: var(--color-surface-page);
|
|
31
|
+
border-top: var(--space-px) solid var(--color-border-default);
|
|
32
|
+
display: flex;
|
|
33
|
+
gap: var(--space-3);
|
|
34
|
+
justify-content: flex-end;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Hover elevation highlights for interactive cards */
|
|
38
|
+
:host([interactive]) {
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
:host([interactive]:hover) {
|
|
43
|
+
transform: translateY(calc(-1 * var(--space-1)));
|
|
44
|
+
box-shadow: var(--shadow-md);
|
|
45
|
+
border-color: var(--color-border-strong);
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<canvas></canvas>
|