create-octane-xplat 0.7.3 → 0.9.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.md +10 -5
- package/index.mjs +9 -1
- package/package.json +8 -8
- package/template/.agents/skills/xplat/SKILL.md +2 -2
- package/template/.agents/skills/xplat/references/platform.md +6 -2
- package/template/AGENTS.md +1 -1
- package/template/package.json +11 -9
- package/template/pnpm-workspace.yaml +52 -33
- package/template/src/App.tsrx +12 -3
- package/template/src/process.d.ts +7 -0
- package/template/tsconfig.json +1 -1
- package/template/vite.config.ts +5 -3
- package/template/App_Resources/iOS/src/OctaneShaders.metal +0 -128
- package/template/App_Resources/iOS/src/ShaderEffectRegistration.swift +0 -31
- package/template/patches/@nativescript-community__octane@0.2.1.patch +0 -16
- package/template/patches/@nativescript-community__vite-octane@0.2.1.patch +0 -41
- package/template/patches/@nativescript__core@9.1.2.patch +0 -794
- package/template/patches/@nativescript__core@9.1.3-next.2.patch +0 -428
- package/template/patches/@nativescript__vite@8.0.11.patch +0 -569
- package/template/patches/@nativescript__vite@8.0.13.patch +0 -233
- package/template/patches/@tsrx__typescript-plugin@0.4.11.patch +0 -11
- package/template/patches/css@3.0.0.patch +0 -52
- package/template/patches/octane@0.6.3.patch +0 -1149
- /package/template/src/{index.ts → main.ts} +0 -0
package/README.md
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
> Get a TypeScript app running, then build its first useful flow with your
|
|
4
4
|
> coding agent.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
starter configures **web, iOS, and Android**. macOS
|
|
8
|
-
|
|
6
|
+
Xplat's target direction spans web, iOS, Android, macOS, Windows, and Linux.
|
|
7
|
+
This starter configures **web, iOS, and Android**. macOS, Windows, and Linux
|
|
8
|
+
are separate experiments — an AppKit host, a WinUI 3 scaffold with runtime
|
|
9
|
+
verification pending, and a WebKitGTK webview host. See
|
|
9
10
|
[target support](https://octane-xplat.goddardai.org/spec#choose-your-targets).
|
|
10
11
|
The framework is `0.x` and its API surface is still changing.
|
|
11
12
|
|
|
12
|
-
With Node.js and pnpm installed:
|
|
13
|
+
With Node.js and pnpm 11.24.0 installed:
|
|
13
14
|
|
|
14
15
|
```sh
|
|
15
16
|
pnpm create octane-xplat my-app
|
|
@@ -25,6 +26,10 @@ clear the list in this in-memory version. The
|
|
|
25
26
|
[first-flow guide](https://octane-xplat.goddardai.org/toolchain#build-and-check-your-first-flow)
|
|
26
27
|
includes the full prompt and failure checks.
|
|
27
28
|
|
|
29
|
+
The generated app pins pnpm 11.24.0. Its `pnpm-workspace.yaml` loads
|
|
30
|
+
`@octane-xplat/patches` as a `configDependencies` package, making the
|
|
31
|
+
framework patches available during the first install.
|
|
32
|
+
|
|
28
33
|
Leave the server running while you edit. After stopping it, restart from the
|
|
29
34
|
app directory:
|
|
30
35
|
|
|
@@ -45,6 +50,6 @@ pnpm build # web production bundle
|
|
|
45
50
|
|
|
46
51
|
[Get a working app and iterate](https://octane-xplat.goddardai.org/toolchain)
|
|
47
52
|
provides prerequisites, live-update boundaries, and version guidance. The
|
|
48
|
-
starter's
|
|
53
|
+
starter's Xplat skill is included; NativeScript's official skills are optional
|
|
49
54
|
and installed separately. Ask your agent to report checks and targets actually
|
|
50
55
|
run. [llms.txt](https://octane-xplat.goddardai.org/llms.txt) indexes the docs.
|
package/index.mjs
CHANGED
|
@@ -4,7 +4,9 @@ import { dirname, join, resolve } from 'node:path'
|
|
|
4
4
|
import { fileURLToPath } from 'node:url'
|
|
5
5
|
import { spawnSync } from 'node:child_process'
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const positional = process.argv.slice(2).filter((arg) => !arg.startsWith('-'))
|
|
8
|
+
const noInstall = process.argv.includes('--no-install')
|
|
9
|
+
const dir = resolve(positional[0] ?? 'octane-xplat-app')
|
|
8
10
|
|
|
9
11
|
if (existsSync(dir) && readdirSync(dir).length > 0) {
|
|
10
12
|
console.error(`✗ ${dir} exists and is not empty`)
|
|
@@ -20,6 +22,12 @@ try {
|
|
|
20
22
|
|
|
21
23
|
console.log(`\n✓ scaffolded ${dir}`)
|
|
22
24
|
|
|
25
|
+
if (noInstall) {
|
|
26
|
+
console.log('\nSkipped install (--no-install). To run manually:')
|
|
27
|
+
console.log(` cd ${dir}\n pnpm install\n pnpm dev`)
|
|
28
|
+
process.exit(0)
|
|
29
|
+
}
|
|
30
|
+
|
|
23
31
|
const install = spawnSync('pnpm', ['install'], { cwd: dir, stdio: 'inherit' })
|
|
24
32
|
if (install.status !== 0) {
|
|
25
33
|
console.log(`\nInstall didn't finish (pnpm missing?). To run manually:`)
|
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-octane-xplat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Scaffold a minimal @octane-xplat/ui app — one Octane codebase for web + iOS + Android",
|
|
5
5
|
"keywords": [
|
|
6
|
-
"octane",
|
|
7
|
-
"xplat",
|
|
8
|
-
"cross-platform",
|
|
9
6
|
"create",
|
|
7
|
+
"cross-platform",
|
|
8
|
+
"init",
|
|
9
|
+
"octane",
|
|
10
|
+
"project-generator",
|
|
10
11
|
"scaffolding",
|
|
11
12
|
"starter-template",
|
|
12
|
-
"
|
|
13
|
-
"init"
|
|
13
|
+
"xplat"
|
|
14
14
|
],
|
|
15
|
-
"license": "MIT",
|
|
16
15
|
"homepage": "https://github.com/octane-xplat/octane-xplat/tree/main/packages/create",
|
|
16
|
+
"license": "MIT",
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|
|
19
|
-
"url": "
|
|
19
|
+
"url": "https://github.com/octane-xplat/octane-xplat",
|
|
20
20
|
"directory": "packages/create"
|
|
21
21
|
},
|
|
22
22
|
"bin": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: xplat
|
|
3
|
-
description: Build and review code in an Octane
|
|
3
|
+
description: Build and review code in an Octane Xplat app — one TypeScript codebase targeting web (DOM) and iOS/Android (NativeScript). Use when writing components, styles, routes, platform leaves, or device-service calls, and when a feature works on one target but not another.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Octane
|
|
6
|
+
# Octane Xplat app
|
|
7
7
|
|
|
8
8
|
Every `src/*.tsrx` file compiles twice: once to the DOM renderer, once to
|
|
9
9
|
NativeScript views. The common failure mode is **silent divergence** — code
|
|
@@ -7,8 +7,12 @@ native default, `.web` is the browser implementation, and `.mobile` or an OS
|
|
|
7
7
|
suffix narrows behavior when needed. Surface: `device`, `appInfo`, `locale`,
|
|
8
8
|
`connectivity`, `useWindowSize`, `useAppState`, `useSafeAreaInsets`, deep
|
|
9
9
|
links, clipboard, `secureStorage`, haptics, notifications, `announce`,
|
|
10
|
-
`systemBars`,
|
|
11
|
-
|
|
10
|
+
`systemBars`, permissions, `openUrl`, `openSettings`. Services that need a
|
|
11
|
+
NativeScript plugin ship as leaf packages: `@octane-xplat/share`,
|
|
12
|
+
`@octane-xplat/files`, `@octane-xplat/media`, `@octane-xplat/biometrics`,
|
|
13
|
+
`@octane-xplat/geolocation`, `@octane-xplat/notifications`,
|
|
14
|
+
`@octane-xplat/secure-storage`, and `haptics` in `@octane-xplat/haptics` —
|
|
15
|
+
each leaf owns its plugin as a real dependency.
|
|
12
16
|
|
|
13
17
|
Services report honestly — `biometrics` returns `unsupported` on web rather
|
|
14
18
|
than faking a result. Handle the tier your app can fall back to instead of
|
package/template/AGENTS.md
CHANGED
package/template/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "octane-xplat-app",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "src/
|
|
5
|
+
"main": "src/main.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"xplat": "xplat",
|
|
8
8
|
"dev": "vite",
|
|
@@ -17,11 +17,12 @@
|
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@nativescript-community/gesturehandler": "2.0.45",
|
|
20
|
-
"@nativescript-community/octane": "0.2.
|
|
20
|
+
"@nativescript-community/octane": "0.2.4",
|
|
21
21
|
"@nativescript-community/ui-drawer": "0.1.32",
|
|
22
22
|
"@nativescript/core": "9.1.2",
|
|
23
|
-
"@octane-xplat/effects": "^0.
|
|
24
|
-
"@octane-xplat/
|
|
23
|
+
"@octane-xplat/effects": "^0.9.0",
|
|
24
|
+
"@octane-xplat/platform": "^0.9.0",
|
|
25
|
+
"@octane-xplat/ui": "^0.9.0",
|
|
25
26
|
"@valor/nativescript-websockets": "2.0.3",
|
|
26
27
|
"octane": "0.6.3"
|
|
27
28
|
},
|
|
@@ -30,14 +31,15 @@
|
|
|
30
31
|
"@nativescript/android": "9.1.1",
|
|
31
32
|
"@nativescript/ios": "9.1.0",
|
|
32
33
|
"@nativescript/types": "9.0.0",
|
|
33
|
-
"@nativescript/vite": "8.0.
|
|
34
|
-
"@octane-xplat/cli": "^0.
|
|
35
|
-
"@octane-xplat/lint": "^0.
|
|
34
|
+
"@nativescript/vite": "8.0.17",
|
|
35
|
+
"@octane-xplat/cli": "^0.9.0",
|
|
36
|
+
"@octane-xplat/lint": "^0.9.0",
|
|
36
37
|
"@octanejs/vite-plugin": "0.1.61",
|
|
37
|
-
"@tsrx/typescript-plugin": "0.4.
|
|
38
|
+
"@tsrx/typescript-plugin": "0.4.11",
|
|
38
39
|
"nativescript": "9.1.1",
|
|
39
40
|
"oxlint": "^1.74.0",
|
|
40
41
|
"typescript": "5.9.3",
|
|
41
42
|
"vite": "8.3.0"
|
|
42
|
-
}
|
|
43
|
+
},
|
|
44
|
+
"packageManager": "pnpm@11.24.0"
|
|
43
45
|
}
|
|
@@ -14,14 +14,21 @@ allowBuilds:
|
|
|
14
14
|
'@parcel/watcher': true
|
|
15
15
|
nativescript: true
|
|
16
16
|
'@nativescript/core': false
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
|
|
17
|
+
# The patch set covers optional leaf deps (tiptap, linkifyjs, …) the starter
|
|
18
|
+
# doesn't install — tolerate entries that apply to nothing.
|
|
19
|
+
allowUnusedPatches: true
|
|
20
|
+
# Framework patches are supplied by the @octane-xplat/patches config dependency and
|
|
21
|
+
# applied via pnpm patchedDependencies. Managed upstream by @octane-xplat/cli (`xplat
|
|
22
|
+
# patches apply` refreshes, `xplat patches check` verifies); do not edit by hand.
|
|
20
23
|
patchedDependencies:
|
|
21
24
|
# Published octane 0.6.3 lacks live signal reads under universal renderers. Adds signal
|
|
22
25
|
# ownership, read tracking, and local signal hooks; JSX prop lowering and __methodDep
|
|
23
|
-
# already ship upstream.
|
|
24
|
-
|
|
26
|
+
# already ship upstream. Routes uncaught universal event-handler errors to onUncaughtError
|
|
27
|
+
# when configured. Pins instance signal/query declarations to their declaring owner so
|
|
28
|
+
# child reads and event handlers share one request. Also allows async and generator
|
|
29
|
+
# helpers that are not components in universal compiler modules, while preserving errors
|
|
30
|
+
# when those functions are used as components (octanejs/octane#1469).
|
|
31
|
+
octane@0.6.3: node_modules/.pnpm-config/@octane-xplat/patches/patches/octane@0.6.3.patch
|
|
25
32
|
# FlexboxLayout getChildMeasureSpec subtracts the child's own padding where AOSP subtracts
|
|
26
33
|
# parent padding + child margins — a column-stretched row re-measurement clamps padded
|
|
27
34
|
# children (37→17 dip, squished controls). Adds squircle (continuous) corner clipping for
|
|
@@ -35,45 +42,57 @@ patchedDependencies:
|
|
|
35
42
|
# frames whose ancestors present them (Frame.topmost stops returning hidden preloaded tab
|
|
36
43
|
# frames). Also maps CSS font-weight to the OpenType wght axis for custom Android fonts on
|
|
37
44
|
# API 26+, preserves explicit variation settings, and falls back to the prior file load
|
|
38
|
-
# when the axis is unsupported.
|
|
39
|
-
|
|
45
|
+
# when the axis is unsupported. Carries NativeScript PR #11481: preserves percentage
|
|
46
|
+
# translate values through parsing and resolves each axis against the view's own
|
|
47
|
+
# dimensions, including native views and animations.
|
|
48
|
+
"@nativescript/core@9.1.2": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript__core@9.1.2.patch
|
|
49
|
+
# @nativescript/types@9.0.0 omits ambient declarations for the native URL and
|
|
50
|
+
# URLSearchParams globals, so projects without DOM libraries cannot typecheck valid native
|
|
51
|
+
# URL code. Adds runtime-aligned declarations that merge with DOM declarations when both
|
|
52
|
+
# libraries are present.
|
|
53
|
+
"@nativescript/types@9.0.0": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript__types@9.0.0.patch
|
|
40
54
|
# @nativescript/vite serializes bundled CSS and dev app.css through reworkcss `css`@3.0.0,
|
|
41
55
|
# which mis-parses @layer into garbage selectors — rules die before reaching
|
|
42
56
|
# _populateRules. Adds atlayer() covering block and statement forms. Applies to a
|
|
43
57
|
# transitive dep — no app declares it directly.
|
|
44
|
-
css@3.0.0: patches/css@3.0.0.patch
|
|
45
|
-
# Dev-server
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
"@nativescript/vite@8.0.
|
|
58
|
+
css@3.0.0: node_modules/.pnpm-config/@octane-xplat/patches/patches/css@3.0.0.patch
|
|
59
|
+
# Dev-server fixes still missing after @nativescript/vite 8.0.17 added percent decoding
|
|
60
|
+
# for path-style /ns/m and Vue /ns/sfc requests (NativeScript#11483): collect re-export
|
|
61
|
+
# specifiers in the .vite/deps rewrite, route /@fs paths outside both roots through /ns/m
|
|
62
|
+
# (linked checkouts bypass the device transform upstream), and probe the non-active
|
|
63
|
+
# platform + `native` suffix tags in module candidates. The deps-bundle esbuild passes
|
|
64
|
+
# ignore vite resolve.alias, so a dep importing bare `octane` bundles the DOM runtime
|
|
65
|
+
# beside the universal one (hooks crash on boot, octane-xplat#3) — an onResolve plugin
|
|
66
|
+
# redirects `octane` → `octane/universal/native`. tsconfig-paths and the generated
|
|
67
|
+
# build-time checker moduleSuffixes also need the `.mobile` tier for iOS/Android/visionOS
|
|
68
|
+
# so `.mobile` leaves resolve under mapped imports and `ns build`.
|
|
69
|
+
"@nativescript/vite@8.0.17": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript__vite@8.0.17.patch
|
|
56
70
|
# Driver setProp skips `undefined` writes — NativeScript native setters coerce it (e.g.
|
|
57
71
|
# editable → ios.userInteractionEnabled = NO), leaving dead UI that still has its JS
|
|
58
|
-
# listeners.
|
|
59
|
-
|
|
72
|
+
# listeners. Advertises the implemented visibility capability so committed suspense bodies
|
|
73
|
+
# can be retained while pending.
|
|
74
|
+
"@nativescript-community/octane@0.2.4": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript-community__octane@0.2.4.patch
|
|
60
75
|
# octaneServerStrategy's SCRIPT_FILE_RE only covers [jt]sx? — .tsrx edits never emit the
|
|
61
76
|
# deferred ns:hmr-delta broadcast, so component HMR silently never reaches the device.
|
|
62
77
|
# Also counts open /ns-hmr sockets so the update summary stops reporting recipients=0 for
|
|
63
78
|
# delivered updates.
|
|
64
|
-
"@nativescript-community/vite-octane@0.2.1": patches/@nativescript-community__vite-octane@0.2.1.patch
|
|
65
|
-
# Same dev-server fixes as the 8.0.11 patch, ported to the @11468 pkg.pr.new preview the
|
|
66
|
-
# windows target uses (apps/windows): /ns/m re-export records, vendor-manifest gating for
|
|
67
|
-
# plugin-patterned packages, percent-decoded specifiers, and the .mobile-aware
|
|
68
|
-
# tsconfig-paths probing.
|
|
69
|
-
"@nativescript/vite@8.0.13": patches/@nativescript__vite@8.0.13.patch
|
|
70
|
-
# Windows spike: the shared-file subset of the 9.1.2 patch — @layer cascade machinery
|
|
71
|
-
# (css-tree parser, css-selector, style-scope), the #11446 frame/tab-view common fixes,
|
|
72
|
-
# and the flexbox common measure-spec — applied to the @11468 preview. iOS/Android
|
|
73
|
-
# impl-file hunks are dead code on windows and were dropped.
|
|
74
|
-
"@nativescript/core@9.1.3-next.2": patches/@nativescript__core@9.1.3-next.2.patch
|
|
79
|
+
"@nativescript-community/vite-octane@0.2.1": node_modules/.pnpm-config/@octane-xplat/patches/patches/@nativescript-community__vite-octane@0.2.1.patch
|
|
75
80
|
# Enables `resolveHiddenExtensions` on the language plugin's typescript facet so
|
|
76
81
|
# extensionless specifiers (e.g. './Text') probe `.tsrx` after moduleSuffixes — required
|
|
77
82
|
# for a single extensionless shared barrel to resolve per-platform leaves under tsrx-tsc
|
|
78
83
|
# and the IDE plugin.
|
|
79
|
-
"@tsrx/typescript-plugin@0.4.11": patches/@tsrx__typescript-plugin@0.4.11.patch
|
|
84
|
+
"@tsrx/typescript-plugin@0.4.11": node_modules/.pnpm-config/@octane-xplat/patches/patches/@tsrx__typescript-plugin@0.4.11.patch
|
|
85
|
+
# linkifyjs declares /\p{L}/u and /\p{Emoji}/u as regex literals at module scope;
|
|
86
|
+
# NativeScript's embedded V8 builds without ICU, so the literals throw SyntaxError at
|
|
87
|
+
# parse time and poison the whole bundle (any bundled app importing @tiptap/extension-link
|
|
88
|
+
# dies on boot). Rewrites them as try/catch new RegExp builds with ASCII-range fallbacks —
|
|
89
|
+
# same behavior where ICU exists, graceful degradation where it doesn't.
|
|
90
|
+
linkifyjs@4.3.3: node_modules/.pnpm-config/@octane-xplat/patches/patches/linkifyjs@4.3.3.patch
|
|
91
|
+
# Two bundler-visible issues: useEditor.ts reads bare process.env.NODE_ENV (no node global
|
|
92
|
+
# in the harness typecheck program) and './Context' resolves to Context.tsrx — a
|
|
93
|
+
# self-import — under suffix-first resolution, so EditorContext/useCurrentEditor were
|
|
94
|
+
# unreachable. Adds a module-local process declaration for typecheck and merges the
|
|
95
|
+
# Context.ts definitions into Context.tsrx.
|
|
96
|
+
"@octanejs/tiptap@0.0.51": node_modules/.pnpm-config/@octane-xplat/patches/patches/@octanejs__tiptap@0.0.51.patch
|
|
97
|
+
configDependencies:
|
|
98
|
+
"@octane-xplat/patches": 0.9.0
|
package/template/src/App.tsrx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useState } from 'octane';
|
|
2
2
|
import { View, Row, Text, Pressable, ScrollView, useColorScheme } from '@octane-xplat/ui';
|
|
3
|
+
import { storage } from '@octane-xplat/platform';
|
|
3
4
|
|
|
4
5
|
const FEATURES = [
|
|
5
6
|
'shared components',
|
|
@@ -13,8 +14,12 @@ const FEATURES = [
|
|
|
13
14
|
* (.web.tsrx / .mobile.tsrx), never inside a component. */
|
|
14
15
|
export function App() @{
|
|
15
16
|
const [count, setCount] = useState(0);
|
|
16
|
-
// null = follow the system
|
|
17
|
-
const [override, setOverride] = useState<boolean | null>(
|
|
17
|
+
// null = follow the system until the user chooses a mode.
|
|
18
|
+
const [override, setOverride] = useState<boolean | null>(() => {
|
|
19
|
+
const saved = storage.getString('theme-preference');
|
|
20
|
+
return saved === 'light' ? false : saved === 'dark' ? true : null;
|
|
21
|
+
});
|
|
22
|
+
|
|
18
23
|
const scheme = useColorScheme();
|
|
19
24
|
const dark = override ?? scheme === 'dark';
|
|
20
25
|
|
|
@@ -32,7 +37,11 @@ export function App() @{
|
|
|
32
37
|
{FEATURES.map((f) => <Text className="chip">{f}</Text>)}
|
|
33
38
|
</ScrollView>
|
|
34
39
|
|
|
35
|
-
<Pressable className="link" onPress={() =>
|
|
40
|
+
<Pressable className="link" onPress={() => {
|
|
41
|
+
const next = !dark;
|
|
42
|
+
storage.setString('theme-preference', next ? 'dark' : 'light');
|
|
43
|
+
setOverride(next);
|
|
44
|
+
}}>
|
|
36
45
|
<Text>{dark ? 'light' : 'dark'} mode (system: {scheme})</Text>
|
|
37
46
|
</Pressable>
|
|
38
47
|
</View>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// npm deps (upstream octane packages included) read `process.env.NODE_ENV`
|
|
2
|
+
// bare — the npm convention where the consumer bundler statically replaces
|
|
3
|
+
// the member expression (vite `define`; see xplatNodeEnvDefine). tsc still
|
|
4
|
+
// needs the name declared in a program with no Node types. Kept narrow on
|
|
5
|
+
// purpose: `process.env.FOO` reads stay type errors because the bundler
|
|
6
|
+
// define cannot rewrite them either.
|
|
7
|
+
declare const process: { env: { NODE_ENV?: string } }
|
package/template/tsconfig.json
CHANGED
package/template/vite.config.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { defineConfig } from 'vite'
|
|
2
2
|
import { octane } from '@octanejs/vite-plugin'
|
|
3
|
+
import { xplatBoundary, xplatNodeEnvDefine } from '@octane-xplat/cli/vite'
|
|
3
4
|
|
|
4
5
|
// The web build. For native (iOS/Android), the same source runs through
|
|
5
6
|
// @nativescript-community/vite-octane with `conditions: ['native']`, the
|
|
6
7
|
// .ios/.android/.mobile suffix chain, and the unsuffixed native default — see
|
|
7
8
|
// the octane-xplat repo's apps/mobile/vite.config.mts.
|
|
8
|
-
export default defineConfig({
|
|
9
|
-
plugins: [...octane()],
|
|
9
|
+
export default defineConfig(({ mode }) => ({
|
|
10
|
+
plugins: [...octane(), xplatBoundary('web')],
|
|
11
|
+
define: xplatNodeEnvDefine(mode),
|
|
10
12
|
server: { port: 5200 },
|
|
11
13
|
resolve: {
|
|
12
14
|
conditions: ['web'],
|
|
@@ -25,4 +27,4 @@ export default defineConfig({
|
|
|
25
27
|
'.json',
|
|
26
28
|
],
|
|
27
29
|
},
|
|
28
|
-
})
|
|
30
|
+
}))
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
#include <metal_stdlib>
|
|
2
|
-
#include <SwiftUI/SwiftUI_Metal.h>
|
|
3
|
-
using namespace metal;
|
|
4
|
-
|
|
5
|
-
// Stitchable shaders for SwiftUI's `.distortionEffect` / `.colorEffect` (iOS 17+).
|
|
6
|
-
// SwiftUI supplies the leading parameters — `position` for a distortion, plus
|
|
7
|
-
// `color` for a colour effect — and appends the arguments passed at the call
|
|
8
|
-
// site in `OctaneLogo.swift`.
|
|
9
|
-
|
|
10
|
-
/// Rising heat haze.
|
|
11
|
-
///
|
|
12
|
-
/// A distortion shader maps a destination pixel back to the source pixel it
|
|
13
|
-
/// should sample, so the returned offset reads as the image bending. Amplitude
|
|
14
|
-
/// is weighted by `base^2` (1 at the bottom, 0 at the top) so the mark shimmers
|
|
15
|
-
/// like air over hot metal instead of wobbling as a rigid block, and the two
|
|
16
|
-
/// waves use incommensurate frequencies so the pattern never visibly repeats.
|
|
17
|
-
[[ stitchable ]] float2 octaneHeatHaze(float2 position, float time, float2 size, float intensity) {
|
|
18
|
-
float2 uv = position / size;
|
|
19
|
-
float base = 1.0 - uv.y;
|
|
20
|
-
float wave = sin(uv.y * 26.0 - time * 3.1) * 0.6
|
|
21
|
-
+ sin(uv.y * 11.0 - time * 1.9 + uv.x * 4.0) * 0.4;
|
|
22
|
-
float amp = 2.4 * base * base * intensity;
|
|
23
|
-
return position + float2(wave * amp, 0.0);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/// Ember sheen sweeping diagonally across the ink.
|
|
27
|
-
///
|
|
28
|
-
/// SwiftUI hands colour effects premultiplied alpha, so the colour has to be
|
|
29
|
-
/// un-premultiplied before mixing and re-premultiplied on the way out —
|
|
30
|
-
/// blending straight into premultiplied RGB would bleed the highlight into the
|
|
31
|
-
/// transparent surround and halo the glyph edges.
|
|
32
|
-
[[ stitchable ]] half4 octaneSheen(float2 position, half4 color, float time, float2 size, float intensity) {
|
|
33
|
-
if (color.a < 0.004h) {
|
|
34
|
-
return color;
|
|
35
|
-
}
|
|
36
|
-
half3 rgb = color.rgb / color.a;
|
|
37
|
-
|
|
38
|
-
float2 uv = position / size;
|
|
39
|
-
float sweep = fract(time * 0.22);
|
|
40
|
-
float d = (uv.x * 0.85 + (1.0 - uv.y) * 0.35) - (sweep * 1.8 - 0.4);
|
|
41
|
-
float band = exp(-(d * d) * 42.0);
|
|
42
|
-
|
|
43
|
-
half3 ember = half3(1.0h, 0.255h, 0.353h); // the logo's #FF415A accent
|
|
44
|
-
half3 gold = half3(1.0h, 0.72h, 0.36h);
|
|
45
|
-
half3 hot = mix(ember, gold, half(band));
|
|
46
|
-
|
|
47
|
-
half glow = half(band) * half(intensity);
|
|
48
|
-
rgb = mix(rgb, hot, glow * 0.9h);
|
|
49
|
-
rgb += half3(glow * 0.25h);
|
|
50
|
-
|
|
51
|
-
return half4(saturate(rgb) * color.a, color.a);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/// Per-shard pseudo-random pair. Deterministic in the cell index, so a shard
|
|
55
|
-
/// keeps the same trajectory for the whole burst instead of jittering per frame.
|
|
56
|
-
static inline float2 shardNoise(float2 cell) {
|
|
57
|
-
float3 a = fract(float3(cell.xyx) * float3(0.1031, 0.1030, 0.0973));
|
|
58
|
-
a += dot(a, a.yzx + 33.33);
|
|
59
|
-
return fract(float2((a.x + a.y) * a.z, (a.x + a.z) * a.y));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/// Ignition burst: the mark shatters outward and reassembles.
|
|
63
|
-
///
|
|
64
|
-
/// A layer effect (rather than a distortion) because each shard has to read
|
|
65
|
-
/// from somewhere else in the image: sampling `position - offset` with an
|
|
66
|
-
/// offset that is constant across a grid cell translates that whole cell as a
|
|
67
|
-
/// rigid chip, which is what reads as breaking apart. A distortion shader can
|
|
68
|
-
/// only nudge its own pixel, so it smears instead of shattering.
|
|
69
|
-
///
|
|
70
|
-
/// `progress` runs 0 -> 1 once per burst and the displacement rides
|
|
71
|
-
/// `sin(p * pi)`, so the shards fly out and land back exactly where they
|
|
72
|
-
/// started — no separate reassembly pass, and the mark cannot get stranded
|
|
73
|
-
/// mid-explosion if a frame is dropped.
|
|
74
|
-
[[ stitchable ]] half4 octaneShatter(float2 position, SwiftUI::Layer layer, float2 size, float progress) {
|
|
75
|
-
if (progress <= 0.0001) {
|
|
76
|
-
return layer.sample(position);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
float p = clamp(progress, 0.0, 1.0);
|
|
80
|
-
float fly = sin(p * M_PI_F);
|
|
81
|
-
float ease = fly * fly;
|
|
82
|
-
|
|
83
|
-
float2 cells = float2(22.0, 5.0);
|
|
84
|
-
float2 cellSize = size / cells;
|
|
85
|
-
float2 rnd = shardNoise(floor(position / cellSize));
|
|
86
|
-
|
|
87
|
-
float2 centre = size * 0.5;
|
|
88
|
-
float2 radial = (position - centre) / max(size.x * 0.5, 1.0);
|
|
89
|
-
float2 dir = radial + (rnd - 0.5) * 0.9;
|
|
90
|
-
float len = max(length(dir), 0.0001);
|
|
91
|
-
dir /= len;
|
|
92
|
-
|
|
93
|
-
// Capped so the outermost shards stay inside the host view; anything
|
|
94
|
-
// travelling further is clipped by its bounds rather than fading out.
|
|
95
|
-
float2 offset = dir * mix(22.0, 70.0, rnd.x) * ease;
|
|
96
|
-
offset.y += (rnd.y - 0.5) * 42.0 * ease;
|
|
97
|
-
float2 src = position - offset;
|
|
98
|
-
|
|
99
|
-
// Chromatic split along the flight path — the shards read as moving fast
|
|
100
|
-
// rather than merely being displaced.
|
|
101
|
-
float split = ease * 3.5;
|
|
102
|
-
half4 cr = layer.sample(src + dir * split);
|
|
103
|
-
half4 cg = layer.sample(src);
|
|
104
|
-
half4 cb = layer.sample(src - dir * split);
|
|
105
|
-
half alpha = max(max(cr.a, cg.a), cb.a);
|
|
106
|
-
if (alpha < 0.004h) {
|
|
107
|
-
return half4(0.0h);
|
|
108
|
-
}
|
|
109
|
-
half3 rgb = half3(cr.r, cg.g, cb.b) / alpha;
|
|
110
|
-
|
|
111
|
-
// Expanding shockwave ring, brightest just after the tap.
|
|
112
|
-
float ring = exp(-pow((length(radial) - p * 1.5) * 5.0, 2.0)) * (1.0 - p);
|
|
113
|
-
|
|
114
|
-
half3 ember = half3(1.0h, 0.255h, 0.353h);
|
|
115
|
-
half3 gold = half3(1.0h, 0.78h, 0.35h);
|
|
116
|
-
half heat = half(ease);
|
|
117
|
-
// Ember-leaning, and only a partial mix: pushing all the way to `hot` turns
|
|
118
|
-
// every shard the same flat gold and the mark stops being recognisable
|
|
119
|
-
// mid-flight. Holding some ink back keeps them reading as burning pieces
|
|
120
|
-
// of the logo.
|
|
121
|
-
half3 hot = mix(ember, gold, heat * 0.75h);
|
|
122
|
-
|
|
123
|
-
rgb = mix(rgb, hot, heat * 0.6h);
|
|
124
|
-
rgb += half3(heat * 0.18h * half(rnd.x) + half(ring) * 0.45h);
|
|
125
|
-
|
|
126
|
-
alpha *= 1.0h - heat * 0.2h;
|
|
127
|
-
return half4(saturate(rgb) * alpha, alpha);
|
|
128
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import SwiftUI
|
|
2
|
-
|
|
3
|
-
// Registers the app's stitchable shaders (OctaneShaders.metal) by name for
|
|
4
|
-
// @octane-xplat/effects' <ShaderEffect>. Names are runtime strings because
|
|
5
|
-
// ShaderLibrary lookups are compile-time — registration is the dispatch
|
|
6
|
-
// table. Call `OctaneShaderEffects.install()` from JS before rendering a
|
|
7
|
-
// ShaderEffect (`declare const OctaneShaderEffects: any`).
|
|
8
|
-
//
|
|
9
|
-
// Pair-bound: this file + OctaneShaders.metal + the @octane-xplat/effects
|
|
10
|
-
// dependency go together — delete all three if you don't want shader
|
|
11
|
-
// effects.
|
|
12
|
-
@objc(OctaneShaderEffects)
|
|
13
|
-
public final class OctaneShaderEffects: NSObject {
|
|
14
|
-
@objc public static func install() {
|
|
15
|
-
XplatShaderEffectRegistry.register("heatHaze", kind: .distortion) { args in
|
|
16
|
-
ShaderLibrary.octaneHeatHaze(
|
|
17
|
-
.float(args.f("time")), .float2(args.size), .float(args.f("intensity"))
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
XplatShaderEffectRegistry.register("sheen", kind: .color) { args in
|
|
21
|
-
ShaderLibrary.octaneSheen(
|
|
22
|
-
.float(args.f("time")), .float2(args.size), .float(args.f("intensity"))
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
XplatShaderEffectRegistry.register("shatter", kind: .layer) { args in
|
|
26
|
-
ShaderLibrary.octaneShatter(
|
|
27
|
-
.float2(args.size), .float(args.f("progress"))
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
diff --git a/dist/driver.js b/dist/driver.js
|
|
2
|
-
index f485f992282c05c58fed810458303be3141f51c0..9b5a654cf0c32be5fc6da4ae5fec04a58560925f 100644
|
|
3
|
-
--- a/dist/driver.js
|
|
4
|
-
+++ b/dist/driver.js
|
|
5
|
-
@@ -83,6 +83,11 @@ function setProp(node, view, name, value) {
|
|
6
|
-
setRenderItem(view, value ?? null, createNativeScriptRoot);
|
|
7
|
-
return;
|
|
8
|
-
}
|
|
9
|
-
+ // `undefined` means "prop not supplied" — skip the write. NativeScript
|
|
10
|
-
+ // native setters coerce it (e.g. editable → ios.userInteractionEnabled =
|
|
11
|
-
+ // NO) leaving dead UI that still has its JS listeners.
|
|
12
|
-
+ if (value === undefined)
|
|
13
|
-
+ return;
|
|
14
|
-
// Core raises `<name>Change` for a script write exactly as for a user
|
|
15
|
-
// edit; a controlled input must not see its own value come back as one.
|
|
16
|
-
const echo = `${name}Change`;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
diff --git a/dist/server/strategy.js b/dist/server/strategy.js
|
|
2
|
-
index 7d84b37e2ed3a026da5b31c8a11027e0580877b2..f6594a913ba64837135e994cb183add13c6a0530 100644
|
|
3
|
-
--- a/dist/server/strategy.js
|
|
4
|
-
+++ b/dist/server/strategy.js
|
|
5
|
-
@@ -14,7 +14,9 @@ import { purgeTransformCachesForHotUpdate, runHotUpdatePrologue, typescriptServe
|
|
6
|
-
* otherwise the re-fetch can be served from the previous save's transform and
|
|
7
|
-
* the screen ends up one save behind.
|
|
8
|
-
*/
|
|
9
|
-
-const SCRIPT_FILE_RE = /\.(?:[mc]?[jt]sx?)$/i;
|
|
10
|
-
+// `.tsrx` is Octane's component extension — without it the deferred delta is
|
|
11
|
-
+// never emitted and component edits silently never reach the device.
|
|
12
|
-
+const SCRIPT_FILE_RE = /\.(?:[mc]?[jt]sx?|tsrx)$/i;
|
|
13
|
-
export const octaneServerStrategy = {
|
|
14
|
-
...typescriptServerStrategy,
|
|
15
|
-
flavor: 'octane',
|
|
16
|
-
@@ -24,7 +26,7 @@ export const octaneServerStrategy = {
|
|
17
|
-
if (!state)
|
|
18
|
-
return;
|
|
19
|
-
const { root, metrics, emitSummary } = state;
|
|
20
|
-
- const { moduleGraph, verbose, sharedTransformRequest } = deps;
|
|
21
|
-
+ const { moduleGraph, verbose, sharedTransformRequest, wss, isSocketClientOpen } = deps;
|
|
22
|
-
const { file, server } = ctx;
|
|
23
|
-
if (!SCRIPT_FILE_RE.test(file)) {
|
|
24
|
-
emitSummary();
|
|
25
|
-
@@ -71,6 +73,16 @@ export const octaneServerStrategy = {
|
|
26
|
-
const gm = moduleGraph.get(normalizedId);
|
|
27
|
-
if (gm)
|
|
28
|
-
moduleGraph.emitDelta([gm], []);
|
|
29
|
-
+ // The ns:hmr-delta broadcast IS the update delivery for this flavor —
|
|
30
|
-
+ // count open sockets so the always-on summary reflects reality
|
|
31
|
-
+ // instead of reporting recipients=0 for a delivered update.
|
|
32
|
-
+ try {
|
|
33
|
-
+ wss?.clients?.forEach((client) => {
|
|
34
|
-
+ if (isSocketClientOpen?.(client))
|
|
35
|
-
+ metrics.recipients += 1;
|
|
36
|
-
+ });
|
|
37
|
-
+ }
|
|
38
|
-
+ catch { }
|
|
39
|
-
}
|
|
40
|
-
catch (e) {
|
|
41
|
-
if (verbose)
|