diff --git a/README.md b/README.md index 65867c7b..714418dc 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,33 @@ fonts, vectors and core masks at Vita's native 960x544 density. Physical controls, left-analog input, and front-panel multi-touch snapshots are supported; PSP builds retain their controller-only fallback. +The experimental Kindle Paperwhite 5 host uses a complete Kindle build source +tree: either a git checkout or an unpacked `@pocketjs/framework` npm tarball +followed by `bun install --frozen-lockfile` at its root. The published tarball +includes the Kindle docs, Hero/Paper Ink examples, assets, native host, and +device tools; the standalone zero-dependency `@pocketjs/cli` is not that source +tree. +It renders the 309×412 logical viewport at the panel's native 1236×1648 +resolution, accepts multitouch through evdev, and uses an external FBInk +executable for bounded DU/A2 partial refresh followed by high-quality cleanup. +The complete jailbreak, USB SSH, build/deploy/reload, recovery, and real-device +verification workflow is in [docs/KINDLE.md](docs/KINDLE.md). + +```sh +# One-time host and attached-device setup while /Volumes/Kindle is mounted: +bun run kindle:setup +bun run kindle:bootstrap --volume /Volumes/Kindle + +# Safely eject the volume, keep the cable attached, start "PocketJS Dev - +# Start USB SSH" on the Kindle, then configure/check the new USB interface: +bun run kindle:usbnet --check +# If the doctor names an unconfigured enN: +# bun run kindle:usbnet --interface enN +bun run kindle probe +bun pocket build --target kindle-pw5 --manifest apps/hero/pocket.kindle.json --project-root . +bun run kindle dev --plan=.pocket/kindle-pw5/plan.json --skip-build --skip-native +``` + ## The Pocket Launcher (on-device app switching) One PSP EBOOT or Vita VPK can embed every app admitted by that target plus a diff --git a/apps/hero/app.tsx b/apps/hero/app.tsx index 1f064539..d62aa3f0 100644 --- a/apps/hero/app.tsx +++ b/apps/hero/app.tsx @@ -2,11 +2,13 @@ // Uses all three public primitives, class literals, a dynamic style object, // focus + onPress, and a signal in text — the exact surface phase v1 supports. -import { createSignal, onMount, Show } from "solid-js"; +import { createSignal, onCleanup, onMount, Show } from "solid-js"; import { Image, Text, View, type NodeMirror } from "@pocketjs/framework/components"; import { animate } from "@pocketjs/framework/animation"; +import { enableTouchPress } from "@pocketjs/framework/input"; import { createSpriteAnimation } from "@pocketjs/framework/lifecycle"; -import { frameworkName } from "@pocketjs/framework"; +import { platform } from "@pocketjs/framework/platform"; +import { frameworkName } from "@pocketjs/framework/solid"; const SPINNER_FRAME_STEP = 3; const SPINNER_FRAMES = [ @@ -30,61 +32,146 @@ function Stat(props: { label: string; value: string; cls: string }) { } export default function Hero() { + const isKindle = platform.target === "kindle-pw5"; const [count, setCount] = createSignal(0); - const spinnerSrc = createSpriteAnimation(SPINNER_FRAMES, { frameStep: SPINNER_FRAME_STEP }); + const disableTouchPress = isKindle ? enableTouchPress() : undefined; + onCleanup(() => disableTouchPress?.()); + const spinnerSrc = isKindle + ? () => SPINNER_FRAMES[0] + : createSpriteAnimation(SPINNER_FRAMES, { frameStep: SPINNER_FRAME_STEP }); let underline: NodeMirror | undefined; onMount(() => { // Underline sweeps in once on mount — native tween, zero steady-state JS. - if (underline) animate(underline, "width", 210, { dur: 700, easing: "out", delay: 150 }); + // Kindle keeps it static so the panel reaches its quiet cleanup window. + if (!isKindle && underline) { + animate(underline, "width", 210, { dur: 700, easing: "out", delay: 150 }); + } }); return ( - + - + PocketJS - {frameworkName()} + RUST + SCEGU + + {isKindle ? `${frameworkName()} + RUST + FBINK` : `${frameworkName()} + RUST + SCEGU`} + - - - - + + + + - - ONE RUST CORE · ONE JSX APP - - JSX at 60 FPS. - - + + + ONE RUST CORE · ONE JSX APP + - - Flexbox, springs and baked type — - running on a 2005 handheld. + class={isKindle + ? "flex-col gap-2" + : "flex-row flex-wrap items-center justify-between"} + > + + {isKindle ? "JSX on e-ink." : "JSX at 60 FPS."} + + } + > + + E-INK NATIVE + + + + } + > + + + + Flexbox, springs and baked type — + running on a 2005 handheld. + + } + > + + Flexbox + Solid signals. + Rendered through Rust + FBInk. + + - + setCount(count() + 1)} > - Press Circle + + {isKindle ? "Tap to increment" : "Press Circle"} + Count: {count()} 3}> - Reactive on real hardware. + + {isKindle ? "Reactive on e-ink hardware." : "Reactive on real hardware."} + diff --git a/apps/hero/main.tsx b/apps/hero/main.tsx index 9970e343..ef513a86 100644 --- a/apps/hero/main.tsx +++ b/apps/hero/main.tsx @@ -1,5 +1,5 @@ // @title PocketJS: Hero import Hero from "./app.tsx"; -import { mount } from "@pocketjs/framework"; +import { mount } from "@pocketjs/framework/solid"; mount(() => ); diff --git a/apps/hero/pocket.kindle.json b/apps/hero/pocket.kindle.json new file mode 100644 index 00000000..f5d75024 --- /dev/null +++ b/apps/hero/pocket.kindle.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://pocketjs.dev/schema/pocket-2.json", + "pocket": 2, + "id": "dev.pocket-stack.hero-kindle", + "name": "pocketjs-hero-kindle", + "title": "PocketJS: Hero for Kindle", + "version": "0.6.0", + "engine": { + "capabilities": { + "requires": [ + "input.touch", + "text.glyphs.baked" + ] + } + }, + "app": { + "entry": "apps/hero/main.tsx", + "output": "hero-kindle-main", + "framework": "solid", + "viewport": { + "fixed": { + "logical": [ + 309, + 412 + ], + "presentation": "native" + } + } + } +} diff --git a/apps/paper-ink/app.tsx b/apps/paper-ink/app.tsx new file mode 100644 index 00000000..dc479629 --- /dev/null +++ b/apps/paper-ink/app.tsx @@ -0,0 +1,104 @@ +import { For, createSignal } from "solid-js"; +import { Text, View } from "@pocketjs/framework/components"; +import { onFrame } from "@pocketjs/framework/lifecycle"; +import { touches } from "@pocketjs/framework/input"; + +interface InkPoint { + readonly id: number; + readonly x: number; + readonly y: number; + readonly shade: string; + readonly size: number; +} + +const TRAIL_SHADES = ["#161616", "#303030", "#505050", "#747474", "#9a9a9a", "#bcbcbc"]; +const TRAIL_SIZES = [14, 12, 10, 8, 7, 6]; + +export default function PaperInk() { + const [points, setPoints] = createSignal([]); + const [samples, setSamples] = createSignal(0); + let nextId = 1; + let previous = ""; + + onFrame(() => { + const contact = touches()[0]; + if (!contact) { + previous = ""; + return; + } + const key = `${contact.x}:${contact.y}`; + if (key === previous) return; + previous = key; + setSamples((value) => value + 1); + setPoints((current) => { + const head = { + id: nextId++, + x: contact.x, + y: contact.y, + shade: TRAIL_SHADES[0], + size: TRAIL_SIZES[0], + }; + return [head, ...current.slice(0, TRAIL_SHADES.length - 1)].map((point, index) => ({ + ...point, + shade: TRAIL_SHADES[index], + size: TRAIL_SIZES[index], + })); + }); + }); + + return ( + + + + PAPER / INK + + + TOUCH TO DRAW A PARTIAL-REFRESH TRACE + + + + + + {(top) => ( + + )} + + + + {(point) => ( + + )} + + + + + AUTO PARTIAL + + + SAMPLES {samples()} + + + + ); +} diff --git a/apps/paper-ink/main.tsx b/apps/paper-ink/main.tsx new file mode 100644 index 00000000..01cf61e7 --- /dev/null +++ b/apps/paper-ink/main.tsx @@ -0,0 +1,5 @@ +// @title PocketJS: Paper Ink +import { mount } from "@pocketjs/framework/solid"; +import PaperInk from "./app.tsx"; + +mount(() => ); diff --git a/apps/paper-ink/pocket.json b/apps/paper-ink/pocket.json new file mode 100644 index 00000000..10d994f0 --- /dev/null +++ b/apps/paper-ink/pocket.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://pocketjs.dev/schema/pocket-2.json", + "pocket": 2, + "id": "dev.pocket-stack.paper-ink", + "name": "pocketjs-paper-ink", + "title": "PocketJS: Paper Ink", + "version": "0.1.0", + "engine": { + "capabilities": { + "requires": [ + "input.touch", + "text.glyphs.baked" + ] + } + }, + "app": { + "entry": "apps/paper-ink/main.tsx", + "output": "paper-ink-main", + "framework": "solid", + "viewport": { + "fixed": { + "logical": [ + 309, + 412 + ], + "presentation": "native" + } + } + } +} diff --git a/contracts/spec/platforms.ts b/contracts/spec/platforms.ts index 7886a286..4c40692e 100644 --- a/contracts/spec/platforms.ts +++ b/contracts/spec/platforms.ts @@ -163,6 +163,7 @@ export const POCKET_TARGETS = defineTargetRegistry; readonly vita: TargetProfile; readonly pocketbook: TargetProfile; + readonly "kindle-pw5": TargetProfile; readonly "macos-widget": TargetProfile; }>({ psp: { @@ -223,6 +224,28 @@ export const POCKET_TARGETS = defineTargetRegistry