diff --git a/apps/iphone16-demo/app.tsx b/apps/iphone16-demo/app.tsx
new file mode 100644
index 00000000..4f624afa
--- /dev/null
+++ b/apps/iphone16-demo/app.tsx
@@ -0,0 +1,13 @@
+import Hero from "../hero/app.tsx";
+import { TICKS_PER_SECOND } from "@pocketjs/framework/clock";
+
+export default function IPhone16Hero() {
+ return (
+
+ );
+}
diff --git a/apps/iphone16-demo/main.tsx b/apps/iphone16-demo/main.tsx
new file mode 100644
index 00000000..965967c0
--- /dev/null
+++ b/apps/iphone16-demo/main.tsx
@@ -0,0 +1,5 @@
+// @title PocketJS: iPhone 16 Hero
+import IPhone16Hero from "./app.tsx";
+import { mount } from "@pocketjs/framework/solid";
+
+mount(() => );
diff --git a/apps/iphone16-demo/pocket.json b/apps/iphone16-demo/pocket.json
new file mode 100644
index 00000000..1a06e734
--- /dev/null
+++ b/apps/iphone16-demo/pocket.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://pocketjs.dev/schema/pocket-2.json",
+ "pocket": 2,
+ "id": "dev.pocket-stack.iphone16-demo",
+ "name": "pocketjs-iphone16-hero",
+ "title": "PocketJS: iPhone 16 Hero",
+ "version": "0.1.0",
+ "engine": {
+ "capabilities": {
+ "requires": ["input.touch", "text.glyphs.baked"]
+ }
+ },
+ "app": {
+ "entry": "apps/iphone16-demo/main.tsx",
+ "output": "iphone16-demo-main",
+ "framework": "solid",
+ "viewport": {
+ "fixed": {
+ "logical": [480, 272],
+ "presentation": "integer-fit"
+ }
+ }
+ }
+}
diff --git a/apps/launcher/images.json b/apps/launcher/images.json
index cc096b09..c9a301cb 100644
--- a/apps/launcher/images.json
+++ b/apps/launcher/images.json
@@ -110,6 +110,12 @@
"covers/refl-vue-sfc-lab-main.png": {
"linear": true
},
+ "covers/cover-iphone16-demo-main.png": {
+ "linear": true
+ },
+ "covers/refl-iphone16-demo-main.png": {
+ "linear": true
+ },
"covers/cover-zoomlab-main.png": {
"linear": true
},
diff --git a/apps/launcher/registry.generated.ts b/apps/launcher/registry.generated.ts
index fc334b60..8a5e4db9 100644
--- a/apps/launcher/registry.generated.ts
+++ b/apps/launcher/registry.generated.ts
@@ -35,5 +35,6 @@ export const REGISTRY: readonly RegistryApp[] = [
{ output: "settings-main", id: "dev.pocket-stack.settings", title: "PocketJS: Settings", cover: "covers/cover-settings-main.png", refl: "covers/refl-settings-main.png" },
{ output: "im-main", id: "dev.pocket-stack.im", title: "PocketJS: Talk", cover: "covers/cover-im-main.png", refl: "covers/refl-im-main.png" },
{ output: "vue-sfc-lab-main", id: "dev.pocket-stack.vue.sfc.lab", title: "PocketJS: Vue SFC Feature Lab", cover: "covers/cover-vue-sfc-lab-main.png", refl: "covers/refl-vue-sfc-lab-main.png" },
+ { output: "iphone16-demo-main", id: "dev.pocket-stack.iphone16-demo", title: "PocketJS: iPhone 16 Hero", cover: "covers/cover-iphone16-demo-main.png", refl: "covers/refl-iphone16-demo-main.png" },
{ output: "zoomlab-main", id: "dev.pocket-stack.zoomlab", title: "Zoom Lab", cover: "covers/cover-zoomlab-main.png", refl: "covers/refl-zoomlab-main.png" },
] as const;
diff --git a/tests/launcher-sim.test.ts b/tests/launcher-sim.test.ts
index 945be8d9..2dd2fc4c 100644
--- a/tests/launcher-sim.test.ts
+++ b/tests/launcher-sim.test.ts
@@ -99,9 +99,9 @@ describe("launcher registry admission", () => {
const vitaOnly = vitaRegistry.apps
.map((a) => a.output)
.filter((output) => !pspOutputs.has(output));
- expect(vitaOnly).toEqual(["nsengine-main"]);
+ expect(vitaOnly.sort()).toEqual(["iphone16-demo-main", "nsengine-main"]);
expect(registry.apps).toHaveLength(17);
- expect(vitaRegistry.apps).toHaveLength(18);
+ expect(vitaRegistry.apps).toHaveLength(19);
});
test("committed registry.generated.ts is fresh (re-run tools/launcher.ts scan)", async () => {
diff --git a/tests/platform-contracts.test.ts b/tests/platform-contracts.test.ts
index f6eec168..9a959ffa 100644
--- a/tests/platform-contracts.test.ts
+++ b/tests/platform-contracts.test.ts
@@ -343,6 +343,7 @@ describe("semantic resolution", () => {
"hero-vue-sfc": [true, true, false],
"hero-vue-vapor": [true, true, false],
im: [true, true, false],
+ "iphone16-demo": [false, true, false], // targets the private ios-dev profile; vita shares its touch + integer-fit contract
"iphone2g-demo": [false, false, false], // admitted only by the private iphone2g-dev profile
nsengine: [false, true, false], // targets the private ios-dev profile; vita shares its touch + integer-fit contract
"ipod-nano": [false, false, false], // admitted by the package-shaped macos-embedded target