flea-qml 0.0.0-stage → 0.2.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 (637) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +511 -2
  3. package/THIRD-PARTY-NOTICES.md +29 -0
  4. package/demos/App.java +5 -0
  5. package/demos/Dockerfile +4 -0
  6. package/demos/Main.kt +3 -0
  7. package/demos/Program.cs +1 -0
  8. package/demos/README.md +12 -0
  9. package/demos/app.dart +3 -0
  10. package/demos/app.deb +0 -0
  11. package/demos/app.js +1 -0
  12. package/demos/backup.tar +0 -0
  13. package/demos/budget.xlsx +0 -0
  14. package/demos/build.sh +2 -0
  15. package/demos/bundle.zip +0 -0
  16. package/demos/clip.mp4 +0 -0
  17. package/demos/data.sqlite +0 -0
  18. package/demos/disk.iso +0 -0
  19. package/demos/engine.cpp +5 -0
  20. package/demos/firmware.bin +0 -0
  21. package/demos/flake.nix +4 -0
  22. package/demos/folder/inside.txt +1 -0
  23. package/demos/font.ttf +0 -0
  24. package/demos/index.html +5 -0
  25. package/demos/index.php +2 -0
  26. package/demos/init.lua +1 -0
  27. package/demos/letter.docx +0 -0
  28. package/demos/lib.rs +3 -0
  29. package/demos/main.c +6 -0
  30. package/demos/main.h +3 -0
  31. package/demos/math-and-diagrams.md +200 -0
  32. package/demos/notes.tar.gz +0 -0
  33. package/demos/notes.txt +3 -0
  34. package/demos/photo.png +0 -0
  35. package/demos/program +2 -0
  36. package/demos/report.pdf +32 -0
  37. package/demos/script.py +1 -0
  38. package/demos/server.go +7 -0
  39. package/demos/settings.json +4 -0
  40. package/demos/slides.pptx +0 -0
  41. package/demos/song.mp3 +0 -0
  42. package/demos/style.css +4 -0
  43. package/demos/tool.rb +1 -0
  44. package/demos/types.ts +2 -0
  45. package/docs/pele.md +23 -0
  46. package/docs/protocol.md +3420 -0
  47. package/docs/temml.md +63 -0
  48. package/index.js +107 -0
  49. package/package.json +34 -4
  50. package/src/archive.js +407 -0
  51. package/src/archiveops.js +728 -0
  52. package/src/backend.js +369 -0
  53. package/src/bulkrename.js +95 -0
  54. package/src/clip-host.js +76 -0
  55. package/src/clip.js +462 -0
  56. package/src/code.js +168 -0
  57. package/src/compare.js +74 -0
  58. package/src/create.js +128 -0
  59. package/src/dirsize.js +147 -0
  60. package/src/figure-svg.js +530 -0
  61. package/src/figure-thread.js +23 -0
  62. package/src/figure.js +132 -0
  63. package/src/find.js +55 -0
  64. package/src/flea.js +39 -0
  65. package/src/fsinfo.js +39 -0
  66. package/src/highlight.js +141 -0
  67. package/src/html.js +52 -0
  68. package/src/kinds.js +89 -0
  69. package/src/listing.js +130 -0
  70. package/src/markdown-html.js +317 -0
  71. package/src/mathml.js +186 -0
  72. package/src/menu.js +180 -0
  73. package/src/meta.js +135 -0
  74. package/src/office.js +810 -0
  75. package/src/order.js +100 -0
  76. package/src/peek.js +49 -0
  77. package/src/pele-wrapper.js +695 -0
  78. package/src/permissions.js +180 -0
  79. package/src/places.js +313 -0
  80. package/src/preview-language.js +10 -0
  81. package/src/terminal-server.js +95 -0
  82. package/src/terminal-shell.js +26 -0
  83. package/src/transfer.js +669 -0
  84. package/src/trash-record.js +41 -0
  85. package/src/trash.js +210 -0
  86. package/src/trashbrowse.js +327 -0
  87. package/src/uistate.js +393 -0
  88. package/src/uistore.js +168 -0
  89. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  90. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  91. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  92. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  93. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  94. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  95. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  96. package/src/vendor/pele.js +145 -0
  97. package/src/vendor/temml.mjs +14708 -0
  98. package/src/watch.js +98 -0
  99. package/src/wayland/client.js +109 -0
  100. package/src/wayland/protocol.js +55 -0
  101. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  102. package/src/wayland/protocols/wayland.xml +3248 -0
  103. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  104. package/src/wayland/resource-io.js +6 -0
  105. package/src/wayland/socket-discovery.js +26 -0
  106. package/src/wayland/transport-posix.js +120 -0
  107. package/src/wayland/transport-win32.js +11 -0
  108. package/src/wayland/transport.js +3 -0
  109. package/src/wayland/win32-resources.js +80 -0
  110. package/src/wayland/win32-wire.js +62 -0
  111. package/test/archive.test.js +80 -0
  112. package/test/bench-markdown-scroll.mjs +190 -0
  113. package/test/clip-host.test.js +113 -0
  114. package/test/code-preview.test.js +35 -0
  115. package/test/compare-ui-paths.test.js +24 -0
  116. package/test/compare.test.js +54 -0
  117. package/test/delete-trash.test.js +67 -0
  118. package/test/dirsize.test.js +138 -0
  119. package/test/file-ops.test.js +307 -0
  120. package/test/file-url.test.js +24 -0
  121. package/test/find-contents.test.js +161 -0
  122. package/test/find-input.test.js +66 -0
  123. package/test/fixtures/pele-parity.json +1006 -0
  124. package/test/highlight-launcher.test.js +35 -0
  125. package/test/html-preview.test.js +22 -0
  126. package/test/input.test.js +15 -0
  127. package/test/keymap.test.js +26 -0
  128. package/test/language-icons.test.js +36 -0
  129. package/test/launcher.test.js +51 -0
  130. package/test/manual-archive-here-ui.mjs +47 -0
  131. package/test/manual-code-scroll.mjs +28 -0
  132. package/test/manual-column-scroll.mjs +24 -0
  133. package/test/manual-compare-ui.mjs +20 -0
  134. package/test/manual-find-status.mjs +37 -0
  135. package/test/manual-folder-focus.mjs +63 -0
  136. package/test/manual-html-security.mjs +23 -0
  137. package/test/manual-html-ui.mjs +30 -0
  138. package/test/manual-syntax-preview.mjs +26 -0
  139. package/test/manual-terminal-lifecycle.mjs +42 -0
  140. package/test/manual-terminal-navigation.mjs +48 -0
  141. package/test/manual-terminal-ui.mjs +47 -0
  142. package/test/manual-trash-ui.mjs +86 -0
  143. package/test/markdown-diagrams.test.js +93 -0
  144. package/test/markdown-html.test.js +105 -0
  145. package/test/mathml.test.js +92 -0
  146. package/test/menu-transfer.test.js +57 -0
  147. package/test/office-preview.test.js +154 -0
  148. package/test/pele-parity.test.js +28 -0
  149. package/test/places.test.js +134 -0
  150. package/test/preview-keys.test.js +60 -0
  151. package/test/preview-language.test.js +22 -0
  152. package/test/status-filename.test.js +20 -0
  153. package/test/transfer-replace-merge.test.js +187 -0
  154. package/test/trash-address.test.js +23 -0
  155. package/test/trash-view.test.js +37 -0
  156. package/test/trashbrowse.test.js +135 -0
  157. package/ui/AboutFacts.qml +78 -0
  158. package/ui/ArchiveCards.qml +95 -0
  159. package/ui/ArchiveCompressDialog.qml +378 -0
  160. package/ui/ArchiveHereConfirm.qml +127 -0
  161. package/ui/ArchivePasswordDialog.qml +257 -0
  162. package/ui/Backend.qml +775 -0
  163. package/ui/BulkOldNames.qml +47 -0
  164. package/ui/BulkRenameDialog.qml +380 -0
  165. package/ui/CardFrame.qml +67 -0
  166. package/ui/CardScroll.qml +79 -0
  167. package/ui/CheckBox.qml +73 -0
  168. package/ui/ChromeBar.qml +360 -0
  169. package/ui/ChromeButton.qml +131 -0
  170. package/ui/ChromeModes.qml +43 -0
  171. package/ui/CodeLines.qml +256 -0
  172. package/ui/CodeRefusal.qml +52 -0
  173. package/ui/CollideConfirm.qml +147 -0
  174. package/ui/CollideHost.qml +167 -0
  175. package/ui/ColumnPane.qml +525 -0
  176. package/ui/ColumnRow.qml +380 -0
  177. package/ui/ColumnsArea.qml +594 -0
  178. package/ui/ColumnsLanding.qml +147 -0
  179. package/ui/CompareView.qml +321 -0
  180. package/ui/ContextMenu.qml +743 -0
  181. package/ui/ConvertDialog.qml +456 -0
  182. package/ui/CountedWorker.qml +7 -0
  183. package/ui/Crumb.qml +64 -0
  184. package/ui/CrumbDrop.qml +25 -0
  185. package/ui/CsvTable.qml +394 -0
  186. package/ui/CursorMark.qml +41 -0
  187. package/ui/DefaultClaim.qml +130 -0
  188. package/ui/DeviceLock.qml +47 -0
  189. package/ui/DeviceMounts.qml +540 -0
  190. package/ui/DialogButton.qml +150 -0
  191. package/ui/DialogField.qml +147 -0
  192. package/ui/DialogHeader.qml +31 -0
  193. package/ui/DialogScrim.qml +20 -0
  194. package/ui/DialogTitle.qml +37 -0
  195. package/ui/Divider.qml +9 -0
  196. package/ui/DropInto.qml +126 -0
  197. package/ui/DropTarget.qml +35 -0
  198. package/ui/DropboxMark.qml +60 -0
  199. package/ui/EmptyState.qml +150 -0
  200. package/ui/EscapeHint.qml +11 -0
  201. package/ui/FactsTable.qml +54 -0
  202. package/ui/FastScrollHandler.qml +476 -0
  203. package/ui/FatNamesConfirm.qml +157 -0
  204. package/ui/Favourites.qml +143 -0
  205. package/ui/FigureService.qml +120 -0
  206. package/ui/FileDrag.qml +267 -0
  207. package/ui/FilterStrip.qml +133 -0
  208. package/ui/FitMetrics.qml +22 -0
  209. package/ui/FlashDriveRows.qml +137 -0
  210. package/ui/FlashHost.qml +72 -0
  211. package/ui/FlashImageRow.qml +104 -0
  212. package/ui/FlashRunner.qml +119 -0
  213. package/ui/FlashStepRow.qml +172 -0
  214. package/ui/FlashStrip.qml +38 -0
  215. package/ui/FlashView.qml +397 -0
  216. package/ui/FleaMark.qml +88 -0
  217. package/ui/FlyoutFade.qml +23 -0
  218. package/ui/FontPane.qml +170 -0
  219. package/ui/Glass.qml +114 -0
  220. package/ui/GlassTagger.qml +59 -0
  221. package/ui/Glyph.qml +64 -0
  222. package/ui/GlyphSolid.qml +22 -0
  223. package/ui/GridArea.qml +664 -0
  224. package/ui/GridTile.qml +325 -0
  225. package/ui/GvfsBridge.qml +164 -0
  226. package/ui/Header.qml +433 -0
  227. package/ui/HoverTimers.qml +39 -0
  228. package/ui/HtmlClip.qml +30 -0
  229. package/ui/HtmlPane.qml +63 -0
  230. package/ui/HyprlandMark.qml +45 -0
  231. package/ui/Ipc.qml +1005 -0
  232. package/ui/IpcControls.qml +7 -0
  233. package/ui/IpcMenuState.qml +48 -0
  234. package/ui/IpcPreviewState.qml +185 -0
  235. package/ui/JumpPath.qml +51 -0
  236. package/ui/KeymapSheet.qml +561 -0
  237. package/ui/KeymapSheetResult.qml +89 -0
  238. package/ui/KindStack.qml +40 -0
  239. package/ui/LangMark.qml +51 -0
  240. package/ui/LangMarkLoader.qml +26 -0
  241. package/ui/List.qml +480 -0
  242. package/ui/LoadingState.qml +54 -0
  243. package/ui/LocalSend.qml +56 -0
  244. package/ui/LocalSendMark.qml +73 -0
  245. package/ui/MarkdownBlockView.qml +311 -0
  246. package/ui/MarkdownBlocks.qml +35 -0
  247. package/ui/MarkdownFigure.qml +254 -0
  248. package/ui/MarkdownImages.qml +72 -0
  249. package/ui/MarkdownList.qml +81 -0
  250. package/ui/MarkdownMathsText.qml +97 -0
  251. package/ui/MarkdownPane.qml +83 -0
  252. package/ui/MarkdownPictures.qml +41 -0
  253. package/ui/MarkdownQuote.qml +52 -0
  254. package/ui/MarkdownRemote.qml +82 -0
  255. package/ui/MarkdownSource.qml +155 -0
  256. package/ui/MarkdownTable.qml +222 -0
  257. package/ui/MarkdownTableScroll.qml +64 -0
  258. package/ui/MarkdownTableWheel.qml +126 -0
  259. package/ui/MarkdownText.qml +87 -0
  260. package/ui/MarkdownWorker.js +132 -0
  261. package/ui/MatchText.qml +99 -0
  262. package/ui/MediaSession.qml +38 -0
  263. package/ui/MediaSound.qml +26 -0
  264. package/ui/MediaStrip.qml +191 -0
  265. package/ui/MediaTrack.qml +36 -0
  266. package/ui/MediaVolume.qml +54 -0
  267. package/ui/MenuActionDialog.qml +396 -0
  268. package/ui/MenuEdgeFade.qml +24 -0
  269. package/ui/MenuHost.qml +227 -0
  270. package/ui/MenuRow.qml +351 -0
  271. package/ui/MountListing.qml +100 -0
  272. package/ui/NetworkDialog.qml +435 -0
  273. package/ui/NetworkForm.qml +426 -0
  274. package/ui/NetworkMounts.qml +913 -0
  275. package/ui/NetworkPlaces.qml +127 -0
  276. package/ui/OpenWithDialog.qml +613 -0
  277. package/ui/Opener.qml +95 -0
  278. package/ui/Pane.qml +1320 -0
  279. package/ui/PaneMenuActions.qml +527 -0
  280. package/ui/PanePath.qml +90 -0
  281. package/ui/PaneRail.qml +268 -0
  282. package/ui/PaneStates.qml +88 -0
  283. package/ui/PaneSwap.qml +339 -0
  284. package/ui/PaneWire.qml +601 -0
  285. package/ui/ParkedWait.qml +9 -0
  286. package/ui/PathEntry.qml +186 -0
  287. package/ui/PathJump.qml +400 -0
  288. package/ui/PdfViewer.qml +297 -0
  289. package/ui/PermissionsDialog.qml +658 -0
  290. package/ui/PermissionsHeadings.qml +31 -0
  291. package/ui/PhoneMounts.qml +67 -0
  292. package/ui/PickerAnswers.qml +75 -0
  293. package/ui/PickerBox.qml +26 -0
  294. package/ui/PickerChrome.qml +295 -0
  295. package/ui/PickerFilter.qml +97 -0
  296. package/ui/PickerGrid.qml +308 -0
  297. package/ui/PickerHeader.qml +28 -0
  298. package/ui/PickerLifecycle.qml +27 -0
  299. package/ui/PickerList.qml +218 -0
  300. package/ui/PickerListing.qml +149 -0
  301. package/ui/PickerPathBar.qml +127 -0
  302. package/ui/PickerPlaces.qml +189 -0
  303. package/ui/PickerRecent.qml +92 -0
  304. package/ui/PickerSave.qml +101 -0
  305. package/ui/PickerSelection.qml +155 -0
  306. package/ui/PickerWindow.qml +846 -0
  307. package/ui/PowerSectorsReader.qml +10 -0
  308. package/ui/PrebuildDriver.qml +146 -0
  309. package/ui/Preview.qml +1005 -0
  310. package/ui/PreviewAnimation.qml +24 -0
  311. package/ui/PreviewArchive.qml +91 -0
  312. package/ui/PreviewColumn.qml +553 -0
  313. package/ui/PreviewEdge.qml +63 -0
  314. package/ui/PreviewFacts.qml +30 -0
  315. package/ui/PreviewFactsLine.qml +34 -0
  316. package/ui/PreviewHtml.qml +233 -0
  317. package/ui/PreviewImage.qml +253 -0
  318. package/ui/PreviewImagePan.qml +13 -0
  319. package/ui/PreviewImageSharp.qml +32 -0
  320. package/ui/PreviewLines.qml +106 -0
  321. package/ui/PreviewMarkdown.qml +706 -0
  322. package/ui/PreviewMedia.qml +250 -0
  323. package/ui/PreviewPdf.qml +482 -0
  324. package/ui/PreviewScroll.qml +201 -0
  325. package/ui/PreviewSwap.qml +335 -0
  326. package/ui/PreviewText.qml +149 -0
  327. package/ui/ProtocolChip.qml +18 -0
  328. package/ui/QuickLookArchive.qml +41 -0
  329. package/ui/QuickLookBar.qml +103 -0
  330. package/ui/QuickLookPrepare.qml +355 -0
  331. package/ui/QuickLookSwap.qml +10 -0
  332. package/ui/QuickLookWarm.qml +65 -0
  333. package/ui/RailDrop.qml +155 -0
  334. package/ui/RailHeading.qml +19 -0
  335. package/ui/RailPlaces.qml +38 -0
  336. package/ui/RailTrashRow.qml +67 -0
  337. package/ui/RenameField.qml +353 -0
  338. package/ui/RendererRetry.qml +11 -0
  339. package/ui/ResizeHandle.qml +52 -0
  340. package/ui/Reveal.qml +54 -0
  341. package/ui/Row.qml +476 -0
  342. package/ui/RowDrag.qml +213 -0
  343. package/ui/RowDropTarget.qml +27 -0
  344. package/ui/RunCard.qml +286 -0
  345. package/ui/Scripts.qml +65 -0
  346. package/ui/SearchStrip.qml +232 -0
  347. package/ui/SelectionBand.qml +140 -0
  348. package/ui/SelectionPreview.qml +370 -0
  349. package/ui/SettingsFavourite.qml +122 -0
  350. package/ui/SettingsGroup.qml +96 -0
  351. package/ui/SettingsHint.qml +12 -0
  352. package/ui/SettingsMetrics.qml +29 -0
  353. package/ui/SettingsPane.qml +178 -0
  354. package/ui/SettingsPanel.qml +547 -0
  355. package/ui/SettingsRail.qml +128 -0
  356. package/ui/SettingsRow.qml +409 -0
  357. package/ui/SettingsRuler.qml +85 -0
  358. package/ui/SettingsSegment.qml +85 -0
  359. package/ui/ShareBrowser.qml +113 -0
  360. package/ui/ShareLink.qml +67 -0
  361. package/ui/ShelfPins.qml +101 -0
  362. package/ui/Sidebar.qml +728 -0
  363. package/ui/SidebarRow.qml +358 -0
  364. package/ui/Spinner.qml +60 -0
  365. package/ui/StableModel.qml +89 -0
  366. package/ui/StateMessage.qml +84 -0
  367. package/ui/StatusBar.qml +512 -0
  368. package/ui/StatusFileName.qml +27 -0
  369. package/ui/TabBar.qml +673 -0
  370. package/ui/TabDragGeometry.qml +60 -0
  371. package/ui/TagMenuMark.qml +52 -0
  372. package/ui/TagRailSection.qml +53 -0
  373. package/ui/TagSlot.qml +40 -0
  374. package/ui/TagState.qml +150 -0
  375. package/ui/TagSwatch.qml +28 -0
  376. package/ui/Taildrop.qml +86 -0
  377. package/ui/TailscaleMark.qml +50 -0
  378. package/ui/TerminalService.qml +69 -0
  379. package/ui/TerminalTab.qml +48 -0
  380. package/ui/TerminalTabs.qml +27 -0
  381. package/ui/Theme.qml +460 -0
  382. package/ui/ThemeColors.qml +43 -0
  383. package/ui/TransferCard.qml +239 -0
  384. package/ui/TrashConfirm.qml +129 -0
  385. package/ui/TrashDropHandler.qml +11 -0
  386. package/ui/TrashHost.qml +202 -0
  387. package/ui/TrashMonitor.qml +30 -0
  388. package/ui/TrashToolbar.qml +19 -0
  389. package/ui/TrashView.qml +492 -0
  390. package/ui/UnlockDialog.qml +450 -0
  391. package/ui/UnlockHost.qml +206 -0
  392. package/ui/UpdateCheck.qml +97 -0
  393. package/ui/UserDirs.qml +25 -0
  394. package/ui/ViewState.qml +565 -0
  395. package/ui/ViewportScrollBar.qml +195 -0
  396. package/ui/ViewportScrollBars.qml +25 -0
  397. package/ui/WebPreview.qml +33 -0
  398. package/ui/WebPreviewView.qml +26 -0
  399. package/ui/WindowBody.qml +829 -0
  400. package/ui/WindowDrag.qml +35 -0
  401. package/ui/boot/Commons/AudioNodes.qml +50 -0
  402. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  403. package/ui/boot/Commons/Border.qml +243 -0
  404. package/ui/boot/Commons/BorderGeometry.js +373 -0
  405. package/ui/boot/Commons/Color.qml +258 -0
  406. package/ui/boot/Commons/I18n.qml +118 -0
  407. package/ui/boot/Commons/InputDevices.qml +112 -0
  408. package/ui/boot/Commons/InputMethodState.qml +38 -0
  409. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  410. package/ui/boot/Commons/Numbers.js +19 -0
  411. package/ui/boot/Commons/Plural.js +91 -0
  412. package/ui/boot/Commons/ShellIpc.qml +12 -0
  413. package/ui/boot/Commons/Style.qml +561 -0
  414. package/ui/boot/Commons/Translations.qml +100 -0
  415. package/ui/boot/Commons/UntypedInput.qml +152 -0
  416. package/ui/boot/Commons/Util.qml +188 -0
  417. package/ui/boot/Commons/qmldir +13 -0
  418. package/ui/boot/LICENSE-omarchy +20 -0
  419. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  420. package/ui/boot/Ui/BarIconButton.qml +106 -0
  421. package/ui/boot/Ui/BarIconContent.qml +38 -0
  422. package/ui/boot/Ui/BarIndicator.qml +53 -0
  423. package/ui/boot/Ui/BarWidget.qml +50 -0
  424. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  425. package/ui/boot/Ui/BorderSurface.qml +40 -0
  426. package/ui/boot/Ui/Button.qml +224 -0
  427. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  428. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  429. package/ui/boot/Ui/CursorSurface.qml +42 -0
  430. package/ui/boot/Ui/Dropdown.qml +245 -0
  431. package/ui/boot/Ui/FilterInput.qml +26 -0
  432. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  433. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  434. package/ui/boot/Ui/MultiSelect.qml +624 -0
  435. package/ui/boot/Ui/NumberField.qml +86 -0
  436. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  437. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  438. package/ui/boot/Ui/Panel.qml +60 -0
  439. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  440. package/ui/boot/Ui/PanelController.qml +16 -0
  441. package/ui/boot/Ui/PanelHero.qml +112 -0
  442. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  443. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  444. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  445. package/ui/boot/Ui/PanelSlider.qml +156 -0
  446. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  447. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  448. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  449. package/ui/boot/Ui/PopupCard.qml +181 -0
  450. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  451. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  452. package/ui/boot/Ui/SizedImage.qml +125 -0
  453. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  454. package/ui/boot/Ui/TextField.qml +58 -0
  455. package/ui/boot/Ui/Toggle.qml +118 -0
  456. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  457. package/ui/boot/Ui/WidgetButton.qml +172 -0
  458. package/ui/boot/Ui/qmldir +41 -0
  459. package/ui/boot/fleatab.qml +50 -0
  460. package/ui/boot/picker.qml +39 -0
  461. package/ui/boot/shell.qml +131 -0
  462. package/ui/boot/tabtearoff.qml +51 -0
  463. package/ui/js/Anchor.js +379 -0
  464. package/ui/js/AnchorHold.js +162 -0
  465. package/ui/js/Archive.js +157 -0
  466. package/ui/js/ArchiveCard.js +44 -0
  467. package/ui/js/ArchiveOps.js +44 -0
  468. package/ui/js/BackReturn.js +118 -0
  469. package/ui/js/BulkRename.js +100 -0
  470. package/ui/js/Buttons.js +54 -0
  471. package/ui/js/ChromeKeys.js +116 -0
  472. package/ui/js/ClipMarks.js +73 -0
  473. package/ui/js/ClipSelection.js +67 -0
  474. package/ui/js/Clipboard.js +179 -0
  475. package/ui/js/Cloud.js +97 -0
  476. package/ui/js/CodeRuns.js +72 -0
  477. package/ui/js/Collide.js +212 -0
  478. package/ui/js/ColumnFit.js +28 -0
  479. package/ui/js/ColumnMenu.js +111 -0
  480. package/ui/js/Columns.js +298 -0
  481. package/ui/js/ColumnsCarry.js +103 -0
  482. package/ui/js/Compare.js +74 -0
  483. package/ui/js/Contrast.js +157 -0
  484. package/ui/js/ControlReader.js +42 -0
  485. package/ui/js/Convert.js +52 -0
  486. package/ui/js/CopyAs.js +52 -0
  487. package/ui/js/Crumbs.js +178 -0
  488. package/ui/js/Csv.js +231 -0
  489. package/ui/js/Density.js +44 -0
  490. package/ui/js/Devices.js +440 -0
  491. package/ui/js/DirSizes.js +180 -0
  492. package/ui/js/Drag.js +360 -0
  493. package/ui/js/DragOut.js +166 -0
  494. package/ui/js/DropDev.js +52 -0
  495. package/ui/js/Dropbox.js +41 -0
  496. package/ui/js/Eject.js +217 -0
  497. package/ui/js/Errors.js +188 -0
  498. package/ui/js/ExtThumbs.js +115 -0
  499. package/ui/js/Facts.js +402 -0
  500. package/ui/js/FigureDate.js +14 -0
  501. package/ui/js/Filter.js +285 -0
  502. package/ui/js/Find.js +46 -0
  503. package/ui/js/Flash.js +184 -0
  504. package/ui/js/FlashCard.js +79 -0
  505. package/ui/js/FlashEndings.js +125 -0
  506. package/ui/js/FlashPane.js +116 -0
  507. package/ui/js/FleaBin.js +12 -0
  508. package/ui/js/Focus.js +504 -0
  509. package/ui/js/FolderSorts.js +108 -0
  510. package/ui/js/FolderViews.js +44 -0
  511. package/ui/js/Format.js +314 -0
  512. package/ui/js/Fuzzy.js +141 -0
  513. package/ui/js/GlassTag.js +72 -0
  514. package/ui/js/Grid.js +33 -0
  515. package/ui/js/GridGeometry.js +90 -0
  516. package/ui/js/GridNames.js +153 -0
  517. package/ui/js/GvfsBridge.js +200 -0
  518. package/ui/js/HostClipboard.js +29 -0
  519. package/ui/js/Icons.js +218 -0
  520. package/ui/js/Input.js +50 -0
  521. package/ui/js/Jump.js +200 -0
  522. package/ui/js/KeyName.js +64 -0
  523. package/ui/js/Keymap.js +401 -0
  524. package/ui/js/Kinds.js +164 -0
  525. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  526. package/ui/js/LangMarkPaths.js +47 -0
  527. package/ui/js/LangMarks.js +120 -0
  528. package/ui/js/Lists.js +11 -0
  529. package/ui/js/LocalSend.js +39 -0
  530. package/ui/js/LockedMenu.js +63 -0
  531. package/ui/js/MakeDefault.js +186 -0
  532. package/ui/js/MarkBox.js +7 -0
  533. package/ui/js/Markdown.js +119 -0
  534. package/ui/js/MarkdownLists.js +86 -0
  535. package/ui/js/MarkdownMaths.js +58 -0
  536. package/ui/js/MarkdownPictures.js +32 -0
  537. package/ui/js/MarkdownPrepared.js +139 -0
  538. package/ui/js/MarkdownTableFit.js +34 -0
  539. package/ui/js/Marks.js +148 -0
  540. package/ui/js/Marquee.js +96 -0
  541. package/ui/js/Match.js +93 -0
  542. package/ui/js/Material.js +109 -0
  543. package/ui/js/MdBlocks.js +437 -0
  544. package/ui/js/MdBreak.js +71 -0
  545. package/ui/js/MdChunks.js +70 -0
  546. package/ui/js/MdContainer.js +137 -0
  547. package/ui/js/MdDocument.js +337 -0
  548. package/ui/js/MdEmph.js +97 -0
  549. package/ui/js/MdEntity.js +108 -0
  550. package/ui/js/MdEntityTable.js +223 -0
  551. package/ui/js/MdEscape.js +59 -0
  552. package/ui/js/MdFront.js +37 -0
  553. package/ui/js/MdHold.js +32 -0
  554. package/ui/js/MdHtml.js +307 -0
  555. package/ui/js/MdHtmlBlock.js +290 -0
  556. package/ui/js/MdHtmlHeading.js +53 -0
  557. package/ui/js/MdHtmlImage.js +179 -0
  558. package/ui/js/MdInline.js +154 -0
  559. package/ui/js/MdItems.js +212 -0
  560. package/ui/js/MdLeaf.js +309 -0
  561. package/ui/js/MdLink.js +196 -0
  562. package/ui/js/MdMath.js +67 -0
  563. package/ui/js/MdParagraphs.js +138 -0
  564. package/ui/js/MdRefs.js +232 -0
  565. package/ui/js/MdResolve.js +226 -0
  566. package/ui/js/MdRun.js +300 -0
  567. package/ui/js/MdUrl.js +226 -0
  568. package/ui/js/Menu.js +514 -0
  569. package/ui/js/MenuContext.js +85 -0
  570. package/ui/js/MenuFit.js +13 -0
  571. package/ui/js/MenuRefresh.js +74 -0
  572. package/ui/js/MenuWheel.js +35 -0
  573. package/ui/js/Messages.js +186 -0
  574. package/ui/js/Motion.js +16 -0
  575. package/ui/js/Mounts.js +320 -0
  576. package/ui/js/MouseNav.js +13 -0
  577. package/ui/js/Nav.js +329 -0
  578. package/ui/js/NetFs.js +26 -0
  579. package/ui/js/OpenWith.js +83 -0
  580. package/ui/js/Ops.js +585 -0
  581. package/ui/js/Palette.js +33 -0
  582. package/ui/js/PathBar.js +260 -0
  583. package/ui/js/Permissions.js +179 -0
  584. package/ui/js/Phones.js +112 -0
  585. package/ui/js/Picker.js +353 -0
  586. package/ui/js/PlaceMenu.js +96 -0
  587. package/ui/js/Places.js +265 -0
  588. package/ui/js/PreviewAsk.js +21 -0
  589. package/ui/js/PreviewKeys.js +155 -0
  590. package/ui/js/PreviewSettle.js +29 -0
  591. package/ui/js/PreviewSwap.js +101 -0
  592. package/ui/js/PropertyFacts.js +144 -0
  593. package/ui/js/Protocols.js +296 -0
  594. package/ui/js/QuickLookKinds.js +27 -0
  595. package/ui/js/QuickLookRect.js +24 -0
  596. package/ui/js/RailDrop.js +51 -0
  597. package/ui/js/RailKeys.js +87 -0
  598. package/ui/js/RailMenu.js +133 -0
  599. package/ui/js/Recent.js +135 -0
  600. package/ui/js/RecentMode.js +156 -0
  601. package/ui/js/Reload.js +60 -0
  602. package/ui/js/Renderer.js +9 -0
  603. package/ui/js/RunFile.js +143 -0
  604. package/ui/js/Scripts.js +28 -0
  605. package/ui/js/Scroll.js +304 -0
  606. package/ui/js/ScrollOff.js +69 -0
  607. package/ui/js/Search.js +478 -0
  608. package/ui/js/Selection.js +97 -0
  609. package/ui/js/Settings.js +476 -0
  610. package/ui/js/SettingsAbout.js +97 -0
  611. package/ui/js/SettingsShelf.js +52 -0
  612. package/ui/js/SheetKeys.js +42 -0
  613. package/ui/js/SheetQuery.js +281 -0
  614. package/ui/js/ShelfPile.js +27 -0
  615. package/ui/js/SlowClick.js +53 -0
  616. package/ui/js/SlowOp.js +6 -0
  617. package/ui/js/Sort.js +134 -0
  618. package/ui/js/Startup.js +63 -0
  619. package/ui/js/StateWords.js +17 -0
  620. package/ui/js/Status.js +238 -0
  621. package/ui/js/Swap.js +166 -0
  622. package/ui/js/TabMove.js +39 -0
  623. package/ui/js/Tabs.js +751 -0
  624. package/ui/js/Tags.js +134 -0
  625. package/ui/js/Taildrop.js +128 -0
  626. package/ui/js/Tap.js +180 -0
  627. package/ui/js/TextSize.js +107 -0
  628. package/ui/js/Thumbs.js +158 -0
  629. package/ui/js/Transfer.js +172 -0
  630. package/ui/js/Trash.js +84 -0
  631. package/ui/js/TrashDates.js +47 -0
  632. package/ui/js/TrashDrop.js +64 -0
  633. package/ui/js/UiState.js +438 -0
  634. package/ui/js/Unlock.js +152 -0
  635. package/ui/js/Update.js +130 -0
  636. package/ui/js/UserDirs.js +56 -0
  637. package/ui/qmldir +230 -0
