@iyexin/mio-cli 0.0.0-stage → 0.1.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 (52) hide show
  1. package/LICENSE +30 -0
  2. package/README.md +108 -2
  3. package/dist/asar.d.ts +11 -0
  4. package/dist/asar.js +43 -0
  5. package/dist/asar.js.map +1 -0
  6. package/dist/bin.d.ts +2 -0
  7. package/dist/bin.js +10 -0
  8. package/dist/bin.js.map +1 -0
  9. package/dist/build-android.d.ts +13 -0
  10. package/dist/build-android.js +93 -0
  11. package/dist/build-android.js.map +1 -0
  12. package/dist/build-windows.d.ts +13 -0
  13. package/dist/build-windows.js +73 -0
  14. package/dist/build-windows.js.map +1 -0
  15. package/dist/config.d.ts +44 -0
  16. package/dist/config.js +38 -0
  17. package/dist/config.js.map +1 -0
  18. package/dist/constants.d.ts +14 -0
  19. package/dist/constants.js +15 -0
  20. package/dist/constants.js.map +1 -0
  21. package/dist/create.d.ts +5 -0
  22. package/dist/create.js +58 -0
  23. package/dist/create.js.map +1 -0
  24. package/dist/dev.d.ts +9 -0
  25. package/dist/dev.js +165 -0
  26. package/dist/dev.js.map +1 -0
  27. package/dist/doctor.d.ts +2 -0
  28. package/dist/doctor.js +56 -0
  29. package/dist/doctor.js.map +1 -0
  30. package/dist/download.d.ts +19 -0
  31. package/dist/download.js +115 -0
  32. package/dist/download.js.map +1 -0
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +118 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/project.d.ts +12 -0
  37. package/dist/project.js +55 -0
  38. package/dist/project.js.map +1 -0
  39. package/dist/templates.d.ts +35 -0
  40. package/dist/templates.js +288 -0
  41. package/dist/templates.js.map +1 -0
  42. package/dist/web.d.ts +3 -0
  43. package/dist/web.js +33 -0
  44. package/dist/web.js.map +1 -0
  45. package/package.json +54 -5
  46. package/templates/web/icon.png +0 -0
  47. package/templates/web/index.html +113 -0
  48. package/templates/web/mio.config.json +20 -0
  49. package/templates/web/package.json +18 -0
  50. package/templates/web/src/main.ts +158 -0
  51. package/templates/web/src/style.css +389 -0
  52. package/templates/web/vite.config.ts +12 -0
