boo-flash 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.
@@ -0,0 +1,3890 @@
1
+ // @bun
2
+ // src/index.tsx
3
+ import { createCliRenderer as createCliRenderer2 } from "@opentui/core";
4
+
5
+ // node_modules/@opentui/solid/index.bun.js
6
+ import { CliRenderer, createCliRenderer, engine as engine2 } from "@opentui/core";
7
+ import { createTestRenderer } from "@opentui/core/testing";
8
+ import {
9
+ ASCIIFontRenderable,
10
+ BoxRenderable,
11
+ CodeRenderable,
12
+ DiffRenderable,
13
+ ImageRenderable,
14
+ InputRenderable,
15
+ LineNumberRenderable,
16
+ MarkdownRenderable,
17
+ ScrollBoxRenderable,
18
+ SelectRenderable,
19
+ TabSelectRenderable,
20
+ TextareaRenderable,
21
+ TextAttributes,
22
+ TextNodeRenderable,
23
+ TextRenderable
24
+ } from "@opentui/core";
25
+ import {
26
+ engine,
27
+ Timeline
28
+ } from "@opentui/core";
29
+ import { createContext, createSignal, onCleanup, onMount, useContext } from "solid-js";
30
+ import { createEffect, createMemo as createMemo2, getOwner, onCleanup as onCleanup3, runWithOwner, splitProps, untrack as untrack2 } from "solid-js";
31
+ import {
32
+ BaseRenderable,
33
+ createTextAttributes,
34
+ ImageRenderable as ImageRenderable2,
35
+ InputRenderable as InputRenderable2,
36
+ InputRenderableEvents,
37
+ isTextNodeRenderable,
38
+ isEditBufferRenderable,
39
+ parseColor,
40
+ Renderable,
41
+ RootTextNodeRenderable,
42
+ ScrollBoxRenderable as ScrollBoxRenderable2,
43
+ SelectRenderable as SelectRenderable2,
44
+ SelectRenderableEvents,
45
+ TabSelectRenderable as TabSelectRenderable2,
46
+ TabSelectRenderableEvents,
47
+ TextNodeRenderable as TextNodeRenderable2,
48
+ TextRenderable as TextRenderable2
49
+ } from "@opentui/core";
50
+
51
+ // node_modules/entities/dist/esm/decode-codepoint.js
52
+ var _a;
53
+ var decodeMap = new Map([
54
+ [0, 65533],
55
+ [128, 8364],
56
+ [130, 8218],
57
+ [131, 402],
58
+ [132, 8222],
59
+ [133, 8230],
60
+ [134, 8224],
61
+ [135, 8225],
62
+ [136, 710],
63
+ [137, 8240],
64
+ [138, 352],
65
+ [139, 8249],
66
+ [140, 338],
67
+ [142, 381],
68
+ [145, 8216],
69
+ [146, 8217],
70
+ [147, 8220],
71
+ [148, 8221],
72
+ [149, 8226],
73
+ [150, 8211],
74
+ [151, 8212],
75
+ [152, 732],
76
+ [153, 8482],
77
+ [154, 353],
78
+ [155, 8250],
79
+ [156, 339],
80
+ [158, 382],
81
+ [159, 376]
82
+ ]);
83
+ var fromCodePoint = (_a = String.fromCodePoint) !== null && _a !== undefined ? _a : (codePoint) => {
84
+ let output = "";
85
+ if (codePoint > 65535) {
86
+ codePoint -= 65536;
87
+ output += String.fromCharCode(codePoint >>> 10 & 1023 | 55296);
88
+ codePoint = 56320 | codePoint & 1023;
89
+ }
90
+ output += String.fromCharCode(codePoint);
91
+ return output;
92
+ };
93
+ function replaceCodePoint(codePoint) {
94
+ var _a2;
95
+ if (codePoint >= 55296 && codePoint <= 57343 || codePoint > 1114111) {
96
+ return 65533;
97
+ }
98
+ return (_a2 = decodeMap.get(codePoint)) !== null && _a2 !== undefined ? _a2 : codePoint;
99
+ }
100
+
101
+ // node_modules/entities/dist/esm/internal/decode-shared.js
102
+ function decodeBase64(input) {
103
+ const binary = typeof atob === "function" ? atob(input) : typeof Buffer.from === "function" ? Buffer.from(input, "base64").toString("binary") : new Buffer(input, "base64").toString("binary");
104
+ const evenLength = binary.length & ~1;
105
+ const out = new Uint16Array(evenLength / 2);
106
+ for (let index = 0, outIndex = 0;index < evenLength; index += 2) {
107
+ const lo = binary.charCodeAt(index);
108
+ const hi = binary.charCodeAt(index + 1);
109
+ out[outIndex++] = lo | hi << 8;
110
+ }
111
+ return out;
112
+ }
113
+
114
+ // node_modules/entities/dist/esm/generated/decode-data-html.js
115
+ var htmlDecodeTree = /* @__PURE__ */ decodeBase64("QR08ALkAAgH6AYsDNQR2BO0EPgXZBQEGLAbdBxMISQrvCmQLfQurDKQNLw4fD4YPpA+6D/IPAAAAAAAAAAAAAAAAKhBMEY8TmxUWF2EYLBkxGuAa3RsJHDscWR8YIC8jSCSIJcMl6ie3Ku8rEC0CLjoupS7kLgAIRU1hYmNmZ2xtbm9wcnN0dVQAWgBeAGUAaQBzAHcAfgCBAIQAhwCSAJoAoACsALMAbABpAGcAO4DGAMZAUAA7gCYAJkBjAHUAdABlADuAwQDBQHIiZXZlAAJhAAFpeW0AcgByAGMAO4DCAMJAEGRyAADgNdgE3XIAYQB2AGUAO4DAAMBA8CFoYZFj4SFjcgBhZAAAoFMqAAFncIsAjgBvAG4ABGFmAADgNdg43fAlbHlGdW5jdGlvbgCgYSBpAG4AZwA7gMUAxUAAAWNzpACoAHIAAOA12Jzc6SFnbgCgVCJpAGwAZABlADuAwwDDQG0AbAA7gMQAxEAABGFjZWZvcnN1xQDYANoA7QDxAPYA+QD8AAABY3LJAM8AayNzbGFzaAAAoBYidgHTANUAAKDnKmUAZAAAoAYjeQARZIABY3J0AOAA5QDrAGEidXNlAACgNSLuI291bGxpcwCgLCFhAJJjcgAA4DXYBd1wAGYAAOA12Dnd5SF2ZdhiYwDyAOoAbSJwZXEAAKBOIgAHSE9hY2RlZmhpbG9yc3UXARoBHwE6AVIBVQFiAWQBZgGCAakB6QHtAfIBYwB5ACdkUABZADuAqQCpQIABY3B5ACUBKAE1AfUhdGUGYWmg0iJ0KGFsRGlmZmVyZW50aWFsRAAAoEUhbCJleXMAAKAtIQACYWVpb0EBRAFKAU0B8iFvbgxhZABpAGwAO4DHAMdAcgBjAAhhbiJpbnQAAKAwIm8AdAAKYQABZG5ZAV0BaSJsbGEAuGB0I2VyRG90ALdg8gA5AWkAp2NyImNsZQAAAkRNUFRwAXQBeQF9AW8AdAAAoJkiaSJudXMAAKCWIuwhdXMAoJUiaSJtZXMAAKCXIm8AAAFjc4cBlAFrKndpc2VDb250b3VySW50ZWdyYWwAAKAyImUjQ3VybHkAAAFEUZwBpAFvJXVibGVRdW90ZQAAoB0gdSJvdGUAAKAZIAACbG5wdbABtgHNAdgBbwBuAGWgNyIAoHQqgAFnaXQAvAHBAcUB8iJ1ZW50AKBhIm4AdAAAoC8i7yV1ckludGVncmFsAKAuIgABZnLRAdMBAKACIe8iZHVjdACgECJuLnRlckNsb2Nrd2lzZUNvbnRvdXJJbnRlZ3JhbAAAoDMi7yFzcwCgLypjAHIAAOA12J7ccABDoNMiYQBwAACgTSKABURKU1phY2VmaW9zAAsCEgIVAhgCGwIsAjQCOQI9AnMCfwNvoEUh9CJyYWhkAKARKWMAeQACZGMAeQAFZGMAeQAPZIABZ3JzACECJQIoAuchZXIAoCEgcgAAoKEhaAB2AACg5CoAAWF5MAIzAvIhb24OYRRkbAB0oAciYQCUY3IAAOA12AfdAAFhZkECawIAAWNtRQJnAvIjaXRpY2FsAAJBREdUUAJUAl8CYwJjInV0ZQC0YG8AdAFZAloC2WJiJGxlQWN1dGUA3WJyImF2ZQBgYGkibGRlANxi7yFuZACgxCJmJWVyZW50aWFsRAAAoEYhcAR9AgAAAAAAAIECjgIAABoDZgAA4DXYO91EoagAhQKJAm8AdAAAoNwgcSJ1YWwAAKBQIuIhbGUAA0NETFJVVpkCqAK1Au8C/wIRA28AbgB0AG8AdQByAEkAbgB0AGUAZwByAGEA7ADEAW8AdAKvAgAAAACwAqhgbiNBcnJvdwAAoNMhAAFlb7kC0AJmAHQAgAFBUlQAwQLGAs0CciJyb3cAAKDQIekkZ2h0QXJyb3cAoNQhZQDlACsCbgBnAAABTFLWAugC5SFmdAABQVLcAuECciJyb3cAAKD4J+kkZ2h0QXJyb3cAoPon6SRnaHRBcnJvdwCg+SdpImdodAAAAUFU9gL7AnIicm93AACg0iFlAGUAAKCoInAAQQIGAwAAAAALA3Iicm93AACg0SFvJHduQXJyb3cAAKDVIWUlcnRpY2FsQmFyAACgJSJuAAADQUJMUlRhJAM2AzoDWgNxA3oDciJyb3cAAKGTIUJVLAMwA2EAcgAAoBMpcCNBcnJvdwAAoPUhciJldmUAEWPlIWZ00gJDAwAASwMAAFIDaSVnaHRWZWN0b3IAAKBQKWUkZVZlY3RvcgAAoF4p5SJjdG9yQqC9IWEAcgAAoFYpaSJnaHQA1AFiAwAAaQNlJGVWZWN0b3IAAKBfKeUiY3RvckKgwSFhAHIAAKBXKWUAZQBBoKQiciJyb3cAAKCnIXIAcgBvAPcAtAIAAWN0gwOHA3IA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YwAAoN8i4SFycnCgtiEAoD0pgKIqImJjZG9zACsVMBU6FT4VQRVyImNhcAAAoEgqAAFhdTQVNxVwAACgRipwAACgSipvAHQAAKCNInIAAKBFKgDgKiIA/gACYWxydksVURVuFXMVcgByAG2gtyEAoDwpeQCAAWV2dwBYFWUVaRVxAHACXxUAAAAAYxVyAGUA4wAXFXUA4wAZFWUAZQAAoM4iZSJkZ2UAAKDPImUAbgA7gKQApEBlI2Fycm93AAABbHJ7FX8V5SFmdACgtiFpImdodAAAoLchZQDkAG0VAAFjaYsVkRVvAG4AaQBuAPQAkwFuAHQAAKAxImwiY3R5AACgLSOACUFIYWJjZGVmaGlqbG9yc3R1d3oAuBW7Fb8V1RXgFegV+RUKFhUWHxZUFlcWZRbFFtsW7xb7FgUXChdyAPIAtAJhAHIAAKBlKQACZ2xyc8YVyhXOFdAV5yFlcgCgICDlIXRoAKA4IfIA9QxoAHagECAAoKMiawHZFd4VYSJyb3cAAKAPKWEA4wBfAgABYXnkFecV8iFvbg9hNGQAoUYhYW/tFfQVAAFnciEC8RVyAACgyiF0InNlcQAAoHcqgAFnbG0A/xUCFgUWO4CwALBAdABhALRjcCJ0eXYAAKCxKQABaXIOFhIW8yFodACgfykA4DXYId1hAHIAAAFschsWHRYAoMMhAKDCIYACYWVnc3YAKBauAjYWOhY+Fm0AAKHEIm9zLhY0Fm4AZABzoMQi9SFpdACgZiZhIm1tYQDdY2kAbgAAoPIiAKH3AGlvQxZRFmQAZQAAgfcAO29KFksW90BuI3RpbWVzAACgxyJuAPgAUBZjAHkAUmRjAG8CXhYAAAAAYhZyAG4AAKAeI28AcAAAoA0jgAJscHR1dwBuFnEWdRaSFp4W7CFhciRgZgAA4DXYVd0AotkCZW1wc30WhBaJFo0WcQBkoFAibwB0AACgUSJpIm51cwAAoDgi7CF1cwCgFCLxInVhcmUAoKEiYgBsAGUAYgBhAHIAdwBlAGQAZwDlANcAbgCAAWFkaAClFqoWtBZyAHIAbwD3APUMbwB3AG4AYQByAHIAbwB3APMA8xVhI3Jwb29uAAABbHK8FsAWZQBmAPQAHBZpAGcAaAD0AB4WYgHJFs8WawBhAHIAbwD3AJILbwLUFgAAAADYFnIAbgAAoB8jbwBwAACgDCOAAWNvdADhFukW7BYAAXJ55RboFgDgNdi53FVkbAAAoPYp8iFvaxFhAAFkcvMW9xZvAHQAAKDxImkA5qC/JVsSAAFhaP8WAhdyAPIANQNhAPIA1wvhIm5nbGUAoKYpAAFjaQ4XEBd5AF9k5yJyYXJyAKD/JwAJRGFjZGVmZ2xtbm9wcXJzdHV4MRc4F0YXWxcyBF4XaRd5F40XrBe0F78X2RcVGCEYLRg1GEAYAAFEbzUXgRZvAPQA+BUAAWNzPBdCF3UAdABlADuA6QDpQPQhZXIAoG4qAAJhaW95TRdQF1YXWhfyIW9uG2FyAGOgViI7gOoA6kDsIW9uAKBVIk1kbwB0ABdhAAFEcmIXZhdvAHQAAKBSIgDgNdgi3XKhmipuF3QXYQB2AGUAO4DoAOhAZKCWKm8AdAAAoJgqgKGZKmlscwCAF4UXhxfuInRlcnMAoOcjAKATIWSglSpvAHQAAKCXKoABYXBzAJMXlheiF2MAcgATYXQAeQBzogUinxcAAAAAoRdlAHQAAKAFInAAMaADIDMBqRerFwCgBCAAoAUgAAFnc7AXsRdLYXAAAKACIAABZ3C4F7sXbwBuABlhZgAA4DXYVt2AAWFscwDFF8sXzxdyAHOg1SJsAACg4yl1AHMAAKBxKmkAAKG1A2x21RfYF28AbgC1Y/VjAAJjc3V24BfoF/0XEBgAAWlv5BdWF3IAYwAAoFYiaQLuFwAAAADwF+0ADQThIW50AAFnbPUX+Rd0AHIAAKCWKuUhc3MAoJUqgAFhZWkAAxgGGAoYbABzAD1gcwB0AACgXyJ2AESgYSJEAACgeCrwImFyc2wAoOUpAAFEYRkYHRhvAHQAAKBTInIAcgAAoHEpgAFjZGkAJxgqGO0XcgAAoC8hbwD0AIwCAAFhaDEYMhi3YzuA8ADwQAABbXI5GD0YbAA7gOsA60BvAACgrCCAAWNpcABGGEgYSxhsACFgcwD0ACwEAAFlb08YVxhjAHQAYQB0AGkAbwDuABoEbgBlAG4AdABpAGEAbADlADME4Ql1GAAAgRgAAIMYiBgAAAAAoRilGAAAqhgAALsYvhjRGAAA1xgnGWwAbABpAG4AZwBkAG8AdABzAGUA8QBlF3kARGRtImFsZQAAoEAmgAFpbHIAjRiRGJ0Y7CFpZwCgA/tpApcYAAAAAJoYZwAAoAD7aQBnAACgBPsA4DXYI93sIWlnAKAB++whaWcA4GYAagCAAWFsdACvGLIYthh0AACgbSZpAGcAAKAC+24AcwAAoLElbwBmAJJh8AHCGAAAxhhmAADgNdhX3QABYWvJGMwYbADsAGsEdqDUIgCg2SphI3J0aW50AACgDSoAAWFv2hgiGQABY3PeGB8ZsQPnGP0YBRkSGRUZAAAdGbID7xjyGPQY9xj5GAAA+xg7gL0AvUAAoFMhO4C8ALxAAKBVIQCgWSEAoFshswEBGQAAAxkAoFQhAKBWIbQCCxkOGQAAAAAQGTuAvgC+QACgVyEAoFwhNQAAoFghtgEZGQAAGxkAoFohAKBdITgAAKBeIWwAAKBEIHcAbgAAoCIjYwByAADgNdi73IAIRWFiY2RlZmdpamxub3JzdHYARhlKGVoZXhlmGWkZkhmWGZkZnRmgGa0ZxhnLGc8Z4BkjGmygZyIAoIwqgAFjbXAAUBlTGVgZ9SF0ZfVhbQBhAOSgswM6FgCghipyImV2ZQAfYQABaXliGWUZcgBjAB1hM2RvAHQAIWGAoWUibHFzAMYEcBl6GfGhZSLOBAAAdhlsAGEAbgD0AN8EgKF+KmNkbACBGYQZjBljAACgqSpvAHQAb6CAKmyggioAoIQqZeDbIgD+cwAAoJQqcgAA4DXYJN3noGsirATtIWVsAKA3IWMAeQBTZIChdyJFYWoApxmpGasZAKCSKgCgpSoAoKQqAAJFYWVztBm2Gb0ZwhkAoGkicABwoIoq8iFveACgiipxoIgq8aCIKrUZaQBtAACg5yJwAGYAAOA12FjdYQB2AOUAYwIAAWNp0xnWGXIAAKAKIW0AAKFzImVs3BneGQCgjioAoJAqAIM+ADtjZGxxco0E6xn0GfgZ/BkBGgABY2nvGfEZAKCnKnIAAKB6Km8AdAAAoNci0CFhcgCglSl1ImVzdAAAoHwqgAJhZGVscwAKGvQZFhrVBCAa8AEPGgAAFBpwAHIAbwD4AFkZcgAAoHgpcQAAAWxxxAQbGmwAZQBzAPMASRlpAO0A5AQAAWVuJxouGnIjdG5lcXEAAOBpIgD+xQAsGgAFQWFiY2Vma29zeUAaQxpmGmoabRqDGocalhrCGtMacgDyAMwCAAJpbG1yShpOGlAaVBpyAHMA8ABxD2YAvWBpAGwA9AASBQABZHJYGlsaYwB5AEpkAKGUIWN3YBpkGmkAcgAAoEgpAKCtIWEAcgAAoA8h6SFyYyVhgAFhbHIAcxp7Gn8a8iF0c3WgZSZpAHQAAKBlJuwhaXAAoCYg4yFvbgCguSJyAADgNdgl3XMAAAFld4wakRphInJvdwAAoCUpYSJyb3cAAKAmKYACYW1vcHIAnxqjGqcauhq+GnIAcgAAoP8h9CFodACgOyJrAAABbHKsGrMaZSRmdGFycm93AACgqSHpJGdodGFycm93AKCqIWYAAOA12Fnd4iFhcgCgFSCAAWNsdADIGswa0BpyAADgNdi93GEAcwDoAGka8iFvaydhAAFicNca2xr1IWxsAKBDIOghZW4AoBAg4Qr2GgAA/RoAAAgbExsaGwAAIRs7GwAAAAA+G2IbmRuVG6sbAACyG80b0htjAHUAdABlADuA7QDtQAChYyBpeQEbBhtyAGMAO4DuAO5AOGQAAWN4CxsNG3kANWRjAGwAO4ChAKFAAAFmcssCFhsA4DXYJt1yAGEAdgBlADuA7ADsQIChSCFpbm8AJxsyGzYbAAFpbisbLxtuAHQAAKAMKnQAAKAtIuYhaW4AoNwpdABhAACgKSHsIWlnM2GAAWFvcABDG1sbXhuAAWNndABJG0sbWRtyACthgAFlbHAAcQVRG1UbaQBuAOUAyAVhAHIA9AByBWgAMWFmAACgtyJlAGQAtWEAoggiY2ZvdGkbbRt1G3kb4SFyZQCgBSFpAG4AdKAeImkAZQAAoN0pZABvAPQAWxsAoisiY2VscIEbhRuPG5QbYQBsAACguiIAAWdyiRuNG2UAcgDzACMQ4wCCG2EicmhrAACgFyryIW9kAKA8KgACY2dwdJ8boRukG6gbeQBRZG8AbgAvYWYAAOA12FrdYQC5Y3UAZQBzAHQAO4C/AL9AAAFjabUbuRtyAADgNdi+3G4AAKIIIkVkc3bCG8QbyBvQAwCg+SJvAHQAAKD1Inag9CIAoPMiaaBiIOwhZGUpYesB1hsAANkbYwB5AFZkbAA7gO8A70AAA2NmbW9zdeYb7hvyG/Ub+hsFHAABaXnqG+0bcgBjADVhOWRyAADgNdgn3eEhdGg3YnAAZgAA4DXYW93jAf8bAAADHHIAAOA12L/c8iFjeVhk6yFjeVRkAARhY2ZnaGpvcxUcGhwiHCYcKhwtHDAcNRzwIXBhdqC6A/BjAAFleR4cIRzkIWls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ZmdobG1vcHJzdHV3CiwNLBEsHSwnLDEsQCxLLFIsYix6LIQsjyzLLOgs7Sz/LAotcgDyAAkDYQByAACgYykAAWNyFSwbLHUAdABlADuA+gD6QPIACQ1yAOMBIywAACUseQBeZHYAZQBtYQABaXkrLDAscgBjADuA+wD7QENkgAFhYmgANyw6LD0scgDyANEO7CFhY3FhYQDyAOAOAAFpckQsSCzzIWh0AKB+KQDgNdgy3XIAYQB2AGUAO4D5APlAYQFWLF8scgAAAWxyWixcLACgvyEAoL4hbABrAACggCUAAWN0Zix2LG8CbCwAAAAAcyxyAG4AZaAcI3IAAKAcI28AcAAAoA8jcgBpAACg+CUAAWFsfiyBLGMAcgBrYTuAqACoQAABZ3CILIssbwBuAHNhZgAA4DXYZt0AA2FkaGxzdZksniynLLgsuyzFLHIAcgBvAPcACQ1vAHcAbgBhAHIAcgBvAPcA2A5hI3Jwb29uAAABbHKvLLMsZQBmAPQAWyxpAGcAaAD0AF0sdQDzAKYOaQAAocUDaGzBLMIs0mNvAG4AxWPwI2Fycm93cwCgyCGAAWNpdADRLOEs5CxvAtcsAAAAAN4scgBuAGWgHSNyAACgHSNvAHAAAKAOI24AZwBvYXIAaQAAoPklYwByAADgNdjK3IABZGlyAPMs9yz6LG8AdAAAoPAi7CFkZWlhaQBmoLUlAKC0JQABYW0DLQYtcgDyAMosbAA7gPwA/EDhIm5nbGUAoKcpgAdBQkRhY2RlZmxub3Byc3oAJy0qLTAtNC2bLZ0toS2/LcMtxy3TLdgt3C3gLfwtcgDyABADYQByAHag6CoAoOkqYQBzAOgA/gIAAW5yOC08LechcnQAoJwpgANla25wcnN0AJkpSC1NLVQtXi1iLYItYQBwAHAA4QAaHG8AdABoAGkAbgDnAKEXgAFoaXIAoSmzJFotbwBwAPQAdCVooJUh7wD4JgABaXVmLWotZwBtAOEAuygAAWJwbi14LXMjZXRuZXEAceCKIgD+AODLKgD+cyNldG5lcQBx4IsiAP4A4MwqAP4AAWhyhi2KLWUAdADhABIraSNhbmdsZQAAAWxyki2WLeUhZnQAoLIiaSJnaHQAAKCzInkAMmThIXNoAKCiIoABZWxyAKcttC24LWKiKCKuLQAAAACyLWEAcgAAoLsicQAAoFoi7CFpcACg7iIAAWJ0vC1eD2EA8gBfD3IAAOA12DPddAByAOkAlS1zAHUAAAFicM0t0C0A4IIi0iAA4IMi0iBwAGYAAOA12GfdcgBvAPAAWQt0AHIA6QCaLQABY3XkLegtcgAA4DXYy9wAAWJw7C30LW4AAAFFZXUt8S0A4IoiAP5uAAABRWV/LfktAOCLIgD+6SJnemFnAKCaKYADY2Vmb3BycwANLhAuJS4pLiMuLi40LukhcmN1YQABZGkULiEuAAFiZxguHC5hAHIAAKBfKmUAcaAnIgCgWSLlIXJwAKAYIXIAAOA12DTdcABmAADgNdho3WWgQCJhAHQA6ABqD2MAcgAA4DXYzNzjCuQRUC4AAFQuAABYLmIuAAAAAGMubS5wLnQuAAAAAIguki4AAJouJxIqEnQAcgDpAB0ScgAA4DXYNd0AAUFhWy5eLnIA8gDnAnIA8gCTB75jAAFBYWYuaS5yAPIA4AJyAPIAjAdhAPAAeh5pAHMAAKD7IoABZHB0APgReS6DLgABZmx9LoAuAOA12GnddQDzAP8RaQBtAOUABBIAAUFhiy6OLnIA8gDuAnIA8gCaBwABY3GVLgoScgAA4DXYzdwAAXB0nS6hLmwAdQDzACUScgDpACASAARhY2VmaW9zdbEuvC7ELsguzC7PLtQu2S5jAAABdXm2LrsudABlADuA/QD9QE9kAAFpecAuwy5yAGMAd2FLZG4AO4ClAKVAcgAA4DXYNt1jAHkAV2RwAGYAAOA12GrdYwByAADgNdjO3AABY23dLt8ueQBOZGwAO4D/AP9AAAVhY2RlZmhpb3N38y73Lv8uAi8MLxAvEy8YLx0vIi9jInV0ZQB6YQABYXn7Lv4u8iFvbn5hN2RvAHQAfGEAAWV0Bi8KL3QAcgDmAB8QYQC2Y3IAAOA12DfdYwB5ADZk5yJyYXJyAKDdIXAAZgAA4DXYa91jAHIAAOA12M/cAAFqbiYvKC8AoA0gagAAoAwg");
116
+
117
+ // node_modules/entities/dist/esm/internal/bin-trie-flags.js
118
+ var BinTrieFlags;
119
+ (function(BinTrieFlags2) {
120
+ BinTrieFlags2[BinTrieFlags2["VALUE_LENGTH"] = 49152] = "VALUE_LENGTH";
121
+ BinTrieFlags2[BinTrieFlags2["FLAG13"] = 8192] = "FLAG13";
122
+ BinTrieFlags2[BinTrieFlags2["BRANCH_LENGTH"] = 8064] = "BRANCH_LENGTH";
123
+ BinTrieFlags2[BinTrieFlags2["JUMP_TABLE"] = 127] = "JUMP_TABLE";
124
+ })(BinTrieFlags || (BinTrieFlags = {}));
125
+
126
+ // node_modules/entities/dist/esm/decode.js
127
+ var CharCodes;
128
+ (function(CharCodes2) {
129
+ CharCodes2[CharCodes2["NUM"] = 35] = "NUM";
130
+ CharCodes2[CharCodes2["SEMI"] = 59] = "SEMI";
131
+ CharCodes2[CharCodes2["EQUALS"] = 61] = "EQUALS";
132
+ CharCodes2[CharCodes2["ZERO"] = 48] = "ZERO";
133
+ CharCodes2[CharCodes2["NINE"] = 57] = "NINE";
134
+ CharCodes2[CharCodes2["LOWER_A"] = 97] = "LOWER_A";
135
+ CharCodes2[CharCodes2["LOWER_F"] = 102] = "LOWER_F";
136
+ CharCodes2[CharCodes2["LOWER_X"] = 120] = "LOWER_X";
137
+ CharCodes2[CharCodes2["LOWER_Z"] = 122] = "LOWER_Z";
138
+ CharCodes2[CharCodes2["UPPER_A"] = 65] = "UPPER_A";
139
+ CharCodes2[CharCodes2["UPPER_F"] = 70] = "UPPER_F";
140
+ CharCodes2[CharCodes2["UPPER_Z"] = 90] = "UPPER_Z";
141
+ })(CharCodes || (CharCodes = {}));
142
+ var TO_LOWER_BIT = 32;
143
+ function isNumber(code) {
144
+ return code >= CharCodes.ZERO && code <= CharCodes.NINE;
145
+ }
146
+ function isHexadecimalCharacter(code) {
147
+ return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_F || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_F;
148
+ }
149
+ function isAsciiAlphaNumeric(code) {
150
+ return code >= CharCodes.UPPER_A && code <= CharCodes.UPPER_Z || code >= CharCodes.LOWER_A && code <= CharCodes.LOWER_Z || isNumber(code);
151
+ }
152
+ function isEntityInAttributeInvalidEnd(code) {
153
+ return code === CharCodes.EQUALS || isAsciiAlphaNumeric(code);
154
+ }
155
+ var EntityDecoderState;
156
+ (function(EntityDecoderState2) {
157
+ EntityDecoderState2[EntityDecoderState2["EntityStart"] = 0] = "EntityStart";
158
+ EntityDecoderState2[EntityDecoderState2["NumericStart"] = 1] = "NumericStart";
159
+ EntityDecoderState2[EntityDecoderState2["NumericDecimal"] = 2] = "NumericDecimal";
160
+ EntityDecoderState2[EntityDecoderState2["NumericHex"] = 3] = "NumericHex";
161
+ EntityDecoderState2[EntityDecoderState2["NamedEntity"] = 4] = "NamedEntity";
162
+ })(EntityDecoderState || (EntityDecoderState = {}));
163
+ var DecodingMode;
164
+ (function(DecodingMode2) {
165
+ DecodingMode2[DecodingMode2["Legacy"] = 0] = "Legacy";
166
+ DecodingMode2[DecodingMode2["Strict"] = 1] = "Strict";
167
+ DecodingMode2[DecodingMode2["Attribute"] = 2] = "Attribute";
168
+ })(DecodingMode || (DecodingMode = {}));
169
+
170
+ class EntityDecoder {
171
+ constructor(decodeTree, emitCodePoint, errors) {
172
+ this.decodeTree = decodeTree;
173
+ this.emitCodePoint = emitCodePoint;
174
+ this.errors = errors;
175
+ this.state = EntityDecoderState.EntityStart;
176
+ this.consumed = 1;
177
+ this.result = 0;
178
+ this.treeIndex = 0;
179
+ this.excess = 1;
180
+ this.decodeMode = DecodingMode.Strict;
181
+ this.runConsumed = 0;
182
+ }
183
+ startEntity(decodeMode) {
184
+ this.decodeMode = decodeMode;
185
+ this.state = EntityDecoderState.EntityStart;
186
+ this.result = 0;
187
+ this.treeIndex = 0;
188
+ this.excess = 1;
189
+ this.consumed = 1;
190
+ this.runConsumed = 0;
191
+ }
192
+ write(input, offset) {
193
+ switch (this.state) {
194
+ case EntityDecoderState.EntityStart: {
195
+ if (input.charCodeAt(offset) === CharCodes.NUM) {
196
+ this.state = EntityDecoderState.NumericStart;
197
+ this.consumed += 1;
198
+ return this.stateNumericStart(input, offset + 1);
199
+ }
200
+ this.state = EntityDecoderState.NamedEntity;
201
+ return this.stateNamedEntity(input, offset);
202
+ }
203
+ case EntityDecoderState.NumericStart: {
204
+ return this.stateNumericStart(input, offset);
205
+ }
206
+ case EntityDecoderState.NumericDecimal: {
207
+ return this.stateNumericDecimal(input, offset);
208
+ }
209
+ case EntityDecoderState.NumericHex: {
210
+ return this.stateNumericHex(input, offset);
211
+ }
212
+ case EntityDecoderState.NamedEntity: {
213
+ return this.stateNamedEntity(input, offset);
214
+ }
215
+ }
216
+ }
217
+ stateNumericStart(input, offset) {
218
+ if (offset >= input.length) {
219
+ return -1;
220
+ }
221
+ if ((input.charCodeAt(offset) | TO_LOWER_BIT) === CharCodes.LOWER_X) {
222
+ this.state = EntityDecoderState.NumericHex;
223
+ this.consumed += 1;
224
+ return this.stateNumericHex(input, offset + 1);
225
+ }
226
+ this.state = EntityDecoderState.NumericDecimal;
227
+ return this.stateNumericDecimal(input, offset);
228
+ }
229
+ stateNumericHex(input, offset) {
230
+ while (offset < input.length) {
231
+ const char = input.charCodeAt(offset);
232
+ if (isNumber(char) || isHexadecimalCharacter(char)) {
233
+ const digit = char <= CharCodes.NINE ? char - CharCodes.ZERO : (char | TO_LOWER_BIT) - CharCodes.LOWER_A + 10;
234
+ this.result = this.result * 16 + digit;
235
+ this.consumed++;
236
+ offset++;
237
+ } else {
238
+ return this.emitNumericEntity(char, 3);
239
+ }
240
+ }
241
+ return -1;
242
+ }
243
+ stateNumericDecimal(input, offset) {
244
+ while (offset < input.length) {
245
+ const char = input.charCodeAt(offset);
246
+ if (isNumber(char)) {
247
+ this.result = this.result * 10 + (char - CharCodes.ZERO);
248
+ this.consumed++;
249
+ offset++;
250
+ } else {
251
+ return this.emitNumericEntity(char, 2);
252
+ }
253
+ }
254
+ return -1;
255
+ }
256
+ emitNumericEntity(lastCp, expectedLength) {
257
+ var _a2;
258
+ if (this.consumed <= expectedLength) {
259
+ (_a2 = this.errors) === null || _a2 === undefined || _a2.absenceOfDigitsInNumericCharacterReference(this.consumed);
260
+ return 0;
261
+ }
262
+ if (lastCp === CharCodes.SEMI) {
263
+ this.consumed += 1;
264
+ } else if (this.decodeMode === DecodingMode.Strict) {
265
+ return 0;
266
+ }
267
+ this.emitCodePoint(replaceCodePoint(this.result), this.consumed);
268
+ if (this.errors) {
269
+ if (lastCp !== CharCodes.SEMI) {
270
+ this.errors.missingSemicolonAfterCharacterReference();
271
+ }
272
+ this.errors.validateNumericCharacterReference(this.result);
273
+ }
274
+ return this.consumed;
275
+ }
276
+ stateNamedEntity(input, offset) {
277
+ const { decodeTree } = this;
278
+ let current = decodeTree[this.treeIndex];
279
+ let valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
280
+ while (offset < input.length) {
281
+ if (valueLength === 0 && (current & BinTrieFlags.FLAG13) !== 0) {
282
+ const runLength = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
283
+ if (this.runConsumed === 0) {
284
+ const firstChar = current & BinTrieFlags.JUMP_TABLE;
285
+ if (input.charCodeAt(offset) !== firstChar) {
286
+ return this.result === 0 ? 0 : this.emitNotTerminatedNamedEntity();
287
+ }
288
+ offset++;
289
+ this.excess++;
290
+ this.runConsumed++;
291
+ }
292
+ while (this.runConsumed < runLength) {
293
+ if (offset >= input.length) {
294
+ return -1;
295
+ }
296
+ const charIndexInPacked = this.runConsumed - 1;
297
+ const packedWord = decodeTree[this.treeIndex + 1 + (charIndexInPacked >> 1)];
298
+ const expectedChar = charIndexInPacked % 2 === 0 ? packedWord & 255 : packedWord >> 8 & 255;
299
+ if (input.charCodeAt(offset) !== expectedChar) {
300
+ this.runConsumed = 0;
301
+ return this.result === 0 ? 0 : this.emitNotTerminatedNamedEntity();
302
+ }
303
+ offset++;
304
+ this.excess++;
305
+ this.runConsumed++;
306
+ }
307
+ this.runConsumed = 0;
308
+ this.treeIndex += 1 + (runLength >> 1);
309
+ current = decodeTree[this.treeIndex];
310
+ valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
311
+ }
312
+ if (offset >= input.length)
313
+ break;
314
+ const char = input.charCodeAt(offset);
315
+ if (char === CharCodes.SEMI && valueLength !== 0 && (current & BinTrieFlags.FLAG13) !== 0) {
316
+ return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
317
+ }
318
+ this.treeIndex = determineBranch(decodeTree, current, this.treeIndex + Math.max(1, valueLength), char);
319
+ if (this.treeIndex < 0) {
320
+ return this.result === 0 || this.decodeMode === DecodingMode.Attribute && (valueLength === 0 || isEntityInAttributeInvalidEnd(char)) ? 0 : this.emitNotTerminatedNamedEntity();
321
+ }
322
+ current = decodeTree[this.treeIndex];
323
+ valueLength = (current & BinTrieFlags.VALUE_LENGTH) >> 14;
324
+ if (valueLength !== 0) {
325
+ if (char === CharCodes.SEMI) {
326
+ return this.emitNamedEntityData(this.treeIndex, valueLength, this.consumed + this.excess);
327
+ }
328
+ if (this.decodeMode !== DecodingMode.Strict && (current & BinTrieFlags.FLAG13) === 0) {
329
+ this.result = this.treeIndex;
330
+ this.consumed += this.excess;
331
+ this.excess = 0;
332
+ }
333
+ }
334
+ offset++;
335
+ this.excess++;
336
+ }
337
+ return -1;
338
+ }
339
+ emitNotTerminatedNamedEntity() {
340
+ var _a2;
341
+ const { result, decodeTree } = this;
342
+ const valueLength = (decodeTree[result] & BinTrieFlags.VALUE_LENGTH) >> 14;
343
+ this.emitNamedEntityData(result, valueLength, this.consumed);
344
+ (_a2 = this.errors) === null || _a2 === undefined || _a2.missingSemicolonAfterCharacterReference();
345
+ return this.consumed;
346
+ }
347
+ emitNamedEntityData(result, valueLength, consumed) {
348
+ const { decodeTree } = this;
349
+ this.emitCodePoint(valueLength === 1 ? decodeTree[result] & ~(BinTrieFlags.VALUE_LENGTH | BinTrieFlags.FLAG13) : decodeTree[result + 1], consumed);
350
+ if (valueLength === 3) {
351
+ this.emitCodePoint(decodeTree[result + 2], consumed);
352
+ }
353
+ return consumed;
354
+ }
355
+ end() {
356
+ var _a2;
357
+ switch (this.state) {
358
+ case EntityDecoderState.NamedEntity: {
359
+ return this.result !== 0 && (this.decodeMode !== DecodingMode.Attribute || this.result === this.treeIndex) ? this.emitNotTerminatedNamedEntity() : 0;
360
+ }
361
+ case EntityDecoderState.NumericDecimal: {
362
+ return this.emitNumericEntity(0, 2);
363
+ }
364
+ case EntityDecoderState.NumericHex: {
365
+ return this.emitNumericEntity(0, 3);
366
+ }
367
+ case EntityDecoderState.NumericStart: {
368
+ (_a2 = this.errors) === null || _a2 === undefined || _a2.absenceOfDigitsInNumericCharacterReference(this.consumed);
369
+ return 0;
370
+ }
371
+ case EntityDecoderState.EntityStart: {
372
+ return 0;
373
+ }
374
+ }
375
+ }
376
+ }
377
+ function getDecoder(decodeTree) {
378
+ let returnValue = "";
379
+ const decoder = new EntityDecoder(decodeTree, (data) => returnValue += fromCodePoint(data));
380
+ return function decodeWithTrie(input, decodeMode) {
381
+ let lastIndex = 0;
382
+ let offset = 0;
383
+ while ((offset = input.indexOf("&", offset)) >= 0) {
384
+ returnValue += input.slice(lastIndex, offset);
385
+ decoder.startEntity(decodeMode);
386
+ const length = decoder.write(input, offset + 1);
387
+ if (length < 0) {
388
+ lastIndex = offset + decoder.end();
389
+ break;
390
+ }
391
+ lastIndex = offset + length;
392
+ offset = length === 0 ? lastIndex + 1 : lastIndex;
393
+ }
394
+ const result = returnValue + input.slice(lastIndex);
395
+ returnValue = "";
396
+ return result;
397
+ };
398
+ }
399
+ function determineBranch(decodeTree, current, nodeIndex, char) {
400
+ const branchCount = (current & BinTrieFlags.BRANCH_LENGTH) >> 7;
401
+ const jumpOffset = current & BinTrieFlags.JUMP_TABLE;
402
+ if (branchCount === 0) {
403
+ return jumpOffset !== 0 && char === jumpOffset ? nodeIndex : -1;
404
+ }
405
+ if (jumpOffset) {
406
+ const value = char - jumpOffset;
407
+ return value < 0 || value >= branchCount ? -1 : decodeTree[nodeIndex + value] - 1;
408
+ }
409
+ const packedKeySlots = branchCount + 1 >> 1;
410
+ let lo = 0;
411
+ let hi = branchCount - 1;
412
+ while (lo <= hi) {
413
+ const mid = lo + hi >>> 1;
414
+ const slot = mid >> 1;
415
+ const packed = decodeTree[nodeIndex + slot];
416
+ const midKey = packed >> (mid & 1) * 8 & 255;
417
+ if (midKey < char) {
418
+ lo = mid + 1;
419
+ } else if (midKey > char) {
420
+ hi = mid - 1;
421
+ } else {
422
+ return decodeTree[nodeIndex + packedKeySlots + mid];
423
+ }
424
+ }
425
+ return -1;
426
+ }
427
+ var htmlDecoder = /* @__PURE__ */ getDecoder(htmlDecodeTree);
428
+ function decodeHTMLStrict(htmlString) {
429
+ return htmlDecoder(htmlString, DecodingMode.Strict);
430
+ }
431
+ // node_modules/entities/dist/esm/index.js
432
+ var EntityLevel;
433
+ (function(EntityLevel2) {
434
+ EntityLevel2[EntityLevel2["XML"] = 0] = "XML";
435
+ EntityLevel2[EntityLevel2["HTML"] = 1] = "HTML";
436
+ })(EntityLevel || (EntityLevel = {}));
437
+ var EncodingMode;
438
+ (function(EncodingMode2) {
439
+ EncodingMode2[EncodingMode2["UTF8"] = 0] = "UTF8";
440
+ EncodingMode2[EncodingMode2["ASCII"] = 1] = "ASCII";
441
+ EncodingMode2[EncodingMode2["Extensive"] = 2] = "Extensive";
442
+ EncodingMode2[EncodingMode2["Attribute"] = 3] = "Attribute";
443
+ EncodingMode2[EncodingMode2["Text"] = 4] = "Text";
444
+ })(EncodingMode || (EncodingMode = {}));
445
+
446
+ // node_modules/@opentui/solid/index.bun.js
447
+ import { onCleanup as onCleanup2, useContext as useContext2 } from "solid-js";
448
+ import { createRoot, createRenderEffect, createMemo, createComponent, untrack, mergeProps } from "solid-js";
449
+ import { mergeProps as mergeProps2 } from "solid-js";
450
+ import { BaseRenderable as BaseRenderable2, isTextNodeRenderable as isTextNodeRenderable2, TextNodeRenderable as TextNodeRenderable3, TextRenderable as TextRenderable3, Yoga } from "@opentui/core";
451
+ import {
452
+ BoxRenderable as BoxRenderable2,
453
+ RootRenderable
454
+ } from "@opentui/core";
455
+ import { createSignal as createSignal2 } from "solid-js";
456
+ import { TimeToFirstDrawRenderable } from "@opentui/core";
457
+ import { createSlotRegistry } from "@opentui/core";
458
+ import { children, createMemo as createMemo3, createSignal as createSignal3, ErrorBoundary, For, onCleanup as onCleanup4, splitProps as splitProps2 } from "solid-js";
459
+
460
+ class SpanRenderable extends TextNodeRenderable {
461
+ _ctx;
462
+ constructor(_ctx, options) {
463
+ super(options);
464
+ this._ctx = _ctx;
465
+ }
466
+ }
467
+ class TextModifierRenderable extends SpanRenderable {
468
+ constructor(options, modifier) {
469
+ super(null, options);
470
+ if (modifier === "b" || modifier === "strong") {
471
+ this.attributes = (this.attributes || 0) | TextAttributes.BOLD;
472
+ } else if (modifier === "i" || modifier === "em") {
473
+ this.attributes = (this.attributes || 0) | TextAttributes.ITALIC;
474
+ } else if (modifier === "u") {
475
+ this.attributes = (this.attributes || 0) | TextAttributes.UNDERLINE;
476
+ }
477
+ }
478
+ }
479
+
480
+ class BoldSpanRenderable extends TextModifierRenderable {
481
+ constructor(options) {
482
+ super(options, "b");
483
+ }
484
+ }
485
+
486
+ class ItalicSpanRenderable extends TextModifierRenderable {
487
+ constructor(options) {
488
+ super(options, "i");
489
+ }
490
+ }
491
+
492
+ class UnderlineSpanRenderable extends TextModifierRenderable {
493
+ constructor(options) {
494
+ super(options, "u");
495
+ }
496
+ }
497
+
498
+ class LineBreakRenderable extends SpanRenderable {
499
+ constructor(_ctx, options) {
500
+ super(null, options);
501
+ this.add();
502
+ }
503
+ add() {
504
+ return super.add(`
505
+ `);
506
+ }
507
+ }
508
+
509
+ class LinkRenderable extends SpanRenderable {
510
+ constructor(_ctx, options) {
511
+ const linkOptions = {
512
+ ...options,
513
+ link: { url: options.href }
514
+ };
515
+ super(null, linkOptions);
516
+ }
517
+ }
518
+ var baseComponents = {
519
+ box: BoxRenderable,
520
+ text: TextRenderable,
521
+ input: InputRenderable,
522
+ select: SelectRenderable,
523
+ textarea: TextareaRenderable,
524
+ ascii_font: ASCIIFontRenderable,
525
+ tab_select: TabSelectRenderable,
526
+ scrollbox: ScrollBoxRenderable,
527
+ code: CodeRenderable,
528
+ diff: DiffRenderable,
529
+ line_number: LineNumberRenderable,
530
+ markdown: MarkdownRenderable,
531
+ image: ImageRenderable,
532
+ span: SpanRenderable,
533
+ strong: BoldSpanRenderable,
534
+ b: BoldSpanRenderable,
535
+ em: ItalicSpanRenderable,
536
+ i: ItalicSpanRenderable,
537
+ u: UnderlineSpanRenderable,
538
+ br: LineBreakRenderable,
539
+ a: LinkRenderable
540
+ };
541
+ var componentCatalogue = { ...baseComponents };
542
+ function extend(objects) {
543
+ Object.assign(componentCatalogue, objects);
544
+ }
545
+ function getComponentCatalogue() {
546
+ return componentCatalogue;
547
+ }
548
+ var RendererContext = createContext();
549
+ var useRenderer = () => {
550
+ const renderer = useContext(RendererContext);
551
+ if (!renderer) {
552
+ throw new Error("No renderer found");
553
+ }
554
+ return renderer;
555
+ };
556
+ var onResize = (callback) => {
557
+ const renderer = useRenderer();
558
+ onMount(() => {
559
+ renderer.on("resize", callback);
560
+ });
561
+ onCleanup(() => {
562
+ renderer.off("resize", callback);
563
+ });
564
+ };
565
+ var useTerminalDimensions = () => {
566
+ const renderer = useRenderer();
567
+ const [terminalDimensions, setTerminalDimensions] = createSignal({ width: renderer.width, height: renderer.height });
568
+ const callback = (width, height) => {
569
+ setTerminalDimensions({ width, height });
570
+ };
571
+ onResize(callback);
572
+ return terminalDimensions;
573
+ };
574
+ var useKeyboard = (callback, options) => {
575
+ const renderer = useRenderer();
576
+ const keyHandler = renderer.keyInput;
577
+ onMount(() => {
578
+ keyHandler.on("keypress", callback);
579
+ if (options?.release) {
580
+ keyHandler.on("keyrelease", callback);
581
+ }
582
+ });
583
+ onCleanup(() => {
584
+ keyHandler.off("keypress", callback);
585
+ if (options?.release) {
586
+ keyHandler.off("keyrelease", callback);
587
+ }
588
+ });
589
+ };
590
+ var useTimeline = (options = {}) => {
591
+ const timeline = new Timeline(options);
592
+ onMount(() => {
593
+ if (options.autoplay !== false) {
594
+ timeline.play();
595
+ }
596
+ engine.register(timeline);
597
+ });
598
+ onCleanup(() => {
599
+ timeline.pause();
600
+ engine.unregister(timeline);
601
+ });
602
+ return timeline;
603
+ };
604
+ var memo = (fn) => createMemo(() => fn());
605
+ function createRenderer({
606
+ createElement,
607
+ createTextNode,
608
+ createDynamicTextNode,
609
+ createSlotNode,
610
+ isTextNode,
611
+ replaceText,
612
+ insertNode,
613
+ removeNode,
614
+ setProperty,
615
+ getParentNode,
616
+ getFirstChild,
617
+ getNextSibling
618
+ }) {
619
+ function insert(parent, accessor, marker, initial) {
620
+ if (marker !== undefined && !initial)
621
+ initial = [];
622
+ if (typeof accessor !== "function")
623
+ return insertExpression(parent, accessor, initial, marker);
624
+ createRenderEffect((current) => insertExpression(parent, accessor(), current, marker), initial);
625
+ }
626
+ function insertExpression(parent, value, current, marker, unwrapArray) {
627
+ while (typeof current === "function")
628
+ current = current();
629
+ if (value === current)
630
+ return current;
631
+ const t = typeof value, multi = marker !== undefined;
632
+ if (t === "string" || t === "number") {
633
+ if (t === "number")
634
+ value = value.toString();
635
+ if (multi) {
636
+ let node = current[0];
637
+ if (node && isTextNode(node)) {
638
+ replaceText(node, value);
639
+ } else
640
+ node = createDynamicTextNode(value);
641
+ current = cleanChildren(parent, current, marker, node);
642
+ } else {
643
+ if (current !== "" && typeof current === "string") {
644
+ replaceText(getFirstChild(parent), current = value);
645
+ } else {
646
+ cleanChildren(parent, current, marker, createDynamicTextNode(value));
647
+ current = value;
648
+ }
649
+ }
650
+ } else if (value == null || t === "boolean") {
651
+ current = cleanChildren(parent, current, marker);
652
+ } else if (t === "function") {
653
+ createRenderEffect(() => {
654
+ let v = value();
655
+ while (typeof v === "function")
656
+ v = v();
657
+ current = insertExpression(parent, v, current, marker);
658
+ });
659
+ return () => current;
660
+ } else if (Array.isArray(value)) {
661
+ const array = [];
662
+ if (normalizeIncomingArray(array, value, unwrapArray)) {
663
+ createRenderEffect(() => current = insertExpression(parent, array, current, marker, true));
664
+ return () => current;
665
+ }
666
+ if (array.length === 0) {
667
+ const replacement = cleanChildren(parent, current, marker);
668
+ if (multi)
669
+ return current = replacement;
670
+ } else {
671
+ if (Array.isArray(current)) {
672
+ if (current.length === 0) {
673
+ appendNodes(parent, array, marker);
674
+ } else
675
+ reconcileArrays(parent, current, array);
676
+ } else if (current == null || current === "") {
677
+ appendNodes(parent, array);
678
+ } else {
679
+ reconcileArrays(parent, multi && current || [getFirstChild(parent)], array);
680
+ }
681
+ }
682
+ current = array;
683
+ } else {
684
+ if (Array.isArray(current)) {
685
+ if (multi)
686
+ return current = cleanChildren(parent, current, marker, value);
687
+ cleanChildren(parent, current, null, value);
688
+ } else if (current == null || current === "" || !getFirstChild(parent)) {
689
+ insertNode(parent, value);
690
+ } else
691
+ replaceNode(parent, value, getFirstChild(parent));
692
+ current = value;
693
+ }
694
+ return current;
695
+ }
696
+ function normalizeIncomingArray(normalized, array, unwrap) {
697
+ let dynamic = false;
698
+ for (let i = 0, len = array.length;i < len; i++) {
699
+ let item = array[i], t;
700
+ if (item == null || item === true || item === false)
701
+ ;
702
+ else if (Array.isArray(item)) {
703
+ dynamic = normalizeIncomingArray(normalized, item) || dynamic;
704
+ } else if ((t = typeof item) === "string" || t === "number") {
705
+ normalized.push(createDynamicTextNode(item));
706
+ } else if (t === "function") {
707
+ if (unwrap) {
708
+ while (typeof item === "function")
709
+ item = item();
710
+ dynamic = normalizeIncomingArray(normalized, Array.isArray(item) ? item : [item]) || dynamic;
711
+ } else {
712
+ normalized.push(item);
713
+ dynamic = true;
714
+ }
715
+ } else
716
+ normalized.push(item);
717
+ }
718
+ return dynamic;
719
+ }
720
+ function reconcileArrays(parentNode, a, b) {
721
+ let bLength = b.length, aEnd = a.length, bEnd = bLength, aStart = 0, bStart = 0, after = getNextSibling(a[aEnd - 1]), map = null;
722
+ while (aStart < aEnd || bStart < bEnd) {
723
+ if (a[aStart] === b[bStart]) {
724
+ aStart++;
725
+ bStart++;
726
+ continue;
727
+ }
728
+ while (a[aEnd - 1] === b[bEnd - 1]) {
729
+ aEnd--;
730
+ bEnd--;
731
+ }
732
+ if (aEnd === aStart) {
733
+ const node = bEnd < bLength ? bStart ? getNextSibling(b[bStart - 1]) : b[bEnd - bStart] : after;
734
+ while (bStart < bEnd)
735
+ insertNode(parentNode, b[bStart++], node);
736
+ } else if (bEnd === bStart) {
737
+ while (aStart < aEnd) {
738
+ if (!map || !map.has(a[aStart]))
739
+ removeNode(parentNode, a[aStart]);
740
+ aStart++;
741
+ }
742
+ } else if (a[aStart] === b[bEnd - 1] && b[bStart] === a[aEnd - 1]) {
743
+ const node = getNextSibling(a[--aEnd]);
744
+ insertNode(parentNode, b[bStart++], getNextSibling(a[aStart++]));
745
+ insertNode(parentNode, b[--bEnd], node);
746
+ a[aEnd] = b[bEnd];
747
+ } else {
748
+ if (!map) {
749
+ map = new Map;
750
+ let i = bStart;
751
+ while (i < bEnd)
752
+ map.set(b[i], i++);
753
+ }
754
+ const index = map.get(a[aStart]);
755
+ if (index != null) {
756
+ if (bStart < index && index < bEnd) {
757
+ let i = aStart, sequence = 1, t;
758
+ while (++i < aEnd && i < bEnd) {
759
+ if ((t = map.get(a[i])) == null || t !== index + sequence)
760
+ break;
761
+ sequence++;
762
+ }
763
+ if (sequence > index - bStart) {
764
+ const node = a[aStart];
765
+ while (bStart < index)
766
+ insertNode(parentNode, b[bStart++], node);
767
+ } else
768
+ replaceNode(parentNode, b[bStart++], a[aStart++]);
769
+ } else
770
+ aStart++;
771
+ } else
772
+ removeNode(parentNode, a[aStart++]);
773
+ }
774
+ }
775
+ }
776
+ function cleanChildren(parent, current, marker, replacement) {
777
+ if (marker === undefined) {
778
+ let removed;
779
+ while (removed = getFirstChild(parent))
780
+ removeNode(parent, removed);
781
+ replacement && insertNode(parent, replacement);
782
+ return replacement ?? "";
783
+ }
784
+ const node = replacement || createSlotNode();
785
+ if (current.length) {
786
+ let inserted = false;
787
+ for (let i = current.length - 1;i >= 0; i--) {
788
+ const el = current[i];
789
+ if (node !== el) {
790
+ const isParent = getParentNode(el) === parent;
791
+ if (!inserted && !i)
792
+ isParent ? replaceNode(parent, node, el) : insertNode(parent, node, marker);
793
+ else
794
+ isParent && removeNode(parent, el);
795
+ } else
796
+ inserted = true;
797
+ }
798
+ } else
799
+ insertNode(parent, node, marker);
800
+ return [node];
801
+ }
802
+ function appendNodes(parent, array, marker) {
803
+ for (let i = 0, len = array.length;i < len; i++)
804
+ insertNode(parent, array[i], marker);
805
+ }
806
+ function replaceNode(parent, newNode, oldNode) {
807
+ insertNode(parent, newNode, oldNode);
808
+ removeNode(parent, oldNode);
809
+ }
810
+ function spreadExpression(node, props, prevProps = {}, skipChildren) {
811
+ props || (props = {});
812
+ if (!skipChildren) {
813
+ createRenderEffect(() => prevProps.children = insertExpression(node, props.children, prevProps.children));
814
+ }
815
+ createRenderEffect(() => props.ref && props.ref(node));
816
+ createRenderEffect(() => {
817
+ for (const prop in props) {
818
+ if (prop === "children" || prop === "ref")
819
+ continue;
820
+ const value = props[prop];
821
+ if (value === prevProps[prop])
822
+ continue;
823
+ setProperty(node, prop, value, prevProps[prop]);
824
+ prevProps[prop] = value;
825
+ }
826
+ });
827
+ return prevProps;
828
+ }
829
+ return {
830
+ render(code, element) {
831
+ let disposer;
832
+ createRoot((dispose) => {
833
+ disposer = dispose;
834
+ insert(element, code());
835
+ });
836
+ return disposer;
837
+ },
838
+ insert,
839
+ spread(node, accessor, skipChildren) {
840
+ if (typeof accessor === "function") {
841
+ createRenderEffect((current) => spreadExpression(node, accessor(), current, skipChildren));
842
+ } else
843
+ spreadExpression(node, accessor, undefined, skipChildren);
844
+ },
845
+ createElement,
846
+ createTextNode,
847
+ insertNode,
848
+ setProp(node, name, value, prev) {
849
+ setProperty(node, name, value, prev);
850
+ return value;
851
+ },
852
+ mergeProps,
853
+ effect: createRenderEffect,
854
+ memo,
855
+ createComponent,
856
+ use(fn, element, arg) {
857
+ return untrack(() => fn(element, arg));
858
+ }
859
+ };
860
+ }
861
+ function createRenderer2(options) {
862
+ const renderer = createRenderer(options);
863
+ renderer.mergeProps = mergeProps2;
864
+ return renderer;
865
+ }
866
+ var idCounter = new Map;
867
+ function getNextId(elementType) {
868
+ if (!idCounter.has(elementType)) {
869
+ idCounter.set(elementType, 0);
870
+ }
871
+ const value = idCounter.get(elementType) + 1;
872
+ idCounter.set(elementType, value);
873
+ return `${elementType}-${value}`;
874
+ }
875
+ var log = (...args) => {
876
+ if (process.env.DEBUG) {
877
+ console.log("[Reconciler]", ...args);
878
+ }
879
+ };
880
+
881
+ class TextNode extends TextNodeRenderable2 {
882
+ static fromString(text, options = {}) {
883
+ const node = new TextNode(options);
884
+ node.add(text);
885
+ return node;
886
+ }
887
+ }
888
+ var logId = (node) => {
889
+ if (!node)
890
+ return;
891
+ return node.id;
892
+ };
893
+ var getNodeChildren = (node) => {
894
+ let children2;
895
+ if (node instanceof TextRenderable2) {
896
+ children2 = node.getTextChildren();
897
+ } else {
898
+ children2 = node.getChildren();
899
+ }
900
+ return children2;
901
+ };
902
+ function _insertNode(parent, node, anchor) {
903
+ log("Inserting node:", logId(node), "into parent:", logId(parent), "with anchor:", logId(anchor), node instanceof TextNode);
904
+ if (node instanceof SlotRenderable) {
905
+ node.parent = parent;
906
+ node = node.getSlotChild(parent);
907
+ }
908
+ if (anchor && anchor instanceof SlotRenderable) {
909
+ anchor = anchor.getSlotChild(parent);
910
+ }
911
+ if (isTextNodeRenderable(node)) {
912
+ if (!(parent instanceof TextRenderable2) && !isTextNodeRenderable(parent)) {
913
+ throw new Error(`Orphan text error: "${node.toChunks().map((c) => c.text).join("")}" must have a <text> as a parent: ${parent.id} above ${node.id}`);
914
+ }
915
+ }
916
+ if (!(parent instanceof BaseRenderable)) {
917
+ console.error("[INSERT]", "Tried to mount a non base renderable");
918
+ throw new Error("Tried to mount a non base renderable");
919
+ }
920
+ if (!anchor) {
921
+ parent.add(node);
922
+ return;
923
+ }
924
+ const children2 = getNodeChildren(parent);
925
+ const anchorIndex = children2.indexOf(anchor);
926
+ if (anchorIndex === -1) {
927
+ log("[INSERT]", "Could not find anchor", logId(parent), logId(anchor), "[children]", ...children2.map((c) => c.id));
928
+ }
929
+ parent.add(node, anchorIndex);
930
+ }
931
+ function _removeNode(parent, node) {
932
+ log("Removing node:", logId(node), "from parent:", logId(parent));
933
+ let slotParent;
934
+ if (node instanceof SlotRenderable) {
935
+ slotParent = node;
936
+ const slotChild = slotParent.getSlotChildForRemoval(parent);
937
+ if (!slotChild) {
938
+ if (slotParent.parent === parent) {
939
+ slotParent.parent = null;
940
+ }
941
+ return;
942
+ }
943
+ node = slotChild;
944
+ }
945
+ parent.remove(node);
946
+ slotParent?.didRemoveSlotChild(parent, node);
947
+ process.nextTick(() => {
948
+ if (node instanceof BaseRenderable && !node.parent) {
949
+ node.destroyRecursively();
950
+ return;
951
+ }
952
+ });
953
+ }
954
+ function _createTextNode(value, decodeEntities) {
955
+ log("Creating text node:", value);
956
+ const id = getNextId("text-node");
957
+ if (typeof value === "number") {
958
+ value = value.toString();
959
+ }
960
+ return TextNode.fromString(decodeEntities ? decodeHTMLStrict(value) : value, { id });
961
+ }
962
+ function createSlotNode() {
963
+ const id = getNextId("slot-node");
964
+ log("Creating slot node", id);
965
+ return new SlotRenderable(id);
966
+ }
967
+ function _getParentNode(childNode) {
968
+ log("Getting parent of node:", logId(childNode));
969
+ let parent = childNode.parent ?? undefined;
970
+ if (parent instanceof RootTextNodeRenderable) {
971
+ parent = parent.textParent ?? undefined;
972
+ }
973
+ const scrollBoxCandidate = parent?.parent?.parent?.parent;
974
+ if (scrollBoxCandidate instanceof ScrollBoxRenderable2 && scrollBoxCandidate.content === parent) {
975
+ parent = scrollBoxCandidate;
976
+ }
977
+ return parent;
978
+ }
979
+ var {
980
+ render: _render,
981
+ effect,
982
+ memo: memo2,
983
+ createComponent: createComponent2,
984
+ createElement,
985
+ createTextNode,
986
+ insertNode,
987
+ insert,
988
+ spread,
989
+ setProp,
990
+ mergeProps: mergeProps3,
991
+ use
992
+ } = createRenderer2({
993
+ createElement(tagName) {
994
+ log("Creating element:", tagName);
995
+ const id = getNextId(tagName);
996
+ const solidRenderer = useContext2(RendererContext);
997
+ if (!solidRenderer) {
998
+ throw new Error("No renderer found");
999
+ }
1000
+ const elements = getComponentCatalogue();
1001
+ if (!elements[tagName]) {
1002
+ throw new Error(`[Reconciler] Unknown component type: ${tagName}`);
1003
+ }
1004
+ const element = new elements[tagName](solidRenderer, { id });
1005
+ if (element instanceof ImageRenderable2) {
1006
+ onCleanup2(() => {
1007
+ element.source = undefined;
1008
+ });
1009
+ }
1010
+ log("Element created with id:", id);
1011
+ return element;
1012
+ },
1013
+ createTextNode: (value) => _createTextNode(value, true),
1014
+ createDynamicTextNode: (value) => _createTextNode(value, false),
1015
+ createSlotNode,
1016
+ replaceText(textNode, value) {
1017
+ log("Replacing text:", value, "in node:", logId(textNode));
1018
+ if (!(textNode instanceof TextNode))
1019
+ return;
1020
+ textNode.replace(value, 0);
1021
+ },
1022
+ setProperty(node, name, value, prev) {
1023
+ if (name.startsWith("on:")) {
1024
+ const eventName = name.slice(3);
1025
+ if (value) {
1026
+ node.on(eventName, value);
1027
+ }
1028
+ if (prev) {
1029
+ node.off(eventName, prev);
1030
+ }
1031
+ return;
1032
+ }
1033
+ if (isTextNodeRenderable(node)) {
1034
+ if (name === "href") {
1035
+ node.link = { url: value };
1036
+ return;
1037
+ }
1038
+ if (name === "style") {
1039
+ node.attributes |= createTextAttributes(value);
1040
+ node.fg = value.fg ? parseColor(value.fg) : node.fg;
1041
+ node.bg = value.bg ? parseColor(value.bg) : node.bg;
1042
+ return;
1043
+ }
1044
+ return;
1045
+ }
1046
+ switch (name) {
1047
+ case "id":
1048
+ log("Id mapped", node.id, "=", value);
1049
+ node[name] = value;
1050
+ break;
1051
+ case "focused":
1052
+ if (!(node instanceof Renderable))
1053
+ return;
1054
+ if (value) {
1055
+ node.focus();
1056
+ } else {
1057
+ node.blur();
1058
+ }
1059
+ break;
1060
+ case "onChange":
1061
+ let event = undefined;
1062
+ if (node instanceof SelectRenderable2) {
1063
+ event = SelectRenderableEvents.SELECTION_CHANGED;
1064
+ } else if (node instanceof TabSelectRenderable2) {
1065
+ event = TabSelectRenderableEvents.SELECTION_CHANGED;
1066
+ } else if (node instanceof InputRenderable2) {
1067
+ event = InputRenderableEvents.CHANGE;
1068
+ }
1069
+ if (!event)
1070
+ break;
1071
+ if (value) {
1072
+ node.on(event, value);
1073
+ }
1074
+ if (prev) {
1075
+ node.off(event, prev);
1076
+ }
1077
+ break;
1078
+ case "onInput":
1079
+ if (node instanceof InputRenderable2) {
1080
+ if (value) {
1081
+ node.on(InputRenderableEvents.INPUT, value);
1082
+ }
1083
+ if (prev) {
1084
+ node.off(InputRenderableEvents.INPUT, prev);
1085
+ }
1086
+ }
1087
+ break;
1088
+ case "onSubmit":
1089
+ if (node instanceof InputRenderable2) {
1090
+ if (value) {
1091
+ node.on(InputRenderableEvents.ENTER, value);
1092
+ }
1093
+ if (prev) {
1094
+ node.off(InputRenderableEvents.ENTER, prev);
1095
+ }
1096
+ } else {
1097
+ node[name] = value;
1098
+ }
1099
+ break;
1100
+ case "onSelect":
1101
+ if (node instanceof SelectRenderable2) {
1102
+ if (value) {
1103
+ node.on(SelectRenderableEvents.ITEM_SELECTED, value);
1104
+ }
1105
+ if (prev) {
1106
+ node.off(SelectRenderableEvents.ITEM_SELECTED, prev);
1107
+ }
1108
+ } else if (node instanceof TabSelectRenderable2) {
1109
+ if (value) {
1110
+ node.on(TabSelectRenderableEvents.ITEM_SELECTED, value);
1111
+ }
1112
+ if (prev) {
1113
+ node.off(TabSelectRenderableEvents.ITEM_SELECTED, prev);
1114
+ }
1115
+ }
1116
+ break;
1117
+ case "style":
1118
+ const nextStyle = value ?? {};
1119
+ const previousStyle = prev ?? {};
1120
+ if (isEditBufferRenderable(node) && previousStyle.selectionOccupancy && !nextStyle.selectionOccupancy) {
1121
+ node.selectionOccupancy = undefined;
1122
+ }
1123
+ if (node instanceof ImageRenderable2) {
1124
+ for (const prop in previousStyle) {
1125
+ if (Object.prototype.hasOwnProperty.call(nextStyle, prop))
1126
+ continue;
1127
+ if (prop !== "fit" && prop !== "protocol")
1128
+ continue;
1129
+ node[prop] = undefined;
1130
+ }
1131
+ }
1132
+ for (const prop in nextStyle) {
1133
+ const propVal = nextStyle[prop];
1134
+ if (propVal === previousStyle[prop])
1135
+ continue;
1136
+ node[prop] = propVal;
1137
+ }
1138
+ break;
1139
+ case "text":
1140
+ case "content": {
1141
+ const textValue = typeof value === "string" ? value : Array.isArray(value) ? value.join("") : `${value}`;
1142
+ node[name] = textValue;
1143
+ break;
1144
+ }
1145
+ default:
1146
+ node[name] = value;
1147
+ }
1148
+ },
1149
+ isTextNode(node) {
1150
+ return node instanceof TextNode;
1151
+ },
1152
+ insertNode: _insertNode,
1153
+ removeNode: _removeNode,
1154
+ getParentNode: _getParentNode,
1155
+ getFirstChild(node) {
1156
+ log("Getting first child of node:", logId(node));
1157
+ const firstChild = getNodeChildren(node)[0];
1158
+ if (!firstChild) {
1159
+ log("No first child found for node:", logId(node));
1160
+ return;
1161
+ }
1162
+ log("First child found:", logId(firstChild), "for node:", logId(node));
1163
+ return firstChild;
1164
+ },
1165
+ getNextSibling(node) {
1166
+ log("Getting next sibling of node:", logId(node));
1167
+ const parent = _getParentNode(node);
1168
+ if (!parent) {
1169
+ log("No parent found for node:", logId(node));
1170
+ return;
1171
+ }
1172
+ if (node instanceof SlotRenderable) {
1173
+ const layoutSlotNode = node.getSlotChildForRemoval(parent);
1174
+ if (layoutSlotNode) {
1175
+ node = layoutSlotNode;
1176
+ }
1177
+ }
1178
+ const siblings = getNodeChildren(parent);
1179
+ const index = siblings.indexOf(node);
1180
+ if (index === -1 || index === siblings.length - 1) {
1181
+ log("No next sibling found for node:", logId(node));
1182
+ return;
1183
+ }
1184
+ const nextSibling = siblings[index + 1];
1185
+ if (!nextSibling) {
1186
+ log("Next sibling is null for node:", logId(node));
1187
+ return;
1188
+ }
1189
+ log("Next sibling found:", logId(nextSibling), "for node:", logId(node));
1190
+ return nextSibling;
1191
+ }
1192
+ });
1193
+ function getLayoutNodeConstructor(parent) {
1194
+ const parentLayoutNode = parent?.getLayoutNode?.();
1195
+ return parentLayoutNode?.constructor;
1196
+ }
1197
+ function createLayoutSlotYogaNode(parentNodeConstructor) {
1198
+ return parentNodeConstructor?.create?.() ?? Yoga.default.Node.create();
1199
+ }
1200
+
1201
+ class SlotBaseRenderable extends BaseRenderable2 {
1202
+ constructor(id) {
1203
+ super({
1204
+ id
1205
+ });
1206
+ }
1207
+ add(obj, index) {
1208
+ throw new Error("Can't add children on an Slot renderable");
1209
+ }
1210
+ getChildren() {
1211
+ return [];
1212
+ }
1213
+ remove(child) {}
1214
+ insertBefore(obj, anchor) {
1215
+ throw new Error("Can't add children on an Slot renderable");
1216
+ }
1217
+ getRenderable(id) {
1218
+ return;
1219
+ }
1220
+ getChildrenCount() {
1221
+ return 0;
1222
+ }
1223
+ requestRender() {}
1224
+ findDescendantById(id) {
1225
+ return;
1226
+ }
1227
+ }
1228
+
1229
+ class TextSlotRenderable extends TextNodeRenderable3 {
1230
+ slotParent;
1231
+ destroyed = false;
1232
+ constructor(id, parent) {
1233
+ super({ id });
1234
+ this._visible = false;
1235
+ this.slotParent = parent;
1236
+ }
1237
+ detachFromSlot() {
1238
+ this.slotParent = undefined;
1239
+ }
1240
+ disposeWithoutSlotCascade() {
1241
+ if (this.destroyed) {
1242
+ return;
1243
+ }
1244
+ this.destroyed = true;
1245
+ this.detachFromSlot();
1246
+ }
1247
+ destroy() {
1248
+ if (this.destroyed) {
1249
+ return;
1250
+ }
1251
+ this.destroyed = true;
1252
+ const slotParent = this.slotParent;
1253
+ this.slotParent = undefined;
1254
+ slotParent?.destroy();
1255
+ super.destroy();
1256
+ }
1257
+ }
1258
+
1259
+ class LayoutSlotRenderable extends SlotBaseRenderable {
1260
+ yogaNode;
1261
+ slotParent;
1262
+ destroyed = false;
1263
+ yogaNodeConstructor;
1264
+ yogaNodeFreed = false;
1265
+ constructor(id, parent, layoutParent) {
1266
+ super(id);
1267
+ this._visible = false;
1268
+ this.slotParent = parent;
1269
+ this.yogaNodeConstructor = getLayoutNodeConstructor(layoutParent);
1270
+ this.yogaNode = createLayoutSlotYogaNode(this.yogaNodeConstructor);
1271
+ this.yogaNode.setDisplay(Yoga.Display.None);
1272
+ }
1273
+ getLayoutNode() {
1274
+ return this.yogaNode;
1275
+ }
1276
+ updateFromLayout() {}
1277
+ updateLayout() {}
1278
+ onRemove() {}
1279
+ isCompatibleWith(layoutParent) {
1280
+ return this.yogaNodeConstructor === getLayoutNodeConstructor(layoutParent);
1281
+ }
1282
+ detachFromSlot() {
1283
+ this.slotParent = undefined;
1284
+ }
1285
+ freeYogaNode() {
1286
+ if (this.yogaNodeFreed) {
1287
+ return;
1288
+ }
1289
+ this.yogaNodeFreed = true;
1290
+ try {
1291
+ this.yogaNode.free();
1292
+ } catch {}
1293
+ }
1294
+ disposeWithoutSlotCascade() {
1295
+ if (this.destroyed) {
1296
+ return;
1297
+ }
1298
+ this.destroyed = true;
1299
+ this.detachFromSlot();
1300
+ this.freeYogaNode();
1301
+ }
1302
+ destroy() {
1303
+ if (this.destroyed) {
1304
+ return;
1305
+ }
1306
+ this.destroyed = true;
1307
+ const slotParent = this.slotParent;
1308
+ this.slotParent = undefined;
1309
+ this.freeYogaNode();
1310
+ slotParent?.destroy();
1311
+ }
1312
+ }
1313
+
1314
+ class SlotRenderable extends SlotBaseRenderable {
1315
+ destroyed = false;
1316
+ layoutNodesByParent = new Map;
1317
+ textNodesByParent = new Map;
1318
+ layoutNodeCount = 0;
1319
+ textNodeCount = 0;
1320
+ constructor(id) {
1321
+ super(id);
1322
+ this._visible = false;
1323
+ }
1324
+ get layoutNode() {
1325
+ return this.getCurrentSlotChild(this.layoutNodesByParent);
1326
+ }
1327
+ get textNode() {
1328
+ return this.getCurrentSlotChild(this.textNodesByParent);
1329
+ }
1330
+ isTextSlotParent(parent) {
1331
+ return isTextNodeRenderable2(parent) || parent instanceof TextRenderable3;
1332
+ }
1333
+ getCurrentSlotChild(nodesByParent) {
1334
+ for (const node of nodesByParent.values()) {
1335
+ if (node.parent) {
1336
+ return node;
1337
+ }
1338
+ }
1339
+ return nodesByParent.values().next().value;
1340
+ }
1341
+ getTextNodeForParent(parent) {
1342
+ const mappedNode = this.textNodesByParent.get(parent);
1343
+ if (mappedNode) {
1344
+ return mappedNode;
1345
+ }
1346
+ for (const [mappedParent, textNode] of this.textNodesByParent) {
1347
+ if (textNode.parent !== parent) {
1348
+ continue;
1349
+ }
1350
+ this.textNodesByParent.delete(mappedParent);
1351
+ this.textNodesByParent.set(parent, textNode);
1352
+ return textNode;
1353
+ }
1354
+ }
1355
+ getLayoutNodeForParent(parent) {
1356
+ const mappedNode = this.layoutNodesByParent.get(parent);
1357
+ if (mappedNode) {
1358
+ return mappedNode;
1359
+ }
1360
+ for (const [mappedParent, layoutNode] of this.layoutNodesByParent) {
1361
+ if (layoutNode.parent !== parent) {
1362
+ continue;
1363
+ }
1364
+ this.layoutNodesByParent.delete(mappedParent);
1365
+ this.layoutNodesByParent.set(parent, layoutNode);
1366
+ return layoutNode;
1367
+ }
1368
+ }
1369
+ takeReusableTextNode(parent) {
1370
+ for (const [mappedParent, textNode] of this.textNodesByParent) {
1371
+ if (textNode.parent) {
1372
+ continue;
1373
+ }
1374
+ this.textNodesByParent.delete(mappedParent);
1375
+ this.textNodesByParent.set(parent, textNode);
1376
+ return textNode;
1377
+ }
1378
+ }
1379
+ takeReusableLayoutNode(parent) {
1380
+ for (const [mappedParent, layoutNode] of this.layoutNodesByParent) {
1381
+ if (layoutNode.parent) {
1382
+ continue;
1383
+ }
1384
+ if (!layoutNode.isCompatibleWith(parent)) {
1385
+ continue;
1386
+ }
1387
+ this.layoutNodesByParent.delete(mappedParent);
1388
+ this.layoutNodesByParent.set(parent, layoutNode);
1389
+ return layoutNode;
1390
+ }
1391
+ }
1392
+ disposeDetachedTextNodes() {
1393
+ for (const [parent, textNode] of this.textNodesByParent) {
1394
+ if (textNode.parent) {
1395
+ continue;
1396
+ }
1397
+ this.textNodesByParent.delete(parent);
1398
+ textNode.disposeWithoutSlotCascade();
1399
+ }
1400
+ }
1401
+ disposeDetachedIncompatibleLayoutNodes(parent) {
1402
+ for (const [mappedParent, layoutNode] of this.layoutNodesByParent) {
1403
+ if (layoutNode.parent || layoutNode.isCompatibleWith(parent)) {
1404
+ continue;
1405
+ }
1406
+ this.layoutNodesByParent.delete(mappedParent);
1407
+ layoutNode.disposeWithoutSlotCascade();
1408
+ }
1409
+ }
1410
+ getAttachedSlotParent(excludedNode) {
1411
+ for (const textNode of this.textNodesByParent.values()) {
1412
+ if (textNode !== excludedNode && textNode.parent) {
1413
+ return textNode.parent;
1414
+ }
1415
+ }
1416
+ for (const layoutNode of this.layoutNodesByParent.values()) {
1417
+ if (layoutNode !== excludedNode && layoutNode.parent) {
1418
+ return layoutNode.parent;
1419
+ }
1420
+ }
1421
+ return null;
1422
+ }
1423
+ hasOtherAttachedSlotChildren(excludedNode) {
1424
+ return this.getAttachedSlotParent(excludedNode) !== null;
1425
+ }
1426
+ getSlotChild(parent) {
1427
+ if (this.isTextSlotParent(parent)) {
1428
+ const existingTextNode = this.getTextNodeForParent(parent);
1429
+ if (existingTextNode) {
1430
+ return existingTextNode;
1431
+ }
1432
+ const reusableTextNode = this.takeReusableTextNode(parent);
1433
+ if (reusableTextNode) {
1434
+ return reusableTextNode;
1435
+ }
1436
+ this.disposeDetachedIncompatibleLayoutNodes(parent);
1437
+ const textNode = new TextSlotRenderable(`slot-text-${this.id}-${++this.textNodeCount}`, this);
1438
+ this.textNodesByParent.set(parent, textNode);
1439
+ return textNode;
1440
+ }
1441
+ const existingLayoutNode = this.getLayoutNodeForParent(parent);
1442
+ if (existingLayoutNode) {
1443
+ return existingLayoutNode;
1444
+ }
1445
+ const reusableLayoutNode = this.takeReusableLayoutNode(parent);
1446
+ if (reusableLayoutNode) {
1447
+ return reusableLayoutNode;
1448
+ }
1449
+ this.disposeDetachedTextNodes();
1450
+ this.disposeDetachedIncompatibleLayoutNodes(parent);
1451
+ const layoutNode = new LayoutSlotRenderable(`slot-layout-${this.id}-${++this.layoutNodeCount}`, this, parent);
1452
+ this.layoutNodesByParent.set(parent, layoutNode);
1453
+ return layoutNode;
1454
+ }
1455
+ getSlotChildForRemoval(parent) {
1456
+ if (this.isTextSlotParent(parent)) {
1457
+ return this.getTextNodeForParent(parent);
1458
+ }
1459
+ return this.getLayoutNodeForParent(parent);
1460
+ }
1461
+ didRemoveSlotChild(parent, child) {
1462
+ const hasOtherAttachedSlotChildren = this.hasOtherAttachedSlotChildren(child);
1463
+ if (hasOtherAttachedSlotChildren && child instanceof TextSlotRenderable && this.getTextNodeForParent(parent) === child) {
1464
+ this.textNodesByParent.delete(parent);
1465
+ child.disposeWithoutSlotCascade();
1466
+ }
1467
+ if (hasOtherAttachedSlotChildren && child instanceof LayoutSlotRenderable && this.getLayoutNodeForParent(parent) === child) {
1468
+ this.layoutNodesByParent.delete(parent);
1469
+ child.disposeWithoutSlotCascade();
1470
+ }
1471
+ if (this.parent === parent) {
1472
+ this.parent = this.getAttachedSlotParent(child);
1473
+ }
1474
+ }
1475
+ destroy() {
1476
+ if (this.destroyed) {
1477
+ return;
1478
+ }
1479
+ this.destroyed = true;
1480
+ const layoutNodes = new Set(this.layoutNodesByParent.values());
1481
+ this.layoutNodesByParent.clear();
1482
+ for (const layoutNode of layoutNodes) {
1483
+ layoutNode.destroy();
1484
+ }
1485
+ const textNodes = new Set(this.textNodesByParent.values());
1486
+ this.textNodesByParent.clear();
1487
+ for (const textNode of textNodes) {
1488
+ textNode.destroy();
1489
+ }
1490
+ }
1491
+ }
1492
+ extend({
1493
+ time_to_first_draw: TimeToFirstDrawRenderable
1494
+ });
1495
+ var mountSolidRoot = (renderer, node) => {
1496
+ let dispose;
1497
+ let disposeRequested = false;
1498
+ let disposed = false;
1499
+ let mounting = true;
1500
+ let destroyRequested = false;
1501
+ const originalDestroy = renderer.destroy.bind(renderer);
1502
+ const runDispose = () => {
1503
+ if (disposed) {
1504
+ return;
1505
+ }
1506
+ if (!dispose) {
1507
+ disposeRequested = true;
1508
+ return;
1509
+ }
1510
+ disposed = true;
1511
+ dispose();
1512
+ };
1513
+ renderer.once("destroy", runDispose);
1514
+ renderer.destroy = () => {
1515
+ if (mounting) {
1516
+ destroyRequested = true;
1517
+ return;
1518
+ }
1519
+ originalDestroy();
1520
+ };
1521
+ try {
1522
+ dispose = _render(() => createComponent2(RendererContext.Provider, {
1523
+ get value() {
1524
+ return renderer;
1525
+ },
1526
+ get children() {
1527
+ return createComponent2(node, {});
1528
+ }
1529
+ }), renderer.root);
1530
+ } finally {
1531
+ mounting = false;
1532
+ renderer.destroy = originalDestroy;
1533
+ }
1534
+ if (disposeRequested) {
1535
+ runDispose();
1536
+ }
1537
+ if (destroyRequested) {
1538
+ originalDestroy();
1539
+ }
1540
+ };
1541
+ var render = async (node, rendererOrConfig = {}) => {
1542
+ const renderer = rendererOrConfig instanceof CliRenderer ? rendererOrConfig : await createCliRenderer({
1543
+ ...rendererOrConfig,
1544
+ onDestroy: () => {
1545
+ rendererOrConfig.onDestroy?.();
1546
+ }
1547
+ });
1548
+ engine2.attach(renderer);
1549
+ mountSolidRoot(renderer, node);
1550
+ };
1551
+
1552
+ // ../../apps/shared/theme.ts
1553
+ import { lstatSync, readFileSync } from "fs";
1554
+ import { homedir } from "os";
1555
+ import { join } from "path";
1556
+
1557
+ // ../../apps/shared/Boo.ghostty
1558
+ var Boo_default = `# Boo: the default terminal palette, matching the native installer.
1559
+ background = #08080E
1560
+ foreground = #F5ECD8
1561
+ cursor-color = #25D9E8
1562
+ cursor-text = #071014
1563
+ selection-background = #0E5965
1564
+ selection-foreground = #F5ECD8
1565
+ palette = 0=#171522
1566
+ palette = 1=#FF577D
1567
+ palette = 2=#2BE566
1568
+ palette = 3=#FFD51A
1569
+ palette = 4=#5355C9
1570
+ palette = 5=#FF2A8A
1571
+ palette = 6=#25D9E8
1572
+ palette = 7=#B7AB9A
1573
+ palette = 8=#686379
1574
+ palette = 9=#FF839D
1575
+ palette = 10=#80F5A4
1576
+ palette = 11=#FFE873
1577
+ palette = 12=#9698FF
1578
+ palette = 13=#FF83BB
1579
+ palette = 14=#8CEEF5
1580
+ palette = 15=#F5ECD8
1581
+ # Extended terminal slots retain Boo's existing UI surface colors.
1582
+ palette = 16=#111018
1583
+ palette = 17=#171522
1584
+ palette = 18=#211D2C
1585
+ palette = 19=#071014
1586
+ `;
1587
+
1588
+ // ../../apps/shared/theme.ts
1589
+ var selectionPath = join(process.env.XDG_CONFIG_HOME || join(homedir(), ".config"), "ghostty/auto/theme.ghostty");
1590
+ var resources = process.env.GHOSTTY_RESOURCES_DIR || "/usr/share/ghostty";
1591
+ function colors(source) {
1592
+ const values = {};
1593
+ for (const line of source.split(/\r?\n/)) {
1594
+ const match = line.match(/^\s*(background|foreground|cursor-color|cursor-text|selection-background|selection-foreground|palette)\s*=\s*(?:(\d+)=)?#?([\da-f]{6})\s*$/i);
1595
+ if (match)
1596
+ values[match[1] === "palette" ? `palette${match[2]}` : match[1]] = `#${match[3]}`;
1597
+ }
1598
+ for (const key of ["background", "foreground", ...Array.from({ length: 16 }, (_, i) => `palette${i}`)]) {
1599
+ if (!values[key])
1600
+ throw new Error(`Theme is missing ${key}`);
1601
+ }
1602
+ return values;
1603
+ }
1604
+ function mix(a, b, amount) {
1605
+ return "#" + [1, 3, 5].map((i) => Math.round(parseInt(a.slice(i, i + 2), 16) * (1 - amount) + parseInt(b.slice(i, i + 2), 16) * amount).toString(16).padStart(2, "0")).join("");
1606
+ }
1607
+ function ui(c, boo, light) {
1608
+ return {
1609
+ background: c.background,
1610
+ foreground: c.foreground,
1611
+ card: boo ? c.palette16 : mix(c.background, c.foreground, 0.04),
1612
+ panel: boo ? c.palette17 : mix(c.background, c.foreground, 0.07),
1613
+ panelAlt: boo ? c.palette18 : mix(c.background, c.foreground, 0.12),
1614
+ selected: boo ? c["selection-background"] : mix(c.background, c.palette6, 0.25),
1615
+ muted: boo ? c.palette7 : mix(c.background, c.foreground, 0.7),
1616
+ accent: c.palette6,
1617
+ highlight: light ? mix(c.palette5, c.foreground, 0.6) : c.palette5,
1618
+ accentInk: boo ? c.palette19 : c.background,
1619
+ warning: c.palette3,
1620
+ danger: c.palette5,
1621
+ success: c.palette2,
1622
+ border: c.palette4
1623
+ };
1624
+ }
1625
+ var themes = [
1626
+ { name: "Boo", kind: "Dark", description: "Electric cyan. Warm ink. Unmistakably Boo." },
1627
+ { name: "Catppuccin Mocha", kind: "Dark", description: "Soft pastels on a deep, calm canvas." },
1628
+ { name: "Catppuccin Latte", kind: "Light", description: "A bright canvas with gentle, colorful accents." }
1629
+ ].map((entry) => {
1630
+ const path = join(resources, "themes", entry.name);
1631
+ try {
1632
+ const native = colors(entry.name === "Boo" ? Boo_default : readFileSync(path, "utf8"));
1633
+ return { ...entry, path, native, ui: ui(native, entry.name === "Boo", entry.kind === "Light"), error: "" };
1634
+ } catch {
1635
+ return { ...entry, path, native: colors(Boo_default), ui: ui(colors(Boo_default), true, false), error: "Bundled theme unavailable or invalid." };
1636
+ }
1637
+ });
1638
+ function readSelection() {
1639
+ let source = "";
1640
+ try {
1641
+ const stat = lstatSync(selectionPath);
1642
+ if (!stat.isFile() || stat.size > 65536)
1643
+ throw new Error("Selection must be a regular file under 64 KiB");
1644
+ source = readFileSync(selectionPath, "utf8");
1645
+ } catch (error) {
1646
+ if (error.code !== "ENOENT")
1647
+ throw error;
1648
+ }
1649
+ const lines = source.split(/\r?\n/).filter((line) => /^\s*theme\s*=/.test(line));
1650
+ const value = lines.at(-1)?.replace(/^\s*theme\s*=\s*/, "").trim().replace(/^"(.*)"$/, "$1");
1651
+ const malformed = source.split(/\r?\n/).some((line) => /^\s*theme(?:\s|=|$)/.test(line) && !/^\s*theme\s*=\s*\S/.test(line));
1652
+ const index = malformed ? -1 : value ? themes.findIndex((theme) => theme.name === value || theme.path === value) : 0;
1653
+ return { source, index, custom: index < 0, name: index < 0 ? "Custom theme" : themes[index].name };
1654
+ }
1655
+ var palette = (() => {
1656
+ try {
1657
+ return themes[readSelection().index]?.ui || themes[0].ui;
1658
+ } catch {
1659
+ return themes[0].ui;
1660
+ }
1661
+ })();
1662
+
1663
+ // src/App.tsx
1664
+ import { TextAttributes as TextAttributes2 } from "@opentui/core";
1665
+ import { For as For2, Show, createSignal as createSignal4, onMount as onMount2 } from "solid-js";
1666
+
1667
+ // src/backend.ts
1668
+ import { createHash as createHash2 } from "crypto";
1669
+ import { chmod as chmod2, mkdtemp, open as open2, rm } from "fs/promises";
1670
+ import { tmpdir as tmpdir4 } from "os";
1671
+ import { dirname as dirname3, join as join5 } from "path";
1672
+ import { setTimeout as delay } from "timers/promises";
1673
+ import { fileURLToPath as fileURLToPath2 } from "url";
1674
+
1675
+ // src/backends/macos.ts
1676
+ import { realpath } from "fs/promises";
1677
+ import { tmpdir, userInfo } from "os";
1678
+
1679
+ // src/shared.ts
1680
+ import { spawn } from "child_process";
1681
+
1682
+ // src/dependencies.ts
1683
+ import { realpathSync, statSync as statSync2 } from "fs";
1684
+ import { dirname } from "path";
1685
+
1686
+ // src/runtime.ts
1687
+ import { constants, accessSync, statSync } from "fs";
1688
+ import { delimiter, join as join2 } from "path";
1689
+ import { fileURLToPath } from "url";
1690
+ function which(name, path = process.env.PATH ?? "") {
1691
+ for (const directory of path.split(delimiter)) {
1692
+ if (!directory)
1693
+ continue;
1694
+ const candidate = join2(directory, name);
1695
+ try {
1696
+ accessSync(candidate, constants.X_OK);
1697
+ if (statSync(candidate).isFile())
1698
+ return candidate;
1699
+ } catch {}
1700
+ }
1701
+ return null;
1702
+ }
1703
+ function resolvePackage(name) {
1704
+ return fileURLToPath(import.meta.resolve(name));
1705
+ }
1706
+
1707
+ // src/dependencies.ts
1708
+ var commonTools = ["curl", "zstd"];
1709
+ var candidateTools = ["gh", "unzip"];
1710
+ function preferredExecutable(name, platform, path, macSystemExists) {
1711
+ if (platform === "darwin" && (name === "curl" || name === "unzip") && macSystemExists)
1712
+ return `/usr/bin/${name}`;
1713
+ return path;
1714
+ }
1715
+ function trustedLinuxPath(path) {
1716
+ if (!path)
1717
+ return null;
1718
+ try {
1719
+ const resolved = realpathSync(path);
1720
+ const binary = statSync2(resolved);
1721
+ if (!binary.isFile() || binary.uid !== 0 || (binary.mode & 18) !== 0 || (binary.mode & 73) === 0)
1722
+ return null;
1723
+ let parent = dirname(resolved);
1724
+ while (true) {
1725
+ const stat = statSync2(parent);
1726
+ if (!stat.isDirectory() || stat.uid !== 0 || (stat.mode & 18) !== 0)
1727
+ return null;
1728
+ const next = dirname(parent);
1729
+ if (next === parent)
1730
+ break;
1731
+ parent = next;
1732
+ }
1733
+ return resolved;
1734
+ } catch {
1735
+ return null;
1736
+ }
1737
+ }
1738
+ function systemExecutable(name) {
1739
+ try {
1740
+ return statSync2(`/usr/bin/${name}`).isFile() ? `/usr/bin/${name}` : null;
1741
+ } catch {
1742
+ return null;
1743
+ }
1744
+ }
1745
+ function executable(name) {
1746
+ const path = preferredExecutable(name, process.platform, which(name), systemExecutable(name) !== null);
1747
+ if (process.platform === "linux")
1748
+ return trustedLinuxPath(path);
1749
+ if (path || process.platform !== "darwin")
1750
+ return path;
1751
+ const prefixes = process.arch === "arm64" ? ["/opt/homebrew", "/usr/local"] : ["/usr/local", "/opt/homebrew"];
1752
+ for (const prefix of prefixes) {
1753
+ const path2 = which(name, `${prefix}/bin`);
1754
+ if (path2)
1755
+ return path2;
1756
+ }
1757
+ return null;
1758
+ }
1759
+ function missing(names) {
1760
+ return names.filter((name) => !executable(name));
1761
+ }
1762
+
1763
+ // src/shared.ts
1764
+ var positive = (n) => Number.isSafeInteger(n) && Number(n) > 0;
1765
+ var cleanText = (s) => String(s).replace(/[\x00-\x1f\x7f-\x9f]/g, " ").slice(0, 300);
1766
+ async function command(argv, signal, options = {}) {
1767
+ signal.throwIfAborted();
1768
+ const binary = argv[0].includes("/") ? argv[0] : executable(argv[0]);
1769
+ if (!binary)
1770
+ throw new Error(`Missing ${argv[0]}. Refresh dependency setup before continuing.`);
1771
+ const child = spawn(binary, argv.slice(1), {
1772
+ stdio: options.terminal ? "inherit" : ["pipe", "pipe", "pipe"],
1773
+ env: { ...process.env, GH_DEBUG: "", GH_PAGER: "cat", PAGER: "cat", GH_PROMPT_DISABLED: "1" }
1774
+ });
1775
+ let failure;
1776
+ const exited = new Promise((resolve) => {
1777
+ child.on("error", (error) => {
1778
+ failure = error;
1779
+ });
1780
+ child.on("close", resolve);
1781
+ });
1782
+ let stderr = "";
1783
+ child.stderr?.on("data", (data) => {
1784
+ if (argv[0] !== "gh")
1785
+ stderr = (stderr + data.toString()).slice(-1500);
1786
+ });
1787
+ child.stdin?.on("error", (error) => {
1788
+ failure = error;
1789
+ });
1790
+ child.stdin?.end(options.input);
1791
+ const stop = () => {
1792
+ child.kill("SIGTERM");
1793
+ };
1794
+ signal.addEventListener("abort", stop, { once: true });
1795
+ let output = "";
1796
+ let count = 0;
1797
+ try {
1798
+ if (child.stdout)
1799
+ for await (const chunk of child.stdout) {
1800
+ signal.throwIfAborted();
1801
+ count += chunk.length;
1802
+ if (count > (options.limit ?? 8 * 1024 ** 2))
1803
+ throw new Error("Command output exceeds the expected size.");
1804
+ if (options.consume)
1805
+ await options.consume(chunk);
1806
+ else
1807
+ output += chunk.toString();
1808
+ }
1809
+ const code = await exited;
1810
+ signal.throwIfAborted();
1811
+ if (failure || code !== 0) {
1812
+ const message = argv[0] === "gh" ? `GitHub request failed. Run ${cleanText(binary)} auth login if needed; check repository access and your connection, then refresh.` : `${argv[0].split("/").pop()} failed (${code ?? "not started"}). ${cleanText(stderr || failure?.message || "")}`;
1813
+ throw new Error(message);
1814
+ }
1815
+ return output;
1816
+ } finally {
1817
+ signal.removeEventListener("abort", stop);
1818
+ if (child.exitCode === null && child.signalCode === null)
1819
+ child.kill("SIGTERM");
1820
+ await exited;
1821
+ }
1822
+ }
1823
+
1824
+ // src/installers/homebrew.ts
1825
+ import { spawn as spawn2 } from "child_process";
1826
+ var packages = [...commonTools, ...candidateTools];
1827
+ function dependencies(includeGitHub = false) {
1828
+ const required = includeGitHub ? packages : commonTools;
1829
+ const absent = missing(required);
1830
+ return {
1831
+ missing: absent,
1832
+ installer: absent.length ? executable("brew") : null,
1833
+ guidance: "Install Homebrew from https://brew.sh, then refresh."
1834
+ };
1835
+ }
1836
+ async function install(approved, signal) {
1837
+ signal.throwIfAborted();
1838
+ if (process.platform !== "darwin" || process.getuid?.() === 0)
1839
+ throw new Error("Run Boo Flash on macOS as your normal user, not with sudo.");
1840
+ const absent = packages.filter((name) => approved.includes(name) && !executable(name));
1841
+ if (!absent.length)
1842
+ return "installed";
1843
+ const brew = executable("brew");
1844
+ if (!brew)
1845
+ throw new Error("Homebrew not found. Follow https://brew.sh, then refresh.");
1846
+ process.stdout.write(`
1847
+ Boo Flash: Homebrew is installing ${absent.join(", ")} and required dependencies.
1848
+ Ctrl-C cancels and quits; wait for Homebrew to finish cleanup.
1849
+
1850
+ `);
1851
+ const child = spawn2(brew, ["install", "--formula", ...absent], {
1852
+ stdio: "inherit",
1853
+ env: {
1854
+ ...process.env,
1855
+ HOMEBREW_NO_ASK: "1",
1856
+ HOMEBREW_NO_AUTO_UPDATE: "1",
1857
+ HOMEBREW_NO_INSTALL_UPGRADE: "1",
1858
+ HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: "1",
1859
+ HOMEBREW_NO_INSTALL_CLEANUP: "1"
1860
+ }
1861
+ });
1862
+ let failed = false;
1863
+ const [code, exitSignal] = await new Promise((resolve) => {
1864
+ child.on("error", () => {
1865
+ failed = true;
1866
+ });
1867
+ child.on("close", (code2, signal2) => resolve([code2, signal2]));
1868
+ });
1869
+ if (signal.aborted || code === 130 || exitSignal === "SIGINT")
1870
+ return "cancelled";
1871
+ if (failed || code !== 0)
1872
+ throw new Error(`Homebrew failed (${code ?? "not started"}). Quit to read its terminal output, resolve the reported issue, then refresh to retry.`);
1873
+ const remaining = approved.filter((name) => !executable(name));
1874
+ if (remaining.length)
1875
+ throw new Error(`Still missing: ${remaining.join(", ")}. Check Homebrew's terminal output and command paths, then refresh.`);
1876
+ return "installed";
1877
+ }
1878
+
1879
+ // src/backends/platform.ts
1880
+ import { spawn as spawn3 } from "child_process";
1881
+ function startWriter(binary, args) {
1882
+ const child = spawn3(binary, args, { stdio: ["pipe", "ignore", "pipe"] });
1883
+ let stderr = "";
1884
+ child.stderr.on("data", (data) => {
1885
+ stderr = (stderr + data.toString()).slice(-1500);
1886
+ });
1887
+ child.stdin.on("error", () => {});
1888
+ const done = new Promise((resolve) => {
1889
+ child.on("error", (error) => {
1890
+ stderr = `Could not start the privileged writer: ${error.message}`;
1891
+ });
1892
+ child.on("close", resolve);
1893
+ });
1894
+ return { stdin: child.stdin, done, error: () => stderr, stop: () => child.stdin.destroy() };
1895
+ }
1896
+
1897
+ // src/backends/macos.ts
1898
+ var diskID = /^disk[0-9]+$/;
1899
+ var deviceID = /^disk[0-9]+(?:s[0-9]+)*$/;
1900
+ async function plist(argv, signal) {
1901
+ const input = await command(argv, signal);
1902
+ return JSON.parse(await command(["/usr/bin/plutil", "-convert", "json", "-o", "-", "--", "-"], signal, { input }));
1903
+ }
1904
+ async function disks(signal, source, staging = tmpdir(), unmounted) {
1905
+ const info = (id) => plist(["/usr/sbin/diskutil", "info", "-plist", id], signal);
1906
+ const listing = await plist(["/usr/sbin/diskutil", "list", "-plist", "physical"], signal);
1907
+ const physical = new Set((listing.WholeDisks ?? []).filter((id) => diskID.test(id)));
1908
+ if (!physical.size)
1909
+ throw new Error("Cannot resolve physical disks. Nothing can be selected.");
1910
+ const apfs = await plist(["/usr/sbin/diskutil", "apfs", "list", "-plist"], signal);
1911
+ if (!Array.isArray(apfs.Containers))
1912
+ throw new Error("Cannot resolve APFS storage. Nothing can be selected.");
1913
+ const containers = apfs.Containers;
1914
+ const protectedDisks = new Set;
1915
+ const resolve = async (id, seen = new Set) => {
1916
+ if (seen.has(id))
1917
+ throw new Error("Unresolved storage cycle. Nothing can be selected.");
1918
+ const next = new Set(seen).add(id);
1919
+ const d = await info(id);
1920
+ if (!deviceID.test(d.DeviceIdentifier))
1921
+ throw new Error("Unresolved backing device. Nothing can be selected.");
1922
+ if (d.WholeDisk === true && physical.has(d.DeviceIdentifier))
1923
+ return [d.DeviceIdentifier];
1924
+ if (d.WholeDisk === false && physical.has(d.ParentWholeDisk))
1925
+ return resolve(d.ParentWholeDisk, next);
1926
+ const container = containers.find((c) => c.ContainerReference === d.DeviceIdentifier || c.ContainerReference === d.APFSContainerReference || c.Volumes?.some((v) => v.DeviceIdentifier === d.DeviceIdentifier));
1927
+ if (container) {
1928
+ if (!Array.isArray(container.PhysicalStores) || !container.PhysicalStores.length)
1929
+ throw new Error("APFS physical stores are unavailable.");
1930
+ const result = [];
1931
+ for (const store of container.PhysicalStores) {
1932
+ if (!deviceID.test(store.DeviceIdentifier))
1933
+ throw new Error("Invalid APFS physical store.");
1934
+ result.push(...await resolve(store.DeviceIdentifier, next));
1935
+ }
1936
+ return result;
1937
+ }
1938
+ if (d.WholeDisk === false && diskID.test(d.ParentWholeDisk))
1939
+ return resolve(d.ParentWholeDisk, next);
1940
+ throw new Error("A protected path has unsupported backing storage (for example RAID or a disk image). Nothing can be selected.");
1941
+ };
1942
+ const protectedPaths = ["/", "/System/Volumes/Data", userInfo().homedir, process.cwd(), source, process.execPath, staging];
1943
+ protectedPaths.push(resolvePackage("@opentui/core"));
1944
+ for (const name of ["@opentui/solid", "solid-js"]) {
1945
+ try {
1946
+ protectedPaths.push(resolvePackage(name));
1947
+ } catch {}
1948
+ }
1949
+ protectedPaths.push(process.env.OTUI_ASSET_ROOT || resolvePackage(`@opentui/core-darwin-${process.arch}`));
1950
+ const zstd = executable("zstd");
1951
+ if (!zstd)
1952
+ throw new Error("Install zstd before flashing.");
1953
+ protectedPaths.push(zstd);
1954
+ const gh = executable("gh");
1955
+ if (gh)
1956
+ protectedPaths.push(gh);
1957
+ for (const path of protectedPaths) {
1958
+ const canonical = await realpath(path);
1959
+ const device = (await command(["/usr/bin/stat", "-f", "%Sd", canonical], signal)).trim();
1960
+ if (!deviceID.test(device))
1961
+ throw new Error("Cannot resolve a protected path to a disk. Nothing can be selected.");
1962
+ for (const id of await resolve(device))
1963
+ protectedDisks.add(id);
1964
+ }
1965
+ for (const container of containers) {
1966
+ if (container.PhysicalStores?.length !== 1 || container.Volumes?.some((v) => v.Roles?.some((r) => r === "System" || r === "Data"))) {
1967
+ for (const id of await resolve(container.ContainerReference))
1968
+ protectedDisks.add(id);
1969
+ }
1970
+ }
1971
+ const registry = await plist(["/usr/sbin/ioreg", "-a", "-r", "-c", "IOMedia", "-d", "1"], signal);
1972
+ if (!Array.isArray(registry))
1973
+ throw new Error("Cannot identify attached media. Nothing can be selected.");
1974
+ const disks2 = [];
1975
+ for (const id of physical) {
1976
+ if (protectedDisks.has(id))
1977
+ continue;
1978
+ const d = await info(id);
1979
+ const media = registry.filter((m) => m["BSD Name"] === id && m.Whole === true);
1980
+ const layout = listing.AllDisksAndPartitions?.find((p) => p.DeviceIdentifier === id);
1981
+ if (!layout || JSON.stringify(layout).includes("Apple_CoreStorage") || JSON.stringify(layout).includes("Apple_RAID"))
1982
+ continue;
1983
+ if (d.DeviceIdentifier !== id || d.DeviceNode !== `/dev/${id}` || d.WholeDisk !== true || d.Internal !== false || d.BusProtocol !== "USB" || d.WritableMedia !== true || d.DeviceBlockSize !== 512 || d.RAIDMaster === true || d.RAIDSlice === true || !positive(d.Size) || media.length !== 1 || !positive(media[0].IORegistryEntryID))
1984
+ continue;
1985
+ const identity = JSON.stringify([media[0].IORegistryEntryID, d.Size, d.DeviceTreePath ?? "", d.MediaName ?? ""]);
1986
+ if (id === unmounted) {
1987
+ const volumes = [id, ...(layout.Partitions ?? []).map((p) => p.DeviceIdentifier)];
1988
+ const targetStores = new Set(volumes);
1989
+ for (const c of containers) {
1990
+ if (c.PhysicalStores?.some((s) => targetStores.has(s.DeviceIdentifier))) {
1991
+ volumes.push(...(c.Volumes ?? []).map((v) => v.DeviceIdentifier));
1992
+ }
1993
+ }
1994
+ for (const volume of volumes) {
1995
+ if (!deviceID.test(volume))
1996
+ throw new Error("Cannot verify all target volumes are unmounted.");
1997
+ const v = await info(volume);
1998
+ if (v.Mounted === true || typeof v.MountPoint === "string" && v.MountPoint !== "") {
1999
+ throw new Error("A target volume is still mounted or was remounted. Close apps using the USB and try again.");
2000
+ }
2001
+ }
2002
+ }
2003
+ disks2.push({ id, bytes: d.Size, name: cleanText(d.MediaName || d.IORegistryEntryName || "USB drive"), identity });
2004
+ }
2005
+ return disks2;
2006
+ }
2007
+ async function authorize(signal) {
2008
+ await command(["/usr/bin/sudo", "-v"], signal, { terminal: true });
2009
+ }
2010
+ var platform = {
2011
+ id: "macos",
2012
+ label: "macOS / x86_64 UEFI",
2013
+ successTitle: "USB safely ejected",
2014
+ successDetail: "You can now remove the USB and boot your target computer.",
2015
+ authorizationDetail: "The ordinary macOS sudo prompt follows. Boo Flash never reads your password.",
2016
+ ejectDetail: "Waiting for macOS to make the USB safe to remove.",
2017
+ incompleteDetail: "USB may be incomplete; not safely ejected. Eject it with macOS before removal.",
2018
+ finalAuthorization: false,
2019
+ reunmountBeforeEject: false,
2020
+ dependencies,
2021
+ installDependencies: install,
2022
+ disks,
2023
+ authorize,
2024
+ async unmount(disk, signal) {
2025
+ if (!diskID.test(disk.id))
2026
+ throw new Error("Invalid macOS disk identifier.");
2027
+ await command(["/usr/sbin/diskutil", "unmountDisk", `/dev/${disk.id}`], signal);
2028
+ },
2029
+ writer(disk) {
2030
+ if (!diskID.test(disk.id))
2031
+ throw new Error("Invalid macOS disk identifier.");
2032
+ return startWriter("/usr/bin/sudo", ["-n", "--", "/bin/dd", `of=/dev/r${disk.id}`, "bs=4m"]);
2033
+ },
2034
+ async flush(_disk, signal) {
2035
+ await command(["/bin/sync"], signal);
2036
+ },
2037
+ async eject(disk, signal) {
2038
+ if (!diskID.test(disk.id))
2039
+ throw new Error("Invalid macOS disk identifier.");
2040
+ await command(["/usr/sbin/diskutil", "eject", `/dev/${disk.id}`], signal);
2041
+ }
2042
+ };
2043
+
2044
+ // src/backends/linux.ts
2045
+ import { lstatSync as lstatSync2, realpathSync as realpathSync2 } from "fs";
2046
+ import { lstat, readFile, realpath as realpath2 } from "fs/promises";
2047
+ import { tmpdir as tmpdir2, userInfo as userInfo2 } from "os";
2048
+ import { basename, dirname as dirname2, join as join3 } from "path";
2049
+ var diskID2 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
2050
+ var stableDevice = /^\/dev\/disk\/by-id\/[A-Za-z0-9._:+\\-]{1,240}$/;
2051
+ var majorMinor = /^(0|[1-9][0-9]*):(0|[1-9][0-9]*)$/;
2052
+ var tools = ["sudo", "dd", "sync", "lsblk", "findmnt", "udevadm", "umount", "udisksctl"];
2053
+ var protectedSystemPaths = ["/", "/boot", "/boot/efi", "/efi", "/usr", "/var", "/opt", "/srv", "/home"];
2054
+ var findmntSourceColumn = "SOURCE";
2055
+ function exactObject(value) {
2056
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2057
+ }
2058
+ function parseTopology(input) {
2059
+ let root;
2060
+ try {
2061
+ root = JSON.parse(input);
2062
+ } catch {
2063
+ throw new Error("Linux block-device inventory is not valid JSON. Nothing can be selected.");
2064
+ }
2065
+ if (!exactObject(root) || !Array.isArray(root.blockdevices)) {
2066
+ throw new Error("Linux block-device inventory is incomplete. Nothing can be selected.");
2067
+ }
2068
+ const devices = new Map;
2069
+ const parents = new Map;
2070
+ const children2 = new Map;
2071
+ const visit = (raw, parent) => {
2072
+ if (!exactObject(raw))
2073
+ throw new Error("Linux block-device inventory contains an invalid device. Nothing can be selected.");
2074
+ const block = raw;
2075
+ if (typeof block.path !== "string" || !/^\/dev\/[^\x00-\x1f\x7f-\x9f]{1,300}$/.test(block.path) || typeof block.name !== "string" || typeof block.kname !== "string" || typeof block.type !== "string" || !majorMinor.test(String(block["maj:min"])) || !Number.isSafeInteger(block.size) || Number(block.size) < 0 || !positive(block["log-sec"]) || typeof block.ro !== "boolean" || typeof block.rm !== "boolean" || !(block.tran === null || typeof block.tran === "string") || !(block.model === null || typeof block.model === "string") || !(block.fstype === null || typeof block.fstype === "string") || !(block.uuid === null || typeof block.uuid === "string") || !(block.label === null || typeof block.label === "string") || !(block.partuuid === null || typeof block.partuuid === "string") || !(block.partlabel === null || typeof block.partlabel === "string") || !Array.isArray(block.mountpoints) || block.mountpoints.some((point) => !(point === null || typeof point === "string"))) {
2076
+ throw new Error("Linux block-device inventory contains unsupported values. Nothing can be selected.");
2077
+ }
2078
+ const key = String(block["maj:min"]);
2079
+ const device = {
2080
+ id: basename(block.path),
2081
+ path: block.path,
2082
+ type: block.type,
2083
+ size: Number(block.size),
2084
+ model: cleanText(block.model ?? "USB drive").trim() || "USB drive",
2085
+ transport: block.tran,
2086
+ removable: block.rm,
2087
+ readonly: block.ro,
2088
+ sector: block["log-sec"],
2089
+ majorMinor: key,
2090
+ mountpoints: block.mountpoints.filter((point) => typeof point === "string" && point.length > 0),
2091
+ fstype: block.fstype,
2092
+ uuid: block.uuid,
2093
+ label: block.label,
2094
+ partuuid: block.partuuid,
2095
+ partlabel: block.partlabel,
2096
+ ambiguous: false
2097
+ };
2098
+ const current = devices.get(key);
2099
+ if (current) {
2100
+ const structural = ["type", "size", "readonly", "sector", "fstype", "uuid", "label", "partuuid", "partlabel"];
2101
+ if (structural.some((field) => current[field] !== device[field])) {
2102
+ throw new Error("Linux block-device inventory disagrees about a device. Nothing can be selected.");
2103
+ }
2104
+ if (JSON.stringify(current) !== JSON.stringify(device))
2105
+ current.ambiguous = true;
2106
+ } else
2107
+ devices.set(key, device);
2108
+ if (parent) {
2109
+ if (!parents.has(key))
2110
+ parents.set(key, new Set);
2111
+ if (!children2.has(parent))
2112
+ children2.set(parent, new Set);
2113
+ parents.get(key).add(parent);
2114
+ children2.get(parent).add(key);
2115
+ }
2116
+ if (block.children !== undefined && !Array.isArray(block.children)) {
2117
+ throw new Error("Linux block-device relationships are invalid. Nothing can be selected.");
2118
+ }
2119
+ for (const child of block.children ?? [])
2120
+ visit(child, key);
2121
+ };
2122
+ for (const block of root.blockdevices)
2123
+ visit(block);
2124
+ if (!devices.size)
2125
+ throw new Error("Cannot resolve Linux block devices. Nothing can be selected.");
2126
+ return { devices, parents, children: children2 };
2127
+ }
2128
+ function related(start, edges) {
2129
+ const found = new Set;
2130
+ const pending = [start];
2131
+ while (pending.length) {
2132
+ const key = pending.pop();
2133
+ if (found.has(key))
2134
+ continue;
2135
+ found.add(key);
2136
+ for (const next of edges.get(key) ?? [])
2137
+ pending.push(next);
2138
+ }
2139
+ return found;
2140
+ }
2141
+ function protectedDisks(topology, protectedDevices) {
2142
+ const result = new Set;
2143
+ for (const key of protectedDevices) {
2144
+ const match = majorMinor.exec(key);
2145
+ if (!match)
2146
+ throw new Error("A protected Linux path has an invalid backing-device identity. Nothing can be selected.");
2147
+ if (!topology.devices.has(key))
2148
+ throw new Error("A protected Linux path cannot be mapped to storage. Nothing can be selected.");
2149
+ const device = topology.devices.get(key);
2150
+ const members = device.uuid ? [...topology.devices.entries()].filter(([, candidate]) => candidate.uuid === device.uuid).map(([candidate]) => candidate) : [key];
2151
+ for (const member of members) {
2152
+ const ancestors = related(member, topology.parents);
2153
+ const disks2 = [...ancestors].filter((candidate) => topology.devices.get(candidate)?.type === "disk");
2154
+ if (!disks2.length)
2155
+ throw new Error("A protected Linux path has unsupported backing storage. Nothing can be selected.");
2156
+ for (const disk of disks2)
2157
+ result.add(disk);
2158
+ }
2159
+ }
2160
+ return result;
2161
+ }
2162
+ function eligibleDevices(topology, protectedDevices, unmounted, usbParents) {
2163
+ const protectedRoots = protectedDisks(topology, protectedDevices);
2164
+ const uuidMembers = new Map;
2165
+ for (const device of topology.devices.values())
2166
+ if (device.uuid) {
2167
+ const members = uuidMembers.get(device.uuid) ?? [];
2168
+ members.push(device);
2169
+ uuidMembers.set(device.uuid, members);
2170
+ }
2171
+ const activeSharedFilesystems = new Set([...uuidMembers.entries()].filter(([, members]) => members.length > 1 && members.some((member) => member.mountpoints.length > 0)).map(([uuid]) => uuid));
2172
+ const parentCounts = new Map;
2173
+ if (usbParents)
2174
+ for (const [key, parent] of usbParents) {
2175
+ if (!topology.devices.has(key) || topology.parents.get(key)?.size)
2176
+ continue;
2177
+ parentCounts.set(parent, (parentCounts.get(parent) ?? 0) + 1);
2178
+ }
2179
+ return [...topology.devices.values()].filter((device) => {
2180
+ if (device.type !== "disk" || device.transport !== "usb" || device.readonly || device.sector !== 512 || !positive(device.size) || device.ambiguous || device.path !== `/dev/${device.id}` || !diskID2.test(device.id) || protectedRoots.has(device.majorMinor) || topology.parents.get(device.majorMinor)?.size || usbParents && (!usbParents.has(device.majorMinor) || parentCounts.get(usbParents.get(device.majorMinor)) !== 1))
2181
+ return false;
2182
+ const descendants = related(device.majorMinor, topology.children);
2183
+ if ([...descendants].some((key) => {
2184
+ const child = topology.devices.get(key);
2185
+ return key !== device.majorMinor && child.type !== "part" || child.mountpoints.some((point) => point === "[SWAP]") || child.uuid !== null && activeSharedFilesystems.has(child.uuid) || unmounted === device.id && child.mountpoints.length > 0;
2186
+ }))
2187
+ return false;
2188
+ return true;
2189
+ });
2190
+ }
2191
+ async function topology(signal) {
2192
+ return parseTopology(await command([
2193
+ "lsblk",
2194
+ "--json",
2195
+ "--bytes",
2196
+ "--paths",
2197
+ "--tree",
2198
+ "--output",
2199
+ "NAME,KNAME,PATH,TYPE,SIZE,MODEL,TRAN,RM,RO,LOG-SEC,MOUNTPOINTS,MAJ:MIN,PKNAME,FSTYPE,UUID,LABEL,PARTUUID,PARTLABEL"
2200
+ ], signal, { limit: 8 * 1024 ** 2 }));
2201
+ }
2202
+ async function protectedPaths(source, staging) {
2203
+ const paths = [
2204
+ ...protectedSystemPaths,
2205
+ userInfo2().homedir,
2206
+ process.cwd(),
2207
+ source,
2208
+ process.execPath,
2209
+ staging
2210
+ ];
2211
+ paths.push(resolvePackage("@opentui/core"));
2212
+ for (const name of ["@opentui/solid", "solid-js"]) {
2213
+ try {
2214
+ paths.push(resolvePackage(name));
2215
+ } catch {}
2216
+ }
2217
+ for (const name of [`@opentui/core-linux-${process.arch}`, `@opentui/core-linux-${process.arch}-musl`]) {
2218
+ try {
2219
+ paths.push(resolvePackage(name));
2220
+ } catch {}
2221
+ }
2222
+ if (process.env.OTUI_ASSET_ROOT)
2223
+ paths.push(process.env.OTUI_ASSET_ROOT);
2224
+ for (const name of [...commonTools, ...candidateTools, ...tools]) {
2225
+ const path = executable(name);
2226
+ if (path)
2227
+ paths.push(path);
2228
+ }
2229
+ const result = [];
2230
+ for (const path of paths) {
2231
+ try {
2232
+ result.push(await realpath2(path));
2233
+ } catch (error) {
2234
+ if (error.code !== "ENOENT")
2235
+ throw error;
2236
+ }
2237
+ }
2238
+ return result;
2239
+ }
2240
+ async function usbParent(device) {
2241
+ let path;
2242
+ try {
2243
+ path = await realpath2(`/sys/class/block/${device.id}/device`);
2244
+ } catch (error) {
2245
+ if (error.code === "ENOENT")
2246
+ return null;
2247
+ throw error;
2248
+ }
2249
+ while (path.startsWith("/sys/")) {
2250
+ try {
2251
+ await Promise.all([readFile(join3(path, "idVendor"), "utf8"), readFile(join3(path, "idProduct"), "utf8")]);
2252
+ return path;
2253
+ } catch (error) {
2254
+ if (error.code !== "ENOENT")
2255
+ throw error;
2256
+ }
2257
+ const next = dirname2(path);
2258
+ if (next === path)
2259
+ break;
2260
+ path = next;
2261
+ }
2262
+ return null;
2263
+ }
2264
+ async function usbParentMap(inventory) {
2265
+ const result = new Map;
2266
+ for (const device of inventory.devices.values()) {
2267
+ if (inventory.parents.get(device.majorMinor)?.size || device.type === "part")
2268
+ continue;
2269
+ const parent = await usbParent(device);
2270
+ if (parent)
2271
+ result.set(device.majorMinor, parent);
2272
+ }
2273
+ return result;
2274
+ }
2275
+ async function sourceIdentity(source, signal, optional = false) {
2276
+ const suffix = source.indexOf("[");
2277
+ const path = suffix < 0 ? source : source.slice(0, suffix);
2278
+ if (!path.startsWith("/dev/") || cleanText(path) !== path)
2279
+ return null;
2280
+ if (optional) {
2281
+ try {
2282
+ await lstat(path);
2283
+ } catch (error) {
2284
+ if (error.code === "ENOENT")
2285
+ return null;
2286
+ throw error;
2287
+ }
2288
+ }
2289
+ const identity = (await command(["lsblk", "--noheadings", "--raw", "--nodeps", "--output", "MAJ:MIN", "--", path], signal)).trim();
2290
+ if (!majorMinor.test(identity) || identity.includes(`
2291
+ `)) {
2292
+ throw new Error("A protected Linux block source is ambiguous. Nothing can be selected.");
2293
+ }
2294
+ return identity;
2295
+ }
2296
+ async function backingDevices(path, signal) {
2297
+ const output = await command([
2298
+ "findmnt",
2299
+ "--json",
2300
+ "--canonicalize",
2301
+ "--first-only",
2302
+ "--target",
2303
+ path,
2304
+ "--output",
2305
+ `MAJ:MIN,${findmntSourceColumn}`
2306
+ ], signal, { limit: 1024 ** 2 });
2307
+ let parsed;
2308
+ try {
2309
+ parsed = JSON.parse(output);
2310
+ } catch {
2311
+ throw new Error("Cannot parse protected Linux storage. Nothing can be selected.");
2312
+ }
2313
+ if (!exactObject(parsed) || !Array.isArray(parsed.filesystems) || parsed.filesystems.length !== 1 || !exactObject(parsed.filesystems[0]) || !majorMinor.test(String(parsed.filesystems[0]["maj:min"])) || !(parsed.filesystems[0].source === null || typeof parsed.filesystems[0].source === "string")) {
2314
+ throw new Error("Cannot resolve a protected Linux path to storage. Nothing can be selected.");
2315
+ }
2316
+ const result = new Set;
2317
+ const mounted = String(parsed.filesystems[0]["maj:min"]);
2318
+ if (!mounted.startsWith("0:"))
2319
+ result.add(mounted);
2320
+ if (typeof parsed.filesystems[0].source === "string") {
2321
+ const identity = await sourceIdentity(parsed.filesystems[0].source, signal);
2322
+ if (identity)
2323
+ result.add(identity);
2324
+ }
2325
+ if (!result.size)
2326
+ throw new Error("A protected Linux path has unsupported non-block backing storage. Nothing can be selected.");
2327
+ return [...result];
2328
+ }
2329
+ async function fstabDevices(inventory, signal) {
2330
+ let fstab;
2331
+ try {
2332
+ fstab = await readFile("/etc/fstab", "utf8");
2333
+ } catch (error) {
2334
+ if (error.code === "ENOENT")
2335
+ return [];
2336
+ throw error;
2337
+ }
2338
+ if (!fstab.split(`
2339
+ `).some((line) => line.trim() && !line.trim().startsWith("#")))
2340
+ return [];
2341
+ const output = await command(["findmnt", "--fstab", "--json", "--output", findmntSourceColumn], signal, { limit: 1024 ** 2 });
2342
+ const sources = parseConfiguredSources(output);
2343
+ const direct = (await Promise.all(sources.map((source) => sourceIdentity(source, signal, true)))).filter((value) => value !== null);
2344
+ return [...configuredTagDevices(inventory, sources), ...direct];
2345
+ }
2346
+ function parseConfiguredSources(output) {
2347
+ let parsed;
2348
+ try {
2349
+ parsed = JSON.parse(output);
2350
+ } catch {
2351
+ throw new Error("Cannot parse configured Linux storage. Nothing can be selected.");
2352
+ }
2353
+ if (!exactObject(parsed) || !Array.isArray(parsed.filesystems)) {
2354
+ throw new Error("Cannot resolve configured Linux storage. Nothing can be selected.");
2355
+ }
2356
+ const sources = [];
2357
+ for (const filesystem of parsed.filesystems) {
2358
+ if (!exactObject(filesystem) || !(filesystem.source === null || typeof filesystem.source === "string")) {
2359
+ throw new Error("Configured Linux storage is invalid. Nothing can be selected.");
2360
+ }
2361
+ if (typeof filesystem.source === "string")
2362
+ sources.push(filesystem.source);
2363
+ }
2364
+ return sources;
2365
+ }
2366
+ function configuredTagDevices(topology2, sources) {
2367
+ const result = [];
2368
+ for (const source of sources) {
2369
+ const match = /^(UUID|LABEL|PARTUUID|PARTLABEL)=(.+)$/.exec(source);
2370
+ if (!match)
2371
+ continue;
2372
+ const field = { UUID: "uuid", LABEL: "label", PARTUUID: "partuuid", PARTLABEL: "partlabel" }[match[1]];
2373
+ for (const device of topology2.devices.values())
2374
+ if (device[field] === match[2])
2375
+ result.push(device.majorMinor);
2376
+ }
2377
+ return result;
2378
+ }
2379
+ function properties(input) {
2380
+ const result = new Map;
2381
+ for (const line of input.trim().split(`
2382
+ `)) {
2383
+ const split = line.indexOf("=");
2384
+ if (split < 1)
2385
+ throw new Error("Linux device identity is invalid. Nothing can be selected.");
2386
+ const key = line.slice(0, split);
2387
+ const value = line.slice(split + 1);
2388
+ if (result.has(key))
2389
+ throw new Error("Linux device identity contains duplicate properties. Nothing can be selected.");
2390
+ result.set(key, value);
2391
+ }
2392
+ return result;
2393
+ }
2394
+ async function identity(device, signal) {
2395
+ const info = properties(await command(["udevadm", "info", "--query=property", `--name=${device.path}`], signal));
2396
+ const stat = await lstat(device.path);
2397
+ const [major, minor] = device.majorMinor.split(":");
2398
+ if (!stat.isBlockDevice() || info.get("DEVNAME") !== device.path || info.get("DEVTYPE") !== "disk" || info.get("DEVPATH")?.startsWith("/devices/") !== true || info.get("MAJOR") !== major || info.get("MINOR") !== minor) {
2399
+ throw new Error("Linux USB identity does not match its block device. Nothing can be selected.");
2400
+ }
2401
+ const links = (info.get("DEVLINKS") ?? "").split(" ").filter((path) => path.startsWith("/dev/disk/by-id/") && !/-part[0-9]+$/.test(path) && cleanText(path) === path).sort();
2402
+ const stable = [];
2403
+ for (const link of links) {
2404
+ try {
2405
+ if ((await lstat(link)).isSymbolicLink() && await realpath2(link) === device.path)
2406
+ stable.push(link);
2407
+ } catch (error) {
2408
+ if (error.code !== "ENOENT")
2409
+ throw error;
2410
+ }
2411
+ }
2412
+ if (!stable.length)
2413
+ return null;
2414
+ await command(["udisksctl", "info", "--block-device", stable[0]], signal, { limit: 1024 ** 2 });
2415
+ return { device: stable[0], identity: JSON.stringify([
2416
+ device.majorMinor,
2417
+ device.size,
2418
+ info.get("DEVPATH"),
2419
+ stat.rdev,
2420
+ stat.ino,
2421
+ stat.ctimeMs,
2422
+ stable[0],
2423
+ info.get("ID_PATH") ?? "",
2424
+ info.get("ID_SERIAL") ?? "",
2425
+ device.model
2426
+ ]) };
2427
+ }
2428
+ async function disks2(signal, source, staging = tmpdir2(), unmounted) {
2429
+ await command(["udisksctl", "status"], signal, { limit: 1024 ** 2 });
2430
+ const inventory = await topology(signal);
2431
+ const usbParents = await usbParentMap(inventory);
2432
+ const roots = [...new Set(await protectedPaths(source, staging))];
2433
+ const protectedDevices = [
2434
+ ...(await Promise.all(roots.map((path) => backingDevices(path, signal)))).flat(),
2435
+ ...await fstabDevices(inventory, signal)
2436
+ ];
2437
+ const eligible = eligibleDevices(inventory, protectedDevices, unmounted, usbParents);
2438
+ const result = [];
2439
+ for (const device of eligible) {
2440
+ const attached = await identity(device, signal);
2441
+ if (attached)
2442
+ result.push({ id: device.id, name: device.model, bytes: device.size, ...attached });
2443
+ }
2444
+ return result;
2445
+ }
2446
+ function dependencies2(includeGitHub = false) {
2447
+ const required = [...commonTools, ...tools, ...includeGitHub ? candidateTools : []];
2448
+ return {
2449
+ missing: missing(required),
2450
+ installer: null,
2451
+ guidance: "Install the missing commands with your Linux distribution's package manager, then refresh."
2452
+ };
2453
+ }
2454
+ function required(name) {
2455
+ const path = executable(name);
2456
+ if (!path)
2457
+ throw new Error(`Missing ${name}. Install it with your Linux distribution's package manager, then refresh.`);
2458
+ return path;
2459
+ }
2460
+ async function currentTarget(disk, signal) {
2461
+ const inventory = await topology(signal);
2462
+ const matches = [...inventory.devices.values()].filter((device) => device.id === disk.id);
2463
+ if (matches.length !== 1)
2464
+ throw new Error("The selected Linux USB is no longer uniquely attached.");
2465
+ const target = matches[0];
2466
+ if (!eligibleDevices(inventory, [], undefined, await usbParentMap(inventory)).includes(target)) {
2467
+ throw new Error("The selected Linux USB is no longer eligible.");
2468
+ }
2469
+ return { inventory, target };
2470
+ }
2471
+ function validateDeviceRoute(disk, inspect = { symlink: (path) => lstatSync2(path).isSymbolicLink(), realpath: realpathSync2 }) {
2472
+ if (!diskID2.test(disk.id) || typeof disk.device !== "string" || !stableDevice.test(disk.device) || !inspect.symlink(disk.device) || inspect.realpath(disk.device) !== `/dev/${disk.id}`) {
2473
+ throw new Error("Invalid or changed Linux USB identity.");
2474
+ }
2475
+ return disk.device;
2476
+ }
2477
+ var platform2 = {
2478
+ id: "linux",
2479
+ label: "Linux / x86_64 UEFI",
2480
+ successTitle: "USB safely powered off",
2481
+ successDetail: "You can now remove the USB and boot your target computer.",
2482
+ authorizationDetail: "The ordinary Linux sudo prompt follows. Boo Flash never reads your password.",
2483
+ ejectDetail: "Asking Linux to flush and safely power off the USB.",
2484
+ incompleteDetail: "USB may be incomplete; not safely powered off. Use Linux's safe-removal control before removal.",
2485
+ finalAuthorization: true,
2486
+ reunmountBeforeEject: true,
2487
+ dependencies: dependencies2,
2488
+ async installDependencies() {
2489
+ throw new Error("Automatic dependency installation is not supported on Linux. Install the missing commands with your distribution's package manager.");
2490
+ },
2491
+ disks: disks2,
2492
+ async authorize(signal) {
2493
+ await command([required("sudo"), "-v"], signal, { terminal: true });
2494
+ },
2495
+ async unmount(disk, signal) {
2496
+ const { inventory, target } = await currentTarget(disk, signal);
2497
+ const descendants = [...related(target.majorMinor, inventory.children)].map((key) => inventory.devices.get(key)).filter((device) => device.mountpoints.length > 0).reverse();
2498
+ for (const device of descendants) {
2499
+ await command([required("sudo"), "-n", "--", required("umount"), "--all-targets", "--", device.path], signal);
2500
+ }
2501
+ },
2502
+ writer(disk) {
2503
+ const device = validateDeviceRoute(disk);
2504
+ return startWriter(required("sudo"), ["-n", "--", required("dd"), `of=${device}`, "bs=4M", "conv=fsync", "status=none"]);
2505
+ },
2506
+ async flush(_disk, signal) {
2507
+ await command([required("sync")], signal);
2508
+ },
2509
+ async eject(disk, signal) {
2510
+ const device = validateDeviceRoute(disk);
2511
+ await command([required("sudo"), "-n", "--", required("udisksctl"), "power-off", "--block-device", device, "--no-user-interaction"], signal);
2512
+ }
2513
+ };
2514
+
2515
+ // src/cache.ts
2516
+ import { createHash } from "crypto";
2517
+ import { constants as constants2 } from "fs";
2518
+ import { chmod, link, lstat as lstat2, mkdir, open, readdir, realpath as realpath3, unlink } from "fs/promises";
2519
+ import { tmpdir as tmpdir3 } from "os";
2520
+ import { join as join4 } from "path";
2521
+ var entryName = /^[1-9][0-9]*-[a-f0-9]{64}\.(zip|raw\.zst)$/;
2522
+ var recovery = "Clear cached images (or run --clear-cache), then retry. Unsafe paths must be corrected manually. No disk was written.";
2523
+ var code = (error) => error.code;
2524
+ var owned = (stat) => stat.uid === process.getuid?.() && (stat.mode & 63) === 0;
2525
+ async function directory() {
2526
+ const uid = process.getuid?.();
2527
+ if (uid === undefined || uid === 0)
2528
+ throw new Error("Use a normal user account for the image cache, not sudo.");
2529
+ const path = join4(await realpath3(tmpdir3()), `boo-flash-cache-${uid}`);
2530
+ try {
2531
+ await mkdir(path, { mode: 448 });
2532
+ } catch (error) {
2533
+ if (code(error) !== "EEXIST")
2534
+ throw error;
2535
+ }
2536
+ const stat = await lstat2(path);
2537
+ if (!stat.isDirectory() || !owned(stat))
2538
+ throw new Error(`Unsafe image cache directory: ${path}. ${recovery}`);
2539
+ return path;
2540
+ }
2541
+ function name(image) {
2542
+ if (!positive(image.id) || !positive(image.bytes) || image.digest.length !== 64 || !/^[a-f0-9]{64}$/.test(image.digest))
2543
+ throw new Error("Invalid cache identity.");
2544
+ return `${image.id}-${image.digest}.${image.cacheKind ?? "zip"}`;
2545
+ }
2546
+ function regular(stat) {
2547
+ if (!stat.isFile() || !owned(stat))
2548
+ throw new Error(`Unsafe cached image. ${recovery}`);
2549
+ }
2550
+ async function reuseArchive(image, snapshot, signal, progress = () => {}) {
2551
+ const filename = name(image);
2552
+ const path = join4(await directory(), filename);
2553
+ let stat;
2554
+ try {
2555
+ stat = await lstat2(path);
2556
+ } catch (error) {
2557
+ if (code(error) === "ENOENT")
2558
+ return false;
2559
+ throw error;
2560
+ }
2561
+ regular(stat);
2562
+ try {
2563
+ await link(path, snapshot);
2564
+ } catch (error) {
2565
+ if (code(error) === "ENOENT")
2566
+ return false;
2567
+ throw new Error(`Cannot pin cached image (${code(error)}). No disk was written.`);
2568
+ }
2569
+ const file = await open(snapshot, constants2.O_RDONLY | constants2.O_NOFOLLOW | constants2.O_NONBLOCK);
2570
+ try {
2571
+ const pinned = await file.stat();
2572
+ regular(pinned);
2573
+ if (pinned.dev !== stat.dev || pinned.ino !== stat.ino || pinned.size !== image.bytes) {
2574
+ throw new Error(`Cached image changed or has the wrong size. ${recovery}`);
2575
+ }
2576
+ const hash = createHash("sha256");
2577
+ const buffer = Buffer.alloc(1024 ** 2);
2578
+ let count = 0;
2579
+ progress(0);
2580
+ while (true) {
2581
+ signal.throwIfAborted();
2582
+ const { bytesRead } = await file.read(buffer);
2583
+ if (!bytesRead)
2584
+ break;
2585
+ count += bytesRead;
2586
+ if (count > image.bytes)
2587
+ throw new Error(`Cached image exceeds its API size. ${recovery}`);
2588
+ hash.update(buffer.subarray(0, bytesRead));
2589
+ progress(count);
2590
+ }
2591
+ if (count !== image.bytes || hash.digest("hex") !== image.digest)
2592
+ throw new Error(`Cached image SHA-256 or size mismatch. ${recovery}`);
2593
+ signal.throwIfAborted();
2594
+ return true;
2595
+ } finally {
2596
+ await file.close();
2597
+ }
2598
+ }
2599
+ async function publishArchive(image, verified, signal) {
2600
+ const filename = name(image);
2601
+ const path = join4(await directory(), filename);
2602
+ await chmod(verified, 256);
2603
+ try {
2604
+ await link(verified, path);
2605
+ } catch (error) {
2606
+ if (code(error) !== "EEXIST")
2607
+ throw new Error(`Cannot retain verified image in cache (${code(error)}). No disk was written.`);
2608
+ const snapshot = `${verified}.cached`;
2609
+ try {
2610
+ if (!await reuseArchive(image, snapshot, signal)) {
2611
+ throw new Error("Image cache was cleared during publication. Retry; no disk was written.");
2612
+ }
2613
+ } finally {
2614
+ try {
2615
+ await unlink(snapshot);
2616
+ } catch (error2) {
2617
+ if (code(error2) !== "ENOENT")
2618
+ throw error2;
2619
+ }
2620
+ }
2621
+ }
2622
+ }
2623
+ async function clearCachedImages(signal) {
2624
+ const path = await directory();
2625
+ let count = 0;
2626
+ for (const filename of await readdir(path)) {
2627
+ signal.throwIfAborted();
2628
+ if (entryName.exec(filename)?.[0] !== filename || !positive(Number(filename.split("-")[0])))
2629
+ continue;
2630
+ const entry = join4(path, filename);
2631
+ try {
2632
+ regular(await lstat2(entry));
2633
+ await unlink(entry);
2634
+ count++;
2635
+ } catch (error) {
2636
+ if (code(error) !== "ENOENT")
2637
+ throw error;
2638
+ }
2639
+ }
2640
+ return `Removed ${count} cached archive(s) from ${path}. USB contents and active flashes are unchanged.`;
2641
+ }
2642
+
2643
+ // src/backend.ts
2644
+ var profiles = ["intel", "amd", "nvidia"];
2645
+ var repository = "anomalyco/boo";
2646
+ var repositoryIdentities = new Set([repository, "anomalyco/ghostty-distro"]);
2647
+ var releaseOrigin = "https://images.os.boo";
2648
+ var diskID3 = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
2649
+ var sha256 = /^[a-f0-9]{64}$/;
2650
+ var semanticVersion = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/;
2651
+ var releaseVersion = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\+[1-9][0-9]*$/;
2652
+ var moduleDirectory = dirname3(fileURLToPath2(import.meta.url));
2653
+ function bytes(n) {
2654
+ return n >= 1024 ** 3 ? `${(n / 1024 ** 3).toFixed(1)} GiB` : `${(n / 1024 ** 2).toFixed(1)} MiB`;
2655
+ }
2656
+ async function prepareSafeRemoval(platform3, disk, signal, authorize2, revalidate, update) {
2657
+ if (platform3.finalAuthorization) {
2658
+ update({ stage: "Authorizing", detail: "Refreshing Linux authorization before safe power-off." });
2659
+ await authorize2();
2660
+ }
2661
+ if (platform3.reunmountBeforeEject) {
2662
+ update({ stage: "Unmounting", detail: "Removing any target volumes remounted after writing, without force." });
2663
+ await platform3.unmount(disk, signal);
2664
+ await revalidate(true);
2665
+ } else
2666
+ await revalidate();
2667
+ }
2668
+ async function discoverImages(signal) {
2669
+ const get = async (url, fresh = false) => JSON.parse(await command([
2670
+ "curl",
2671
+ "--fail",
2672
+ "--silent",
2673
+ "--show-error",
2674
+ "--proto",
2675
+ "=https",
2676
+ ...fresh ? ["--header", "Cache-Control: no-cache"] : [],
2677
+ url
2678
+ ], signal, { limit: 1024 ** 2 }));
2679
+ const channel = await get(`${releaseOrigin}/channels/main.json`, true);
2680
+ if (channel?.schema !== 1 || !releaseVersion.test(channel.version) || !/^[a-f0-9]{40}$/.test(channel.revision) || channel.prefix !== `releases/${channel.version}`) {
2681
+ throw new Error("The Boo main release channel is invalid.");
2682
+ }
2683
+ const manifest = await get(`${releaseOrigin}/${channel.prefix}/release.json`);
2684
+ const build = Number(channel.version.slice(channel.version.lastIndexOf("+") + 1));
2685
+ if (manifest?.schema !== 1 || manifest.version !== channel.version || manifest.revision !== channel.revision || !semanticVersion.test(manifest.semantic_version) || manifest.version !== `${manifest.semantic_version}+${manifest.build_number}` || manifest.build_number !== build || !positive(build) || !manifest.artifacts || Object.keys(manifest.artifacts).sort().join(",") !== [...profiles].sort().join(",")) {
2686
+ throw new Error("The Boo main release manifest is invalid.");
2687
+ }
2688
+ const images = profiles.map((profile) => {
2689
+ const artifact = manifest.artifacts[profile];
2690
+ const name2 = `boo-${channel.version}-x86_64-${profile}.raw.zst`;
2691
+ if (artifact?.image !== name2 || !positive(artifact.compressed_bytes) || !sha256.test(artifact.compressed_sha256) || !positive(artifact.raw_apparent_bytes) || !sha256.test(artifact.raw_sha256)) {
2692
+ throw new Error(`The Boo ${profile.toUpperCase()} release metadata is invalid.`);
2693
+ }
2694
+ return {
2695
+ profile,
2696
+ id: build,
2697
+ name: name2,
2698
+ bytes: artifact.compressed_bytes,
2699
+ digest: artifact.compressed_sha256,
2700
+ cacheKind: "raw.zst",
2701
+ url: `${releaseOrigin}/${channel.prefix}/${name2}`,
2702
+ rawBytes: artifact.raw_apparent_bytes,
2703
+ rawDigest: artifact.raw_sha256
2704
+ };
2705
+ });
2706
+ return { run: build, version: channel.version, revision: channel.revision, images };
2707
+ }
2708
+ async function discoverCandidates(signal) {
2709
+ const api = async (path) => JSON.parse(await command(["gh", "api", "--hostname", "github.com", `repos/${repository}/${path}`], signal));
2710
+ const result = await api("actions/workflows/candidate.yml/runs?status=success&per_page=10");
2711
+ if (!Array.isArray(result.workflow_runs))
2712
+ throw new Error("Invalid GitHub candidate workflow response.");
2713
+ const candidates = [];
2714
+ for (const run of result.workflow_runs.slice(0, 10)) {
2715
+ if (run.status !== "completed" || run.conclusion !== "success" || run.path !== ".github/workflows/candidate.yml" || !positive(run.id) || !positive(run.run_number) || !Number.isFinite(Date.parse(run.created_at)))
2716
+ continue;
2717
+ const batch = await api(`actions/runs/${run.id}/artifacts?per_page=100`);
2718
+ if (!Array.isArray(batch.artifacts))
2719
+ throw new Error("Invalid GitHub artifact response.");
2720
+ for (const a of batch.artifacts) {
2721
+ const match = /^boo-(intel|amd|nvidia)-candidate-([a-f0-9]{40})$/.exec(a.name);
2722
+ if (!match || a.expired !== false || !(Date.parse(a.expires_at) > Date.now()) || !positive(a.id) || !positive(a.size_in_bytes) || !/^sha256:[a-f0-9]{64}$/.test(a.digest) || batch.artifacts.filter((other) => other.name === a.name).length !== 1)
2723
+ continue;
2724
+ candidates.push({
2725
+ run: run.id,
2726
+ revision: match[2],
2727
+ images: [{
2728
+ profile: match[1],
2729
+ id: a.id,
2730
+ name: a.name,
2731
+ bytes: a.size_in_bytes,
2732
+ digest: a.digest.slice(7)
2733
+ }],
2734
+ candidate: { number: run.run_number, date: new Date(run.created_at).toISOString().slice(0, 10), title: cleanText(run.display_title ?? "") }
2735
+ });
2736
+ if (candidates.length === 10)
2737
+ return candidates;
2738
+ }
2739
+ }
2740
+ return candidates;
2741
+ }
2742
+
2743
+ class FlashBackend {
2744
+ demo;
2745
+ demoInstalled = false;
2746
+ constructor(demo) {
2747
+ this.demo = demo;
2748
+ }
2749
+ requirePlatform() {
2750
+ if (process.platform === "darwin")
2751
+ return platform;
2752
+ if (process.platform === "linux")
2753
+ return platform2;
2754
+ throw new Error("Boo Flash supports macOS and Linux. Use --demo for a non-writing preview.");
2755
+ }
2756
+ get platformLabel() {
2757
+ return this.demo ? "DEMO - no disks written" : this.requirePlatform().label;
2758
+ }
2759
+ get successTitle() {
2760
+ return this.demo ? "Demo complete" : this.requirePlatform().successTitle;
2761
+ }
2762
+ get successDetail() {
2763
+ return this.demo ? "Simulated success only. No USB was written or ejected." : this.requirePlatform().successDetail;
2764
+ }
2765
+ async clearCache(signal) {
2766
+ if (this.demo)
2767
+ return "Demo only: no cached images or USB contents were changed.";
2768
+ return clearCachedImages(signal);
2769
+ }
2770
+ dependencies(includeGitHub = false) {
2771
+ if (!this.demo)
2772
+ return this.requirePlatform().dependencies(includeGitHub);
2773
+ const setup = this.demo.startsWith("setup") || this.demo === "no-homebrew";
2774
+ return {
2775
+ missing: !setup || this.demoInstalled ? [] : this.demo === "setup-gh" ? includeGitHub ? ["gh"] : [] : this.demo === "setup-zstd" ? ["zstd"] : includeGitHub ? ["gh", "zstd"] : ["zstd"],
2776
+ installer: this.demo === "no-homebrew" ? null : "Homebrew",
2777
+ guidance: "Demo dependency guidance."
2778
+ };
2779
+ }
2780
+ async installDependencies(approved, signal, terminal) {
2781
+ if (!this.demo) {
2782
+ return terminal(() => this.requirePlatform().installDependencies(approved, signal));
2783
+ }
2784
+ await delay(2000, undefined, { signal });
2785
+ return terminal(async () => {
2786
+ process.stdout.write(`
2787
+ DEMO dependency setup: no software is installed.
2788
+ Simulating Homebrew terminal handoff. Ctrl-C cancels and quits.
2789
+ `);
2790
+ await delay(3000);
2791
+ if (this.demo === "setup-interrupted")
2792
+ process.stdout.write(`Simulated Homebrew interruption result, without signaling Boo Flash. Quitting.
2793
+ `);
2794
+ if (signal.aborted || this.demo === "setup-interrupted")
2795
+ return "cancelled";
2796
+ if (this.demo === "setup-error")
2797
+ throw new Error("Simulated Homebrew failure. No software was installed. Resolve the reported issue, then refresh to retry.");
2798
+ this.demoInstalled = true;
2799
+ return "installed";
2800
+ });
2801
+ }
2802
+ async catalog(signal) {
2803
+ if (!this.demo)
2804
+ return discoverImages(signal);
2805
+ await delay(350, undefined, { signal });
2806
+ return { run: 149, version: "0.1.0+149", revision: "9b7574859120add5a1a478bc7923aeb1629be4ba", images: profiles.map((profile, index) => ({
2807
+ profile,
2808
+ id: index,
2809
+ name: `demo-${profile}`,
2810
+ bytes: 1083197973,
2811
+ digest: "demo"
2812
+ })) };
2813
+ }
2814
+ async candidates(signal) {
2815
+ if (!this.demo)
2816
+ return discoverCandidates(signal);
2817
+ const catalog = await this.catalog(signal);
2818
+ return this.demo === "empty" ? [] : [{
2819
+ ...catalog,
2820
+ images: [catalog.images[0]],
2821
+ candidate: { number: 1, date: "2026-08-28", title: "Simulated candidate" }
2822
+ }];
2823
+ }
2824
+ async disks(signal, staging = tmpdir4(), unmounted) {
2825
+ if (this.demo) {
2826
+ await delay(250, undefined, { signal });
2827
+ return this.demo === "empty" ? [] : [
2828
+ { id: "demo-usb-a", name: "SanDisk Ultra USB 3.0", bytes: 32 * 1024 ** 3, identity: "demo-a" },
2829
+ { id: "demo-usb-b", name: "Kingston DataTraveler", bytes: 64 * 1024 ** 3, identity: "demo-b" }
2830
+ ];
2831
+ }
2832
+ if (process.getuid?.() === 0)
2833
+ throw new Error(`Run Boo Flash on ${this.requirePlatform().id === "macos" ? "macOS" : "Linux"} as your normal user, not with sudo.`);
2834
+ return this.requirePlatform().disks(signal, moduleDirectory, staging, unmounted);
2835
+ }
2836
+ async flash(catalog, image, disk, signal, update, authorize2) {
2837
+ if (this.demo) {
2838
+ if (!disk.id.startsWith("demo-") || image.digest !== "demo")
2839
+ throw new Error("Invalid demo selection.");
2840
+ for (const stage of ["Downloading", "Verifying", "Unmounting", "Writing", "Flushing", "Ejecting"]) {
2841
+ const total = stage === "Downloading" ? image.bytes : stage === "Writing" ? 8 * 1024 ** 3 : undefined;
2842
+ for (let i = 0;i <= (total ? 30 : 5); i++) {
2843
+ signal.throwIfAborted();
2844
+ update({
2845
+ stage,
2846
+ detail: "Simulated activity. No disk commands are run.",
2847
+ total,
2848
+ bytes: total ? Math.round(total * i / 30) : undefined,
2849
+ speed: total ? total / 3 : undefined
2850
+ });
2851
+ await delay(100, undefined, { signal });
2852
+ if (this.demo === "error" && stage === "Writing" && i === 13)
2853
+ throw new Error("Demo write failure. No disks were written or ejected. Refresh to choose again.");
2854
+ }
2855
+ }
2856
+ return;
2857
+ }
2858
+ const platform3 = this.requirePlatform();
2859
+ if (process.getuid?.() === 0)
2860
+ throw new Error(`Run Boo Flash on ${platform3.id === "macos" ? "macOS" : "Linux"} as your normal user, not with sudo.`);
2861
+ if (!diskID3.test(disk.id) || !catalog.images.includes(image) || !profiles.includes(image.profile) || !positive(image.id) || !positive(image.bytes) || !sha256.test(image.digest))
2862
+ throw new Error("Invalid flash selection.");
2863
+ if (catalog.candidate) {
2864
+ if (!positive(catalog.run) || !/^[a-f0-9]{40}$/.test(catalog.revision) || image.cacheKind || image.name !== `boo-${image.profile}-candidate-${catalog.revision}`)
2865
+ throw new Error("Invalid candidate selection.");
2866
+ const artifact = JSON.parse(await command(["gh", "api", "--hostname", "github.com", `repos/${repository}/actions/artifacts/${image.id}`], signal));
2867
+ if (artifact.id !== image.id || artifact.name !== image.name || artifact.workflow_run?.id !== catalog.run || artifact.expired !== false || !(Date.parse(artifact.expires_at) > Date.now()) || artifact.size_in_bytes !== image.bytes || artifact.digest !== `sha256:${image.digest}`) {
2868
+ throw new Error("Selected candidate artifact changed or expired. Refresh Candidates. No disk was written.");
2869
+ }
2870
+ } else {
2871
+ const build = Number(catalog.version?.slice(catalog.version.lastIndexOf("+") + 1));
2872
+ const name2 = `boo-${catalog.version}-x86_64-${image.profile}.raw.zst`;
2873
+ if (!releaseVersion.test(catalog.version ?? "") || !positive(build) || catalog.run !== build || image.id !== build || !/^[a-f0-9]{40}$/.test(catalog.revision) || image.name !== name2 || image.cacheKind !== "raw.zst" || image.url !== `${releaseOrigin}/releases/${catalog.version}/${name2}` || !positive(image.rawBytes) || !sha256.test(image.rawDigest ?? ""))
2874
+ throw new Error("Invalid Boo release selection.");
2875
+ }
2876
+ const staging = await mkdtemp(join5(tmpdir4(), "boo-flash-"));
2877
+ const payload = join5(staging, catalog.candidate ? "image.zip" : "image.raw.zst");
2878
+ let unretained = false;
2879
+ let destructive = false;
2880
+ try {
2881
+ await chmod2(staging, 448);
2882
+ let count = 0;
2883
+ if (!await reuseArchive(image, payload, signal, (count2) => update({
2884
+ stage: "Using cached image",
2885
+ detail: `Verifying cached ${catalog.candidate ? "GitHub ZIP" : `Boo ${catalog.version} image`} size and SHA-256; no download.`,
2886
+ bytes: count2,
2887
+ total: image.bytes
2888
+ }))) {
2889
+ const payloadFile = await open2(payload, "wx", 384);
2890
+ const payloadHash = createHash2("sha256");
2891
+ const started = performance.now();
2892
+ const detail = catalog.candidate ? "Selected candidate artifact only; authenticated with GitHub." : `Boo ${catalog.version} from images.os.boo.`;
2893
+ update({ stage: "Downloading", detail, bytes: 0, total: image.bytes });
2894
+ try {
2895
+ const argv = catalog.candidate ? ["gh", "api", "--hostname", "github.com", `repos/${repository}/actions/artifacts/${image.id}/zip`] : ["curl", "--fail", "--silent", "--show-error", "--proto", "=https", image.url];
2896
+ await command(argv, signal, {
2897
+ limit: image.bytes,
2898
+ consume: async (chunk) => {
2899
+ payloadHash.update(chunk);
2900
+ await payloadFile.writeFile(chunk);
2901
+ count += chunk.length;
2902
+ update({
2903
+ stage: "Downloading",
2904
+ detail,
2905
+ bytes: count,
2906
+ total: image.bytes,
2907
+ speed: count / Math.max(0.001, (performance.now() - started) / 1000)
2908
+ });
2909
+ }
2910
+ });
2911
+ } finally {
2912
+ await payloadFile.close();
2913
+ }
2914
+ if (count !== image.bytes || payloadHash.digest("hex") !== image.digest) {
2915
+ throw new Error(`${catalog.candidate ? "GitHub ZIP" : "Release image"} size or SHA-256 mismatch. No disk was written.`);
2916
+ }
2917
+ unretained = true;
2918
+ await publishArchive(image, payload, signal);
2919
+ unretained = false;
2920
+ }
2921
+ signal.throwIfAborted();
2922
+ let compressed = payload;
2923
+ let rawBytes = image.rawBytes;
2924
+ let rawDigest = image.rawDigest;
2925
+ if (catalog.candidate) {
2926
+ update({ stage: "Verifying", detail: "Checking GitHub ZIP digest, build identity and image checksums." });
2927
+ const names = (await command(["unzip", "-Z1", payload], signal)).trim().split(`
2928
+ `);
2929
+ for (const name2 of ["build-info.json", "SHA256SUMS", "SHA256SUMS.raw"]) {
2930
+ if (names.filter((n) => n === name2).length !== 1)
2931
+ throw new Error(`Archive is missing a unique ${name2}.`);
2932
+ }
2933
+ const extractText = (name2) => command(["unzip", "-p", payload, name2], signal, { limit: 1024 ** 2 });
2934
+ const metadata = JSON.parse(await extractText("build-info.json"));
2935
+ const basename2 = `boo-${image.profile}-x86_64.raw.zst`;
2936
+ if (metadata.semantic_version !== undefined && (!semanticVersion.test(metadata.semantic_version) || metadata.build_number !== null || metadata.version !== metadata.semantic_version)) {
2937
+ throw new Error("Build metadata has an invalid candidate image version.");
2938
+ }
2939
+ if (metadata.image !== basename2 || names.filter((name2) => name2 === basename2).length !== 1 || metadata.schema !== 1 || !repositoryIdentities.has(metadata.repository) || metadata.revision !== catalog.revision || metadata.variant !== image.profile || metadata.source_state !== "clean" || !positive(metadata.compressed_bytes) || metadata.compressed_bytes > image.bytes || !positive(metadata.raw_apparent_bytes) || !sha256.test(metadata.compressed_sha256) || !sha256.test(metadata.raw_sha256)) {
2940
+ throw new Error("Build metadata does not match the selected candidate.");
2941
+ }
2942
+ if (metadata.candidate?.resolved_sha !== catalog.revision || metadata.candidate?.profile !== image.profile || metadata.candidate?.qualification !== "build-only" || typeof metadata.candidate?.source_ref !== "string" || !metadata.candidate.source_ref.trim()) {
2943
+ throw new Error("Candidate source metadata does not match the selected artifact.");
2944
+ }
2945
+ if ((await extractText("SHA256SUMS")).trim() !== `${metadata.compressed_sha256} ${basename2}` || (await extractText("SHA256SUMS.raw")).trim() !== `${metadata.raw_sha256} ${basename2.slice(0, -4)}`) {
2946
+ throw new Error("Image checksum manifests disagree with build metadata.");
2947
+ }
2948
+ compressed = join5(staging, basename2);
2949
+ const file = await open2(compressed, "wx", 384);
2950
+ const hash = createHash2("sha256");
2951
+ count = 0;
2952
+ try {
2953
+ await command(["unzip", "-p", payload, basename2], signal, {
2954
+ limit: metadata.compressed_bytes,
2955
+ consume: async (chunk) => {
2956
+ hash.update(chunk);
2957
+ await file.writeFile(chunk);
2958
+ count += chunk.length;
2959
+ update({ stage: "Verifying", detail: "Extracting only the selected image; checking compressed SHA-256.", bytes: count, total: metadata.compressed_bytes });
2960
+ }
2961
+ });
2962
+ } finally {
2963
+ await file.close();
2964
+ }
2965
+ if (count !== metadata.compressed_bytes || hash.digest("hex") !== metadata.compressed_sha256)
2966
+ throw new Error("Compressed image size or SHA-256 mismatch.");
2967
+ rawBytes = metadata.raw_apparent_bytes;
2968
+ rawDigest = metadata.raw_sha256;
2969
+ } else {
2970
+ update({ stage: "Verifying", detail: `Boo ${catalog.version} release metadata and compressed SHA-256 verified.` });
2971
+ }
2972
+ if (!positive(rawBytes) || !sha256.test(rawDigest ?? ""))
2973
+ throw new Error("Image raw-stream metadata is invalid.");
2974
+ if (rawBytes > disk.bytes)
2975
+ throw new Error(`Image needs ${bytes(rawBytes)}; the selected USB is too small.`);
2976
+ const revalidate = async (unmounted = false) => {
2977
+ const current = (await this.disks(signal, staging, unmounted ? disk.id : undefined)).find((d) => d.id === disk.id);
2978
+ if (!current || current.identity !== disk.identity || current.bytes < rawBytes) {
2979
+ throw new Error("USB identity, capacity or eligibility changed. Go back and select it again.");
2980
+ }
2981
+ };
2982
+ update({ stage: "Authorizing", detail: platform3.authorizationDetail });
2983
+ await authorize2();
2984
+ await revalidate();
2985
+ update({ stage: "Unmounting", detail: "Unmounting every volume on the USB, without force." });
2986
+ await platform3.unmount(disk, signal);
2987
+ await revalidate(true);
2988
+ signal.throwIfAborted();
2989
+ update({ stage: "Writing", detail: "Opening the raw USB device.", bytes: 0, total: rawBytes });
2990
+ const writer = platform3.writer(disk);
2991
+ destructive = true;
2992
+ const stopWriter = writer.stop;
2993
+ signal.addEventListener("abort", stopWriter, { once: true });
2994
+ const rawHash = createHash2("sha256");
2995
+ let written = 0;
2996
+ const writeStarted = performance.now();
2997
+ try {
2998
+ await command(["zstd", "-dc", "--", compressed], signal, {
2999
+ limit: rawBytes,
3000
+ consume: async (chunk) => {
3001
+ await new Promise((resolve, reject) => writer.stdin.write(chunk, (error) => error ? reject(error) : resolve()));
3002
+ rawHash.update(chunk);
3003
+ written += chunk.length;
3004
+ update({
3005
+ stage: "Writing",
3006
+ detail: "Raw bytes delivered to dd; device flush follows.",
3007
+ bytes: written,
3008
+ total: rawBytes,
3009
+ speed: written / Math.max(0.001, (performance.now() - writeStarted) / 1000)
3010
+ });
3011
+ }
3012
+ });
3013
+ writer.stdin.end();
3014
+ update({ stage: "Flushing", detail: "Waiting for dd to close the raw device." });
3015
+ if (await writer.done !== 0)
3016
+ throw new Error(`USB write failed. ${cleanText(writer.error())}`);
3017
+ signal.throwIfAborted();
3018
+ if (written !== rawBytes || rawHash.digest("hex") !== rawDigest)
3019
+ throw new Error("Raw stream length or SHA-256 mismatch. The USB is incomplete.");
3020
+ } finally {
3021
+ signal.removeEventListener("abort", stopWriter);
3022
+ writer.stdin.destroy();
3023
+ await writer.done;
3024
+ }
3025
+ update({ stage: "Flushing", detail: "Raw stream verified. Synchronizing pending writes (not a device read-back)." });
3026
+ await platform3.flush(disk, signal);
3027
+ await prepareSafeRemoval(platform3, disk, signal, authorize2, revalidate, update);
3028
+ update({ stage: "Ejecting", detail: platform3.ejectDetail });
3029
+ await platform3.eject(disk, signal);
3030
+ signal.throwIfAborted();
3031
+ } catch (error) {
3032
+ const retained = unretained ? ` Verified download retained for manual recovery (not cached): ${payload.replace(/[\x00-\x1f\x7f-\x9f]/g, " ")}` : "";
3033
+ if (signal.aborted)
3034
+ throw new Error((destructive ? `Cancelled. The writer has stopped; ${platform3.incompleteDetail}` : "Cancelled. No image was written; no safe-removal claim is made.") + retained);
3035
+ throw new Error(`${cleanText(error instanceof Error ? error.message : error)}${destructive ? ` ${platform3.incompleteDetail}` : ""}${retained}`);
3036
+ } finally {
3037
+ if (!unretained)
3038
+ await rm(staging, { recursive: true, force: true });
3039
+ }
3040
+ }
3041
+ async authorize(signal) {
3042
+ if (this.demo)
3043
+ return;
3044
+ if (process.getuid?.() === 0)
3045
+ throw new Error("Run Boo Flash as your normal user, not with sudo.");
3046
+ await this.requirePlatform().authorize(signal);
3047
+ }
3048
+ }
3049
+
3050
+ // node_modules/@opentui/solid/jsx-runtime.js
3051
+ function normalizeProps(props) {
3052
+ if (!props) {
3053
+ return {};
3054
+ }
3055
+ if (!("key" in props)) {
3056
+ return props;
3057
+ }
3058
+ const { key: _key, ...rest } = props;
3059
+ return rest;
3060
+ }
3061
+ function createIntrinsicElement(type, props) {
3062
+ const element = createElement(type);
3063
+ spread(element, props);
3064
+ return element;
3065
+ }
3066
+ function jsx(type, props = {}) {
3067
+ const normalizedProps = normalizeProps(props);
3068
+ if (typeof type === "function") {
3069
+ return createComponent2(type, normalizedProps);
3070
+ }
3071
+ return createIntrinsicElement(type, normalizedProps);
3072
+ }
3073
+ function jsxDEV(type, props = {}) {
3074
+ return jsx(type, props);
3075
+ }
3076
+ function Fragment(props) {
3077
+ return props.children ?? null;
3078
+ }
3079
+ // src/App.tsx
3080
+ function App(props) {
3081
+ const renderer = useRenderer();
3082
+ const dimensions = useTerminalDimensions();
3083
+ const [screen, setScreen] = createSignal4("profile");
3084
+ const [catalog, setCatalog] = createSignal4();
3085
+ const [candidates, setCandidates] = createSignal4([]);
3086
+ const [image, setImage] = createSignal4();
3087
+ const [disks3, setDisks] = createSignal4([]);
3088
+ const [disk, setDisk] = createSignal4();
3089
+ const [cursor, setCursor] = createSignal4(0);
3090
+ const [loading, setLoading] = createSignal4(true);
3091
+ const [stopping, setStopping] = createSignal4(false);
3092
+ const [error, setError] = createSignal4("");
3093
+ const [notice, setNotice] = createSignal4("");
3094
+ const [dependencies3, setDependencies] = createSignal4({ missing: [], installer: null, guidance: "" });
3095
+ const [setupCandidates, setSetupCandidates] = createSignal4(false);
3096
+ const [progress, setProgress] = createSignal4({ stage: "Preparing", detail: "" });
3097
+ const [frame, setFrame] = createSignal4(0);
3098
+ let controller = new AbortController;
3099
+ let pending = Promise.resolve();
3100
+ let closing = false;
3101
+ const spin = { value: 0 };
3102
+ const timeline = useTimeline({ duration: 800, loop: true });
3103
+ timeline.add(spin, { value: 4, duration: 800, onUpdate: () => setFrame(Math.floor(spin.value) % 4) });
3104
+ const spinner = () => ["|", "/", "-", "\\"][frame()];
3105
+ const busy = () => loading() || screen() === "busy";
3106
+ const compact = () => dimensions().height < 24;
3107
+ const tight = () => dimensions().height < 20;
3108
+ const visible = () => dimensions().width >= 40 && dimensions().height >= 16;
3109
+ const options = () => screen() === "profile" ? 5 : screen() === "cleared" ? 1 : screen() === "candidates" ? Math.max(1, candidates().length) : screen() === "disks" ? disks3().length : 2;
3110
+ const percent = () => progress().total ? Math.min(100, Math.floor((progress().bytes ?? 0) / progress().total * 100)) : undefined;
3111
+ const path = (d) => props.backend.demo ? d.id : `/dev/${d.id}`;
3112
+ const operation = (work) => {
3113
+ controller = new AbortController;
3114
+ setStopping(false);
3115
+ setLoading(true);
3116
+ pending = (async () => {
3117
+ try {
3118
+ await work(controller.signal);
3119
+ } catch (e) {
3120
+ if (!closing) {
3121
+ const setup = screen() === "installing";
3122
+ setError(controller.signal.aborted && setup ? "Setup cancelled. Refresh to check what is still missing." : controller.signal.aborted && props.backend.demo ? "Demo cancelled. No disks were written or ejected." : e instanceof Error ? e.message : "Operation failed. Refresh and try again.");
3123
+ if (setup)
3124
+ setDependencies(props.backend.dependencies(setupCandidates()));
3125
+ setScreen(setup ? "setup" : "error");
3126
+ setCursor(0);
3127
+ }
3128
+ } finally {
3129
+ setLoading(false);
3130
+ setStopping(false);
3131
+ }
3132
+ })();
3133
+ };
3134
+ const loadCatalog = async (signal) => {
3135
+ setSetupCandidates(false);
3136
+ const state = props.backend.dependencies();
3137
+ setDependencies(state);
3138
+ if (state.missing.length) {
3139
+ setScreen("setup");
3140
+ return;
3141
+ }
3142
+ setScreen("profile");
3143
+ setCatalog(await props.backend.catalog(signal));
3144
+ };
3145
+ const refresh = () => {
3146
+ setError("");
3147
+ setCatalog(undefined);
3148
+ setDisk(undefined);
3149
+ setScreen("profile");
3150
+ setCursor(0);
3151
+ operation(loadCatalog);
3152
+ };
3153
+ const loadCandidates = () => {
3154
+ setSetupCandidates(true);
3155
+ setCandidates([]);
3156
+ setCursor(0);
3157
+ setScreen("candidates");
3158
+ operation(async (signal) => {
3159
+ const state = props.backend.dependencies(true);
3160
+ if (state.missing.length) {
3161
+ setDependencies(state);
3162
+ setScreen("setup");
3163
+ return;
3164
+ }
3165
+ setCandidates(await props.backend.candidates(signal));
3166
+ });
3167
+ };
3168
+ const chooseUSB = () => {
3169
+ setDisk(undefined);
3170
+ setCursor(-1);
3171
+ setScreen("disks");
3172
+ operation(async (signal) => {
3173
+ const state = props.backend.dependencies();
3174
+ if (state.missing.length) {
3175
+ setDependencies(state);
3176
+ setCatalog(undefined);
3177
+ setScreen("setup");
3178
+ setCursor(0);
3179
+ return;
3180
+ }
3181
+ setDisks(await props.backend.disks(signal));
3182
+ });
3183
+ };
3184
+ const stop = async () => {
3185
+ closing = true;
3186
+ if (busy())
3187
+ setStopping(true);
3188
+ controller.abort();
3189
+ await pending;
3190
+ };
3191
+ props.registerStop(stop);
3192
+ onMount2(refresh);
3193
+ const activate = () => {
3194
+ if (busy() || !visible())
3195
+ return;
3196
+ if (screen() === "setup") {
3197
+ if (cursor() === 0) {
3198
+ setScreen("profile");
3199
+ return;
3200
+ }
3201
+ if (!dependencies3().installer) {
3202
+ refresh();
3203
+ return;
3204
+ }
3205
+ const approved = [...dependencies3().missing];
3206
+ setError("");
3207
+ setScreen("installing");
3208
+ operation(async (signal) => {
3209
+ const result = await props.backend.installDependencies(approved, signal, async (work) => {
3210
+ renderer.suspend();
3211
+ try {
3212
+ const result2 = await work();
3213
+ if (result2 === "cancelled")
3214
+ props.quit();
3215
+ return result2;
3216
+ } finally {
3217
+ if (!closing)
3218
+ renderer.resume();
3219
+ }
3220
+ });
3221
+ if (result === "cancelled")
3222
+ return;
3223
+ signal.throwIfAborted();
3224
+ setCursor(0);
3225
+ if (setupCandidates()) {
3226
+ setCandidates(await props.backend.candidates(signal));
3227
+ setScreen("candidates");
3228
+ } else
3229
+ await loadCatalog(signal);
3230
+ });
3231
+ } else if (screen() === "profile" && cursor() === 3) {
3232
+ loadCandidates();
3233
+ } else if (screen() === "profile" && cursor() === 4) {
3234
+ setScreen("clear");
3235
+ setCursor(0);
3236
+ } else if (screen() === "clear") {
3237
+ if (cursor() === 0) {
3238
+ setScreen("profile");
3239
+ setCursor(4);
3240
+ return;
3241
+ }
3242
+ operation(async (signal) => {
3243
+ setNotice(await props.backend.clearCache(signal));
3244
+ setScreen("cleared");
3245
+ setCursor(0);
3246
+ });
3247
+ } else if (screen() === "cleared") {
3248
+ setScreen("profile");
3249
+ setCursor(4);
3250
+ } else if (screen() === "candidates") {
3251
+ const candidate = candidates()[cursor()];
3252
+ if (!candidate) {
3253
+ refresh();
3254
+ return;
3255
+ }
3256
+ setCatalog(candidate);
3257
+ setImage(candidate.images[0]);
3258
+ chooseUSB();
3259
+ } else if (screen() === "profile" && !catalog()) {
3260
+ refresh();
3261
+ } else if (screen() === "profile" && catalog()) {
3262
+ setImage(catalog().images[cursor()]);
3263
+ chooseUSB();
3264
+ } else if (screen() === "disks" && disks3()[cursor()]) {
3265
+ setDisk(disks3()[cursor()]);
3266
+ setCursor(0);
3267
+ setScreen("confirm");
3268
+ } else if (screen() === "confirm") {
3269
+ if (cursor() === 0) {
3270
+ chooseUSB();
3271
+ return;
3272
+ }
3273
+ setScreen("busy");
3274
+ setProgress({ stage: "Preparing", detail: "Checking the selected image and USB." });
3275
+ operation(async (signal) => {
3276
+ await props.backend.flash(catalog(), image(), disk(), signal, setProgress, async () => {
3277
+ renderer.suspend();
3278
+ try {
3279
+ await props.backend.authorize(signal);
3280
+ } finally {
3281
+ renderer.resume();
3282
+ }
3283
+ });
3284
+ signal.throwIfAborted();
3285
+ setScreen("success");
3286
+ setCursor(0);
3287
+ });
3288
+ } else if (screen() === "error" || screen() === "success") {
3289
+ if (cursor() === 0)
3290
+ refresh();
3291
+ else
3292
+ props.quit();
3293
+ }
3294
+ };
3295
+ useKeyboard((key) => {
3296
+ if (key.eventType === "release")
3297
+ return;
3298
+ if (key.name === "q" || key.ctrl && key.name === "c") {
3299
+ props.quit();
3300
+ return;
3301
+ }
3302
+ if (key.name === "escape") {
3303
+ if (busy()) {
3304
+ setStopping(true);
3305
+ controller.abort();
3306
+ return;
3307
+ }
3308
+ if (screen() === "confirm")
3309
+ chooseUSB();
3310
+ else if (screen() === "clear" || screen() === "cleared") {
3311
+ setScreen("profile");
3312
+ setCursor(4);
3313
+ } else if (screen() === "setup") {
3314
+ setScreen("profile");
3315
+ setCursor(0);
3316
+ } else
3317
+ refresh();
3318
+ return;
3319
+ }
3320
+ if (busy() || !visible())
3321
+ return;
3322
+ if (key.name === "r") {
3323
+ screen() === "disks" ? chooseUSB() : screen() === "candidates" ? loadCandidates() : refresh();
3324
+ return;
3325
+ }
3326
+ if (["down", "right", "tab"].includes(key.name))
3327
+ setCursor((n) => Math.min(options() - 1, n + 1));
3328
+ if (["up", "left"].includes(key.name))
3329
+ setCursor((n) => Math.max(0, n - 1));
3330
+ if (key.name === "return" && !key.repeated)
3331
+ activate();
3332
+ });
3333
+ const row = (label, detail, index, danger = false) => /* @__PURE__ */ jsxDEV("box", {
3334
+ paddingX: 1,
3335
+ height: detail && !tight() ? compact() ? 2 : 3 : 1,
3336
+ flexShrink: 0,
3337
+ backgroundColor: cursor() === index ? palette.selected : palette.panel,
3338
+ children: [
3339
+ /* @__PURE__ */ jsxDEV("text", {
3340
+ fg: danger ? palette.warning : cursor() === index ? palette.accent : palette.foreground,
3341
+ attributes: TextAttributes2.BOLD,
3342
+ wrapMode: "none",
3343
+ children: [
3344
+ cursor() === index ? "> " : " ",
3345
+ label
3346
+ ]
3347
+ }, undefined, true, undefined, this),
3348
+ /* @__PURE__ */ jsxDEV(Show, {
3349
+ when: detail && !tight(),
3350
+ children: /* @__PURE__ */ jsxDEV("text", {
3351
+ fg: palette.muted,
3352
+ wrapMode: "none",
3353
+ children: [
3354
+ " ",
3355
+ detail
3356
+ ]
3357
+ }, undefined, true, undefined, this)
3358
+ }, undefined, false, undefined, this)
3359
+ ]
3360
+ }, undefined, true, undefined, this);
3361
+ return /* @__PURE__ */ jsxDEV("box", {
3362
+ width: "100%",
3363
+ height: "100%",
3364
+ backgroundColor: palette.background,
3365
+ justifyContent: "center",
3366
+ alignItems: "center",
3367
+ children: /* @__PURE__ */ jsxDEV("box", {
3368
+ width: dimensions().width <= 80 ? "100%" : 80,
3369
+ height: dimensions().height <= 24 ? "100%" : 24,
3370
+ border: true,
3371
+ borderStyle: "single",
3372
+ borderColor: palette.border,
3373
+ backgroundColor: palette.card,
3374
+ flexDirection: "column",
3375
+ paddingX: 1,
3376
+ children: [
3377
+ /* @__PURE__ */ jsxDEV("box", {
3378
+ height: 1,
3379
+ flexDirection: "row",
3380
+ justifyContent: "space-between",
3381
+ flexShrink: 0,
3382
+ children: [
3383
+ /* @__PURE__ */ jsxDEV("text", {
3384
+ fg: palette.accent,
3385
+ attributes: TextAttributes2.BOLD,
3386
+ children: "BOO FLASH"
3387
+ }, undefined, false, undefined, this),
3388
+ /* @__PURE__ */ jsxDEV("text", {
3389
+ fg: props.backend.demo ? palette.warning : palette.muted,
3390
+ children: props.backend.platformLabel
3391
+ }, undefined, false, undefined, this)
3392
+ ]
3393
+ }, undefined, true, undefined, this),
3394
+ /* @__PURE__ */ jsxDEV("box", {
3395
+ height: 1,
3396
+ backgroundColor: palette.danger,
3397
+ marginTop: tight() ? 0 : 1,
3398
+ flexShrink: 0
3399
+ }, undefined, false, undefined, this),
3400
+ /* @__PURE__ */ jsxDEV("box", {
3401
+ flexGrow: 1,
3402
+ minHeight: 0,
3403
+ paddingTop: tight() ? 0 : 1,
3404
+ gap: tight() || (compact() || dimensions().width < 56) && screen() === "setup" ? 0 : 1,
3405
+ overflow: "hidden",
3406
+ children: /* @__PURE__ */ jsxDEV(Show, {
3407
+ when: visible(),
3408
+ fallback: /* @__PURE__ */ jsxDEV("text", {
3409
+ fg: palette.warning,
3410
+ children: "Enlarge the terminal to show the full choices and erase warning. Q quits."
3411
+ }, undefined, false, undefined, this),
3412
+ children: [
3413
+ /* @__PURE__ */ jsxDEV(Show, {
3414
+ when: screen() === "profile",
3415
+ children: [
3416
+ /* @__PURE__ */ jsxDEV("text", {
3417
+ fg: palette.foreground,
3418
+ attributes: TextAttributes2.BOLD,
3419
+ children: "Choose the target computer's GPU"
3420
+ }, undefined, false, undefined, this),
3421
+ /* @__PURE__ */ jsxDEV("text", {
3422
+ fg: palette.muted,
3423
+ children: loading() ? `${spinner()} Finding latest Boo release...` : catalog() ? `Boo ${catalog().version} / ${catalog().revision.slice(0, 7)}` : "Dependencies needed. Enter to set up."
3424
+ }, undefined, false, undefined, this),
3425
+ /* @__PURE__ */ jsxDEV("box", {
3426
+ gap: 0,
3427
+ children: [
3428
+ /* @__PURE__ */ jsxDEV(For2, {
3429
+ each: profiles,
3430
+ children: (profile, index) => row(profile.toUpperCase(), profile === "nvidia" ? "NVIDIA graphics" : `${profile === "intel" ? "Intel" : "AMD Radeon"} graphics`, index())
3431
+ }, undefined, false, undefined, this),
3432
+ row("Candidates", "", 3),
3433
+ row("Clear cached images", "", 4)
3434
+ ]
3435
+ }, undefined, true, undefined, this),
3436
+ /* @__PURE__ */ jsxDEV("text", {
3437
+ fg: palette.muted,
3438
+ children: "All images support Intel and AMD x86_64 CPUs."
3439
+ }, undefined, false, undefined, this)
3440
+ ]
3441
+ }, undefined, true, undefined, this),
3442
+ /* @__PURE__ */ jsxDEV(Show, {
3443
+ when: screen() === "clear",
3444
+ children: [
3445
+ /* @__PURE__ */ jsxDEV("text", {
3446
+ fg: palette.warning,
3447
+ attributes: TextAttributes2.BOLD,
3448
+ children: "Clear cached images?"
3449
+ }, undefined, false, undefined, this),
3450
+ /* @__PURE__ */ jsxDEV("text", {
3451
+ fg: palette.foreground,
3452
+ children: "Removes downloaded archives only, NOT USB contents. Active flashes are unaffected."
3453
+ }, undefined, false, undefined, this),
3454
+ /* @__PURE__ */ jsxDEV("text", {
3455
+ fg: palette.muted,
3456
+ children: "Images will download again when needed. Pending downloads may still finish and be cached."
3457
+ }, undefined, false, undefined, this),
3458
+ /* @__PURE__ */ jsxDEV("box", {
3459
+ gap: tight() ? 0 : 1,
3460
+ children: [
3461
+ row("Go back", "", 0),
3462
+ row(loading() ? "Clearing..." : "Clear cached images", "", 1, true)
3463
+ ]
3464
+ }, undefined, true, undefined, this)
3465
+ ]
3466
+ }, undefined, true, undefined, this),
3467
+ /* @__PURE__ */ jsxDEV(Show, {
3468
+ when: screen() === "cleared",
3469
+ children: [
3470
+ /* @__PURE__ */ jsxDEV("text", {
3471
+ fg: palette.success,
3472
+ attributes: TextAttributes2.BOLD,
3473
+ children: "Cache cleared"
3474
+ }, undefined, false, undefined, this),
3475
+ /* @__PURE__ */ jsxDEV("text", {
3476
+ fg: palette.foreground,
3477
+ children: notice()
3478
+ }, undefined, false, undefined, this),
3479
+ row("Go back", "", 0)
3480
+ ]
3481
+ }, undefined, true, undefined, this),
3482
+ /* @__PURE__ */ jsxDEV(Show, {
3483
+ when: screen() === "candidates",
3484
+ children: [
3485
+ /* @__PURE__ */ jsxDEV("text", {
3486
+ fg: palette.foreground,
3487
+ attributes: TextAttributes2.BOLD,
3488
+ children: "Candidates"
3489
+ }, undefined, false, undefined, this),
3490
+ /* @__PURE__ */ jsxDEV("text", {
3491
+ fg: palette.warning,
3492
+ children: "Build-only; not boot/install/hardware qualified."
3493
+ }, undefined, false, undefined, this),
3494
+ /* @__PURE__ */ jsxDEV(Show, {
3495
+ when: !loading(),
3496
+ fallback: /* @__PURE__ */ jsxDEV("text", {
3497
+ fg: palette.muted,
3498
+ children: [
3499
+ spinner(),
3500
+ " Finding completed candidates..."
3501
+ ]
3502
+ }, undefined, true, undefined, this),
3503
+ children: /* @__PURE__ */ jsxDEV(Show, {
3504
+ when: candidates().length,
3505
+ fallback: /* @__PURE__ */ jsxDEV(Fragment, {
3506
+ children: [
3507
+ /* @__PURE__ */ jsxDEV("text", {
3508
+ fg: palette.muted,
3509
+ children: "No completed candidates available"
3510
+ }, undefined, false, undefined, this),
3511
+ row("Go back", "", 0)
3512
+ ]
3513
+ }, undefined, true, undefined, this),
3514
+ children: [
3515
+ /* @__PURE__ */ jsxDEV("box", {
3516
+ gap: 0,
3517
+ children: /* @__PURE__ */ jsxDEV(For2, {
3518
+ each: candidates().slice(Math.max(0, cursor() - 2), Math.max(3, cursor() + 1)),
3519
+ children: (c, index) => row(`${c.images[0].profile.toUpperCase()} ${c.revision.slice(0, 7)} #${c.candidate.number} ${c.candidate.date}`, c.candidate.title, index() + Math.max(0, cursor() - 2))
3520
+ }, undefined, false, undefined, this)
3521
+ }, undefined, false, undefined, this),
3522
+ /* @__PURE__ */ jsxDEV("text", {
3523
+ fg: palette.muted,
3524
+ children: [
3525
+ cursor() + 1,
3526
+ " of ",
3527
+ candidates().length,
3528
+ " candidates / Enter to choose"
3529
+ ]
3530
+ }, undefined, true, undefined, this)
3531
+ ]
3532
+ }, undefined, true, undefined, this)
3533
+ }, undefined, false, undefined, this)
3534
+ ]
3535
+ }, undefined, true, undefined, this),
3536
+ /* @__PURE__ */ jsxDEV(Show, {
3537
+ when: screen() === "setup",
3538
+ children: [
3539
+ /* @__PURE__ */ jsxDEV("text", {
3540
+ fg: palette.accent,
3541
+ attributes: TextAttributes2.BOLD,
3542
+ children: "Set up required tools"
3543
+ }, undefined, false, undefined, this),
3544
+ /* @__PURE__ */ jsxDEV("text", {
3545
+ fg: palette.foreground,
3546
+ children: [
3547
+ "Missing: ",
3548
+ dependencies3().missing.join(", ")
3549
+ ]
3550
+ }, undefined, true, undefined, this),
3551
+ /* @__PURE__ */ jsxDEV(Show, {
3552
+ when: !error(),
3553
+ fallback: /* @__PURE__ */ jsxDEV("text", {
3554
+ fg: palette.warning,
3555
+ children: error()
3556
+ }, undefined, false, undefined, this),
3557
+ children: /* @__PURE__ */ jsxDEV("text", {
3558
+ fg: palette.muted,
3559
+ children: dependencies3().installer ? `${dependencies3().installer} will install ${dependencies3().missing.join(" and ")} and required dependencies, then continue.` : dependencies3().guidance
3560
+ }, undefined, false, undefined, this)
3561
+ }, undefined, false, undefined, this),
3562
+ /* @__PURE__ */ jsxDEV(Show, {
3563
+ when: !tight(),
3564
+ children: /* @__PURE__ */ jsxDEV("text", {
3565
+ fg: palette.muted,
3566
+ children: dependencies3().installer ? "Uses the ordinary terminal. Ctrl-C cancels and quits." : "Boo Flash will not change system packages on this platform."
3567
+ }, undefined, false, undefined, this)
3568
+ }, undefined, false, undefined, this),
3569
+ /* @__PURE__ */ jsxDEV(Show, {
3570
+ when: props.backend.demo,
3571
+ children: /* @__PURE__ */ jsxDEV("text", {
3572
+ fg: palette.warning,
3573
+ children: "Setup simulation; no software installed."
3574
+ }, undefined, false, undefined, this)
3575
+ }, undefined, false, undefined, this),
3576
+ /* @__PURE__ */ jsxDEV("box", {
3577
+ gap: tight() ? 0 : 1,
3578
+ children: [
3579
+ row("Go back", "", 0),
3580
+ row(dependencies3().installer ? `Install ${dependencies3().missing.join(" + ")}` : "Refresh", "", 1)
3581
+ ]
3582
+ }, undefined, true, undefined, this)
3583
+ ]
3584
+ }, undefined, true, undefined, this),
3585
+ /* @__PURE__ */ jsxDEV(Show, {
3586
+ when: screen() === "installing",
3587
+ children: [
3588
+ /* @__PURE__ */ jsxDEV("text", {
3589
+ fg: palette.accent,
3590
+ attributes: TextAttributes2.BOLD,
3591
+ children: [
3592
+ spinner(),
3593
+ " ",
3594
+ stopping() ? "Waiting for setup to stop..." : "Installing required tools"
3595
+ ]
3596
+ }, undefined, true, undefined, this),
3597
+ /* @__PURE__ */ jsxDEV("text", {
3598
+ fg: palette.foreground,
3599
+ children: [
3600
+ dependencies3().missing.join(" + "),
3601
+ " via ",
3602
+ dependencies3().installer
3603
+ ]
3604
+ }, undefined, true, undefined, this),
3605
+ /* @__PURE__ */ jsxDEV("text", {
3606
+ fg: palette.muted,
3607
+ children: [
3608
+ dependencies3().installer,
3609
+ " uses the ordinary terminal. Ctrl-C cancels and quits; wait for cleanup."
3610
+ ]
3611
+ }, undefined, true, undefined, this),
3612
+ /* @__PURE__ */ jsxDEV(Show, {
3613
+ when: props.backend.demo,
3614
+ children: /* @__PURE__ */ jsxDEV("text", {
3615
+ fg: palette.warning,
3616
+ children: "Setup simulation; no software installed."
3617
+ }, undefined, false, undefined, this)
3618
+ }, undefined, false, undefined, this)
3619
+ ]
3620
+ }, undefined, true, undefined, this),
3621
+ /* @__PURE__ */ jsxDEV(Show, {
3622
+ when: screen() === "disks",
3623
+ children: [
3624
+ /* @__PURE__ */ jsxDEV("text", {
3625
+ fg: palette.foreground,
3626
+ attributes: TextAttributes2.BOLD,
3627
+ children: "Choose a USB drive"
3628
+ }, undefined, false, undefined, this),
3629
+ /* @__PURE__ */ jsxDEV("text", {
3630
+ fg: palette.muted,
3631
+ children: loading() ? `${spinner()} Checking disks and protected storage...` : "System, home and app storage are excluded."
3632
+ }, undefined, false, undefined, this),
3633
+ /* @__PURE__ */ jsxDEV(Show, {
3634
+ when: !loading(),
3635
+ children: /* @__PURE__ */ jsxDEV(Show, {
3636
+ when: disks3().length > 0,
3637
+ fallback: /* @__PURE__ */ jsxDEV("text", {
3638
+ fg: palette.warning,
3639
+ children: "No eligible USB drives. Connect a writable external USB drive, then press R to refresh."
3640
+ }, undefined, false, undefined, this),
3641
+ children: [
3642
+ /* @__PURE__ */ jsxDEV("box", {
3643
+ gap: 0,
3644
+ children: /* @__PURE__ */ jsxDEV(For2, {
3645
+ each: disks3().slice(Math.max(0, cursor() - 2), Math.max(3, cursor() + 1)),
3646
+ children: (d, index) => row(d.name, `${path(d)} / ${bytes(d.bytes)} / USB`, index() + Math.max(0, cursor() - 2))
3647
+ }, undefined, false, undefined, this)
3648
+ }, undefined, false, undefined, this),
3649
+ /* @__PURE__ */ jsxDEV("text", {
3650
+ fg: palette.muted,
3651
+ children: cursor() < 0 ? "Use arrows to select a drive. Nothing is selected yet." : `${cursor() + 1} of ${disks3().length} drives / Enter to review`
3652
+ }, undefined, false, undefined, this)
3653
+ ]
3654
+ }, undefined, true, undefined, this)
3655
+ }, undefined, false, undefined, this)
3656
+ ]
3657
+ }, undefined, true, undefined, this),
3658
+ /* @__PURE__ */ jsxDEV(Show, {
3659
+ when: screen() === "confirm",
3660
+ children: [
3661
+ /* @__PURE__ */ jsxDEV("text", {
3662
+ fg: palette.warning,
3663
+ attributes: TextAttributes2.BOLD,
3664
+ children: "Erase this entire USB drive?"
3665
+ }, undefined, false, undefined, this),
3666
+ /* @__PURE__ */ jsxDEV("box", {
3667
+ backgroundColor: palette.panelAlt,
3668
+ paddingX: 1,
3669
+ children: [
3670
+ /* @__PURE__ */ jsxDEV("text", {
3671
+ fg: palette.foreground,
3672
+ attributes: TextAttributes2.BOLD,
3673
+ height: 1,
3674
+ wrapMode: "none",
3675
+ children: disk()?.name
3676
+ }, undefined, false, undefined, this),
3677
+ /* @__PURE__ */ jsxDEV("text", {
3678
+ fg: palette.accent,
3679
+ children: disk() ? `${path(disk())} / ${bytes(disk().bytes)}` : ""
3680
+ }, undefined, false, undefined, this),
3681
+ /* @__PURE__ */ jsxDEV("text", {
3682
+ fg: catalog()?.candidate ? palette.warning : palette.muted,
3683
+ children: [
3684
+ image()?.profile.toUpperCase(),
3685
+ " / ",
3686
+ catalog()?.candidate ? `candidate (build-only) ${catalog()?.revision.slice(0, 7)}` : `Boo ${catalog()?.version}`
3687
+ ]
3688
+ }, undefined, true, undefined, this)
3689
+ ]
3690
+ }, undefined, true, undefined, this),
3691
+ /* @__PURE__ */ jsxDEV("text", {
3692
+ fg: palette.warning,
3693
+ children: "ALL data and partitions on this drive will be lost. Back up anything you need before continuing."
3694
+ }, undefined, false, undefined, this),
3695
+ /* @__PURE__ */ jsxDEV("box", {
3696
+ gap: tight() ? 0 : 1,
3697
+ children: [
3698
+ row("Go back", "", 0),
3699
+ row("Erase & flash", "", 1, true)
3700
+ ]
3701
+ }, undefined, true, undefined, this)
3702
+ ]
3703
+ }, undefined, true, undefined, this),
3704
+ /* @__PURE__ */ jsxDEV(Show, {
3705
+ when: screen() === "busy",
3706
+ children: [
3707
+ /* @__PURE__ */ jsxDEV("text", {
3708
+ fg: palette.accent,
3709
+ attributes: TextAttributes2.BOLD,
3710
+ children: [
3711
+ spinner(),
3712
+ " ",
3713
+ stopping() ? "Stopping safely..." : progress().stage
3714
+ ]
3715
+ }, undefined, true, undefined, this),
3716
+ /* @__PURE__ */ jsxDEV("text", {
3717
+ fg: palette.foreground,
3718
+ children: [
3719
+ disk()?.name,
3720
+ " / ",
3721
+ image()?.profile.toUpperCase()
3722
+ ]
3723
+ }, undefined, true, undefined, this),
3724
+ /* @__PURE__ */ jsxDEV("text", {
3725
+ fg: palette.muted,
3726
+ children: stopping() ? "Waiting for all child processes to stop. Keep the USB connected." : progress().detail
3727
+ }, undefined, false, undefined, this),
3728
+ /* @__PURE__ */ jsxDEV(Show, {
3729
+ when: percent() !== undefined,
3730
+ children: [
3731
+ /* @__PURE__ */ jsxDEV("box", {
3732
+ border: true,
3733
+ borderColor: palette.accent,
3734
+ height: 3,
3735
+ backgroundColor: palette.panelAlt,
3736
+ children: /* @__PURE__ */ jsxDEV(Show, {
3737
+ when: percent() > 0,
3738
+ children: /* @__PURE__ */ jsxDEV("box", {
3739
+ height: 1,
3740
+ width: `${percent() ?? 0}%`,
3741
+ backgroundColor: palette.accent
3742
+ }, undefined, false, undefined, this)
3743
+ }, undefined, false, undefined, this)
3744
+ }, undefined, false, undefined, this),
3745
+ /* @__PURE__ */ jsxDEV("text", {
3746
+ fg: palette.foreground,
3747
+ attributes: TextAttributes2.BOLD,
3748
+ children: [
3749
+ percent(),
3750
+ "% ",
3751
+ bytes(progress().bytes ?? 0),
3752
+ " / ",
3753
+ bytes(progress().total ?? 0)
3754
+ ]
3755
+ }, undefined, true, undefined, this),
3756
+ /* @__PURE__ */ jsxDEV("text", {
3757
+ fg: palette.muted,
3758
+ children: progress().speed ? `${bytes(progress().speed)} / s` : ""
3759
+ }, undefined, false, undefined, this)
3760
+ ]
3761
+ }, undefined, true, undefined, this),
3762
+ /* @__PURE__ */ jsxDEV("text", {
3763
+ fg: palette.warning,
3764
+ children: "Keep the USB connected until safe removal succeeds."
3765
+ }, undefined, false, undefined, this)
3766
+ ]
3767
+ }, undefined, true, undefined, this),
3768
+ /* @__PURE__ */ jsxDEV(Show, {
3769
+ when: screen() === "success",
3770
+ children: [
3771
+ /* @__PURE__ */ jsxDEV("text", {
3772
+ fg: palette.success,
3773
+ attributes: TextAttributes2.BOLD,
3774
+ children: props.backend.successTitle
3775
+ }, undefined, false, undefined, this),
3776
+ /* @__PURE__ */ jsxDEV("text", {
3777
+ fg: palette.foreground,
3778
+ children: props.backend.successDetail
3779
+ }, undefined, false, undefined, this),
3780
+ /* @__PURE__ */ jsxDEV("text", {
3781
+ fg: palette.muted,
3782
+ children: props.backend.demo ? "The real flow checks the write, flush and safe removal." : catalog()?.candidate ? "GitHub ZIP, compressed image and raw stream verified. Not a device read-back." : "Compressed image and raw stream verified. Not a device read-back."
3783
+ }, undefined, false, undefined, this),
3784
+ /* @__PURE__ */ jsxDEV("box", {
3785
+ gap: 1,
3786
+ children: [
3787
+ row("Choose another image", "", 0),
3788
+ row("Quit", "", 1)
3789
+ ]
3790
+ }, undefined, true, undefined, this)
3791
+ ]
3792
+ }, undefined, true, undefined, this),
3793
+ /* @__PURE__ */ jsxDEV(Show, {
3794
+ when: screen() === "error",
3795
+ children: [
3796
+ /* @__PURE__ */ jsxDEV("text", {
3797
+ fg: palette.danger,
3798
+ attributes: TextAttributes2.BOLD,
3799
+ children: "Flash not completed"
3800
+ }, undefined, false, undefined, this),
3801
+ /* @__PURE__ */ jsxDEV("text", {
3802
+ fg: palette.foreground,
3803
+ children: error()
3804
+ }, undefined, false, undefined, this),
3805
+ /* @__PURE__ */ jsxDEV("box", {
3806
+ gap: 1,
3807
+ children: [
3808
+ row("Refresh & try again", "", 0),
3809
+ row("Quit", "", 1)
3810
+ ]
3811
+ }, undefined, true, undefined, this)
3812
+ ]
3813
+ }, undefined, true, undefined, this)
3814
+ ]
3815
+ }, undefined, true, undefined, this)
3816
+ }, undefined, false, undefined, this),
3817
+ /* @__PURE__ */ jsxDEV("text", {
3818
+ height: 1,
3819
+ flexShrink: 0,
3820
+ fg: palette.muted,
3821
+ wrapMode: "none",
3822
+ children: busy() ? "Esc cancel / Q stop & quit" : dimensions().width < 56 ? "Arrows / Enter / Esc / R / Q quit" : "Arrows / Enter / Esc back / R refresh / Q quit"
3823
+ }, undefined, false, undefined, this)
3824
+ ]
3825
+ }, undefined, true, undefined, this)
3826
+ }, undefined, false, undefined, this);
3827
+ }
3828
+
3829
+ // src/index.tsx
3830
+ var args = process.argv.slice(2);
3831
+ var demo = args.includes("--demo");
3832
+ var fixture = args.find((arg) => arg.startsWith("--demo="))?.slice(7);
3833
+ if (args.length > 1 || args.some((arg) => !["--demo", "--demo=empty", "--demo=error", "--demo=setup", "--demo=setup-gh", "--demo=setup-zstd", "--demo=setup-error", "--demo=setup-interrupted", "--demo=no-homebrew", "--list-images", "--clear-cache"].includes(arg))) {
3834
+ console.error("Usage: bun start [--demo[=empty|error|setup|setup-gh|setup-zstd|setup-error|setup-interrupted|no-homebrew] | --list-images | --clear-cache]");
3835
+ process.exitCode = 1;
3836
+ } else if (args.includes("--clear-cache")) {
3837
+ try {
3838
+ console.log(await new FlashBackend().clearCache(new AbortController().signal));
3839
+ } catch (e) {
3840
+ console.error(e instanceof Error ? e.message : "Cache clear failed.");
3841
+ process.exitCode = 1;
3842
+ }
3843
+ } else if (args.includes("--list-images")) {
3844
+ try {
3845
+ console.log(JSON.stringify(await discoverImages(new AbortController().signal), null, 2));
3846
+ } catch (e) {
3847
+ console.error(e instanceof Error ? e.message : "Image discovery failed.");
3848
+ process.exitCode = 1;
3849
+ }
3850
+ } else if (!demo && !fixture && (!["darwin", "linux"].includes(process.platform) || process.getuid?.() === 0)) {
3851
+ console.error("Boo Flash requires macOS or Linux and a normal user account. Use --demo for a non-writing UI preview.");
3852
+ process.exitCode = 1;
3853
+ } else {
3854
+ const backend = new FlashBackend(demo ? "normal" : fixture);
3855
+ const renderer = await createCliRenderer2({ backgroundColor: palette.background, targetFps: 30, exitOnCtrlC: false, exitSignals: [] });
3856
+ let stop = async () => {};
3857
+ let finishing;
3858
+ let finished;
3859
+ const done = new Promise((resolve) => {
3860
+ finished = resolve;
3861
+ });
3862
+ const quit = () => {
3863
+ finishing ??= (async () => {
3864
+ try {
3865
+ await stop();
3866
+ } finally {
3867
+ renderer.destroy();
3868
+ finished();
3869
+ }
3870
+ })();
3871
+ };
3872
+ const signals = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
3873
+ for (const signal of signals)
3874
+ process.on(signal, quit);
3875
+ try {
3876
+ await render(() => /* @__PURE__ */ jsxDEV(App, {
3877
+ backend,
3878
+ quit,
3879
+ registerStop: (handler) => {
3880
+ stop = handler;
3881
+ }
3882
+ }, undefined, false, undefined, this), renderer);
3883
+ await done;
3884
+ } finally {
3885
+ await stop();
3886
+ renderer.destroy();
3887
+ for (const signal of signals)
3888
+ process.off(signal, quit);
3889
+ }
3890
+ }