@monimesl/dot-cli 0.1.0-alpha.16 → 0.1.0-alpha.18

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 (108) hide show
  1. package/dist/cli.js +16 -9
  2. package/dist/cli.js.map +1 -1
  3. package/dist/templates/showcase/dot.ts +32 -17
  4. package/dist/templates/showcase/pages/basics/bars/controller.ts +36 -0
  5. package/dist/templates/showcase/pages/basics/bars/state.ts +6 -0
  6. package/dist/templates/showcase/pages/basics/bars/view.dml +28 -0
  7. package/dist/templates/showcase/pages/{text → basics/bindings}/controller.ts +4 -0
  8. package/dist/templates/showcase/pages/basics/bindings/state.ts +14 -0
  9. package/dist/templates/showcase/pages/basics/bindings/view.dml +41 -0
  10. package/dist/templates/showcase/pages/basics/control-flow/controller.ts +17 -0
  11. package/dist/templates/showcase/pages/basics/control-flow/state.ts +16 -0
  12. package/dist/templates/showcase/pages/basics/control-flow/view.dml +55 -0
  13. package/dist/templates/showcase/pages/basics/events/controller.ts +42 -0
  14. package/dist/templates/showcase/pages/basics/events/state.ts +11 -0
  15. package/dist/templates/showcase/pages/basics/events/view.dml +51 -0
  16. package/dist/templates/showcase/pages/basics/hooks/controller.ts +36 -0
  17. package/dist/templates/showcase/pages/basics/hooks/state.ts +10 -0
  18. package/dist/templates/showcase/pages/basics/hooks/view.dml +32 -0
  19. package/dist/templates/showcase/pages/basics/navigation/controller.ts +33 -0
  20. package/dist/templates/showcase/pages/basics/navigation/state.ts +9 -0
  21. package/dist/templates/showcase/pages/basics/navigation/view.dml +37 -0
  22. package/dist/templates/showcase/pages/basics/state/view.dml +46 -0
  23. package/dist/templates/showcase/pages/{carousel → components/carousel}/view.dml +7 -5
  24. package/dist/templates/showcase/pages/{feedback → components/feedback}/view.dml +18 -9
  25. package/dist/templates/showcase/pages/{forms → components/forms}/controller.ts +4 -1
  26. package/dist/templates/showcase/pages/{forms → components/forms}/state.ts +3 -0
  27. package/dist/templates/showcase/pages/components/forms/view.dml +90 -0
  28. package/dist/templates/showcase/pages/components/icons/controller.ts +14 -0
  29. package/dist/templates/showcase/pages/components/icons/state.ts +75 -0
  30. package/dist/templates/showcase/pages/components/icons/view.dml +36 -0
  31. package/dist/templates/showcase/pages/components/images/view.dml +70 -0
  32. package/dist/templates/showcase/pages/components/layout/view.dml +143 -0
  33. package/dist/templates/showcase/pages/components/lists/controller.ts +17 -0
  34. package/dist/templates/showcase/pages/{lists → components/lists}/state.ts +43 -30
  35. package/dist/templates/showcase/pages/components/lists/view.dml +70 -0
  36. package/dist/templates/showcase/pages/{payments → components/money}/view.dml +17 -8
  37. package/dist/templates/showcase/pages/components/reading/state.ts +23 -0
  38. package/dist/templates/showcase/pages/components/reading/view.dml +42 -0
  39. package/dist/templates/showcase/pages/{lists → components/sheets}/controller.ts +8 -10
  40. package/dist/templates/showcase/pages/components/sheets/state.ts +26 -0
  41. package/dist/templates/showcase/pages/components/sheets/view.dml +38 -0
  42. package/dist/templates/showcase/pages/components/styles/controller.ts +5 -0
  43. package/dist/templates/showcase/pages/components/styles/view.dml +101 -0
  44. package/dist/templates/showcase/pages/{filters → components/tabs}/controller.ts +1 -0
  45. package/dist/templates/showcase/pages/{filters → components/tabs}/state.ts +1 -0
  46. package/dist/templates/showcase/pages/components/tabs/view.dml +56 -0
  47. package/dist/templates/showcase/pages/components/text/controller.ts +9 -0
  48. package/dist/templates/showcase/pages/components/text/state.ts +3 -0
  49. package/dist/templates/showcase/pages/components/text/view.dml +95 -0
  50. package/dist/templates/showcase/pages/{cache → dash/cache}/controller.ts +4 -0
  51. package/dist/templates/showcase/pages/dash/cache/view.dml +48 -0
  52. package/dist/templates/showcase/pages/dash/calls/controller.ts +48 -0
  53. package/dist/templates/showcase/pages/dash/calls/state.ts +6 -0
  54. package/dist/templates/showcase/pages/dash/calls/view.dml +51 -0
  55. package/dist/templates/showcase/pages/dash/files/view.dml +79 -0
  56. package/dist/templates/showcase/pages/dash/media/controller.ts +32 -0
  57. package/dist/templates/showcase/pages/{device → dash/media}/state.ts +0 -4
  58. package/dist/templates/showcase/pages/dash/media/view.dml +34 -0
  59. package/dist/templates/showcase/pages/dash/network/view.dml +80 -0
  60. package/dist/templates/showcase/pages/dash/secrets/view.dml +26 -0
  61. package/dist/templates/showcase/pages/dash/system/controller.ts +61 -0
  62. package/dist/templates/showcase/pages/dash/system/state.ts +9 -0
  63. package/dist/templates/showcase/pages/dash/system/view.dml +59 -0
  64. package/dist/templates/showcase/pages/dash/ui/controller.ts +69 -0
  65. package/dist/templates/showcase/pages/dash/ui/state.ts +8 -0
  66. package/dist/templates/showcase/pages/dash/ui/view.dml +31 -0
  67. package/dist/templates/showcase/pages/index/state.ts +135 -64
  68. package/dist/templates/showcase/pages/index/view.dml +6 -3
  69. package/package.json +6 -6
  70. package/dist/templates/showcase/pages/cache/view.dml +0 -36
  71. package/dist/templates/showcase/pages/device/controller.ts +0 -105
  72. package/dist/templates/showcase/pages/device/view.dml +0 -69
  73. package/dist/templates/showcase/pages/files/view.dml +0 -64
  74. package/dist/templates/showcase/pages/filters/view.dml +0 -44
  75. package/dist/templates/showcase/pages/forms/view.dml +0 -49
  76. package/dist/templates/showcase/pages/images/view.dml +0 -53
  77. package/dist/templates/showcase/pages/layout/view.dml +0 -103
  78. package/dist/templates/showcase/pages/lists/view.dml +0 -59
  79. package/dist/templates/showcase/pages/navigation/controller.ts +0 -65
  80. package/dist/templates/showcase/pages/navigation/state.ts +0 -17
  81. package/dist/templates/showcase/pages/navigation/view.dml +0 -34
  82. package/dist/templates/showcase/pages/network/view.dml +0 -67
  83. package/dist/templates/showcase/pages/secrets/view.dml +0 -24
  84. package/dist/templates/showcase/pages/state/view.dml +0 -33
  85. package/dist/templates/showcase/pages/styles/view.dml +0 -61
  86. package/dist/templates/showcase/pages/text/state.ts +0 -8
  87. package/dist/templates/showcase/pages/text/view.dml +0 -58
  88. /package/dist/templates/showcase/pages/{state → basics/state}/controller.ts +0 -0
  89. /package/dist/templates/showcase/pages/{state → basics/state}/state.ts +0 -0
  90. /package/dist/templates/showcase/pages/{carousel → components/carousel}/controller.ts +0 -0
  91. /package/dist/templates/showcase/pages/{carousel → components/carousel}/state.ts +0 -0
  92. /package/dist/templates/showcase/pages/{feedback → components/feedback}/controller.ts +0 -0
  93. /package/dist/templates/showcase/pages/{feedback → components/feedback}/state.ts +0 -0
  94. /package/dist/templates/showcase/pages/{images → components/images}/controller.ts +0 -0
  95. /package/dist/templates/showcase/pages/{images → components/images}/state.ts +0 -0
  96. /package/dist/templates/showcase/pages/{layout → components/layout}/controller.ts +0 -0
  97. /package/dist/templates/showcase/pages/{layout → components/layout}/state.ts +0 -0
  98. /package/dist/templates/showcase/pages/{payments → components/money}/controller.ts +0 -0
  99. /package/dist/templates/showcase/pages/{payments → components/money}/state.ts +0 -0
  100. /package/dist/templates/showcase/pages/{styles → components/reading}/controller.ts +0 -0
  101. /package/dist/templates/showcase/pages/{styles → components/styles}/state.ts +0 -0
  102. /package/dist/templates/showcase/pages/{cache → dash/cache}/state.ts +0 -0
  103. /package/dist/templates/showcase/pages/{files → dash/files}/controller.ts +0 -0
  104. /package/dist/templates/showcase/pages/{files → dash/files}/state.ts +0 -0
  105. /package/dist/templates/showcase/pages/{network → dash/network}/controller.ts +0 -0
  106. /package/dist/templates/showcase/pages/{network → dash/network}/state.ts +0 -0
  107. /package/dist/templates/showcase/pages/{secrets → dash/secrets}/controller.ts +0 -0
  108. /package/dist/templates/showcase/pages/{secrets → dash/secrets}/state.ts +0 -0
