@monimesl/dot-cli 0.1.0-alpha.3 → 0.1.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +34 -15
  2. package/dist/cli.js +252 -39
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-z7M4FLjf.js → server-DFOUQnLA.js} +437 -60
  5. package/dist/server-DFOUQnLA.js.map +1 -0
  6. package/dist/{template → templates/showcase}/dot.ts +3 -0
  7. package/dist/templates/showcase/pages/carousel/controller.ts +10 -0
  8. package/dist/templates/showcase/pages/carousel/state.ts +40 -0
  9. package/dist/templates/showcase/pages/carousel/view.dml +37 -0
  10. package/dist/templates/showcase/pages/filters/controller.ts +28 -0
  11. package/dist/templates/showcase/pages/filters/state.ts +72 -0
  12. package/dist/templates/showcase/pages/filters/view.dml +43 -0
  13. package/dist/{template → templates/showcase}/pages/index/state.ts +19 -1
  14. package/dist/{template → templates/showcase}/pages/layout/controller.ts +3 -0
  15. package/dist/{template → templates/showcase}/pages/layout/state.ts +2 -0
  16. package/dist/{template → templates/showcase}/pages/layout/view.dml +17 -0
  17. package/dist/{template → templates/showcase}/pages/navigation/controller.ts +22 -0
  18. package/dist/{template → templates/showcase}/pages/navigation/state.ts +4 -0
  19. package/dist/{template → templates/showcase}/pages/navigation/view.dml +10 -0
  20. package/dist/templates/showcase/pages/payments/controller.ts +42 -0
  21. package/dist/templates/showcase/pages/payments/state.ts +10 -0
  22. package/dist/templates/showcase/pages/payments/view.dml +35 -0
  23. package/dist/templates/starter/.env +3 -0
  24. package/dist/templates/starter/dot.ts +3 -0
  25. package/dist/templates/starter/pages/index/controller.ts +9 -0
  26. package/dist/templates/starter/pages/index/state.ts +4 -0
  27. package/dist/templates/starter/pages/index/view.dml +16 -0
  28. package/dist/templates/starter/statics/logo.png +0 -0
  29. package/package.json +8 -7
  30. package/dist/server-z7M4FLjf.js.map +0 -1
  31. /package/dist/{template → templates/showcase}/.env +0 -0
  32. /package/dist/{template → templates/showcase}/pages/device/controller.ts +0 -0
  33. /package/dist/{template → templates/showcase}/pages/device/state.ts +0 -0
  34. /package/dist/{template → templates/showcase}/pages/device/view.dml +0 -0
  35. /package/dist/{template → templates/showcase}/pages/feedback/controller.ts +0 -0
  36. /package/dist/{template → templates/showcase}/pages/feedback/state.ts +0 -0
  37. /package/dist/{template → templates/showcase}/pages/feedback/view.dml +0 -0
  38. /package/dist/{template → templates/showcase}/pages/files/controller.ts +0 -0
  39. /package/dist/{template → templates/showcase}/pages/files/state.ts +0 -0
  40. /package/dist/{template → templates/showcase}/pages/files/view.dml +0 -0
  41. /package/dist/{template → templates/showcase}/pages/forms/controller.ts +0 -0
  42. /package/dist/{template → templates/showcase}/pages/forms/state.ts +0 -0
  43. /package/dist/{template → templates/showcase}/pages/forms/view.dml +0 -0
  44. /package/dist/{template → templates/showcase}/pages/images/controller.ts +0 -0
  45. /package/dist/{template → templates/showcase}/pages/images/state.ts +0 -0
  46. /package/dist/{template → templates/showcase}/pages/images/view.dml +0 -0
  47. /package/dist/{template → templates/showcase}/pages/index/controller.ts +0 -0
  48. /package/dist/{template → templates/showcase}/pages/index/view.dml +0 -0
  49. /package/dist/{template → templates/showcase}/pages/lists/controller.ts +0 -0
  50. /package/dist/{template → templates/showcase}/pages/lists/state.ts +0 -0
  51. /package/dist/{template → templates/showcase}/pages/lists/view.dml +0 -0
  52. /package/dist/{template → templates/showcase}/pages/network/controller.ts +0 -0
  53. /package/dist/{template → templates/showcase}/pages/network/state.ts +0 -0
  54. /package/dist/{template → templates/showcase}/pages/network/view.dml +0 -0
  55. /package/dist/{template → templates/showcase}/pages/state/controller.ts +0 -0
  56. /package/dist/{template → templates/showcase}/pages/state/state.ts +0 -0
  57. /package/dist/{template → templates/showcase}/pages/state/view.dml +0 -0
  58. /package/dist/{template → templates/showcase}/pages/text/controller.ts +0 -0
  59. /package/dist/{template → templates/showcase}/pages/text/state.ts +0 -0
  60. /package/dist/{template → templates/showcase}/pages/text/view.dml +0 -0
  61. /package/dist/{template → templates/showcase}/statics/logo.png +0 -0
