@wcstack/tilt 1.16.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/README.ja.md ADDED
@@ -0,0 +1,108 @@
1
+ # @wcstack/tilt
2
+
3
+ `@wcstack/tilt` は wcstack エコシステム向けのヘッドレスな Device Orientation コンポーネントです。
4
+
5
+ 視覚的な UI ウィジェットではありません。デバイスの傾き(`deviceorientation`)をリアクティブな state に変える**非同期プリミティブノード**です。
6
+
7
+ `@wcstack/state` と組み合わせると、`<wcs-tilt>` はパス契約で直接バインドできます:
8
+
9
+ - **入力サーフェス**: 無し
10
+ - **出力 state サーフェス**: `alpha`、`beta`、`gamma`、`absolute`、`permissionState`、`error`
11
+
12
+ ## なぜ存在するか — `@wcstack/idle`の兄弟、iOSのgesture-gateを吸収する
13
+
14
+ iOS 13+ Safariは`deviceorientation`が発火する前に、明示的でgesture-gatedな`DeviceOrientationEvent.requestPermission()`を要求します。他の全プラットフォーム(Android Chrome、デスクトップ)にはこのgateがありません。`<wcs-tilt>`はこの差異を吸収します: gatingの無いプラットフォームでは`requestPermission()`は即座に`"granted"`でresolveするため、呼び出し元は**どこでも動く単一の**`requestPermission()` → `start()`フローを書けます。
15
+
16
+ > **`permissionState`は3値の語彙**(`"granted"` / `"denied"` / `"unknown"`)——このAPIに対応する`navigator.permissions.query()`エントリが存在しないため、意図的に4値のPermissions API状態とは別物にしています。`@wcstack/idle`(`@wcstack/permission`と合成)とは異なり、ローカルで追跡します。
17
+
18
+ > **connect時に自動startしません** — `@wcstack/idle`と同じ理由: 許可前の購読はiOSで無音のまま何も受け取れません。
19
+
20
+ > **secure context(HTTPSまたはlocalhost)が必須です。**
21
+
22
+ ## インストール
23
+
24
+ ```bash
25
+ npm install @wcstack/tilt
26
+ ```
27
+
28
+ ## クイックスタート
29
+
30
+ ```html
31
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
32
+ <script type="module" src="https://esm.run/@wcstack/tilt/auto"></script>
33
+
34
+ <wcs-state>
35
+ <script type="module">
36
+ export default {
37
+ tiltBeta: null,
38
+ tiltGamma: null,
39
+ async enableTilt() {
40
+ const el = document.querySelector("wcs-tilt");
41
+ const result = await el.requestPermission();
42
+ if (result === "granted") el.start();
43
+ },
44
+ get tiltTransform() {
45
+ return `rotate(${this.tiltBeta ?? 0}deg)`;
46
+ },
47
+ };
48
+ </script>
49
+ </wcs-state>
50
+
51
+ <wcs-tilt data-wcs="beta: tiltBeta; gamma: tiltGamma"></wcs-tilt>
52
+ <button data-wcs="onclick: enableTilt">傾き検知を有効にする</button>
53
+ <div data-wcs="style.transform: tiltTransform"></div>
54
+ ```
55
+
56
+ `data-wcs` が参照する全てのパス(`tiltBeta`、`tiltGamma`、`tiltTransform`、`enableTilt`)は state オブジェクトに宣言が必須です——未宣言の top-level パスはバインド初期化時に例外を投げます。`tiltTransform` は素の path getter(計算プロパティ、`@wcstack/state` の README 参照)です——`@wcstack/state` には文字列テンプレート化フィルタが無いため、CSS 文字列は getter 側で組み立てます。`tiltBeta` が変わるたびに再計算されます。
57
+
58
+ ## 観測可能プロパティ(出力)
59
+
60
+ | プロパティ | イベント | 説明 |
61
+ | ----------------- | ----------------------------- | ---- |
62
+ | `alpha` | `wcs-tilt:change` | Z軸回転、`start()`前は`null`。 |
63
+ | `beta` | `wcs-tilt:change` | X軸回転。 |
64
+ | `gamma` | `wcs-tilt:change` | Y軸回転。 |
65
+ | `absolute` | `wcs-tilt:change` | 地磁気に対する絶対方位かどうか。ブラウザにより信頼性が異なるため実機で確認してください。 |
66
+ | `permissionState` | `wcs-tilt:permission-changed` | `"granted"` \| `"denied"` \| `"unknown"`。 |
67
+ | `error` | `wcs-tilt:error` | 直近の`requestPermission()`の失敗(gesture文脈外呼び出しのrejectなど)、無ければ`null`。never-throw: 失敗はreject/throwせず、ここに流れます。 |
68
+
69
+ ## コマンド
70
+
71
+ | コマンド | 非同期 | 説明 |
72
+ | -------------------- | ------ | ---- |
73
+ | `requestPermission` | はい | iOSではgesture-gatedな静的メソッドを呼ぶ(**実際のuser gestureハンドラ内から呼ぶ必要があります**)。それ以外の全プラットフォームでは即座に`"granted"`でresolve。 |
74
+ | `start` | いいえ | `deviceorientation`を購読。冪等。 |
75
+ | `stop` | いいえ | 購読解除。未開始でも安全に呼べます。 |
76
+
77
+ ## 属性 / 入力
78
+
79
+ **無し。**
80
+
81
+ ## 注意・制限
82
+
83
+ - **connect時に自動startしません。**
84
+ - **`@wcstack/permission`とは合成しません**(対応するPermissions APIエントリが存在しないため)——`permissionState`はローカルで追跡します。
85
+ - `_gen`世代ガードは無し: 購読は完全に同期的です。
86
+ - **非secure context(素のHTTP)では`deviceorientation`は一切発火しません**——ブラウザがネイティブに抑止し、`<wcs-tilt>`自身はガードを持たない(nativeの非発火を信頼する設計)ため、傾き値は`null`のまま・`wcs-tilt:change`は飛ばず・`permissionState`も(`requestPermission()`を呼ばない限り)`"unknown"`のまま変化しません。`requestPermission()`でも検出できません: gatingの無いプラットフォームではフォールバックが何も問い合わせずに`"granted"`を返します。何も起きないときは、まず配信元(HTTPS/localhost)を確認してください。
87
+ - **Permissions-Policyでゲートされます。** `deviceorientation`イベントは`accelerometer`と`gyroscope`のPermissions-Policyディレクティブが許可されている場合にのみ発火します(既定allowlist: `self`、Device Orientation Events仕様§4準拠)。クロスオリジンの`<iframe>`内で`<wcs-tilt>`を使うには、その`<iframe>`要素に`allow="accelerometer; gyroscope"`が必要です——無いとイベントは無音のまま一切発火しません。上記の非secure originのケースと同じ失敗モードです。
88
+ - **高頻度なイベントストリーム。** `deviceorientation`は多くのデバイスで毎秒数十回発火します。`@wcstack/state`にビルトインのdebounce/throttleフィルタは無いため、より粗い頻度に間引きたい場合は`@wcstack/debounce`のvalue surfaceで中継してください: `<wcs-tilt data-wcs="beta: tiltBeta">`のあと`<wcs-throttle wait="100" data-wcs="source: tiltBeta; value: throttledBeta"></wcs-throttle>`(詳細は`@wcstack/debounce`のREADME参照)。
89
+
90
+ ## ヘッドレス利用(`TiltCore`)
91
+
92
+ ```typescript
93
+ import { TiltCore } from "@wcstack/tilt";
94
+
95
+ const core = new TiltCore();
96
+ core.addEventListener("wcs-tilt:change", (e) => {
97
+ console.log((e as CustomEvent).detail); // { alpha, beta, gamma, absolute }
98
+ });
99
+
100
+ await core.requestPermission();
101
+ core.start();
102
+ // 後始末:
103
+ core.dispose();
104
+ ```
105
+
106
+ ## ライセンス
107
+
108
+ MIT
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # @wcstack/tilt
2
+
3
+ `@wcstack/tilt` is a headless Device Orientation component for the wcstack ecosystem.
4
+
5
+ It is not a visual UI widget.
6
+ It is an **async primitive node** that turns device tilt (`deviceorientation`) into reactive state.
7
+
8
+ With `@wcstack/state`, `<wcs-tilt>` can be bound directly through path contracts:
9
+
10
+ - **input surface**: none
11
+ - **output state surface**: `alpha`, `beta`, `gamma`, `absolute`, `permissionState`, `error`
12
+
13
+ ## Why this exists — the iOS gesture-gate absorption sibling to `@wcstack/idle`
14
+
15
+ iOS 13+ Safari requires an explicit, gesture-gated `DeviceOrientationEvent.requestPermission()` before `deviceorientation` fires at all. Every other platform (Android Chrome, desktop) has no such gate. `<wcs-tilt>` absorbs this difference: `requestPermission()` resolves immediately to `"granted"` on ungated platforms, so callers can write **one** `requestPermission()` → `start()` flow that works everywhere.
16
+
17
+ > **`permissionState` is a 3-value vocabulary** (`"granted"` / `"denied"` / `"unknown"`) — deliberately distinct from the 4-value Permissions API state, since there is no `navigator.permissions.query()` entry for this feature. It is tracked locally, unlike `@wcstack/idle` (which composes with `@wcstack/permission`).
18
+
19
+ > **Does not auto-start on connect** — same reasoning as `@wcstack/idle`: attempting to subscribe before permission is granted silently receives nothing on iOS.
20
+
21
+ > **Requires a secure context** (HTTPS or localhost).
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ npm install @wcstack/tilt
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```html
32
+ <script type="module" src="https://esm.run/@wcstack/state/auto"></script>
33
+ <script type="module" src="https://esm.run/@wcstack/tilt/auto"></script>
34
+
35
+ <wcs-state>
36
+ <script type="module">
37
+ export default {
38
+ tiltBeta: null,
39
+ tiltGamma: null,
40
+ async enableTilt() {
41
+ const el = document.querySelector("wcs-tilt");
42
+ const result = await el.requestPermission();
43
+ if (result === "granted") el.start();
44
+ },
45
+ get tiltTransform() {
46
+ return `rotate(${this.tiltBeta ?? 0}deg)`;
47
+ },
48
+ };
49
+ </script>
50
+ </wcs-state>
51
+
52
+ <wcs-tilt data-wcs="beta: tiltBeta; gamma: tiltGamma"></wcs-tilt>
53
+ <button data-wcs="onclick: enableTilt">Enable tilt</button>
54
+ <div data-wcs="style.transform: tiltTransform"></div>
55
+ ```
56
+
57
+ Every path referenced by `data-wcs` (`tiltBeta`, `tiltGamma`, `tiltTransform`, `enableTilt`) must be declared on the state object — an undeclared top-level path throws during bind initialization. `tiltTransform` is a plain path getter (computed property, see `@wcstack/state`'s README) — `@wcstack/state` has no string-templating filter, so build the CSS string in a getter instead. It recomputes whenever `tiltBeta` changes.
58
+
59
+ ## Observable Properties (outputs)
60
+
61
+ | Property | Event | Description |
62
+ | ----------------- | --------------------------- | ------------ |
63
+ | `alpha` | `wcs-tilt:change` | Z-axis rotation, or `null` before `start()`. |
64
+ | `beta` | `wcs-tilt:change` | X-axis rotation. |
65
+ | `gamma` | `wcs-tilt:change` | Y-axis rotation. |
66
+ | `absolute` | `wcs-tilt:change` | Whether the reading is relative to the Earth's frame. Reliability varies by browser — verify on your target devices. |
67
+ | `permissionState` | `wcs-tilt:permission-changed` | `"granted"` \| `"denied"` \| `"unknown"`. |
68
+ | `error` | `wcs-tilt:error` | The last `requestPermission()` failure (e.g. a gesture-context rejection), or `null`. never-throw: failures never reject/throw, they land here instead. |
69
+
70
+ ## Commands
71
+
72
+ | Command | Async | Description |
73
+ | ------------------- | ----- | ------------ |
74
+ | `requestPermission` | yes | On iOS, calls the gesture-gated static method (**must be invoked from within a real user gesture handler**). On every other platform, resolves to `"granted"` immediately. |
75
+ | `start` | no | Subscribe to `deviceorientation`. Idempotent. |
76
+ | `stop` | no | Unsubscribe. Safe to call when not started. |
77
+
78
+ ## Attributes / Inputs
79
+
80
+ **None.**
81
+
82
+ ## Notes & limitations
83
+
84
+ - **Does not auto-start on connect.**
85
+ - **Does not compose with `@wcstack/permission`** (no matching Permissions API entry exists) — `permissionState` is tracked locally.
86
+ - No `_gen` generation guard: subscribing is fully synchronous.
87
+ - **On a non-secure origin (plain HTTP), `deviceorientation` never fires** — the browser suppresses it natively and `<wcs-tilt>` adds no guard of its own, so tilt values stay `null`, `wcs-tilt:change` never dispatches, and `permissionState` stays `"unknown"` (until you call `requestPermission()`). Calling `requestPermission()` cannot detect this either: on gate-less platforms its fallback resolves `"granted"` without probing anything. If nothing seems to happen, check your origin first.
88
+ - **Permissions-Policy gate.** The `deviceorientation` event is only dispatched when the `accelerometer` and `gyroscope` Permissions-Policy directives are allowed (default allowlist: `self`, per the Device Orientation Events spec §4). Using `<wcs-tilt>` inside a cross-origin `<iframe>` requires `allow="accelerometer; gyroscope"` on that `<iframe>` element — otherwise the event silently never fires, the same failure mode as the non-secure-origin case above.
89
+ - **High-frequency stream.** `deviceorientation` can fire tens of times per second on many devices. `@wcstack/state` has no built-in debounce/throttle filter — if you need a coarser rate, relay through `@wcstack/debounce`'s value surface: `<wcs-tilt data-wcs="beta: tiltBeta">` then `<wcs-throttle wait="100" data-wcs="source: tiltBeta; value: throttledBeta"></wcs-throttle>` (see `@wcstack/debounce`'s README).
90
+
91
+ ## Headless usage (`TiltCore`)
92
+
93
+ ```typescript
94
+ import { TiltCore } from "@wcstack/tilt";
95
+
96
+ const core = new TiltCore();
97
+ core.addEventListener("wcs-tilt:change", (e) => {
98
+ console.log((e as CustomEvent).detail); // { alpha, beta, gamma, absolute }
99
+ });
100
+
101
+ await core.requestPermission();
102
+ core.start();
103
+ // later:
104
+ core.dispose();
105
+ ```
106
+
107
+ ## License
108
+
109
+ MIT
package/dist/auto.js ADDED
@@ -0,0 +1,3 @@
1
+ import { bootstrapTilt } from "./index.esm.js";
2
+
3
+ bootstrapTilt();
@@ -0,0 +1 @@
1
+ import{bootstrapTilt}from"./index.esm.min.js";bootstrapTilt();
@@ -0,0 +1,160 @@
1
+ interface IWcBindableProperty {
2
+ readonly name: string;
3
+ readonly event: string;
4
+ readonly getter?: (event: Event) => any;
5
+ }
6
+ interface IWcBindableInput {
7
+ readonly name: string;
8
+ readonly attribute?: string;
9
+ }
10
+ interface IWcBindableCommand {
11
+ readonly name: string;
12
+ readonly async?: boolean;
13
+ }
14
+ interface IWcBindable {
15
+ readonly protocol: "wc-bindable";
16
+ readonly version: 1;
17
+ readonly properties: readonly IWcBindableProperty[];
18
+ readonly inputs?: readonly IWcBindableInput[];
19
+ readonly commands?: readonly IWcBindableCommand[];
20
+ }
21
+
22
+ interface ITagNames {
23
+ readonly tilt: string;
24
+ }
25
+ interface IWritableTagNames {
26
+ tilt?: string;
27
+ }
28
+ interface IConfig {
29
+ readonly tagNames: ITagNames;
30
+ }
31
+ interface IWritableConfig {
32
+ tagNames?: IWritableTagNames;
33
+ }
34
+
35
+ /**
36
+ * 3-value vocabulary, deliberately distinct from the 4-value permission state
37
+ * (prompt/granted/denied/unsupported) used elsewhere: `"unknown"` means
38
+ * "no gating exists on this platform, so there is nothing to have asked"
39
+ * (docs/device-orientation-tag-design.md §2) — not the same concept as
40
+ * Permissions API's `"prompt"`.
41
+ */
42
+ type TiltPermissionState = "granted" | "denied" | "unknown";
43
+ /**
44
+ * Value types for TiltCore (headless) — the observable state properties.
45
+ */
46
+ interface WcsTiltCoreValues {
47
+ alpha: number | null;
48
+ beta: number | null;
49
+ gamma: number | null;
50
+ absolute: boolean | null;
51
+ permissionState: TiltPermissionState;
52
+ error: any;
53
+ }
54
+ /**
55
+ * Value types for the Shell (`<wcs-tilt>`) — identical observable surface to
56
+ * the Core.
57
+ */
58
+ type WcsTiltValues = WcsTiltCoreValues;
59
+
60
+ declare function bootstrapTilt(userConfig?: IWritableConfig): void;
61
+
62
+ declare function getConfig(): IConfig;
63
+
64
+ /**
65
+ * Headless Device Orientation primitive. A thin, framework-agnostic wrapper
66
+ * around `window`'s `deviceorientation` event exposed through the wc-bindable
67
+ * protocol.
68
+ *
69
+ * The batch2 sibling of `@wcstack/idle` (docs/device-orientation-tag-design.md).
70
+ * Unlike Idle Detection, there is no matching Permissions API entry for this
71
+ * feature (`navigator.permissions.query` has no "device-orientation" name), so
72
+ * `permissionState` must be tracked **locally** rather than composed with
73
+ * `<wcs-permission>` — the defining asymmetry within batch2.
74
+ *
75
+ * No `_gen` generation guard: subscribing/unsubscribing to `deviceorientation`
76
+ * (start/stop) is fully synchronous, so that path never needs one. This node
77
+ * does have an async probe — `requestPermission()` awaits the static
78
+ * `DeviceOrientationEvent.requestPermission()` — but its post-await write is
79
+ * a benign `permissionState`/`error` value set + dispatch with no
80
+ * subscription/resource management to race, so `_gen` is unneeded there too
81
+ * (docs/device-orientation-tag-design.md §4, corrected after an earlier
82
+ * revision mistakenly reused `@wcstack/network`'s "no async probe at all"
83
+ * rationale).
84
+ */
85
+ declare class TiltCore extends EventTarget {
86
+ static wcBindable: IWcBindable;
87
+ private _target;
88
+ private _snapshot;
89
+ private _permissionState;
90
+ private _error;
91
+ private _subscribed;
92
+ private _ready;
93
+ constructor(target?: EventTarget);
94
+ get ready(): Promise<void>;
95
+ get alpha(): number | null;
96
+ get beta(): number | null;
97
+ get gamma(): number | null;
98
+ get absolute(): boolean | null;
99
+ get permissionState(): TiltPermissionState;
100
+ get error(): any;
101
+ observe(): Promise<void>;
102
+ dispose(): void;
103
+ private _deviceOrientationEventCtor;
104
+ private _setPermissionState;
105
+ private _setError;
106
+ /**
107
+ * Wraps iOS 13+ Safari's static, gesture-gated
108
+ * `DeviceOrientationEvent.requestPermission()`. On platforms without this
109
+ * gate (Android Chrome, desktop) there is nothing to ask, so this resolves
110
+ * to `"granted"` immediately without querying anything — callers can write
111
+ * one `requestPermission()` → `start()` flow that works everywhere
112
+ * (docs/device-orientation-tag-design.md §3).
113
+ *
114
+ * never-throw (§3.6): a gesture-context rejection resolves to `"denied"`
115
+ * and the raw error lands in `error` instead of propagating. Mirrors
116
+ * `<wcs-idle>`'s `requestPermission()` (docs/idle-detection-tag-design.md
117
+ * §4.1) — any settled (non-throwing) outcome supersedes a stale `error`
118
+ * from an earlier attempt.
119
+ */
120
+ requestPermission(): Promise<TiltPermissionState>;
121
+ /** Subscribe to `deviceorientation`. Idempotent — a second start() while already subscribed is a no-op. */
122
+ start(): void;
123
+ /** Unsubscribe from `deviceorientation`. Safe to call when not started. */
124
+ stop(): void;
125
+ private _onOrientation;
126
+ private _apply;
127
+ }
128
+
129
+ /**
130
+ * `<wcs-tilt>` — declarative Device Orientation API monitor.
131
+ *
132
+ * Named `tilt` (not `orientation`/`device-orientation`) to avoid colliding
133
+ * with `<wcs-screen-orientation>` (docs/device-orientation-tag-design.md §9).
134
+ *
135
+ * Does NOT auto-start on connect, mirroring `<wcs-idle>`: on iOS, subscribing
136
+ * before permission is granted silently receives no events. Callers drive
137
+ * `requestPermission()` → `start()` explicitly.
138
+ */
139
+ declare class WcsTilt extends HTMLElement {
140
+ static hasConnectedCallbackPromise: boolean;
141
+ static wcBindable: IWcBindable;
142
+ private _core;
143
+ private _connectedCallbackPromise;
144
+ constructor();
145
+ get alpha(): number | null;
146
+ get beta(): number | null;
147
+ get gamma(): number | null;
148
+ get absolute(): boolean | null;
149
+ get permissionState(): TiltPermissionState;
150
+ get error(): any;
151
+ get connectedCallbackPromise(): Promise<void>;
152
+ requestPermission(): Promise<TiltPermissionState>;
153
+ start(): void;
154
+ stop(): void;
155
+ connectedCallback(): void;
156
+ disconnectedCallback(): void;
157
+ }
158
+
159
+ export { TiltCore, WcsTilt, bootstrapTilt, getConfig };
160
+ export type { IWritableConfig, IWritableTagNames, TiltPermissionState, WcsTiltCoreValues, WcsTiltValues };
@@ -0,0 +1,305 @@
1
+ const _config = {
2
+ tagNames: {
3
+ tilt: "wcs-tilt",
4
+ },
5
+ };
6
+ function deepFreeze(obj) {
7
+ if (obj === null || typeof obj !== "object")
8
+ return obj;
9
+ Object.freeze(obj);
10
+ for (const key of Object.keys(obj)) {
11
+ deepFreeze(obj[key]);
12
+ }
13
+ return obj;
14
+ }
15
+ function deepClone(obj) {
16
+ if (obj === null || typeof obj !== "object")
17
+ return obj;
18
+ const clone = {};
19
+ for (const key of Object.keys(obj)) {
20
+ clone[key] = deepClone(obj[key]);
21
+ }
22
+ return clone;
23
+ }
24
+ let frozenConfig = null;
25
+ const config = _config;
26
+ function getConfig() {
27
+ if (!frozenConfig) {
28
+ frozenConfig = deepFreeze(deepClone(_config));
29
+ }
30
+ return frozenConfig;
31
+ }
32
+ function setConfig(partialConfig) {
33
+ if (partialConfig.tagNames) {
34
+ Object.assign(_config.tagNames, partialConfig.tagNames);
35
+ }
36
+ frozenConfig = null;
37
+ }
38
+
39
+ const UNSUPPORTED_SNAPSHOT = Object.freeze({
40
+ alpha: null,
41
+ beta: null,
42
+ gamma: null,
43
+ absolute: null,
44
+ });
45
+ /**
46
+ * Headless Device Orientation primitive. A thin, framework-agnostic wrapper
47
+ * around `window`'s `deviceorientation` event exposed through the wc-bindable
48
+ * protocol.
49
+ *
50
+ * The batch2 sibling of `@wcstack/idle` (docs/device-orientation-tag-design.md).
51
+ * Unlike Idle Detection, there is no matching Permissions API entry for this
52
+ * feature (`navigator.permissions.query` has no "device-orientation" name), so
53
+ * `permissionState` must be tracked **locally** rather than composed with
54
+ * `<wcs-permission>` — the defining asymmetry within batch2.
55
+ *
56
+ * No `_gen` generation guard: subscribing/unsubscribing to `deviceorientation`
57
+ * (start/stop) is fully synchronous, so that path never needs one. This node
58
+ * does have an async probe — `requestPermission()` awaits the static
59
+ * `DeviceOrientationEvent.requestPermission()` — but its post-await write is
60
+ * a benign `permissionState`/`error` value set + dispatch with no
61
+ * subscription/resource management to race, so `_gen` is unneeded there too
62
+ * (docs/device-orientation-tag-design.md §4, corrected after an earlier
63
+ * revision mistakenly reused `@wcstack/network`'s "no async probe at all"
64
+ * rationale).
65
+ */
66
+ class TiltCore extends EventTarget {
67
+ static wcBindable = {
68
+ protocol: "wc-bindable",
69
+ version: 1,
70
+ properties: [
71
+ { name: "alpha", event: "wcs-tilt:change", getter: (e) => e.detail.alpha },
72
+ { name: "beta", event: "wcs-tilt:change", getter: (e) => e.detail.beta },
73
+ { name: "gamma", event: "wcs-tilt:change", getter: (e) => e.detail.gamma },
74
+ { name: "absolute", event: "wcs-tilt:change", getter: (e) => e.detail.absolute },
75
+ { name: "permissionState", event: "wcs-tilt:permission-changed" },
76
+ // never-throw (§3.6): requestPermission() failures land here instead of
77
+ // rejecting/throwing. Mirrors idle (same batch2) and the accelerometer
78
+ // family (batch5) — see docs/io-node-batch-implementation-plan.md.
79
+ { name: "error", event: "wcs-tilt:error" },
80
+ ],
81
+ commands: [
82
+ { name: "requestPermission", async: true },
83
+ { name: "start" },
84
+ { name: "stop" },
85
+ ],
86
+ };
87
+ _target;
88
+ _snapshot = UNSUPPORTED_SNAPSHOT;
89
+ _permissionState = "unknown";
90
+ _error = null;
91
+ _subscribed = false;
92
+ // SSR (§3.8): never auto-starts on connect, so there is no probe to await —
93
+ // readiness is always immediate.
94
+ _ready = Promise.resolve();
95
+ constructor(target) {
96
+ super();
97
+ this._target = target ?? this;
98
+ }
99
+ get ready() {
100
+ return this._ready;
101
+ }
102
+ get alpha() {
103
+ return this._snapshot.alpha;
104
+ }
105
+ get beta() {
106
+ return this._snapshot.beta;
107
+ }
108
+ get gamma() {
109
+ return this._snapshot.gamma;
110
+ }
111
+ get absolute() {
112
+ return this._snapshot.absolute;
113
+ }
114
+ get permissionState() {
115
+ return this._permissionState;
116
+ }
117
+ get error() {
118
+ return this._error;
119
+ }
120
+ // Lifecycle (§3.5). observe() is a synchronous no-op: like `<wcs-idle>`,
121
+ // this Core deliberately does NOT auto-start on connect (§6) on platforms
122
+ // that gate deviceorientation behind requestPermission().
123
+ observe() {
124
+ return this._ready;
125
+ }
126
+ dispose() {
127
+ this.stop();
128
+ }
129
+ _deviceOrientationEventCtor() {
130
+ const g = globalThis;
131
+ return typeof g.DeviceOrientationEvent !== "undefined" ? g.DeviceOrientationEvent : undefined;
132
+ }
133
+ _setPermissionState(state) {
134
+ if (this._permissionState === state)
135
+ return;
136
+ this._permissionState = state;
137
+ this._target.dispatchEvent(new CustomEvent("wcs-tilt:permission-changed", {
138
+ detail: state,
139
+ bubbles: true,
140
+ }));
141
+ }
142
+ _setError(error) {
143
+ if (this._error === error)
144
+ return;
145
+ this._error = error;
146
+ this._target.dispatchEvent(new CustomEvent("wcs-tilt:error", {
147
+ detail: error,
148
+ bubbles: true,
149
+ }));
150
+ }
151
+ /**
152
+ * Wraps iOS 13+ Safari's static, gesture-gated
153
+ * `DeviceOrientationEvent.requestPermission()`. On platforms without this
154
+ * gate (Android Chrome, desktop) there is nothing to ask, so this resolves
155
+ * to `"granted"` immediately without querying anything — callers can write
156
+ * one `requestPermission()` → `start()` flow that works everywhere
157
+ * (docs/device-orientation-tag-design.md §3).
158
+ *
159
+ * never-throw (§3.6): a gesture-context rejection resolves to `"denied"`
160
+ * and the raw error lands in `error` instead of propagating. Mirrors
161
+ * `<wcs-idle>`'s `requestPermission()` (docs/idle-detection-tag-design.md
162
+ * §4.1) — any settled (non-throwing) outcome supersedes a stale `error`
163
+ * from an earlier attempt.
164
+ */
165
+ async requestPermission() {
166
+ const Ctor = this._deviceOrientationEventCtor();
167
+ if (typeof Ctor?.requestPermission !== "function") {
168
+ this._setError(null);
169
+ this._setPermissionState("granted");
170
+ return "granted";
171
+ }
172
+ try {
173
+ const result = await Ctor.requestPermission();
174
+ const state = result === "granted" ? "granted" : "denied";
175
+ this._setError(null);
176
+ this._setPermissionState(state);
177
+ return state;
178
+ }
179
+ catch (e) {
180
+ // never-throw: a gesture-context rejection resolves to "denied".
181
+ this._setError({ error: e });
182
+ this._setPermissionState("denied");
183
+ return "denied";
184
+ }
185
+ }
186
+ /** Subscribe to `deviceorientation`. Idempotent — a second start() while already subscribed is a no-op. */
187
+ start() {
188
+ if (this._subscribed)
189
+ return;
190
+ this._subscribed = true;
191
+ globalThis.window?.addEventListener("deviceorientation", this._onOrientation);
192
+ }
193
+ /** Unsubscribe from `deviceorientation`. Safe to call when not started. */
194
+ stop() {
195
+ if (!this._subscribed)
196
+ return;
197
+ this._subscribed = false;
198
+ globalThis.window?.removeEventListener("deviceorientation", this._onOrientation);
199
+ }
200
+ _onOrientation = (event) => {
201
+ this._apply({
202
+ alpha: event.alpha,
203
+ beta: event.beta,
204
+ gamma: event.gamma,
205
+ absolute: event.absolute,
206
+ });
207
+ };
208
+ // Same-value guard (§3.3 MUST).
209
+ _apply(next) {
210
+ const prev = this._snapshot;
211
+ if (prev.alpha === next.alpha &&
212
+ prev.beta === next.beta &&
213
+ prev.gamma === next.gamma &&
214
+ prev.absolute === next.absolute) {
215
+ return;
216
+ }
217
+ this._snapshot = next;
218
+ this._target.dispatchEvent(new CustomEvent("wcs-tilt:change", {
219
+ detail: next,
220
+ bubbles: true,
221
+ }));
222
+ }
223
+ }
224
+
225
+ /**
226
+ * `<wcs-tilt>` — declarative Device Orientation API monitor.
227
+ *
228
+ * Named `tilt` (not `orientation`/`device-orientation`) to avoid colliding
229
+ * with `<wcs-screen-orientation>` (docs/device-orientation-tag-design.md §9).
230
+ *
231
+ * Does NOT auto-start on connect, mirroring `<wcs-idle>`: on iOS, subscribing
232
+ * before permission is granted silently receives no events. Callers drive
233
+ * `requestPermission()` → `start()` explicitly.
234
+ */
235
+ class WcsTilt extends HTMLElement {
236
+ static hasConnectedCallbackPromise = true;
237
+ static wcBindable = {
238
+ ...TiltCore.wcBindable,
239
+ inputs: [],
240
+ // Core の commands をそのまま継承(単一情報源)。
241
+ commands: TiltCore.wcBindable.commands,
242
+ };
243
+ _core;
244
+ _connectedCallbackPromise = Promise.resolve();
245
+ constructor() {
246
+ super();
247
+ this._core = new TiltCore(this);
248
+ }
249
+ // --- Core delegated getters ---
250
+ get alpha() {
251
+ return this._core.alpha;
252
+ }
253
+ get beta() {
254
+ return this._core.beta;
255
+ }
256
+ get gamma() {
257
+ return this._core.gamma;
258
+ }
259
+ get absolute() {
260
+ return this._core.absolute;
261
+ }
262
+ get permissionState() {
263
+ return this._core.permissionState;
264
+ }
265
+ get error() {
266
+ return this._core.error;
267
+ }
268
+ get connectedCallbackPromise() {
269
+ return this._connectedCallbackPromise;
270
+ }
271
+ // --- Commands (delegated to Core) ---
272
+ requestPermission() {
273
+ return this._core.requestPermission();
274
+ }
275
+ start() {
276
+ this._core.start();
277
+ }
278
+ stop() {
279
+ this._core.stop();
280
+ }
281
+ // --- Lifecycle ---
282
+ connectedCallback() {
283
+ this.style.display = "none";
284
+ this._connectedCallbackPromise = this._core.observe();
285
+ }
286
+ disconnectedCallback() {
287
+ this._core.dispose();
288
+ }
289
+ }
290
+
291
+ function registerComponents() {
292
+ if (!customElements.get(config.tagNames.tilt)) {
293
+ customElements.define(config.tagNames.tilt, WcsTilt);
294
+ }
295
+ }
296
+
297
+ function bootstrapTilt(userConfig) {
298
+ if (userConfig) {
299
+ setConfig(userConfig);
300
+ }
301
+ registerComponents();
302
+ }
303
+
304
+ export { TiltCore, WcsTilt, bootstrapTilt, getConfig };
305
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/TiltCore.ts","../src/components/Tilt.ts","../src/registerComponents.ts","../src/bootstrapTilt.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n tilt: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n tilt: \"wcs-tilt\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, TiltPermissionState } from \"../types.js\";\n\ninterface DeviceOrientationEventCtor {\n requestPermission?: () => Promise<\"granted\" | \"denied\">;\n}\n\ninterface WcsTiltSnapshot {\n alpha: number | null;\n beta: number | null;\n gamma: number | null;\n absolute: boolean | null;\n}\n\nconst UNSUPPORTED_SNAPSHOT: WcsTiltSnapshot = Object.freeze({\n alpha: null,\n beta: null,\n gamma: null,\n absolute: null,\n});\n\n/**\n * Headless Device Orientation primitive. A thin, framework-agnostic wrapper\n * around `window`'s `deviceorientation` event exposed through the wc-bindable\n * protocol.\n *\n * The batch2 sibling of `@wcstack/idle` (docs/device-orientation-tag-design.md).\n * Unlike Idle Detection, there is no matching Permissions API entry for this\n * feature (`navigator.permissions.query` has no \"device-orientation\" name), so\n * `permissionState` must be tracked **locally** rather than composed with\n * `<wcs-permission>` — the defining asymmetry within batch2.\n *\n * No `_gen` generation guard: subscribing/unsubscribing to `deviceorientation`\n * (start/stop) is fully synchronous, so that path never needs one. This node\n * does have an async probe — `requestPermission()` awaits the static\n * `DeviceOrientationEvent.requestPermission()` — but its post-await write is\n * a benign `permissionState`/`error` value set + dispatch with no\n * subscription/resource management to race, so `_gen` is unneeded there too\n * (docs/device-orientation-tag-design.md §4, corrected after an earlier\n * revision mistakenly reused `@wcstack/network`'s \"no async probe at all\"\n * rationale).\n */\nexport class TiltCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"alpha\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.alpha },\n { name: \"beta\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.beta },\n { name: \"gamma\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.gamma },\n { name: \"absolute\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.absolute },\n { name: \"permissionState\", event: \"wcs-tilt:permission-changed\" },\n // never-throw (§3.6): requestPermission() failures land here instead of\n // rejecting/throwing. Mirrors idle (same batch2) and the accelerometer\n // family (batch5) — see docs/io-node-batch-implementation-plan.md.\n { name: \"error\", event: \"wcs-tilt:error\" },\n ],\n commands: [\n { name: \"requestPermission\", async: true },\n { name: \"start\" },\n { name: \"stop\" },\n ],\n };\n\n private _target: EventTarget;\n private _snapshot: WcsTiltSnapshot = UNSUPPORTED_SNAPSHOT;\n private _permissionState: TiltPermissionState = \"unknown\";\n private _error: any = null;\n private _subscribed = false;\n\n // SSR (§3.8): never auto-starts on connect, so there is no probe to await —\n // readiness is always immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get alpha(): number | null {\n return this._snapshot.alpha;\n }\n\n get beta(): number | null {\n return this._snapshot.beta;\n }\n\n get gamma(): number | null {\n return this._snapshot.gamma;\n }\n\n get absolute(): boolean | null {\n return this._snapshot.absolute;\n }\n\n get permissionState(): TiltPermissionState {\n return this._permissionState;\n }\n\n get error(): any {\n return this._error;\n }\n\n // Lifecycle (§3.5). observe() is a synchronous no-op: like `<wcs-idle>`,\n // this Core deliberately does NOT auto-start on connect (§6) on platforms\n // that gate deviceorientation behind requestPermission().\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this.stop();\n }\n\n private _deviceOrientationEventCtor(): DeviceOrientationEventCtor | undefined {\n const g = globalThis as any;\n return typeof g.DeviceOrientationEvent !== \"undefined\" ? g.DeviceOrientationEvent : undefined;\n }\n\n private _setPermissionState(state: TiltPermissionState): void {\n if (this._permissionState === state) return;\n this._permissionState = state;\n this._target.dispatchEvent(new CustomEvent(\"wcs-tilt:permission-changed\", {\n detail: state,\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-tilt:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n /**\n * Wraps iOS 13+ Safari's static, gesture-gated\n * `DeviceOrientationEvent.requestPermission()`. On platforms without this\n * gate (Android Chrome, desktop) there is nothing to ask, so this resolves\n * to `\"granted\"` immediately without querying anything — callers can write\n * one `requestPermission()` → `start()` flow that works everywhere\n * (docs/device-orientation-tag-design.md §3).\n *\n * never-throw (§3.6): a gesture-context rejection resolves to `\"denied\"`\n * and the raw error lands in `error` instead of propagating. Mirrors\n * `<wcs-idle>`'s `requestPermission()` (docs/idle-detection-tag-design.md\n * §4.1) — any settled (non-throwing) outcome supersedes a stale `error`\n * from an earlier attempt.\n */\n async requestPermission(): Promise<TiltPermissionState> {\n const Ctor = this._deviceOrientationEventCtor();\n if (typeof Ctor?.requestPermission !== \"function\") {\n this._setError(null);\n this._setPermissionState(\"granted\");\n return \"granted\";\n }\n try {\n const result = await Ctor.requestPermission();\n const state: TiltPermissionState = result === \"granted\" ? \"granted\" : \"denied\";\n this._setError(null);\n this._setPermissionState(state);\n return state;\n } catch (e) {\n // never-throw: a gesture-context rejection resolves to \"denied\".\n this._setError({ error: e });\n this._setPermissionState(\"denied\");\n return \"denied\";\n }\n }\n\n /** Subscribe to `deviceorientation`. Idempotent — a second start() while already subscribed is a no-op. */\n start(): void {\n if (this._subscribed) return;\n this._subscribed = true;\n globalThis.window?.addEventListener(\"deviceorientation\", this._onOrientation as EventListener);\n }\n\n /** Unsubscribe from `deviceorientation`. Safe to call when not started. */\n stop(): void {\n if (!this._subscribed) return;\n this._subscribed = false;\n globalThis.window?.removeEventListener(\"deviceorientation\", this._onOrientation as EventListener);\n }\n\n private _onOrientation = (event: DeviceOrientationEvent): void => {\n this._apply({\n alpha: event.alpha,\n beta: event.beta,\n gamma: event.gamma,\n absolute: event.absolute,\n });\n };\n\n // Same-value guard (§3.3 MUST).\n private _apply(next: WcsTiltSnapshot): void {\n const prev = this._snapshot;\n if (\n prev.alpha === next.alpha &&\n prev.beta === next.beta &&\n prev.gamma === next.gamma &&\n prev.absolute === next.absolute\n ) {\n return;\n }\n this._snapshot = next;\n this._target.dispatchEvent(new CustomEvent(\"wcs-tilt:change\", {\n detail: next,\n bubbles: true,\n }));\n }\n}\n","import { IWcBindable, TiltPermissionState } from \"../types.js\";\nimport { TiltCore } from \"../core/TiltCore.js\";\n\n/**\n * `<wcs-tilt>` — declarative Device Orientation API monitor.\n *\n * Named `tilt` (not `orientation`/`device-orientation`) to avoid colliding\n * with `<wcs-screen-orientation>` (docs/device-orientation-tag-design.md §9).\n *\n * Does NOT auto-start on connect, mirroring `<wcs-idle>`: on iOS, subscribing\n * before permission is granted silently receives no events. Callers drive\n * `requestPermission()` → `start()` explicitly.\n */\nexport class WcsTilt extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...TiltCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: TiltCore.wcBindable.commands,\n };\n\n private _core: TiltCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new TiltCore(this);\n }\n\n // --- Core delegated getters ---\n\n get alpha(): number | null {\n return this._core.alpha;\n }\n\n get beta(): number | null {\n return this._core.beta;\n }\n\n get gamma(): number | null {\n return this._core.gamma;\n }\n\n get absolute(): boolean | null {\n return this._core.absolute;\n }\n\n get permissionState(): TiltPermissionState {\n return this._core.permissionState;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands (delegated to Core) ---\n\n requestPermission(): Promise<TiltPermissionState> {\n return this._core.requestPermission();\n }\n\n start(): void {\n this._core.start();\n }\n\n stop(): void {\n this._core.stop();\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsTilt } from \"./components/Tilt.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.tilt)) {\n customElements.define(config.tagNames.tilt, WcsTilt);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapTilt(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const t={tagNames:{tilt:"wcs-tilt"}};function e(t){if(null===t||"object"!=typeof t)return t;Object.freeze(t);for(const s of Object.keys(t))e(t[s]);return t}function s(t){if(null===t||"object"!=typeof t)return t;const e={};for(const r of Object.keys(t))e[r]=s(t[r]);return e}let r=null;const n=t;function a(){return r||(r=e(s(t))),r}const i=Object.freeze({alpha:null,beta:null,gamma:null,absolute:null});class o extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"alpha",event:"wcs-tilt:change",getter:t=>t.detail.alpha},{name:"beta",event:"wcs-tilt:change",getter:t=>t.detail.beta},{name:"gamma",event:"wcs-tilt:change",getter:t=>t.detail.gamma},{name:"absolute",event:"wcs-tilt:change",getter:t=>t.detail.absolute},{name:"permissionState",event:"wcs-tilt:permission-changed"},{name:"error",event:"wcs-tilt:error"}],commands:[{name:"requestPermission",async:!0},{name:"start"},{name:"stop"}]};_target;_snapshot=i;_permissionState="unknown";_error=null;_subscribed=!1;_ready=Promise.resolve();constructor(t){super(),this._target=t??this}get ready(){return this._ready}get alpha(){return this._snapshot.alpha}get beta(){return this._snapshot.beta}get gamma(){return this._snapshot.gamma}get absolute(){return this._snapshot.absolute}get permissionState(){return this._permissionState}get error(){return this._error}observe(){return this._ready}dispose(){this.stop()}_deviceOrientationEventCtor(){const t=globalThis;return void 0!==t.DeviceOrientationEvent?t.DeviceOrientationEvent:void 0}_setPermissionState(t){this._permissionState!==t&&(this._permissionState=t,this._target.dispatchEvent(new CustomEvent("wcs-tilt:permission-changed",{detail:t,bubbles:!0})))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-tilt:error",{detail:t,bubbles:!0})))}async requestPermission(){const t=this._deviceOrientationEventCtor();if("function"!=typeof t?.requestPermission)return this._setError(null),this._setPermissionState("granted"),"granted";try{const e="granted"===await t.requestPermission()?"granted":"denied";return this._setError(null),this._setPermissionState(e),e}catch(t){return this._setError({error:t}),this._setPermissionState("denied"),"denied"}}start(){this._subscribed||(this._subscribed=!0,globalThis.window?.addEventListener("deviceorientation",this._onOrientation))}stop(){this._subscribed&&(this._subscribed=!1,globalThis.window?.removeEventListener("deviceorientation",this._onOrientation))}_onOrientation=t=>{this._apply({alpha:t.alpha,beta:t.beta,gamma:t.gamma,absolute:t.absolute})};_apply(t){const e=this._snapshot;e.alpha===t.alpha&&e.beta===t.beta&&e.gamma===t.gamma&&e.absolute===t.absolute||(this._snapshot=t,this._target.dispatchEvent(new CustomEvent("wcs-tilt:change",{detail:t,bubbles:!0})))}}class c extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...o.wcBindable,inputs:[],commands:o.wcBindable.commands};_core;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new o(this)}get alpha(){return this._core.alpha}get beta(){return this._core.beta}get gamma(){return this._core.gamma}get absolute(){return this._core.absolute}get permissionState(){return this._core.permissionState}get error(){return this._core.error}get connectedCallbackPromise(){return this._connectedCallbackPromise}requestPermission(){return this._core.requestPermission()}start(){this._core.start()}stop(){this._core.stop()}connectedCallback(){this.style.display="none",this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}function l(e){var s;e&&((s=e).tagNames&&Object.assign(t.tagNames,s.tagNames),r=null),customElements.get(n.tagNames.tilt)||customElements.define(n.tagNames.tilt,c)}export{o as TiltCore,c as WcsTilt,l as bootstrapTilt,a as getConfig};
2
+ //# sourceMappingURL=index.esm.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/TiltCore.ts","../src/components/Tilt.ts","../src/bootstrapTilt.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n tagNames: {\n tilt: string;\n };\n}\n\nconst _config: IInternalConfig = {\n tagNames: {\n tilt: \"wcs-tilt\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import { IWcBindable, TiltPermissionState } from \"../types.js\";\n\ninterface DeviceOrientationEventCtor {\n requestPermission?: () => Promise<\"granted\" | \"denied\">;\n}\n\ninterface WcsTiltSnapshot {\n alpha: number | null;\n beta: number | null;\n gamma: number | null;\n absolute: boolean | null;\n}\n\nconst UNSUPPORTED_SNAPSHOT: WcsTiltSnapshot = Object.freeze({\n alpha: null,\n beta: null,\n gamma: null,\n absolute: null,\n});\n\n/**\n * Headless Device Orientation primitive. A thin, framework-agnostic wrapper\n * around `window`'s `deviceorientation` event exposed through the wc-bindable\n * protocol.\n *\n * The batch2 sibling of `@wcstack/idle` (docs/device-orientation-tag-design.md).\n * Unlike Idle Detection, there is no matching Permissions API entry for this\n * feature (`navigator.permissions.query` has no \"device-orientation\" name), so\n * `permissionState` must be tracked **locally** rather than composed with\n * `<wcs-permission>` — the defining asymmetry within batch2.\n *\n * No `_gen` generation guard: subscribing/unsubscribing to `deviceorientation`\n * (start/stop) is fully synchronous, so that path never needs one. This node\n * does have an async probe — `requestPermission()` awaits the static\n * `DeviceOrientationEvent.requestPermission()` — but its post-await write is\n * a benign `permissionState`/`error` value set + dispatch with no\n * subscription/resource management to race, so `_gen` is unneeded there too\n * (docs/device-orientation-tag-design.md §4, corrected after an earlier\n * revision mistakenly reused `@wcstack/network`'s \"no async probe at all\"\n * rationale).\n */\nexport class TiltCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"alpha\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.alpha },\n { name: \"beta\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.beta },\n { name: \"gamma\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.gamma },\n { name: \"absolute\", event: \"wcs-tilt:change\", getter: (e: Event) => (e as CustomEvent).detail.absolute },\n { name: \"permissionState\", event: \"wcs-tilt:permission-changed\" },\n // never-throw (§3.6): requestPermission() failures land here instead of\n // rejecting/throwing. Mirrors idle (same batch2) and the accelerometer\n // family (batch5) — see docs/io-node-batch-implementation-plan.md.\n { name: \"error\", event: \"wcs-tilt:error\" },\n ],\n commands: [\n { name: \"requestPermission\", async: true },\n { name: \"start\" },\n { name: \"stop\" },\n ],\n };\n\n private _target: EventTarget;\n private _snapshot: WcsTiltSnapshot = UNSUPPORTED_SNAPSHOT;\n private _permissionState: TiltPermissionState = \"unknown\";\n private _error: any = null;\n private _subscribed = false;\n\n // SSR (§3.8): never auto-starts on connect, so there is no probe to await —\n // readiness is always immediate.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n }\n\n get ready(): Promise<void> {\n return this._ready;\n }\n\n get alpha(): number | null {\n return this._snapshot.alpha;\n }\n\n get beta(): number | null {\n return this._snapshot.beta;\n }\n\n get gamma(): number | null {\n return this._snapshot.gamma;\n }\n\n get absolute(): boolean | null {\n return this._snapshot.absolute;\n }\n\n get permissionState(): TiltPermissionState {\n return this._permissionState;\n }\n\n get error(): any {\n return this._error;\n }\n\n // Lifecycle (§3.5). observe() is a synchronous no-op: like `<wcs-idle>`,\n // this Core deliberately does NOT auto-start on connect (§6) on platforms\n // that gate deviceorientation behind requestPermission().\n observe(): Promise<void> {\n return this._ready;\n }\n\n dispose(): void {\n this.stop();\n }\n\n private _deviceOrientationEventCtor(): DeviceOrientationEventCtor | undefined {\n const g = globalThis as any;\n return typeof g.DeviceOrientationEvent !== \"undefined\" ? g.DeviceOrientationEvent : undefined;\n }\n\n private _setPermissionState(state: TiltPermissionState): void {\n if (this._permissionState === state) return;\n this._permissionState = state;\n this._target.dispatchEvent(new CustomEvent(\"wcs-tilt:permission-changed\", {\n detail: state,\n bubbles: true,\n }));\n }\n\n private _setError(error: any): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-tilt:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n /**\n * Wraps iOS 13+ Safari's static, gesture-gated\n * `DeviceOrientationEvent.requestPermission()`. On platforms without this\n * gate (Android Chrome, desktop) there is nothing to ask, so this resolves\n * to `\"granted\"` immediately without querying anything — callers can write\n * one `requestPermission()` → `start()` flow that works everywhere\n * (docs/device-orientation-tag-design.md §3).\n *\n * never-throw (§3.6): a gesture-context rejection resolves to `\"denied\"`\n * and the raw error lands in `error` instead of propagating. Mirrors\n * `<wcs-idle>`'s `requestPermission()` (docs/idle-detection-tag-design.md\n * §4.1) — any settled (non-throwing) outcome supersedes a stale `error`\n * from an earlier attempt.\n */\n async requestPermission(): Promise<TiltPermissionState> {\n const Ctor = this._deviceOrientationEventCtor();\n if (typeof Ctor?.requestPermission !== \"function\") {\n this._setError(null);\n this._setPermissionState(\"granted\");\n return \"granted\";\n }\n try {\n const result = await Ctor.requestPermission();\n const state: TiltPermissionState = result === \"granted\" ? \"granted\" : \"denied\";\n this._setError(null);\n this._setPermissionState(state);\n return state;\n } catch (e) {\n // never-throw: a gesture-context rejection resolves to \"denied\".\n this._setError({ error: e });\n this._setPermissionState(\"denied\");\n return \"denied\";\n }\n }\n\n /** Subscribe to `deviceorientation`. Idempotent — a second start() while already subscribed is a no-op. */\n start(): void {\n if (this._subscribed) return;\n this._subscribed = true;\n globalThis.window?.addEventListener(\"deviceorientation\", this._onOrientation as EventListener);\n }\n\n /** Unsubscribe from `deviceorientation`. Safe to call when not started. */\n stop(): void {\n if (!this._subscribed) return;\n this._subscribed = false;\n globalThis.window?.removeEventListener(\"deviceorientation\", this._onOrientation as EventListener);\n }\n\n private _onOrientation = (event: DeviceOrientationEvent): void => {\n this._apply({\n alpha: event.alpha,\n beta: event.beta,\n gamma: event.gamma,\n absolute: event.absolute,\n });\n };\n\n // Same-value guard (§3.3 MUST).\n private _apply(next: WcsTiltSnapshot): void {\n const prev = this._snapshot;\n if (\n prev.alpha === next.alpha &&\n prev.beta === next.beta &&\n prev.gamma === next.gamma &&\n prev.absolute === next.absolute\n ) {\n return;\n }\n this._snapshot = next;\n this._target.dispatchEvent(new CustomEvent(\"wcs-tilt:change\", {\n detail: next,\n bubbles: true,\n }));\n }\n}\n","import { IWcBindable, TiltPermissionState } from \"../types.js\";\nimport { TiltCore } from \"../core/TiltCore.js\";\n\n/**\n * `<wcs-tilt>` — declarative Device Orientation API monitor.\n *\n * Named `tilt` (not `orientation`/`device-orientation`) to avoid colliding\n * with `<wcs-screen-orientation>` (docs/device-orientation-tag-design.md §9).\n *\n * Does NOT auto-start on connect, mirroring `<wcs-idle>`: on iOS, subscribing\n * before permission is granted silently receives no events. Callers drive\n * `requestPermission()` → `start()` explicitly.\n */\nexport class WcsTilt extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n\n static wcBindable: IWcBindable = {\n ...TiltCore.wcBindable,\n inputs: [],\n // Core の commands をそのまま継承(単一情報源)。\n commands: TiltCore.wcBindable.commands,\n };\n\n private _core: TiltCore;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new TiltCore(this);\n }\n\n // --- Core delegated getters ---\n\n get alpha(): number | null {\n return this._core.alpha;\n }\n\n get beta(): number | null {\n return this._core.beta;\n }\n\n get gamma(): number | null {\n return this._core.gamma;\n }\n\n get absolute(): boolean | null {\n return this._core.absolute;\n }\n\n get permissionState(): TiltPermissionState {\n return this._core.permissionState;\n }\n\n get error(): any {\n return this._core.error;\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Commands (delegated to Core) ---\n\n requestPermission(): Promise<TiltPermissionState> {\n return this._core.requestPermission();\n }\n\n start(): void {\n this._core.start();\n }\n\n stop(): void {\n this._core.stop();\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapTilt(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsTilt } from \"./components/Tilt.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.tilt)) {\n customElements.define(config.tagNames.tilt, WcsTilt);\n }\n}\n"],"names":["_config","tagNames","tilt","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","UNSUPPORTED_SNAPSHOT","alpha","beta","gamma","absolute","TiltCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","commands","async","_target","_snapshot","_permissionState","_error","_subscribed","_ready","Promise","resolve","constructor","target","super","this","ready","permissionState","error","observe","dispose","stop","_deviceOrientationEventCtor","g","globalThis","DeviceOrientationEvent","undefined","_setPermissionState","state","dispatchEvent","CustomEvent","bubbles","_setError","requestPermission","Ctor","start","window","addEventListener","_onOrientation","removeEventListener","_apply","next","prev","WcsTilt","HTMLElement","wcBindable","inputs","_core","_connectedCallbackPromise","connectedCallbackPromise","connectedCallback","style","display","disconnectedCallback","bootstrapTilt","userConfig","partialConfig","assign","customElements","get","define"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@wcstack/tilt",
3
+ "version": "1.16.0",
4
+ "description": "Declarative Device Orientation component for Web Components. Framework-agnostic deviceorientation wrapper via wc-bindable-protocol.",
5
+ "type": "module",
6
+ "main": "./dist/index.esm.js",
7
+ "module": "./dist/index.esm.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.esm.js"
13
+ },
14
+ "./auto": "./dist/auto.min.js"
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "clean": "rimraf dist .tsc-out",
21
+ "build": "rimraf dist .tsc-out && tsc && rollup -c",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
25
+ "lint": "eslint src",
26
+ "version:patch": "npm version patch",
27
+ "version:minor": "npm version minor",
28
+ "version:major": "npm version major",
29
+ "prepublishOnly": "npm run build && npm run test:coverage"
30
+ },
31
+ "keywords": [
32
+ "web-components",
33
+ "device-orientation",
34
+ "tilt",
35
+ "custom-elements",
36
+ "wc-bindable",
37
+ "declarative",
38
+ "zero-dependencies",
39
+ "framework-agnostic"
40
+ ],
41
+ "author": "mogera551",
42
+ "homepage": "https://wcstack.github.io",
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/wcstack/wcstack.git",
46
+ "directory": "packages/tilt"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/wcstack/wcstack/issues"
50
+ },
51
+ "license": "MIT",
52
+ "devDependencies": {
53
+ "@eslint/js": "^9.39.1",
54
+ "@rollup/plugin-terser": "^0.4.4",
55
+ "@rollup/plugin-typescript": "^11.1.6",
56
+ "@vitest/coverage-v8": "^4.0.15",
57
+ "@vitest/ui": "^4.0.15",
58
+ "eslint": "^9.39.1",
59
+ "globals": "^16.5.0",
60
+ "happy-dom": "^20.0.11",
61
+ "rimraf": "^6.0.1",
62
+ "rollup": "^4.22.4",
63
+ "rollup-plugin-dts": "^6.1.1",
64
+ "rollup-plugin-copy": "^3.5.0",
65
+ "tslib": "^2.8.1",
66
+ "typescript": "^5.9.3",
67
+ "typescript-eslint": "^8.49.0",
68
+ "vitest": "^4.0.15"
69
+ }
70
+ }