waybetter 0.1.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/README.md +143 -9
  2. package/bin/waybetter.mjs +46 -0
  3. package/build/runtime.mjs +105 -0
  4. package/cli/buildenv.mjs +130 -0
  5. package/cli/dev.mjs +97 -0
  6. package/cli/i18n.mjs +382 -0
  7. package/cli/manifests.mjs +383 -0
  8. package/cli/privacy.mjs +768 -0
  9. package/cli/release.mjs +444 -0
  10. package/cli/remote.mjs +260 -0
  11. package/cli/store.mjs +326 -0
  12. package/cli/strings.mjs +121 -0
  13. package/dist/runtime/column.d.ts +93 -0
  14. package/dist/runtime/column.js +451 -0
  15. package/dist/runtime/column.js.map +1 -0
  16. package/dist/runtime/commandReport.d.ts +19 -0
  17. package/dist/runtime/commandReport.js +47 -0
  18. package/dist/runtime/commandReport.js.map +1 -0
  19. package/dist/runtime/commands.d.ts +72 -0
  20. package/dist/runtime/commands.js +214 -0
  21. package/dist/runtime/commands.js.map +1 -0
  22. package/dist/runtime/devErrors.d.ts +16 -0
  23. package/dist/runtime/devErrors.js +30 -0
  24. package/dist/runtime/devErrors.js.map +1 -0
  25. package/dist/runtime/domHub.d.ts +21 -0
  26. package/dist/runtime/domHub.js +139 -4
  27. package/dist/runtime/domHub.js.map +1 -1
  28. package/dist/runtime/featureModules.d.ts +157 -0
  29. package/dist/runtime/featureModules.js +484 -0
  30. package/dist/runtime/featureModules.js.map +1 -0
  31. package/dist/runtime/featureTable.d.ts +144 -0
  32. package/dist/runtime/featureTable.js +223 -0
  33. package/dist/runtime/featureTable.js.map +1 -0
  34. package/dist/runtime/fonts.d.ts +22 -0
  35. package/dist/runtime/fonts.js +72 -0
  36. package/dist/runtime/fonts.js.map +1 -0
  37. package/dist/runtime/i18n.d.ts +138 -0
  38. package/dist/runtime/i18n.js +614 -0
  39. package/dist/runtime/i18n.js.map +1 -0
  40. package/dist/runtime/i18nCheck.d.ts +30 -0
  41. package/dist/runtime/i18nCheck.js +224 -0
  42. package/dist/runtime/i18nCheck.js.map +1 -0
  43. package/dist/runtime/pageLang.d.ts +8 -0
  44. package/dist/runtime/pageLang.js +64 -0
  45. package/dist/runtime/pageLang.js.map +1 -0
  46. package/dist/runtime/pageStrings.d.ts +35 -0
  47. package/dist/runtime/pageStrings.js +76 -0
  48. package/dist/runtime/pageStrings.js.map +1 -0
  49. package/dist/runtime/panelButton.d.ts +33 -0
  50. package/dist/runtime/panelButton.js +83 -0
  51. package/dist/runtime/panelButton.js.map +1 -0
  52. package/dist/runtime/permissions.d.ts +47 -0
  53. package/dist/runtime/permissions.js +141 -0
  54. package/dist/runtime/permissions.js.map +1 -0
  55. package/dist/runtime/pluginApi.d.ts +51 -2
  56. package/dist/runtime/pluginApi.js +137 -22
  57. package/dist/runtime/pluginApi.js.map +1 -1
  58. package/dist/runtime/pluginI18n.d.ts +29 -0
  59. package/dist/runtime/pluginI18n.js +119 -0
  60. package/dist/runtime/pluginI18n.js.map +1 -0
  61. package/dist/runtime/settingsForm.d.ts +66 -0
  62. package/dist/runtime/settingsForm.js +114 -0
  63. package/dist/runtime/settingsForm.js.map +1 -0
  64. package/dist/runtime/site.d.ts +5 -0
  65. package/dist/runtime/site.js +1 -1
  66. package/dist/runtime/site.js.map +1 -1
  67. package/dist/runtime/siteRuntime.d.ts +144 -0
  68. package/dist/runtime/siteRuntime.js +175 -0
  69. package/dist/runtime/siteRuntime.js.map +1 -0
  70. package/dist/runtime/stringPackHook.d.ts +84 -0
  71. package/dist/runtime/stringPackHook.js +220 -0
  72. package/dist/runtime/stringPackHook.js.map +1 -0
  73. package/dist/runtime/stringPackTranslate.d.ts +59 -0
  74. package/dist/runtime/stringPackTranslate.js +264 -0
  75. package/dist/runtime/stringPackTranslate.js.map +1 -0
  76. package/dist/runtime/stringPacks.d.ts +200 -0
  77. package/dist/runtime/stringPacks.js +474 -0
  78. package/dist/runtime/stringPacks.js.map +1 -0
  79. package/dist/runtime/styles.d.ts +15 -0
  80. package/dist/runtime/styles.js +81 -0
  81. package/dist/runtime/styles.js.map +1 -0
  82. package/dist/runtime/textNet.d.ts +15 -0
  83. package/dist/runtime/textNet.js +131 -0
  84. package/dist/runtime/textNet.js.map +1 -0
  85. package/dist/runtime/themeApi.d.ts +121 -0
  86. package/dist/runtime/themeApi.js +220 -0
  87. package/dist/runtime/themeApi.js.map +1 -0
  88. package/dist/runtime/themeChoice.d.ts +52 -0
  89. package/dist/runtime/themeChoice.js +69 -0
  90. package/dist/runtime/themeChoice.js.map +1 -0
  91. package/dist/runtime/themeComponents.d.ts +435 -0
  92. package/dist/runtime/themeComponents.js +463 -0
  93. package/dist/runtime/themeComponents.js.map +1 -0
  94. package/dist/runtime/themeDecor.d.ts +73 -0
  95. package/dist/runtime/themeDecor.js +131 -0
  96. package/dist/runtime/themeDecor.js.map +1 -0
  97. package/dist/runtime/themeDoctor.d.ts +11 -0
  98. package/dist/runtime/themeDoctor.js +51 -0
  99. package/dist/runtime/themeDoctor.js.map +1 -0
  100. package/dist/runtime/themeIcons.d.ts +130 -0
  101. package/dist/runtime/themeIcons.js +263 -0
  102. package/dist/runtime/themeIcons.js.map +1 -0
  103. package/dist/runtime/themeRuntime.d.ts +100 -0
  104. package/dist/runtime/themeRuntime.js +497 -0
  105. package/dist/runtime/themeRuntime.js.map +1 -0
  106. package/dist/runtime/themeSchema.d.ts +10 -0
  107. package/dist/runtime/themeSchema.js +150 -0
  108. package/dist/runtime/themeSchema.js.map +1 -0
  109. package/dist/runtime/themeSpec.d.ts +259 -0
  110. package/dist/runtime/themeSpec.js +439 -0
  111. package/dist/runtime/themeSpec.js.map +1 -0
  112. package/dist/runtime/themeTokens.d.ts +12 -0
  113. package/dist/runtime/themeTokens.js +14 -6
  114. package/dist/runtime/themeTokens.js.map +1 -1
  115. package/dist/runtime/themeVariants.d.ts +30 -0
  116. package/dist/runtime/themeVariants.js +53 -0
  117. package/dist/runtime/themeVariants.js.map +1 -0
  118. package/dist/runtime/ui.d.ts +192 -0
  119. package/dist/runtime/ui.js +926 -0
  120. package/dist/runtime/ui.js.map +1 -0
  121. package/dist/shell/InstallDialog.d.ts +40 -0
  122. package/dist/shell/InstallDialog.js +170 -0
  123. package/dist/shell/InstallDialog.js.map +1 -0
  124. package/dist/shell/StoreView.d.ts +66 -0
  125. package/dist/shell/StoreView.js +103 -0
  126. package/dist/shell/StoreView.js.map +1 -0
  127. package/dist/shell/addressModel.js.map +1 -1
  128. package/dist/shell/chrome.js.map +1 -1
  129. package/dist/shell/deckModel.d.ts +117 -0
  130. package/dist/shell/deckModel.js +267 -0
  131. package/dist/shell/deckModel.js.map +1 -0
  132. package/dist/shell/frame/AccountSwitcher.d.ts +42 -0
  133. package/dist/shell/frame/AccountSwitcher.js +243 -0
  134. package/dist/shell/frame/AccountSwitcher.js.map +1 -0
  135. package/dist/shell/frame/AddColumnDialog.d.ts +8 -0
  136. package/dist/shell/frame/AddColumnDialog.js +81 -0
  137. package/dist/shell/frame/AddColumnDialog.js.map +1 -0
  138. package/dist/shell/frame/AppMenu.d.ts +21 -0
  139. package/dist/shell/frame/AppMenu.js +642 -0
  140. package/dist/shell/frame/AppMenu.js.map +1 -0
  141. package/dist/shell/frame/CommandPalette.d.ts +7 -0
  142. package/dist/shell/frame/CommandPalette.js +118 -0
  143. package/dist/shell/frame/CommandPalette.js.map +1 -0
  144. package/dist/shell/frame/DeckView.d.ts +13 -0
  145. package/dist/shell/frame/DeckView.js +287 -0
  146. package/dist/shell/frame/DeckView.js.map +1 -0
  147. package/dist/shell/frame/FindBar.d.ts +10 -0
  148. package/dist/shell/frame/FindBar.js +108 -0
  149. package/dist/shell/frame/FindBar.js.map +1 -0
  150. package/dist/shell/frame/MediaPanel.d.ts +8 -0
  151. package/dist/shell/frame/MediaPanel.js +76 -0
  152. package/dist/shell/frame/MediaPanel.js.map +1 -0
  153. package/dist/shell/frame/NewTabPage.d.ts +9 -0
  154. package/dist/shell/frame/NewTabPage.js +71 -0
  155. package/dist/shell/frame/NewTabPage.js.map +1 -0
  156. package/dist/shell/frame/PaneTheme.d.ts +16 -0
  157. package/dist/shell/frame/PaneTheme.js +67 -0
  158. package/dist/shell/frame/PaneTheme.js.map +1 -0
  159. package/dist/shell/frame/ReportDialog.d.ts +3 -0
  160. package/dist/shell/frame/ReportDialog.js +95 -0
  161. package/dist/shell/frame/ReportDialog.js.map +1 -0
  162. package/dist/shell/frame/Shell.d.ts +8 -0
  163. package/dist/shell/frame/Shell.js +1131 -0
  164. package/dist/shell/frame/Shell.js.map +1 -0
  165. package/dist/shell/frame/Titlebar.d.ts +16 -0
  166. package/dist/shell/frame/Titlebar.js +77 -0
  167. package/dist/shell/frame/Titlebar.js.map +1 -0
  168. package/dist/shell/frame/TitlebarExtras.d.ts +4 -0
  169. package/dist/shell/frame/TitlebarExtras.js +202 -0
  170. package/dist/shell/frame/TitlebarExtras.js.map +1 -0
  171. package/dist/shell/frame/WebPanel.d.ts +4 -0
  172. package/dist/shell/frame/WebPanel.js +182 -0
  173. package/dist/shell/frame/WebPanel.js.map +1 -0
  174. package/dist/shell/frame/accountModel.d.ts +49 -0
  175. package/dist/shell/frame/accountModel.js +67 -0
  176. package/dist/shell/frame/accountModel.js.map +1 -0
  177. package/dist/shell/frame/accountSwitcher.css +91 -0
  178. package/dist/shell/frame/appI18n.d.ts +44 -0
  179. package/dist/shell/frame/appI18n.js +64 -0
  180. package/dist/shell/frame/appI18n.js.map +1 -0
  181. package/dist/shell/frame/appMenu.css +121 -0
  182. package/dist/shell/frame/backupModel.d.ts +38 -0
  183. package/dist/shell/frame/backupModel.js +192 -0
  184. package/dist/shell/frame/backupModel.js.map +1 -0
  185. package/dist/shell/frame/boot.d.ts +8 -0
  186. package/dist/shell/frame/boot.js +35 -0
  187. package/dist/shell/frame/boot.js.map +1 -0
  188. package/dist/shell/frame/brand.d.ts +1 -0
  189. package/dist/shell/frame/brand.js +4 -0
  190. package/dist/shell/frame/brand.js.map +1 -0
  191. package/dist/shell/frame/bridge.d.ts +35 -0
  192. package/dist/shell/frame/bridge.js +70 -0
  193. package/dist/shell/frame/bridge.js.map +1 -0
  194. package/dist/shell/frame/chromeTheme.d.ts +29 -0
  195. package/dist/shell/frame/chromeTheme.js +81 -0
  196. package/dist/shell/frame/chromeTheme.js.map +1 -0
  197. package/dist/shell/frame/deck.css +209 -0
  198. package/dist/shell/frame/deckStore.d.ts +89 -0
  199. package/dist/shell/frame/deckStore.js +239 -0
  200. package/dist/shell/frame/deckStore.js.map +1 -0
  201. package/dist/shell/frame/devHost.d.ts +35 -0
  202. package/dist/shell/frame/devHost.js +80 -0
  203. package/dist/shell/frame/devHost.js.map +1 -0
  204. package/dist/shell/frame/devModel.d.ts +7 -0
  205. package/dist/shell/frame/devModel.js +17 -0
  206. package/dist/shell/frame/devModel.js.map +1 -0
  207. package/dist/shell/frame/diag.d.ts +7 -0
  208. package/dist/shell/frame/diag.js +39 -0
  209. package/dist/shell/frame/diag.js.map +1 -0
  210. package/dist/shell/frame/findBar.css +14 -0
  211. package/dist/shell/frame/frameMessages.d.ts +248 -0
  212. package/dist/shell/frame/frameMessages.js +252 -0
  213. package/dist/shell/frame/frameMessages.js.map +1 -0
  214. package/dist/shell/frame/hooks.d.ts +39 -0
  215. package/dist/shell/frame/hooks.js +97 -0
  216. package/dist/shell/frame/hooks.js.map +1 -0
  217. package/dist/shell/frame/i18n.d.ts +27 -0
  218. package/dist/shell/frame/i18n.js +66 -0
  219. package/dist/shell/frame/i18n.js.map +1 -0
  220. package/dist/shell/frame/iconPack.d.ts +11 -0
  221. package/dist/shell/frame/iconPack.js +45 -0
  222. package/dist/shell/frame/iconPack.js.map +1 -0
  223. package/dist/shell/frame/icons.d.ts +71 -0
  224. package/dist/shell/frame/icons.js +75 -0
  225. package/dist/shell/frame/icons.js.map +1 -0
  226. package/dist/shell/frame/index.d.ts +34 -0
  227. package/dist/shell/frame/index.js +43 -0
  228. package/dist/shell/frame/index.js.map +1 -0
  229. package/dist/shell/frame/language.d.ts +58 -0
  230. package/dist/shell/frame/language.js +165 -0
  231. package/dist/shell/frame/language.js.map +1 -0
  232. package/dist/shell/frame/mediaModel.d.ts +34 -0
  233. package/dist/shell/frame/mediaModel.js +53 -0
  234. package/dist/shell/frame/mediaModel.js.map +1 -0
  235. package/dist/shell/frame/menuMessages.en.json +54 -0
  236. package/dist/shell/frame/menuModel.d.ts +277 -0
  237. package/dist/shell/frame/menuModel.js +293 -0
  238. package/dist/shell/frame/menuModel.js.map +1 -0
  239. package/dist/shell/frame/paletteModel.d.ts +139 -0
  240. package/dist/shell/frame/paletteModel.js +112 -0
  241. package/dist/shell/frame/paletteModel.js.map +1 -0
  242. package/dist/shell/frame/paneTheme.css +9 -0
  243. package/dist/shell/frame/pluginCommandStore.d.ts +23 -0
  244. package/dist/shell/frame/pluginCommandStore.js +70 -0
  245. package/dist/shell/frame/pluginCommandStore.js.map +1 -0
  246. package/dist/shell/frame/reportModel.d.ts +52 -0
  247. package/dist/shell/frame/reportModel.js +105 -0
  248. package/dist/shell/frame/reportModel.js.map +1 -0
  249. package/dist/shell/frame/safeStore.d.ts +7 -0
  250. package/dist/shell/frame/safeStore.js +40 -0
  251. package/dist/shell/frame/safeStore.js.map +1 -0
  252. package/dist/shell/frame/searchInput.d.ts +2 -0
  253. package/dist/shell/frame/searchInput.js +11 -0
  254. package/dist/shell/frame/searchInput.js.map +1 -0
  255. package/dist/shell/frame/shell.css +629 -0
  256. package/dist/shell/frame/site.d.ts +206 -0
  257. package/dist/shell/frame/site.js +22 -0
  258. package/dist/shell/frame/site.js.map +1 -0
  259. package/dist/shell/frame/state.d.ts +99 -0
  260. package/dist/shell/frame/state.js +284 -0
  261. package/dist/shell/frame/state.js.map +1 -0
  262. package/dist/shell/frame/syncGate.d.ts +10 -0
  263. package/dist/shell/frame/syncGate.js +36 -0
  264. package/dist/shell/frame/syncGate.js.map +1 -0
  265. package/dist/shell/frame/syncHost.d.ts +35 -0
  266. package/dist/shell/frame/syncHost.js +85 -0
  267. package/dist/shell/frame/syncHost.js.map +1 -0
  268. package/dist/shell/frame/tabStill.d.ts +2 -0
  269. package/dist/shell/frame/tabStill.js +62 -0
  270. package/dist/shell/frame/tabStill.js.map +1 -0
  271. package/dist/shell/frame/themeScopes.d.ts +29 -0
  272. package/dist/shell/frame/themeScopes.js +61 -0
  273. package/dist/shell/frame/themeScopes.js.map +1 -0
  274. package/dist/shell/frame/themeStore.d.ts +99 -0
  275. package/dist/shell/frame/themeStore.js +252 -0
  276. package/dist/shell/frame/themeStore.js.map +1 -0
  277. package/dist/shell/frame/windowBinding.d.ts +2 -0
  278. package/dist/shell/frame/windowBinding.js +7 -0
  279. package/dist/shell/frame/windowBinding.js.map +1 -0
  280. package/dist/shell/frame/workspaceModel.d.ts +82 -0
  281. package/dist/shell/frame/workspaceModel.js +250 -0
  282. package/dist/shell/frame/workspaceModel.js.map +1 -0
  283. package/dist/shell/frame/workspaces.css +84 -0
  284. package/dist/shell/frame/workspaces.d.ts +22 -0
  285. package/dist/shell/frame/workspaces.js +125 -0
  286. package/dist/shell/frame/workspaces.js.map +1 -0
  287. package/dist/shell/frame/zoomStore.d.ts +10 -0
  288. package/dist/shell/frame/zoomStore.js +65 -0
  289. package/dist/shell/frame/zoomStore.js.map +1 -0
  290. package/dist/shell/installController.d.ts +122 -0
  291. package/dist/shell/installController.js +146 -0
  292. package/dist/shell/installController.js.map +1 -0
  293. package/dist/shell/installerMessages.d.ts +30 -0
  294. package/dist/shell/installerMessages.js +36 -0
  295. package/dist/shell/installerMessages.js.map +1 -0
  296. package/dist/shell/linkDrop.js.map +1 -1
  297. package/dist/shell/messages.d.ts +16 -0
  298. package/dist/shell/messages.js +27 -0
  299. package/dist/shell/messages.js.map +1 -0
  300. package/dist/shell/palette.js.map +1 -1
  301. package/dist/shell/paneModel.d.ts +22 -0
  302. package/dist/shell/paneModel.js +24 -1
  303. package/dist/shell/paneModel.js.map +1 -1
  304. package/dist/shell/panel/Appearance.d.ts +8 -0
  305. package/dist/shell/panel/Appearance.js +256 -0
  306. package/dist/shell/panel/Appearance.js.map +1 -0
  307. package/dist/shell/panel/BackupPanel.d.ts +5 -0
  308. package/dist/shell/panel/BackupPanel.js +198 -0
  309. package/dist/shell/panel/BackupPanel.js.map +1 -0
  310. package/dist/shell/panel/DeveloperPanel.d.ts +4 -0
  311. package/dist/shell/panel/DeveloperPanel.js +51 -0
  312. package/dist/shell/panel/DeveloperPanel.js.map +1 -0
  313. package/dist/shell/panel/FeatureToggles.d.ts +13 -0
  314. package/dist/shell/panel/FeatureToggles.js +42 -0
  315. package/dist/shell/panel/FeatureToggles.js.map +1 -0
  316. package/dist/shell/panel/Home.d.ts +3 -0
  317. package/dist/shell/panel/Home.js +23 -0
  318. package/dist/shell/panel/Home.js.map +1 -0
  319. package/dist/shell/panel/IconPackInstall.d.ts +15 -0
  320. package/dist/shell/panel/IconPackInstall.js +41 -0
  321. package/dist/shell/panel/IconPackInstall.js.map +1 -0
  322. package/dist/shell/panel/LanguageSettings.d.ts +1 -0
  323. package/dist/shell/panel/LanguageSettings.js +54 -0
  324. package/dist/shell/panel/LanguageSettings.js.map +1 -0
  325. package/dist/shell/panel/MakePanel.d.ts +3 -0
  326. package/dist/shell/panel/MakePanel.js +237 -0
  327. package/dist/shell/panel/MakePanel.js.map +1 -0
  328. package/dist/shell/panel/MarkdownDoc.d.ts +6 -0
  329. package/dist/shell/panel/MarkdownDoc.js +35 -0
  330. package/dist/shell/panel/MarkdownDoc.js.map +1 -0
  331. package/dist/shell/panel/Modes.d.ts +40 -0
  332. package/dist/shell/panel/Modes.js +270 -0
  333. package/dist/shell/panel/Modes.js.map +1 -0
  334. package/dist/shell/panel/NavIcon.d.ts +4 -0
  335. package/dist/shell/panel/NavIcon.js +6 -0
  336. package/dist/shell/panel/NavIcon.js.map +1 -0
  337. package/dist/shell/panel/Panel.d.ts +7 -0
  338. package/dist/shell/panel/Panel.js +139 -0
  339. package/dist/shell/panel/Panel.js.map +1 -0
  340. package/dist/shell/panel/PluginFeatures.d.ts +24 -0
  341. package/dist/shell/panel/PluginFeatures.js +165 -0
  342. package/dist/shell/panel/PluginFeatures.js.map +1 -0
  343. package/dist/shell/panel/Plugins.d.ts +33 -0
  344. package/dist/shell/panel/Plugins.js +112 -0
  345. package/dist/shell/panel/Plugins.js.map +1 -0
  346. package/dist/shell/panel/PowerSettings.d.ts +4 -0
  347. package/dist/shell/panel/PowerSettings.js +52 -0
  348. package/dist/shell/panel/PowerSettings.js.map +1 -0
  349. package/dist/shell/panel/Settings.d.ts +23 -0
  350. package/dist/shell/panel/Settings.js +116 -0
  351. package/dist/shell/panel/Settings.js.map +1 -0
  352. package/dist/shell/panel/ShareSetup.d.ts +8 -0
  353. package/dist/shell/panel/ShareSetup.js +130 -0
  354. package/dist/shell/panel/ShareSetup.js.map +1 -0
  355. package/dist/shell/panel/StoreSection.d.ts +6 -0
  356. package/dist/shell/panel/StoreSection.js +48 -0
  357. package/dist/shell/panel/StoreSection.js.map +1 -0
  358. package/dist/shell/panel/StringPacks.d.ts +11 -0
  359. package/dist/shell/panel/StringPacks.js +106 -0
  360. package/dist/shell/panel/StringPacks.js.map +1 -0
  361. package/dist/shell/panel/ThemeBuilder.d.ts +5 -0
  362. package/dist/shell/panel/ThemeBuilder.js +205 -0
  363. package/dist/shell/panel/ThemeBuilder.js.map +1 -0
  364. package/dist/shell/panel/backup.css +34 -0
  365. package/dist/shell/panel/builder.css +232 -0
  366. package/dist/shell/panel/builderModel.d.ts +55 -0
  367. package/dist/shell/panel/builderModel.js +95 -0
  368. package/dist/shell/panel/builderModel.js.map +1 -0
  369. package/dist/shell/panel/compare.d.ts +10 -0
  370. package/dist/shell/panel/compare.js +51 -0
  371. package/dist/shell/panel/compare.js.map +1 -0
  372. package/dist/shell/panel/controls.d.ts +135 -0
  373. package/dist/shell/panel/controls.js +213 -0
  374. package/dist/shell/panel/controls.js.map +1 -0
  375. package/dist/shell/panel/featureModel.d.ts +69 -0
  376. package/dist/shell/panel/featureModel.js +79 -0
  377. package/dist/shell/panel/featureModel.js.map +1 -0
  378. package/dist/shell/panel/fonts/FontBrowser.d.ts +10 -0
  379. package/dist/shell/panel/fonts/FontBrowser.js +189 -0
  380. package/dist/shell/panel/fonts/FontBrowser.js.map +1 -0
  381. package/dist/shell/panel/fonts/FontRoles.d.ts +20 -0
  382. package/dist/shell/panel/fonts/FontRoles.js +77 -0
  383. package/dist/shell/panel/fonts/FontRoles.js.map +1 -0
  384. package/dist/shell/panel/fonts/catalog.d.ts +37 -0
  385. package/dist/shell/panel/fonts/catalog.js +68 -0
  386. package/dist/shell/panel/fonts/catalog.js.map +1 -0
  387. package/dist/shell/panel/fonts/googleFontsCatalog.json +1 -0
  388. package/dist/shell/panel/index.d.ts +20 -0
  389. package/dist/shell/panel/index.js +34 -0
  390. package/dist/shell/panel/index.js.map +1 -0
  391. package/dist/shell/panel/languageModel.d.ts +5 -0
  392. package/dist/shell/panel/languageModel.js +7 -0
  393. package/dist/shell/panel/languageModel.js.map +1 -0
  394. package/dist/shell/panel/make.css +72 -0
  395. package/dist/shell/panel/makeModel.d.ts +32 -0
  396. package/dist/shell/panel/makeModel.js +84 -0
  397. package/dist/shell/panel/makeModel.js.map +1 -0
  398. package/dist/shell/panel/markdown.d.ts +46 -0
  399. package/dist/shell/panel/markdown.js +100 -0
  400. package/dist/shell/panel/markdown.js.map +1 -0
  401. package/dist/shell/panel/nav.d.ts +12 -0
  402. package/dist/shell/panel/nav.js +50 -0
  403. package/dist/shell/panel/nav.js.map +1 -0
  404. package/dist/shell/panel/panel.css +401 -0
  405. package/dist/shell/panel/panelMessages.d.ts +763 -0
  406. package/dist/shell/panel/panelMessages.js +767 -0
  407. package/dist/shell/panel/panelMessages.js.map +1 -0
  408. package/dist/shell/panel/plugins.css +59 -0
  409. package/dist/shell/panel/setupModel.d.ts +87 -0
  410. package/dist/shell/panel/setupModel.js +152 -0
  411. package/dist/shell/panel/setupModel.js.map +1 -0
  412. package/dist/shell/panel/site.d.ts +259 -0
  413. package/dist/shell/panel/site.js +25 -0
  414. package/dist/shell/panel/site.js.map +1 -0
  415. package/dist/shell/panel/strings.css +7 -0
  416. package/dist/shell/panel/themes.d.ts +10 -0
  417. package/dist/shell/panel/themes.js +8 -0
  418. package/dist/shell/panel/themes.js.map +1 -0
  419. package/dist/shell/profileModel.d.ts +3 -2
  420. package/dist/shell/profileModel.js +7 -5
  421. package/dist/shell/profileModel.js.map +1 -1
  422. package/dist/shell/store.css +230 -0
  423. package/dist/shell/storeMessages.d.ts +169 -0
  424. package/dist/shell/storeMessages.js +172 -0
  425. package/dist/shell/storeMessages.js.map +1 -0
  426. package/dist/shell/storeModel.d.ts +181 -0
  427. package/dist/shell/storeModel.js +133 -0
  428. package/dist/shell/storeModel.js.map +1 -0
  429. package/dist/shell/stringPacksModel.d.ts +44 -0
  430. package/dist/shell/stringPacksModel.js +94 -0
  431. package/dist/shell/stringPacksModel.js.map +1 -0
  432. package/dist/shell/updateModel.d.ts +2 -1
  433. package/dist/shell/updateModel.js +5 -4
  434. package/dist/shell/updateModel.js.map +1 -1
  435. package/dist/shell/windowControls.d.ts +4 -0
  436. package/dist/shell/windowControls.js +5 -0
  437. package/dist/shell/windowControls.js.map +1 -1
  438. package/i18n-config.schema.json +29 -0
  439. package/i18n-meta.schema.json +26 -0
  440. package/i18n.schema.json +42 -0
  441. package/icons.schema.json +646 -0
  442. package/languages.schema.json +23 -0
  443. package/locales/ar-x-fm.json +11 -0
  444. package/locales/ar.json +1281 -0
  445. package/locales/bg.json +1280 -0
  446. package/locales/bn.json +1280 -0
  447. package/locales/ca.json +1280 -0
  448. package/locales/cs.json +1280 -0
  449. package/locales/da.json +1280 -0
  450. package/locales/de.json +1280 -0
  451. package/locales/el.json +1280 -0
  452. package/locales/en-GB.json +27 -0
  453. package/locales/en.json +1280 -0
  454. package/locales/en.meta.json +51 -0
  455. package/locales/es.json +1280 -0
  456. package/locales/eu.json +1280 -0
  457. package/locales/fa.json +1281 -0
  458. package/locales/fi.json +1280 -0
  459. package/locales/fil.json +1280 -0
  460. package/locales/fr.json +1280 -0
  461. package/locales/ga.json +1280 -0
  462. package/locales/gl.json +1280 -0
  463. package/locales/gu.json +1280 -0
  464. package/locales/ha.json +1280 -0
  465. package/locales/he.json +1281 -0
  466. package/locales/hi.json +1280 -0
  467. package/locales/hr.json +1280 -0
  468. package/locales/hu.json +1279 -0
  469. package/locales/id.json +1280 -0
  470. package/locales/ig.json +1280 -0
  471. package/locales/it.json +1280 -0
  472. package/locales/ja.json +1280 -0
  473. package/locales/kn.json +1280 -0
  474. package/locales/ko.json +1280 -0
  475. package/locales/mr.json +1280 -0
  476. package/locales/ms.json +1280 -0
  477. package/locales/nb.json +1280 -0
  478. package/locales/nl.json +1280 -0
  479. package/locales/pl.json +1280 -0
  480. package/locales/pt.json +1280 -0
  481. package/locales/ro.json +1280 -0
  482. package/locales/ru.json +1280 -0
  483. package/locales/sk.json +1280 -0
  484. package/locales/sr.json +1280 -0
  485. package/locales/sv.json +1280 -0
  486. package/locales/ta.json +1280 -0
  487. package/locales/th.json +1280 -0
  488. package/locales/tr.json +1280 -0
  489. package/locales/uk.json +1280 -0
  490. package/locales/ur.json +1281 -0
  491. package/locales/vi.json +1280 -0
  492. package/locales/yo.json +1280 -0
  493. package/locales/zh-Hans.json +1280 -0
  494. package/locales/zh-Hant.json +1280 -0
  495. package/package.json +148 -6
  496. package/store-catalog.schema.json +65 -0
  497. package/theme.schema.json +4659 -0
  498. package/windows/installer.nsi +926 -0
  499. package/windows/languages/Basque.nsh +28 -0
  500. package/windows/languages/Catalan.nsh +28 -0
  501. package/windows/languages/Croatian.nsh +28 -0
  502. package/windows/languages/Czech.nsh +28 -0
  503. package/windows/languages/Danish.nsh +28 -0
  504. package/windows/languages/Finnish.nsh +28 -0
  505. package/windows/languages/Galician.nsh +28 -0
  506. package/windows/languages/Greek.nsh +28 -0
  507. package/windows/languages/Hungarian.nsh +28 -0
  508. package/windows/languages/Indonesian.nsh +28 -0
  509. package/windows/languages/Irish.nsh +28 -0
  510. package/windows/languages/Malay.nsh +28 -0
  511. package/windows/languages/Polish.nsh +28 -0
  512. package/windows/languages/Romanian.nsh +28 -0
  513. package/windows/languages/Serbian.nsh +28 -0
  514. package/windows/languages/Slovak.nsh +28 -0
  515. package/windows/languages/Thai.nsh +28 -0
