@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alec Larson
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,4 +1,233 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Render Octane components in AppKit
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
`@octane-xplat/macos-renderer` mounts Octane components into an existing
|
|
4
|
+
`NSView`. It is the experimental macOS AppKit renderer, using the
|
|
5
|
+
`@nativescript/macos-node-api` runtime and the CLI's JavaScriptCore host.
|
|
6
|
+
The supported packaging target is Apple Silicon with macOS 13.5 or later.
|
|
7
|
+
|
|
8
|
+
Your application owns startup, windows, menus, host services, and custom font
|
|
9
|
+
assets. The renderer owns views, layout, events, accessibility, and its hosted
|
|
10
|
+
popups and sheets. It does not load web stylesheets or implement every
|
|
11
|
+
NativeScript widget. See the [macOS harness](../../apps/macos/README.md) for
|
|
12
|
+
the current component and service limits. The image-crop integration test compiles and mounts the public UI barrel.
|
|
13
|
+
The full application harness has not been reverified by that test. A raw AppKit button
|
|
14
|
+
`performClick` check also exposes an existing action-selector mismatch. Native
|
|
15
|
+
click delivery needs separate repair and verification; the maintained consumer
|
|
16
|
+
checks direct handler dispatch.
|
|
17
|
+
|
|
18
|
+
## Configure an app
|
|
19
|
+
|
|
20
|
+
Declare the renderer, `octane`, and `@nativescript/macos-node-api` as application
|
|
21
|
+
dependencies. The CLI also requires the runtime to be directly declared, even
|
|
22
|
+
though the renderer depends on it. Use one copy of Octane per application.
|
|
23
|
+
Declare `@octane-xplat/cli`, `@octanejs/vite-plugin`, and `vite` as development
|
|
24
|
+
dependencies; the preset resolves these from your app, not from the CLI.
|
|
25
|
+
The maintained consumer uses Octane 0.6.3, the Vite plugin 0.1.61, Vite 8.3.0,
|
|
26
|
+
and the runtime `0.4.4-next.2026-08-09-31292056208`.
|
|
27
|
+
|
|
28
|
+
Apply the framework's [patch setup](../cli/README.md)
|
|
29
|
+
before building. For TSX checking, install TypeScript; for TSRX source, also
|
|
30
|
+
install `@tsrx/typescript-plugin` and use `tsrx-tsc`.
|
|
31
|
+
|
|
32
|
+
Use this production Vite config:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import { defineConfig } from 'vite'
|
|
36
|
+
import { xplatMacOS } from '@octane-xplat/cli/macos/vite'
|
|
37
|
+
|
|
38
|
+
export default defineConfig(({ mode }) => xplatMacOS(mode))
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The async preset bundles `src/main.mjs` into `dist/package-build/main.cjs`,
|
|
42
|
+
selects macOS platform files, registers the universal renderer, aliases
|
|
43
|
+
NativeScript compatibility imports, and externalizes only the native runtime
|
|
44
|
+
and `node:` host imports. Unsupported host imports still fail CLI packaging.
|
|
45
|
+
No repository paths or harness fonts are needed.
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
import { xplatMacOS } from '@octane-xplat/cli/macos/vite'
|
|
49
|
+
|
|
50
|
+
export default await xplatMacOS('production', { entry: 'src/main.mjs' })
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Set these TypeScript options:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"compilerOptions": {
|
|
58
|
+
"target": "ESNext",
|
|
59
|
+
"module": "ESNext",
|
|
60
|
+
"moduleResolution": "Bundler",
|
|
61
|
+
"jsx": "react-jsx",
|
|
62
|
+
"jsxImportSource": "@octane-xplat/macos-renderer",
|
|
63
|
+
"customConditions": ["macos"],
|
|
64
|
+
"moduleSuffixes": [".macos", ""],
|
|
65
|
+
"lib": ["ESNext"],
|
|
66
|
+
"strict": true,
|
|
67
|
+
"skipLibCheck": true,
|
|
68
|
+
"noEmit": true
|
|
69
|
+
},
|
|
70
|
+
"include": ["src/**/*"]
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
JSX runtime subpaths contain compiler typing declarations; use the Octane Vite
|
|
75
|
+
compiler to produce runnable code. The preset compiles reachable TSX/TSRX
|
|
76
|
+
source by default. `rules` can override that selection.
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import { xplatMacOS } from '@octane-xplat/cli/macos/vite'
|
|
80
|
+
|
|
81
|
+
export default await xplatMacOS('production', {
|
|
82
|
+
rules: [{ include: '**/*.tsx', renderer: 'macos' }],
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Mount and dispose a root
|
|
87
|
+
|
|
88
|
+
In your existing host bootstrap, pass its content view to the renderer:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
import { createMacOSRoot } from '@octane-xplat/macos-renderer'
|
|
92
|
+
import App from './App.macos.tsx'
|
|
93
|
+
|
|
94
|
+
const root = createMacOSRoot(contentView)
|
|
95
|
+
root.render(App, {})
|
|
96
|
+
|
|
97
|
+
// Call when the owning window or application closes.
|
|
98
|
+
root.unmount()
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`contentView` is the `NSView` supplied by your window host. Keep one root for
|
|
102
|
+
that view and call `unmount()` before disposing the host. Root creation does
|
|
103
|
+
not create a window or start the application run loop. The package also
|
|
104
|
+
re-exports Octane's universal native runtime for compiler and hook imports.
|
|
105
|
+
|
|
106
|
+
Use [the packaging guide](../../docs/start/toolchain.md#experimental-appkit-target)
|
|
107
|
+
for `xplat.targets.macos` metadata, signing, and CLI commands. The
|
|
108
|
+
[independent fixture](test/fixtures/main.mjs) demonstrates a complete host
|
|
109
|
+
entry, including startup and shutdown, without importing the harness.
|
|
110
|
+
|
|
111
|
+
## Render local images and overlays
|
|
112
|
+
|
|
113
|
+
Image sources can be base64 data URIs, local file paths, or `file://` URLs.
|
|
114
|
+
An empty or unsupported source clears the old image. Remote URLs require a
|
|
115
|
+
separate download; image loading does not perform synchronous network work.
|
|
116
|
+
For centered contain-fit, pass `style={{ objectFit: 'contain' }}` to `Image`.
|
|
117
|
+
This scales smaller images up while preserving their aspect ratio.
|
|
118
|
+
|
|
119
|
+
`style={{ pointerEvents: 'none' }}` makes `Image` and `View` (the flexbox host)
|
|
120
|
+
pass hit-testing through to siblings below them. Use it for decorative crop
|
|
121
|
+
source/guide layers; keep gesture controls interactive. The AppKit crop test
|
|
122
|
+
checks those native hit-test boundaries separately from pan handler dispatch.
|
|
123
|
+
|
|
124
|
+
## Choose fonts
|
|
125
|
+
|
|
126
|
+
Roots default to Apple's system font. Sizes and CSS numeric or named weights
|
|
127
|
+
map to weighted AppKit fonts. `system-ui`, `-apple-system`, and `sans-serif`
|
|
128
|
+
select the system font; unavailable families fall through the family stack,
|
|
129
|
+
then fall back to the system font.
|
|
130
|
+
|
|
131
|
+
```tsx
|
|
132
|
+
// Text.macos.tsx — the AppKit renderer is configured above.
|
|
133
|
+
export function Title() {
|
|
134
|
+
return <label text="Trip notes" style={{ fontFamily: 'system-ui', fontWeight: 'bold' }} />
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
For an installed family, set `style.fontFamily` on text or choose a root
|
|
139
|
+
default with `createMacOSRoot(contentView, { fontFamily: 'Helvetica Neue' })`.
|
|
140
|
+
Renderer-hosted popups and sheets inherit that root default.
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
// contentView is your existing window's NSView, as in the host example above.
|
|
144
|
+
import { createMacOSRoot } from '@octane-xplat/macos-renderer'
|
|
145
|
+
|
|
146
|
+
const root = createMacOSRoot(contentView, { fontFamily: 'Helvetica Neue' })
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
For bundled fonts, the application loads its assets and license and creates
|
|
150
|
+
native descriptors. Register those descriptors before mounting:
|
|
151
|
+
|
|
152
|
+
```js
|
|
153
|
+
import { createMacOSRoot, registerFontFamily } from '@octane-xplat/macos-renderer'
|
|
154
|
+
|
|
155
|
+
// regularFace and boldFace are NSFontDescriptors created from your font asset.
|
|
156
|
+
registerFontFamily('Acme Sans', [
|
|
157
|
+
{ weight: 400, descriptor: regularFace },
|
|
158
|
+
{ weight: 700, descriptor: boldFace },
|
|
159
|
+
])
|
|
160
|
+
const root = createMacOSRoot(contentView, { fontFamily: 'Acme Sans' })
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Registration does not change the renderer default. Registered weights resolve
|
|
164
|
+
to the first face at or above the requested weight, or the heaviest face.
|
|
165
|
+
Invalid names, empty face lists, and missing descriptors or invalid weights are
|
|
166
|
+
rejected. The renderer does not read assets, register Geist automatically, or
|
|
167
|
+
write a font cache. The [harness font setup](../../apps/macos/src/fonts.mjs)
|
|
168
|
+
demonstrates app-owned descriptors and packaged font bytes.
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
// regularFace is an NSFontDescriptor created by the app from its loaded font.
|
|
172
|
+
registerFontFamily('Acme Sans', [{ weight: 400, descriptor: regularFace }])
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Development bundles
|
|
176
|
+
|
|
177
|
+
Call `xplatMacOS(mode, { packaged: false, hmr: true, entry: 'src/App.macos.tsx' })`
|
|
178
|
+
for the component bundle. It retains renderer and Octane imports so the
|
|
179
|
+
CLI-managed shell can share their live instances across edits. Build the
|
|
180
|
+
shell with `packaged: true`, and populate `__xplatDevModules` with the renderer
|
|
181
|
+
and Octane modules. The [independent development shell](test/fixtures/dev-shell.mjs)
|
|
182
|
+
shows the module mapping and retained-root HMR lifecycle.
|
|
183
|
+
|
|
184
|
+
```js
|
|
185
|
+
// vite.component.config.mjs
|
|
186
|
+
import { xplatMacOS } from '@octane-xplat/cli/macos/vite'
|
|
187
|
+
|
|
188
|
+
export default await xplatMacOS('development', {
|
|
189
|
+
packaged: false,
|
|
190
|
+
hmr: true,
|
|
191
|
+
entry: 'src/App.macos.tsx',
|
|
192
|
+
})
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Other preset options are `root` (defaults to cwd), `entry`, `outDir`, and
|
|
196
|
+
`rules`. Packaged output defaults to CommonJS; component output defaults to
|
|
197
|
+
ES modules. Component builds preserve the output directory so they do not
|
|
198
|
+
delete a shell sharing it; shell configurations should also preserve that
|
|
199
|
+
directory when used for development. Applications may override the component format to CommonJS for
|
|
200
|
+
the CLI watcher, as the maintained fixture does.
|
|
201
|
+
|
|
202
|
+
```js
|
|
203
|
+
import { xplatMacOS } from '@octane-xplat/cli/macos/vite'
|
|
204
|
+
|
|
205
|
+
const config = await xplatMacOS('development', {
|
|
206
|
+
root: process.cwd(),
|
|
207
|
+
entry: 'src/App.macos.tsx',
|
|
208
|
+
outDir: 'dist/dev',
|
|
209
|
+
packaged: false,
|
|
210
|
+
})
|
|
211
|
+
config.build = {
|
|
212
|
+
...config.build,
|
|
213
|
+
lib: { entry: 'src/App.macos.tsx', formats: ['cjs'], fileName: 'app' },
|
|
214
|
+
}
|
|
215
|
+
export default config
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Verify the package
|
|
219
|
+
|
|
220
|
+
From this repository:
|
|
221
|
+
|
|
222
|
+
```sh
|
|
223
|
+
pnpm --filter @octane-xplat/macos-renderer test
|
|
224
|
+
pnpm --filter @octane-xplat/macos-renderer test:packed
|
|
225
|
+
pnpm --filter @xplat/macos test:fonts
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The packed check uses the renderer and CLI tarballs in a temporary app outside
|
|
229
|
+
the checkout. It checks declarations and production compilation on every
|
|
230
|
+
host; on Apple Silicon macOS it also packages and launches the app and verifies
|
|
231
|
+
retained-root HMR. Programmatic action dispatch establishes handler behavior,
|
|
232
|
+
not OS input or hit-testing. Publication requires the repository's normal
|
|
233
|
+
[npm bootstrap and trusted-publisher setup](../../.agents/docs/releases.md).
|
package/package.json
CHANGED
|
@@ -1,11 +1,46 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octane-xplat/macos-renderer",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.10.0",
|
|
4
|
+
"description": "Experimental AppKit renderer for Octane on macOS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/octane-xplat/octane-xplat",
|
|
9
9
|
"directory": "packages/macos-renderer"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"src",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./src/index.d.ts",
|
|
19
|
+
"import": "./src/index.ts",
|
|
20
|
+
"default": "./src/index.ts"
|
|
21
|
+
},
|
|
22
|
+
"./intrinsics": "./src/intrinsics.d.ts",
|
|
23
|
+
"./jsx-runtime": {
|
|
24
|
+
"types": "./src/jsx-runtime.d.ts"
|
|
25
|
+
},
|
|
26
|
+
"./jsx-dev-runtime": {
|
|
27
|
+
"types": "./src/jsx-runtime.d.ts"
|
|
28
|
+
},
|
|
29
|
+
"./compat/native-core": "./src/native-core-shim.ts",
|
|
30
|
+
"./compat/escape-props": "./src/native-escape-props.ts"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@nativescript/macos-node-api": "0.4.4-next.2026-08-09-31292056208"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"octane": ">=0.6.3 <1"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"test": "node --test test/*.test.mjs",
|
|
43
|
+
"test:packed": "node test/packed-consumer.mjs",
|
|
44
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
10
45
|
}
|
|
11
|
-
}
|
|
46
|
+
}
|
package/src/appkit.d.ts
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Ambient host globals injected by @nativescript/macos-node-api and the
|
|
2
|
+
// embedding Node process. The prerelease ships no generated AppKit metadata
|
|
3
|
+
// types, so bridge classes expose members loosely while the type names stay
|
|
4
|
+
// usable in our own signatures.
|
|
5
|
+
|
|
6
|
+
type NSClass<T> = {
|
|
7
|
+
new (...args: any[]): T
|
|
8
|
+
alloc(): T
|
|
9
|
+
[key: string]: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Objective-C classes. Instances expose bridged members as `any`; the type
|
|
13
|
+
// name is real so parameters and node structs can reference them.
|
|
14
|
+
declare class NSObject {
|
|
15
|
+
[key: string]: any
|
|
16
|
+
static alloc(): any
|
|
17
|
+
static new(): any
|
|
18
|
+
static extend(methods: object, options?: object): any
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface NSAttributedString extends NSObject {}
|
|
22
|
+
declare const NSAttributedString: NSClass<NSAttributedString>
|
|
23
|
+
|
|
24
|
+
interface NSBundle extends NSObject {}
|
|
25
|
+
declare const NSBundle: NSClass<NSBundle>
|
|
26
|
+
|
|
27
|
+
interface NSError extends NSObject {}
|
|
28
|
+
declare const NSError: NSClass<NSError>
|
|
29
|
+
|
|
30
|
+
interface NSURLRequest extends NSObject {}
|
|
31
|
+
declare const NSURLRequest: NSClass<NSURLRequest>
|
|
32
|
+
|
|
33
|
+
// WebKit is dlopen'd at runtime by the webview leaf.
|
|
34
|
+
interface WKWebView extends NSView {}
|
|
35
|
+
declare const WKWebView: NSClass<WKWebView>
|
|
36
|
+
|
|
37
|
+
interface WKWebViewConfiguration extends NSObject {}
|
|
38
|
+
declare const WKWebViewConfiguration: NSClass<WKWebViewConfiguration>
|
|
39
|
+
|
|
40
|
+
interface NSButton extends NSView {}
|
|
41
|
+
declare const NSButton: NSClass<NSButton>
|
|
42
|
+
|
|
43
|
+
interface NSClickGestureRecognizer extends NSObject {}
|
|
44
|
+
declare const NSClickGestureRecognizer: NSClass<NSClickGestureRecognizer>
|
|
45
|
+
|
|
46
|
+
interface NSColor extends NSObject {}
|
|
47
|
+
declare const NSColor: NSClass<NSColor>
|
|
48
|
+
|
|
49
|
+
interface NSColorSpace extends NSObject {}
|
|
50
|
+
declare const NSColorSpace: NSClass<NSColorSpace>
|
|
51
|
+
|
|
52
|
+
interface NSData extends NSObject {}
|
|
53
|
+
declare const NSData: NSClass<NSData>
|
|
54
|
+
|
|
55
|
+
interface NSDate extends NSObject {}
|
|
56
|
+
declare const NSDate: NSClass<NSDate>
|
|
57
|
+
|
|
58
|
+
interface NSDatePicker extends NSView {}
|
|
59
|
+
declare const NSDatePicker: NSClass<NSDatePicker>
|
|
60
|
+
|
|
61
|
+
interface NSEdgeInsets extends NSObject {}
|
|
62
|
+
declare const NSEdgeInsets: NSClass<NSEdgeInsets>
|
|
63
|
+
|
|
64
|
+
interface NSEvent extends NSObject {}
|
|
65
|
+
declare const NSEvent: NSClass<NSEvent>
|
|
66
|
+
|
|
67
|
+
interface NSFont extends NSObject {}
|
|
68
|
+
declare const NSFont: NSClass<NSFont>
|
|
69
|
+
|
|
70
|
+
interface NSFontDescriptor extends NSObject {}
|
|
71
|
+
declare const NSFontDescriptor: NSClass<NSFontDescriptor>
|
|
72
|
+
|
|
73
|
+
interface NSFontManager extends NSObject {}
|
|
74
|
+
declare const NSFontManager: NSClass<NSFontManager>
|
|
75
|
+
|
|
76
|
+
interface NSImage extends NSObject {}
|
|
77
|
+
declare const NSImage: NSClass<NSImage>
|
|
78
|
+
|
|
79
|
+
interface NSImageView extends NSView {}
|
|
80
|
+
declare const NSImageView: NSClass<NSImageView>
|
|
81
|
+
|
|
82
|
+
interface NSMenu extends NSObject {}
|
|
83
|
+
declare const NSMenu: NSClass<NSMenu>
|
|
84
|
+
|
|
85
|
+
interface NSMenuItem extends NSObject {}
|
|
86
|
+
declare const NSMenuItem: NSClass<NSMenuItem>
|
|
87
|
+
|
|
88
|
+
interface NSMutableParagraphStyle extends NSObject {}
|
|
89
|
+
declare const NSMutableParagraphStyle: NSClass<NSMutableParagraphStyle>
|
|
90
|
+
|
|
91
|
+
interface NSNotification extends NSObject {}
|
|
92
|
+
declare const NSNotification: NSClass<NSNotification>
|
|
93
|
+
|
|
94
|
+
interface NSNotificationCenter extends NSObject {}
|
|
95
|
+
declare const NSNotificationCenter: NSClass<NSNotificationCenter>
|
|
96
|
+
|
|
97
|
+
interface NSNumber extends NSObject {}
|
|
98
|
+
declare const NSNumber: NSClass<NSNumber>
|
|
99
|
+
|
|
100
|
+
interface NSPanGestureRecognizer extends NSObject {}
|
|
101
|
+
declare const NSPanGestureRecognizer: NSClass<NSPanGestureRecognizer>
|
|
102
|
+
|
|
103
|
+
interface NSPopover extends NSObject {}
|
|
104
|
+
declare const NSPopover: NSClass<NSPopover>
|
|
105
|
+
|
|
106
|
+
interface NSScrollView extends NSView {}
|
|
107
|
+
declare const NSScrollView: NSClass<NSScrollView>
|
|
108
|
+
|
|
109
|
+
interface NSSlider extends NSView {}
|
|
110
|
+
declare const NSSlider: NSClass<NSSlider>
|
|
111
|
+
|
|
112
|
+
interface NSStackView extends NSView {}
|
|
113
|
+
declare const NSStackView: NSClass<NSStackView>
|
|
114
|
+
|
|
115
|
+
interface NSTextField extends NSView {}
|
|
116
|
+
declare const NSTextField: NSClass<NSTextField>
|
|
117
|
+
|
|
118
|
+
interface NSTextView extends NSView {}
|
|
119
|
+
declare const NSTextView: NSClass<NSTextView>
|
|
120
|
+
|
|
121
|
+
interface NSTrackingArea extends NSObject {}
|
|
122
|
+
declare const NSTrackingArea: NSClass<NSTrackingArea>
|
|
123
|
+
|
|
124
|
+
interface NSURL extends NSObject {}
|
|
125
|
+
declare const NSURL: NSClass<NSURL>
|
|
126
|
+
|
|
127
|
+
interface NSView extends NSObject {}
|
|
128
|
+
declare const NSView: NSClass<NSView>
|
|
129
|
+
|
|
130
|
+
interface NSViewController extends NSObject {}
|
|
131
|
+
declare const NSViewController: NSClass<NSViewController>
|
|
132
|
+
|
|
133
|
+
interface NSWindow extends NSObject {}
|
|
134
|
+
declare const NSWindow: NSClass<NSWindow>
|
|
135
|
+
|
|
136
|
+
// Enum containers. Members resolve to `number`, so bitwise masks compose.
|
|
137
|
+
declare const NSBackingStoreType: { [key: string]: number }
|
|
138
|
+
declare const NSBezelStyle: { [key: string]: number }
|
|
139
|
+
declare const NSBorderType: { [key: string]: number }
|
|
140
|
+
declare const NSButtonType: { [key: string]: number }
|
|
141
|
+
declare const NSDatePickerElementFlags: { [key: string]: number }
|
|
142
|
+
declare const NSDatePickerStyle: { [key: string]: number }
|
|
143
|
+
declare const NSLayoutAttribute: { [key: string]: number }
|
|
144
|
+
declare const NSPopoverBehavior: { [key: string]: number }
|
|
145
|
+
declare const NSRectEdge: { [key: string]: number }
|
|
146
|
+
declare const NSStackViewDistribution: { [key: string]: number }
|
|
147
|
+
declare const NSStackViewGravity: { [key: string]: number }
|
|
148
|
+
declare const NSTextAlignment: { [key: string]: number }
|
|
149
|
+
declare const NSTrackingAreaOptions: { [key: string]: number }
|
|
150
|
+
declare const NSUserInterfaceLayoutOrientation: { [key: string]: number }
|
|
151
|
+
declare const NSWindowStyleMask: { [key: string]: number }
|
|
152
|
+
|
|
153
|
+
// Standalone constants.
|
|
154
|
+
declare const NSImageScaleProportionallyDown: number
|
|
155
|
+
declare const NSImageScaleProportionallyUpOrDown: number
|
|
156
|
+
|
|
157
|
+
// NSString notification names and attributed-string keys.
|
|
158
|
+
declare const NSPopoverDidCloseNotification: string
|
|
159
|
+
declare const NSViewBoundsDidChangeNotification: string
|
|
160
|
+
declare const NSViewFrameDidChangeNotification: string
|
|
161
|
+
declare const NSFontAttributeName: string
|
|
162
|
+
declare const NSForegroundColorAttributeName: string
|
|
163
|
+
declare const NSParagraphStyleAttributeName: string
|
|
164
|
+
declare const NSURLErrorFailingURLErrorKey: string
|
|
165
|
+
|
|
166
|
+
// Protocol objects (opaque bridge references used in ObjCProtocols arrays).
|
|
167
|
+
declare const NSTextViewDelegate: any
|
|
168
|
+
declare const NSWindowDelegate: any
|
|
169
|
+
declare const WKNavigationDelegate: any
|
|
170
|
+
|
|
171
|
+
// Runtime framework loader injected by the bridge host.
|
|
172
|
+
declare function dlopen(path: string, flags: number): unknown
|
|
173
|
+
|
|
174
|
+
// Cross-package bridge published by this renderer for AppKit-specific leaves
|
|
175
|
+
// (hover, popups, menus, sheets) and the VirtualList offset channel.
|
|
176
|
+
declare var __xplatAppKit: {
|
|
177
|
+
observeHover?: (view: NSView, handlers: any) => void
|
|
178
|
+
showAnchoredPopup?: (options: any) => void
|
|
179
|
+
attachContextMenu?: (view: NSView, options: any, onSelect: any) => void
|
|
180
|
+
attachDatePicker?: (view: NSView, options: any, onChange: any) => void
|
|
181
|
+
presentSheet?: (view: NSView, options: any) => void
|
|
182
|
+
[key: string]: any
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
declare var __xplatVlistOffsets: { [key: string]: number } | undefined
|
|
186
|
+
|
|
187
|
+
// Node host globals.
|
|
188
|
+
declare const performance: { now(): number }
|
|
189
|
+
declare function setTimeout(callback: () => void, ms?: number): unknown
|
|
190
|
+
declare function clearTimeout(timer: unknown): void
|
|
191
|
+
declare function queueMicrotask(callback: () => void): void
|
|
192
|
+
declare const console: {
|
|
193
|
+
log(...args: any[]): void
|
|
194
|
+
warn(...args: any[]): void
|
|
195
|
+
error(...args: any[]): void
|
|
196
|
+
[key: string]: any
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
declare const process: { env: { [key: string]: string | undefined } }
|
package/src/fonts.ts
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
interface FontFace {
|
|
2
|
+
weight: number
|
|
3
|
+
descriptor: NSFontDescriptor
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
const families = new Map<string, FontFace[]>()
|
|
7
|
+
const weights = [
|
|
8
|
+
[100, -0.8, 1],
|
|
9
|
+
[200, -0.6, 2],
|
|
10
|
+
[300, -0.4, 3],
|
|
11
|
+
[400, 0, 5],
|
|
12
|
+
[500, 0.23, 6],
|
|
13
|
+
[600, 0.3, 8],
|
|
14
|
+
[700, 0.4, 9],
|
|
15
|
+
[800, 0.56, 10],
|
|
16
|
+
[900, 0.62, 12],
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
const aliases = new Map([
|
|
20
|
+
['thin', 100],
|
|
21
|
+
['extralight', 200],
|
|
22
|
+
['light', 300],
|
|
23
|
+
['normal', 400],
|
|
24
|
+
['regular', 400],
|
|
25
|
+
['medium', 500],
|
|
26
|
+
['semibold', 600],
|
|
27
|
+
['bold', 700],
|
|
28
|
+
['extrabold', 800],
|
|
29
|
+
['black', 900],
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
const systemFamilies = new Set(['system-ui', '-apple-system', 'sans-serif'])
|
|
33
|
+
|
|
34
|
+
/** Associate an app-owned family with native descriptors, without loading assets. */
|
|
35
|
+
export function registerFontFamily(family: string, faces: readonly FontFace[]) {
|
|
36
|
+
if (!family?.trim() || !faces?.length) {
|
|
37
|
+
throw new Error('A font family needs a name and at least one face')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const sorted = faces
|
|
41
|
+
.map(({ weight, descriptor }) => {
|
|
42
|
+
if (!Number.isFinite(weight) || !descriptor) {
|
|
43
|
+
throw new Error('A font face needs a finite weight and a native descriptor')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return { weight, descriptor }
|
|
47
|
+
})
|
|
48
|
+
.sort((left, right) => left.weight - right.weight)
|
|
49
|
+
|
|
50
|
+
const name = family.trim().toLowerCase()
|
|
51
|
+
if (systemFamilies.has(name)) {
|
|
52
|
+
throw new Error('System font family names cannot be registered')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
families.set(name, sorted)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Resolve an explicit family stack, falling back to the weighted system font. */
|
|
59
|
+
export function resolveFont(size: number, weight: number | string = 400, family?: string): NSFont {
|
|
60
|
+
const parsed = aliases.get(String(weight).toLowerCase()) ?? Number(weight)
|
|
61
|
+
const numeric = Number.isFinite(parsed) ? Math.min(900, Math.max(100, parsed)) : 400
|
|
62
|
+
const [, nativeWeight, managerWeight] = weights.find(([limit]) => numeric <= limit)!
|
|
63
|
+
const systemFont = () => {
|
|
64
|
+
const font = NSFont.systemFontOfSizeWeight(Number(size), nativeWeight)
|
|
65
|
+
if (!font) {
|
|
66
|
+
throw new Error('Failed to create the AppKit system font face')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return font
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const name of String(family ?? '').split(',')) {
|
|
73
|
+
const trimmed = name.trim().replace(/^['"]|['"]$/g, '')
|
|
74
|
+
if (!trimmed) {
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (systemFamilies.has(trimmed.toLowerCase())) {
|
|
79
|
+
return systemFont()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const faces = families.get(trimmed.toLowerCase())
|
|
83
|
+
if (faces) {
|
|
84
|
+
const face = faces.find((face) => numeric <= face.weight) ?? faces.at(-1)!
|
|
85
|
+
const font = NSFont.fontWithDescriptorSize(face.descriptor, Number(size))
|
|
86
|
+
if (font) {
|
|
87
|
+
return font
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
const manager = NSFontManager.sharedFontManager
|
|
91
|
+
const font = manager.fontWithFamilyTraitsWeightSize(trimmed, 0, managerWeight, Number(size))
|
|
92
|
+
|
|
93
|
+
if (font) {
|
|
94
|
+
return font
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return systemFont()
|
|
100
|
+
}
|
package/src/image.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Local and embedded sources avoid synchronous network work on AppKit's main thread. */
|
|
2
|
+
export function loadImage(source: unknown): NSImage | null {
|
|
3
|
+
const src = String(source ?? '')
|
|
4
|
+
const match = /^data:[^,]*;base64,(.+)$/s.exec(src)
|
|
5
|
+
if (match) {
|
|
6
|
+
const data = NSData.alloc().initWithBase64EncodedStringOptions(match[1], 0)
|
|
7
|
+
return data ? NSImage.alloc().initWithData(data) : null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (src.startsWith('file://')) {
|
|
11
|
+
return NSImage.alloc().initWithContentsOfURL(NSURL.URLWithString(src))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (src && !/^[a-z][a-z\d+.-]*:/i.test(src)) {
|
|
15
|
+
return NSImage.alloc().initWithContentsOfFile(src)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (src) {
|
|
19
|
+
console.warn('[macos-image] Unsupported source; use a base64 data URI or local file')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return null
|
|
23
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import '@nativescript/macos-node-api'
|
|
2
|
+
|
|
3
|
+
export * from 'octane/universal/native'
|
|
4
|
+
export interface MacOSRootOptions {
|
|
5
|
+
/** Default font family for this root and its renderer-hosted overlays. */
|
|
6
|
+
fontFamily?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Map an application-registered family to its weighted native faces. */
|
|
10
|
+
export declare function registerFontFamily(
|
|
11
|
+
family: string,
|
|
12
|
+
faces: readonly { weight: number; descriptor: NSFontDescriptor }[],
|
|
13
|
+
): void
|
|
14
|
+
|
|
15
|
+
export declare function createMacOSRoot(
|
|
16
|
+
hostView: NSView,
|
|
17
|
+
options?: MacOSRootOptions,
|
|
18
|
+
): import('octane/universal/native').UniversalRoot
|