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,239 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ import "." as Flea
4
+ import "js/Ops.js" as Ops
5
+ import "js/Transfer.js" as Transfer
6
+
7
+ // The released transfer card remains the pointer cancellation surface above the informational footer.
8
+ Item {
9
+ id: root
10
+
11
+ // ui/js/Ops.js's transfer, reassigned on every wire line; running false is what hides the card.
12
+ property var transfer: Ops.emptyTransfer()
13
+ property var owner: null
14
+ readonly property var cancelItem: cancelButton
15
+ readonly property alias byteText: byteLine.text
16
+ readonly property alias headlineText: headline.text
17
+ signal cancelRequested(int id)
18
+
19
+ // Everything drawn comes off this sample rather than straight off the wire, because thirty
20
+ // thousand small files change the name faster than it can be read. Idle rather than bound to
21
+ // transfer: a binding would track the wire live until the first tick happened to break it.
22
+ property var shown: Ops.emptyTransfer()
23
+ // About four changes a second, which is what the eye reads. The backend's own byte heartbeat is
24
+ // 150 ms (src/backend/opsreq.rs PROGRESS_EVERY), so a large file loses almost nothing here.
25
+ readonly property int publishMs: 250
26
+ // TransferCard rule 3: the rate is what this card published over the last two seconds, so one
27
+ // slow beat cannot make it jump and a stall reads 0 B/s rather than a stale number.
28
+ readonly property int rateWindowMs: 2000
29
+ property var rateSamples: []
30
+ readonly property real rate: {
31
+ if (root.rateSamples.length < 2)
32
+ return 0
33
+ var first = root.rateSamples[0]
34
+ var last = root.rateSamples[root.rateSamples.length - 1]
35
+ var span = (last.at - first.at) / 1000
36
+ return span > 0 ? Math.max(0, (last.bytes - first.bytes) / span) : 0
37
+ }
38
+
39
+ // Set the instant Cancel is pressed. src/backend/copyfile.rs stops the item in flight and
40
+ // removes what it wrote, so this covers only the round trip to transferdone and the beat above.
41
+ property bool cancelling: false
42
+
43
+ // The bar's thickness comes off the type scale the way a mark does, so it follows the display
44
+ // text size instead of pinning a pixel.
45
+ readonly property int barHeight: Math.round(Theme.font.caption / 2)
46
+ readonly property real trackOpacity: 0.25
47
+ // The one other popup in this design, ui/ConvertDialog.qml, is 300 design pixels wide; a second
48
+ // popup at a second width would be two languages.
49
+ readonly property int cardWidth: 300
50
+
51
+ visible: root.shown.running
52
+ implicitWidth: Theme.space(root.cardWidth)
53
+ implicitHeight: body.implicitHeight + 2 * Theme.spacing.rowPaddingX
54
+ width: root.implicitWidth
55
+ height: root.implicitHeight
56
+
57
+ onTransferChanged: {
58
+ // The first sample and the last are published at once; the ones between wait for the beat.
59
+ if (!root.transfer.running || !root.shown.running || root.transfer.id !== root.shown.id) {
60
+ root.publish()
61
+ }
62
+ }
63
+ onOwnerChanged: root.publish()
64
+
65
+ Timer {
66
+ interval: root.publishMs
67
+ repeat: true
68
+ running: root.transfer.running
69
+ onTriggered: root.publish()
70
+ }
71
+
72
+ // One published sample, and the window the rate is measured over: everything older than two
73
+ // seconds goes except the one sample just past the edge, which is what the span is taken from.
74
+ function publish() {
75
+ var fresh = root.transfer.id !== root.shown.id ? [] : root.rateSamples
76
+ root.shown = root.transfer
77
+ var now = Date.now()
78
+ var next = fresh.concat([{at: now, bytes: Transfer.movedBytes(root.shown)}])
79
+ var keep = 0
80
+ while (keep + 1 < next.length && now - next[keep + 1].at > root.rateWindowMs) {
81
+ keep += 1
82
+ }
83
+ root.rateSamples = next.slice(keep)
84
+ }
85
+
86
+ // Dimmed while the drive is being flushed: every file is already complete, so there
87
+ // is nothing left to stop.
88
+ function cancel() {
89
+ if (!Transfer.cancelEnabled(root.transfer) || root.cancelling) return
90
+ root.cancelRequested(root.transfer.id)
91
+ }
92
+
93
+ MouseArea {
94
+ anchors.fill: parent
95
+ hoverEnabled: true
96
+ acceptedButtons: Qt.AllButtons
97
+ onWheel: function(wheel) { wheel.accepted = true }
98
+ }
99
+
100
+ Rectangle {
101
+ anchors.fill: parent
102
+ color: Theme.color.surface
103
+ border.width: Theme.spacing.hairline
104
+ border.color: Theme.color.mutedFrame
105
+ // Mirrors hyprland decoration:rounding, the same as the menu and the convert popup; 0 on a
106
+ // stock box, and the bar inside stays square either way.
107
+ radius: Style.cornerRadius
108
+ }
109
+
110
+ Column {
111
+ id: body
112
+ x: Theme.spacing.rowPaddingX
113
+ y: Theme.spacing.rowPaddingX
114
+ width: root.width - 2 * Theme.spacing.rowPaddingX
115
+ spacing: Theme.spacing.gap
116
+
117
+ Item {
118
+ width: parent.width
119
+ height: headline.implicitHeight
120
+
121
+ // The card is chrome, so its mark is the OEM icon token the chrome bar's own marks
122
+ // take; the status bar's spiral is caption-sized because the bar's own text is.
123
+ Flea.Spinner {
124
+ id: crawl
125
+ anchors.left: parent.left
126
+ anchors.verticalCenter: parent.verticalCenter
127
+ width: Theme.chromeMarkSize
128
+ height: Theme.chromeMarkSize
129
+ color: Theme.color.mutedFrame
130
+ }
131
+
132
+ Text {
133
+ id: headline
134
+ anchors.left: crawl.right
135
+ anchors.leftMargin: Theme.spacing.gap
136
+ anchors.right: parent.right
137
+ anchors.verticalCenter: parent.verticalCenter
138
+ text: Transfer.head(root.shown)
139
+ color: Theme.color.foreground
140
+ font.family: Theme.font.family
141
+ font.pixelSize: Theme.font.body
142
+ textFormat: Text.PlainText
143
+ elide: Text.ElideRight
144
+ }
145
+ }
146
+
147
+ Text {
148
+ width: parent.width
149
+ visible: text.length > 0
150
+ text: Transfer.fileLine(root.shown)
151
+ color: Theme.color.muted
152
+ font.family: Theme.font.family
153
+ font.pixelSize: Theme.font.caption
154
+ textFormat: Text.PlainText
155
+ // The middle goes and the extension stays: the extension is what says what the file is.
156
+ elide: Text.ElideMiddle
157
+ }
158
+
159
+ // Square ends, track and fill both: no row or bar in this language rounds. The fill is a
160
+ // sibling of the track and not its child, because opacity multiplies down into children.
161
+ Item {
162
+ width: parent.width
163
+ height: root.barHeight
164
+
165
+ Rectangle {
166
+ anchors.fill: parent
167
+ color: Theme.color.mutedFrame
168
+ opacity: root.trackOpacity
169
+ }
170
+
171
+ Rectangle {
172
+ width: parent.width * Transfer.fraction(root.shown)
173
+ height: parent.height
174
+ color: Theme.color.accentFrame
175
+
176
+ // The card publishes four times a second; easing across the gap is what makes the
177
+ // fill read as movement rather than as four steps a second.
178
+ Behavior on width {
179
+ enabled: !Theme.reducedMotion
180
+ NumberAnimation { duration: root.publishMs; easing.type: Easing.Linear }
181
+ }
182
+ }
183
+ }
184
+
185
+ // TransferCard rule 1: the bar's own caption, the figures the operator is waiting on inked
186
+ // in the foreground and the words between them muted. Rule 4 makes it absent, not blank,
187
+ // until the first byte sample lands, so the card is one row shorter until then.
188
+ Row {
189
+ id: byteLine
190
+ readonly property var parts: Transfer.byteParts(root.shown, root.rate)
191
+ // The drawn line as one string, so a driven case reads what the card says rather than a shot.
192
+ readonly property string text: byteLine.parts.map(function (p) { return p.text }).join("")
193
+ visible: byteLine.parts.length > 0
194
+ height: visible ? implicitHeight : 0
195
+
196
+ Repeater {
197
+ model: byteLine.parts
198
+
199
+ delegate: Text {
200
+ required property var modelData
201
+ text: modelData.text
202
+ color: modelData.figure ? Theme.color.foreground : Theme.color.muted
203
+ font.family: Theme.font.family
204
+ font.pixelSize: Theme.font.caption
205
+ textFormat: Text.PlainText
206
+ }
207
+ }
208
+ }
209
+
210
+ Item {
211
+ width: parent.width
212
+ height: cancelButton.implicitHeight
213
+
214
+ // Variant A: the card's Cancel is the one control every dialog draws.
215
+ Flea.DialogButton {
216
+ id: cancelButton
217
+ anchors.right: parent.right
218
+ visible: !root.cancelling
219
+ label: "Cancel"
220
+ available: Transfer.cancelEnabled(root.shown)
221
+ enabled: available
222
+ onActivated: root.cancel()
223
+ }
224
+
225
+ // The cancel is in and the item in flight is being finished rather than torn in half,
226
+ // which is a state the operator has to be able to see rather than infer from a dead button.
227
+ Text {
228
+ visible: root.cancelling
229
+ anchors.right: parent.right
230
+ anchors.verticalCenter: parent.verticalCenter
231
+ text: "Cancelling"
232
+ color: Theme.color.muted
233
+ font.family: Theme.font.family
234
+ font.pixelSize: Theme.font.body
235
+ textFormat: Text.PlainText
236
+ }
237
+ }
238
+ }
239
+ }
@@ -0,0 +1,129 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ import "." as Flea
4
+ import "js/Trash.js" as Trash
5
+
6
+ // The destructive choice must be reached deliberately; a reflexive Enter activates Cancel.
7
+ FocusScope {
8
+ id: root
9
+ anchors.fill: parent
10
+ visible: opened || opacity > 0
11
+ // Closed means no input, even while the fade-out is still drawn.
12
+ enabled: opened
13
+ opacity: 0
14
+ property bool opened: false
15
+ property var snapshot: ({})
16
+ property bool destructiveFocus: false
17
+ property string scopeName: "Trash items"
18
+ readonly property real referenceScale: Theme.font.bodySmall / 13
19
+ readonly property real cardPadding: Math.round(16 * referenceScale)
20
+ readonly property real cardBottomPadding: Theme.spacing.rowPaddingX
21
+ signal confirmed(int token)
22
+ signal cancelled()
23
+ readonly property var cardItem: card
24
+ readonly property var cancelItem: cancelButton
25
+ readonly property var dangerItem: dangerButton
26
+ readonly property string titleText: title.text
27
+ readonly property string bodyText: bodyLabel.text
28
+ function open(value) { snapshot = value; destructiveFocus = false; body.contentY = 0; opened = true; forceActiveFocus() }
29
+ function close() { opened = false }
30
+ function cancel() { close(); cancelled() }
31
+ function activate() {
32
+ if (!destructiveFocus) { cancel(); return }
33
+ var token = snapshot.token
34
+ close()
35
+ confirmed(token)
36
+ }
37
+ Flea.Reveal { target: root; shown: root.opened; lift: card }
38
+ Keys.onPressed: function(event) {
39
+ if (event.key === Qt.Key_Escape) root.cancel()
40
+ else if (event.modifiers & (Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier)) { event.accepted = true; return }
41
+ else if (event.key === Qt.Key_Tab || event.key === Qt.Key_Backtab) root.destructiveFocus = !root.destructiveFocus
42
+ else if (event.key === Qt.Key_L || event.key === Qt.Key_Right) root.destructiveFocus = true
43
+ else if (event.key === Qt.Key_H || event.key === Qt.Key_Left) root.destructiveFocus = false
44
+ else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_Space) root.activate()
45
+ if (root.opened) body.reveal(root.destructiveFocus ? dangerButton : cancelButton)
46
+ event.accepted = true
47
+ }
48
+ Rectangle {
49
+ anchors.fill: parent
50
+ color: Theme.color.background
51
+ opacity: 0.5
52
+ MouseArea {
53
+ anchors.fill: parent
54
+ hoverEnabled: true
55
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
56
+ onClicked: root.cancel()
57
+ onWheel: function(wheel) { wheel.accepted = true }
58
+ }
59
+ }
60
+ Rectangle {
61
+ id: card
62
+ x: Theme.cardOrigin(root.width, width)
63
+ y: Theme.cardOrigin(root.height, height)
64
+ // TrashSidebar's 340px content width excludes its two 16px paddings and hairlines.
65
+ width: Theme.cardSpan(Math.round(340 * root.referenceScale * Theme.dialogWidthRatio) + 2 * root.cardPadding + 2 * Theme.spacing.hairline, root.width - 2 * Theme.spacing.gap)
66
+ height: Theme.cardSpan(body.wanted + root.cardPadding + root.cardBottomPadding + 2 * Theme.spacing.hairline, root.height - 2 * Theme.spacing.gap)
67
+ color: Theme.color.surface
68
+ border.color: Theme.color.mutedFrame
69
+ border.width: Theme.spacing.hairline
70
+ radius: Style.cornerRadius
71
+ MouseArea {
72
+ anchors.fill: parent
73
+ hoverEnabled: true
74
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
75
+ onWheel: function(wheel) { wheel.accepted = true }
76
+ }
77
+ Flea.CardFrame {
78
+ id: body
79
+ bleed: Theme.focusClearance
80
+ anchors.fill: parent
81
+ anchors.leftMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
82
+ anchors.rightMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
83
+ anchors.topMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
84
+ anchors.bottomMargin: root.cardBottomPadding + Theme.spacing.hairline - Theme.focusClearance
85
+ title: Row {
86
+ width: parent.width
87
+ spacing: Theme.spacing.gap
88
+ Flea.Glyph { id: alertMark; width: Theme.font.bodySmall * 1.3; height: title.height; name: "alert"; color: Theme.color.error }
89
+ Text {
90
+ id: title
91
+ width: parent.width - alertMark.width - parent.spacing
92
+ text: Trash.confirmTitle(root.snapshot)
93
+ textFormat: Text.PlainText
94
+ wrapMode: Text.Wrap
95
+ color: Theme.color.foreground
96
+ font { family: Theme.font.family; pixelSize: Theme.font.body; bold: true }
97
+ }
98
+ }
99
+ buttons: Flow {
100
+ width: Math.min(parent.width, cancelButton.implicitWidth + dangerButton.implicitWidth + spacing)
101
+ anchors.right: parent.right
102
+ spacing: Theme.spacing.gap
103
+ Flea.DialogButton {
104
+ id: cancelButton
105
+ label: "Cancel"
106
+ primary: true
107
+ focused: !root.destructiveFocus
108
+ onActivated: root.cancel()
109
+ }
110
+ Flea.DialogButton {
111
+ id: dangerButton
112
+ label: root.snapshot.all ? "Empty Trash" : "Delete"
113
+ destructive: true
114
+ focused: root.destructiveFocus
115
+ onActivated: { root.destructiveFocus = true; root.activate() }
116
+ }
117
+ }
118
+ Text {
119
+ id: bodyLabel
120
+ width: parent.width
121
+ text: Trash.confirmBody(root.snapshot, root.scopeName)
122
+ textFormat: Text.PlainText
123
+ wrapMode: Text.Wrap
124
+ color: Theme.color.foreground
125
+ font { family: Theme.font.family; pixelSize: Theme.font.body }
126
+ }
127
+ }
128
+ }
129
+ }
@@ -0,0 +1,11 @@
1
+ import QtQuick
2
+ import "js/TrashDrop.js" as TrashDrop
3
+
4
+ // The rail loads this bridge on its first matching drag, keeping TrashDrop.js off the first frame.
5
+ FileDrag {
6
+ id: root
7
+
8
+ function accepts(marker, urls, path) { return TrashDrop.accepts(marker, urls, path, root.pane ? root.pane.backend : null) }
9
+ function line(marker, urls, path) { return TrashDrop.line(marker, urls, path, root.pane ? root.pane.backend : null) }
10
+ function drop(pane, marker, urls) { return TrashDrop.drop(pane, marker, urls) }
11
+ }
@@ -0,0 +1,202 @@
1
+ import QtQuick
2
+ import "." as Flea
3
+ import "js/Focus.js" as Focus
4
+ import "js/Menu.js" as Menu
5
+ import "js/Ops.js" as Ops
6
+ import "js/RecentMode.js" as RecentMode
7
+ import "js/TrashDates.js" as TrashDates
8
+
9
+ // Only Trash-specific operations live here. Pane owns the visible listing,
10
+ // keyboard, filtering, thumbnails and Quick Look, just as for any other location.
11
+ Item {
12
+ id: root
13
+ required property var pane
14
+ property Item overlayParent: null
15
+ readonly property bool browsing: pane.recentMode === "trash"
16
+ // Compatibility with callers checking for the old full-pane overlay.
17
+ readonly property bool opened: false
18
+ readonly property bool confirming: (confirmation !== null && confirmation.opened) || pendingReview !== null || pendingOp === "prepare"
19
+ readonly property var item: root
20
+ readonly property var sheetPane: pane
21
+ readonly property int total: browsing ? pane.total : 0
22
+ readonly property int selectedCount: browsing ? pane.selectionCount() : 0
23
+ readonly property bool active: browsing
24
+ property int requestId: 1000000
25
+ property string pendingOp: ""
26
+ property bool pendingAll: false
27
+ property int pendingToken: 0
28
+ property var pendingReview: null
29
+ property var pendingRows: []
30
+ property int pendingListing: 0
31
+ readonly property bool busy: pendingOp.length > 0
32
+ readonly property var confirmationItem: confirmation
33
+ readonly property bool confirmationOpen: confirmation !== null && confirmation.opened
34
+ // The 30 day sweep, decided 2026-09-11. It runs the same three requests the window runs,
35
+ // in the same order, so prepare still reviews every item against the identity the listing
36
+ // reported and delete still refuses a token the trash has moved under. This host drives it
37
+ // because the backend keeps ONE Trash session: two clients listing into it would take each
38
+ // other's snapshot away, so the window and the sweep are never in flight together.
39
+ readonly property int sweepDays: 30
40
+ // One window of the listing, which is also the most the backend will answer with. A Trash holding
41
+ // more than this is swept over as many days as it takes, which is slower and never wrong.
42
+ readonly property int sweepWindow: 350
43
+ property int sweepId: 0
44
+ property string sweepStage: ""
45
+ property var sweepDoomed: []
46
+ readonly property bool sweeping: root.sweepStage.length > 0
47
+
48
+ function sweep() {
49
+ if (root.sweeping || root.browsing || !ViewState.trashAutoEmpty)
50
+ return
51
+ if (TrashDates.dayNumber(Date.now()) === ViewState.trashSweptOn)
52
+ return
53
+ root.sweepSend("list", {start: 0, count: root.sweepWindow, recover: false})
54
+ }
55
+
56
+ function sweepSend(op, fields) {
57
+ var message = fields || {}
58
+ message.c = "trashbrowse"
59
+ message.op = op
60
+ message.id = ++root.sweepId
61
+ root.sweepStage = op
62
+ root.pane.backend.send(message)
63
+ }
64
+
65
+ // The operator reaching for Trash outranks a sweep nobody asked to watch. Cancelling drops the
66
+ // prepared confirmation in the backend, so the window's own list starts from a clean session.
67
+ function sweepStop() {
68
+ if (!root.sweeping)
69
+ return
70
+ root.sweepStage = ""
71
+ root.sweepDoomed = []
72
+ root.pane.backend.send({c: "trashbrowse", op: "cancel", id: ++root.sweepId, clearSelection: true})
73
+ }
74
+
75
+ // Every reply while a sweep is in flight is the sweep's, because the window cannot be open then.
76
+ // A failure at any stage ends the sweep and records nothing, so the next launch tries again.
77
+ function sweepReceive(message) {
78
+ if (message.ok !== true) { root.sweepStage = ""; root.sweepDoomed = []; return }
79
+ if (message.op === "list") {
80
+ var rows = message.rows || []
81
+ var now = Date.now()
82
+ var doomed = []
83
+ for (var i = 0; i < rows.length; i++) {
84
+ if (TrashDates.expired(rows[i].deleted, now, root.sweepDays))
85
+ doomed.push(rows[i])
86
+ }
87
+ if (doomed.length === 0) { root.sweepFinished(); return }
88
+ root.sweepDoomed = doomed
89
+ root.sweepSend("prepare", {all: false, emptyTrash: false,
90
+ uris: doomed.map(function (row) { return row.uri }),
91
+ identities: doomed.map(function (row) { return row.identity })})
92
+ return
93
+ }
94
+ if (message.op === "prepare") {
95
+ // A prepare that reviewed nothing has nothing to delete, and asking anyway would spend a
96
+ // token on an empty confirmation.
97
+ if (!message.count) { root.sweepFinished(); return }
98
+ root.sweepSend("delete", {token: message.token})
99
+ return
100
+ }
101
+ if (message.op === "delete") {
102
+ // The rail is hidden, so its TrashMonitor is unloaded with it: nothing to refresh.
103
+ if (root.pane.sidebar) root.pane.sidebar.refreshTrash()
104
+ root.sweepFinished()
105
+ }
106
+ }
107
+
108
+ function sweepFinished() {
109
+ root.sweepStage = ""
110
+ root.sweepDoomed = []
111
+ ViewState.recordTrashSweep(TrashDates.dayNumber(Date.now()))
112
+ }
113
+
114
+
115
+ function open(action) {
116
+ if (confirming || busy) return
117
+ sweepStop()
118
+ if (!browsing) {
119
+ if (pane.preview) pane.preview.close()
120
+ pane.shareBrowser.close()
121
+ pane.clearSelection()
122
+ pane.focusView = Focus.LIST
123
+ RecentMode.run(pane, [], {}, "trash")
124
+ }
125
+ if (action === "emptyTrash" || action === "restoreAll") root.action(action)
126
+ else pane.listArea.forceActiveFocus()
127
+ }
128
+ // Navigation already owns the next listing; do not issue a second list here.
129
+ function close() { if (browsing && !confirming) RecentMode.leave(pane) }
130
+ function send(op, fields) {
131
+ pendingOp = op
132
+ pane.backend.send(Object.assign({c:"trashbrowse",id:++requestId,op:op},fields || {}))
133
+ }
134
+ function action(name, menuId) {
135
+ if (busy || confirming) return
136
+ if (name === "openTrash") { open(); return }
137
+ var all = name === "emptyTrash" || name === "restoreAll"
138
+ if (!all && !browsing) return
139
+ if (!all && pane.listInFlight) { pane.message("A directory is already loading.", false); return }
140
+ pendingAll = all
141
+ pendingRows = all ? [] : Ops.targetIndices(pane)
142
+ pendingListing = pane.backend.heldListing
143
+ if (!all && !menuId && pendingRows.length === 0) return
144
+ var fields = all ? {all:true,emptyTrash:name === "emptyTrash"}
145
+ : {all:false,rows:pendingRows,listing:pendingListing}
146
+ if (menuId && !all) fields.menuId = menuId
147
+ send(name === "restoreAll" || name === "restoreTrashSelection" ? "restore" : "prepare", fields)
148
+ }
149
+ function refresh() { if (browsing && !pane.listInFlight) RecentMode.refresh(pane, "") }
150
+ function menuEntries(selection, hidden) {
151
+ return selection ? Menu.applyHidden([
152
+ {label:"Restore",action:"restoreTrashSelection",glyph:"undo",disabled:busy},
153
+ {separator:true},
154
+ {label:"Delete permanently",action:"deletePermanently",glyph:"trash",danger:true,disabled:busy}
155
+ ],hidden) : Menu.trashEntries(total,busy)
156
+ }
157
+ function sheetFocus() { pane.listArea.forceActiveFocus() }
158
+ function sheetActionRows(rows) { return rows }
159
+ function sheetMenuModel() { return menuEntries(true,[]).concat(menuEntries(false,[])) }
160
+ function sheetMenuAction(name) { action(name) }
161
+ function sheetAction(name) { Focus.dispatchAction(name,pane) }
162
+ readonly property var confirmation: confirmationLoader.item
163
+ Loader { id:confirmationLoader; active:false; source:"TrashConfirm.qml"; onLoaded:{item.parent=root.overlayParent || root;item.z=5;if(root.pendingReview){item.open(root.pendingReview);root.pendingReview=null}} }
164
+ Connections {
165
+ target:root.confirmation
166
+ function onConfirmed(token) { root.send("delete",{token:token}) }
167
+ function onCancelled() { root.send("cancel");root.pane.listArea.forceActiveFocus() }
168
+ }
169
+ Connections {
170
+ target: root.pane.backend
171
+ function onTrashResult(message) {
172
+ if (root.sweeping) { root.sweepReceive(message); return }
173
+ if (message.id !== root.requestId || message.op !== root.pendingOp) return
174
+ root.pendingOp = ""
175
+ if (!message.ok) {
176
+ if (confirmation) confirmation.close()
177
+ root.pane.message(message.error || "Trash operation failed",true)
178
+ root.pane.listArea.forceActiveFocus()
179
+ root.refresh()
180
+ return
181
+ }
182
+ if (message.op === "prepare") { root.pendingToken=message.token; if(confirmation)confirmation.open(message);else{root.pendingReview=message;confirmationLoader.active=true;} return }
183
+ if (message.op === "check" && !message.valid) { if (confirmation) confirmation.close(); root.pane.message("The Trash changed; review it again.",true) }
184
+ if (message.op === "restore" || message.op === "delete") {
185
+ root.pane.operationResult((message.op === "restore" ? "Restored " : "Deleted ")+message.done+" items",
186
+ (message.failures || []).map(function(f){return f.error}).join("\n"),message.failed > 0)
187
+ if(root.pane.sidebar)root.pane.sidebar.refreshTrash()
188
+ root.refresh()
189
+ }
190
+ if (message.op !== "check") root.pane.listArea.forceActiveFocus()
191
+ }
192
+ }
193
+ Timer { interval:2500; repeat:true; running:root.confirmationOpen && !root.busy; onTriggered:root.send("check",{token:root.pendingToken}) }
194
+ Connections {
195
+ target: root.pane.sidebar
196
+ function onTrashChanged() { if(root.browsing && !root.busy && !root.confirming)root.refresh() }
197
+ }
198
+ Connections {
199
+ target: root.pane.contextMenu()
200
+ function onRailChosen(action,key) { if(key === "trash")root.action(action) }
201
+ }
202
+ }
@@ -0,0 +1,30 @@
1
+ import QtQuick
2
+
3
+ // flea-qml: the shared Trash count, as the pane's backend reports it with a `trashcount` line at
4
+ // start and on every change. The backend reads the FreeDesktop.org Trash that src/trash.js writes;
5
+ // upstream ran `gio monitor` and `gio info` under setpriv and sh here. Nothing extra runs: the
6
+ // backend already lives as long as the interface.
7
+ Item {
8
+ id: root
9
+ // Not named enabled, which would shadow Item's own input switch.
10
+ property bool watching: true
11
+ property var backend: null
12
+ readonly property int reported: root.backend ? root.backend.trashCount : -1
13
+ readonly property int revision: root.backend ? root.backend.trashRevision : 0
14
+ property int count: 0
15
+ property bool ready: false
16
+ signal changed()
17
+ signal failed(string message)
18
+
19
+ // The backend reports every change itself, so there is nothing to ask for again.
20
+ function refresh() {}
21
+
22
+ onRevisionChanged: if (root.watching && root.ready) root.changed()
23
+ onReportedChanged: {
24
+ if (!root.watching || root.reported < 0) return
25
+ root.count = root.reported
26
+ root.ready = true
27
+ }
28
+ onWatchingChanged: if (root.watching && root.reported >= 0) { root.count = root.reported; root.ready = true }
29
+ Component.onCompleted: if (root.reported >= 0) { root.count = root.reported; root.ready = true }
30
+ }
@@ -0,0 +1,19 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ Item {
4
+ id: root
5
+ property var trash: null
6
+ Repeater {
7
+ model: [{label:"Restore",action:"restoreTrashSelection"},{label:"Restore all",action:"restoreAll"},{label:"Empty Trash",action:"emptyTrash"}]
8
+ delegate: Rectangle {
9
+ required property var modelData
10
+ required property int index
11
+ x: index * (width + Theme.spacing.gap)
12
+ height: root.height
13
+ width: Math.min(Math.max(110, Theme.font.body * 8), (root.width - 2*Theme.spacing.gap)/3)
14
+ color: Theme.color.surface
15
+ Text { anchors.centerIn:parent; text:modelData.label; color:Theme.color.foreground; font {family:Theme.font.family;pixelSize:Theme.font.body} }
16
+ MouseArea { anchors.fill:parent; onClicked:if(root.trash)root.trash.action(modelData.action) }
17
+ }
18
+ }
19
+ }