@monimesl/dot-cli 0.1.0-alpha.9 → 1.0.0-beta.0

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 (143) hide show
  1. package/README.md +72 -59
  2. package/dist/cli.js +530 -339
  3. package/dist/cli.js.map +1 -1
  4. package/dist/{server-BfPAnsSf.js → server-DMnWDdJ6.js} +570 -478
  5. package/dist/server-DMnWDdJ6.js.map +1 -0
  6. package/dist/templates/showcase/dot.ts +53 -16
  7. package/dist/templates/showcase/pages/basics/bars/controller.ts +47 -0
  8. package/dist/templates/showcase/pages/basics/bars/state.ts +7 -0
  9. package/dist/templates/showcase/pages/basics/bars/view.dml +36 -0
  10. package/dist/templates/showcase/pages/{text → basics/bindings}/controller.ts +5 -0
  11. package/dist/templates/showcase/pages/basics/bindings/state.ts +14 -0
  12. package/dist/templates/showcase/pages/basics/bindings/view.dml +41 -0
  13. package/dist/templates/showcase/pages/basics/colors/controller.ts +112 -0
  14. package/dist/templates/showcase/pages/basics/colors/palette.ts +52 -0
  15. package/dist/templates/showcase/pages/basics/colors/state.ts +17 -0
  16. package/dist/templates/showcase/pages/basics/colors/view.dml +142 -0
  17. package/dist/templates/showcase/pages/basics/concert/controller.ts +16 -0
  18. package/dist/templates/showcase/pages/basics/concert/state.ts +7 -0
  19. package/dist/templates/showcase/pages/basics/concert/view.dml +20 -0
  20. package/dist/templates/showcase/pages/basics/control-flow/controller.ts +18 -0
  21. package/dist/templates/showcase/pages/basics/control-flow/state.ts +16 -0
  22. package/dist/templates/showcase/pages/basics/control-flow/view.dml +55 -0
  23. package/dist/templates/showcase/pages/basics/events/controller.ts +43 -0
  24. package/dist/templates/showcase/pages/basics/events/state.ts +11 -0
  25. package/dist/templates/showcase/pages/basics/events/view.dml +51 -0
  26. package/dist/templates/showcase/pages/basics/hooks/controller.ts +43 -0
  27. package/dist/templates/showcase/pages/basics/hooks/state.ts +10 -0
  28. package/dist/templates/showcase/pages/basics/hooks/view.dml +37 -0
  29. package/dist/templates/showcase/pages/basics/navigation/controller.ts +41 -0
  30. package/dist/templates/showcase/pages/basics/navigation/state.ts +11 -0
  31. package/dist/templates/showcase/pages/basics/navigation/view.dml +48 -0
  32. package/dist/templates/showcase/pages/basics/pick-genre/controller.ts +18 -0
  33. package/dist/templates/showcase/pages/basics/pick-genre/state.ts +3 -0
  34. package/dist/templates/showcase/pages/basics/pick-genre/view.dml +10 -0
  35. package/dist/templates/showcase/pages/basics/share/concerts.ts +31 -0
  36. package/dist/templates/showcase/pages/basics/share/controller.ts +12 -0
  37. package/dist/templates/showcase/pages/basics/share/state.ts +5 -0
  38. package/dist/templates/showcase/pages/basics/share/view.dml +21 -0
  39. package/dist/templates/showcase/pages/{state → basics/state}/controller.ts +1 -0
  40. package/dist/templates/showcase/pages/basics/state/view.dml +46 -0
  41. package/dist/templates/showcase/pages/{carousel → components/carousel}/controller.ts +2 -1
  42. package/dist/templates/showcase/pages/{carousel → components/carousel}/state.ts +5 -5
  43. package/dist/templates/showcase/pages/{carousel → components/carousel}/view.dml +7 -5
  44. package/dist/templates/showcase/pages/{feedback → components/feedback}/controller.ts +13 -2
  45. package/dist/templates/showcase/pages/components/feedback/state.ts +41 -0
  46. package/dist/templates/showcase/pages/{feedback → components/feedback}/view.dml +38 -9
  47. package/dist/templates/showcase/pages/{forms → components/forms}/controller.ts +23 -19
  48. package/dist/templates/showcase/pages/{forms → components/forms}/state.ts +6 -3
  49. package/dist/templates/showcase/pages/components/forms/view.dml +97 -0
  50. package/dist/templates/showcase/pages/components/icons/controller.ts +15 -0
  51. package/dist/templates/showcase/pages/components/icons/state.ts +75 -0
  52. package/dist/templates/showcase/pages/components/icons/view.dml +36 -0
  53. package/dist/templates/showcase/pages/{images → components/images}/controller.ts +4 -1
  54. package/dist/templates/showcase/pages/components/images/view.dml +83 -0
  55. package/dist/templates/showcase/pages/{layout → components/layout}/controller.ts +3 -2
  56. package/dist/templates/showcase/pages/components/layout/view.dml +143 -0
  57. package/dist/templates/showcase/pages/components/lists/controller.ts +18 -0
  58. package/dist/templates/showcase/pages/{lists → components/lists}/state.ts +43 -30
  59. package/dist/templates/showcase/pages/components/lists/view.dml +88 -0
  60. package/dist/templates/showcase/pages/{payments → components/money}/controller.ts +7 -8
  61. package/dist/templates/showcase/pages/components/money/view.dml +54 -0
  62. package/dist/templates/showcase/pages/components/reading/controller.ts +6 -0
  63. package/dist/templates/showcase/pages/components/reading/state.ts +23 -0
  64. package/dist/templates/showcase/pages/components/reading/view.dml +42 -0
  65. package/dist/templates/showcase/pages/{lists → components/sheets}/controller.ts +10 -11
  66. package/dist/templates/showcase/pages/components/sheets/state.ts +26 -0
  67. package/dist/templates/showcase/pages/components/sheets/view.dml +38 -0
  68. package/dist/templates/showcase/pages/components/styles/controller.ts +6 -0
  69. package/dist/templates/showcase/pages/components/styles/state.ts +3 -0
  70. package/dist/templates/showcase/pages/components/styles/view.dml +101 -0
  71. package/dist/templates/showcase/pages/components/tabs/controller.ts +41 -0
  72. package/dist/templates/showcase/pages/{filters → components/tabs}/state.ts +32 -23
  73. package/dist/templates/showcase/pages/components/tabs/view.dml +57 -0
  74. package/dist/templates/showcase/pages/components/text/controller.ts +10 -0
  75. package/dist/templates/showcase/pages/components/text/state.ts +3 -0
  76. package/dist/templates/showcase/pages/components/text/view.dml +95 -0
  77. package/dist/templates/showcase/pages/dash/account/controller.ts +32 -0
  78. package/dist/templates/showcase/pages/dash/account/state.ts +9 -0
  79. package/dist/templates/showcase/pages/dash/account/view.dml +33 -0
  80. package/dist/templates/showcase/pages/{cache → dash/cache}/controller.ts +9 -4
  81. package/dist/templates/showcase/pages/dash/cache/view.dml +48 -0
  82. package/dist/templates/showcase/pages/dash/calls/controller.ts +67 -0
  83. package/dist/templates/showcase/pages/dash/calls/state.ts +9 -0
  84. package/dist/templates/showcase/pages/dash/calls/view.dml +69 -0
  85. package/dist/templates/showcase/pages/dash/contacts/controller.ts +30 -0
  86. package/dist/templates/showcase/pages/dash/contacts/state.ts +6 -0
  87. package/dist/templates/showcase/pages/dash/contacts/view.dml +33 -0
  88. package/dist/templates/showcase/pages/dash/crypto/controller.ts +38 -0
  89. package/dist/templates/showcase/pages/dash/crypto/state.ts +10 -0
  90. package/dist/templates/showcase/pages/dash/crypto/view.dml +48 -0
  91. package/dist/templates/showcase/pages/{files → dash/files}/controller.ts +7 -2
  92. package/dist/templates/showcase/pages/{files → dash/files}/state.ts +4 -0
  93. package/dist/templates/showcase/pages/dash/files/view.dml +78 -0
  94. package/dist/templates/showcase/pages/dash/media/controller.ts +65 -0
  95. package/dist/templates/showcase/pages/dash/media/state.ts +12 -0
  96. package/dist/templates/showcase/pages/dash/media/view.dml +53 -0
  97. package/dist/templates/showcase/pages/{network → dash/network}/controller.ts +84 -20
  98. package/dist/templates/showcase/pages/{network → dash/network}/state.ts +14 -1
  99. package/dist/templates/showcase/pages/dash/network/view.dml +108 -0
  100. package/dist/templates/showcase/pages/dash/payments/cases.ts +445 -0
  101. package/dist/templates/showcase/pages/dash/payments/controller.ts +149 -0
  102. package/dist/templates/showcase/pages/dash/payments/state.ts +33 -0
  103. package/dist/templates/showcase/pages/dash/payments/view.dml +71 -0
  104. package/dist/templates/showcase/pages/{secrets → dash/secrets}/controller.ts +2 -1
  105. package/dist/templates/showcase/pages/dash/secrets/view.dml +26 -0
  106. package/dist/templates/showcase/pages/dash/system/controller.ts +89 -0
  107. package/dist/templates/showcase/pages/dash/system/state.ts +13 -0
  108. package/dist/templates/showcase/pages/dash/system/view.dml +96 -0
  109. package/dist/templates/showcase/pages/dash/ui/controller.ts +70 -0
  110. package/dist/templates/showcase/pages/dash/ui/state.ts +8 -0
  111. package/dist/templates/showcase/pages/dash/ui/view.dml +31 -0
  112. package/dist/templates/showcase/pages/index/controller.ts +1 -1
  113. package/dist/templates/showcase/pages/index/state.ts +175 -60
  114. package/dist/templates/showcase/pages/index/view.dml +6 -3
  115. package/package.json +6 -6
  116. package/dist/server-BfPAnsSf.js.map +0 -1
  117. package/dist/templates/showcase/pages/cache/view.dml +0 -36
  118. package/dist/templates/showcase/pages/device/controller.ts +0 -80
  119. package/dist/templates/showcase/pages/device/state.ts +0 -7
  120. package/dist/templates/showcase/pages/device/view.dml +0 -49
  121. package/dist/templates/showcase/pages/feedback/state.ts +0 -8
  122. package/dist/templates/showcase/pages/files/view.dml +0 -50
  123. package/dist/templates/showcase/pages/filters/controller.ts +0 -27
  124. package/dist/templates/showcase/pages/filters/view.dml +0 -44
  125. package/dist/templates/showcase/pages/forms/view.dml +0 -49
  126. package/dist/templates/showcase/pages/images/view.dml +0 -53
  127. package/dist/templates/showcase/pages/layout/view.dml +0 -103
  128. package/dist/templates/showcase/pages/lists/view.dml +0 -59
  129. package/dist/templates/showcase/pages/navigation/controller.ts +0 -65
  130. package/dist/templates/showcase/pages/navigation/state.ts +0 -17
  131. package/dist/templates/showcase/pages/navigation/view.dml +0 -34
  132. package/dist/templates/showcase/pages/network/view.dml +0 -67
  133. package/dist/templates/showcase/pages/payments/view.dml +0 -45
  134. package/dist/templates/showcase/pages/secrets/view.dml +0 -24
  135. package/dist/templates/showcase/pages/state/view.dml +0 -33
  136. package/dist/templates/showcase/pages/text/state.ts +0 -8
  137. package/dist/templates/showcase/pages/text/view.dml +0 -58
  138. /package/dist/templates/showcase/pages/{state → basics/state}/state.ts +0 -0
  139. /package/dist/templates/showcase/pages/{images → components/images}/state.ts +0 -0
  140. /package/dist/templates/showcase/pages/{layout → components/layout}/state.ts +0 -0
  141. /package/dist/templates/showcase/pages/{payments → components/money}/state.ts +0 -0
  142. /package/dist/templates/showcase/pages/{cache → dash/cache}/state.ts +0 -0
  143. /package/dist/templates/showcase/pages/{secrets → dash/secrets}/state.ts +0 -0
