@octane-xplat/keyboard 0.0.1 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -1
- package/UPSTREAM.md +12 -0
- package/package.json +77 -9
- package/src/KeyboardAwareScrollView.mobile.tsrx +54 -0
- package/src/KeyboardAwareScrollView.tsrx +53 -0
- package/src/KeyboardStickyView.mobile.tsrx +66 -0
- package/src/KeyboardStickyView.tsrx +9 -0
- package/src/Provider.tsrx +11 -0
- package/src/binding-types.ts +16 -0
- package/src/controller.ts +243 -0
- package/src/focus-clock.macos.ts +13 -0
- package/src/focus-geometry.ts +34 -0
- package/src/focus-publish.ts +14 -0
- package/src/focus-runtime-types.ts +9 -0
- package/src/focus-runtime.macos.ts +62 -0
- package/src/focus-runtime.mobile.ts +57 -0
- package/src/focus-runtime.ts +14 -0
- package/src/focus-runtime.web.ts +139 -0
- package/src/focus-source.android.ts +173 -0
- package/src/focus-source.ios.ts +202 -0
- package/src/focus-source.macos.ts +113 -0
- package/src/focus-source.ts +12 -0
- package/src/focus-source.web.ts +157 -0
- package/src/focus-types.ts +19 -0
- package/src/focused-input.ts +43 -0
- package/src/index.ts +12 -0
- package/src/motion.ts +82 -0
- package/src/native-scope.ts +28 -0
- package/src/prediction.ts +32 -0
- package/src/props.ts +37 -0
- package/src/runtime-types.ts +10 -0
- package/src/runtime.mobile.ts +62 -0
- package/src/runtime.ts +16 -0
- package/src/scroll-controller.ts +136 -0
- package/src/scroll-geometry.ts +72 -0
- package/src/scroll-runtime-types.ts +6 -0
- package/src/scroll-runtime.macos.ts +41 -0
- package/src/scroll-runtime.mobile.ts +46 -0
- package/src/scroll-runtime.ts +7 -0
- package/src/scroll-runtime.web.ts +25 -0
- package/src/scroll-types.ts +24 -0
- package/src/source.android.ts +179 -0
- package/src/source.ios.ts +283 -0
- package/src/source.ts +5 -0
- package/src/tsconfig.json +1 -0
- package/src/types.ts +45 -0
- package/src/viewport-types.ts +7 -0
- package/src/viewport.android.ts +238 -0
- package/src/viewport.ios.ts +6 -0
- package/src/viewport.ts +6 -0
- package/types/focus-types.d.ts +17 -0
- package/types/index.d.ts +20 -0
- package/types/props.d.ts +35 -0
- package/types/scroll-types.d.ts +22 -0
- package/types/types.d.ts +40 -0
- package/index.js +0 -1
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { leaseKeyboardProperty } from '@octane-xplat/platform/keyboard-property'
|
|
2
|
+
import type { KeyboardPropertyLease } from '@octane-xplat/platform/keyboard-property'
|
|
3
|
+
import type { KeyboardViewportLease } from './viewport-types'
|
|
4
|
+
|
|
5
|
+
type Entry = {
|
|
6
|
+
owner: any
|
|
7
|
+
native: any
|
|
8
|
+
previous: string
|
|
9
|
+
count: number
|
|
10
|
+
padding: KeyboardPropertyLease[]
|
|
11
|
+
listener: (event: any) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const entries = new WeakMap<object, Entry>()
|
|
15
|
+
const ownedPolicy = 'dont-apply'
|
|
16
|
+
const noLease = (supported: boolean): KeyboardViewportLease => ({
|
|
17
|
+
supported,
|
|
18
|
+
update() {},
|
|
19
|
+
release() {},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const nativeFor = (view: any) => view?.nativeViewProtected ?? view?.android
|
|
23
|
+
const supportsPadding = (native: any) =>
|
|
24
|
+
native && typeof native.getEdgeInsets === 'function' && typeof native.getImeInsets === 'function'
|
|
25
|
+
|
|
26
|
+
const paddingOf = (native: any): number[] => {
|
|
27
|
+
const edges = native.getEdgeInsets(),
|
|
28
|
+
ime = native.getImeInsets()
|
|
29
|
+
// Own base padding, not the automatically applied strip. This remains valid
|
|
30
|
+
// if another owner changes the overflow policy while the lease is active.
|
|
31
|
+
return [
|
|
32
|
+
native.getPaddingLeft() - edges.left,
|
|
33
|
+
native.getPaddingTop() - edges.top,
|
|
34
|
+
native.getPaddingRight() - edges.right,
|
|
35
|
+
native.getPaddingBottom() - Math.max(edges.bottom, ime.bottom),
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const paddingLeases = (native: any) =>
|
|
40
|
+
[0, 1, 2, 3].map((index) =>
|
|
41
|
+
leaseKeyboardProperty(
|
|
42
|
+
() => paddingOf(native)[index],
|
|
43
|
+
(value) => {
|
|
44
|
+
const values = paddingOf(native)
|
|
45
|
+
values[index] = value
|
|
46
|
+
native.setPadding(...values)
|
|
47
|
+
},
|
|
48
|
+
),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
const applyBars = (entry: Entry, inset: any) => {
|
|
52
|
+
const cutout = entry.previous.includes('cutout')
|
|
53
|
+
const values = [inset.left, inset.top, inset.right, inset.bottom]
|
|
54
|
+
const cuts = [
|
|
55
|
+
inset.cutoutLeft ?? 0,
|
|
56
|
+
inset.cutoutTop ?? 0,
|
|
57
|
+
inset.cutoutRight ?? 0,
|
|
58
|
+
inset.cutoutBottom ?? 0,
|
|
59
|
+
]
|
|
60
|
+
entry.padding.forEach((lease, index) =>
|
|
61
|
+
lease.set(Math.max(0, values[index] ?? 0, cutout ? cuts[index] : 0)),
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const requestInsets = (native: any) =>
|
|
66
|
+
(globalThis as any).androidx?.core?.view?.ViewCompat?.requestApplyInsets(native)
|
|
67
|
+
|
|
68
|
+
const seedBars = (entry: Entry) => {
|
|
69
|
+
const core = (globalThis as any).androidx?.core?.view
|
|
70
|
+
const insets = core?.ViewCompat?.getRootWindowInsets(entry.native)
|
|
71
|
+
const type = core?.WindowInsetsCompat?.Type
|
|
72
|
+
if (!insets || !type) {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
const bars = insets.getInsets(type.systemBars())
|
|
76
|
+
const cutout = type.displayCutout ? insets.getInsets(type.displayCutout()) : undefined
|
|
77
|
+
applyBars(entry, {
|
|
78
|
+
left: bars.left,
|
|
79
|
+
top: bars.top,
|
|
80
|
+
right: bars.right,
|
|
81
|
+
bottom: bars.bottom,
|
|
82
|
+
cutoutLeft: cutout?.left,
|
|
83
|
+
cutoutTop: cutout?.top,
|
|
84
|
+
cutoutRight: cutout?.right,
|
|
85
|
+
cutoutBottom: cutout?.bottom,
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const update = (entry: Entry, request = true) => {
|
|
90
|
+
if (entry.owner.androidOverflowEdge !== ownedPolicy) {
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
const native = nativeFor(entry.owner)
|
|
94
|
+
if (!supportsPadding(native) || entry.native === native) {
|
|
95
|
+
return
|
|
96
|
+
}
|
|
97
|
+
// A detached native view no longer owns the replacement's properties.
|
|
98
|
+
entry.padding.forEach((lease) => lease.release())
|
|
99
|
+
entry.native = native
|
|
100
|
+
entry.padding = paddingLeases(native)
|
|
101
|
+
if (request) {
|
|
102
|
+
seedBars(entry)
|
|
103
|
+
requestInsets(native)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Lease NativeScript's public inset-distribution event. Its default Page
|
|
108
|
+
* pads by max(navigation, IME) independently of softInputMode. We retain bars
|
|
109
|
+
* and consume IME here so consumers can follow measured frames themselves.
|
|
110
|
+
* No listener on the Activity/decor is replaced. @internal */
|
|
111
|
+
export function leaseKeyboardViewport(host: any): KeyboardViewportLease {
|
|
112
|
+
const chain: any[] = []
|
|
113
|
+
for (let view = host; view; view = view.parent) {
|
|
114
|
+
chain.push(view)
|
|
115
|
+
}
|
|
116
|
+
chain.reverse()
|
|
117
|
+
let owner: any
|
|
118
|
+
for (const view of chain) {
|
|
119
|
+
if (entries.has(view)) {
|
|
120
|
+
owner = view
|
|
121
|
+
break
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const policy = view.androidOverflowEdge
|
|
125
|
+
if (policy === 'none' || policy === 'none,cutout') {
|
|
126
|
+
owner = view
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (policy && policy !== 'ignore') {
|
|
131
|
+
return noLease(false)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (!owner) {
|
|
135
|
+
return noLease(!!host?.page)
|
|
136
|
+
}
|
|
137
|
+
let entry = entries.get(owner)
|
|
138
|
+
if (!entry) {
|
|
139
|
+
const native = nativeFor(owner)
|
|
140
|
+
if (
|
|
141
|
+
!supportsPadding(native) ||
|
|
142
|
+
typeof native.setPadding !== 'function' ||
|
|
143
|
+
owner.hasListeners?.('androidOverflowInset')
|
|
144
|
+
) {
|
|
145
|
+
return noLease(false)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const previous = owner.androidOverflowEdge
|
|
149
|
+
// Clear the old automatically applied strip before capturing base padding.
|
|
150
|
+
owner.androidOverflowEdge = 'ignore'
|
|
151
|
+
entry = {
|
|
152
|
+
owner,
|
|
153
|
+
native,
|
|
154
|
+
previous,
|
|
155
|
+
count: 0,
|
|
156
|
+
padding: paddingLeases(native),
|
|
157
|
+
listener: () => {},
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const own = entry
|
|
161
|
+
own.listener = (event) => {
|
|
162
|
+
if (own.owner.androidOverflowEdge !== ownedPolicy || !event.inset) {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
update(own, false)
|
|
166
|
+
if (own.native !== nativeFor(own.owner)) {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
169
|
+
const inset = event.inset
|
|
170
|
+
applyBars(own, inset)
|
|
171
|
+
inset.leftConsumed = inset.topConsumed = inset.rightConsumed = inset.bottomConsumed = true
|
|
172
|
+
inset.imeBottom = 0
|
|
173
|
+
inset.imeBottomConsumed = true
|
|
174
|
+
if (own.previous.includes('cutout')) {
|
|
175
|
+
inset.cutoutLeftConsumed =
|
|
176
|
+
inset.cutoutTopConsumed =
|
|
177
|
+
inset.cutoutRightConsumed =
|
|
178
|
+
inset.cutoutBottomConsumed =
|
|
179
|
+
true
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
entries.set(owner, own)
|
|
184
|
+
try {
|
|
185
|
+
owner.on('androidOverflowInset', own.listener)
|
|
186
|
+
owner.androidOverflowEdge = ownedPolicy
|
|
187
|
+
seedBars(own)
|
|
188
|
+
requestInsets(native)
|
|
189
|
+
} catch (error) {
|
|
190
|
+
entries.delete(owner)
|
|
191
|
+
owner.off('androidOverflowInset', own.listener)
|
|
192
|
+
if (owner.androidOverflowEdge === ownedPolicy || owner.androidOverflowEdge === 'ignore') {
|
|
193
|
+
own.padding.forEach((lease) => lease.release())
|
|
194
|
+
owner.androidOverflowEdge = previous
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
throw error
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const own = entry
|
|
201
|
+
own.count++
|
|
202
|
+
let active = true
|
|
203
|
+
return {
|
|
204
|
+
get supported() {
|
|
205
|
+
return (
|
|
206
|
+
active &&
|
|
207
|
+
own.owner.androidOverflowEdge === ownedPolicy &&
|
|
208
|
+
own.native === nativeFor(own.owner)
|
|
209
|
+
)
|
|
210
|
+
},
|
|
211
|
+
update() {
|
|
212
|
+
if (active) {
|
|
213
|
+
update(own)
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
release() {
|
|
217
|
+
if (!active) {
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
active = false
|
|
221
|
+
if (--own.count) {
|
|
222
|
+
return
|
|
223
|
+
}
|
|
224
|
+
entries.delete(own.owner)
|
|
225
|
+
own.owner.off('androidOverflowInset', own.listener)
|
|
226
|
+
if (own.native === nativeFor(own.owner)) {
|
|
227
|
+
own.padding.forEach((lease) => lease.release())
|
|
228
|
+
}
|
|
229
|
+
if (own.owner.androidOverflowEdge === ownedPolicy) {
|
|
230
|
+
own.owner.androidOverflowEdge = own.previous
|
|
231
|
+
const native = nativeFor(own.owner)
|
|
232
|
+
if (native) {
|
|
233
|
+
requestInsets(native)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { KeyboardViewportLease } from './viewport-types'
|
|
2
|
+
|
|
3
|
+
/** @internal UIKit's owning-window keyboard guide needs no ancestor padding lease. */
|
|
4
|
+
export function leaseKeyboardViewport(_host: object): KeyboardViewportLease {
|
|
5
|
+
return { supported: true, update() {}, release() {} }
|
|
6
|
+
}
|
package/src/viewport.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { KeyboardViewportLease } from './viewport-types'
|
|
2
|
+
|
|
3
|
+
/** @internal Unsupported software-keyboard targets retain their native viewport. */
|
|
4
|
+
export function leaseKeyboardViewport(_host: object): KeyboardViewportLease {
|
|
5
|
+
return { supported: false, update() {}, release() {} }
|
|
6
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FocusedInputState } from '@octane-xplat/platform/focused-input-state';
|
|
2
|
+
/** Focused input geometry in device-independent window coordinates. Identity
|
|
3
|
+
* and scroll owners are opaque native/DOM handles. No text contents are read.
|
|
4
|
+
* Unsupported targets return null geometry and false capabilities. */
|
|
5
|
+
export type FocusedInputSnapshot = FocusedInputState;
|
|
6
|
+
/** A vertical scroll viewport containing the input, nearest owner first. */
|
|
7
|
+
export type { FocusedInputScrollOwner } from '@octane-xplat/platform/focused-input-state';
|
|
8
|
+
/** Separate focus/selection geometry from software-keyboard movement. */
|
|
9
|
+
export interface FocusedInputService {
|
|
10
|
+
/** Cached read; does not start observation. */
|
|
11
|
+
getSnapshot(host: object): FocusedInputSnapshot;
|
|
12
|
+
/** Observe identity, selection and layout changes; release on host removal.
|
|
13
|
+
* Geometry callbacks can occur each frame during native layout movement. */
|
|
14
|
+
subscribe(host: object, listener: (snapshot: FocusedInputSnapshot) => void): () => void;
|
|
15
|
+
/** Refresh an actively observed host. No-op without a reader. */
|
|
16
|
+
recheck(host: object): void;
|
|
17
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { UniversalComponent } from 'octane/universal'
|
|
2
|
+
import type { KeyboardProviderProps } from './props.js'
|
|
3
|
+
import type { SoftwareKeyboardController } from './types.js'
|
|
4
|
+
import type { FocusedInputService } from './focus-types.js'
|
|
5
|
+
|
|
6
|
+
export type * from './types.js'
|
|
7
|
+
export type * from './props.js'
|
|
8
|
+
export type * from './focus-types.js'
|
|
9
|
+
|
|
10
|
+
/** Share native tracking across roots and existing UI keyboard consumers. */
|
|
11
|
+
export declare const KeyboardProvider: UniversalComponent<KeyboardProviderProps>
|
|
12
|
+
export declare const KeyboardController: SoftwareKeyboardController
|
|
13
|
+
/** Focus/selection geometry without collecting text contents. */
|
|
14
|
+
export declare const FocusedInputController: FocusedInputService
|
|
15
|
+
|
|
16
|
+
export type * from './scroll-types.js'
|
|
17
|
+
export declare const KeyboardScrollController: import('./scroll-types.js').KeyboardScrollService
|
|
18
|
+
export declare const KeyboardAwareScrollView: UniversalComponent<import('./props.js').KeyboardAwareScrollViewProps>
|
|
19
|
+
|
|
20
|
+
export declare const KeyboardStickyView: UniversalComponent<import('./props.js').KeyboardStickyViewProps>
|
package/types/props.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { KeyboardAvoidingProps, ViewProps } from '@octane-xplat/ui';
|
|
2
|
+
/** Shared tracking for existing keyboard consumers. Native observers start with
|
|
3
|
+
* the first window reader and stop with the last. Web/desktop are neutral.
|
|
4
|
+
* @example
|
|
5
|
+
* ```tsx
|
|
6
|
+
* import { KeyboardProvider } from '@octane-xplat/keyboard'
|
|
7
|
+
* import { KeyboardAvoiding, TextInput } from '@octane-xplat/ui'
|
|
8
|
+
* <KeyboardProvider><KeyboardAvoiding><TextInput /></KeyboardAvoiding></KeyboardProvider>
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export interface KeyboardProviderProps {
|
|
12
|
+
children?: unknown;
|
|
13
|
+
/** Activate native tracking; false retains children and fallback behavior. @default true */
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** A vertical form that exposes the focused field/caret while the keyboard moves. */
|
|
17
|
+
export interface KeyboardAwareScrollViewProps extends Omit<KeyboardAvoidingProps, 'ref'> {
|
|
18
|
+
/** Minimum visible gap; does not create scroll range. @default 8 */
|
|
19
|
+
clearance?: number;
|
|
20
|
+
/** Additional bottom scroll range while the keyboard is open. @default 0 */
|
|
21
|
+
extraScrollSpace?: number;
|
|
22
|
+
manualScroll?: 'pause' | 'follow';
|
|
23
|
+
keyboardClose?: 'preserve' | 'restore';
|
|
24
|
+
/** Mounted visibility binding; null on cleanup. */
|
|
25
|
+
ref?: (binding: import('./scroll-types').KeyboardScrollBinding | null) => void;
|
|
26
|
+
}
|
|
27
|
+
/** Translate a control independently of surrounding layout as the native keyboard moves. */
|
|
28
|
+
export interface KeyboardStickyViewProps extends ViewProps {
|
|
29
|
+
/** Disabled keeps its ordinary layout and releases native movement. @default true */
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
/** Additional upward offset while open, in dips. @default 0 */
|
|
32
|
+
openOffset?: number;
|
|
33
|
+
/** Upward offset while closed, in dips. @default 0 */
|
|
34
|
+
closedOffset?: number;
|
|
35
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Geometry policy for vertical focused-input scrolling. */
|
|
2
|
+
export interface KeyboardScrollOptions {
|
|
3
|
+
/** Minimum gap around the input/caret, in window layout units. @default 8 */
|
|
4
|
+
clearance?: number;
|
|
5
|
+
/** Pause after a user scroll until focus/selection changes or recheck. @default 'pause' */
|
|
6
|
+
manualScroll?: 'pause' | 'follow';
|
|
7
|
+
/** Keep the resulting offset, or restore untouched offsets on close. @default 'preserve' */
|
|
8
|
+
keyboardClose?: 'preserve' | 'restore';
|
|
9
|
+
}
|
|
10
|
+
export interface KeyboardScrollBinding {
|
|
11
|
+
/** Explicitly resume visibility enforcement after validation or manual scrolling. */
|
|
12
|
+
recheck(): void;
|
|
13
|
+
/** Apply changed options without restarting window observation. */
|
|
14
|
+
update(options: KeyboardScrollOptions): void;
|
|
15
|
+
/** Release observation; preserves the current scroll position. */
|
|
16
|
+
release(): void;
|
|
17
|
+
}
|
|
18
|
+
export interface KeyboardScrollService {
|
|
19
|
+
/** Attach to a vertical scroll viewport. Only scroll ancestors through this
|
|
20
|
+
* viewport are eligible; unrelated fields in the same window are ignored. */
|
|
21
|
+
bind(host: object, options?: KeyboardScrollOptions): KeyboardScrollBinding;
|
|
22
|
+
}
|
package/types/types.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { KeyboardRect } from '@octane-xplat/platform/keyboard-geometry';
|
|
2
|
+
export type { KeyboardRect } from '@octane-xplat/platform/keyboard-geometry';
|
|
3
|
+
/** Lifecycle of one keyboard transition. An interrupted transition cancels
|
|
4
|
+
* before the replacement starts; current geometry never jumps to its target. */
|
|
5
|
+
export type KeyboardPhase = 'idle' | 'start' | 'move' | 'end' | 'cancel';
|
|
6
|
+
/** Capabilities belong to the actual host window and active adapter. */
|
|
7
|
+
export interface KeyboardCapabilities {
|
|
8
|
+
readonly geometry: boolean;
|
|
9
|
+
readonly nativeFrames: boolean;
|
|
10
|
+
readonly interactive: boolean;
|
|
11
|
+
readonly floating: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Geometry is in device-independent coordinates in the owning window.
|
|
14
|
+
* Null frames are unavailable; source distinguishes unknown from measured hidden.
|
|
15
|
+
* A destination frame is never presented as measured intermediate movement. */
|
|
16
|
+
export interface KeyboardSnapshot {
|
|
17
|
+
readonly inset: number;
|
|
18
|
+
readonly windowRect: KeyboardRect | null;
|
|
19
|
+
readonly currentFrame: KeyboardRect | null;
|
|
20
|
+
readonly targetFrame: KeyboardRect | null;
|
|
21
|
+
readonly source: 'unknown' | 'destination' | 'native-frames';
|
|
22
|
+
readonly capabilities: KeyboardCapabilities;
|
|
23
|
+
readonly phase: KeyboardPhase;
|
|
24
|
+
readonly transitionId: number;
|
|
25
|
+
/** Unix milliseconds at delivery. */
|
|
26
|
+
readonly timestampMs: number;
|
|
27
|
+
/** Native destination transition duration, or null when unknown. */
|
|
28
|
+
readonly durationMs: number | null;
|
|
29
|
+
}
|
|
30
|
+
export interface SoftwareKeyboardController {
|
|
31
|
+
/** Activate shared native tracking until release. Observation starts only
|
|
32
|
+
* when a window has readers. Repeated activations share ownership. */
|
|
33
|
+
activate(): () => void;
|
|
34
|
+
/** Read available cached state without activating observation. */
|
|
35
|
+
getSnapshot(host: object): KeyboardSnapshot;
|
|
36
|
+
/** Observe destination/lifecycle changes, excluding per-frame movement. */
|
|
37
|
+
subscribe(host: object, listener: (snapshot: KeyboardSnapshot) => void): () => void;
|
|
38
|
+
/** Observe measured movement directly; callbacks must not render per frame. */
|
|
39
|
+
onMove(host: object, listener: (snapshot: KeyboardSnapshot) => void): () => void;
|
|
40
|
+
}
|
package/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
throw new Error('This is the keyboard bootstrap stub. Install a supported release instead.');
|