@@ -0,0 +1,51 @@
1
+ <View>
2
+ <Text variant="title">Calls and errors</Text>
3
+ <Text>Every this.dash call goes to Dash and comes back later, so await it. When it fails, it throws a HostError with a code that says why.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">timeout</Text>
7
+ <Text variant="caption" tone="muted">The last argument of a call can set a timeout, in milliseconds. This waits 5 seconds with a timeout of 1.</Text>
8
+ <Button variant="secondary" data-call="timeout" onClick="show">Wait too long</Button>
9
+ </View>
10
+
11
+ <View gap="small">
12
+ <Text variant="subtitle">cancel</Text>
13
+ <Text variant="caption" tone="muted">A call can be cancelled while it runs. Calls still running when the page closes are cancelled for you.</Text>
14
+ <View direction="row" gap="small">
15
+ <Button variant="secondary" data-call="cancelled" onClick="show" disabled="{{ waiting }}">Wait 10 seconds</Button>
16
+ <Button variant="secondary" onClick="cancelWait" disabled="{{ !waiting }}">Cancel</Button>
17
+ </View>
18
+ </View>
19
+
20
+ <View gap="small">
21
+ <Text variant="subtitle">HostError codes</Text>
22
+ <View variant="card" gap="none">
23
+ <DetailRow label="cancelled" value="The person closed it, or the call was cancelled" />
24
+ <DetailRow label="timeout" value="It took longer than the timeout" />
25
+ <DetailRow label="denied" value="Not allowed, such as a missing permission" />
26
+ <DetailRow label="invalid" value="The call's options are wrong" />
27
+ <DetailRow label="unsupported" value="This Dash can't do it" />
28
+ <DetailRow label="failed" value="Anything else went wrong" />
29
+ </View>
30
+ <View direction="row" wrap gap="small">
31
+ <Button variant="secondary" data-call="invalid" onClick="show">Pick a date between bad limits</Button>
32
+ <Button variant="secondary" data-call="denied" onClick="show">Reach an unlisted host</Button>
33
+ </View>
34
+ </View>
35
+
36
+ <View gap="small">
37
+ <Text variant="subtitle">The last call</Text>
38
+ <View variant="card" gap="none">
39
+ <If condition="{{ code }}">
40
+ <DetailRow label="error.code" value="{{ code }}" weight="bold" />
41
+ <DetailRow label="error.message" value="{{ message }}" />
42
+ </If>
43
+ <ElseIf condition="{{ waiting }}">
44
+ <Text tone="muted">Waiting…</Text>
45
+ </ElseIf>
46
+ <Else>
47
+ <Text tone="muted">Use a button above</Text>
48
+ </Else>
49
+ </View>
50
+ </View>
51
+ </View>
@@ -0,0 +1,79 @@
1
+ <View>
2
+ <Text variant="title">dash.storage.file</Text>
3
+ <Text>Each dot has its own folder on the phone. Other dots can't see it, and its files stay after the dot closes.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">write, read and list</Text>
7
+ <Text variant="caption" tone="muted">A setlist, where each song is a text file. Add some, then close and reopen the dot: they are still here.</Text>
8
+ <View variant="card" gap="small">
9
+ <Input label="Song" placeholder="Exodus, by Drizilik" value="{{ draft }}" maxLength="80" onChange="setDraft" onSubmit="save" />
10
+ <View direction="row" gap="small" wrap>
11
+ <Button onClick="save" disabled="{{ !draft }}">Add to the setlist</Button>
12
+ <Button variant="secondary" onClick="archive" disabled="{{ songs.length === 0 }}">Archive it</Button>
13
+ <Button variant="secondary" onClick="clearSetlist" disabled="{{ songs.length === 0 }}">Clear it</Button>
14
+ </View>
15
+ <For each="{{ songs }}" item="song" key="path">
16
+ <View gap="none">
17
+ <Text>{{ song.text }}</Text>
18
+ <Text variant="caption" tone="muted">{{ song.path }}</Text>
19
+ </View>
20
+ </For>
21
+ <If condition="{{ songs.length === 0 }}">
22
+ <Text variant="caption" tone="muted">No songs yet</Text>
23
+ </If>
24
+ </View>
25
+ </View>
26
+
27
+ <View gap="small">
28
+ <Text variant="subtitle">createFolder, move, copy and remove</Text>
29
+ <Text variant="caption" tone="muted">Archive it moves the setlist folder into archive/. Tap an archived setlist to copy it back. Clear it removes the folder and all in it.</Text>
30
+ <View variant="card" gap="small">
31
+ <If condition="{{ archives.length > 0 }}">
32
+ <List>
33
+ <For each="{{ archives }}" item="archive" key="path">
34
+ <ListItem icon="playlist" title="Archived at {{ archive.time }}" subtitle="{{ archive.path }}" value="Copy back" data-path="{{ archive.path }}" onTap="copyBack" />
35
+ </For>
36
+ </List>
37
+ </If>
38
+ <Else>
39
+ <Text variant="caption" tone="muted">None yet. Add songs, then archive the setlist.</Text>
40
+ </Else>
41
+ </View>
42
+ </View>
43
+
44
+ <View gap="small">
45
+ <Text variant="subtitle">pick and save</Text>
46
+ <Text variant="caption" tone="muted">pick lets the person choose a file from the phone, with the files permission. A picked file goes when the dot closes, so save copies it into the dot's folder.</Text>
47
+ <View variant="card" gap="small">
48
+ <Button variant="secondary" onClick="attach">Attach your concert ticket</Button>
49
+ <For each="{{ documents }}" item="document" key="path">
50
+ <View gap="none">
51
+ <Text>{{ document.name }}</Text>
52
+ <Text variant="caption" tone="muted">{{ document.path }}</Text>
53
+ </View>
54
+ </For>
55
+ <If condition="{{ attachNote }}">
56
+ <Text variant="caption" tone="muted">{{ attachNote }}</Text>
57
+ </If>
58
+ </View>
59
+ </View>
60
+
61
+ <View gap="small">
62
+ <Text variant="subtitle">stat and open</Text>
63
+ <Text variant="caption" tone="muted">stat tells whether a file is there. open gives an address to show it with. This is the artwork kept on the dash.network page.</Text>
64
+ <View variant="card" gap="small">
65
+ <If condition="{{ picture }}">
66
+ <Image src="{{ picture }}" alt="Kept artwork" width="64" height="64" />
67
+ <Button variant="secondary" onClick="forgetPicture">Forget it</Button>
68
+ </If>
69
+ <Else>
70
+ <Text variant="caption" tone="muted">None yet. On the dash.network page, search, download the artwork and tap Keep.</Text>
71
+ </Else>
72
+ </View>
73
+ </View>
74
+
75
+ <View gap="small">
76
+ <Text variant="subtitle">usage</Text>
77
+ <Text>{{ usage }}</Text>
78
+ </View>
79
+ </View>
@@ -0,0 +1,32 @@
1
+ import { HostError } from '@monimesl/dot-runtime';
2
+
3
+ import state from './state';
4
+
5
+ // Closing a picker or scanner isn't a problem to show.
6
+ function problemToShow(error: unknown): string {
7
+ const closedByPerson = error instanceof HostError && error.code === 'cancelled';
8
+ return closedByPerson ? '' : (error as Error).message;
9
+ }
10
+
11
+ export default definePage({
12
+ initialState: state,
13
+ actions: {
14
+ async pickPhoto(_state, event: DotEvent<unknown, { source: 'library' | 'camera' }>) {
15
+ try {
16
+ const photo = await this.dash.media.pickPhoto({ source: event.data.source });
17
+ const details = `${photo.name}, ${Math.round(photo.size / 1024)} KB as a JPEG`;
18
+ this.setState({ photo, photoDetails: details, note: '' });
19
+ } catch (error) {
20
+ this.setState({ note: problemToShow(error) });
21
+ }
22
+ },
23
+ async scanTicket() {
24
+ try {
25
+ const { text } = await this.dash.media.scanCode();
26
+ this.setState({ scanned: text, note: '' });
27
+ } catch (error) {
28
+ this.setState({ note: problemToShow(error) });
29
+ }
30
+ },
31
+ },
32
+ });
@@ -8,9 +8,5 @@ export default {
8
8
  photo: null as Photo | null,
9
9
  photoDetails: '',
10
10
  scanned: '',
11
- choice: '',
12
- torch: 'off' as 'on' | 'off',
13
11
  note: '',
14
- toast: '',
15
- showIn: 0,
16
12
  };