@@ -0,0 +1,3 @@
1
+ export default {
2
+ balance: 'SLE 1,250.00',
3
+ };
@@ -0,0 +1,101 @@
1
+ <View>
2
+ <Text variant="title">Styles</Text>
3
+ <Text>A View's colours, outlines, corners and sizes come from the theme, so a dot looks right in light and dark.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">background</Text>
7
+ <Text variant="caption" tone="muted">With primary, write text with tone="onPrimary" so it reads.</Text>
8
+ <View background="primary" radius="large" padding="large" gap="small">
9
+ <Text variant="caption" tone="onPrimary">Your balance</Text>
10
+ <Text variant="display" tone="onPrimary" numeric>{{ balance }}</Text>
11
+ </View>
12
+ <Text variant="caption" tone="muted">The page's own colours</Text>
13
+ <View direction="row" gap="small" wrap>
14
+ <View background="background" border="subtle" radius="medium" padding="small">
15
+ <Text>background</Text>
16
+ </View>
17
+ <View background="surface" border="subtle" radius="medium" padding="small">
18
+ <Text>surface</Text>
19
+ </View>
20
+ <View background="muted" radius="medium" padding="small">
21
+ <Text>muted</Text>
22
+ </View>
23
+ </View>
24
+ <Text variant="caption" tone="muted">A tone is a soft tint</Text>
25
+ <View direction="row" gap="small" wrap>
26
+ <View background="info" radius="medium" padding="small">
27
+ <Text tone="info" weight="medium">New</Text>
28
+ </View>
29
+ <View background="success" radius="medium" padding="small">
30
+ <Text tone="success" weight="medium">Paid</Text>
31
+ </View>
32
+ <View background="warning" radius="medium" padding="small">
33
+ <Text tone="warning" weight="medium">Due Friday</Text>
34
+ </View>
35
+ <View background="danger" radius="medium" padding="small">
36
+ <Text tone="danger" weight="medium">Failed</Text>
37
+ </View>
38
+ </View>
39
+ </View>
40
+
41
+ <View gap="small">
42
+ <Text variant="subtitle">Theme colours</Text>
43
+ <Text variant="caption" tone="muted">The theme in dot.ts can set primary, background, surface, text, muted, border and subtle, for light and dark, and a page can set its own with configs.colors. Left out, they're Dash's, or worked out from a new background. The theme can also name its own colours, such as gold and ink here, to use as a background or a text tone.</Text>
44
+ <View direction="row" gap="small" align="center">
45
+ <View background="gold" radius="full" paddingTop="small" paddingBottom="small" paddingStart="medium" paddingEnd="medium">
46
+ <Text weight="medium" tone="ink">Gold member</Text>
47
+ </View>
48
+ <Text tone="gold" weight="bold">Since 2024</Text>
49
+ </View>
50
+ </View>
51
+
52
+ <View gap="small">
53
+ <Text variant="subtitle">border and radius</Text>
54
+ <Text variant="caption" tone="muted">border is none, subtle or strong. radius rounds the corners, and full makes a circle or a pill.</Text>
55
+ <View direction="row" gap="medium" align="end" wrap>
56
+ <View width="56" height="56" background="muted" radius="none" />
57
+ <View width="56" height="56" background="muted" radius="small" />
58
+ <View width="56" height="56" background="muted" radius="medium" />
59
+ <View width="56" height="56" background="muted" radius="large" />
60
+ <View width="56" height="56" background="muted" radius="full" />
61
+ <View width="56" height="56" border="subtle" radius="medium" />
62
+ <View width="56" height="56" border="strong" radius="medium" />
63
+ </View>
64
+ </View>
65
+
66
+ <View gap="small">
67
+ <Text variant="subtitle">width and height</Text>
68
+ <Text variant="caption" tone="muted">In points, or fill for all the room there is.</Text>
69
+ <View background="muted" radius="medium" width="120" height="40" justify="center" align="center">
70
+ <Text>120 points</Text>
71
+ </View>
72
+ <View background="muted" radius="medium" width="fill" height="40" justify="center" align="center">
73
+ <Text>fill</Text>
74
+ </View>
75
+ </View>
76
+
77
+ <View gap="small">
78
+ <Text variant="subtitle">minHeight, maxWidth and aspect</Text>
79
+ <Text variant="caption" tone="muted">minHeight keeps a box tall when it holds little. maxWidth stops it growing too wide. aspect sets its height from its width.</Text>
80
+ <View variant="card" minHeight="96" justify="center">
81
+ <Text>At least 96 points tall</Text>
82
+ </View>
83
+ <View variant="card" maxWidth="200">
84
+ <Text>No wider than 200 points, however wide the phone is</Text>
85
+ </View>
86
+ <View background="muted" radius="large" aspect="16:9" justify="center" align="center">
87
+ <Text>16:9</Text>
88
+ </View>
89
+ </View>
90
+
91
+ <View gap="small">
92
+ <Text variant="subtitle">Together</Text>
93
+ <Text variant="caption" tone="muted">A price with even digits, and the old one crossed out.</Text>
94
+ <View variant="card" direction="row" align="center" gap="small">
95
+ <Text variant="subtitle">Freetown Jazz Night</Text>
96
+ <View grow />
97
+ <Text tone="muted" decoration="strikethrough" numeric>SLE 150</Text>
98
+ <Text weight="bold" numeric>SLE 120</Text>
99
+ </View>
100
+ </View>
101
+ </View>
@@ -0,0 +1,41 @@
1
+ import type { Release } from './state';
2
+ import state from './state';
3
+
4
+ export default definePage({
5
+ initialState: state,
6
+ configs: { navigationBar: { title: 'Tabs and chips' } },
7
+ mutations: {
8
+ chooseTab: (state, event: ValueEvent) => {
9
+ const tab = event.detail.value;
10
+ return { tab, shown: filter(state.releases, { ...state, tab }) };
11
+ },
12
+ chooseGenre: (state, event: DataEvent<{ genre: string }>) => {
13
+ const { genre } = event.data;
14
+ return { genre, shown: filter(state.releases, { ...state, genre }) };
15
+ },
16
+ search: (state, event: ValueEvent) => {
17
+ const query = event.detail.value;
18
+ return { query, shown: filter(state.releases, { ...state, query }) };
19
+ },
20
+ clearFilters: (state) => ({ tab: 'all', genre: 'All', query: '', shown: state.releases }),
21
+ chooseWhere: (_state, event: ValueEvent) => ({ where: event.detail.value }),
22
+ toggleLiked: (state) => ({ liked: !state.liked }),
23
+ },
24
+ });
25
+
26
+ interface Filters {
27
+ tab: string;
28
+ genre: string;
29
+ // Words the title contains, in any case.
30
+ query: string;
31
+ }
32
+
33
+ function filter(releases: readonly Release[], { tab, genre, query }: Filters): Release[] {
34
+ const words = query.trim().toLowerCase();
35
+ return releases.filter(
36
+ (release) =>
37
+ (tab === 'all' || release.kind === tab) &&
38
+ (genre === 'All' || release.genre === genre) &&
39
+ release.title.toLowerCase().includes(words),
40
+ );
41
+ }
@@ -67,7 +67,9 @@ export default {
67
67
  songs: releases.filter((item) => item.kind === 'song').length,
68
68
  genres: ['All', 'Kriofusion', 'Afropop', 'Hip hop', 'R&B', 'Palm-wine', 'Gospel', 'Bubu'],
69
69
  genre: 'All',
70
- // Gen Z's shows, at home in Sierra Leone and abroad.
70
+ query: '',
71
+ liked: false,
72
+ // Shows at home in Sierra Leone, and abroad in the Mano River region.
71
73
  where: 'home',
72
74
  home: [
73
75
  {
@@ -84,35 +86,42 @@ export default {
84
86
  date: '28 November 2025',
85
87
  city: 'Freetown',
86
88
  },
87
- ] as Show[],
88
- abroad: [
89
89
  {
90
- id: 'detty-rave',
91
- title: 'Detty Rave, with Mr Eazi',
92
- place: 'Drizilik joins him for "Sabi Road"',
93
- date: 'December 2025',
94
- city: 'Accra',
90
+ id: 'christmas-jam',
91
+ title: 'Christmas Jam',
92
+ place: 'Coronation Field',
93
+ date: '26 December 2025',
94
+ city: 'Bo',
95
+ },
96
+ {
97
+ id: 'gospel-night',
98
+ title: 'Gospel Night',
99
+ place: 'Kenema City Hall',
100
+ date: '15 November 2025',
101
+ city: 'Kenema',
95
102
  },
103
+ ] as Show[],
104
+ abroad: [
96
105
  {
97
- id: 'steel-yard',
98
- title: 'Drizilik Live In London',
99
- place: 'The Steel Yard',
100
- date: '16 November 2025',
101
- city: 'London',
106
+ id: 'monrovia-live',
107
+ title: 'Salone Sounds Live',
108
+ place: 'EJS Ministerial Complex',
109
+ date: '20 December 2025',
110
+ city: 'Monrovia',
102
111
  },
103
112
  {
104
- id: 'howard-theatre',
105
- title: 'Drizilik live',
106
- place: 'Howard Theatre',
107
- date: '6 July 2024',
108
- city: 'Washington',
113
+ id: 'mano-river-festival',
114
+ title: 'Mano River Music Festival',
115
+ place: 'Palais du Peuple',
116
+ date: '8 November 2025',
117
+ city: 'Conakry',
109
118
  },
110
119
  {
111
- id: 'freedom-festival',
112
- title: 'Freedom Festival',
113
- place: "In Hull, Freetown's twin city",
114
- date: '2–3 September 2022',
115
- city: 'Hull',
120
+ id: 'abidjan-night',
121
+ title: 'Salone Night',
122
+ place: 'Palais de la Culture',
123
+ date: '13 September 2025',
124
+ city: 'Abidjan',
116
125
  },
117
126
  ] as Show[],
118
127
  };
@@ -0,0 +1,57 @@
1
+ <View>
2
+ <Text variant="title">Tabs and chips</Text>
3
+ <Text>Tabs change what the page shows. Chips and a search narrow it down. Try Songs with Palm-wine: nothing matches.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">Tabs and Chip</Text>
7
+ <Text variant="caption" tone="muted">The underline tabs, with a badge for each count. A chip is selected when the page says so. An Input with type="search" and clearable makes a search box.</Text>
8
+ <Tabs variant="underline" value="{{ tab }}" onChange="chooseTab">
9
+ <Tab value="all" label="All" />
10
+ <Tab value="album" label="Albums" badge="{{ albums }}" />
11
+ <Tab value="song" label="Songs" badge="{{ songs }}" />
12
+ </Tabs>
13
+ <Input type="search" placeholder="Search by title" clearable value="{{ query }}" onChange="search" />
14
+ <View direction="row" gap="small" wrap>
15
+ <For each="{{ genres }}" item="option" key="*this">
16
+ <Chip label="{{ option }}" selected="{{ option === genre }}" data-genre="{{ option }}" onTap="chooseGenre" />
17
+ </For>
18
+ </View>
19
+ <If condition="{{ shown.length > 0 }}">
20
+ <List>
21
+ <For each="{{ shown }}" item="release" key="id">
22
+ <ListItem icon="{{ release.kind === 'album' ? 'playlist' : 'music' }}" title="{{ release.title }}" subtitle="{{ release.artist }} · {{ release.genre }}">
23
+ <Badge>{{ release.year }}</Badge>
24
+ </ListItem>
25
+ </For>
26
+ </List>
27
+ </If>
28
+ <Else>
29
+ <EmptyState icon="search" title="Nothing here" text="No release matches this tab, genre and search.">
30
+ <Button variant="secondary" onClick="clearFilters">Clear filters</Button>
31
+ </EmptyState>
32
+ </Else>
33
+ </View>
34
+
35
+ <View gap="small">
36
+ <Text variant="subtitle">Chip with an icon</Text>
37
+ <Text variant="caption" tone="muted">A disabled chip can't be tapped, such as a filter with nothing behind it.</Text>
38
+ <View direction="row" gap="small" wrap>
39
+ <Chip icon="heart" label="Liked" selected="{{ liked }}" onTap="toggleLiked" />
40
+ <Chip icon="download" label="Downloaded" disabled />
41
+ </View>
42
+ </View>
43
+
44
+ <View gap="small">
45
+ <Text variant="subtitle">variant="segmented"</Text>
46
+ <Text variant="caption" tone="muted">For a few short choices. Shows at home and in the Mano River region.</Text>
47
+ <Tabs variant="segmented" value="{{ where }}" onChange="chooseWhere">
48
+ <Tab value="home" label="Sierra Leone" />
49
+ <Tab value="abroad" label="Abroad" />
50
+ </Tabs>
51
+ <List>
52
+ <For each="{{ where === 'home' ? home : abroad }}" item="show" key="id">
53
+ <ListItem icon="ticket" title="{{ show.title }}" subtitle="{{ show.place }} · {{ show.date }}" value="{{ show.city }}" />
54
+ </For>
55
+ </List>
56
+ </View>
57
+ </View>
@@ -0,0 +1,10 @@
1
+ import state from './state';
2
+
3
+ export default definePage({
4
+ initialState: state,
5
+ configs: { navigationBar: { title: 'Text and links' } },
6
+ mutations: {
7
+ like: (state) => ({ likes: state.likes + 1 }),
8
+ unlike: (state) => ({ likes: Math.max(0, state.likes - 1) }),
9
+ },
10
+ });
@@ -0,0 +1,3 @@
1
+ export default {
2
+ likes: 12,
3
+ };
@@ -0,0 +1,95 @@
1
+ <View>
2
+ <Text variant="title">Text and links</Text>
3
+ <Text>Text shows words. Its variant sets the size, and the theme sets the font and colours.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">variant</Text>
7
+ <View variant="card" gap="small">
8
+ <Text variant="display" numeric>SLE 1,250</Text>
9
+ <Text variant="title">Palm-wine, a title</Text>
10
+ <Text variant="subtitle">S. E. Rogie, a subtitle</Text>
11
+ <Text variant="lead">A lead opens an article. It's a little bigger than body text.</Text>
12
+ <Text variant="body">Guitar music for a calm evening. This is body text, the default.</Text>
13
+ <Text variant="caption">Recorded in Freetown, in a caption</Text>
14
+ </View>
15
+ </View>
16
+
17
+ <View gap="small">
18
+ <Text variant="subtitle">tone</Text>
19
+ <View variant="card" direction="row" wrap gap="medium">
20
+ <Text tone="default">default</Text>
21
+ <Text tone="muted">muted</Text>
22
+ <Text tone="info">info</Text>
23
+ <Text tone="success">success</Text>
24
+ <Text tone="warning">warning</Text>
25
+ <Text tone="danger">danger</Text>
26
+ </View>
27
+ </View>
28
+
29
+ <View gap="small">
30
+ <Text variant="subtitle">weight, italic and decoration</Text>
31
+ <View variant="card" gap="small">
32
+ <View direction="row" wrap gap="medium">
33
+ <Text weight="regular">regular</Text>
34
+ <Text weight="medium">medium</Text>
35
+ <Text weight="bold">bold</Text>
36
+ <Text italic>italic</Text>
37
+ </View>
38
+ <View direction="row" wrap gap="medium">
39
+ <Text decoration="underline">underline</Text>
40
+ <Text decoration="strikethrough">strikethrough</Text>
41
+ <Text decoration="none">none</Text>
42
+ </View>
43
+ </View>
44
+ </View>
45
+
46
+ <View gap="small">
47
+ <Text variant="subtitle">align and numeric</Text>
48
+ <Text variant="caption" tone="muted">numeric gives every digit the same width, so amounts line up.</Text>
49
+ <View variant="card" gap="small">
50
+ <Text align="start">At the start</Text>
51
+ <Text align="center">In the center</Text>
52
+ <Text align="end">At the end</Text>
53
+ <Text align="end" numeric>SLE 1,111.11</Text>
54
+ <Text align="end" numeric>SLE 8,888.88</Text>
55
+ </View>
56
+ </View>
57
+
58
+ <View gap="small">
59
+ <Text variant="subtitle">maxLines</Text>
60
+ <Text variant="caption" tone="muted">Cuts the text off after this many lines.</Text>
61
+ <View variant="card">
62
+ <Text maxLines="2">Sierra Leone's Refugee All Stars came together in refugee camps in Guinea, recorded their first album, Living Like a Refugee, back in Freetown, and carried Salone music to stages around the world. Two lines are allowed here, so the rest is cut off.</Text>
63
+ </View>
64
+ </View>
65
+
66
+ <View gap="small">
67
+ <Text variant="subtitle">leading and selectable</Text>
68
+ <Text variant="caption" tone="muted">leading moves the lines closer or further apart. selectable lets the person copy the text: hold your finger on the last one.</Text>
69
+ <View variant="card" gap="medium">
70
+ <Text leading="tight">Tight lines. Palm-wine music grew in the bars of Freetown, played on guitar and sung in Krio.</Text>
71
+ <Text leading="normal">Normal lines. Palm-wine music grew in the bars of Freetown, played on guitar and sung in Krio.</Text>
72
+ <Text leading="relaxed" selectable>Relaxed lines, and you can select them. Palm-wine music grew in the bars of Freetown, played on guitar and sung in Krio.</Text>
73
+ </View>
74
+ </View>
75
+
76
+ <View gap="small">
77
+ <Text variant="subtitle">Text inside text</Text>
78
+ <View variant="card">
79
+ <Text>Text can hold <Text weight="bold">bold</Text> and <Text tone="danger">red</Text> words.</Text>
80
+ </View>
81
+ </View>
82
+
83
+ <View gap="small">
84
+ <Text variant="subtitle">Link</Text>
85
+ <Text variant="caption" tone="muted">With href, a link opens a web page in the phone's browser. With onTap, it runs a handler.</Text>
86
+ <View variant="card" gap="small">
87
+ <Text>Read about <Link href="https://en.wikipedia.org/wiki/Music_of_Sierra_Leone">the music of Sierra Leone</Link>.</Text>
88
+ <View direction="row" gap="medium">
89
+ <Link onTap="like">Like the song</Link>
90
+ <Link tone="danger" onTap="unlike">Take it back</Link>
91
+ </View>
92
+ <Text tone="muted">{{ likes }} {{ likes === 1 ? 'like' : 'likes' }}</Text>
93
+ </View>
94
+ </View>
95
+ </View>
@@ -0,0 +1,32 @@
1
+ import { errorMessage } from '@monimesl/dot-runtime';
2
+
3
+ import state from './state';
4
+
5
+ export default definePage({
6
+ initialState: state,
7
+ configs: { navigationBar: { title: 'Account' } },
8
+ mutations: {
9
+ setNonce: (_state, event: ValueEvent) => ({ nonce: event.detail.value }),
10
+ },
11
+ actions: {
12
+ async signIn(state) {
13
+ try {
14
+ const { fullName, alias, accountIdToken } = await this.dash.account.signIn({
15
+ mode: 'simple',
16
+ nonce: state.nonce,
17
+ });
18
+ this.setState({ fullName, alias, accountIdToken, note: '' });
19
+ } catch (error) {
20
+ this.setState({ note: errorMessage(error) });
21
+ }
22
+ },
23
+ async signInWithOidc() {
24
+ try {
25
+ const { id } = await this.dash.account.signIn({ mode: 'oidc', scopes: ['profile'] });
26
+ this.setState({ oidcNote: `Signed in as ${id}` });
27
+ } catch (error) {
28
+ this.setState({ oidcNote: errorMessage(error) });
29
+ }
30
+ },
31
+ },
32
+ });
@@ -0,0 +1,9 @@
1
+ export default {
2
+ // A fixed nonce, so the token names the person the same way each time in this dot.
3
+ nonce: 'salone-sounds',
4
+ fullName: '',
5
+ alias: '',
6
+ accountIdToken: '',
7
+ note: '',
8
+ oidcNote: '',
9
+ };
@@ -0,0 +1,33 @@
1
+ <View>
2
+ <Text variant="title">dash.account</Text>
3
+ <Text>Who is signed in to Dash. It needs the account permission in dot.json.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">signIn</Text>
7
+ <Text variant="caption" tone="muted">With mode "simple", Dash says who the person is: their full name, the phone number or email address they use Dash with, and a token Monime makes from this dot's id, their Monimeer id and the nonce, with a key only it knows. The same nonce gives the same token, on any phone. It names the person, but proves nothing to the dot's server.</Text>
8
+ <View variant="card" gap="medium">
9
+ <Input label="Nonce" value="{{ nonce }}" onChange="setNonce" />
10
+ <Button onClick="signIn">Sign in with Dash</Button>
11
+ <If condition="{{ fullName }}">
12
+ <View gap="none">
13
+ <DetailRow label="fullName" value="{{ fullName }}" />
14
+ <DetailRow label="alias" value="{{ alias }}" />
15
+ <DetailRow label="accountIdToken" value="{{ accountIdToken }}" />
16
+ </View>
17
+ </If>
18
+ </View>
19
+ </View>
20
+
21
+ <View gap="small">
22
+ <Text variant="subtitle">signIn with OpenID Connect</Text>
23
+ <Text variant="caption" tone="muted">With mode "oidc", Dash will give an ID token the dot's server checks with Monime's keys, and only the details the person agrees to share. It isn't ready yet, so Dash says so.</Text>
24
+ <Button variant="secondary" onClick="signInWithOidc">Sign in with OpenID Connect</Button>
25
+ <If condition="{{ oidcNote }}">
26
+ <Notice tone="info">{{ oidcNote }}</Notice>
27
+ </If>
28
+ </View>
29
+
30
+ <If condition="{{ note }}">
31
+ <Notice tone="danger">{{ note }}</Notice>
32
+ </If>
33
+ </View>
@@ -2,10 +2,11 @@ import state from './state';
2
2
 
3
3
  const RECENT = 'recent/';
4
4
  const GENRE = 'genre';
5
- const RECENT_SECONDS = 30;
5
+ const RECENT_MILLISECONDS = 30_000;
6
6
 
7
7
  export default definePage({
8
8
  initialState: state,
9
+ configs: { navigationBar: { title: 'Cache' } },
9
10
  hooks: {
10
11
  async onShow() {
11
12
  await this.actions.refresh();
@@ -26,17 +27,21 @@ export default definePage({
26
27
  });
27
28
  },
28
29
  // Each view lasts 30 seconds, then the cache forgets it.
29
- async view(_state, event: DotEvent<unknown, { id: string; name: string }>) {
30
+ async view(_state, event: DataEvent<{ id: string; name: string }>) {
30
31
  await this.dash.storage.cache.set(`${RECENT}${event.data.id}`, event.data.name, {
31
- ttl: RECENT_SECONDS,
32
+ ttl: RECENT_MILLISECONDS,
32
33
  });
33
34
  await this.actions.refresh();
34
35
  },
35
36
  // With no ttl, the genre lasts until it's removed.
36
- async setGenre(_state, event: DotEvent<{ value: string }>) {
37
+ async setGenre(_state, event: ValueEvent) {
37
38
  await this.dash.storage.cache.set(GENRE, event.detail.value);
38
39
  await this.actions.refresh();
39
40
  },
41
+ async forgetGenre() {
42
+ await this.dash.storage.cache.remove(GENRE);
43
+ await this.actions.refresh();
44
+ },
40
45
  async clear() {
41
46
  await this.dash.storage.cache.clear();
42
47
  await this.actions.refresh();
@@ -0,0 +1,48 @@
1
+ <View>
2
+ <Text variant="title">dash.storage.cache</Text>
3
+ <Text>The cache keeps small values by name, such as text, numbers or JSON. A value can expire after a while, or stay until it's removed.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">set with a ttl, keys and get</Text>
7
+ <Text variant="caption" tone="muted">Tap an artist. ttl: 30_000 keeps it for 30 seconds, as times are in milliseconds.</Text>
8
+ <List>
9
+ <For each="{{ artists }}" item="artist" key="id">
10
+ <ListItem icon="microphone" title="{{ artist.name }}" data-id="{{ artist.id }}" data-name="{{ artist.name }}" onTap="view" />
11
+ </For>
12
+ </List>
13
+ <View variant="card" gap="small">
14
+ <If condition="{{ recent.length > 0 }}">
15
+ <For each="{{ recent }}" item="name" key="*this">
16
+ <Text>{{ name }}</Text>
17
+ </For>
18
+ </If>
19
+ <Else>
20
+ <Text variant="caption" tone="muted">No artists viewed in the last 30 seconds</Text>
21
+ </Else>
22
+ <Button variant="secondary" onClick="refresh">Check again</Button>
23
+ </View>
24
+ </View>
25
+
26
+ <View gap="small">
27
+ <Text variant="subtitle">set without a ttl, and exists</Text>
28
+ <Text variant="caption" tone="muted">Your favourite genre stays until it's removed, even after the dot closes.</Text>
29
+ <View variant="card">
30
+ <Select label="Genre" placeholder="Choose a genre" value="{{ genre }}" onChange="setGenre">
31
+ <Option value="Afropop" label="Afropop" />
32
+ <Option value="Palm-wine" label="Palm-wine" />
33
+ <Option value="Gumbe" label="Gumbe" />
34
+ </Select>
35
+ </View>
36
+ </View>
37
+
38
+ <View gap="small">
39
+ <Text variant="subtitle">remove and clear</Text>
40
+ <View variant="card" gap="small">
41
+ <Text>Keys in the cache: {{ keys || 'none' }}</Text>
42
+ <View direction="row" wrap gap="small">
43
+ <Button variant="secondary" onClick="forgetGenre" disabled="{{ !genre }}">Forget the genre</Button>
44
+ <Button variant="secondary" onClick="clear" disabled="{{ !keys }}">Clear the cache</Button>
45
+ </View>
46
+ </View>
47
+ </View>
48
+ </View>
@@ -0,0 +1,67 @@
1
+ import type { DashRequest } from '@monimesl/dot-runtime';
2
+ import { errorMessage, isDashError } from '@monimesl/dot-runtime';
3
+
4
+ import state from './state';
5
+
6
+ // The long wait, kept so the Cancel button can stop it.
7
+ let wait: DashRequest<void> | null = null;
8
+
9
+ export default definePage({
10
+ initialState: state,
11
+ configs: { navigationBar: { title: 'Calls and errors' } },
12
+ hooks: {
13
+ async onLoad() {
14
+ this.setState({ picksPhones: this.dash.supports('contacts.pickPhone') });
15
+ try {
16
+ const { apiVersions } = await this.dash.system.info();
17
+ this.setState({ apiVersions: apiVersions.join(', ') });
18
+ } catch (error) {
19
+ this.setState({ apiVersions: errorMessage(error) });
20
+ }
21
+ },
22
+ },
23
+ actions: {
24
+ // Makes the call a button names, and shows how it ended.
25
+ async show(_state, event: DataEvent<{ call: string }>) {
26
+ this.setState({ code: '', message: '' });
27
+ try {
28
+ switch (event.data.call) {
29
+ case 'timeout':
30
+ // Gives up after 1 second on a wait of 5.
31
+ await this.dash.system.delay(5000, { timeout: 1000 });
32
+ break;
33
+ case 'cancelled':
34
+ wait = this.dash.system.delay(10_000);
35
+ this.setState({ waiting: true });
36
+ await wait;
37
+ break;
38
+ case 'invalid':
39
+ await this.dash.ui.pickDate({ min: '2026-12-31', max: '2026-01-01' });
40
+ break;
41
+ case 'denied':
42
+ await this.dash.network.http.request({ url: 'https://example.com' });
43
+ break;
44
+ case 'missing':
45
+ await this.dash.storage.file.read('never-saved.txt');
46
+ break;
47
+ }
48
+ this.setState({ code: 'none', message: 'It worked' });
49
+ } catch (error) {
50
+ if (isDashError(error)) {
51
+ this.setState({ code: error.code, message: error.message });
52
+ } else {
53
+ this.setState({ code: 'not a DashError', message: errorMessage(error) });
54
+ }
55
+ } finally {
56
+ wait = null;
57
+ this.setState({ waiting: false });
58
+ }
59
+ },
60
+ async closeDot() {
61
+ await this.dash.dot.close();
62
+ },
63
+ cancelWait() {
64
+ wait?.cancel();
65
+ },
66
+ },
67
+ });
@@ -0,0 +1,9 @@
1
+ export default {
2
+ waiting: false,
3
+ // The last call's outcome: the DashError's code and message, or how it ended.
4
+ code: '',
5
+ message: '',
6
+ // What this Dash offers, for the supports section.
7
+ apiVersions: '',
8
+ picksPhones: false,
9
+ };