lecodes-cli 0.19.0 → 0.19.2
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/dist/index.js +730 -235
- package/package.json +14 -13
- package/runtime/scene-harness.json +1 -1
- package/runtime/sdk-types.json +1 -1
- package/src/api.ts +302 -0
- package/src/browserAuth.ts +87 -0
- package/src/cmgenTool.ts +104 -0
- package/src/commands/app.ts +891 -0
- package/src/commands/appAndroid.ts +603 -0
- package/src/commands/appDesktop.ts +326 -0
- package/src/commands/appDesktopMac.ts +470 -0
- package/src/commands/appIcon.ts +187 -0
- package/src/commands/appShared.ts +448 -0
- package/src/commands/appTemplates.ts +883 -0
- package/src/commands/appTemplatesAndroid.ts +599 -0
- package/src/commands/appTemplatesGradlew.ts +9 -0
- package/src/commands/assets.ts +28 -0
- package/src/commands/clone.ts +60 -0
- package/src/commands/compile.ts +253 -0
- package/src/commands/create.ts +59 -0
- package/src/commands/design.ts +863 -0
- package/src/commands/designTemplates.ts +11 -0
- package/src/commands/desktop.ts +122 -0
- package/src/commands/dev.ts +214 -0
- package/src/commands/diff.ts +59 -0
- package/src/commands/init.ts +191 -0
- package/src/commands/install.ts +198 -0
- package/src/commands/lightmap.ts +290 -0
- package/src/commands/link.ts +147 -0
- package/src/commands/login.ts +24 -0
- package/src/commands/navmesh.ts +225 -0
- package/src/commands/pn.ts +305 -0
- package/src/commands/projectTemplates.ts +129 -0
- package/src/commands/pull.ts +109 -0
- package/src/commands/push.ts +173 -0
- package/src/commands/render.ts +414 -0
- package/src/commands/scene.ts +148 -0
- package/src/commands/shaders.ts +157 -0
- package/src/commands/status.ts +29 -0
- package/src/commands/test.ts +370 -0
- package/src/commands/thumbs.ts +178 -0
- package/src/commands/types.ts +87 -0
- package/src/commands/update.ts +190 -0
- package/src/compile/assetIcons.ts +214 -0
- package/src/compile/collect.ts +46 -0
- package/src/compile/collectLocal.ts +37 -0
- package/src/compile/designCompile.ts +104 -0
- package/src/compile/fonts.ts +156 -0
- package/src/compile/headlessBundle.ts +129 -0
- package/src/compile/nativeStack.ts +21 -0
- package/src/compile/projectCompile.ts +109 -0
- package/src/compile/sceneCompile.ts +113 -0
- package/src/compile/screenEntry.ts +127 -0
- package/src/compile/shaders.ts +245 -0
- package/src/config.ts +42 -0
- package/src/designMeta.ts +35 -0
- package/src/desktopRenderer.ts +532 -0
- package/src/desktopScript.ts +276 -0
- package/src/dev/clientTemplates.ts +160 -0
- package/src/dev/devServer.ts +292 -0
- package/src/dev/wsServer.ts +144 -0
- package/src/distRoot.ts +20 -0
- package/src/ignore.ts +163 -0
- package/src/index.ts +491 -0
- package/src/lecodes-3d-editor.d.ts +41 -0
- package/src/lecodes-assets.d.ts +7 -0
- package/src/lecodes-design.d.ts +191 -0
- package/src/lecodes-renderer.d.ts +131 -0
- package/src/localFiles.ts +144 -0
- package/src/manifest.ts +46 -0
- package/src/matcTool.ts +137 -0
- package/src/peers.ts +40 -0
- package/src/project.ts +61 -0
- package/src/projectEnv.ts +94 -0
- package/src/qrcode-terminal.d.ts +9 -0
- package/src/releases.ts +125 -0
- package/src/serverDiff.ts +78 -0
- package/src/textDiff.ts +103 -0
- package/src/types.ts +0 -0
- package/src/util.ts +146 -0
- package/runtime/sdk/animate/animate.ts +0 -238
- package/runtime/sdk/animate/bezier.ts +0 -138
- package/runtime/sdk/animate/easings.ts +0 -126
- package/runtime/sdk/canvas/Canvas.ts +0 -305
- package/runtime/sdk/compile/aspectMacro.ts +0 -86
- package/runtime/sdk/compile/assetIconMacro.ts +0 -384
- package/runtime/sdk/compile/assetMacro.ts +0 -146
- package/runtime/sdk/compile/assetName.ts +0 -50
- package/runtime/sdk/compile/bundler.ts +0 -287
- package/runtime/sdk/compile/compileProject.ts +0 -129
- package/runtime/sdk/compile/detectEntry.ts +0 -128
- package/runtime/sdk/compile/fontMacro.ts +0 -459
- package/runtime/sdk/compile/fontRegistry.ts +0 -78
- package/runtime/sdk/compile/header.ts +0 -67
- package/runtime/sdk/compile/index.ts +0 -100
- package/runtime/sdk/compile/libraryImports.ts +0 -52
- package/runtime/sdk/compile/liteMaterial.ts +0 -247
- package/runtime/sdk/compile/sceneEditor.ts +0 -88
- package/runtime/sdk/compile/serverSplit.ts +0 -233
- package/runtime/sdk/compile/serverTypes.ts +0 -227
- package/runtime/sdk/compile/sfnt.ts +0 -98
- package/runtime/sdk/compile/shaderSchema.ts +0 -202
- package/runtime/sdk/compile/shaderTargets.ts +0 -81
- package/runtime/sdk/compile/sourcemap.ts +0 -25
- package/runtime/sdk/core/Aspect.ts +0 -568
- package/runtime/sdk/core/InspectorUI.ts +0 -212
- package/runtime/sdk/core/StateMachine.ts +0 -308
- package/runtime/sdk/core/color.ts +0 -66
- package/runtime/sdk/core/compWrite.ts +0 -42
- package/runtime/sdk/core/events.ts +0 -38
- package/runtime/sdk/core/fields.ts +0 -120
- package/runtime/sdk/core/registry.ts +0 -23
- package/runtime/sdk/core/signals.ts +0 -277
- package/runtime/sdk/core/time.ts +0 -84
- package/runtime/sdk/g2/Camera2D.ts +0 -40
- package/runtime/sdk/g2/CharacterController2D.ts +0 -276
- package/runtime/sdk/g2/Node2D.ts +0 -267
- package/runtime/sdk/g2/OneWay2D.ts +0 -66
- package/runtime/sdk/g2/Physics2D.ts +0 -346
- package/runtime/sdk/g2/Scene2D.ts +0 -209
- package/runtime/sdk/g2/Shape2D.ts +0 -259
- package/runtime/sdk/g2/Sprite.ts +0 -89
- package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
- package/runtime/sdk/g2/SpriteSheet.ts +0 -166
- package/runtime/sdk/g2/Texture2D.ts +0 -47
- package/runtime/sdk/g2/Tilemap.ts +0 -41
- package/runtime/sdk/g2/Tileset.ts +0 -71
- package/runtime/sdk/g2/Trigger2D.ts +0 -77
- package/runtime/sdk/g2/autotile.ts +0 -433
- package/runtime/sdk/g2/cells.ts +0 -91
- package/runtime/sdk/g2/defineScene2d.ts +0 -381
- package/runtime/sdk/g2/groups2d.ts +0 -106
- package/runtime/sdk/g2/loop.ts +0 -50
- package/runtime/sdk/g2/scenarios2d.ts +0 -69
- package/runtime/sdk/g2/touch.ts +0 -83
- package/runtime/sdk/gl/Camera.ts +0 -160
- package/runtime/sdk/gl/CameraPlace.ts +0 -52
- package/runtime/sdk/gl/CharacterController.ts +0 -238
- package/runtime/sdk/gl/Gearbox.ts +0 -212
- package/runtime/sdk/gl/Geometry.ts +0 -279
- package/runtime/sdk/gl/IK.ts +0 -193
- package/runtime/sdk/gl/InstancedMesh.ts +0 -132
- package/runtime/sdk/gl/Light.ts +0 -107
- package/runtime/sdk/gl/Lightmap.ts +0 -256
- package/runtime/sdk/gl/Material.ts +0 -260
- package/runtime/sdk/gl/Mesh.ts +0 -83
- package/runtime/sdk/gl/Model.ts +0 -124
- package/runtime/sdk/gl/NavAgent.ts +0 -337
- package/runtime/sdk/gl/NavMesh.ts +0 -397
- package/runtime/sdk/gl/Node.ts +0 -350
- package/runtime/sdk/gl/Noise.ts +0 -30
- package/runtime/sdk/gl/Particles.ts +0 -676
- package/runtime/sdk/gl/Physics.ts +0 -222
- package/runtime/sdk/gl/Plane.ts +0 -53
- package/runtime/sdk/gl/Ragdoll.ts +0 -270
- package/runtime/sdk/gl/Ray.ts +0 -16
- package/runtime/sdk/gl/Scene.ts +0 -486
- package/runtime/sdk/gl/Shape.ts +0 -409
- package/runtime/sdk/gl/Terrain.ts +0 -1092
- package/runtime/sdk/gl/Texture.ts +0 -63
- package/runtime/sdk/gl/Trigger.ts +0 -45
- package/runtime/sdk/gl/Vehicle.ts +0 -473
- package/runtime/sdk/gl/Wheel.ts +0 -240
- package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
- package/runtime/sdk/gl/animation/Animator.ts +0 -120
- package/runtime/sdk/gl/animation/Layer.ts +0 -31
- package/runtime/sdk/gl/animation/Loop.ts +0 -30
- package/runtime/sdk/gl/animation/Playback.ts +0 -44
- package/runtime/sdk/gl/animation/core.ts +0 -336
- package/runtime/sdk/gl/controls.ts +0 -95
- package/runtime/sdk/gl/physicsEvents.ts +0 -20
- package/runtime/sdk/gl/scenarios.ts +0 -291
- package/runtime/sdk/gl/state.ts +0 -6
- package/runtime/sdk/gl/terrainMesh.ts +0 -219
- package/runtime/sdk/gl/touch.ts +0 -68
- package/runtime/sdk/inject.ts +0 -197
- package/runtime/sdk/math/Mathf.ts +0 -118
- package/runtime/sdk/math/mat4.ts +0 -278
- package/runtime/sdk/math/quat.ts +0 -232
- package/runtime/sdk/math/vec.ts +0 -255
- package/runtime/sdk/net/codec.ts +0 -119
- package/runtime/sdk/net/core.ts +0 -384
- package/runtime/sdk/net/index.ts +0 -181
- package/runtime/sdk/net/replication.ts +0 -622
- package/runtime/sdk/plugins/camera.ts +0 -81
- package/runtime/sdk/plugins/geolocation.ts +0 -123
- package/runtime/sdk/plugins/oauth.ts +0 -61
- package/runtime/sdk/plugins/permission.ts +0 -7
- package/runtime/sdk/plugins/push.ts +0 -132
- package/runtime/sdk/plugins/qr.ts +0 -73
- package/runtime/sdk/plugins/service.ts +0 -47
- package/runtime/sdk/runtime/app.ts +0 -101
- package/runtime/sdk/runtime/appEvents.ts +0 -54
- package/runtime/sdk/runtime/channel.ts +0 -61
- package/runtime/sdk/runtime/clipboard.ts +0 -20
- package/runtime/sdk/runtime/datetime.ts +0 -329
- package/runtime/sdk/runtime/device.ts +0 -293
- package/runtime/sdk/runtime/fetch.ts +0 -77
- package/runtime/sdk/runtime/files.ts +0 -23
- package/runtime/sdk/runtime/input.ts +0 -175
- package/runtime/sdk/runtime/media.ts +0 -111
- package/runtime/sdk/runtime/misc.ts +0 -16
- package/runtime/sdk/runtime/net.ts +0 -36
- package/runtime/sdk/runtime/rpc.ts +0 -218
- package/runtime/sdk/runtime/service.ts +0 -83
- package/runtime/sdk/runtime/share.ts +0 -9
- package/runtime/sdk/runtime/storage.ts +0 -13
- package/runtime/sdk/runtime/touch.ts +0 -76
- package/runtime/sdk/scene/defineScene.ts +0 -1353
- package/runtime/sdk/scene/editorPlugins.ts +0 -110
- package/runtime/sdk/scene/gizmos.ts +0 -148
- package/runtime/sdk/scene/grammar.ts +0 -120
- package/runtime/sdk/scene/material.ts +0 -188
- package/runtime/sdk/server/auth/appConfig.ts +0 -12
- package/runtime/sdk/server/auth/global.ts +0 -80
- package/runtime/sdk/server/auth/host.ts +0 -318
- package/runtime/sdk/server/auth/models.ts +0 -83
- package/runtime/sdk/server/auth/types.ts +0 -50
- package/runtime/sdk/server/channel.ts +0 -56
- package/runtime/sdk/server/context.ts +0 -36
- package/runtime/sdk/server/db/defineDb.ts +0 -237
- package/runtime/sdk/server/db/fields.ts +0 -132
- package/runtime/sdk/server/db/httpTransport.ts +0 -93
- package/runtime/sdk/server/db/index.ts +0 -7
- package/runtime/sdk/server/db/marci/query.ts +0 -412
- package/runtime/sdk/server/db/types.ts +0 -202
- package/runtime/sdk/server/errors.ts +0 -12
- package/runtime/sdk/server/host.ts +0 -74
- package/runtime/sdk/server/inject.ts +0 -13
- package/runtime/sdk/server/runtime.ts +0 -133
- package/runtime/sdk/server/validate.ts +0 -87
- package/runtime/sdk/ui/NativeView.ts +0 -142
- package/runtime/sdk/ui/UI.ts +0 -39
- package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
- package/runtime/sdk/ui/UIButton.ts +0 -101
- package/runtime/sdk/ui/UIContainer.ts +0 -60
- package/runtime/sdk/ui/UIImage.ts +0 -83
- package/runtime/sdk/ui/UIInput.ts +0 -185
- package/runtime/sdk/ui/UIModal.ts +0 -139
- package/runtime/sdk/ui/UINode.ts +0 -830
- package/runtime/sdk/ui/UIPager.ts +0 -362
- package/runtime/sdk/ui/UIPopover.ts +0 -100
- package/runtime/sdk/ui/UIScreen.ts +0 -123
- package/runtime/sdk/ui/UIScrollable.ts +0 -87
- package/runtime/sdk/ui/UISpacer.ts +0 -14
- package/runtime/sdk/ui/UITabs.ts +0 -236
- package/runtime/sdk/ui/UIText.ts +0 -51
- package/runtime/sdk/ui/UIVideo.ts +0 -88
- package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
- package/runtime/sdk/ui/UIWidget.ts +0 -127
- package/runtime/sdk/ui/fonts.ts +0 -13
- package/runtime/sdk/ui/presentable.ts +0 -117
- package/runtime/sdk/ui/router.ts +0 -132
- package/runtime/sdk/ui/theme.ts +0 -84
|
@@ -0,0 +1,599 @@
|
|
|
1
|
+
/* Templates for `lecodes app init android` / `lecodes app sync` — the Android mirror of
|
|
2
|
+
* appTemplates.ts, distilled from the LetaryCreatorViewer host (lecodes-android :app). The
|
|
3
|
+
* same three ownership tiers: app/ is USER-owned after generation (init writes once, sync
|
|
4
|
+
* never touches it — except app/src/main/assets/, the Resources/ analog, and values-only
|
|
5
|
+
* versionName/versionCode restamps in app/build.gradle.kts); settings/root gradle files,
|
|
6
|
+
* lecodes-runtime/ and plugins/ are CLI-owned (sync regenerates); the gradle wrapper is
|
|
7
|
+
* written once at init. Unlike the pbxproj there is no structural-edit problem — every
|
|
8
|
+
* CLI-owned file is plain text and fully regenerated. */
|
|
9
|
+
|
|
10
|
+
export const GRADLE_VERSION = "8.13"
|
|
11
|
+
export const AGP_VERSION = "8.13.2"
|
|
12
|
+
export const KOTLIN_VERSION = "2.1.0"
|
|
13
|
+
export const COMPILE_SDK = 36
|
|
14
|
+
export const MIN_SDK = 23
|
|
15
|
+
export const TARGET_SDK = 36
|
|
16
|
+
|
|
17
|
+
/** The fat AAR ships no POM (the ivy repository uses metadataSources { artifact() }), so the
|
|
18
|
+
* SDK's own dependencies are declared explicitly in the generated build files. Keep in sync
|
|
19
|
+
* with lecodes-android lecodes/build.gradle.kts per SDK release. */
|
|
20
|
+
export const SDK_RUNTIME_DEPS = [
|
|
21
|
+
"androidx.core:core-ktx:1.18.0",
|
|
22
|
+
"androidx.appcompat:appcompat:1.7.1",
|
|
23
|
+
"com.squareup.okhttp3:okhttp:4.12.0",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
/** Known app-level Gradle plugins a plugin manifest may request by bare id ("id:version" in
|
|
27
|
+
* the manifest pins explicitly; an unknown bare id is a hard error at sync). */
|
|
28
|
+
export const GRADLE_PLUGIN_VERSIONS: Record<string, string> = {
|
|
29
|
+
"com.google.gms.google-services": "4.5.0",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface AndroidShellIdentity {
|
|
33
|
+
displayName: string
|
|
34
|
+
applicationId: string
|
|
35
|
+
version: string
|
|
36
|
+
versionCode: number
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface AndroidRuntimePlugin { id: string, module: string, register: string }
|
|
40
|
+
|
|
41
|
+
/* ------------------------------ CLI-owned (regenerated) ------------------------------ */
|
|
42
|
+
|
|
43
|
+
export const settingsGradleTemplate = (slug: string, pluginModules: string[], aarWrapperPaths: string[], sdkRepoUrl: string): string => {
|
|
44
|
+
const insecure = sdkRepoUrl.startsWith("http://")
|
|
45
|
+
? "\n isAllowInsecureProtocol = true // LECODES_ANDROID_SDK_URL dev override"
|
|
46
|
+
: ""
|
|
47
|
+
const includes = pluginModules.map(m => `\ninclude(":plugins:${m}")`).join("")
|
|
48
|
+
+ aarWrapperPaths.map(p => `\ninclude("${p}")`).join("")
|
|
49
|
+
return `// Generated by \`lecodes app sync\` — regenerated on every sync, do not edit.
|
|
50
|
+
pluginManagement {
|
|
51
|
+
repositories {
|
|
52
|
+
google()
|
|
53
|
+
mavenCentral()
|
|
54
|
+
gradlePluginPortal()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
dependencyResolutionManagement {
|
|
58
|
+
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
|
|
59
|
+
repositories {
|
|
60
|
+
google()
|
|
61
|
+
mavenCentral()
|
|
62
|
+
// The LeCodes Android SDK: a fat AAR attached to GitHub Releases of the dist repo,
|
|
63
|
+
// resolved straight off the release-download URL pattern (no Maven registry). The
|
|
64
|
+
// content filter keeps Gradle from probing GitHub for every other coordinate.
|
|
65
|
+
ivy {
|
|
66
|
+
url = uri("${sdkRepoUrl}")
|
|
67
|
+
patternLayout { artifact("[revision]/[artifact]-[revision].[ext]") }
|
|
68
|
+
metadataSources { artifact() }${insecure}
|
|
69
|
+
content { includeGroup("io.letary") }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
rootProject.name = "${slug}"
|
|
74
|
+
include(":app")
|
|
75
|
+
include(":lecodes-runtime")${includes}
|
|
76
|
+
`
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const rootBuildGradleTemplate = (extraPlugins: { id: string, version: string }[]): string => {
|
|
80
|
+
const extras = extraPlugins.map(p => `\n id("${p.id}") version "${p.version}" apply false`).join("")
|
|
81
|
+
return `// Generated by \`lecodes app sync\` — regenerated on every sync, do not edit.
|
|
82
|
+
plugins {
|
|
83
|
+
id("com.android.application") version "${AGP_VERSION}" apply false
|
|
84
|
+
id("com.android.library") version "${AGP_VERSION}" apply false
|
|
85
|
+
id("org.jetbrains.kotlin.android") version "${KOTLIN_VERSION}" apply false${extras}
|
|
86
|
+
}
|
|
87
|
+
`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export const GRADLE_PROPERTIES = `# Generated by \`lecodes app sync\` — regenerated on every sync, do not edit.
|
|
91
|
+
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|
92
|
+
android.useAndroidX=true
|
|
93
|
+
android.nonTransitiveRClass=true
|
|
94
|
+
kotlin.code.style=official
|
|
95
|
+
`
|
|
96
|
+
|
|
97
|
+
export const gradleWrapperProperties = (): string => `distributionBase=GRADLE_USER_HOME
|
|
98
|
+
distributionPath=wrapper/dists
|
|
99
|
+
distributionUrl=https\\://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-bin.zip
|
|
100
|
+
networkTimeout=10000
|
|
101
|
+
validateDistributionUrl=true
|
|
102
|
+
zipStoreBase=GRADLE_USER_HOME
|
|
103
|
+
zipStorePath=wrapper/dists
|
|
104
|
+
`
|
|
105
|
+
|
|
106
|
+
export const runtimeBuildGradleTemplate = (sdkVersion: string, pluginModules: string[], sdkArtifact = "lecodes-sdk"): string => {
|
|
107
|
+
const deps = SDK_RUNTIME_DEPS.map(d => `\n api("${d}")`).join("")
|
|
108
|
+
const plugins = pluginModules.map(m => `\n api(project(":plugins:${m}"))`).join("")
|
|
109
|
+
return `// Generated by \`lecodes app sync\` — regenerated on every sync, do not edit.
|
|
110
|
+
// The SDK pin lives here (\`lecodes app update\` moves it); everything is \`api\` so app code
|
|
111
|
+
// sees the SDK and plugin classes through the single :lecodes-runtime dependency.
|
|
112
|
+
plugins {
|
|
113
|
+
id("com.android.library")
|
|
114
|
+
id("org.jetbrains.kotlin.android")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
android {
|
|
118
|
+
namespace = "io.letary.lecodes.runtime"
|
|
119
|
+
compileSdk = ${COMPILE_SDK}
|
|
120
|
+
defaultConfig {
|
|
121
|
+
minSdk = ${MIN_SDK}
|
|
122
|
+
}
|
|
123
|
+
compileOptions {
|
|
124
|
+
sourceCompatibility = JavaVersion.VERSION_11
|
|
125
|
+
targetCompatibility = JavaVersion.VERSION_11
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
kotlin {
|
|
130
|
+
compilerOptions {
|
|
131
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
dependencies {
|
|
136
|
+
// Exact pin — the release asset URL is derived from this artifact + version (see
|
|
137
|
+
// settings.gradle.kts). The artifact is the SDK variant sync picked (lecodes-sdk = full,
|
|
138
|
+
// -3d / -2d / -core = smaller engine builds); every variant vends the same Kotlin API.
|
|
139
|
+
api("io.letary:${sdkArtifact}:${sdkVersion}@aar")
|
|
140
|
+
// The fat AAR carries no POM, so its own dependencies ride here explicitly.${deps}${plugins}
|
|
141
|
+
}
|
|
142
|
+
`
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** lecodes-runtime/src/main/kotlin/io/letary/lecodes/runtime/LeCodesRuntime.kt — the generated
|
|
146
|
+
* registration + host-hook surface. Plugins are called by FQN (no import collisions); the
|
|
147
|
+
* activity hooks are always present and inert without a consuming plugin (the Android analog
|
|
148
|
+
* of iOS's buffered LeCodesAppHooks — here the only launch-time state, a notification tap's
|
|
149
|
+
* intent extra, survives until the host forwards it, so no buffering is needed). */
|
|
150
|
+
export const runtimeKotlinTemplate = (plugins: AndroidRuntimePlugin[], projectUuid?: string): string => {
|
|
151
|
+
const identity = projectUuid ? ` engine.bootProjectUuid = "${projectUuid}"\n` : ""
|
|
152
|
+
const calls = plugins.map(p => ` ${p.register}.register(engine, context)\n`).join("")
|
|
153
|
+
const body = identity + calls
|
|
154
|
+
const push = plugins.find(p => p.id === "push")
|
|
155
|
+
const pushNs = push ? push.register.split(".").slice(0, -1).join(".") : null
|
|
156
|
+
return `// Generated by \`lecodes app sync\` — regenerated on every sync, do not edit.
|
|
157
|
+
package io.letary.lecodes.runtime
|
|
158
|
+
|
|
159
|
+
import android.content.Context
|
|
160
|
+
import android.content.Intent
|
|
161
|
+
import io.letary.lecodes.LecodesEngine
|
|
162
|
+
|
|
163
|
+
object LeCodesRuntime {
|
|
164
|
+
|
|
165
|
+
/** Register the plugins installed via app.json. Call from Activity.onCreate BEFORE the
|
|
166
|
+
* engine runs the bundle — app code resolves plugin factories at synchronous top level. */
|
|
167
|
+
fun registerBundledPlugins(engine: LecodesEngine, context: Context) {
|
|
168
|
+
${body === "" ? " // no plugins installed\n" : body} }
|
|
169
|
+
|
|
170
|
+
/** Cold create only (savedInstanceState == null): forward the launch intent so a plugin
|
|
171
|
+
* can park launch-time state (a push notification tap) before the engine boots. */
|
|
172
|
+
fun onActivityLaunch(intent: Intent) {
|
|
173
|
+
${push ? ` ${pushNs}.PushPlugin.parkLaunchPayload(intent)\n` : " // no plugin consumes launch intents\n"} }
|
|
174
|
+
|
|
175
|
+
/** Warm intent (Activity.onNewIntent). @return true when a plugin consumed it — the
|
|
176
|
+
* caller must then SKIP its generic deep-link handling. */
|
|
177
|
+
fun onNewIntent(intent: Intent): Boolean {
|
|
178
|
+
${push ? ` return ${pushNs}.PushPlugin.handleTapIntent(intent)\n` : " return false\n"} }
|
|
179
|
+
|
|
180
|
+
/** Activity.onResume(true) / onPause(false). */
|
|
181
|
+
fun setForeground(foreground: Boolean) {
|
|
182
|
+
${push ? ` ${pushNs}.PushPlugin.setForeground(foreground)\n` : " // no plugin tracks foreground state\n"} }
|
|
183
|
+
}
|
|
184
|
+
`
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export const pluginBuildGradleTemplate = (
|
|
188
|
+
id: string, module: string, namespace: string, sdkVersion: string,
|
|
189
|
+
dependencies: string[], aarNames: string[], hasConsumerRules: boolean,
|
|
190
|
+
sdkArtifact = "lecodes-sdk",
|
|
191
|
+
): string => {
|
|
192
|
+
const consumer = hasConsumerRules ? `\n consumerProguardFiles("consumer-rules.pro")` : ""
|
|
193
|
+
// AGP forbids direct local .aar file deps in library modules — each rides in its own
|
|
194
|
+
// generated wrapper module (libs/<name>/) consumed as a project dependency.
|
|
195
|
+
const aars = aarNames.map(n => `\n implementation(project(":plugins:${module}:libs:${n.replace(/\.aar$/, "")}"))`).join("")
|
|
196
|
+
const sdkDeps = SDK_RUNTIME_DEPS.map(d => `\n implementation("${d}")`).join("")
|
|
197
|
+
const manifestDeps = dependencies.map(d => d.startsWith("platform:")
|
|
198
|
+
? `\n implementation(platform("${d.slice("platform:".length)}"))`
|
|
199
|
+
: `\n implementation("${d}")`).join("")
|
|
200
|
+
return `// Generated by \`lecodes app sync\` for plugin "${id}" — regenerated on every sync, do not edit.
|
|
201
|
+
plugins {
|
|
202
|
+
id("com.android.library")
|
|
203
|
+
id("org.jetbrains.kotlin.android")
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
android {
|
|
207
|
+
namespace = "${namespace}"
|
|
208
|
+
compileSdk = ${COMPILE_SDK}
|
|
209
|
+
defaultConfig {
|
|
210
|
+
minSdk = ${MIN_SDK}${consumer}
|
|
211
|
+
}
|
|
212
|
+
compileOptions {
|
|
213
|
+
sourceCompatibility = JavaVersion.VERSION_11
|
|
214
|
+
targetCompatibility = JavaVersion.VERSION_11
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
kotlin {
|
|
219
|
+
compilerOptions {
|
|
220
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
dependencies {
|
|
225
|
+
// Same artifact (SDK variant) as lecodes-runtime — two different variant coordinates in one
|
|
226
|
+
// shell would put two copies of the SDK classes on the runtime classpath.
|
|
227
|
+
implementation("io.letary:${sdkArtifact}:${sdkVersion}@aar")${sdkDeps}${aars}${manifestDeps}
|
|
228
|
+
}
|
|
229
|
+
`
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export const ANDROID_README = `# Android shell
|
|
233
|
+
|
|
234
|
+
Generated by \`lecodes app init android\`. Daily workflow:
|
|
235
|
+
|
|
236
|
+
\`\`\`
|
|
237
|
+
lecodes app sync # embed the compiled bundle + plugins into this shell
|
|
238
|
+
lecodes app open android # open in Android Studio
|
|
239
|
+
lecodes app build android # signed release APK + AAB (gradlew assembleRelease bundleRelease)
|
|
240
|
+
\`\`\`
|
|
241
|
+
|
|
242
|
+
## Ownership
|
|
243
|
+
|
|
244
|
+
| Path | Owner |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| \`app/\` (sources, manifest, res, build.gradle.kts) | **Yours** after init — sync never rewrites them (it only restamps \`versionName\`/\`versionCode\` values, manages \`// lecodes:plugin\`-tagged lines in the plugins block, and — when \`app.json\` sets \`"icon"\` — the icon files below) |
|
|
247
|
+
| \`app/src/main/assets/\` | CLI-owned — wiped and regenerated by every sync (the compiled \`app.js\` + its \`// preload:\` assets) |
|
|
248
|
+
| \`res/mipmap-*/ic_launcher*\`, \`res/values/lecodes_icon.xml\` | CLI-owned **only when** \`app.json\` sets \`"icon"\` — regenerated by every sync (plus a value-only \`android:icon\` restamp in the manifest); without the key sync never touches icons |
|
|
249
|
+
| \`settings.gradle.kts\`, \`build.gradle.kts\` (root), \`gradle.properties\`, \`lecodes-runtime/\`, \`plugins/\` | CLI-owned — regenerated by every sync |
|
|
250
|
+
| \`gradle/\`, \`gradlew\`, \`gradlew.bat\` | Written once at init |
|
|
251
|
+
|
|
252
|
+
## The SDK
|
|
253
|
+
|
|
254
|
+
\`lecodes-runtime/build.gradle.kts\` pins the exact \`io.letary:lecodes-sdk\` release, resolved
|
|
255
|
+
from the GitHub Releases of \`letary/lecodes-android-sdk\` through the ivy repository declared
|
|
256
|
+
in \`settings.gradle.kts\`. \`lecodes app update\` moves the pin (or set \`app.json\`
|
|
257
|
+
\`android.sdk\`). The SDK AAR ships no POM, so its own dependencies are declared explicitly in
|
|
258
|
+
the generated runtime build file.
|
|
259
|
+
|
|
260
|
+
## SDK variant
|
|
261
|
+
|
|
262
|
+
Sync links the smallest SDK artifact covering the engines the compiled bundle actually uses —
|
|
263
|
+
\`lecodes-sdk\` (full: 3D+2D), \`lecodes-sdk-3d\`, \`lecodes-sdk-2d\` or \`lecodes-sdk-core\`
|
|
264
|
+
(UI only) — so an app without 3D scenes doesn't ship Filament. If the project starts using an
|
|
265
|
+
engine, sync switches automatically on the next run; set \`android.variant\` in \`../app.json\`
|
|
266
|
+
to force one (needs lecodes-android-sdk >= 1.3.0; older releases ship only the full AAR).
|
|
267
|
+
|
|
268
|
+
Physics, navigation meshes and multiplayer ship in \`lecodes-sdk\` (full) ONLY —
|
|
269
|
+
\`lecodes-sdk-3d\` is 3D rendering without the game stack. Auto-selection reads which RENDERING
|
|
270
|
+
engines the bundle uses and cannot see those, so a project using \`Physics\`,
|
|
271
|
+
\`CharacterController\`, \`NavMesh\` or networking must set \`android.variant: "full"\` in
|
|
272
|
+
\`../app.json\` — otherwise those aspects silently do nothing on device.
|
|
273
|
+
|
|
274
|
+
## App-local native code
|
|
275
|
+
|
|
276
|
+
Drop Kotlin files into \`app/src/main/java/\` and register them in \`MainActivity\` after the
|
|
277
|
+
\`LeCodesRuntime.registerBundledPlugins(...)\` call — \`engine.registerCallback\` for sync
|
|
278
|
+
globals, \`engine.registerService\`/\`registerView\` for channels (see the SDK docs). Add any
|
|
279
|
+
dependencies they need to \`app/build.gradle.kts\` — that file is yours.
|
|
280
|
+
|
|
281
|
+
## Release signing
|
|
282
|
+
|
|
283
|
+
Create \`android/keystore.properties\` (gitignored):
|
|
284
|
+
|
|
285
|
+
\`\`\`
|
|
286
|
+
storeFile=release.keystore
|
|
287
|
+
storePassword=…
|
|
288
|
+
keyAlias=…
|
|
289
|
+
keyPassword=…
|
|
290
|
+
\`\`\`
|
|
291
|
+
|
|
292
|
+
or export \`LECODES_ANDROID_KEYSTORE_FILE\` / \`LECODES_ANDROID_KEYSTORE_PASSWORD\` /
|
|
293
|
+
\`LECODES_ANDROID_KEY_ALIAS\` / \`LECODES_ANDROID_KEY_PASSWORD\`. Never put passwords in app.json.
|
|
294
|
+
|
|
295
|
+
## Icons
|
|
296
|
+
|
|
297
|
+
The easy path: set \`app.json\` \`"icon"\` to a square ≥1024px PNG/JPEG in the project (e.g.
|
|
298
|
+
\`"icon": "icon.png"\`, optional \`"iconBackground": "#RRGGBB"\` — the adaptive-icon backdrop,
|
|
299
|
+
default white). Every \`lecodes app sync\` then renders the full launcher set: legacy density
|
|
300
|
+
PNGs, the API 26+ adaptive icon (your image at 72dp on the 108dp canvas), and an Android 13
|
|
301
|
+
themed-icon (monochrome) layer that reuses the foreground silhouette.
|
|
302
|
+
|
|
303
|
+
Prefer hand-made icons? Leave \`"icon"\` unset and use Android Studio (right-click \`res\` →
|
|
304
|
+
New → Image Asset) — sync never touches icon files then. \`drawable/ic_launcher.xml\` is only
|
|
305
|
+
the init-time placeholder.
|
|
306
|
+
|
|
307
|
+
## Notes
|
|
308
|
+
|
|
309
|
+
- No native OTA on Android yet — app.json \`"update"\` is ignored for this shell (iOS-only).
|
|
310
|
+
- Push: the notification service needs \`app/google-services.json\` from the le.codes Firebase
|
|
311
|
+
project; \`lecodes app sync\` applies the google-services Gradle plugin only when that file
|
|
312
|
+
exists (and warns otherwise).
|
|
313
|
+
`
|
|
314
|
+
|
|
315
|
+
/* ---------------------------- USER-owned (written once) ---------------------------- */
|
|
316
|
+
|
|
317
|
+
export const appBuildGradleTemplate = ({ applicationId, version, versionCode }: AndroidShellIdentity): string => `import java.util.Properties
|
|
318
|
+
|
|
319
|
+
// Generated by \`lecodes app init\` — yours to edit. \`lecodes app sync\` only restamps the
|
|
320
|
+
// versionName/versionCode VALUES and manages \`// lecodes:plugin\`-tagged lines in the plugins
|
|
321
|
+
// block below; everything else is left alone.
|
|
322
|
+
plugins {
|
|
323
|
+
id("com.android.application")
|
|
324
|
+
id("org.jetbrains.kotlin.android")
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Release signing: android/keystore.properties (gitignored) or LECODES_ANDROID_* env vars —
|
|
328
|
+
// see README.md. Without either, release builds come out unsigned.
|
|
329
|
+
val keystoreProps = Properties().apply {
|
|
330
|
+
val f = rootProject.file("keystore.properties")
|
|
331
|
+
if (f.exists()) f.inputStream().use { load(it) }
|
|
332
|
+
}
|
|
333
|
+
fun signing(key: String, env: String): String? = keystoreProps.getProperty(key) ?: System.getenv(env)
|
|
334
|
+
|
|
335
|
+
android {
|
|
336
|
+
namespace = "${applicationId}"
|
|
337
|
+
compileSdk = ${COMPILE_SDK}
|
|
338
|
+
|
|
339
|
+
defaultConfig {
|
|
340
|
+
applicationId = "${applicationId}"
|
|
341
|
+
minSdk = ${MIN_SDK}
|
|
342
|
+
targetSdk = ${TARGET_SDK}
|
|
343
|
+
versionCode = ${versionCode}
|
|
344
|
+
versionName = "${version}"
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
val storeFilePath = signing("storeFile", "LECODES_ANDROID_KEYSTORE_FILE")
|
|
348
|
+
if (storeFilePath != null) {
|
|
349
|
+
signingConfigs.create("release") {
|
|
350
|
+
storeFile = rootProject.file(storeFilePath)
|
|
351
|
+
storePassword = signing("storePassword", "LECODES_ANDROID_KEYSTORE_PASSWORD")
|
|
352
|
+
keyAlias = signing("keyAlias", "LECODES_ANDROID_KEY_ALIAS")
|
|
353
|
+
keyPassword = signing("keyPassword", "LECODES_ANDROID_KEY_PASSWORD")
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
buildTypes {
|
|
358
|
+
release {
|
|
359
|
+
isMinifyEnabled = false
|
|
360
|
+
signingConfig = signingConfigs.findByName("release")
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
compileOptions {
|
|
364
|
+
sourceCompatibility = JavaVersion.VERSION_11
|
|
365
|
+
targetCompatibility = JavaVersion.VERSION_11
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
kotlin {
|
|
370
|
+
compilerOptions {
|
|
371
|
+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
dependencies {
|
|
376
|
+
implementation(project(":lecodes-runtime"))
|
|
377
|
+
// App-local native code deps go here — this file is yours after init.
|
|
378
|
+
}
|
|
379
|
+
`
|
|
380
|
+
|
|
381
|
+
export const appManifestTemplate = (): string => `<?xml version="1.0" encoding="utf-8"?>
|
|
382
|
+
<!-- Generated by \`lecodes app init\` — yours to edit. Sync leaves it alone, except that with
|
|
383
|
+
app.json "icon" set it restamps the android:icon VALUE to the generated @mipmap set.
|
|
384
|
+
Plugin permissions/services arrive through the plugin modules' own manifests via the AGP
|
|
385
|
+
manifest merger, so installing/removing plugins needs no edits here. -->
|
|
386
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
387
|
+
|
|
388
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
389
|
+
|
|
390
|
+
<application
|
|
391
|
+
android:allowBackup="true"
|
|
392
|
+
android:icon="@drawable/ic_launcher"
|
|
393
|
+
android:label="@string/app_name"
|
|
394
|
+
android:supportsRtl="true"
|
|
395
|
+
android:theme="@style/Theme.LeCodesApp">
|
|
396
|
+
<!-- singleTask: a warm deep link re-enters the SAME instance via onNewIntent (the "url"
|
|
397
|
+
app event) instead of stacking a second activity with its own engine. -->
|
|
398
|
+
<activity
|
|
399
|
+
android:name=".MainActivity"
|
|
400
|
+
android:exported="true"
|
|
401
|
+
android:launchMode="singleTask"
|
|
402
|
+
android:windowSoftInputMode="adjustResize">
|
|
403
|
+
<intent-filter>
|
|
404
|
+
<action android:name="android.intent.action.MAIN" />
|
|
405
|
+
<category android:name="android.intent.category.LAUNCHER" />
|
|
406
|
+
</intent-filter>
|
|
407
|
+
|
|
408
|
+
<!-- le.codes/qr deep links (QR scans, notification taps). Editable — remove if the
|
|
409
|
+
app should not claim them. -->
|
|
410
|
+
<intent-filter android:autoVerify="true">
|
|
411
|
+
<action android:name="android.intent.action.VIEW" />
|
|
412
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
413
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
414
|
+
<data android:scheme="https" />
|
|
415
|
+
<data android:scheme="http" />
|
|
416
|
+
<data android:host="le.codes" />
|
|
417
|
+
<data android:pathPrefix="/qr" />
|
|
418
|
+
</intent-filter>
|
|
419
|
+
</activity>
|
|
420
|
+
</application>
|
|
421
|
+
|
|
422
|
+
</manifest>
|
|
423
|
+
`
|
|
424
|
+
|
|
425
|
+
export const mainActivityTemplate = (pkg: string): string => `//
|
|
426
|
+
// MainActivity.kt
|
|
427
|
+
// Generated by lecodes app init — yours to edit.
|
|
428
|
+
//
|
|
429
|
+
// Runs the bundled app.js (produced by \`lecodes app sync\`) offline: everything the script
|
|
430
|
+
// needs is provided by the SDK core plus the plugins registered via LeCodesRuntime.
|
|
431
|
+
//
|
|
432
|
+
|
|
433
|
+
package ${pkg}
|
|
434
|
+
|
|
435
|
+
import android.content.Intent
|
|
436
|
+
import android.os.Bundle
|
|
437
|
+
import android.view.KeyEvent
|
|
438
|
+
import androidx.appcompat.app.AppCompatActivity
|
|
439
|
+
import androidx.lifecycle.ViewModelProvider
|
|
440
|
+
import io.letary.lecodes.LeCodesView
|
|
441
|
+
import io.letary.lecodes.runtime.LeCodesRuntime
|
|
442
|
+
|
|
443
|
+
class MainActivity : AppCompatActivity() {
|
|
444
|
+
|
|
445
|
+
lateinit var viewModel: AppViewModel
|
|
446
|
+
|
|
447
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
448
|
+
super.onCreate(savedInstanceState)
|
|
449
|
+
|
|
450
|
+
viewModel = ViewModelProvider(this)[AppViewModel::class.java]
|
|
451
|
+
setContentView(LeCodesView(engine = viewModel.engine, context = applicationContext))
|
|
452
|
+
|
|
453
|
+
viewModel.engine.setLanguage(resources.configuration.locales[0]?.language ?: "en")
|
|
454
|
+
|
|
455
|
+
// Plugins BEFORE the engine runs the bundle — app code resolves plugin factories at
|
|
456
|
+
// synchronous top level.
|
|
457
|
+
LeCodesRuntime.registerBundledPlugins(viewModel.engine, applicationContext)
|
|
458
|
+
|
|
459
|
+
// A cold start from a notification tap parks its payload BEFORE the engine boots (inert
|
|
460
|
+
// without the push plugin). Only on a fresh creation — a recreation must not re-park.
|
|
461
|
+
if (savedInstanceState == null) {
|
|
462
|
+
LeCodesRuntime.onActivityLaunch(intent)
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// BEFORE onActivityCreate: the bundle reads the launch link synchronously at boot.
|
|
466
|
+
viewModel.engine.setLaunchUrl(intent.data?.toString())
|
|
467
|
+
|
|
468
|
+
viewModel.onActivityCreate(this)
|
|
469
|
+
// System back is wired inside engine.attach() (SDK >= 1.2.0): widget → destination →
|
|
470
|
+
// pager → router; an unhandled press is swallowed. To leave the app on unhandled back
|
|
471
|
+
// instead: viewModel.engine.onUnhandledBackPressed = { moveTaskToBack(true) }
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Physical-key state for Input.key(). Record, never consume.
|
|
475
|
+
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
|
476
|
+
viewModel.engine.handleKeyEvent(event)
|
|
477
|
+
return super.dispatchKeyEvent(event)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
override fun onResume() {
|
|
481
|
+
super.onResume()
|
|
482
|
+
LeCodesRuntime.setForeground(true)
|
|
483
|
+
viewModel.engine.resume()
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
override fun onPause() {
|
|
487
|
+
super.onPause()
|
|
488
|
+
LeCodesRuntime.setForeground(false)
|
|
489
|
+
viewModel.engine.pause()
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// Warm deep link (the activity is singleTask). A consumed intent (a notification tap the
|
|
493
|
+
// push plugin routed) must SKIP the generic path — a same-project tap must not reload the
|
|
494
|
+
// running app through its own link.
|
|
495
|
+
override fun onNewIntent(intent: Intent) {
|
|
496
|
+
super.onNewIntent(intent)
|
|
497
|
+
if (LeCodesRuntime.onNewIntent(intent)) return
|
|
498
|
+
val url = intent.data?.toString() ?: return
|
|
499
|
+
viewModel.engine.emitUrl(url)
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
`
|
|
503
|
+
|
|
504
|
+
export const appViewModelTemplate = (pkg: string): string => `//
|
|
505
|
+
// AppViewModel.kt
|
|
506
|
+
// Generated by lecodes app init — yours to edit.
|
|
507
|
+
//
|
|
508
|
+
// The engine lives here so it survives configuration changes (rotation recreates the
|
|
509
|
+
// Activity, not the ViewModel); onCleared is the one "never coming back" signal where the
|
|
510
|
+
// engine's system registrations are released.
|
|
511
|
+
//
|
|
512
|
+
|
|
513
|
+
package ${pkg}
|
|
514
|
+
|
|
515
|
+
import android.app.Activity
|
|
516
|
+
import android.app.Application
|
|
517
|
+
import androidx.lifecycle.AndroidViewModel
|
|
518
|
+
import io.letary.lecodes.LecodesEngine
|
|
519
|
+
|
|
520
|
+
class AppViewModel(application: Application) : AndroidViewModel(application) {
|
|
521
|
+
|
|
522
|
+
val engine: LecodesEngine = LecodesEngine(application.applicationContext)
|
|
523
|
+
|
|
524
|
+
init {
|
|
525
|
+
// \`// preload:\` assets (images, fonts) resolve out of app/src/main/assets/ —
|
|
526
|
+
// \`lecodes app sync\` copies them there flat by name.
|
|
527
|
+
engine.onFetchLocal = { path ->
|
|
528
|
+
getApplication<Application>().assets.open(path).use { it.readBytes() }
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
override fun onCleared() {
|
|
533
|
+
engine.dispose()
|
|
534
|
+
super.onCleared()
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
private var isCreated = false
|
|
538
|
+
fun onActivityCreate(activity: Activity) {
|
|
539
|
+
engine.attach(activity)
|
|
540
|
+
if (!isCreated) {
|
|
541
|
+
val code = getApplication<Application>().assets.open("app.js").bufferedReader().use { it.readText() }
|
|
542
|
+
engine.run(code)
|
|
543
|
+
isCreated = true
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
`
|
|
548
|
+
|
|
549
|
+
export const themesXmlTemplate = (): string => `<resources>
|
|
550
|
+
<style name="Theme.LeCodesApp" parent="Theme.AppCompat.Light.NoActionBar">
|
|
551
|
+
<item name="android:windowTranslucentNavigation">true</item>
|
|
552
|
+
<item name="android:statusBarColor">@android:color/transparent</item>
|
|
553
|
+
<item name="android:windowNoTitle">true</item>
|
|
554
|
+
<item name="android:windowTranslucentStatus">true</item>
|
|
555
|
+
<item name="android:windowBackground">@color/black</item>
|
|
556
|
+
</style>
|
|
557
|
+
</resources>
|
|
558
|
+
`
|
|
559
|
+
|
|
560
|
+
export const COLORS_XML = `<resources>
|
|
561
|
+
<color name="black">#FF000000</color>
|
|
562
|
+
</resources>
|
|
563
|
+
`
|
|
564
|
+
|
|
565
|
+
export const stringsXmlTemplate = (name: string): string => `<resources>
|
|
566
|
+
<string name="app_name">${name.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "\\'")}</string>
|
|
567
|
+
</resources>
|
|
568
|
+
`
|
|
569
|
+
|
|
570
|
+
/** Placeholder launcher icon — a plain vector (valid from API 21, no adaptive/mipmap set, no
|
|
571
|
+
* binary assets). README points at Android Studio's Image Asset tool for the real one. */
|
|
572
|
+
export const IC_LAUNCHER_XML = `<?xml version="1.0" encoding="utf-8"?>
|
|
573
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
574
|
+
android:width="108dp"
|
|
575
|
+
android:height="108dp"
|
|
576
|
+
android:viewportWidth="108"
|
|
577
|
+
android:viewportHeight="108">
|
|
578
|
+
<path
|
|
579
|
+
android:fillColor="#FF000000"
|
|
580
|
+
android:pathData="M0,0h108v108h-108z"/>
|
|
581
|
+
<path
|
|
582
|
+
android:fillColor="#FFFFFFFF"
|
|
583
|
+
android:pathData="M34,38 h40 a4,4 0 0 1 4,4 v24 a4,4 0 0 1 -4,4 h-40 a4,4 0 0 1 -4,-4 v-24 a4,4 0 0 1 4,-4 z M38,44 v20 h32 v-20 z"/>
|
|
584
|
+
</vector>
|
|
585
|
+
`
|
|
586
|
+
|
|
587
|
+
export const ANDROID_GITIGNORE = `.gradle/
|
|
588
|
+
build/
|
|
589
|
+
local.properties
|
|
590
|
+
keystore.properties
|
|
591
|
+
*.keystore
|
|
592
|
+
`
|
|
593
|
+
|
|
594
|
+
/** libs/<name>/build.gradle.kts — wraps a plugin's local AAR as a consumable artifact
|
|
595
|
+
* (the AGP-supported alternative to direct local .aar file deps in library modules). */
|
|
596
|
+
export const aarWrapperBuildGradle = (aarName: string): string => `// Generated by \`lecodes app sync\` — regenerated on every sync, do not edit.
|
|
597
|
+
configurations.maybeCreate("default")
|
|
598
|
+
artifacts.add("default", file("${aarName}"))
|
|
599
|
+
`
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* Gradle wrapper launcher scripts, verbatim from the gradle/gradle v8.13.0 tag (the same
|
|
2
|
+
* pinned source as the wrapper jar download in appAndroid.ts). JSON-escaped mechanically —
|
|
3
|
+
* regenerate from the tag rather than editing. gradlew must be written with LF and
|
|
4
|
+
* gradlew.bat with CRLF (initAndroid handles the conversion); both are version-agnostic
|
|
5
|
+
* launchers that read gradle/wrapper/gradle-wrapper.properties for the real distribution. */
|
|
6
|
+
|
|
7
|
+
export const GRADLEW_SH: string = "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n# Gradle start up script for POSIX generated by Gradle.\n#\n# Important for running:\n#\n# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n# noncompliant, but you have some other compliant shell such as ksh or\n# bash, then to run this script, type that shell name before the whole\n# command line, like:\n#\n# ksh Gradle\n#\n# Busybox and similar reduced shells will NOT work, because this script\n# requires all of these POSIX shell features:\n# * functions;\n# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n# «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n# * compound commands having a testable exit status, especially «case»;\n# * various built-in commands including «command», «set», and «ulimit».\n#\n# Important for patching:\n#\n# (2) This script targets any POSIX shell, so it avoids extensions provided\n# by Bash, Ksh, etc; in particular arrays are avoided.\n#\n# The \"traditional\" practice of packing multiple parameters into a\n# space-separated string is a well documented source of bugs and security\n# problems, so this is (mostly) avoided, by progressively accumulating\n# options in \"$@\", and eventually passing that to Java.\n#\n# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n# see the in-line comments for details.\n#\n# There are tweaks for specific operating systems such as AIX, CygWin,\n# Darwin, MinGW, and NonStop.\n#\n# (3) This script is generated from the Groovy template\n# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n# within the Gradle project.\n#\n# You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n APP_HOME=${app_path%\"${app_path##*/}\"} # leaves a trailing /; empty if no leading path\n [ -h \"$app_path\" ]\ndo\n ls=$( ls -ld \"$app_path\" )\n link=${ls#*' -> '}\n case $link in #(\n /*) app_path=$link ;; #(\n *) app_path=$APP_HOME$link ;;\n esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n echo \"$*\"\n} >&2\n\ndie () {\n echo\n echo \"$*\"\n echo\n exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in #(\n CYGWIN* ) cygwin=true ;; #(\n Darwin* ) darwin=true ;; #(\n MSYS* | MINGW* ) msys=true ;; #(\n NONSTOP* ) nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n # IBM's JDK on AIX uses strange locations for the executables\n JAVACMD=$JAVA_HOME/jre/sh/java\n else\n JAVACMD=$JAVA_HOME/bin/java\n fi\n if [ ! -x \"$JAVACMD\" ] ; then\n die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n fi\nelse\n JAVACMD=java\n if ! command -v java >/dev/null 2>&1\n then\n die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n case $MAX_FD in #(\n max*)\n # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n # shellcheck disable=SC2039,SC3045\n MAX_FD=$( ulimit -H -n ) ||\n warn \"Could not query maximum file descriptor limit\"\n esac\n case $MAX_FD in #(\n '' | soft) :;; #(\n *)\n # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n # shellcheck disable=SC2039,SC3045\n ulimit -n \"$MAX_FD\" ||\n warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n# * args from the command line\n# * the main class name\n# * -classpath\n# * -D...appname settings\n# * --module-path (only if needed)\n# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n # Now convert the arguments - kludge to limit ourselves to /bin/sh\n for arg do\n if\n case $arg in #(\n -*) false ;; # don't mess with options #(\n /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath\n [ -e \"$t\" ] ;; #(\n *) false ;;\n esac\n then\n arg=$( cygpath --path --ignore --mixed \"$arg\" )\n fi\n # Roll the args list around exactly as many times as the number of\n # args, so each arg winds up back in the position where it started, but\n # possibly modified.\n #\n # NB: a `for` loop captures its iteration list before it begins, so\n # changing the positional parameters here affects neither the number of\n # iterations, nor the values presented in `arg`.\n shift # remove old arg\n set -- \"$@\" \"$arg\" # push replacement arg\n done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='-Dfile.encoding=UTF-8 \"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n# and any embedded shellness will be escaped.\n# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n# treated as '${Hostname}' itself on the command line.\n\nset -- \\\n \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n -classpath \"$CLASSPATH\" \\\n org.gradle.wrapper.GradleWrapperMain \\\n \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n# readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n# set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n xargs -n1 |\n sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n tr '\\n' ' '\n )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
|
|
8
|
+
|
|
9
|
+
export const GRADLEW_BAT: string = "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n@rem SPDX-License-Identifier: Apache-2.0\n@rem\n\n@if \"%DEBUG%\"==\"\" @echo off\n@rem ##########################################################################\n@rem\n@rem Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\n@rem This is normally unused\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=-Dfile.encoding=UTF-8 \"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif %ERRORLEVEL% equ 0 goto execute\n\necho. 1>&2\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\necho. 1>&2\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\necho location of your Java installation. 1>&2\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto execute\n\necho. 1>&2\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\necho. 1>&2\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\necho location of your Java installation. 1>&2\n\ngoto fail\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\n\n:end\n@rem End local scope for the variables with windows NT shell\nif %ERRORLEVEL% equ 0 goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nset EXIT_CODE=%ERRORLEVEL%\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\nexit /b %EXIT_CODE%\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CliError, type Args } from "../util"
|
|
2
|
+
import { resolveCmgenExe } from "../cmgenTool"
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* `lecodes assets <convert|probe|doctor|sky> …` — the LeCodes asset pipeline (packages/lecodes-assets):
|
|
6
|
+
* FBX → GLB (ufbx wasm + glTF-Transform), `--clips` merged by bone name, textures embedded, GLB
|
|
7
|
+
* doctor (bone cap / texture normalize / KTX2). The package is an optional peer (like the design
|
|
8
|
+
* board and the renderer); its own argv parser handles the sub-command flags, so the raw argv is
|
|
9
|
+
* forwarded untouched (`assets` gets `_raw` from main). See docs/animation-plan.md §2.9.
|
|
10
|
+
*/
|
|
11
|
+
export const assets = async (args: Args & { _raw?: string[] }) => {
|
|
12
|
+
let mod: typeof import("lecodes-assets/cli")
|
|
13
|
+
try {
|
|
14
|
+
mod = await import("lecodes-assets/cli")
|
|
15
|
+
} catch {
|
|
16
|
+
throw new CliError("The asset pipeline isn't installed. Run: npm install -g lecodes-assets")
|
|
17
|
+
}
|
|
18
|
+
const raw = args._raw ?? args._
|
|
19
|
+
// `sky` shells out to filament's cmgen, which the asset package deliberately never downloads
|
|
20
|
+
// itself (it stays network-free and usable standalone). The CLI owns release artifacts, so it
|
|
21
|
+
// resolves the binary — cache or first-use download — and hands it over through the env var the
|
|
22
|
+
// package already honours. An explicit LECODES_CMGEN wins, so a local filament build still rules.
|
|
23
|
+
if (raw[0] === "sky" && !process.env.LECODES_CMGEN) {
|
|
24
|
+
process.env.LECODES_CMGEN = await resolveCmgenExe()
|
|
25
|
+
}
|
|
26
|
+
const code = await mod.runAssetsCli(raw)
|
|
27
|
+
if (code !== 0) process.exitCode = code
|
|
28
|
+
}
|