@@ -0,0 +1,34 @@
1
+ <View>
2
+ <Text variant="title">dash.media</Text>
3
+ <Text>Photos and codes from the camera. Each needs a permission in dot.json, which Dash asks the person for the first time.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">pickPhoto</Text>
7
+ <Text variant="caption" tone="muted">source="library" needs the photoLibrary permission, and source="camera" the camera one. The photo comes back as a JPEG file.</Text>
8
+ <View variant="card" gap="small">
9
+ <View direction="row" wrap gap="small">
10
+ <Button variant="secondary" data-source="library" onClick="pickPhoto">Choose a photo</Button>
11
+ <Button variant="secondary" data-source="camera" onClick="pickPhoto">Take a photo</Button>
12
+ </View>
13
+ <If condition="{{ photo }}">
14
+ <Image src="{{ photo.url }}" alt="The photo you picked" aspect="4:3" fit="contain" />
15
+ <Text variant="caption" tone="muted">{{ photoDetails }}. It goes when the dot closes, unless the dot saves it with dash.storage.file.save.</Text>
16
+ </If>
17
+ </View>
18
+ </View>
19
+
20
+ <View gap="small">
21
+ <Text variant="subtitle">scanCode</Text>
22
+ <Text variant="caption" tone="muted">Opens Dash's scanner until it reads a QR code. It needs the camera permission.</Text>
23
+ <View variant="card" gap="small">
24
+ <Button variant="secondary" onClick="scanTicket">Scan a ticket's QR code</Button>
25
+ <If condition="{{ scanned }}">
26
+ <Text>{{ scanned }}</Text>
27
+ </If>
28
+ </View>
29
+ </View>
30
+
31
+ <If condition="{{ note }}">
32
+ <Notice tone="danger">{{ note }}</Notice>
33
+ </If>
34
+ </View>
@@ -0,0 +1,80 @@
1
+ <View>
2
+ <Text variant="title">dash.network.http</Text>
3
+ <Text>Dash sends the dot's requests, only to the hosts listed in dot.json. This page searches Apple's music catalog.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">request</Text>
7
+ <Text variant="caption" tone="muted">The answer comes as text, or as JSON with as: 'json'. timeout gives up after a while.</Text>
8
+ <View variant="card" gap="small">
9
+ <Input label="Artist" placeholder="Drizilik" value="{{ artist }}" maxLength="40" onChange="setArtist" onSubmit="search" />
10
+ <View direction="row" wrap gap="small">
11
+ <Button onClick="search" loading="{{ searching }}" disabled="{{ !artist }}">Search</Button>
12
+ <Button variant="secondary" onClick="searchAsText" disabled="{{ !artist }}">Read as text</Button>
13
+ </View>
14
+ <If condition="{{ raw }}">
15
+ <Text variant="caption" tone="muted">The answer as text starts with: {{ raw }}</Text>
16
+ </If>
17
+ </View>
18
+
19
+ <Text variant="caption" tone="muted">The same answer read as JSON, with artwork. Near the end, the list loads 10 more, up to 50. {{ releases.length }} so far.</Text>
20
+ <If condition="{{ releases.length > 0 }}">
21
+ <List onEndReached="loadMore">
22
+ <For each="{{ releases }}" item="release" key="id">
23
+ <ListItem image="{{ release.artwork }}" title="{{ release.title }}" subtitle="{{ release.album }} · {{ release.year }}" />
24
+ </For>
25
+ </List>
26
+ </If>
27
+ <Else>
28
+ <View variant="card">
29
+ <EmptyState icon="search" title="No songs yet" text="Search for an artist, such as Drizilik, Kao Denero or Emmerson." />
30
+ </View>
31
+ </Else>
32
+ </View>
33
+
34
+ <View gap="small">
35
+ <Text variant="subtitle">Sending JSON</Text>
36
+ <Text variant="caption" tone="muted">method: 'POST' with a body. httpbin sends back what it got.</Text>
37
+ <View variant="card" gap="small">
38
+ <Button variant="secondary" onClick="sendJson">Send the first song as JSON</Button>
39
+ <If condition="{{ echo }}">
40
+ <Text>httpbin got {{ echo }}</Text>
41
+ </If>
42
+ </View>
43
+ </View>
44
+
45
+ <View gap="small">
46
+ <Text variant="subtitle">download and upload</Text>
47
+ <Text variant="caption" tone="muted">download saves the answer as a file. upload sends files with form fields. Then share the file, or keep it in the dot's folder.</Text>
48
+ <View variant="card" gap="small">
49
+ <View direction="row" wrap gap="small">
50
+ <Button variant="secondary" onClick="download" disabled="{{ releases.length === 0 }}">Download</Button>
51
+ <Button variant="secondary" onClick="upload" disabled="{{ !picture.id }}">Upload</Button>
52
+ <Button variant="secondary" onClick="sharePicture" disabled="{{ !picture.id }}">Share</Button>
53
+ <Button variant="secondary" onClick="keep" disabled="{{ !picture.id || kept }}">Keep</Button>
54
+ </View>
55
+ <If condition="{{ picture.url }}">
56
+ <View direction="row" align="center" gap="medium">
57
+ <Image src="{{ picture.url }}" alt="Artwork" width="64" height="64" />
58
+ <Text variant="caption" tone="muted">{{ picture.size }} bytes, in a temporary file until the dot closes</Text>
59
+ </View>
60
+ </If>
61
+ <If condition="{{ uploaded }}">
62
+ <Text>httpbin got the file field "{{ uploaded }}"</Text>
63
+ </If>
64
+ <If condition="{{ kept }}">
65
+ <Text>Saved in the dot's folder. See the dash.storage.file page.</Text>
66
+ </If>
67
+ </View>
68
+ </View>
69
+
70
+ <View gap="small">
71
+ <Text variant="subtitle">Hosts dot.json doesn't list</Text>
72
+ <Text variant="caption" tone="muted">Dash turns the request down.</Text>
73
+ <View variant="card" gap="small">
74
+ <Button variant="secondary" onClick="tryUnlistedHost">Reach example.com</Button>
75
+ <If condition="{{ note }}">
76
+ <Text variant="caption" tone="muted">{{ note }}</Text>
77
+ </If>
78
+ </View>
79
+ </View>
80
+ </View>
@@ -0,0 +1,26 @@
1
+ <View>
2
+ <Text variant="title">dash.storage.secret</Text>
3
+ <Text>Secrets hold sign-in tokens from the dot's own server. Dash keeps them encrypted on the phone. A dot can keep up to 8, of up to 4 KB each.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">set, get, remove and clear</Text>
7
+ <Text variant="caption" tone="muted">A sign-in that stays. Sign out removes the token, and Forget every secret clears them all.</Text>
8
+ <View variant="card" gap="small">
9
+ <If condition="{{ token }}">
10
+ <Text>Signed in as {{ signedInAs }}</Text>
11
+ <Text variant="caption" tone="muted">Session token {{ token }}</Text>
12
+ <View direction="row" gap="small">
13
+ <Button variant="secondary" onClick="signOut">Sign out</Button>
14
+ <Button variant="secondary" onClick="forgetAll">Forget every secret</Button>
15
+ </View>
16
+ </If>
17
+ <Else>
18
+ <Input label="Name" placeholder="Mariama" value="{{ name }}" maxLength="40" onChange="setName" onSubmit="signIn" />
19
+ <Button onClick="signIn" disabled="{{ !name }}">Sign in</Button>
20
+ <Text variant="caption" tone="muted">Sign in, then close and reopen the dot. You stay signed in.</Text>
21
+ </Else>
22
+ </View>
23
+ </View>
24
+
25
+ <Notice tone="warning" title="Keep keys off the phone">Only the token is a secret here: your name is in the cache. Never put API keys in a dot's code or .env, since anyone with the dot can read them.</Notice>
26
+ </View>
@@ -0,0 +1,61 @@
1
+ import type { VibrationStyle } from '@monimesl/dot-runtime';
2
+
3
+ import state from './state';
4
+
5
+ export default definePage({
6
+ initialState: state,
7
+ mutations: {
8
+ clearToast: () => ({ toast: '' }),
9
+ },
10
+ actions: {
11
+ async vibrate(_state, event: DotEvent<unknown, { style: VibrationStyle }>) {
12
+ await this.dash.system.vibrate({ style: event.data.style });
13
+ },
14
+ async copySong() {
15
+ await this.dash.system.copyText('Exodus, by Drizilik');
16
+ this.setState({ toast: 'Song copied' });
17
+ },
18
+ async shareText() {
19
+ try {
20
+ await this.dash.system.share({
21
+ text: 'Salone Sounds Live, Freetown, 24 October. See you there!',
22
+ });
23
+ } catch (error) {
24
+ this.setState({ note: (error as Error).message });
25
+ }
26
+ },
27
+ async shareLink() {
28
+ try {
29
+ await this.dash.system.share({
30
+ title: 'Music of Sierra Leone',
31
+ url: 'https://en.wikipedia.org/wiki/Music_of_Sierra_Leone',
32
+ });
33
+ } catch (error) {
34
+ this.setState({ note: (error as Error).message });
35
+ }
36
+ },
37
+ async openDrizilik() {
38
+ try {
39
+ await this.dash.system.openUrl('https://drizilik.com/');
40
+ } catch (error) {
41
+ this.setState({ note: (error as Error).message });
42
+ }
43
+ },
44
+ async countDown() {
45
+ for (let seconds = 10; seconds > 0; seconds--) {
46
+ this.setState({ showIn: seconds });
47
+ await this.dash.system.delay(1000);
48
+ }
49
+ this.setState({ showIn: 0, toast: "It's showtime" });
50
+ },
51
+ async toggleTorch(state) {
52
+ const next = state.torch === 'on' ? 'off' : 'on';
53
+ try {
54
+ await this.dash.system.torch(next);
55
+ this.setState({ torch: next, note: '' });
56
+ } catch (error) {
57
+ this.setState({ note: (error as Error).message });
58
+ }
59
+ },
60
+ },
61
+ });
@@ -0,0 +1,9 @@
1
+ import type { VibrationStyle } from '@monimesl/dot-runtime';
2
+
3
+ export default {
4
+ styles: ['light', 'medium', 'heavy', 'success', 'warning', 'error'] as VibrationStyle[],
5
+ torch: 'off' as 'on' | 'off',
6
+ showIn: 0,
7
+ note: '',
8
+ toast: '',
9
+ };
@@ -0,0 +1,59 @@
1
+ <View>
2
+ <Text variant="title">dash.system</Text>
3
+ <Text>The phone's own features: vibration, the clipboard, sharing, the browser, timers and the torch.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">vibrate</Text>
7
+ <Text variant="caption" tone="muted">A short buzz, in a style that suits what happened.</Text>
8
+ <View variant="card" direction="row" wrap gap="small">
9
+ <For each="{{ styles }}" item="style" key="*this">
10
+ <Button variant="secondary" data-style="{{ style }}" onClick="vibrate">{{ style }}</Button>
11
+ </For>
12
+ </View>
13
+ </View>
14
+
15
+ <View gap="small">
16
+ <Text variant="subtitle">copyText</Text>
17
+ <View variant="card">
18
+ <Button variant="secondary" onClick="copySong">Copy a song's name</Button>
19
+ </View>
20
+ </View>
21
+
22
+ <View gap="small">
23
+ <Text variant="subtitle">share</Text>
24
+ <Text variant="caption" tone="muted">Opens the phone's share sheet with text, a link or a file. The dash.network page shares a file.</Text>
25
+ <View variant="card" direction="row" wrap gap="small">
26
+ <Button variant="secondary" onClick="shareText">Share text</Button>
27
+ <Button variant="secondary" onClick="shareLink">Share a link</Button>
28
+ </View>
29
+ </View>
30
+
31
+ <View gap="small">
32
+ <Text variant="subtitle">openUrl</Text>
33
+ <Text variant="caption" tone="muted">Opens a web page in the phone's browser.</Text>
34
+ <View variant="card">
35
+ <Button variant="secondary" onClick="openDrizilik">Open Drizilik's website</Button>
36
+ </View>
37
+ </View>
38
+
39
+ <View gap="small">
40
+ <Text variant="subtitle">delay</Text>
41
+ <Text variant="caption" tone="muted">Waits for a number of milliseconds. Dash keeps time, so it's cancelled when the page closes.</Text>
42
+ <View variant="card">
43
+ <Button variant="secondary" onClick="countDown" disabled="{{ showIn > 0 }}">{{ showIn > 0 ? 'Showtime in ' + showIn + 's' : 'Count down to showtime' }}</Button>
44
+ </View>
45
+ </View>
46
+
47
+ <View gap="small">
48
+ <Text variant="subtitle">torch</Text>
49
+ <Text variant="caption" tone="muted">Turns the camera's light on or off.</Text>
50
+ <View variant="card">
51
+ <Button variant="secondary" onClick="toggleTorch">{{ torch === 'on' ? 'Turn the torch off' : 'Lights up for the slow song' }}</Button>
52
+ </View>
53
+ </View>
54
+
55
+ <If condition="{{ note }}">
56
+ <Notice tone="danger">{{ note }}</Notice>
57
+ </If>
58
+ <Toast message="{{ toast }}" tone="success" onHide="clearToast" />
59
+ </View>
@@ -0,0 +1,69 @@
1
+ import { HostError } from '@monimesl/dot-runtime';
2
+
3
+ import state from './state';
4
+
5
+ // Closing a picker isn't a problem to show. Anything else is.
6
+ function problemToShow(error: unknown): string {
7
+ const closedByPerson = error instanceof HostError && error.code === 'cancelled';
8
+ return closedByPerson ? '' : (error as Error).message;
9
+ }
10
+
11
+ export default definePage({
12
+ initialState: state,
13
+ actions: {
14
+ async greet() {
15
+ try {
16
+ const { action } = await this.dash.ui.alert({
17
+ title: 'Kushé',
18
+ message: 'Kushé means hello in Krio, and tenki means thanks.',
19
+ actions: [
20
+ { id: 'tenki', label: 'Tenki' },
21
+ { id: 'close', label: 'Close', role: 'cancel' },
22
+ ],
23
+ });
24
+ this.setState({ choice: action, note: '' });
25
+ } catch (error) {
26
+ this.setState({ note: problemToShow(error) });
27
+ }
28
+ },
29
+ async confirmRemove() {
30
+ try {
31
+ const { action } = await this.dash.ui.alert({
32
+ title: 'Remove this song from your library?',
33
+ message: 'Its download goes too.',
34
+ actions: [
35
+ { id: 'keep', label: 'Keep', role: 'cancel' },
36
+ { id: 'remove', label: 'Remove', role: 'destructive' },
37
+ ],
38
+ });
39
+ this.setState({ choice: action, note: '' });
40
+ } catch (error) {
41
+ this.setState({ note: problemToShow(error) });
42
+ }
43
+ },
44
+ async chooseNight(state) {
45
+ try {
46
+ const night = await this.dash.ui.pickDate({
47
+ title: 'Choose a night',
48
+ value: state.night || undefined,
49
+ min: '2026-10-01',
50
+ max: '2026-12-31',
51
+ });
52
+ this.setState({ night, note: '' });
53
+ } catch (error) {
54
+ this.setState({ note: problemToShow(error) });
55
+ }
56
+ },
57
+ async chooseArrival(state) {
58
+ try {
59
+ const arrival = await this.dash.ui.pickTime({
60
+ title: 'Arriving at',
61
+ value: state.arrival || '18:00',
62
+ });
63
+ this.setState({ arrival, note: '' });
64
+ } catch (error) {
65
+ this.setState({ note: problemToShow(error) });
66
+ }
67
+ },
68
+ },
69
+ });
@@ -0,0 +1,8 @@
1
+ export default {
2
+ // The id of the alert button tapped.
3
+ choice: '',
4
+ // A date such as "2026-10-24", and a time such as "19:30", from the phone's own pickers.
5
+ night: '',
6
+ arrival: '',
7
+ note: '',
8
+ };
@@ -0,0 +1,31 @@
1
+ <View>
2
+ <Text variant="title">dash.ui</Text>
3
+ <Text>Dash shows these over the dot, in the phone's own style. Each call waits until the person answers.</Text>
4
+
5
+ <View gap="small">
6
+ <Text variant="subtitle">alert</Text>
7
+ <Text variant="caption" tone="muted">A title, a message and buttons. It gives back the id of the button tapped. role="cancel" and role="destructive" style a button.</Text>
8
+ <View variant="card" gap="small">
9
+ <View direction="row" wrap gap="small">
10
+ <Button onClick="greet">Say kushé</Button>
11
+ <Button variant="secondary" onClick="confirmRemove">Remove a song</Button>
12
+ </View>
13
+ <If condition="{{ choice }}">
14
+ <Text>You tapped "{{ choice }}"</Text>
15
+ </If>
16
+ </View>
17
+ </View>
18
+
19
+ <View gap="small">
20
+ <Text variant="subtitle">pickDate and pickTime</Text>
21
+ <Text variant="caption" tone="muted">The phone's own pickers. A date comes back as "2026-10-24" and a time as "19:30". min and max limit the dates. Closing a picker rejects with a HostError coded cancelled.</Text>
22
+ <List>
23
+ <ListItem icon="calendar" title="Night" subtitle="Shows run from October to December" value="{{ night || 'Choose' }}" onTap="chooseNight" />
24
+ <ListItem icon="clock" title="Arriving at" subtitle="Doors open at 18:00" value="{{ arrival || 'Choose' }}" onTap="chooseArrival" />
25
+ </List>
26
+ </View>
27
+
28
+ <If condition="{{ note }}">
29
+ <Notice tone="danger">{{ note }}</Notice>
30
+ </If>
31
+ </View>