@@ -0,0 +1,1131 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ // The shell: a trusted local page that fills the window and hosts the dock (dockview): tabs in the
3
+ // titlebar row, splits, the new-tab page, decks, the app's panel tab, link drags into the dock,
4
+ // keyboard shortcuts, overlays (menus, palette, dialogs) and session layout persistence. Each web
5
+ // tab is a native child webview that native code places over its pane (`tabs_sync`, state.ts).
6
+ import { DockviewReact, themeDark, } from 'dockview';
7
+ import 'dockview/dist/styles/dockview.css';
8
+ import { useEffect, useRef, useState } from 'react';
9
+ import { createPortal } from 'react-dom';
10
+ import { carriesLink, dropUrl, placementFor } from "../linkDrop.js";
11
+ import { besideGroup } from "../paneModel.js";
12
+ import { tabProfile } from "../profileModel.js";
13
+ import { accountAction } from "./accountModel.js";
14
+ import { accountLabel } from "./AccountSwitcher.js";
15
+ import { openAppMenu } from "./AppMenu.js";
16
+ import { invoke, listen } from "./bridge.js";
17
+ import { CommandPalette } from "./CommandPalette.js";
18
+ import { DeckTabBadge, DeckView, focusColumn, runDeckCommand } from "./DeckView.js";
19
+ import { openFind, stepFind } from "./FindBar.js";
20
+ import { columnSourceUrl, createDeck, deckById, decks, findColumnTab, loadDecks, setRuntime } from "./deckStore.js";
21
+ import { installDevHost } from "./devHost.js";
22
+ import { useProfiles, useT } from "./hooks.js";
23
+ import { t } from "./i18n.js";
24
+ import { Icon } from "./icons.js";
25
+ import { MediaPanel } from "./MediaPanel.js";
26
+ import { NewTabPage } from "./NewTabPage.js";
27
+ import { PaneThemeHost, openPaneTheme } from "./PaneTheme.js";
28
+ import { focusedCommands, installPluginCommands, notePanelActive, refreshPluginNames, runPluginCommand } from "./pluginCommandStore.js";
29
+ import { initDiag } from "./diag.js";
30
+ import { installSafeMode } from "./safeStore.js";
31
+ import { hasFeature, site } from "./site.js";
32
+ import { flushLayer, flushSync, isWeb, loadLayout, newTabId, paneInfos, panelId, patchParams, resync, saveLayoutSoon, scheduleSync, setOverlay, setProfiles, shell, tabAction, } from "./state.js";
33
+ import { installSyncHost } from "./syncHost.js";
34
+ import { ResizeGrips, Titlebar, titlebarContextMenu } from "./Titlebar.js";
35
+ import { openWorkspaceSave } from "./TitlebarExtras.js";
36
+ import { WebPanel } from "./WebPanel.js";
37
+ import { frameless, isTauri, onMaximizedChange, os } from "./windowBinding.js";
38
+ import { loadWorkspaces, restoreWorkspace, workspaceList, workspaces } from "./workspaces.js";
39
+ import { stepZoom, zoomTarget } from "./zoomStore.js";
40
+ import './shell.css';
41
+ /** How long the startup cover waits, once the window is shown, for the visible site tabs to say they painted. */
42
+ export const STARTUP_CONTENT_WAIT = 8000;
43
+ /** A development build (createShell's `dev`): the shell keeps the devtools shortcut. */
44
+ let devBuild = false;
45
+ export function setDevBuild(value) {
46
+ devBuild = value;
47
+ }
48
+ /** Opens a web tab. `profile` defaults to the switcher's active account. */
49
+ const originOf = (url) => { try {
50
+ return new URL(url).origin;
51
+ }
52
+ catch {
53
+ return '';
54
+ } };
55
+ function addWeb(url, placement, activate = true, profile = shell.profiles.active, besideOf) {
56
+ const api = shell.api;
57
+ if (!api)
58
+ return;
59
+ const valid = placement && ('referencePanel' in placement ? !!api.getPanel(placement.referencePanel)
60
+ : 'referenceGroup' in placement ? !!api.getGroup(placement.referenceGroup) : true);
61
+ const reference = valid ? placement : undefined;
62
+ return api.addPanel({
63
+ id: newTabId(),
64
+ component: 'web',
65
+ title: t('wb.shell.tab.new'),
66
+ params: { kind: 'web', url, profile: tabProfile(shell.profiles, profile).id, ...(besideOf ? { besideOf } : {}) },
67
+ position: reference ?? (api.activeGroup ? { referenceGroup: api.activeGroup, direction: 'within' } : undefined),
68
+ inactive: !activate,
69
+ });
70
+ }
71
+ /**
72
+ * The "pane" link setting: opens `url` in the side pane right of `source`'s group: split once, then
73
+ * reused. A link clicked inside a side pane stays in that pane instead of splitting again.
74
+ */
75
+ function openBeside(url, source, activate, profile) {
76
+ const api = shell.api;
77
+ if (!api)
78
+ return;
79
+ const origin = isWeb(source) ? source.params.besideOf : undefined;
80
+ if (origin) {
81
+ addWeb(url, { referenceGroup: source.group.id, direction: 'within' }, activate, profile, origin);
82
+ return;
83
+ }
84
+ const box = (group) => {
85
+ const rect = group.element.getBoundingClientRect();
86
+ const besideOf = group.panels.map(panel => panel.params?.besideOf).filter((id) => !!id);
87
+ return { id: group.id, left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom, besideOf };
88
+ };
89
+ const target = besideGroup(box(source.group), api.groups.map(box));
90
+ addWeb(url, target ? { referenceGroup: target, direction: 'within' } : { referencePanel: source.id, direction: 'right' }, activate, profile, source.group.id);
91
+ }
92
+ /** Opens the new-tab page (shell-drawn, no webview) in the active group; links from it keep `profile`. */
93
+ function addStart(profile = shell.profiles.active, placement) {
94
+ const api = shell.api;
95
+ if (!api)
96
+ return;
97
+ const reference = placement && api.getPanel(placement.referencePanel) ? placement : undefined;
98
+ const panel = api.addPanel({
99
+ id: newTabId(),
100
+ component: 'start',
101
+ title: t('wb.shell.tab.new'),
102
+ params: { kind: 'start', profile: tabProfile(shell.profiles, profile).id },
103
+ position: reference ?? (api.activeGroup ? { referenceGroup: api.activeGroup, direction: 'within' } : undefined),
104
+ });
105
+ focusAddress(panel.id);
106
+ return panel;
107
+ }
108
+ /** A link chosen on a new-tab page opens in that page's place; the page itself goes away. */
109
+ function openFromStart(url, profile, startId) {
110
+ const start = startId ? shell.api?.getPanel(startId) : undefined;
111
+ if (!start) {
112
+ addWeb(url, undefined, true, profile);
113
+ return;
114
+ }
115
+ const group = start.group;
116
+ addWeb(url, { referenceGroup: group.id, direction: 'within', index: group.panels.indexOf(start) }, true, profile);
117
+ start.api.close();
118
+ }
119
+ /**
120
+ * Account switcher click: a visible pane of that account gets focus; otherwise its home feed (or the
121
+ * site's sign-in) opens in a new pane right of the focused one. `force` opens a tab in the focused
122
+ * group instead; `url` opens that page of the account (e.g. the site's sign-out) beside.
123
+ */
124
+ function openAccount(profile, options = {}) {
125
+ const api = shell.api;
126
+ if (!api)
127
+ return;
128
+ const active = api.activePanel;
129
+ const { homeUrl, loginUrl, isSiteUrl } = site();
130
+ const action = options.url
131
+ ? { type: 'open', url: options.url, profile: tabProfile(shell.profiles, profile).id, where: 'beside' }
132
+ : accountAction(shell.profiles, paneInfos(), active?.id, profile, { force: options.force, homeUrl, loginUrl, isSiteUrl });
133
+ if (action.type === 'focus') {
134
+ const panel = api.getPanel(action.paneId);
135
+ panel?.api.setActive();
136
+ if (panel)
137
+ tabAction(panel.id, 'focus');
138
+ return;
139
+ }
140
+ const placement = active ? { referencePanel: active.id, direction: action.where === 'tab' ? 'within' : 'right' } : undefined;
141
+ addWeb(action.url, placement, true, action.profile);
142
+ }
143
+ // ---------------------------------------------------------------------------------------------
144
+ // Decks of site columns (DeckView.tsx; only with `site.deck`)
145
+ const deckPanelId = (deckId) => `deck-${deckId}`;
146
+ /** The last focused tab that is not a deck, for Ctrl+Shift+D back and forth. */
147
+ let lastNonDeck = null;
148
+ /** The last web tab that was in front (Ctrl+F from the app's own tab searches it). */
149
+ let lastWeb = null;
150
+ /**
151
+ * The web tab Ctrl+F searches: the one given, else the last web tab that was in front and is still
152
+ * on screen, else the first web tab on screen. The app's own tab has no page to search.
153
+ */
154
+ function findTarget(panel) {
155
+ if (panel && isWeb(panel))
156
+ return panel;
157
+ const shown = (shell.api?.panels ?? []).filter(item => isWeb(item) && item.api.isVisible);
158
+ return shown.find(item => item.id === lastWeb) ?? shown[0];
159
+ }
160
+ /** Opens (or focuses) a deck tab; with no deck yet, creates one. */
161
+ function openDeck(deckId) {
162
+ const api = shell.api;
163
+ if (!api || !site().deck)
164
+ return;
165
+ const deck = (deckId && deckById(deckId)) || decks.list[0] || createDeck(t('wb.shell.deck.default'));
166
+ const existing = api.getPanel(deckPanelId(deck.id));
167
+ if (existing) {
168
+ existing.api.setActive();
169
+ return;
170
+ }
171
+ api.addPanel({
172
+ id: deckPanelId(deck.id), component: 'deck', title: deck.name, params: { kind: 'deck', deckId: deck.id },
173
+ position: api.activeGroup ? { referenceGroup: api.activeGroup, direction: 'within' } : undefined,
174
+ });
175
+ }
176
+ /** Ctrl+Shift+D: to the deck, and from the deck back to the tab you came from. */
177
+ function toggleDeck() {
178
+ const api = shell.api;
179
+ const active = api?.activePanel;
180
+ if (active && active.params.kind === 'deck') {
181
+ const back = lastNonDeck ? api?.getPanel(lastNonDeck) : undefined;
182
+ back?.api.setActive();
183
+ if (back && isWeb(back))
184
+ tabAction(back.id, 'focus');
185
+ return;
186
+ }
187
+ openDeck();
188
+ }
189
+ /** The deck a shortcut is about: the column's deck (keys pressed in a column), else the active deck tab. */
190
+ function deckFor(id) {
191
+ if (id !== undefined) {
192
+ const found = findColumnTab(id);
193
+ if (found)
194
+ return found.deck.id;
195
+ const params = shell.api?.getPanel(id)?.params;
196
+ return params?.kind === 'deck' ? params.deckId : null;
197
+ }
198
+ const params = shell.api?.activePanel?.params;
199
+ return params?.kind === 'deck' ? params.deckId : null;
200
+ }
201
+ function openPanel(section, from) {
202
+ const api = shell.api;
203
+ if (!api)
204
+ return;
205
+ const existing = api.getPanel(panelId());
206
+ const nonce = Date.now();
207
+ if (existing) {
208
+ if (section)
209
+ existing.api.updateParameters({ section, nonce });
210
+ existing.api.setActive();
211
+ return;
212
+ }
213
+ // Open beside the site so both stay visible; a second group means split view.
214
+ const reference = from && api.getPanel(from) ? { referencePanel: from, direction: 'right' } : undefined;
215
+ api.addPanel({ id: panelId(), component: 'panel', title: site().panel.title, params: { kind: 'panel', section, nonce }, position: reference });
216
+ }
217
+ function closeTab(panel) {
218
+ if (panel.params.pinned)
219
+ return;
220
+ if (isWeb(panel)) {
221
+ const { url, profile } = panel.params;
222
+ shell.closed.push({ url, profile, title: panel.api.title || undefined });
223
+ }
224
+ panel.api.close();
225
+ }
226
+ export function splitTab(panel, direction) {
227
+ // A lone tab cannot be moved out of its group, so split opens a copy of it instead.
228
+ if (panel.group.panels.length > 1) {
229
+ panel.api.moveTo({ group: panel.group, position: direction === 'right' ? 'right' : 'bottom' });
230
+ }
231
+ else if (isWeb(panel)) {
232
+ const { url, profile } = panel.params;
233
+ addWeb(url, { referencePanel: panel.id, direction }, true, profile);
234
+ }
235
+ }
236
+ function cycleTabs(step) {
237
+ const group = shell.api?.activeGroup;
238
+ if (!group || group.panels.length < 2)
239
+ return;
240
+ const index = group.panels.findIndex(panel => panel.id === group.activePanel?.id);
241
+ group.panels[(index + step + group.panels.length) % group.panels.length]?.api.setActive();
242
+ }
243
+ function focusAddress(id) {
244
+ invoke('shell_focus').catch(() => { });
245
+ requestAnimationFrame(() => window.dispatchEvent(new CustomEvent('bt-focus-address', { detail: id })));
246
+ }
247
+ let togglePalette = () => { };
248
+ let toggleMedia = () => { };
249
+ function paletteContext() {
250
+ const panels = shell.api?.panels.filter(isWeb) ?? [];
251
+ const deck = site().deck;
252
+ return {
253
+ tabs: panels.map(panel => ({ id: panel.id, title: panel.api.title ?? '', url: panel.params.url, favicon: panel.params.favicon })),
254
+ profiles: shell.profiles.profiles.map(({ id, name, color }) => ({ id, name, color })),
255
+ activeProfile: shell.profiles.active,
256
+ decks: deck ? decks.list.map(item => ({
257
+ id: item.id, name: item.name,
258
+ columns: item.columns.map(column => ({ id: column.id, title: deck.title(column.source, column.title).title, profile: column.profile })),
259
+ })) : undefined,
260
+ activeDeck: (() => { const params = shell.api?.activePanel?.params; return params?.kind === 'deck' ? params.deckId : undefined; })(),
261
+ workspaces: hasFeature('workspaces') ? workspaceList() : undefined,
262
+ activeWorkspace: workspaces.active ?? undefined,
263
+ // Plugin commands of the focused site tab (they run there).
264
+ commands: focusedCommands(),
265
+ };
266
+ }
267
+ /** Palette actions only navigate; account changes are completed by the user on the site's own pages. */
268
+ function runPalette(action) {
269
+ const api = shell.api;
270
+ if (!api)
271
+ return;
272
+ switch (action.type) {
273
+ case 'focus-tab':
274
+ api.getPanel(action.id)?.api.setActive();
275
+ break;
276
+ case 'new-tab':
277
+ shell.newTab(action.profile);
278
+ break;
279
+ case 'panel':
280
+ openPanel(action.section, api.activePanel?.id);
281
+ break;
282
+ case 'open':
283
+ addWeb(action.url);
284
+ break;
285
+ case 'media':
286
+ toggleMedia();
287
+ break;
288
+ case 'deck':
289
+ openDeck(action.deckId);
290
+ break;
291
+ case 'deck-new':
292
+ openDeck(createDeck(`Deck ${decks.list.length + 1}`).id);
293
+ break;
294
+ case 'deck-add':
295
+ openDeck(action.deckId);
296
+ requestAnimationFrame(() => window.dispatchEvent(new CustomEvent('bt-deck-add', { detail: action.deckId })));
297
+ break;
298
+ case 'deck-focus':
299
+ openDeck(action.deckId);
300
+ requestAnimationFrame(() => focusColumn(action.deckId, action.columnId));
301
+ break;
302
+ case 'plugin-command':
303
+ runPluginCommand(action.tab, action.command);
304
+ break;
305
+ case 'workspace':
306
+ void restoreWorkspace(action.id);
307
+ break;
308
+ case 'workspace-save':
309
+ openWorkspaceSave();
310
+ break;
311
+ case 'find':
312
+ runShortcut('find');
313
+ break;
314
+ case 'menu':
315
+ openAppMenu({ keyboard: true });
316
+ break;
317
+ }
318
+ }
319
+ function runShortcut(shortcut, id) {
320
+ const api = shell.api;
321
+ if (!api)
322
+ return;
323
+ if (shortcut === 'command-palette') {
324
+ togglePalette();
325
+ return;
326
+ }
327
+ // Alt+F / F10: the app menu, opened for the keyboard (pressed again, it closes).
328
+ if (shortcut === 'app-menu') {
329
+ openAppMenu({ keyboard: true, toggle: true });
330
+ return;
331
+ }
332
+ // Page zoom of the tab the keys were pressed in (a deck column zooms itself), else the pane in front.
333
+ if (shortcut === 'zoom-in' || shortcut === 'zoom-out' || shortcut === 'zoom-reset') {
334
+ void stepZoom(shortcut === 'zoom-in' ? 1 : shortcut === 'zoom-out' ? -1 : 0, id ?? zoomTarget());
335
+ return;
336
+ }
337
+ if (shortcut === 'deck-toggle') {
338
+ toggleDeck();
339
+ return;
340
+ }
341
+ if (shortcut.startsWith('column-') || shortcut === 'deck-compose') {
342
+ const deckId = deckFor(id);
343
+ if (deckId)
344
+ runDeckCommand(deckId, shortcut);
345
+ return;
346
+ }
347
+ // Find in page in a deck column: that column's own find bar (the keys were pressed in it, or it is the
348
+ // focused column of the deck in front).
349
+ if (shortcut === 'find' || shortcut === 'find-next' || shortcut === 'find-previous') {
350
+ const column = id && !api.getPanel(id) ? id : id === undefined && api.activePanel?.params?.kind === 'deck' ? zoomTarget() : null;
351
+ if (column && findColumnTab(column)?.column) {
352
+ if (shortcut !== 'find')
353
+ stepFind(column, shortcut === 'find-next' ? 1 : -1);
354
+ else if (hasFeature('find'))
355
+ openFind(column);
356
+ return;
357
+ }
358
+ }
359
+ // A browser shortcut pressed inside a deck column acts on its deck tab.
360
+ if (id && !api.getPanel(id) && findColumnTab(id))
361
+ id = deckPanelId(findColumnTab(id).deck.id);
362
+ const panel = id === undefined ? api.activePanel : api.getPanel(id);
363
+ switch (shortcut) {
364
+ case 'new-tab':
365
+ addStart();
366
+ break;
367
+ // Find in page: the find bar of that tab (the one the keys were pressed in, else the one in front).
368
+ case 'find': {
369
+ const target = findTarget(panel);
370
+ if (target && hasFeature('find')) {
371
+ target.api.setActive();
372
+ openFind(target.id);
373
+ }
374
+ break;
375
+ }
376
+ case 'find-next':
377
+ case 'find-previous': {
378
+ const target = findTarget(panel);
379
+ if (target)
380
+ stepFind(target.id, shortcut === 'find-next' ? 1 : -1);
381
+ break;
382
+ }
383
+ case 'close-tab':
384
+ if (panel)
385
+ closeTab(panel);
386
+ break;
387
+ case 'reopen-tab': {
388
+ const tab = shell.closed.pop();
389
+ if (tab)
390
+ addWeb(tab.url, undefined, true, tab.profile);
391
+ break;
392
+ }
393
+ case 'focus-address':
394
+ if (panel && (isWeb(panel) || panel.params.kind === 'start'))
395
+ focusAddress(panel.id);
396
+ break;
397
+ case 'next-tab':
398
+ cycleTabs(1);
399
+ break;
400
+ case 'prev-tab':
401
+ cycleTabs(-1);
402
+ break;
403
+ case 'reload':
404
+ case 'hard-reload':
405
+ if (panel && isWeb(panel))
406
+ tabAction(panel.id, 'reload');
407
+ break;
408
+ case 'back':
409
+ if (panel && isWeb(panel))
410
+ tabAction(panel.id, 'back');
411
+ break;
412
+ case 'forward':
413
+ if (panel && isWeb(panel))
414
+ tabAction(panel.id, 'forward');
415
+ break;
416
+ case 'split-right':
417
+ if (panel)
418
+ splitTab(panel, 'right');
419
+ break;
420
+ case 'split-down':
421
+ if (panel)
422
+ splitTab(panel, 'below');
423
+ break;
424
+ case 'open-panel':
425
+ if (id === undefined || panel)
426
+ openPanel(undefined, panel?.id);
427
+ break;
428
+ case 'media-panel':
429
+ toggleMedia();
430
+ break;
431
+ }
432
+ }
433
+ const editable = (target) => {
434
+ const el = target;
435
+ return !!el && (el.isContentEditable || /^(input|textarea|select)$/i.test(el.tagName));
436
+ };
437
+ /** Deck keys pressed in the shell itself (a column header, the deck bar) while a deck tab is active. */
438
+ function deckShortcutFor(event) {
439
+ const mod = event.ctrlKey || event.metaKey;
440
+ if (mod && event.shiftKey && !event.altKey && event.code === 'KeyD')
441
+ return 'deck-toggle';
442
+ if (shell.api?.activePanel?.params?.kind !== 'deck' || editable(event.target))
443
+ return null;
444
+ if (event.ctrlKey && event.altKey && !event.shiftKey && event.code === 'KeyN')
445
+ return 'deck-compose';
446
+ if (event.altKey && !mod && !event.shiftKey) {
447
+ const digit = /^Digit([1-9])$/.exec(event.code);
448
+ if (digit)
449
+ return `column-${digit[1]}`;
450
+ if (event.key === 'PageUp')
451
+ return 'column-page-prev';
452
+ if (event.key === 'PageDown')
453
+ return 'column-page-next';
454
+ if (event.code === 'KeyR')
455
+ return 'column-mark-read';
456
+ if (event.code === 'KeyU')
457
+ return 'column-jump-unread';
458
+ }
459
+ if (!mod && !event.altKey && event.key === '[')
460
+ return 'column-prev';
461
+ if (!mod && !event.altKey && event.key === ']')
462
+ return 'column-next';
463
+ return null;
464
+ }
465
+ function shortcutFor(event) {
466
+ const deck = site().deck ? deckShortcutFor(event) : null;
467
+ if (deck)
468
+ return deck;
469
+ const mod = event.ctrlKey || event.metaKey;
470
+ const key = event.key.toLowerCase();
471
+ if (mod && !event.altKey) {
472
+ if (key === 't')
473
+ return event.shiftKey ? 'reopen-tab' : 'new-tab';
474
+ if (key === 'w')
475
+ return event.shiftKey ? 'open-panel' : 'close-tab';
476
+ if (key === 'l')
477
+ return 'focus-address';
478
+ if (key === 'g' && event.shiftKey)
479
+ return 'media-panel';
480
+ if (key === 'k' && !event.shiftKey)
481
+ return 'command-palette';
482
+ if (key === 'f' && !event.shiftKey && hasFeature('find'))
483
+ return 'find';
484
+ if (key === 'tab')
485
+ return event.shiftKey ? 'prev-tab' : 'next-tab';
486
+ if (key === 'r')
487
+ return 'reload';
488
+ if (event.code === 'Backslash' || event.code === 'IntlBackslash')
489
+ return event.shiftKey ? 'split-down' : 'split-right';
490
+ if (hasFeature('zoom')) {
491
+ if (key === '=' || key === '+' || event.code === 'NumpadAdd')
492
+ return 'zoom-in';
493
+ if (key === '-' || event.code === 'NumpadSubtract')
494
+ return 'zoom-out';
495
+ if (key === '0' || event.code === 'Numpad0')
496
+ return 'zoom-reset';
497
+ }
498
+ }
499
+ if (event.key === 'F3' && !mod && !event.altKey)
500
+ return event.shiftKey ? 'find-previous' : 'find-next';
501
+ // The app menu, like a browser's: F10, and Alt+F where Alt is not a typing key (not on macOS).
502
+ if (event.key === 'F10' && !mod && !event.altKey && !event.shiftKey)
503
+ return 'app-menu';
504
+ if (event.altKey && !mod && !event.shiftKey && event.code === 'KeyF' && os !== 'macos')
505
+ return 'app-menu';
506
+ if (event.altKey && !mod && key === 'arrowleft')
507
+ return 'back';
508
+ if (event.altKey && !mod && key === 'arrowright')
509
+ return 'forward';
510
+ return null;
511
+ }
512
+ // ---------------------------------------------------------------------------------------------
513
+ // Link drags: a link dragged out of a tab (or in from another app) drops as a new tab or split.
514
+ /**
515
+ * While active, the native tab webviews are hidden so the OS drag reaches the shell and dockview's
516
+ * drop zones. `internal`: a drag that started in the shell itself (tab moves, list reordering).
517
+ */
518
+ const linkDrag = {
519
+ active: false, internal: false, url: null, from: undefined,
520
+ lastOver: 0, sawOver: false, blockedUntil: 0, timer: undefined,
521
+ };
522
+ function beginLinkDrag(from, url) {
523
+ if (linkDrag.internal)
524
+ return;
525
+ if (!linkDrag.active) {
526
+ // A tab that keeps announcing drags that never reach the shell cannot keep the panes hidden.
527
+ if (Date.now() < linkDrag.blockedUntil)
528
+ return;
529
+ Object.assign(linkDrag, { active: true, sawOver: false, lastOver: Date.now(), url: null, from: undefined });
530
+ setOverlay(1);
531
+ document.querySelector('.bt-shell')?.classList.add('bt-shell--link-drag');
532
+ // Esc or a drop outside the window may end the drag without any event here.
533
+ linkDrag.timer = setInterval(() => { if (Date.now() - linkDrag.lastOver > 1500)
534
+ endLinkDrag(true); }, 250);
535
+ }
536
+ if (url) {
537
+ linkDrag.url = url;
538
+ linkDrag.from = from;
539
+ }
540
+ }
541
+ function endLinkDrag(timedOut = false) {
542
+ if (!linkDrag.active)
543
+ return;
544
+ clearInterval(linkDrag.timer);
545
+ if (timedOut && !linkDrag.sawOver)
546
+ linkDrag.blockedUntil = Date.now() + 5000;
547
+ Object.assign(linkDrag, { active: false, url: null, from: undefined, timer: undefined });
548
+ document.querySelector('.bt-shell')?.classList.remove('bt-shell--link-drag');
549
+ setOverlay(-1);
550
+ }
551
+ /** Opens the dropped link where it was dropped, in the source tab's account (else the active one). */
552
+ function dropLink(event, placement) {
553
+ const url = dropUrl(type => event.dataTransfer?.getData(type) ?? '', linkDrag.url);
554
+ const source = linkDrag.from ? shell.api?.getPanel(linkDrag.from) : undefined;
555
+ const profile = source && isWeb(source) ? source.params.profile : shell.profiles.active;
556
+ endLinkDrag();
557
+ if (url)
558
+ addWeb(url, placement, true, profile);
559
+ }
560
+ const dragTypes = (event) => Array.from(event.dataTransfer?.types ?? []);
561
+ let openMenu = () => { };
562
+ function ContextMenu({ menu, onClose }) {
563
+ const t = useT();
564
+ const { panel } = menu;
565
+ const params = panel.params;
566
+ const web = params.kind === 'web' ? params : null;
567
+ const group = panel.group;
568
+ useEffect(() => {
569
+ setOverlay(1);
570
+ const close = () => onClose();
571
+ const onKey = (event) => { if (event.key === 'Escape')
572
+ onClose(); };
573
+ window.addEventListener('blur', close);
574
+ window.addEventListener('keydown', onKey);
575
+ return () => { setOverlay(-1); window.removeEventListener('blur', close); window.removeEventListener('keydown', onKey); };
576
+ }, []);
577
+ const item = (label, icon, action, disabled = false, keys) => _jsxs("button", { role: "menuitem", disabled: disabled, onClick: () => { onClose(); action(); }, children: [_jsx(Icon, { name: icon }), _jsx("span", { className: "bt-menu-label", children: label }), keys && _jsx("span", { className: "bt-menu-keys", children: keys })] });
578
+ const others = group.panels.filter(other => other.id !== panel.id && !other.params.pinned);
579
+ return createPortal(_jsx("div", { className: "bt-menu-backdrop", onMouseDown: onClose, onContextMenu: event => { event.preventDefault(); onClose(); }, children: _jsxs("div", { className: "bt-menu bt-pop", role: "menu", style: { left: Math.max(8, Math.min(menu.x, window.innerWidth - 248)), top: Math.max(8, Math.min(menu.y, window.innerHeight - 360)) }, onMouseDown: event => event.stopPropagation(), children: [item(params.pinned ? t('wb.shell.menu.unpin') : t('wb.shell.menu.pin'), params.pinned ? 'unpin' : 'pin', () => {
580
+ panel.api.updateParameters({ pinned: !params.pinned });
581
+ if (!params.pinned)
582
+ panel.api.moveTo({ group, index: 0 });
583
+ saveLayoutSoon();
584
+ }), web && item(t('wb.shell.menu.duplicate'), 'duplicate', () => addWeb(web.url, { referencePanel: panel.id, direction: 'within' }, true, web.profile)), web && item(t('wb.shell.menu.reload'), 'reload', () => tabAction(panel.id, 'reload'), false, 'Ctrl R'), _jsx("hr", {}), item(t('wb.shell.menu.splitRight'), 'split', () => splitTab(panel, 'right'), !web && group.panels.length < 2, 'Ctrl \\'), item(t('wb.shell.menu.splitDown'), 'splitDown', () => splitTab(panel, 'below'), !web && group.panels.length < 2, 'Ctrl ⇧ \\'), web && _jsxs(_Fragment, { children: [_jsx("hr", {}), site().themes && item(t('wb.shell.menu.paneTheme'), 'palette', () => openPaneTheme({ id: panel.id, profile: tabProfile(shell.profiles, web.profile).id, x: menu.x, y: menu.y })), item(t('wb.shell.menu.copyLink'), 'copy', () => void navigator.clipboard?.writeText(web.url)), item(t('wb.shell.menu.openBrowser'), 'external', () => invoke('open_external_url', { url: web.url }).catch(() => { }))] }), _jsx("hr", {}), item(t('wb.shell.menu.close'), 'close', () => closeTab(panel), !!params.pinned, 'Ctrl W'), item(t('wb.shell.menu.closeOthers'), 'closeOthers', () => others.forEach(closeTab), others.length === 0)] }) }), document.body);
585
+ }
586
+ function Favicon({ params }) {
587
+ const t = useT();
588
+ const [broken, setBroken] = useState(false);
589
+ useEffect(() => setBroken(false), [params.kind === 'web' ? params.favicon : '']);
590
+ if (params.kind === 'panel')
591
+ return _jsx("img", { className: "bt-favicon", src: site().logo, alt: "" });
592
+ if (params.kind === 'start')
593
+ return _jsx(Icon, { name: "sparkle", className: "bt-favicon bt-favicon--glyph" });
594
+ if (params.kind === 'deck')
595
+ return _jsx(Icon, { name: "grid", className: "bt-favicon bt-favicon--glyph bt-favicon--deck" });
596
+ if (params.loading)
597
+ return _jsx("span", { className: "bt-favicon bt-spinner", role: "img", "aria-label": t('wb.common.loading') });
598
+ if (params.favicon && !broken)
599
+ return _jsx("img", { className: "bt-favicon bt-favicon--img", src: params.favicon, alt: "", onError: () => setBroken(true) }, params.favicon);
600
+ return _jsx(Icon, { name: "globe", className: "bt-favicon bt-favicon--glyph" });
601
+ }
602
+ function Tab(props) {
603
+ const t = useT();
604
+ const profiles = useProfiles();
605
+ const [title, setTitle] = useState(props.api.title ?? '');
606
+ useEffect(() => { const sub = props.api.onDidTitleChange(event => setTitle(event.title)); return () => sub.dispose(); }, [props.api]);
607
+ const params = props.params;
608
+ const panel = shell.api?.getPanel(props.api.id);
609
+ const onContextMenu = (event) => { event.preventDefault(); if (panel)
610
+ openMenu({ x: event.clientX, y: event.clientY, panel }); };
611
+ const account = params.kind !== 'panel' && params.kind !== 'deck' && profiles.profiles.length > 1 ? tabProfile(profiles, params.profile) : null;
612
+ const accountName = account ? `${accountLabel(account).title}${account.account ? ` (${site().accounts.handlePrefix}${account.account.handle})` : ''}` : '';
613
+ const loading = params.kind === 'web' && !!params.loading;
614
+ // Titles saved by older sessions (or before the page reports one) said "New tab" in English.
615
+ const shown = params.kind === 'start' || !title || title === 'New tab' ? t('wb.shell.tab.new') : title;
616
+ return _jsxs("div", { className: `bt-tab${params.pinned ? ' pinned bt-tab--pinned' : ''}${loading ? ' bt-tab--loading' : ''}`, "data-panel-id": props.api.id, title: account ? `${shown} · ${accountName}` : shown, onContextMenu: onContextMenu, onAuxClick: event => { if (event.button === 1 && panel) {
617
+ event.preventDefault();
618
+ closeTab(panel);
619
+ } }, children: [_jsxs("span", { className: "bt-tab-icon", children: [_jsx(Favicon, { params: params }), account && _jsx("span", { className: "bt-tab-account", style: { background: account.color }, "aria-label": t('wb.shell.tab.account', { name: accountName }) })] }), !params.pinned && _jsx("span", { className: "bt-tab-title", children: shown }), params.kind === 'deck' && _jsx(DeckTabBadge, { deckId: params.deckId }), !params.pinned && _jsx("button", { className: "bt-tab-close", title: t('wb.shell.tab.closeTitle'), "aria-label": t('wb.shell.menu.close'), onPointerDown: event => event.stopPropagation(), onClick: event => { event.stopPropagation(); if (panel)
620
+ closeTab(panel); }, children: _jsx(Icon, { name: "close" }) })] });
621
+ }
622
+ function GroupActions(props) {
623
+ const t = useT();
624
+ return _jsx("div", { className: "bt-group-actions", children: _jsx("button", { className: "bt-icon bt-newtab", title: t('wb.shell.tab.newTitle'), "aria-label": t('wb.shell.tab.new'), onClick: () => addStart(undefined, { referencePanel: props.group.activePanel?.id ?? '', direction: 'within' }), children: _jsx(Icon, { name: "plus" }) }) });
625
+ }
626
+ function AppPanel(props) {
627
+ const section = props.params.section ? { name: props.params.section, nonce: props.params.nonce ?? 0 } : undefined;
628
+ const Panel = site().panel.component;
629
+ return _jsx("div", { className: "bt-panel", children: _jsx(Panel, { section: section }) });
630
+ }
631
+ function StartPanel(props) {
632
+ return _jsx(NewTabPage, { panelId: props.api.id, profile: props.params.profile, onProfile: profile => { props.api.updateParameters({ profile }); saveLayoutSoon(); } });
633
+ }
634
+ /** Every tab closed: the dock shows the new-tab page itself. */
635
+ function Watermark() {
636
+ return _jsx(NewTabPage, {});
637
+ }
638
+ const components = { web: WebPanel, panel: AppPanel, start: StartPanel, deck: DeckView };
639
+ /**
640
+ * Groups whose top edge touches the dock top host their tabs in the titlebar row: their header is
641
+ * TITLEBAR_H tall and transparent, the empty part drags the window. The leftmost one leaves room
642
+ * for the logo, the rightmost one for the caption buttons.
643
+ */
644
+ function markTitlebarGroups() {
645
+ const api = shell.api;
646
+ const dock = document.querySelector('.bt-dock');
647
+ if (!api || !dock)
648
+ return;
649
+ const bounds = dock.getBoundingClientRect();
650
+ document.querySelector('.bt-shell')?.classList.toggle('bt-shell--empty', api.groups.length === 0);
651
+ for (const group of api.groups) {
652
+ const el = group.element;
653
+ const rect = el.getBoundingClientRect();
654
+ const top = group.api.location.type === 'grid' && Math.abs(rect.top - bounds.top) < 1;
655
+ el.classList.toggle('bt-group--in-titlebar', top);
656
+ el.classList.toggle('bt-group--tb-left', top && Math.abs(rect.left - bounds.left) < 1);
657
+ el.classList.toggle('bt-group--tb-right', top && Math.abs(rect.right - bounds.right) < 1);
658
+ const voidContainer = el.querySelector('.dv-void-container');
659
+ if (voidContainer) {
660
+ if (top)
661
+ voidContainer.setAttribute('data-tauri-drag-region', '');
662
+ else
663
+ voidContainer.removeAttribute('data-tauri-drag-region');
664
+ }
665
+ }
666
+ }
667
+ export default function Shell() {
668
+ const contentReadyTabs = useRef(new Set());
669
+ const [menu, setMenu] = useState(null);
670
+ const [maximized, setMaximized] = useState(false);
671
+ const [palette, setPalette] = useState(null);
672
+ const [media, setMedia] = useState(false);
673
+ const { overlays = [] } = site();
674
+ openMenu = setMenu;
675
+ togglePalette = () => { invoke('shell_focus').catch(() => { }); void refreshPluginNames(); setPalette(open => open ? null : paletteContext()); };
676
+ toggleMedia = () => { if (isTauri && site().media)
677
+ setMedia(open => !open); };
678
+ shell.split = (id, direction) => { const panel = shell.api?.getPanel(id); if (panel)
679
+ splitTab(panel, direction); };
680
+ shell.openPanel = from => openPanel(undefined, from);
681
+ shell.newTab = profile => { addStart(profile); };
682
+ shell.openFromStart = openFromStart;
683
+ shell.openAccount = openAccount;
684
+ shell.openUrl = (url, profile) => { addWeb(url, undefined, true, profile); };
685
+ shell.openPanelSection = section => openPanel(section, shell.api?.activePanel?.id);
686
+ shell.shortcut = name => runShortcut(name);
687
+ shell.run = runPalette;
688
+ useEffect(() => {
689
+ document.documentElement.dataset.os = os;
690
+ if (!isTauri)
691
+ return;
692
+ const unlisten = onMaximizedChange(value => setMaximized(value));
693
+ return () => { unlisten.then(dispose => dispose()).catch(() => { }); };
694
+ }, []);
695
+ // Maximize/restore changes the resize-band inset: re-send the tab rectangles once laid out.
696
+ useEffect(() => { requestAnimationFrame(() => { markTitlebarGroups(); scheduleSync(); }); }, [maximized]);
697
+ // Plugin palette commands, developer mode (live reload) and encrypted folder sync.
698
+ useEffect(() => {
699
+ if (!isTauri)
700
+ return;
701
+ const off = [
702
+ hasFeature('pluginCommands') ? installPluginCommands() : null,
703
+ hasFeature('developer') ? installDevHost() : null,
704
+ hasFeature('sync') ? installSyncHost() : null,
705
+ ].filter((dispose) => !!dispose);
706
+ return () => off.forEach(dispose => dispose());
707
+ }, []);
708
+ // Safe mode for this run (the host's flag): the titlebar says so and the chrome drops its theme.
709
+ useEffect(() => (hasFeature('safeMode') ? installSafeMode() : undefined), []);
710
+ // The site's own settings and events (e.g. X's Twitter/X wording).
711
+ useEffect(() => site().start?.(), []);
712
+ // Focus and layering diagnostics, when the app was started with them (`--diag`).
713
+ useEffect(() => { void initDiag(); }, []);
714
+ useEffect(() => {
715
+ const unlisten = [
716
+ // A tab saw its account sign in or out (native code already saved it): refresh the switcher.
717
+ listen('profiles-changed', ({ payload }) => setProfiles(payload)),
718
+ listen('tab-state', ({ payload }) => {
719
+ const panel = shell.api?.getPanel(payload.id);
720
+ const previousUrl = panel ? panel.params.url : decks.runtime.get(payload.id)?.url;
721
+ if (payload.loading || (payload.url && previousUrl && payload.url !== previousUrl))
722
+ contentReadyTabs.current.delete(payload.id);
723
+ if (payload.contentReady)
724
+ contentReadyTabs.current.add(payload.id);
725
+ const notifyStartup = () => window.dispatchEvent(new Event('bt-tab-startup-state'));
726
+ if (!panel) {
727
+ // A deck column (or a deck's compose dock) reports about its own webview.
728
+ const found = findColumnTab(payload.id);
729
+ if (!found) {
730
+ notifyStartup();
731
+ return;
732
+ }
733
+ setRuntime(payload.id, { url: payload.url, title: payload.title, loading: payload.loading, unread: payload.unread });
734
+ if (found.compose)
735
+ site().deck?.compose?.navigated?.(payload.id, payload.url, payload.loading);
736
+ if (payload.focused && found.column && decks.focus.get(found.deck.id) !== found.column.id) {
737
+ decks.focus.set(found.deck.id, found.column.id);
738
+ window.dispatchEvent(new CustomEvent('bt-decks'));
739
+ }
740
+ notifyStartup();
741
+ return;
742
+ }
743
+ // Another site must not keep the previous one's icon until (or unless) it reports its own.
744
+ const favicon = payload.favicon ?? (payload.url && originOf(payload.url) !== originOf(panel.params.url) ? '' : undefined);
745
+ patchParams(panel, { url: payload.url, loading: payload.loading, favicon, title: payload.title });
746
+ if (payload.title !== undefined)
747
+ panel.api.setTitle(payload.title || panel.params.url);
748
+ // A click into a visible pane makes it the active tab. A tab that is no longer shown (a link it
749
+ // opened just took its place in the group) reports a late focus too: it must not jump back.
750
+ if (payload.focused && !panel.api.isActive && panel.api.isVisible)
751
+ panel.api.setActive();
752
+ saveLayoutSoon();
753
+ notifyStartup();
754
+ }),
755
+ listen('tab-open', ({ payload }) => {
756
+ // Links opened from a tab stay in that tab's account; they never jump to another login.
757
+ const source = payload.from ? shell.api?.getPanel(payload.from) : undefined;
758
+ const column = payload.from && !source ? findColumnTab(payload.from) : null;
759
+ const profile = source && isWeb(source) ? source.params.profile
760
+ : column ? (column.column?.profile ?? decks.compose.get(column.deck.id)?.profile ?? shell.profiles.active) : shell.profiles.active;
761
+ if (column) {
762
+ addWeb(payload.url, undefined, !payload.background, profile);
763
+ return;
764
+ }
765
+ if (payload.beside && source) {
766
+ openBeside(payload.url, source, !payload.background, profile);
767
+ return;
768
+ }
769
+ addWeb(payload.url, payload.from ? { referencePanel: payload.from, direction: 'within' } : undefined, !payload.background, profile);
770
+ }),
771
+ // Native code builds every tab webview anew (safe mode switched): it needs the layout again.
772
+ listen('tabs-rebuild', () => resync()),
773
+ listen('tab-shortcut', ({ payload }) => runShortcut(payload.shortcut, payload.id)),
774
+ listen('open-panel', ({ payload }) => openPanel(payload.section, payload.from)),
775
+ // A tab's link drag: `start` carries the natively validated link, `enter` is an external drag.
776
+ listen('link-drag', ({ payload }) => {
777
+ if (payload.phase === 'end')
778
+ endLinkDrag();
779
+ else if (shell.api?.getPanel(payload.id))
780
+ beginLinkDrag(payload.id, payload.url);
781
+ }),
782
+ ];
783
+ const onKey = (event) => {
784
+ const shortcut = shortcutFor(event);
785
+ if (shortcut) {
786
+ // Leave text editing shortcuts (e.g. Ctrl+L/T in inputs) alone except tab management.
787
+ event.preventDefault();
788
+ runShortcut(shortcut);
789
+ return;
790
+ }
791
+ if (!isTauri)
792
+ return;
793
+ // Browser-engine keys that make no sense in the shell itself (the tab webviews handle their own).
794
+ const mod = event.ctrlKey || event.metaKey;
795
+ const key = event.key.toLowerCase();
796
+ if (event.key === 'F5') {
797
+ event.preventDefault();
798
+ runShortcut('reload');
799
+ return;
800
+ }
801
+ if (mod && !event.altKey && (key === 'p' || key === 'f')) {
802
+ event.preventDefault();
803
+ return;
804
+ }
805
+ if (mod && event.shiftKey && key === 'i' && !devBuild)
806
+ event.preventDefault();
807
+ };
808
+ const onContextMenu = (event) => {
809
+ const target = event.target;
810
+ if (target?.closest?.('input, textarea'))
811
+ return;
812
+ if (frameless && target?.closest?.('.bt-group--in-titlebar .dv-void-container')) {
813
+ titlebarContextMenu(event);
814
+ return;
815
+ }
816
+ event.preventDefault();
817
+ };
818
+ const onMouseUp = (event) => {
819
+ if (event.button !== 3 && event.button !== 4)
820
+ return;
821
+ event.preventDefault();
822
+ runShortcut(event.button === 3 ? 'back' : 'forward');
823
+ };
824
+ // The app's settings ask for a deck tab.
825
+ const onDeckOpen = (event) => openDeck(event.detail);
826
+ window.addEventListener('bt-deck-open', onDeckOpen);
827
+ if (isTauri) {
828
+ window.addEventListener('contextmenu', onContextMenu);
829
+ window.addEventListener('mouseup', onMouseUp);
830
+ }
831
+ const relayout = () => { markTitlebarGroups(); scheduleSync(); };
832
+ // Native webviews cover drop zones; hide them for the duration of a tab drag.
833
+ let dragging = false;
834
+ const dragStart = () => { linkDrag.internal = true; endLinkDrag(); if (!dragging) {
835
+ dragging = true;
836
+ setOverlay(1);
837
+ } };
838
+ const dragEnd = () => { linkDrag.internal = false; if (dragging) {
839
+ dragging = false;
840
+ setOverlay(-1);
841
+ } };
842
+ // A link dragged in from another app straight onto the shell chrome.
843
+ const dragEnter = (event) => { if (!linkDrag.internal && carriesLink(dragTypes(event)))
844
+ beginLinkDrag(); };
845
+ const dragOver = (event) => {
846
+ if (!linkDrag.active)
847
+ return;
848
+ linkDrag.lastOver = Date.now();
849
+ linkDrag.sawOver = true;
850
+ // Anywhere outside dockview's zones (e.g. the titlebar of an empty dock) still accepts: new tab.
851
+ event.preventDefault();
852
+ if (event.dataTransfer)
853
+ event.dataTransfer.dropEffect = 'copy';
854
+ };
855
+ // Dockview stops propagation of drops it handled; this only sees drops outside its zones.
856
+ const drop = (event) => { if (linkDrag.active) {
857
+ event.preventDefault();
858
+ dropLink(event, undefined);
859
+ } };
860
+ window.addEventListener('keydown', onKey);
861
+ window.addEventListener('resize', relayout);
862
+ // `resize` fires before dockview has laid its groups out at the new size (its own
863
+ // ResizeObserver does that), so the titlebar marks read stale rects there: the group at the
864
+ // right edge lost `bt-group--tb-right` and its tabs ran under the titlebar buttons. The dock's
865
+ // size changes are marked again one frame later, once dockview's own observer has laid out
866
+ // (measured live: marking inside the same observer pass still read the old rects).
867
+ const dockElement = document.querySelector('.bt-dock');
868
+ let markFrame = 0;
869
+ const dockResize = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => {
870
+ cancelAnimationFrame(markFrame);
871
+ markFrame = requestAnimationFrame(markTitlebarGroups);
872
+ });
873
+ if (dockElement)
874
+ dockResize?.observe(dockElement);
875
+ document.addEventListener('dragstart', dragStart, true);
876
+ document.addEventListener('dragend', dragEnd, true);
877
+ document.addEventListener('drop', dragEnd, true);
878
+ document.addEventListener('dragenter', dragEnter, true);
879
+ document.addEventListener('dragover', dragOver);
880
+ document.addEventListener('drop', drop);
881
+ return () => {
882
+ endLinkDrag();
883
+ unlisten.forEach(promise => promise.then(dispose => dispose(), () => { }));
884
+ window.removeEventListener('keydown', onKey);
885
+ window.removeEventListener('resize', relayout);
886
+ dockResize?.disconnect();
887
+ cancelAnimationFrame(markFrame);
888
+ window.removeEventListener('bt-deck-open', onDeckOpen);
889
+ window.removeEventListener('contextmenu', onContextMenu);
890
+ window.removeEventListener('mouseup', onMouseUp);
891
+ document.removeEventListener('dragstart', dragStart, true);
892
+ document.removeEventListener('dragend', dragEnd, true);
893
+ document.removeEventListener('drop', dragEnd, true);
894
+ document.removeEventListener('dragenter', dragEnter, true);
895
+ document.removeEventListener('dragover', dragOver);
896
+ document.removeEventListener('drop', drop);
897
+ };
898
+ }, []);
899
+ const onReady = async (event) => {
900
+ const api = event.api;
901
+ // Establish the cover before any restore-triggered sync creates a visible native tab.
902
+ // This also keeps the no-raise path from exposing X while a late still is captured.
903
+ let coverActive = isTauri && !!site().deferWindowShow;
904
+ let coverWatchdog;
905
+ const releaseStartupCover = () => {
906
+ if (!coverActive)
907
+ return;
908
+ coverActive = false;
909
+ clearTimeout(coverWatchdog);
910
+ setOverlay(-1);
911
+ document.documentElement.classList.remove('bt-startup-cover');
912
+ };
913
+ if (coverActive) {
914
+ document.documentElement.classList.add('bt-startup-cover');
915
+ setOverlay(1);
916
+ // Guard shell initialization if settings or native startup never complete.
917
+ // Once shell_ready succeeds, semantic X readiness owns this cover.
918
+ coverWatchdog = setTimeout(releaseStartupCover, 10_000);
919
+ }
920
+ shell.api = api;
921
+ // Chrome colors and fonts follow the theme; panes with their own theme follow theirs.
922
+ site().themes?.attachDock(api);
923
+ let restoreTabs = true;
924
+ try {
925
+ const settings = await invoke('settings_get');
926
+ shell.homeUrl = settings.homeUrl;
927
+ restoreTabs = settings.restoreTabs;
928
+ }
929
+ catch { /* Defaults. */ }
930
+ // Profiles before the first tabs_sync, so restored tabs open in their own account right away.
931
+ try {
932
+ setProfiles(await invoke('profiles_get'));
933
+ }
934
+ catch { /* Default profile only. */ }
935
+ loadDecks(shell.profiles.profiles.map(profile => profile.id), shell.profiles.active);
936
+ // A workspace chosen to open on start replaces the previous session's layout.
937
+ const startWorkspace = isTauri && hasFeature('workspaces') ? (await loadWorkspaces()).default : null;
938
+ const saved = startWorkspace ? null : loadLayout();
939
+ if (startWorkspace)
940
+ await restoreWorkspace(startWorkspace);
941
+ shell.restoring = true;
942
+ try {
943
+ if (saved) {
944
+ api.fromJSON(saved);
945
+ // Layouts saved before a rename still carry the old panel title.
946
+ api.getPanel(panelId())?.api.setTitle(site().panel.title);
947
+ for (const panel of [...api.panels]) {
948
+ const params = panel.params;
949
+ if (params.kind === 'web')
950
+ patchParams(panel, { loading: false });
951
+ // Without session restore only pinned tabs come back.
952
+ if (!restoreTabs && !params.pinned)
953
+ panel.api.close();
954
+ }
955
+ }
956
+ }
957
+ catch (error) {
958
+ console.warn('[shell] layout restore failed', error);
959
+ api.clear();
960
+ }
961
+ finally {
962
+ shell.restoring = false;
963
+ }
964
+ // A restored deck tab counts: deck sessions come back to their deck, not a new Home tab.
965
+ if (!api.panels.some(panel => isWeb(panel) || panel.params?.kind === 'deck'))
966
+ addWeb(shell.homeUrl);
967
+ api.onDidLayoutChange(() => { markTitlebarGroups(); scheduleSync(); saveLayoutSoon(); });
968
+ api.onDidAddGroup(() => requestAnimationFrame(markTitlebarGroups));
969
+ api.onDidRemoveGroup(() => requestAnimationFrame(markTitlebarGroups));
970
+ // The titlebar switcher highlights the focused pane's account.
971
+ const paneChanged = () => window.dispatchEvent(new CustomEvent('bt-active-pane'));
972
+ api.onDidActivePanelChange(event => {
973
+ if (event && event.params?.kind !== 'deck')
974
+ lastNonDeck = event.id;
975
+ if (event && isWeb(event))
976
+ lastWeb = event.id;
977
+ notePanelActive(event?.id);
978
+ scheduleSync();
979
+ paneChanged();
980
+ });
981
+ // A renamed deck renames its tab.
982
+ window.addEventListener('bt-decks', () => {
983
+ for (const panel of api.panels) {
984
+ const params = panel.params;
985
+ if (params.kind !== 'deck')
986
+ continue;
987
+ const name = deckById(params.deckId)?.name;
988
+ if (name && panel.api.title !== name)
989
+ panel.api.setTitle(name);
990
+ }
991
+ });
992
+ api.onDidLayoutChange(paneChanged);
993
+ paneChanged();
994
+ api.onDidRemovePanel(scheduleSync);
995
+ api.onDidAddPanel(scheduleSync);
996
+ // Dockview's overflow list (the "N more tabs" button) is a shell popover: hide the tab webviews while it is open.
997
+ const popover = document.querySelector('.bt-dock .dv-popover-anchor');
998
+ if (popover) {
999
+ let shown = false;
1000
+ new MutationObserver(() => {
1001
+ const open = popover.childElementCount > 0;
1002
+ if (open !== shown) {
1003
+ shown = open;
1004
+ setOverlay(open ? 1 : -1);
1005
+ }
1006
+ }).observe(popover, { childList: true });
1007
+ }
1008
+ // External (non-panel) drags that carry a link get dockview's split/tab drop zones.
1009
+ api.onUnhandledDragOverEvent(event => {
1010
+ if (!linkDrag.internal && (linkDrag.active || carriesLink(dragTypes(event.nativeEvent))))
1011
+ event.accept();
1012
+ });
1013
+ api.onDidDrop(event => {
1014
+ if (linkDrag.internal || !(linkDrag.active || carriesLink(dragTypes(event.nativeEvent))))
1015
+ return;
1016
+ const tabIndex = event.panel && event.group ? event.group.panels.indexOf(event.panel) : -1;
1017
+ dropLink(event.nativeEvent, placementFor({ position: event.position, groupId: event.group?.id, tabIndex: tabIndex >= 0 ? tabIndex : undefined }));
1018
+ });
1019
+ markTitlebarGroups();
1020
+ scheduleSync();
1021
+ if (isTauri && site().deferWindowShow) {
1022
+ // React commits dock placeholders after Dockview's ready callback. Keep the native
1023
+ // window hidden until their real bounds have been sent and its restored panes exist.
1024
+ for (let attempt = 0; attempt < 40; attempt++) {
1025
+ const visibleWeb = api.panels.filter(panel => isWeb(panel) && panel.api.isVisible);
1026
+ const webReady = visibleWeb.every(panel => {
1027
+ const element = shell.placeholders.get(panel.id);
1028
+ const rect = element?.getBoundingClientRect();
1029
+ return !!rect && rect.width > 1 && rect.height > 1;
1030
+ });
1031
+ // On the first run Dockview can report no visible panels for a frame after addPanel.
1032
+ // An empty `every()` must not reveal the native window before the loading shape mounts.
1033
+ const visibleDeck = api.panels.some(panel => panel.params?.kind === 'deck' && panel.api.isVisible);
1034
+ const deckReady = api.panels.every(panel => {
1035
+ const params = panel.params;
1036
+ if (params?.kind !== 'deck' || !panel.api.isVisible)
1037
+ return true;
1038
+ const deck = deckById(params.deckId);
1039
+ if (!deck)
1040
+ return true;
1041
+ return deck.columns.every(column => decks.ready.has(column.tabId)) &&
1042
+ (deck.columns.length === 0 || deck.columns.some(column => {
1043
+ const element = shell.placeholders.get(column.tabId);
1044
+ const rect = element?.isConnected ? element.getBoundingClientRect() : null;
1045
+ return !!rect && rect.width > 1 && rect.height > 1;
1046
+ }));
1047
+ });
1048
+ if ((visibleWeb.length > 0 || visibleDeck) && webReady && deckReady)
1049
+ break;
1050
+ await new Promise(resolve => setTimeout(resolve, 50));
1051
+ }
1052
+ const origin = new URL(site().homeUrl).origin;
1053
+ const matchesSite = (url) => {
1054
+ try {
1055
+ return !!url && new URL(url).origin === origin;
1056
+ }
1057
+ catch {
1058
+ return false;
1059
+ }
1060
+ };
1061
+ const visibleSiteIds = () => {
1062
+ const ids = new Set();
1063
+ for (const panel of api.panels.filter(panel => panel.api.isVisible)) {
1064
+ const params = panel.params;
1065
+ if (params.kind === 'web' && matchesSite(params.url))
1066
+ ids.add(panel.id);
1067
+ if (params.kind === 'deck') {
1068
+ for (const column of deckById(params.deckId)?.columns ?? []) {
1069
+ const rect = shell.placeholders.get(column.tabId)?.getBoundingClientRect();
1070
+ const url = decks.runtime.get(column.tabId)?.url ?? columnSourceUrl(column.source);
1071
+ if (decks.ready.has(column.tabId) && rect && rect.width > 1 && rect.height > 1 && matchesSite(url)) {
1072
+ ids.add(column.tabId);
1073
+ }
1074
+ }
1075
+ }
1076
+ }
1077
+ return ids;
1078
+ };
1079
+ const coverIds = visibleSiteIds();
1080
+ if (!coverIds.size)
1081
+ releaseStartupCover();
1082
+ await flushLayer();
1083
+ try {
1084
+ await flushSync();
1085
+ // Give the shell webview two paint opportunities after its placeholder DOM is committed.
1086
+ await new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve())));
1087
+ await invoke('shell_ready');
1088
+ // The global watchdog protects initialization. Once shown, the visible X tabs
1089
+ // keep the shell cover until content is ready or the tab is no longer visible.
1090
+ clearTimeout(coverWatchdog);
1091
+ if (coverIds.size && coverActive) {
1092
+ // Keep the shell's loading shape over the native tab until X has a real page.
1093
+ // The toolbar remains usable to reload a slow or offline page.
1094
+ await new Promise(resolve => {
1095
+ const done = () => {
1096
+ const visible = visibleSiteIds();
1097
+ return [...coverIds].every(id => !visible.has(id) || contentReadyTabs.current.has(id));
1098
+ };
1099
+ if (done()) {
1100
+ resolve();
1101
+ return;
1102
+ }
1103
+ const check = () => { if (done())
1104
+ finish(); };
1105
+ const finish = () => {
1106
+ clearInterval(poll);
1107
+ clearTimeout(limit);
1108
+ window.removeEventListener('bt-tab-startup-state', check);
1109
+ resolve();
1110
+ };
1111
+ // Dockview closes and deck layout changes do not always emit tab-state.
1112
+ const poll = setInterval(check, 200);
1113
+ // Never longer than this: a page that never says it painted (its script failed, the
1114
+ // engine held its frames back) shows as it is rather than behind a cover for good.
1115
+ const limit = setTimeout(finish, STARTUP_CONTENT_WAIT);
1116
+ window.addEventListener('bt-tab-startup-state', check);
1117
+ });
1118
+ // The X DOM signal can precede its separate native WebView surface by a frame.
1119
+ // Give that surface one compositor turn before exposing it under the shell.
1120
+ await new Promise(resolve => setTimeout(resolve, 100));
1121
+ }
1122
+ }
1123
+ finally {
1124
+ releaseStartupCover();
1125
+ }
1126
+ }
1127
+ };
1128
+ const chrome = frameless ? (maximized ? 'maximized' : 'restored') : 'native';
1129
+ return _jsxs("div", { className: "bt-shell", "data-chrome": chrome, children: [_jsx(Titlebar, { maximized: maximized }), _jsx(DockviewReact, { className: "bt-dock", theme: themeDark, components: components, defaultTabComponent: Tab, rightHeaderActionsComponent: GroupActions, watermarkComponent: Watermark, onReady: event => void onReady(event) }), palette && _jsx(CommandPalette, { context: palette, onRun: runPalette, onClose: () => setPalette(null) }), overlays.map((Overlay, index) => _jsx(Overlay, {}, index)), media && _jsx(MediaPanel, { onClose: () => setMedia(false) }), menu && _jsx(ContextMenu, { menu: menu, onClose: () => setMenu(null) }), _jsx(PaneThemeHost, {}), frameless && !maximized && _jsx(ResizeGrips, {})] });
1130
+ }
1131
+ //# sourceMappingURL=Shell.js.map