@@ -0,0 +1,158 @@
1
+ import { Mio, app, clipboard, fs } from "@iyexin/mio-api";
2
+
3
+ const el = <T extends HTMLElement>(selector: string): T => {
4
+ const node = document.querySelector<T>(selector);
5
+ if (!node) throw new Error(`Missing element: ${selector}`);
6
+ return node;
7
+ };
8
+
9
+ const output = el<HTMLPreElement>("#output");
10
+ const logId = el<HTMLSpanElement>("#log-id");
11
+ const bridgeStatus = el<HTMLSpanElement>("#bridge-status");
12
+ const bridgeText = el<HTMLSpanElement>("#bridge-text");
13
+
14
+ let callCounter = 0;
15
+
16
+ function print(value: unknown, tone: "normal" | "error" = "normal", label?: string): void {
17
+ output.dataset.tone = tone;
18
+ output.textContent =
19
+ typeof value === "string" ? value : JSON.stringify(value, null, 2);
20
+ callCounter += 1;
21
+ logId.textContent = label ? `${label} · #${callCounter}` : `#${callCounter}`;
22
+ }
23
+
24
+ function setBridge(state: "pending" | "ready" | "error", label: string): void {
25
+ bridgeStatus.dataset.state = state;
26
+ bridgeText.textContent = label;
27
+ }
28
+
29
+ function describe(error: unknown): string {
30
+ if (error instanceof Error) {
31
+ const code = (error as Error & { code?: string }).code;
32
+ return code ? `${code}: ${error.message}` : error.message;
33
+ }
34
+ return String(error);
35
+ }
36
+
37
+ async function boot(): Promise<void> {
38
+ try {
39
+ await Mio.ready;
40
+ setBridge("ready", "connected");
41
+
42
+ const info = await app.info();
43
+ el("#app-name").textContent = info.name;
44
+ el("#spec-id").textContent = info.id;
45
+ el("#spec-version").textContent = info.version;
46
+ el("#spec-platform").textContent = info.platform;
47
+ el("#spec-api").textContent = `api ${info.apiVersion}`;
48
+ el("#spec-template").textContent = `template ${info.templateVersion}`;
49
+ el("#masthead-meta").textContent = `shell ${info.templateVersion}`;
50
+ document.title = info.name;
51
+
52
+ print(
53
+ {
54
+ bridge: "connected",
55
+ app: info.id,
56
+ platform: info.platform,
57
+ api: info.apiVersion,
58
+ },
59
+ "normal",
60
+ "ready",
61
+ );
62
+
63
+ await Mio.on("app.lifecycle", (event) => {
64
+ print({ event: "app.lifecycle", data: event }, "normal", "lifecycle");
65
+ });
66
+ } catch (error) {
67
+ setBridge("error", "unavailable");
68
+ print(describe(error), "error", "error");
69
+ }
70
+ }
71
+
72
+ const actions: Record<string, () => Promise<{ result: unknown; label: string }>> = {
73
+ async ping() {
74
+ return { label: "ping", result: await Mio.invoke("ping") };
75
+ },
76
+
77
+ async clipboard() {
78
+ const text = `Mio clipboard ${Date.now()}`;
79
+ await clipboard.writeText(text);
80
+ const readBack = await clipboard.readText();
81
+ return { label: "clipboard", result: { wrote: text.length, match: readBack === text } };
82
+ },
83
+
84
+ async write() {
85
+ const path = "notes/hello.txt";
86
+ const text = `Written at ${new Date().toISOString()}`;
87
+ await fs.writeText(path, text);
88
+ return {
89
+ label: "file",
90
+ result: {
91
+ path,
92
+ bytes: text.length,
93
+ exists: await fs.exists(path),
94
+ content: await fs.readText(path),
95
+ },
96
+ };
97
+ },
98
+
99
+ async binary() {
100
+ const size = 64 * 1024 * 1024;
101
+ const bytes = new Uint8Array(size);
102
+ crypto.getRandomValues(bytes.subarray(0, 65536));
103
+ for (let index = 65536; index < size; index += 1) {
104
+ bytes[index] = bytes[index % 65536]!;
105
+ }
106
+
107
+ const path = "blobs/round-trip.bin";
108
+ const started = performance.now();
109
+ await fs.writeBytes(path, bytes);
110
+ const readBack = await fs.readBytes(path);
111
+ const elapsedMs = Math.round(performance.now() - started);
112
+
113
+ let firstDifference = -1;
114
+ for (let index = 0; index < bytes.length; index += 1) {
115
+ if (bytes[index] !== readBack[index]) {
116
+ firstDifference = index;
117
+ break;
118
+ }
119
+ }
120
+
121
+ return {
122
+ label: "binary",
123
+ result: {
124
+ path,
125
+ written: bytes.length,
126
+ read: readBack.length,
127
+ identical: firstDifference === -1 && readBack.length === bytes.length,
128
+ firstDifference,
129
+ elapsedMs,
130
+ },
131
+ };
132
+ },
133
+ };
134
+
135
+ for (const button of document.querySelectorAll<HTMLButtonElement>("[data-action]")) {
136
+ button.addEventListener("click", async () => {
137
+ const action = button.dataset.action;
138
+ const run = action ? actions[action] : undefined;
139
+ if (!run) return;
140
+
141
+ const name = button.querySelector<HTMLSpanElement>(".call-name");
142
+ const original = name?.textContent ?? "";
143
+ button.disabled = true;
144
+ if (name) name.textContent = "Running…";
145
+
146
+ try {
147
+ const { result, label } = await run();
148
+ print(result, "normal", label);
149
+ } catch (error) {
150
+ print(describe(error), "error", action ?? "error");
151
+ } finally {
152
+ button.disabled = false;
153
+ if (name) name.textContent = original;
154
+ }
155
+ });
156
+ }
157
+
158
+ void boot();
@@ -0,0 +1,389 @@
1
+ :root {
2
+ --paper: #fbfaf7;
3
+ --ink: #17181a;
4
+ --ink-soft: #55585e;
5
+ --ink-faint: #8b8d92;
6
+ --rule: #e2dfd8;
7
+ --rule-strong: #cfcbc2;
8
+ --accent: #4e6b85;
9
+ --ok: #3d8b63;
10
+ --warn: #b2863f;
11
+ --err: #b04a42;
12
+
13
+ --mono: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas,
14
+ "Liberation Mono", monospace;
15
+ --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
16
+ "Helvetica Neue", Arial, "Noto Sans SC", "PingFang SC", "Microsoft YaHei",
17
+ sans-serif;
18
+ }
19
+
20
+ * {
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ html {
25
+ color-scheme: light;
26
+ }
27
+
28
+ body {
29
+ margin: 0;
30
+ background: var(--paper);
31
+ color: var(--ink);
32
+ font-family: var(--sans);
33
+ font-size: 15px;
34
+ line-height: 1.55;
35
+ -webkit-font-smoothing: antialiased;
36
+ }
37
+
38
+ .frame {
39
+ width: min(760px, calc(100vw - 48px));
40
+ margin: 0 auto;
41
+ padding: 34px 0 64px;
42
+ }
43
+
44
+ /* ---------------- masthead ---------------- */
45
+
46
+ .masthead {
47
+ display: flex;
48
+ align-items: baseline;
49
+ justify-content: space-between;
50
+ padding-bottom: 14px;
51
+ border-bottom: 1px solid var(--rule);
52
+ }
53
+
54
+ .wordmark {
55
+ font-family: var(--mono);
56
+ font-size: 12px;
57
+ font-weight: 600;
58
+ letter-spacing: 0.34em;
59
+ text-indent: 0.34em;
60
+ color: var(--ink);
61
+ }
62
+
63
+ .masthead-meta {
64
+ font-family: var(--mono);
65
+ font-size: 11.5px;
66
+ color: var(--ink-faint);
67
+ letter-spacing: 0.02em;
68
+ }
69
+
70
+ /* ---------------- intro ---------------- */
71
+
72
+ .intro {
73
+ display: grid;
74
+ grid-template-columns: 1fr auto;
75
+ align-items: end;
76
+ gap: 32px;
77
+ padding: 52px 0 44px;
78
+ }
79
+
80
+ .intro h1 {
81
+ margin: 0;
82
+ font-size: clamp(38px, 7.2vw, 58px);
83
+ font-weight: 600;
84
+ letter-spacing: -0.035em;
85
+ line-height: 1.02;
86
+ }
87
+
88
+ .lede {
89
+ margin: 16px 0 0;
90
+ max-width: 38ch;
91
+ color: var(--ink-soft);
92
+ font-size: 15px;
93
+ }
94
+
95
+ .mark {
96
+ width: 104px;
97
+ height: 104px;
98
+ display: block;
99
+ margin-bottom: 4px;
100
+ }
101
+
102
+ /* ---------------- spec strip ---------------- */
103
+
104
+ .spec {
105
+ display: grid;
106
+ grid-template-columns: 1.5fr 1fr 1fr 1.2fr;
107
+ margin: 0;
108
+ border-top: 1px solid var(--rule);
109
+ border-bottom: 1px solid var(--rule);
110
+ }
111
+
112
+ .spec-cell {
113
+ padding: 14px 0 15px;
114
+ min-width: 0;
115
+ }
116
+
117
+ .spec-cell + .spec-cell {
118
+ padding-left: 20px;
119
+ border-left: 1px solid var(--rule);
120
+ }
121
+
122
+ .spec dt {
123
+ font-family: var(--mono);
124
+ font-size: 10.5px;
125
+ letter-spacing: 0.12em;
126
+ text-transform: uppercase;
127
+ color: var(--ink-faint);
128
+ margin-bottom: 5px;
129
+ }
130
+
131
+ .spec dd {
132
+ margin: 0;
133
+ font-size: 13px;
134
+ color: var(--ink);
135
+ white-space: nowrap;
136
+ overflow: hidden;
137
+ text-overflow: ellipsis;
138
+ }
139
+
140
+ .mono {
141
+ font-family: var(--mono);
142
+ font-size: 12.5px;
143
+ font-variant-numeric: tabular-nums;
144
+ }
145
+
146
+ /* ---------------- status ---------------- */
147
+
148
+ .status {
149
+ display: inline-flex;
150
+ align-items: center;
151
+ gap: 7px;
152
+ }
153
+
154
+ .status-dot {
155
+ width: 6px;
156
+ height: 6px;
157
+ border-radius: 50%;
158
+ background: var(--ink-faint);
159
+ flex: none;
160
+ }
161
+
162
+ .status[data-state="pending"] .status-dot {
163
+ background: var(--warn);
164
+ }
165
+
166
+ .status[data-state="ready"] .status-dot {
167
+ background: var(--ok);
168
+ }
169
+
170
+ .status[data-state="error"] .status-dot {
171
+ background: var(--err);
172
+ }
173
+
174
+ /* ---------------- calls ---------------- */
175
+
176
+ .section-label {
177
+ font-family: var(--mono);
178
+ font-size: 10.5px;
179
+ font-weight: 500;
180
+ letter-spacing: 0.14em;
181
+ text-transform: uppercase;
182
+ color: var(--ink-faint);
183
+ margin: 0;
184
+ }
185
+
186
+ .calls {
187
+ margin-top: 40px;
188
+ }
189
+
190
+ .call-list {
191
+ list-style: none;
192
+ margin: 12px 0 0;
193
+ padding: 0;
194
+ border-top: 1px solid var(--rule);
195
+ }
196
+
197
+ .call-list li {
198
+ border-bottom: 1px solid var(--rule);
199
+ }
200
+
201
+ .call-list button {
202
+ appearance: none;
203
+ display: grid;
204
+ grid-template-columns: 40px minmax(0, auto) 1fr 20px;
205
+ align-items: baseline;
206
+ gap: 16px;
207
+ width: 100%;
208
+ padding: 15px 8px 15px 0;
209
+ border: 0;
210
+ background: transparent;
211
+ color: inherit;
212
+ font: inherit;
213
+ text-align: left;
214
+ cursor: pointer;
215
+ transition: background-color 130ms ease;
216
+ }
217
+
218
+ .call-list button:hover {
219
+ background: rgba(0, 0, 0, 0.022);
220
+ }
221
+
222
+ .call-list button:focus-visible {
223
+ outline: 2px solid var(--accent);
224
+ outline-offset: -2px;
225
+ }
226
+
227
+ .call-list button[disabled] {
228
+ cursor: default;
229
+ opacity: 0.55;
230
+ }
231
+
232
+ .num {
233
+ font-family: var(--mono);
234
+ font-size: 11.5px;
235
+ color: var(--ink-faint);
236
+ font-variant-numeric: tabular-nums;
237
+ }
238
+
239
+ .call-name {
240
+ font-size: 15px;
241
+ color: var(--ink);
242
+ }
243
+
244
+ .call-note {
245
+ font-size: 12.5px;
246
+ color: var(--ink-faint);
247
+ overflow: hidden;
248
+ text-overflow: ellipsis;
249
+ white-space: nowrap;
250
+ }
251
+
252
+ .arrow {
253
+ justify-self: end;
254
+ color: var(--ink-faint);
255
+ transition:
256
+ transform 140ms ease,
257
+ color 140ms ease;
258
+ }
259
+
260
+ .call-list button:hover .arrow {
261
+ transform: translateX(3px);
262
+ color: var(--accent);
263
+ }
264
+
265
+ /* ---------------- log ---------------- */
266
+
267
+ .log {
268
+ margin-top: 40px;
269
+ }
270
+
271
+ .log-head {
272
+ display: flex;
273
+ align-items: baseline;
274
+ justify-content: space-between;
275
+ gap: 16px;
276
+ margin-bottom: 12px;
277
+ }
278
+
279
+ .log-id {
280
+ font-family: var(--mono);
281
+ font-size: 11.5px;
282
+ color: var(--ink-faint);
283
+ }
284
+
285
+ #output {
286
+ margin: 0;
287
+ padding: 16px 18px;
288
+ min-height: 92px;
289
+ max-height: 240px;
290
+ overflow: auto;
291
+ background: #f4f2ee;
292
+ border: 1px solid var(--rule);
293
+ border-left: 2px solid var(--rule-strong);
294
+ font-family: var(--mono);
295
+ font-size: 12.5px;
296
+ line-height: 1.7;
297
+ color: #2c2f33;
298
+ white-space: pre-wrap;
299
+ overflow-wrap: anywhere;
300
+ }
301
+
302
+ #output[data-tone="error"] {
303
+ color: var(--err);
304
+ background: #fbf3f2;
305
+ border-left-color: var(--err);
306
+ }
307
+
308
+ /* ---------------- colophon ---------------- */
309
+
310
+ .colophon {
311
+ display: flex;
312
+ flex-wrap: wrap;
313
+ gap: 4px 18px;
314
+ margin-top: 44px;
315
+ padding-top: 16px;
316
+ border-top: 1px solid var(--rule);
317
+ font-family: var(--mono);
318
+ font-size: 11.5px;
319
+ color: var(--ink-faint);
320
+ }
321
+
322
+ /* ---------------- responsive ---------------- */
323
+
324
+ @media (max-width: 640px) {
325
+ .frame {
326
+ width: calc(100vw - 36px);
327
+ padding-top: 24px;
328
+ }
329
+
330
+ .intro {
331
+ grid-template-columns: 1fr;
332
+ gap: 28px;
333
+ padding: 36px 0 32px;
334
+ }
335
+
336
+ .intro h1 {
337
+ font-size: clamp(34px, 11vw, 46px);
338
+ }
339
+
340
+ .mark {
341
+ width: 72px;
342
+ height: 72px;
343
+ order: -1;
344
+ margin: 0 0 4px;
345
+ }
346
+
347
+ .spec {
348
+ grid-template-columns: 1fr 1fr;
349
+ }
350
+
351
+ .spec-cell:nth-child(3) {
352
+ border-left: 0;
353
+ padding-left: 0;
354
+ }
355
+
356
+ .spec-cell:nth-child(n + 3) {
357
+ border-top: 1px solid var(--rule);
358
+ }
359
+
360
+ .call-list button {
361
+ grid-template-columns: 30px minmax(0, 1fr) 18px;
362
+ grid-template-areas:
363
+ "num name arrow"
364
+ "num note arrow";
365
+ row-gap: 2px;
366
+ column-gap: 12px;
367
+ padding: 14px 4px 14px 0;
368
+ }
369
+
370
+ .num {
371
+ grid-area: num;
372
+ align-self: start;
373
+ padding-top: 3px;
374
+ }
375
+
376
+ .call-name {
377
+ grid-area: name;
378
+ }
379
+
380
+ .call-note {
381
+ grid-area: note;
382
+ white-space: normal;
383
+ }
384
+
385
+ .arrow {
386
+ grid-area: arrow;
387
+ align-self: center;
388
+ }
389
+ }
@@ -0,0 +1,12 @@
1
+ export default {
2
+ base: "./",
3
+ build: {
4
+ outDir: "dist",
5
+ emptyOutDir: true,
6
+ },
7
+ server: {
8
+ watch: {
9
+ ignored: ["**/.mio/**", "**/build/**", "**/dist/**"],
10
+ },
11
+ },
12
+ };