@@ -6,8 +6,11 @@ export default defineDot({
6
6
  'layout',
7
7
  'text',
8
8
  'images',
9
+ 'carousel',
9
10
  'lists',
11
+ 'filters',
10
12
  'forms',
13
+ 'payments',
11
14
  'feedback',
12
15
  'state',
13
16
  'navigation',
@@ -0,0 +1,10 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ mutations: {
6
+ showTip: (_state, event: DotEvent<{ index: number }>) => ({ tip: event.detail.index }),
7
+ back: (state) => ({ tip: Math.max(state.tip - 1, 0) }),
8
+ next: (state) => ({ tip: Math.min(state.tip + 1, state.tips.length - 1) }),
9
+ },
10
+ });
@@ -0,0 +1,40 @@
1
+ export interface Promo {
2
+ id: string;
3
+ title: string;
4
+ text: string;
5
+ icon: string;
6
+ }
7
+
8
+ export interface Tip {
9
+ title: string;
10
+ text: string;
11
+ }
12
+
13
+ export default {
14
+ promos: [
15
+ {
16
+ id: 'send',
17
+ title: 'Send money home',
18
+ text: 'Free transfers to any mobile wallet this week',
19
+ icon: 'send',
20
+ },
21
+ {
22
+ id: 'bills',
23
+ title: 'Pay bills in seconds',
24
+ text: 'Electricity, water and airtime in one place',
25
+ icon: 'bolt',
26
+ },
27
+ {
28
+ id: 'save',
29
+ title: 'Save a little each day',
30
+ text: 'Set a goal and watch it grow',
31
+ icon: 'wallet',
32
+ },
33
+ ] as Promo[],
34
+ tips: [
35
+ { title: 'Welcome', text: 'Swipe, tap a dot, or use the buttons below.' },
36
+ { title: 'The page decides', text: "The carousel's index comes from state." },
37
+ { title: 'And hears back', text: 'A swipe raises change, which sets the index again.' },
38
+ ] as Tip[],
39
+ tip: 0,
40
+ };
@@ -0,0 +1,37 @@
1
+ <View>
2
+ <Text variant="title">Carousel</Text>
3
+ <Text>A Carousel shows its children one at a time. Swipe between them, or tap a dot.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="caption" tone="muted">Plays by itself and loops, pausing while touched, with a gap between slides that scale in as they come</Text>
7
+ <Carousel autoplay loop interval="3000" gap="medium" effect="scale">
8
+ <For each="{{ promos }}" item="promo" key="id">
9
+ <View variant="card" direction="row" align="center" gap="large">
10
+ <Icon name="{{ promo.icon }}" size="large" tone="info" />
11
+ <View gap="small" grow>
12
+ <Text weight="bold">{{ promo.title }}</Text>
13
+ <Text tone="muted">{{ promo.text }}</Text>
14
+ </View>
15
+ </View>
16
+ </For>
17
+ </Carousel>
18
+ </View>
19
+
20
+ <View gap="small">
21
+ <Text variant="caption" tone="muted">The page chooses the slide, and hears when a swipe changes it</Text>
22
+ <Carousel index="{{ tip }}" onChange="showTip">
23
+ <For each="{{ tips }}" item="card" key="title">
24
+ <View variant="card" align="center" gap="medium">
25
+ <Image src="statics/logo.png" alt="" width="48" height="48" />
26
+ <Text weight="bold">{{ card.title }}</Text>
27
+ <Text tone="muted" align="center">{{ card.text }}</Text>
28
+ </View>
29
+ </For>
30
+ </Carousel>
31
+ <View direction="row" gap="small">
32
+ <Button variant="secondary" onClick="back" disabled="{{ tip === 0 }}">Back</Button>
33
+ <Button onClick="next" disabled="{{ tip === tips.length - 1 }}">Next</Button>
34
+ </View>
35
+ <Text>Tip {{ tip + 1 }} of {{ tips.length }}</Text>
36
+ </View>
37
+ </View>
@@ -0,0 +1,28 @@
1
+ import type { Payment } from './state';
2
+ import state from './state';
3
+
4
+ type Chosen = DotEvent<{ value: string }>;
5
+
6
+ export default definePage({
7
+ initialState: state,
8
+ mutations: {
9
+ chooseTab: (state, event: Chosen) => {
10
+ const tab = event.detail.value;
11
+ return { tab, shown: filter(state.payments, tab, state.status) };
12
+ },
13
+ chooseStatus: (state, event: DotEvent<unknown, { status: string }>) => {
14
+ const { status } = event.data;
15
+ return { status, shown: filter(state.payments, state.tab, status) };
16
+ },
17
+ clearFilters: (state) => ({ tab: 'all', status: 'All', shown: state.payments }),
18
+ choosePeriod: (_state, event: Chosen) => ({ period: event.detail.value }),
19
+ },
20
+ });
21
+
22
+ function filter(payments: readonly Payment[], tab: string, status: string): Payment[] {
23
+ return payments.filter(
24
+ (payment) =>
25
+ (tab === 'all' || payment.direction === tab) &&
26
+ (status === 'All' || payment.status === status),
27
+ );
28
+ }
@@ -0,0 +1,72 @@
1
+ export type Direction = 'in' | 'out';
2
+ export type Status = 'Paid' | 'Pending' | 'Failed';
3
+
4
+ export interface Payment {
5
+ id: string;
6
+ title: string;
7
+ date: string;
8
+ amount: string;
9
+ direction: Direction;
10
+ status: Status;
11
+ tone: string;
12
+ }
13
+
14
+ const payments: Payment[] = [
15
+ {
16
+ id: 'p1',
17
+ title: 'Salary',
18
+ date: 'Today',
19
+ amount: 'Le 2,400,000',
20
+ direction: 'in',
21
+ status: 'Paid',
22
+ tone: 'success',
23
+ },
24
+ {
25
+ id: 'p2',
26
+ title: 'Electricity',
27
+ date: 'Today',
28
+ amount: 'Le 50,000',
29
+ direction: 'out',
30
+ status: 'Paid',
31
+ tone: 'success',
32
+ },
33
+ {
34
+ id: 'p3',
35
+ title: 'From Aminata',
36
+ date: 'Yesterday',
37
+ amount: 'Le 75,000',
38
+ direction: 'in',
39
+ status: 'Pending',
40
+ tone: 'warning',
41
+ },
42
+ {
43
+ id: 'p4',
44
+ title: 'Airtime',
45
+ date: 'Yesterday',
46
+ amount: 'Le 10,000',
47
+ direction: 'out',
48
+ status: 'Pending',
49
+ tone: 'warning',
50
+ },
51
+ {
52
+ id: 'p5',
53
+ title: 'Water',
54
+ date: '12 September',
55
+ amount: 'Le 35,000',
56
+ direction: 'out',
57
+ status: 'Failed',
58
+ tone: 'danger',
59
+ },
60
+ ];
61
+
62
+ export default {
63
+ payments,
64
+ // The payments the tab and the chosen status leave.
65
+ shown: payments,
66
+ tab: 'all',
67
+ incoming: payments.filter((payment) => payment.direction === 'in').length,
68
+ outgoing: payments.filter((payment) => payment.direction === 'out').length,
69
+ statuses: ['All', 'Paid', 'Pending', 'Failed'],
70
+ status: 'All',
71
+ period: 'week',
72
+ };
@@ -0,0 +1,43 @@
1
+ <View>
2
+ <Text variant="title">Tabs and filters</Text>
3
+ <Text>Tabs switch what a page shows, chips narrow it down, and an empty state says when nothing is left.</Text>
4
+
5
+ <Tabs value="{{ tab }}" onChange="chooseTab">
6
+ <Tab value="all" label="All" />
7
+ <Tab value="in" label="Incoming" badge="{{ incoming }}" />
8
+ <Tab value="out" label="Outgoing" badge="{{ outgoing }}" />
9
+ </Tabs>
10
+
11
+ <View direction="row" gap="small" wrap>
12
+ <For each="{{ statuses }}" item="option" key="*this">
13
+ <Chip label="{{ option }}" selected="{{ option === status }}" data-status="{{ option }}" onTap="chooseStatus" />
14
+ </For>
15
+ </View>
16
+
17
+ <If condition="{{ shown.length > 0 }}">
18
+ <List>
19
+ <For each="{{ shown }}" item="payment" key="id">
20
+ <ListItem icon="{{ payment.direction === 'in' ? 'arrow-down' : 'arrow-up' }}" title="{{ payment.title }}" subtitle="{{ payment.date }} · {{ payment.amount }}">
21
+ <Badge tone="{{ payment.tone }}">{{ payment.status }}</Badge>
22
+ </ListItem>
23
+ </For>
24
+ </List>
25
+ </If>
26
+ <Else>
27
+ <EmptyState icon="search" title="No payments here" text="Nothing matches this tab and status.">
28
+ <Button variant="secondary" onClick="clearFilters">Clear filters</Button>
29
+ </EmptyState>
30
+ </Else>
31
+
32
+ <View gap="small">
33
+ <Text variant="caption" tone="muted">Segmented tabs, for a few short choices</Text>
34
+ <Tabs variant="segmented" value="{{ period }}" onChange="choosePeriod">
35
+ <Tab value="week" label="Week" />
36
+ <Tab value="month" label="Month" />
37
+ <Tab value="year" label="Year" />
38
+ </Tabs>
39
+ <View variant="card">
40
+ <Text>Spent this {{ period }}: {{ period === 'week' ? 'Le 95,000' : period === 'month' ? 'Le 410,000' : 'Le 4,870,000' }}</Text>
41
+ </View>
42
+ </View>
43
+ </View>
@@ -19,7 +19,7 @@ export default {
19
19
  {
20
20
  page: 'layout',
21
21
  title: 'Layout',
22
- summary: 'Rows, columns, gaps, alignment and cards',
22
+ summary: 'Rows, columns, grids, gaps, alignment and cards',
23
23
  icon: 'grid',
24
24
  },
25
25
  {
@@ -34,18 +34,36 @@ export default {
34
34
  summary: 'Pictures from statics/, their shapes and events',
35
35
  icon: 'image',
36
36
  },
37
+ {
38
+ page: 'carousel',
39
+ title: 'Carousel',
40
+ summary: 'Slides to swipe, played by themselves, with dots',
41
+ icon: 'image',
42
+ },
37
43
  {
38
44
  page: 'lists',
39
45
  title: 'Lists and icons',
40
46
  summary: 'Rows to tap, sheets of actions, badges and icons',
41
47
  icon: 'list',
42
48
  },
49
+ {
50
+ page: 'filters',
51
+ title: 'Tabs and filters',
52
+ summary: 'Tabs, chips to narrow a list, and an empty state',
53
+ icon: 'search',
54
+ },
43
55
  {
44
56
  page: 'forms',
45
57
  title: 'Forms',
46
58
  summary: 'Fields, choices, switches, and a footer to send them',
47
59
  icon: 'check',
48
60
  },
61
+ {
62
+ page: 'payments',
63
+ title: 'Payments',
64
+ summary: 'Amounts, one-time codes, PINs and QR codes',
65
+ icon: 'cash',
66
+ },
49
67
  {
50
68
  page: 'feedback',
51
69
  title: 'Feedback',
@@ -14,6 +14,9 @@ export default definePage({
14
14
  nextAlign: (state) => ({ align: after(ALIGNS, state.align) }),
15
15
  nextGap: (state) => ({ gap: after(SPACES, state.gap) }),
16
16
  toggleWrap: (state) => ({ wrap: !state.wrap }),
17
+ openService: (_state, event: DotEvent<unknown, { name: string }>) => ({
18
+ service: event.data.name,
19
+ }),
17
20
  },
18
21
  });
19
22
 
@@ -9,4 +9,6 @@ export default {
9
9
  align: 'center' as Align,
10
10
  gap: 'small' as Space,
11
11
  wrap: true,
12
+ // The service tapped in the grid.
13
+ service: '',
12
14
  };
@@ -60,4 +60,21 @@
60
60
  <Text>start</Text>
61
61
  </View>
62
62
  </View>
63
+
64
+ <Text variant="caption" tone="muted">A Grid keeps equal columns, here of services to tap</Text>
65
+ <View variant="card" gap="medium">
66
+ <Grid columns="4">
67
+ <GridItem icon="send" label="Send" data-name="Send" onTap="openService" />
68
+ <GridItem icon="phone" label="Airtime" data-name="Airtime" onTap="openService" />
69
+ <GridItem icon="bolt" label="Electricity" badge="New" data-name="Electricity" onTap="openService" />
70
+ <GridItem icon="water" label="Water" data-name="Water" onTap="openService" />
71
+ <GridItem icon="qr-code" label="Scan to pay" data-name="Scan to pay" onTap="openService" />
72
+ <GridItem icon="wallet" label="Save" data-name="Save" onTap="openService" />
73
+ <GridItem icon="receipt" label="Receipts" data-name="Receipts" onTap="openService" />
74
+ <GridItem icon="more" label="More" data-name="More" onTap="openService" />
75
+ </Grid>
76
+ <If condition="{{ service }}">
77
+ <Text tone="muted">You tapped {{ service }}</Text>
78
+ </If>
79
+ </View>
63
80
  </View>
@@ -3,12 +3,15 @@ import state from './state';
3
3
 
4
4
  export default definePage({
5
5
  initialState: state,
6
+ // What Dash's navigation bar shows until the page changes it.
7
+ configs: { navigationBar: { title: 'Navigation' } },
6
8
  hooks: {
7
9
  // Options come from the page that opened this one, as strings.
8
10
  onLoad(options) {
9
11
  const depth = Number(options.depth ?? '1') + 1;
10
12
  const from = options.trail ?? 'Home';
11
13
  this.setState({ depth, from, trail: `${from} › ${depth}` });
14
+ this.setNavigationBar({ title: `Page ${depth}` });
12
15
  this.setState((current) => ({ hooks: ran(current.hooks, 'onLoad') }));
13
16
  },
14
17
  onShow() {
@@ -34,6 +37,25 @@ export default definePage({
34
37
  home() {
35
38
  this.navigation.reset('index');
36
39
  },
40
+ toggleColors(state) {
41
+ const colored = !state.colored;
42
+ // Undefined puts Dash's own colors back.
43
+ this.setNavigationBar({
44
+ backgroundColor: colored ? '#1A73E8' : undefined,
45
+ textColor: colored ? '#FFFFFF' : undefined,
46
+ });
47
+ this.setState({ colored });
48
+ },
49
+ toggleNavigationBar(state) {
50
+ const navigationBarHidden = !state.navigationBarHidden;
51
+ this.setNavigationBar({ hidden: navigationBarHidden });
52
+ this.setState({ navigationBarHidden });
53
+ },
54
+ toggleTabBar(state) {
55
+ const tabBarHidden = !state.tabBarHidden;
56
+ this.setTabBar({ hidden: tabBarHidden });
57
+ this.setState({ tabBarHidden });
58
+ },
37
59
  },
38
60
  });
39
61
 
@@ -10,4 +10,8 @@ export default {
10
10
  from: '',
11
11
  trail: '',
12
12
  hooks: [] as HookRun[],
13
+ // What this page set in Dash's bars.
14
+ colored: false,
15
+ navigationBarHidden: false,
16
+ tabBarHidden: false,
13
17
  };
@@ -14,6 +14,16 @@
14
14
  </View>
15
15
  </View>
16
16
 
17
+ <Text variant="caption" tone="muted">Dash's bars, which each page sets: this one's title is its number</Text>
18
+ <View variant="card" gap="small">
19
+ <View direction="row" wrap gap="small">
20
+ <Button variant="secondary" onClick="toggleColors">{{ colored ? 'Plain bar' : 'Blue bar' }}</Button>
21
+ <Button variant="secondary" onClick="toggleNavigationBar">{{ navigationBarHidden ? 'Show the navigation bar' : 'Hide the navigation bar' }}</Button>
22
+ <Button variant="secondary" onClick="toggleTabBar">{{ tabBarHidden ? 'Show the tab bar' : 'Hide the tab bar' }}</Button>
23
+ </View>
24
+ <Text variant="caption" tone="muted">Going back to a page shows its bars again, and leaving the dot puts Dash's own back.</Text>
25
+ </View>
26
+
17
27
  <Text variant="caption" tone="muted">The hooks this page ran, in order</Text>
18
28
  <View variant="card" gap="small">
19
29
  <For each="{{ hooks }}" item="run" key="id">
@@ -0,0 +1,42 @@
1
+ import state from './state';
2
+
3
+ type Typed = DotEvent<{ value: string }>;
4
+
5
+ // The code this page accepts; a real dot checks it with its server.
6
+ const RIGHT_CODE = '123456';
7
+
8
+ export default definePage({
9
+ initialState: state,
10
+ mutations: {
11
+ setAmount: (_state, event: DotEvent<{ value: number | null }>) => {
12
+ const amount = event.detail.value;
13
+ if (!amount) {
14
+ return { amount, amountText: '', request: '' };
15
+ }
16
+ return {
17
+ amount,
18
+ amountText: groupThousands(amount),
19
+ request: `https://pay.example.com/request?amount=${amount}&currency=SLE`,
20
+ };
21
+ },
22
+ setCode: (_state, event: Typed) => ({ code: event.detail.value, codeError: '' }),
23
+ checkCode: (_state, event: Typed) => {
24
+ if (event.detail.value === RIGHT_CODE) {
25
+ return { verified: true };
26
+ }
27
+ return { code: '', codeError: 'That code is wrong. Try again.' };
28
+ },
29
+ startAgain: () => ({ verified: false, code: '' }),
30
+ setPin: (_state, event: Typed) => ({ pin: event.detail.value }),
31
+ },
32
+ });
33
+
34
+ // 1250000 -> "1,250,000"
35
+ function groupThousands(amount: number): string {
36
+ const digits = String(Math.round(amount));
37
+ const groups: string[] = [];
38
+ for (let end = digits.length; end > 0; end -= 3) {
39
+ groups.unshift(digits.slice(Math.max(0, end - 3), end));
40
+ }
41
+ return groups.join(',');
42
+ }
@@ -0,0 +1,10 @@
1
+ export default {
2
+ amount: null as number | null,
3
+ // The amount with its thousands grouped, and what the QR code asks for.
4
+ amountText: '',
5
+ request: '',
6
+ code: '',
7
+ codeError: '',
8
+ verified: false,
9
+ pin: '',
10
+ };
@@ -0,0 +1,35 @@
1
+ <View>
2
+ <Text variant="title">Payments</Text>
3
+ <Text>Fields for money and codes, and a QR code to be paid with.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="caption" tone="muted">An amount, grouped as it's typed and sent to the page as a number</Text>
7
+ <AmountInput label="Amount to request" currency="Le" value="{{ amount }}" onChange="setAmount" hint="The QR code below asks for it" />
8
+ <View variant="card" align="center" gap="medium">
9
+ <If condition="{{ request }}">
10
+ <QRCode value="{{ request }}" size="180" label="A QR code to pay Le {{ amountText }}" />
11
+ <Text tone="muted">Scan to pay Le {{ amountText }}</Text>
12
+ </If>
13
+ <Else>
14
+ <EmptyState icon="qr-code" title="No amount yet" text="Type an amount above to make a QR code for it." />
15
+ </Else>
16
+ </View>
17
+ </View>
18
+
19
+ <View gap="small">
20
+ <Text variant="caption" tone="muted">A one-time code: type it, paste it, or let the phone fill it in from an SMS</Text>
21
+ <If condition="{{ verified }}">
22
+ <Notice tone="success" title="Code accepted">The payment is confirmed.</Notice>
23
+ <Button variant="secondary" onClick="startAgain">Start again</Button>
24
+ </If>
25
+ <Else>
26
+ <CodeInput label="The code we sent to 076 •••• 21" value="{{ code }}" onChange="setCode" onComplete="checkCode" error="{{ codeError }}" hint="For this page, it's 123456" />
27
+ </Else>
28
+ </View>
29
+
30
+ <View gap="small">
31
+ <Text variant="caption" tone="muted">A PIN, hidden as it's typed</Text>
32
+ <CodeInput label="PIN" length="4" secure value="{{ pin }}" onChange="setPin" />
33
+ <Text tone="muted">{{ pin.length }} of 4 digits</Text>
34
+ </View>
35
+ </View>
@@ -0,0 +1,3 @@
1
+ # Public configuration for the dot. Anyone with the dot can read these variables: never put a
2
+ # secret here. Read them in the service as `env.GREETING`.
3
+ GREETING=Hello from your dot
@@ -0,0 +1,3 @@
1
+ export default defineDot({
2
+ pages: ['index'],
3
+ });
@@ -0,0 +1,9 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ mutations: {
6
+ increment: (state) => ({ count: state.count + 1 }),
7
+ reset: () => ({ count: 0 }),
8
+ },
9
+ });
@@ -0,0 +1,4 @@
1
+ export default {
2
+ greeting: env.GREETING,
3
+ count: 0,
4
+ };
@@ -0,0 +1,16 @@
1
+ <View>
2
+ <View direction="row" align="center" gap="medium">
3
+ <Image src="statics/logo.png" alt="Logo" width="40" height="40" />
4
+ <Text variant="title">{{ greeting }}</Text>
5
+ </View>
6
+ <Text>Change pages/index/view.dml and save: the simulator shows it at once.</Text>
7
+
8
+ <View variant="card" align="center" gap="medium">
9
+ <Text variant="title">{{ count }}</Text>
10
+ <Text tone="muted">{{ count === 1 ? 'tap' : 'taps' }} so far</Text>
11
+ <View direction="row" gap="small">
12
+ <Button variant="secondary" onClick="reset" disabled="{{ count === 0 }}">Reset</Button>
13
+ <Button onClick="increment">Tap me</Button>
14
+ </View>
15
+ </View>
16
+ </View>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monimesl/dot-cli",
3
- "version": "0.1.0-alpha.3",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "The dotctl command: build mini apps and run them in the simulator",
5
5
  "type": "module",
6
6
  "repository": {
@@ -22,18 +22,19 @@
22
22
  "./package.json": "./package.json"
23
23
  },
24
24
  "dependencies": {
25
- "@monimesl/dot-compiler": "0.1.0-alpha.3",
26
- "@monimesl/dot-core": "0.1.0-alpha.3",
27
- "@monimesl/dot-script-parser": "0.1.0-alpha.3",
28
- "@monimesl/dot-dml-parser": "0.1.0-alpha.3",
29
- "@monimesl/dot-simulator": "0.1.0-alpha.3"
25
+ "typescript": "^5.9.3",
26
+ "uqr": "^0.1.3",
27
+ "@monimesl/dot-compiler": "0.1.0-alpha.4",
28
+ "@monimesl/dot-core": "0.1.0-alpha.4",
29
+ "@monimesl/dot-dml-parser": "0.1.0-alpha.4",
30
+ "@monimesl/dot-script-parser": "0.1.0-alpha.4",
31
+ "@monimesl/dot-simulator": "0.1.0-alpha.4"
30
32
  },
31
33
  "devDependencies": {
32
34
  "@types/node": "^22.20.4",
33
35
  "publint": "^0.3.24",
34
36
  "rimraf": "^6.1.3",
35
37
  "tsdown": "^0.23.0",
36
- "typescript": "^5.9.3",
37
38
  "vitest": "^5.0.1",
38
39
  "vscode-languageserver": "^9.0.1",
39
40
  "vscode-languageserver-protocol": "^3.17.5",