@@ -0,0 +1,60 @@
1
+ import QtQuick
2
+ import Quickshell
3
+ import Quickshell.Io
4
+
5
+ // Wayland window positions are not QWindow positions. Match the one source client
6
+ // by pid using Theme's hyprctl Process pattern, once per lift, never on motion.
7
+ Item {
8
+ id: root
9
+ property string token: ""
10
+ property string queryToken: ""
11
+ property bool queryDrained: true
12
+ property var rect: null
13
+ property var strip: null
14
+
15
+ function begin(lift, band) {
16
+ root.token = lift
17
+ root.rect = null
18
+ root.strip = band
19
+ // An old query drains before the latest token starts its own query.
20
+ if (query.running || !root.queryDrained)
21
+ return
22
+ root.queryDrained = false
23
+ root.queryToken = lift
24
+ query.running = true
25
+ }
26
+
27
+ // Exit must clear running and drain old stdout before the next token starts.
28
+ function restartLatest() {
29
+ if (!query.running && root.queryDrained && root.queryToken !== root.token) root.begin(root.token, root.strip)
30
+ }
31
+
32
+ Process {
33
+ id: query
34
+ command: ["hyprctl", "clients", "-j"]
35
+ onExited: Qt.callLater(root.restartLatest)
36
+ stdout: StdioCollector {
37
+ waitForEnd: true
38
+ onStreamFinished: {
39
+ root.queryDrained = true
40
+ Qt.callLater(root.restartLatest)
41
+ if (root.queryToken !== root.token)
42
+ return
43
+ try {
44
+ // Sample input: [{"pid":111,"at":[0,0],"size":[900,500],"mapped":true,"hidden":false}].
45
+ var clients = JSON.parse(text)
46
+ var matches = clients.filter(function (client) {
47
+ return String(client.pid) === String(Quickshell.processId)
48
+ && client.mapped !== false && client.hidden !== true
49
+ })
50
+ if (matches.length === 1) {
51
+ var client = matches[0]
52
+ if (client.at && client.size && isFinite(client.at[0]) && isFinite(client.at[1])
53
+ && client.size[0] > 0 && client.size[1] > 0)
54
+ root.rect = { x: client.at[0], y: client.at[1], width: client.size[0], height: client.size[1] }
55
+ }
56
+ } catch (error) { root.rect = null }
57
+ }
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,52 @@
1
+ import QtQuick
2
+ import "js/Tags.js" as Tags
3
+
4
+ // A Tags flyout row's rule-14 box and swatch, built only for those rows; the box is the root so it anchors as a sibling of the hint.
5
+ Item {
6
+ id: root
7
+ required property Item row
8
+ required property Item mark
9
+ required property Item hint
10
+ readonly property var tagRow: root.row.entry.tagRow || ({ swatch: "", fallback: false, state: Tags.ABSENT, digit: "" })
11
+ readonly property color ink: root.row.labelColor
12
+ // The thinnest mixed bar.
13
+ readonly property int mixedBarMin: 2
14
+ visible: root.row.entry.tagRow !== undefined
15
+ anchors.right: root.hint.left
16
+ anchors.rightMargin: Theme.spacing.gap
17
+ anchors.verticalCenter: parent.verticalCenter
18
+ width: Tags.SWATCH_SIZE
19
+ height: Tags.SWATCH_SIZE
20
+
21
+ Rectangle {
22
+ anchors.fill: parent
23
+ color: "transparent"
24
+ border.width: Theme.spacing.hairline
25
+ border.color: root.ink
26
+ opacity: root.row.available ? 1 : Theme.disabledOpacity
27
+ }
28
+ Glyph {
29
+ anchors.fill: parent
30
+ visible: root.tagRow.state === Tags.APPLIED
31
+ name: "check"
32
+ color: root.ink
33
+ }
34
+ Rectangle {
35
+ visible: root.tagRow.state === Tags.MIXED
36
+ anchors.left: parent.left
37
+ anchors.right: parent.right
38
+ anchors.verticalCenter: parent.verticalCenter
39
+ height: Math.max(root.mixedBarMin, Theme.spacing.hairline * 2)
40
+ color: root.ink
41
+ }
42
+ // Reparented to the mark slot, so the swatch is centred in it as the glyph would be.
43
+ TagSwatch {
44
+ parent: root.mark
45
+ anchors.centerIn: parent
46
+ visible: root.visible
47
+ swatch: root.tagRow.swatch
48
+ fallback: root.tagRow.fallback
49
+ digit: root.tagRow.digit
50
+ tagColors: Theme.tag.menuColors
51
+ }
52
+ }
@@ -0,0 +1,53 @@
1
+ pragma ComponentBehavior: Bound
2
+
3
+ import QtQuick
4
+ import qs.Commons
5
+ import "js/Tags.js" as Tags
6
+
7
+ // The rail's TAGS section: the heading and one row per tag in use. The rail builds it once, when the setting is on and a tag is in use.
8
+ Column {
9
+ id: root
10
+ required property var sidebar
11
+ width: parent.width
12
+ // A rail that follows a pane with no tag in use draws no heading, and the network gap reserves no height for it.
13
+ visible: sidebar.placesState.showTags === true && root.entries.length > 0
14
+ height: root.visible ? root.implicitHeight : 0
15
+ // Guarded as Sidebar.tagsListed is, because the rail can move to a pane that has built no tag state.
16
+ readonly property var entries: Tags.railEntries(root.sidebar.navigationPane !== null && root.sidebar.navigationPane.tagState !== null
17
+ ? root.sidebar.navigationPane.tagState.inUse : ({}))
18
+ // Where this section's first row stands among the rail's entries.
19
+ readonly property int base: root.sidebar.placesEntries.length - root.entries.length
20
+ function itemAt(i) { return tagRepeater.itemAt(i) }
21
+ // The Loader sizes its item to the 13 px slot, so the 14 px swatch is a child the Loader leaves alone.
22
+ Component {
23
+ id: markComponent
24
+ Item {
25
+ id: slot
26
+ property var entry: ({})
27
+ TagSwatch {
28
+ anchors.centerIn: parent
29
+ swatch: slot.entry.swatch || ""
30
+ fallback: Theme.tag.fallback
31
+ digit: slot.entry.digit || ""
32
+ tagColors: Theme.tag.menuColors
33
+ }
34
+ }
35
+ }
36
+
37
+ RailHeading {
38
+ text: "TAGS"
39
+ topExtra: Style.spacing.panelGap
40
+ }
41
+ Repeater {
42
+ id: tagRepeater
43
+ model: root.entries
44
+ delegate: SidebarRow {
45
+ lit: index + root.base === root.sidebar.litIndex
46
+ highlighted: index + root.base === root.sidebar.highlightedIndex
47
+ cursor: root.sidebar.cursorDrawn && index + root.base === root.sidebar.cursorIndex
48
+ focused: root.sidebar.focused
49
+ tagMark: markComponent
50
+ onActivated: function (idx) { root.sidebar.activate(idx + root.base) }
51
+ }
52
+ }
53
+ }
package/ui/TagSlot.qml ADDED
@@ -0,0 +1,40 @@
1
+ pragma ComponentBehavior: Bound
2
+
3
+ import QtQuick
4
+ import "js/Tags.js" as Tags
5
+
6
+ // One row's tag swatches: 14 px squares at 3 px gaps, each carrying its digit in the fallback.
7
+ Item {
8
+ id: root
9
+ readonly property int swatchGap: 3
10
+ property var tags: []
11
+ // A row reserves the 88 px slot and a tile its own cell, so only the whole swatches that fit are drawn, with no counter.
12
+ readonly property real room: root.below && root.parent ? root.parent.width : Theme.tagSlotWidth
13
+ readonly property var drawn: root.tags.slice(0, Math.max(0, Math.floor((root.room + root.swatchGap) / (Tags.SWATCH_SIZE + root.swatchGap))))
14
+ // The item this slot follows on a row, or the caption it sits under on a tile; hug follows a text's own end.
15
+ property Item after: null
16
+ property Item below: null
17
+ property bool hug: false
18
+ visible: root.tags.length > 0
19
+ width: Math.max(0, root.drawn.length * (Tags.SWATCH_SIZE + root.swatchGap) - root.swatchGap)
20
+ height: Tags.SWATCH_SIZE
21
+ anchors.left: root.after ? (root.hug ? root.after.left : root.after.right) : undefined
22
+ anchors.leftMargin: root.after && root.hug ? root.after.contentWidth + Theme.spacing.gap : Theme.spacing.gap
23
+ anchors.verticalCenter: root.after ? root.after.verticalCenter : undefined
24
+ anchors.top: root.below ? root.below.bottom : undefined
25
+ anchors.horizontalCenter: root.below ? parent.horizontalCenter : undefined
26
+
27
+ Repeater {
28
+ model: root.drawn
29
+ delegate: TagSwatch {
30
+ id: cell
31
+ required property int index
32
+ required property string modelData
33
+ x: cell.index * (Tags.SWATCH_SIZE + root.swatchGap)
34
+ swatch: cell.modelData
35
+ fallback: Theme.tag.fallback
36
+ digit: Tags.keyFor(cell.modelData)
37
+ tagColors: Theme.tag.colors
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,150 @@
1
+ import QtQuick
2
+ import "js/Filter.js" as Filter
3
+ import "js/Menu.js" as Menu
4
+ import "js/Ops.js" as Ops
5
+ import "js/Search.js" as Search
6
+ import "js/Tags.js" as Tags
7
+
8
+ // One pane's file tags, built only when a listing first carries one or a tag key or menu first asks.
9
+ Item {
10
+ id: root
11
+ required property var pane
12
+ // The 88 px slot stands on every row of a folder once a tagged row has been seen in it.
13
+ property bool latched: false
14
+ // Every tag this session has seen set, which is what the rail's TAGS section lists.
15
+ property var inUse: ({})
16
+ // The changes sent and not yet answered, oldest first; each settagged answer or settags refusal takes the oldest.
17
+ property var pending: []
18
+ // The backend's read of a selection reaching past the held window, and the one still out.
19
+ property var asked: null
20
+ property var asking: null
21
+ property int askId: 0
22
+
23
+ // A rows reply is the whole held window; a fresh listing starts the latch over.
24
+ function take(items, fresh) {
25
+ var seen = Tags.stamp(items, root.latched, root.inUse, fresh)
26
+ root.latched = seen.latched
27
+ if (seen.grew) root.inUse = Object.assign({}, root.inUse)
28
+ }
29
+
30
+ // Each target's tags while every target is in the held window; null once one is outside it, where only the backend knows.
31
+ function filesFor(indices) {
32
+ var files = []
33
+ for (var i = 0; i < indices.length; i++) {
34
+ var row = root.pane.rowFor(indices[i])
35
+ if (!row) return null
36
+ files.push(row.tg || [])
37
+ }
38
+ return files
39
+ }
40
+
41
+ // Names the selection a backend read answers for: the numbering, the selection's version and, with no selection, the cursor.
42
+ function selectionKey() {
43
+ var pane = root.pane
44
+ return pane.backend.heldListing + ":" + pane.selectionVersion + ":" + (pane.selectionCount() > 0 ? "" : pane.cursorIndex)
45
+ }
46
+
47
+ // The flyout's rows for the selection: seven tags with digit hints, then Remove all tags; absent until a backend read lands.
48
+ function flyout() {
49
+ var files = root.filesFor(Ops.targetIndices(root.pane))
50
+ if (files !== null) return Tags.tagEntries(Tags.statesFor(files), Theme.tag.fallback)
51
+ var asked = root.asked !== null && root.asked.key === root.selectionKey() ? root.asked : null
52
+ return Tags.tagEntries(asked ? Tags.statesFrom(asked.applied, asked.mixed) : {}, Theme.tag.fallback)
53
+ }
54
+
55
+ // A selection past the held window asks what its rows carry (again: re-ask after a write); the answer redraws the flyout.
56
+ function ask(again) {
57
+ var rows = Ops.targetIndices(root.pane)
58
+ if (rows.length === 0 || root.filesFor(rows) !== null) return
59
+ var key = root.selectionKey()
60
+ if (again !== true && ((root.asked !== null && root.asked.key === key) || (root.asking !== null && root.asking.key === key))) return
61
+ root.askId += 1
62
+ root.asking = { id: root.askId, key: key, listing: root.pane.backend.heldListing }
63
+ root.pane.backend.send({ c: "tagstates", rows: rows, id: root.askId })
64
+ }
65
+
66
+ // A flyout choice toggles that tag over the selection, or clears every tag off it; the backend decides add or remove for all.
67
+ function choose(name) {
68
+ var rows = Ops.targetIndices(root.pane)
69
+ if (rows.length === 0) return
70
+ var clear = name === Tags.CLEAR_ID
71
+ root.pending = root.pending.concat([{ tag: clear ? "" : name }])
72
+ root.pane.backend.send({ c: "settags", rows: rows, op: clear ? "clear" : "toggle", tag: clear ? "" : name })
73
+ }
74
+
75
+ // T opens the Tags flyout straight from the listing, the same list the menu's Tags row opens.
76
+ function openMenu() {
77
+ var pane = root.pane
78
+ if (pane.lockedTarget.length > 0) return
79
+ if (!pane.canTag) {
80
+ pane.message("This filesystem does not support file tags.", true)
81
+ return
82
+ }
83
+ var menu = pane.contextMenu()
84
+ if (!Menu.openAtCursor(pane, menu, Theme.spacing.rowPaddingX)) return
85
+ for (var i = 0; i < menu.entries.length; i++) {
86
+ if (menu.entries[i].action === "tags") {
87
+ menu.openSubmenu(i)
88
+ return
89
+ }
90
+ }
91
+ }
92
+
93
+ // A tag walks Home for that tag, an explicit walk like any search; esc returns to the listing.
94
+ function openTag(name) {
95
+ if (!name || !Search.takesQuery(root.pane)) return
96
+ Filter.close(root.pane)
97
+ Search.openQuery(root.pane, "tag:" + name)
98
+ }
99
+
100
+ // A tagstates answer for the read still out lands only in the numbering it was asked in.
101
+ function statesDone(message) {
102
+ var asking = root.asking
103
+ if (asking === null || message.id !== asking.id) return
104
+ root.asking = null
105
+ if (message.listing !== asking.listing) return
106
+ root.asked = { key: asking.key, applied: message.applied || [], mixed: message.mixed || [] }
107
+ root.pane.contextMenu().refreshProviderRows()
108
+ }
109
+
110
+ // A settagged answer lands the oldest pending change: forget every read made before it, ask the visible window again, and say it when a file changed or a row was skipped.
111
+ function setTaggedDone(message) {
112
+ var change = root.pending.length > 0 ? root.pending[0] : null
113
+ root.pending = root.pending.slice(1)
114
+ var flyoutUp = root.pane.menuVisible === true
115
+ // An open flyout keeps drawing its last read until the fresh one lands, so no box blanks between the two.
116
+ if (!flyoutUp) root.asked = null
117
+ root.asking = null
118
+ if (!change) return
119
+ root.pane.backend.window(root.pane.held, root.pane.windowSize)
120
+ if (flyoutUp) root.ask(true)
121
+ // The backend sends data and the UI words it: the changed sentence, the skipped one, or both.
122
+ var line = Tags.doneLine(message.op, message.ok || 0, change.tag, message.skipped)
123
+ if (line !== "") root.pane.message(line, false)
124
+ }
125
+
126
+ // A held window that lands while a flyout is up carries the tags just written; the menu's rows source follows it a turn later, so the redraw waits for that turn.
127
+ Connections {
128
+ target: root.pane
129
+ function onRowsChanged() {
130
+ var menu = root.pane.contextMenu()
131
+ if (menu.submenuOpen === true) Qt.callLater(function() { menu.refreshProviderRows() })
132
+ }
133
+ }
134
+
135
+ Connections {
136
+ target: root.pane.backend
137
+ function onTagStatesResult(message) { root.statesDone(message) }
138
+ function onSettagged(message) { root.setTaggedDone(message) }
139
+ // A refused settags answers its own pending change; a backend that stopped answers none of them.
140
+ function onFailed(where, input) {
141
+ if (where === "backend" || where === "read") {
142
+ root.pending = []
143
+ root.asked = null
144
+ root.asking = null
145
+ } else if (where === "settags" || (where === "stale" && input === "settags")) {
146
+ root.pending = root.pending.slice(1)
147
+ }
148
+ }
149
+ }
150
+ }
@@ -0,0 +1,28 @@
1
+ import QtQuick
2
+ import "js/Tags.js" as Tags
3
+
4
+ // One tag swatch: the fill, the fallback border, the digit text.
5
+ Rectangle {
6
+ id: root
7
+ property string swatch: ""
8
+ property bool fallback: false
9
+ property string digit: ""
10
+ required property var tagColors
11
+ property int size: Tags.SWATCH_SIZE
12
+ readonly property int fallbackBorder: 2
13
+ width: root.size
14
+ height: root.size
15
+ color: root.fallback ? "transparent" : root.tagColors[root.swatch] || "transparent"
16
+ border.width: root.fallback ? root.fallbackBorder : 0
17
+ border.color: root.tagColors[root.swatch] || "transparent"
18
+ Text {
19
+ visible: root.fallback
20
+ anchors.centerIn: parent
21
+ text: root.digit
22
+ color: root.tagColors[root.swatch] || "transparent"
23
+ font.family: Theme.font.family
24
+ font.pixelSize: Theme.font.caption
25
+ font.bold: true
26
+ textFormat: Text.PlainText
27
+ }
28
+ }
@@ -0,0 +1,86 @@
1
+ import QtQuick
2
+ import Quickshell
3
+ import Quickshell.Io
4
+ import "js/Taildrop.js" as TaildropJs
5
+
6
+ // The context menu's own Service, the same shape as ui/NetworkMounts.qml: this is the only thing
7
+ // here that touches tailscale, ui/ContextMenu.qml only reads "peers" and renders it.
8
+ Item {
9
+ id: root
10
+
11
+ property var peers: []
12
+ property string reason: "checking"
13
+ // What state the reason is, for logic: ui/js/Taildrop.js status() names the codes.
14
+ property string code: ""
15
+ property string sendCommand: ""
16
+ property bool checking: false
17
+ property bool _awaitingStart: false
18
+ // The OEM Tailscale service's pollWatchdog bounds status queries at fifteen seconds.
19
+ readonly property int statusTimeoutSeconds: 15
20
+ signal refreshed()
21
+ // onExited can race the StdioCollector's own text, see ui/NetworkMounts.qml's header comment.
22
+ property string _statusOutput: ""
23
+ property string _statusError: ""
24
+
25
+ function refresh(facts) {
26
+ if (checking) return false
27
+ peers = []
28
+ var provider = facts.taildrop || {}, sender = facts.taildropSend || {}
29
+ sendCommand = sender.command || ""
30
+ reason = provider.reason || sender.reason || "checking"
31
+ code = ""
32
+ if (!provider.command || !sendCommand) return true
33
+ checking = true
34
+ _awaitingStart = true
35
+ _statusOutput = ""
36
+ _statusError = ""
37
+ statusProcess.command = ["timeout", "--signal=KILL", String(statusTimeoutSeconds), provider.command, "status", "--json"]
38
+ statusProcess.running = true
39
+ return true
40
+ }
41
+
42
+ // argv-direct and detached: the script owns its own success/failure notification, see
43
+ // docs/superpowers/specs/2026-08-31-flea-operations-design.md "4.1 Taildrop".
44
+ function send(peerId, paths) {
45
+ var peer = TaildropJs.byId(root.peers, peerId)
46
+ if (!peer || !sendCommand || checking) return false
47
+ Quickshell.execDetached([sendCommand, peer.address].concat(paths))
48
+ return true
49
+ }
50
+
51
+ // ui/Pane.qml's own dispatch-confirmation message reads a name, not the id chosen() carries.
52
+ function labelFor(peerId) {
53
+ var peer = TaildropJs.byId(root.peers, peerId)
54
+ return peer ? peer.label : "that peer"
55
+ }
56
+
57
+ Process {
58
+ id: statusProcess
59
+ stdout: StdioCollector {
60
+ id: statusOut
61
+ waitForEnd: true
62
+ onStreamFinished: root._statusOutput = text
63
+ }
64
+ stderr: StdioCollector { id: statusErr; waitForEnd: true; onStreamFinished: root._statusError = text }
65
+ onStarted: root._awaitingStart = false
66
+ onRunningChanged: {
67
+ if (root._awaitingStart && !running) {
68
+ root._awaitingStart = false
69
+ root.checking = false
70
+ root.reason = "Tailscale could not be checked"
71
+ root.code = ""
72
+ root.refreshed()
73
+ }
74
+ }
75
+ onExited: function (exitCode) {
76
+ root._awaitingStart = false
77
+ var state = TaildropJs.status(statusOut.text || root._statusOutput, exitCode,
78
+ exitCode === 137 || exitCode === 9 ? "Tailscale did not answer in time" : statusErr.text || root._statusError)
79
+ root.peers = state.peers
80
+ root.reason = state.reason
81
+ root.code = state.code
82
+ root.checking = false
83
+ root.refreshed()
84
+ }
85
+ }
86
+ }
@@ -0,0 +1,50 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+
4
+ // The Tailscale mark, reproduced from the official artwork rather than recut. Decided on the
5
+ // reproduction after both recuts were rendered beside the original at the sizes this actually draws
6
+ // at: a stroked mark spends most of a small glyph on outline, and a one-weight language cannot carry
7
+ // the mark's own emphasis. It sits where FleaMark sits, outside the cut and outside Glyph.qml.
8
+ Item {
9
+ id: root
10
+
11
+ // The type scale, not the slot, and clamped so a reproduction can never out-size the cut glyph
12
+ // beside it. A smaller caller slot still wins, the way Glyph's own min() lets it.
13
+ property real iconSize: Theme.markSize
14
+ property color color: "transparent"
15
+
16
+ // The official file's own box and geometry: circles of radius 18 at 30.5, 84.5 and 138.5.
17
+ readonly property real grid: 169
18
+ readonly property real dotRadius: 18
19
+ readonly property var centres: [30.5, 84.5, 138.5]
20
+ // The official draws the middle row plus the bottom centre solid and the rest at 0.4; the two
21
+ // opacities are the mark, because the brand toolkit says the gray dots adapt in opacity.
22
+ readonly property real mutedOpacity: 0.4
23
+ readonly property real drawn: Math.min(Theme.markSize, root.iconSize)
24
+ readonly property real unit: root.drawn / root.grid
25
+
26
+ implicitWidth: root.drawn
27
+ implicitHeight: root.drawn
28
+ width: root.drawn
29
+ height: root.drawn
30
+
31
+ Repeater {
32
+ model: 9
33
+
34
+ delegate: Rectangle {
35
+ required property int index
36
+ readonly property int row: Math.floor(index / 3)
37
+ readonly property int col: index % 3
38
+ // Row 1 is the middle row, and column 1 of row 2 is the bottom centre.
39
+ readonly property bool solid: row === 1 || (row === 2 && col === 1)
40
+
41
+ width: 2 * root.dotRadius * root.unit
42
+ height: width
43
+ radius: width / 2
44
+ x: root.centres[col] * root.unit - width / 2
45
+ y: root.centres[row] * root.unit - height / 2
46
+ color: root.color
47
+ opacity: solid ? 1.0 : root.mutedOpacity
48
+ }
49
+ }
50
+ }
@@ -0,0 +1,69 @@
1
+ pragma Singleton
2
+ import QtQuick
3
+ import Quickshell
4
+ import Quickshell.Io
5
+
6
+ // All terminal views share this one server; each has its own reconnect token/PTY.
7
+ QtObject {
8
+ id: root
9
+ property var tabs: ({})
10
+ property int serial: 0
11
+ property bool ready: false
12
+ property bool stopping: false
13
+ function acquire(path) {
14
+ var id = "terminal-" + (++serial)
15
+ var next = Object.assign({}, tabs)
16
+ next[id] = { path: path, url: "", error: "" }
17
+ tabs = next
18
+ if (!server.running && !stopping) server.running = true
19
+ if (ready) server.write(JSON.stringify({id:id,path:path}) + "\n")
20
+ return id
21
+ }
22
+ function release(id) {
23
+ var next = Object.assign({}, tabs)
24
+ delete next[id]
25
+ tabs = next
26
+ if (Object.keys(next).length === 0) {
27
+ ready = false
28
+ if (server.running && !stopping) {
29
+ stopping = true
30
+ server.write(JSON.stringify({shutdown:true}) + "\n")
31
+ }
32
+ } else if (ready) server.write(JSON.stringify({id:id,close:true}) + "\n")
33
+ }
34
+ function fail(message) {
35
+ ready = false
36
+ var next = {}
37
+ for (var id in tabs) next[id] = { path: tabs[id].path, url: "", error: message }
38
+ tabs = next
39
+ }
40
+ property var process: Process {
41
+ id: server
42
+ stdinEnabled: true
43
+ command: [Quickshell.env("BUNINU_BUN") || "bun", Quickshell.shellDir + "/../../src/terminal-server.js"]
44
+ stdout: SplitParser {
45
+ onRead: function(line) {
46
+ try {
47
+ var result = JSON.parse(line)
48
+ if (result.ready) {
49
+ if (root.stopping) return
50
+ root.ready = true
51
+ for (var id in root.tabs) if (!root.tabs[id].error) server.write(JSON.stringify({id:id,path:root.tabs[id].path}) + "\n")
52
+ } else if (result.id && root.tabs[result.id]) {
53
+ var next = Object.assign({}, root.tabs)
54
+ next[result.id] = { path: next[result.id].path, url: result.url || "", error: result.error || "" }
55
+ root.tabs = next
56
+ } else if (!result.id && result.error) root.fail(result.error)
57
+ } catch(e) { root.fail("Invalid terminal server response.") }
58
+ }
59
+ }
60
+ onExited: {
61
+ if (root.stopping) {
62
+ root.stopping = false
63
+ root.ready = false
64
+ // A tab opened during shutdown waits for the old listener to exit.
65
+ if (Object.keys(root.tabs).length) Qt.callLater(function() { server.running = true })
66
+ } else root.fail("Terminal server exited.")
67
+ }
68
+ }
69
+ }
@@ -0,0 +1,48 @@
1
+ import QtQuick
2
+ import QtWebEngine
3
+ import Quickshell
4
+ import "." as Flea
5
+ import Quickshell.Io
6
+ import qs.Commons
7
+
8
+ // Kept alive while another tab is selected; destroyed only when this tab closes.
9
+ Item {
10
+ id: root
11
+ property string directory: ""
12
+ property string sessionId: ""
13
+ readonly property var session: Flea.TerminalService.tabs[sessionId] || ({})
14
+ readonly property string address: session.url || ""
15
+ readonly property string status: session.error || "Starting terminal…"
16
+ property bool selected: false
17
+ function focusTerminal() {
18
+ if (selected && address.length) Qt.callLater(function() {
19
+ if (root.selected && root.address.length) web.forceActiveFocus()
20
+ })
21
+ }
22
+ onSelectedChanged: focusTerminal()
23
+ onAddressChanged: focusTerminal()
24
+ visible: selected
25
+ anchors.fill: parent
26
+ Rectangle { anchors.fill: parent; color: Theme.color.background }
27
+ WebEngineView {
28
+ id: web
29
+ anchors.fill: parent
30
+ visible: root.address.length > 0
31
+ url: root.address
32
+ backgroundColor: Theme.color.background
33
+ settings.javascriptEnabled: true
34
+ onLoadingChanged: function(info) { if(info.errorCode) console.warn("Terminal WebEngineView:", info.errorString) }
35
+ }
36
+ Text {
37
+ anchors.centerIn: parent
38
+ width: Math.max(0, parent.width - 32)
39
+ horizontalAlignment: Text.AlignHCenter
40
+ wrapMode: Text.Wrap
41
+ textFormat: Text.PlainText
42
+ color: Theme.color.foreground
43
+ font.family: Theme.font.family
44
+ font.pixelSize: Theme.font.body
45
+ visible: root.address.length === 0
46
+ text: root.status
47
+ }
48
+ }
@@ -0,0 +1,27 @@
1
+ import QtQuick
2
+ import "." as Flea
3
+
4
+ // Dynamic objects preserve each iframe/PTY when tab order or selection changes.
5
+ Item {
6
+ id: root
7
+ property var pane: null
8
+ property var sessions: ({})
9
+ function open(directory) {
10
+ var component = Qt.createComponent("TerminalTab.qml")
11
+ if (component.status !== Component.Ready) {
12
+ pane.message(component.errorString(), true)
13
+ return ""
14
+ }
15
+ var key = Flea.TerminalService.acquire(directory)
16
+ var item = component.createObject(root, { directory: directory, sessionId: key })
17
+ if (!item) { Flea.TerminalService.release(key); pane.message("Could not create the terminal view.", true); return "" }
18
+ item.selected = Qt.binding(function() { return root.pane.terminalId === key })
19
+ sessions[key] = item
20
+ return key
21
+ }
22
+ Component.onDestruction: { for (var key in sessions) Flea.TerminalService.release(key) }
23
+ function close(key) {
24
+ var item = sessions[key]
25
+ if (item) { Flea.TerminalService.release(key); delete sessions[key]; item.destroy() }
26
+ }
27
+ }