NoWayOS
ES
GitHub
MenúCerrar

Construir

Su primera pantalla

Escriba una pantalla en TSX, compruébela y conviértala, véala en QEMU y en una Raspberry Pi 5, y luego contrólela desde Rust: sus acciones, campos, slots y grupos.

Escriba una pantalla como lo haría en React. El Screen Compiler la comprueba frente a su subconjunto y la convierte en Rust una sola vez, en tiempo de compilación, en su máquina. Lo que llega al dispositivo es ese Rust, integrado en la imagen del sistema: sin JavaScript, sin navegador, sin intérprete. Esta guía lleva una pantalla desde un archivo TSX, pasando por QEMU, hasta una Raspberry Pi 5, y luego muestra cómo una aplicación controla una pantalla. Pantallas desde React enumera todo lo que se convierte.

Preparación

Clone NoWayOS, el Screen Compiler y el Showcase uno junto a otro, y construya una vez el contenedor de desarrollo. Cada objetivo de make se ejecuta dentro de él, así que no se instala nada más en su máquina; Primeros pasos indica lo que necesita el contenedor.

mkdir nowayos-work && cd nowayos-work
git clone https://github.com/NoWayOS/nowayos.git
git clone https://github.com/NoWayOS/screen-compiler.git
git clone https://github.com/NoWayOS/showcase.git
# the NoWayOS the Showcase builds on
git -C nowayos checkout $(cat showcase/nowayos.rev)
make -C nowayos container

El Showcase es la demostración con la que arranca una Pi. Su React gallery muestra cada componente que convierte el Screen Compiler, una pantalla cada vez, y su primera pantalla se suma a ella.

Escribir una pantalla

Guarde esto como showcase/showcase/showcase-app/tsx/gallery-hello.tsx:

import { Button, Column, HeaderBar, Screen, Spacer, Text } from "nowayos:kit";

type HelloProps = {
  onHome: () => void;
  onBack: () => void;
  onNext: () => void;
  onWave: () => void;
};

export function GalleryHelloScreen(props: HelloProps) {
  return (
    <Screen>
      <Column gap={12} padding={16}>
        <HeaderBar gap={8} padding={8}>
          <Button variant="secondary" onPress={props.onHome}>Home</Button>
          <Text role="heading">Hello</Text>
          <Spacer />
          <Button variant="secondary" onPress={props.onBack}>Back</Button>
          <Button onPress={props.onNext}>Next</Button>
        </HeaderBar>
        <Text role="title">Hello from TSX</Text>
        <Text>Written in React's syntax, compiled to Rust at build time.</Text>
        <Button onClick={() => props.onWave()}>Wave</Button>
      </Column>
    </Screen>
  );
}

Cada prop de callback se convierte en una acción, numerada desde 1 en el orden en que la pantalla las usa. Cada pantalla de la galería empieza con la misma barra, Home, Back y Next, de modo que el Showcase trata esas tres igual en todas las pantallas; onWave es propia de esta pantalla, y aquí nada la atiende.

Comprobarla

cd screen-compiler
make run ARGS="check ../showcase/showcase/showcase-app/tsx/gallery-hello.tsx"

Una pantalla que se convierte recibe una línea de respuesta:

../showcase/showcase/showcase-app/tsx/gallery-hello.tsx: supported (1 screen, 10 elements, 0 design tokens, 4 actions)

Todo lo que queda fuera del subconjunto se rechaza donde está escrito, con lo que hay que escribir en su lugar, y no se convierte nada. Ponga en la columna padding="1rem" y en el botón un onClick que registre algo, y la comprobación dice:

../showcase/showcase/showcase-app/tsx/gallery-hello.tsx: not supported
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx:13:32: error[unsupported]: `padding` in `rem` is not supported: lengths are layout units, written in px
  help: write padding="16px", padding={16} or a design token
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx:23:26: error[unsupported]: a function here may only call a callback prop, with no arguments
  help: pass a callback prop of the component: onClick={onContinue}, or onClick={() => props.onContinue()}
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx: 2 errors
cd ../showcase
make screens-bless

convierte cada una de las pantallas del Showcase con el Screen Compiler que tiene al lado y escribe el Rust en showcase/showcase-app/src/generated/: gallery-hello.rs, y una función que la construye, generated::build_gallery_hello. Después, en showcase/showcase-app/src/lib.rs, añada su pantalla al final de la galería:

pub const GALLERY: [(&str, generated::Build); 12] = [
    // ... the gallery's own eleven screens ...
    ("gallery-hello", generated::build_gallery_hello),
];

