اكتب برنامجًا وتحقق منه واختبره وشغّله، في خمس دقائق.
يبني هذا الشرح عدّادًا: مداريّ واحد بسمة واحدة. إنه صغير، لكنه برنامج Orb كامل، وكل خطوة فيه هي نفسها في البرامج الكبيرة.
أنشئ الملف counter.lolo:
1app counter "1.0.0"
2"A counter with increment, decrement and reset."
3orbital CounterOrbital {
4 entity Counter [runtime] {
5 id : string!
6 count : number = 0
7 }
8
9 trait Counting -> Counter [interaction, instance] {
10 initial: ready
11
12 state ready {
13 INIT -> ready
14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [
15 { type: button, label: "−", action: DECREMENT, variant: secondary, aria-label: "Decrease" },
16 { type: typography, content: @entity.count, variant: h2 },
17 { type: button, label: "+", action: INCREMENT, variant: primary, aria-label: "Increase" },
18 { type: button, label: "Reset", action: RESET, variant: ghost }
19 ] })
20 INCREMENT -> ready
21 (set @entity.count (+ @entity.count 1))
22 DECREMENT -> ready
23 (set @entity.count (- @entity.count 1))
24 RESET -> ready
25 (set @entity.count 0)
26 }
27 }
28
29 page "/counter" -> Counting
30}اقرأه من أعلى إلى أسفل: الكيان Counter يحمل رقمًا واحدًا، والسمة Counting لها حالة واحدة هي ready. عند INIT (حين تُفتح الصفحة) ترسم صفًا من الأزرار والعدد. كل زر يرسل حدثًا، ولكل حدث انتقال يحدّث العدد. النص مرتبط بـ @entity.count، فيُعاد رسمه عندما يتغير العدد.
orb validate counter.loloالمعيار صفر أخطاء وصفر تحذيرات. جرّب كسره: احذف الانتقال RESET -> ready وتحقق مجددًا. يبلغ المترجم بأن زر Reset يرسل حدثًا لا تتعامل معه أي حالة. هذه هي الدائرة المغلقة: كل عنصر تفاعلي يجب أن يؤدي إلى مكان ما.
orb verify counter.loloيشغّل التحقق كل انتقال ويتأكد من أنه يفعل ما يعلنه: الحالة التي يصل إليها، والتأثيرات التي ينفذها، والشاشة التي يرسمها.
orb serve counter.lolo --openيترجم orb serve البرنامج إلى تطبيق TypeScript ويقدّمه ببيئة تشغيل مدمجة، دون حاجة إلى Node.js. هذا البرنامج نفسه يعمل في هذه الصفحة:
trait Counting -> Counter [interaction, instance] {
state ready {
INIT -> ready
(render-ui main <Stack align={center} direction={horizontal} gap={md}>
<Button action={DECREMENT} aria-label={Decrease} label={"−"} variant={secondary} />
<Typography content={@entity.count} variant={h2} />
<Button action={INCREMENT} aria-label={Increase} label={"+"} variant={primary} />
<Button action={RESET} label={Reset} variant={ghost} />
</Stack>)
INCREMENT -> ready
(emit MOVED { event: INCREMENT })
(set @entity.count (+ @entity.count 1))
DECREMENT -> ready
(emit MOVED { event: DECREMENT })
(set @entity.count (- @entity.count 1))
RESET -> ready
(emit MOVED { event: RESET })
(set @entity.count 0)
}
emits {
MOVED -> external {
event : string!
}
@description "Each move names the event it took, so the diagram beside the demo can follow it."
}
}1app std-orb-docex-counter "1.0.0"
2"std-orb-docex-counter — the smallest complete orbital: one entity, one trait with one state, three buttons and a closed circuit."
3orbital CounterOrbital {
4 entity Counter [runtime] {
5 id : string!
6 count : number = 0
7 }
8
9 trait Counting -> Counter [interaction, instance] {
10 initial: ready
11
12 state ready {
13 INIT -> ready
14 (render-ui main { type: stack, direction: horizontal, gap: md, align: center, children: [
15 { type: button, label: "−", action: DECREMENT, variant: secondary, aria-label: "Decrease" },
16 { type: typography, content: @entity.count, variant: h2 },
17 { type: button, label: "+", action: INCREMENT, variant: primary, aria-label: "Increase" },
18 { type: button, label: "Reset", action: RESET, variant: ghost }
19 ] })
20 INCREMENT -> ready
21 (emit MOVED { event: "INCREMENT" })
22 (set @entity.count (+ @entity.count 1))
23 DECREMENT -> ready
24 (emit MOVED { event: "DECREMENT" })
25 (set @entity.count (- @entity.count 1))
26 RESET -> ready
27 (emit MOVED { event: "RESET" })
28 (set @entity.count 0)
29 }
30
31 emits {
32 MOVED -> external {
33 event : string!
34 }
35 @description "Each move names the event it took, so the diagram beside the demo can follow it."
36 }
37 }
38
39 page "/counter" -> Counting
40}ما الذي يحدث عندما يقع الحدث INCREMENT:
(set @entity.count (+ @entity.count 1))orb compile counter.lolo --output ./counter-appيكتب هذا تطبيقًا كاملًا يمكنك نشره في أي مكان. البرنامج هو المصدر، ولا تُحرَّر الشيفرة المولَّدة يدويًا أبدًا.
تعرّف على عمل كل جزء: الكيانات والسمات والصفحات. أو تابع درس المداريّ الكامل.