@octane-xplat/macos-renderer 0.0.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +232 -3
- package/package.json +38 -3
- package/src/appkit.d.ts +199 -0
- package/src/fonts.ts +100 -0
- package/src/image.ts +23 -0
- package/src/index.d.ts +18 -0
- package/src/index.ts +4652 -0
- package/src/intrinsics.d.ts +53 -0
- package/src/jsx-runtime.d.ts +7 -0
- package/src/layer.d.mts +17 -0
- package/src/layer.mjs +221 -0
- package/src/native-core-shim.ts +47 -0
- package/src/native-escape-props.ts +30 -0
- package/src/presentation.mjs +501 -0
- package/src/webview.ts +252 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export namespace JSX {
|
|
2
|
+
export type Element = unknown
|
|
3
|
+
|
|
4
|
+
export interface IntrinsicAttributes {
|
|
5
|
+
key?: string | number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface HostProps {
|
|
9
|
+
id?: string
|
|
10
|
+
className?: string
|
|
11
|
+
style?: Record<string, unknown>
|
|
12
|
+
children?: unknown
|
|
13
|
+
accessibilityLabel?: string
|
|
14
|
+
accessibilityRole?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface IntrinsicElements {
|
|
18
|
+
stack: HostProps & {
|
|
19
|
+
children?: unknown
|
|
20
|
+
spacing?: number
|
|
21
|
+
}
|
|
22
|
+
label: HostProps & {
|
|
23
|
+
text?: string | number
|
|
24
|
+
fontSize?: number
|
|
25
|
+
whiteSpace?: string
|
|
26
|
+
maxLines?: number
|
|
27
|
+
textOverflow?: string
|
|
28
|
+
}
|
|
29
|
+
button: HostProps & {
|
|
30
|
+
title?: string
|
|
31
|
+
enabled?: boolean
|
|
32
|
+
onPress?: () => void
|
|
33
|
+
}
|
|
34
|
+
flexboxlayout: Record<string, unknown>
|
|
35
|
+
gridlayout: Record<string, unknown>
|
|
36
|
+
absolutelayout: Record<string, unknown>
|
|
37
|
+
stacklayout: Record<string, unknown>
|
|
38
|
+
rootlayout: Record<string, unknown>
|
|
39
|
+
scrollview: Record<string, unknown>
|
|
40
|
+
textfield: Record<string, unknown>
|
|
41
|
+
textview: Record<string, unknown>
|
|
42
|
+
image: Record<string, unknown>
|
|
43
|
+
span: Record<string, unknown>
|
|
44
|
+
formattedstring: Record<string, unknown>
|
|
45
|
+
pager: Record<string, unknown>
|
|
46
|
+
webview: Record<string, unknown>
|
|
47
|
+
cameraplus: Record<string, unknown>
|
|
48
|
+
xplatvideo: Record<string, unknown>
|
|
49
|
+
canvasview: Record<string, unknown>
|
|
50
|
+
svgview: Record<string, unknown>
|
|
51
|
+
[key: string]: any
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { JSX } from './intrinsics.js'
|
|
2
|
+
|
|
3
|
+
import type { JSX } from './intrinsics.js'
|
|
4
|
+
|
|
5
|
+
export declare function jsx(type: unknown, props: unknown, key?: unknown): JSX.Element
|
|
6
|
+
export declare function jsxs(type: unknown, props: unknown, key?: unknown): JSX.Element
|
|
7
|
+
export declare function Fragment(props: { children?: JSX.Element }): JSX.Element
|
package/src/layer.d.mts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Internal bridge for the self-drawn window layer; native objects are supplied
|
|
2
|
+
* by the renderer or test host. Coordinates use top-left window points. */
|
|
3
|
+
export function showWindowLayer(
|
|
4
|
+
options: Record<string, any>,
|
|
5
|
+
driver: {
|
|
6
|
+
createRoot: (view: any, anchor: any) => any
|
|
7
|
+
fittingSize: (view: any) => { width: number; height: number } | null
|
|
8
|
+
View?: any
|
|
9
|
+
Event?: any
|
|
10
|
+
Center?: any
|
|
11
|
+
},
|
|
12
|
+
): {
|
|
13
|
+
contentView: any
|
|
14
|
+
closed: boolean
|
|
15
|
+
update(props: Record<string, any>, positioning?: Record<string, any>): void
|
|
16
|
+
close(): void
|
|
17
|
+
} | null
|
package/src/layer.mjs
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
const notificationOwners = new WeakMap()
|
|
2
|
+
|
|
3
|
+
function ownNotifications(view) {
|
|
4
|
+
let record = notificationOwners.get(view)
|
|
5
|
+
if (!record) {
|
|
6
|
+
record = {
|
|
7
|
+
count: 0,
|
|
8
|
+
frame: view.postsFrameChangedNotifications,
|
|
9
|
+
bounds: view.postsBoundsChangedNotifications,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
notificationOwners.set(view, record)
|
|
13
|
+
view.postsFrameChangedNotifications = true
|
|
14
|
+
view.postsBoundsChangedNotifications = true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
record.count++
|
|
18
|
+
return () => {
|
|
19
|
+
if (--record.count) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
notificationOwners.delete(view)
|
|
24
|
+
if (view.postsFrameChangedNotifications === true) {
|
|
25
|
+
view.postsFrameChangedNotifications = record.frame
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (view.postsBoundsChangedNotifications === true) {
|
|
29
|
+
view.postsBoundsChangedNotifications = record.bounds
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** A self-drawn in-window layer. Position callback uses top-left window points.
|
|
35
|
+
* AppKit popovers remain available for platform-authentic consumers. */
|
|
36
|
+
export function showWindowLayer(
|
|
37
|
+
options,
|
|
38
|
+
{
|
|
39
|
+
createRoot,
|
|
40
|
+
fittingSize,
|
|
41
|
+
View = globalThis.NSView,
|
|
42
|
+
Event = globalThis.NSEvent,
|
|
43
|
+
Center = globalThis.NSNotificationCenter,
|
|
44
|
+
},
|
|
45
|
+
) {
|
|
46
|
+
const anchor = options.anchor
|
|
47
|
+
const container = anchor?.window?.contentView
|
|
48
|
+
if (
|
|
49
|
+
!container ||
|
|
50
|
+
!View ||
|
|
51
|
+
!anchor.convertRectToView ||
|
|
52
|
+
(options.lightDismiss &&
|
|
53
|
+
typeof Event?.addLocalMonitorForEventsMatchingMaskHandler !== 'function')
|
|
54
|
+
) {
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const contentView = View.alloc().initWithFrame({
|
|
59
|
+
origin: { x: 0, y: 0 },
|
|
60
|
+
size: { width: 1, height: 1 },
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
const root = createRoot(contentView, anchor)
|
|
64
|
+
let surface = options.props ?? {}
|
|
65
|
+
let positioning = options
|
|
66
|
+
let monitor = null
|
|
67
|
+
const observers = []
|
|
68
|
+
const releases = []
|
|
69
|
+
const updatePosition = () => {
|
|
70
|
+
if (layer.closed) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const viewport = container.bounds
|
|
75
|
+
const height = viewport.size.height
|
|
76
|
+
const rect = anchor.convertRectToView(anchor.bounds, container)
|
|
77
|
+
const flipped = !!container.isFlipped
|
|
78
|
+
const bounds = {
|
|
79
|
+
left: rect.origin.x - viewport.origin.x,
|
|
80
|
+
top: flipped
|
|
81
|
+
? rect.origin.y - viewport.origin.y
|
|
82
|
+
: height - (rect.origin.y - viewport.origin.y) - rect.size.height,
|
|
83
|
+
width: rect.size.width,
|
|
84
|
+
height: rect.size.height,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const size = fittingSize(contentView) ?? { width: 1, height: 1 }
|
|
88
|
+
const point = positioning.position(bounds, size, {
|
|
89
|
+
left: 0,
|
|
90
|
+
top: 0,
|
|
91
|
+
width: viewport.size.width,
|
|
92
|
+
height,
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
contentView.frame = {
|
|
96
|
+
origin: {
|
|
97
|
+
x: viewport.origin.x + point.left,
|
|
98
|
+
y: viewport.origin.y + (flipped ? point.top : height - point.top - size.height),
|
|
99
|
+
},
|
|
100
|
+
size,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const layer = {
|
|
105
|
+
contentView,
|
|
106
|
+
closed: false,
|
|
107
|
+
update(props, nextPositioning) {
|
|
108
|
+
if (layer.closed) {
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
surface = props
|
|
113
|
+
if (
|
|
114
|
+
nextPositioning?.lightDismiss &&
|
|
115
|
+
typeof Event?.addLocalMonitorForEventsMatchingMaskHandler !== 'function'
|
|
116
|
+
) {
|
|
117
|
+
throw new TypeError('AppKit lightDismiss requires a local mouse monitor')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (nextPositioning) {
|
|
121
|
+
positioning = { ...positioning, ...nextPositioning }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
root.render(options.component, surface)
|
|
125
|
+
updatePosition()
|
|
126
|
+
},
|
|
127
|
+
close() {
|
|
128
|
+
if (layer.closed) {
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
layer.closed = true
|
|
133
|
+
if (monitor) {
|
|
134
|
+
Event.removeMonitor(monitor)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const observer of observers) {
|
|
138
|
+
Center.defaultCenter.removeObserver(observer)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
for (const release of releases) {
|
|
142
|
+
release()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
root.unmount()
|
|
147
|
+
} finally {
|
|
148
|
+
contentView.removeFromSuperview()
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
container.addSubview(contentView)
|
|
155
|
+
layer.update(surface)
|
|
156
|
+
// Reposition for window resize, anchor movement and enclosing scroll views.
|
|
157
|
+
for (let view = anchor; view; view = view.superview) {
|
|
158
|
+
releases.push(ownNotifications(view))
|
|
159
|
+
for (const name of [
|
|
160
|
+
'NSViewFrameDidChangeNotification',
|
|
161
|
+
'NSViewBoundsDidChangeNotification',
|
|
162
|
+
]) {
|
|
163
|
+
observers.push(
|
|
164
|
+
Center.defaultCenter.addObserverForNameObjectQueueUsingBlock(
|
|
165
|
+
name,
|
|
166
|
+
view,
|
|
167
|
+
null,
|
|
168
|
+
updatePosition,
|
|
169
|
+
),
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
observers.push(
|
|
175
|
+
Center.defaultCenter.addObserverForNameObjectQueueUsingBlock(
|
|
176
|
+
'NSWindowWillCloseNotification',
|
|
177
|
+
anchor.window,
|
|
178
|
+
null,
|
|
179
|
+
() => layer.close(),
|
|
180
|
+
),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
observers.push(
|
|
184
|
+
Center.defaultCenter.addObserverForNameObjectQueueUsingBlock(
|
|
185
|
+
'NSWindowDidResizeNotification',
|
|
186
|
+
anchor.window,
|
|
187
|
+
null,
|
|
188
|
+
updatePosition,
|
|
189
|
+
),
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
if (Event?.addLocalMonitorForEventsMatchingMaskHandler) {
|
|
193
|
+
monitor = Event.addLocalMonitorForEventsMatchingMaskHandler(
|
|
194
|
+
(1 << 1) | (1 << 3) | (1 << 25),
|
|
195
|
+
(event) => {
|
|
196
|
+
if (layer.closed || !positioning.lightDismiss || event.window !== anchor.window) {
|
|
197
|
+
return event
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const point = container.convertPointFromView(event.locationInWindow, null)
|
|
201
|
+
const frame = contentView.frame
|
|
202
|
+
if (
|
|
203
|
+
point.x < frame.origin.x ||
|
|
204
|
+
point.y < frame.origin.y ||
|
|
205
|
+
point.x > frame.origin.x + frame.size.width ||
|
|
206
|
+
point.y > frame.origin.y + frame.size.height
|
|
207
|
+
) {
|
|
208
|
+
options.onClose?.()
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return event
|
|
212
|
+
},
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return layer
|
|
217
|
+
} catch (error) {
|
|
218
|
+
layer.close()
|
|
219
|
+
throw error
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// The shared pan leaf imports Utils eagerly, but this spike doesn't implement
|
|
2
|
+
// NativeScript gestures. Fail explicitly if pan velocity is ever requested.
|
|
3
|
+
export const Utils = {
|
|
4
|
+
layout: {
|
|
5
|
+
getDisplayDensity() {
|
|
6
|
+
throw new Error('NativeScript display density is unavailable in the AppKit spike')
|
|
7
|
+
},
|
|
8
|
+
},
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// The AppKit build may resolve a shared native leaf before tree-shaking it.
|
|
12
|
+
// These values keep import-time platform checks safe; components that need
|
|
13
|
+
// NativeScript application services have explicit AppKit leaves or render
|
|
14
|
+
// an unsupported state.
|
|
15
|
+
export const Application = { ios: null, android: null, on() {}, off() {} }
|
|
16
|
+
export const isAndroid = false
|
|
17
|
+
export const isIOS = false
|
|
18
|
+
export const AccessibilityRole = {
|
|
19
|
+
button: 'button',
|
|
20
|
+
link: 'link',
|
|
21
|
+
heading: 'heading',
|
|
22
|
+
image: 'image',
|
|
23
|
+
text: 'text',
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Octane's universal entry may evaluate the NativeScript driver module
|
|
27
|
+
// while the AppKit renderer is selected. The AppKit host never instantiates
|
|
28
|
+
// these NativeScript node classes.
|
|
29
|
+
export class ActionBar {}
|
|
30
|
+
export class ContentView {}
|
|
31
|
+
export class FormattedString {}
|
|
32
|
+
export class LayoutBase {}
|
|
33
|
+
export class ListView {}
|
|
34
|
+
export class Span {}
|
|
35
|
+
export class TextBase {}
|
|
36
|
+
export class View {}
|
|
37
|
+
export const unsetValue = Symbol('unsetValue')
|
|
38
|
+
|
|
39
|
+
// The AppKit spike has no display-link driver — motion's clock just needs a
|
|
40
|
+
// frame-shaped timer; the host's pump runs setTimeout on the main run loop.
|
|
41
|
+
export function requestAnimationFrame(callback: (time: number) => void): unknown {
|
|
42
|
+
return setTimeout(() => callback(Date.now()), 16)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function cancelAnimationFrame(id: unknown) {
|
|
46
|
+
clearTimeout(id)
|
|
47
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// AppKit has no NativeScript Core device globals or iOS/Android escape bags.
|
|
2
|
+
export function applyEscapeProps() {}
|
|
3
|
+
|
|
4
|
+
export function nativeAccessibilityRole(role: string) {
|
|
5
|
+
return role
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function nativeAccessibilityState(state?: {
|
|
9
|
+
disabled?: boolean
|
|
10
|
+
selected?: boolean
|
|
11
|
+
checked?: boolean
|
|
12
|
+
}) {
|
|
13
|
+
if (state?.disabled) {
|
|
14
|
+
return 'disabled'
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (state?.selected) {
|
|
18
|
+
return 'selected'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (state?.checked === true) {
|
|
22
|
+
return 'checked'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (state?.checked === false) {
|
|
26
|
+
return 'unchecked'
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return undefined
|
|
30
|
+
}
|