create-mithril-lynx 2.4.0 → 2.5.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.
@@ -9,7 +9,7 @@
9
9
  "preview": "rspeedy preview"
10
10
  },
11
11
  "dependencies": {
12
- "mithril-runtime": "^1.1.0",
12
+ "mithril-runtime": "latest",
13
13
  "mithril-lynx": "{{MITHRIL_LYNX_VERSION}}"
14
14
  },
15
15
  "devDependencies": {
@@ -1,3 +1,21 @@
1
1
  /// <reference types="@lynx-js/rspeedy/client" />
2
2
  /// <reference types="@lynx-js/types" />
3
3
  /// <reference types="@lynx-js/type-element-api" />
4
+
5
+ declare module "@lynx-js/types" {
6
+ interface NativeModules {
7
+ /** Android host bridge used by mithril-lynx/route.listenBackButton(). */
8
+ MithrilLynxNavigationModule?: {
9
+ setCanGoBack(canGoBack: boolean): void;
10
+ };
11
+ LynxBatteryPlugin?: { getStatus(requestId: string): void };
12
+ LynxCameraPlugin?: { takePhoto(requestId: string): void };
13
+ LynxDevicePlugin?: { getInfo(requestId: string): void };
14
+ LynxGeolocationPlugin?: { getCurrentPosition(requestId: string, highAccuracy: boolean): void };
15
+ LynxNetworkPlugin?: { getInfo(requestId: string): void };
16
+ LynxVibrationPlugin?: {
17
+ vibrate(requestId: string, durationMs: number): void;
18
+ cancel(requestId: string): void;
19
+ };
20
+ }
21
+ }
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copies the Android resources kept beside the JS source into the sibling
4
+ * Gradle host. Edit <project>/android/*.xml, then run:
5
+ *
6
+ * bun run android:prepare
7
+ */
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+
12
+ const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
13
+ const sourceDir = path.join(projectRoot, "android");
14
+ const androidDir = path.resolve(projectRoot, "{{ANDROID_REL_DIR}}");
15
+
16
+ const files = [
17
+ ["AndroidManifest.xml", ["app", "src", "main", "AndroidManifest.xml"]],
18
+ ["strings.xml", ["app", "src", "main", "res", "values", "strings.xml"]],
19
+ ["styles.xml", ["app", "src", "main", "res", "values", "styles.xml"]],
20
+ ["config.xml", ["app", "src", "main", "res", "xml", "config.xml"]],
21
+ ["network_security_config.xml", ["app", "src", "main", "res", "xml", "network_security_config.xml"]],
22
+ ["file_paths.xml", ["app", "src", "main", "res", "xml", "file_paths.xml"]],
23
+ ];
24
+
25
+ function fail(message) {
26
+ console.error(`\n ✖ ${message}\n`);
27
+ process.exit(1);
28
+ }
29
+
30
+ if (!fs.existsSync(path.join(androidDir, "settings.gradle.kts"))) {
31
+ fail(`Can't find the Android host at ${androidDir}.`);
32
+ }
33
+
34
+ for (const [name, destination] of files) {
35
+ const source = path.join(sourceDir, name);
36
+ if (!fs.existsSync(source)) {
37
+ fail(`Missing ${path.relative(projectRoot, source)}. Restore it before preparing the Android host.`);
38
+ }
39
+ const target = path.join(androidDir, ...destination);
40
+ fs.mkdirSync(path.dirname(target), { recursive: true });
41
+ fs.copyFileSync(source, target);
42
+ console.log(` → ${path.relative(projectRoot, target)}`);
43
+ }
44
+
45
+ const mapsConfig = path.join(sourceDir, "maps.json");
46
+ if (fs.existsSync(mapsConfig)) {
47
+ const mapsTarget = path.join(androidDir, "app", "src", "main", "assets", "lynx_maps", "config.json");
48
+ fs.mkdirSync(path.dirname(mapsTarget), { recursive: true });
49
+ fs.copyFileSync(mapsConfig, mapsTarget);
50
+ console.log(` → ${path.relative(projectRoot, mapsTarget)}`);
51
+ }
52
+
53
+ console.log("\n ✔ Android resources prepared.\n");
@@ -98,6 +98,10 @@ dependencies {
98
98
  // gesture then never works).
99
99
  implementation("androidx.viewpager2:viewpager2:1.1.0")
100
100
 
101
+ // <create-mithril-lynx:android-connectors>
102
+ {{ANDROID_PLUGIN_DEPENDENCIES}}
103
+ // </create-mithril-lynx:android-connectors>
104
+
101
105
  implementation("androidx.appcompat:appcompat:1.7.0")
102
106
  implementation("androidx.core:core-splashscreen:1.0.1")
103
107
  }
@@ -11,6 +11,7 @@
11
11
  android:roundIcon="@mipmap/ic_launcher_round"
12
12
  android:hardwareAccelerated="true"
13
13
  android:usesCleartextTraffic="true"
14
+ android:networkSecurityConfig="@xml/network_security_config"
14
15
  android:theme="@style/AppTheme">
15
16
 
16
17
  <activity
@@ -1,14 +1,20 @@
1
1
  package {{PACKAGE_NAME}}
2
2
 
3
3
  import android.os.Bundle
4
+ // <create-mithril-lynx:connector-imports>
5
+ // </create-mithril-lynx:connector-imports>
6
+ import androidx.activity.OnBackPressedCallback
4
7
  import androidx.appcompat.app.AppCompatActivity
5
8
  import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
9
+ import com.lynx.react.bridge.JavaOnlyArray
6
10
  import com.lynx.tasm.LynxBooleanOption
7
11
  import com.lynx.tasm.LynxViewBuilder
8
12
  import com.lynx.tasm.ThreadStrategyForRendering
9
13
  import com.lynx.xelement.XElementBehaviors
10
14
 
11
15
  class MainActivity : AppCompatActivity() {
16
+ private var backCallback: OnBackPressedCallback? = null
17
+
12
18
  override fun onCreate(savedInstanceState: Bundle?) {
13
19
  // Must run BEFORE super.onCreate() — a Splash Screen API requirement.
14
20
  installSplashScreen()
@@ -20,6 +26,11 @@ class MainActivity : AppCompatActivity() {
20
26
  // keyboard. Remove this line (and the xelement dependencies) if you
21
27
  // don't use them.
22
28
  builder.addBehaviors(XElementBehaviors().create())
29
+ // Lets mithril-lynx/route tell Android when its in-memory history can
30
+ // handle Back. See MithrilLynxNavigationModule and the callback below.
31
+ builder.registerModule("MithrilLynxNavigationModule", MithrilLynxNavigationModule::class.java)
32
+ // <create-mithril-lynx:connector-registration>
33
+ // </create-mithril-lynx:connector-registration>
23
34
  // lynx-family/lynx's own explorer/android registers a
24
35
  // GenericResourceFetcher unconditionally (LynxViewShellActivity —
25
36
  // "used inside LynxEngine for resource loading capabilities of
@@ -37,9 +48,32 @@ class MainActivity : AppCompatActivity() {
37
48
  val lynxView = builder.build(this)
38
49
  setContentView(lynxView)
39
50
 
51
+ // Disabled until route.listenBackButton() reports that JS has an
52
+ // in-app history entry. When disabled, Android's normal Back behavior
53
+ // remains in charge and closes the activity from the first screen.
54
+ val callback = object : OnBackPressedCallback(false) {
55
+ override fun handleOnBackPressed() {
56
+ lynxView.sendGlobalEvent("mithrilLynx:back", JavaOnlyArray())
57
+ }
58
+ }
59
+ onBackPressedDispatcher.addCallback(this, callback)
60
+ backCallback = callback
61
+
40
62
  // The bundle lives in app/src/main/assets/main-thread.bundle, copied
41
63
  // there by `npm run android` (scripts/android.mjs) from the JS
42
64
  // project's dist/. The name has to match exactly.
43
65
  lynxView.renderTemplateUrl("main-thread.bundle", "")
44
66
  }
67
+
68
+ fun setCanGoBack(canGoBack: Boolean) {
69
+ backCallback?.isEnabled = canGoBack
70
+ }
71
+
72
+ // <create-mithril-lynx:connector-callbacks>
73
+ // </create-mithril-lynx:connector-callbacks>
74
+
75
+ override fun onDestroy() {
76
+ backCallback = null
77
+ super.onDestroy()
78
+ }
45
79
  }
@@ -0,0 +1,28 @@
1
+ package {{PACKAGE_NAME}}
2
+
3
+ import android.content.Context
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import com.lynx.jsbridge.LynxMethod
7
+ import com.lynx.jsbridge.LynxModule
8
+ import com.lynx.tasm.behavior.LynxContext
9
+
10
+ /**
11
+ * JS -> Android half of mithril-lynx/route's system Back integration.
12
+ *
13
+ * MainActivity supplies the Android -> JS half with sendGlobalEvent(). Keeping
14
+ * the callback disabled when the route history is empty preserves Android's
15
+ * default behavior on the app's first screen, including predictive Back.
16
+ */
17
+ class MithrilLynxNavigationModule(context: Context) : LynxModule(context) {
18
+ private val activity = (context as? LynxContext)?.activity as? MainActivity
19
+
20
+ @LynxMethod
21
+ fun setCanGoBack(canGoBack: Boolean) {
22
+ // Lynx invokes native modules from its JS thread; Android's callback
23
+ // belongs to the main thread.
24
+ Handler(Looper.getMainLooper()).post {
25
+ activity?.setCanGoBack(canGoBack)
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ Project-owned Android configuration entry point. Android does not consume a
4
+ generic config.xml by itself; use it for XML metadata that a manifest entry
5
+ or a native integration needs, keeping that configuration beside the other
6
+ editable Android files in <project>/android/.
7
+ -->
8
+ <config />
@@ -0,0 +1,10 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ Ready for an androidx.core.content.FileProvider declaration in
4
+ AndroidManifest.xml when the app needs to share a file with another app.
5
+ -->
6
+ <paths>
7
+ <files-path name="files" path="." />
8
+ <cache-path name="cache" path="." />
9
+ <external-files-path name="external_files" path="." />
10
+ </paths>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!--
3
+ The generated manifest references this resource. It deliberately matches the
4
+ previous host default (cleartext allowed) so existing local HTTP workflows do
5
+ not change. Restrict this before a production release when possible.
6
+ -->
7
+ <network-security-config>
8
+ <base-config cleartextTrafficPermitted="true" />
9
+ </network-security-config>
@@ -1,10 +1,9 @@
1
1
  import m from "mithril-runtime";
2
2
 
3
3
  // A reusable app bar: a title, plus a back affordance when `onBack` is
4
- // given. No m.route.Link involved for the back button — see
5
- // mithril-lynx/route.js's README section on route.back(): Lynx has no
6
- // hardware back button, so a screen's own back affordance has to call
7
- // route.back() explicitly.
4
+ // given. No m.route.Link involved for this on-screen affordance: it calls
5
+ // route.back() explicitly. The generated Android host's system Back bridge
6
+ // reaches the same history separately through route.listenBackButton().
8
7
  export function AppBar(title: string, onBack?: () => void) {
9
8
  return m("view", { class: "AppBar" }, [
10
9
  onBack != null ? m("text", { class: "AppBar-back", ontap: onBack }, "←") : null,
@@ -21,12 +21,24 @@ route("/", {
21
21
  "/detail": DetailHost,
22
22
  });
23
23
 
24
+ // The host generated by --android forwards the system Back button through a
25
+ // Lynx global event. The module is optional so this template still runs in
26
+ // LynxExplorer, or when generated without its native Android host.
27
+ route.listenBackButton({
28
+ onCanGoBackChange: (canGoBack) =>
29
+ NativeModules.MithrilLynxNavigationModule?.setCanGoBack(canGoBack),
30
+ });
31
+
24
32
  declare const module: {
25
33
  hot?: {
26
34
  accept(path: string, callback: () => void): void;
27
35
  };
28
36
  };
29
- declare const require: (id: string) => unknown;
37
+ // Overloads: HMR reloads the two screen modules; DEV font registration
38
+ // prepended by --with-font/--find-font requires .ttf paths as string URLs.
39
+ declare function require(id: "./screens/home.js"): typeof homeModule;
40
+ declare function require(id: "./screens/detail.js"): typeof detailModule;
41
+ declare function require(id: string): string;
30
42
 
31
43
  if (module.hot) {
32
44
  module.hot.accept("./screens/home.js", () => {