बिल्ड करें
आपकी पहली स्क्रीन
TSX में एक स्क्रीन लिखें, उसे जाँचें और बदलें, उसे QEMU में और Raspberry Pi 5 पर देखें, फिर उसे Rust से चलाएँ: उसके एक्शन, फ़ील्ड, स्लॉट और ग्रुप।
एक स्क्रीन वैसे ही लिखें जैसे आप React में लिखते। Screen Compiler उसे अपने सबसेट से जाँचता है और उसे एक ही बार, बिल्ड के समय, आपकी मशीन पर Rust में बदल देता है। डिवाइस तक वही Rust पहुँचता है, सिस्टम इमेज में बिल्ड किया हुआ: न JavaScript, न ब्राउज़र, न इंटरप्रेटर। यह गाइड एक स्क्रीन को TSX फ़ाइल से QEMU होते हुए Raspberry Pi 5 तक ले जाती है, फिर दिखाती है कि कोई एप्लिकेशन स्क्रीन को कैसे चलाता है। जो कुछ बदलता है, उसकी सूची React से स्क्रीन में है।
तैयारी
NoWayOS, Screen Compiler और Showcase को एक-दूसरे के बगल में क्लोन करें, और डेवलपमेंट कंटेनर एक बार बनाएँ। हर make टारगेट उसी के अंदर चलता है, इसलिए आपकी मशीन पर और कुछ इंस्टॉल नहीं होता; कंटेनर को क्या चाहिए, यह शुरुआत करें बताता है।
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
Showcase वह प्रदर्शन है जिसमें Pi बूट होता है। उसकी React gallery Screen Compiler से बदलने वाला हर कंपोनेंट दिखाती है, एक बार में एक स्क्रीन, और आपकी पहली स्क्रीन भी उसमें जुड़ जाती है।
स्क्रीन लिखें
इसे 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>
);
}
हर कॉलबैक prop एक एक्शन बन जाता है, जिसे 1 से उसी क्रम में नंबर दिया जाता है जिसमें स्क्रीन उनका उपयोग करती है। गैलरी की हर स्क्रीन एक ही बार से शुरू होती है, Home, Back और Next, ताकि Showcase हर स्क्रीन पर इन तीनों को एक जैसा संभाले; onWave सिर्फ़ इसी स्क्रीन का है, और यहाँ इसे कोई नहीं संभालता।
जाँचें
cd screen-compiler
make run ARGS="check ../showcase/showcase/showcase-app/tsx/gallery-hello.tsx"
जो स्क्रीन बदल जाती है, उसे जवाब में एक पंक्ति मिलती है:
../showcase/showcase/showcase-app/tsx/gallery-hello.tsx: supported (1 screen, 10 elements, 0 design tokens, 4 actions)
सबसेट के बाहर की हर चीज़ वहीं अस्वीकार की जाती है जहाँ वह लिखी है, साथ में यह भी कि उसकी जगह क्या लिखें, और कुछ भी नहीं बदला जाता। कॉलम को padding="1rem" दें और बटन को ऐसा onClick जो लॉग करता हो, तो जाँच यह कहती है:
../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
बगल वाले Screen Compiler से Showcase की हर स्क्रीन को बदलता है और Rust को showcase/showcase-app/src/generated/ में लिखता है: gallery-hello.rs, और उसे बनाने वाला एक फ़ंक्शन, generated::build_gallery_hello। फिर showcase/showcase-app/src/lib.rs में अपनी स्क्रीन को गैलरी के अंत में जोड़ें:
pub const GALLERY: [(&str, generated::Build); 12] = [
// ... the gallery's own eleven screens ...
("gallery-hello", generated::build_gallery_hello),
];
और उसके ठीक नीचे की सूची में भी, यानी उन स्क्रीन की सूची जिनका बार जाँचा जाता है: gallery_hello::gallery_hello_screen, और उस सूची के use में gallery_hello। यह जाँच Showcase के कंपाइल होते समय चलती है, इसलिए ग़लत क्रम वाला बार बिल्ड रोक देता है।
QEMU में देखें
make qemu-test SYSTEM=showcase-gallery
गैलरी का स्क्रिप्ट किया हुआ चक्कर चलाता है और उसकी अपनी हर स्क्रीन को, पिक्सेल-दर-पिक्सेल, उसकी स्वीकृत इमेज से मिलाता है: बाकी गैलरी अब भी वैसी ही बनती है जैसी स्वीकृत हुई थी। अपनी स्क्रीन देखने के लिए:
make qemu SYSTEM=showcase-gallery VNC=1
QEMU की स्क्रीन आपकी मशीन के localhost:5900 पर VNC के ज़रिए खोली जाती है, एक ऐसे पासवर्ड के साथ जो सिर्फ़ उसी रन के लिए प्रिंट किया जाता है; macOS पर vnc://localhost:5900 खोलें। आप टर्मिनल में जो टाइप करते हैं, वह Showcase तक पहुँचता है: नीचे वाली ऐरो की, दाईं ऐरो की और Enter से React gallery खुलती है, फिर Shift-Tab और Enter से Back दबता है, जो पहली स्क्रीन से घूमकर आख़िरी पर, यानी आपकी स्क्रीन पर, ले जाता है। QEMU से बाहर निकलने के लिए Ctrl-A दबाएँ, फिर x।
Pi पर देखें
बोर्ड को एक बार वैसे तैयार करें जैसा Raspberry Pi 5 पर चलाना दिखाता है: उसका कार्ड, Debug Probe, पावर-ऑन से पहले जुड़ा मॉनिटर और एक USB माउस। फिर showcase/ से, कंटेनर में नहीं बल्कि अपनी मशीन पर:
make pi-show PI_DEVICE=/dev/cu.usbmodemXXXX SYSTEM=showcase
यह आपकी स्क्रीन के साथ Showcase बनाता है, उसे प्रोब के ज़रिए बोर्ड पर भेजता है और तब तक चलता छोड़ देता है जब तक बोर्ड को बंद करके फिर से चालू न किया जाए। React gallery पर क्लिक करें, फिर Back पर: Pi के HDMI मॉनिटर पर आपकी स्क्रीन।
Rust से स्क्रीन चलाएँ
स्क्रीन एप्लिकेशन को बताती है कि क्या हुआ, और एप्लिकेशन स्क्रीन को बताता है कि क्या दिखाना है, चार तरीक़ों से: एक्शन, फ़ील्ड, स्लॉट और ग्रुप। Screen Compiler का फ़िक्स्चर fixtures/visit-booking.tsx चारों का उपयोग करता है:
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>
उसका Rust हर एक को नाम देता है, 1 से उसी क्रम में नंबर देकर जिसमें स्क्रीन उन्हें लिखती है:
| TSX में | Rust में | एप्लिकेशन |
|---|---|---|
एक कॉलबैक prop, onBook |
एक एक्शन, actions::BOOK |
को बटन दबने पर बताया जाता है |
id वाला एक कंट्रोल, id="name" |
एक फ़ील्ड, fields::NAME |
पढ़ता है कि क्या दर्ज किया गया |
टेक्स्ट के रूप में लिखा prop, {props.phoneError} |
एक स्लॉट, slots::PHONE_ERROR |
टेक्स्ट सेट करता है |
ऐसा prop जो तय करता है कि क्या दिखे, {props.phoneError && …} |
एक ग्रुप, groups::PHONE_ERROR |
उसे दिखाता या छिपाता है |
एप्लिकेशन स्क्रीन को NoWayOS के एक UI ट्री में बनाता है, उसके स्लॉट और ग्रुप सेट करता है, और हर इनपुट इवेंट ट्री को सौंपता है। Book पर टैप, Book के एक्शन के रूप में लौटता है, और तब एप्लिकेशन फ़ील्ड पढ़ता है:
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,
_ => {}
}
यह Screen Compiler की रिपॉज़िटरी के एक टेस्ट, contract-check/tests/driving.rs, का कोड है, जो बदली हुई स्क्रीन को NoWayOS के अपने UI कोर पर बनाता है और अपने make ci में Book पर टैप करता है। डिवाइस पर इवेंट NoWayOS के इनपुट ब्रोकर से आते हैं, और ट्री को उसका रेंडरर बनाता है। props.step === "review" जैसी तुलना भी इसी तरह एक स्विच बन जाती है, switches::STEP, जिसे एप्लिकेशन set_case से सेट करता है।
आगे क्या
Screen Compiler जो भी कंपोनेंट, prop और स्टाइल प्रॉपर्टी लेता है, वे सब React से स्क्रीन में हैं। किसी मौजूदा React एप्लिकेशन को लाने के लिए React से पोर्ट करना और उसकी कवरेज रिपोर्ट से शुरू करें।