y a la lista que hay justo debajo, la de las pantallas cuya barra se comprueba: gallery_hello::gallery_hello_screen, con gallery_hello en el use de esa lista. La comprobación se ejecuta mientras se compila el Showcase, así que una barra en el orden equivocado detiene la compilación.

Verla en QEMU

make qemu-test SYSTEM=showcase-gallery

reproduce el recorrido guionizado de la galería y compara cada una de sus propias pantallas, píxel a píxel, con su imagen aprobada: el resto de la galería sigue dibujándose tal como se aprobó. Para ver la suya:

make qemu SYSTEM=showcase-gallery VNC=1

La pantalla de QEMU se publica por VNC en localhost:5900 de su máquina, con una contraseña que se imprime solo para esa ejecución; en macOS, abra vnc://localhost:5900. Lo que escriba en el terminal llega al Showcase: Flecha abajo, Flecha derecha e Intro abren la React gallery; luego Mayús-Tab e Intro pulsan Back, que da la vuelta de la primera pantalla a la última, la suya. Salga de QEMU con Ctrl-A y después x.

Verla en la Pi

Prepare la placa una vez, como muestra Ejecución en una Raspberry Pi 5: su tarjeta, la Debug Probe, un monitor conectado antes del encendido y un ratón USB. Después, desde showcase/, en su máquina y no en el contenedor:

make pi-show PI_DEVICE=/dev/cu.usbmodemXXXX SYSTEM=showcase

Esto construye el Showcase con su pantalla dentro, lo envía a la placa a través de la sonda y lo deja en marcha hasta que se apaga y se vuelve a encender la placa. Haga clic en React gallery y luego en Back: su pantalla, en el monitor HDMI de la Pi.

Controlar una pantalla desde Rust

Una pantalla le dice a la aplicación qué ha pasado, y la aplicación le dice a la pantalla qué mostrar, de cuatro maneras: acciones, campos, slots y grupos. La fixture fixtures/visit-booking.tsx del Screen Compiler usa las cuatro:

type VisitBookingProps = {
  phoneError: string;
  onBack: () => void;
  onBook: () => void;
};
// ...
<TextInput id="name" placeholder="Your name" maxLength={NAME_LEN} />
{props.phoneError && <Text>{props.phoneError}</Text>}
<RadioGroup id="when" options={["Morning", "Afternoon", "Evening"]} />
<Checkbox id="remind" label="Remind me the day before" />
<Button id="book" onPress={props.onBook}>Book</Button>

Su Rust nombra cada una, numerada desde 1 en el orden en que la pantalla las escribe:

En el TSX En el Rust La aplicación
Una prop de callback, onBook Una acción, actions::BOOK recibe aviso cuando se pulsa el botón
Un control con un id, id="name" Un campo, fields::NAME lee lo que se ha introducido
Una prop escrita como texto, {props.phoneError} Un slot, slots::PHONE_ERROR establece el texto
Una prop que decide lo que se muestra, {props.phoneError && …} Un grupo, groups::PHONE_ERROR lo muestra u oculta

La aplicación construye la pantalla en un árbol de interfaz de NoWayOS, establece sus slots y grupos, y entrega al árbol cada evento de entrada. Un toque en Book vuelve como la acción de Book, y entonces la aplicación lee los campos:

let mut tree = Tree::<64>::new();
let screen = visit_booking::build(&mut tree)?;
tree.set_root(screen.root)?;

// A slot is text the application sets; a group is content it shows or hides.
tree.set_slot(slots::PHONE_ERROR, "Digits only, please")?;
tree.set_visible(groups::PHONE_ERROR, true)?;

// Every input event goes to the tree; a tap on Book comes back as its action.
match tree.handle(event) {
    Some(UiEvent::Action(actions::BOOK)) => {
        let name = tree.value(fields::NAME).unwrap_or("");
        let when = tree.selected(fields::WHEN);
        let remind = tree.checked(fields::REMIND).unwrap_or(false);
        booked = Some((name.to_owned(), when, remind));
    }
    Some(UiEvent::Action(actions::BACK)) => back = true,
    _ => {}
}

Este es el código de una prueba del repositorio del Screen Compiler, contract-check/tests/driving.rs, que construye la pantalla convertida sobre el propio núcleo de interfaz de NoWayOS y toca Book en su make ci. En un dispositivo, los eventos llegan del intermediario de entrada de NoWayOS, y el árbol lo dibuja su renderizador. Una comparación como props.step === "review" se convierte del mismo modo en un switch, switches::STEP, que la aplicación establece con set_case.

Qué sigue

Pantallas desde React recoge cada componente, prop y propiedad de estilo que acepta el Screen Compiler. Para traer una aplicación React existente, empiece por Portar desde React y su informe de cobertura.