flea-qml 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (625) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +478 -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 +402 -0
  51. package/src/archiveops.js +697 -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 +129 -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/transfer.js +669 -0
  82. package/src/trash-record.js +41 -0
  83. package/src/trash.js +210 -0
  84. package/src/trashbrowse.js +326 -0
  85. package/src/uistate.js +393 -0
  86. package/src/uistore.js +168 -0
  87. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  88. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  89. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  90. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  91. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  92. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  93. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  94. package/src/vendor/pele.js +145 -0
  95. package/src/vendor/temml.mjs +14708 -0
  96. package/src/watch.js +98 -0
  97. package/src/wayland/client.js +109 -0
  98. package/src/wayland/protocol.js +55 -0
  99. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  100. package/src/wayland/protocols/wayland.xml +3248 -0
  101. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  102. package/src/wayland/resource-io.js +6 -0
  103. package/src/wayland/socket-discovery.js +26 -0
  104. package/src/wayland/transport-posix.js +120 -0
  105. package/src/wayland/transport-win32.js +11 -0
  106. package/src/wayland/transport.js +3 -0
  107. package/src/wayland/win32-resources.js +80 -0
  108. package/src/wayland/win32-wire.js +62 -0
  109. package/test/archive.test.js +80 -0
  110. package/test/bench-markdown-scroll.mjs +190 -0
  111. package/test/clip-host.test.js +113 -0
  112. package/test/code-preview.test.js +35 -0
  113. package/test/compare-ui-paths.test.js +24 -0
  114. package/test/compare.test.js +54 -0
  115. package/test/delete-trash.test.js +67 -0
  116. package/test/dirsize.test.js +138 -0
  117. package/test/file-ops.test.js +241 -0
  118. package/test/find-contents.test.js +161 -0
  119. package/test/find-input.test.js +66 -0
  120. package/test/fixtures/pele-parity.json +1006 -0
  121. package/test/highlight-launcher.test.js +35 -0
  122. package/test/html-preview.test.js +22 -0
  123. package/test/input.test.js +15 -0
  124. package/test/keymap.test.js +26 -0
  125. package/test/language-icons.test.js +36 -0
  126. package/test/launcher.test.js +51 -0
  127. package/test/manual-code-scroll.mjs +28 -0
  128. package/test/manual-column-scroll.mjs +24 -0
  129. package/test/manual-compare-ui.mjs +20 -0
  130. package/test/manual-find-status.mjs +37 -0
  131. package/test/manual-html-security.mjs +23 -0
  132. package/test/manual-html-ui.mjs +30 -0
  133. package/test/manual-syntax-preview.mjs +26 -0
  134. package/test/manual-trash-ui.mjs +86 -0
  135. package/test/markdown-diagrams.test.js +93 -0
  136. package/test/markdown-html.test.js +105 -0
  137. package/test/mathml.test.js +92 -0
  138. package/test/menu-transfer.test.js +57 -0
  139. package/test/office-preview.test.js +154 -0
  140. package/test/pele-parity.test.js +28 -0
  141. package/test/places.test.js +134 -0
  142. package/test/preview-keys.test.js +60 -0
  143. package/test/preview-language.test.js +22 -0
  144. package/test/status-filename.test.js +20 -0
  145. package/test/transfer-replace-merge.test.js +187 -0
  146. package/test/trash-address.test.js +23 -0
  147. package/test/trash-view.test.js +37 -0
  148. package/test/trashbrowse.test.js +135 -0
  149. package/ui/AboutFacts.qml +78 -0
  150. package/ui/ArchiveCards.qml +68 -0
  151. package/ui/ArchiveCompressDialog.qml +378 -0
  152. package/ui/ArchivePasswordDialog.qml +257 -0
  153. package/ui/Backend.qml +771 -0
  154. package/ui/BulkOldNames.qml +47 -0
  155. package/ui/BulkRenameDialog.qml +380 -0
  156. package/ui/CardFrame.qml +67 -0
  157. package/ui/CardScroll.qml +79 -0
  158. package/ui/CheckBox.qml +73 -0
  159. package/ui/ChromeBar.qml +360 -0
  160. package/ui/ChromeButton.qml +131 -0
  161. package/ui/ChromeModes.qml +43 -0
  162. package/ui/CodeLines.qml +256 -0
  163. package/ui/CodeRefusal.qml +52 -0
  164. package/ui/CollideConfirm.qml +147 -0
  165. package/ui/CollideHost.qml +167 -0
  166. package/ui/ColumnPane.qml +523 -0
  167. package/ui/ColumnRow.qml +380 -0
  168. package/ui/ColumnsArea.qml +594 -0
  169. package/ui/ColumnsLanding.qml +147 -0
  170. package/ui/CompareView.qml +321 -0
  171. package/ui/ContextMenu.qml +743 -0
  172. package/ui/ConvertDialog.qml +456 -0
  173. package/ui/CountedWorker.qml +7 -0
  174. package/ui/Crumb.qml +64 -0
  175. package/ui/CrumbDrop.qml +25 -0
  176. package/ui/CsvTable.qml +394 -0
  177. package/ui/CursorMark.qml +41 -0
  178. package/ui/DefaultClaim.qml +130 -0
  179. package/ui/DeviceLock.qml +47 -0
  180. package/ui/DeviceMounts.qml +540 -0
  181. package/ui/DialogButton.qml +150 -0
  182. package/ui/DialogField.qml +147 -0
  183. package/ui/DialogHeader.qml +31 -0
  184. package/ui/DialogScrim.qml +20 -0
  185. package/ui/DialogTitle.qml +37 -0
  186. package/ui/Divider.qml +9 -0
  187. package/ui/DropInto.qml +126 -0
  188. package/ui/DropTarget.qml +35 -0
  189. package/ui/DropboxMark.qml +60 -0
  190. package/ui/EmptyState.qml +150 -0
  191. package/ui/EscapeHint.qml +11 -0
  192. package/ui/FactsTable.qml +54 -0
  193. package/ui/FastScrollHandler.qml +476 -0
  194. package/ui/FatNamesConfirm.qml +157 -0
  195. package/ui/Favourites.qml +143 -0
  196. package/ui/FigureService.qml +120 -0
  197. package/ui/FileDrag.qml +267 -0
  198. package/ui/FilterStrip.qml +133 -0
  199. package/ui/FitMetrics.qml +22 -0
  200. package/ui/FlashDriveRows.qml +137 -0
  201. package/ui/FlashHost.qml +72 -0
  202. package/ui/FlashImageRow.qml +104 -0
  203. package/ui/FlashRunner.qml +119 -0
  204. package/ui/FlashStepRow.qml +172 -0
  205. package/ui/FlashStrip.qml +38 -0
  206. package/ui/FlashView.qml +397 -0
  207. package/ui/FleaMark.qml +88 -0
  208. package/ui/FlyoutFade.qml +23 -0
  209. package/ui/FontPane.qml +170 -0
  210. package/ui/Glass.qml +114 -0
  211. package/ui/GlassTagger.qml +59 -0
  212. package/ui/Glyph.qml +64 -0
  213. package/ui/GlyphSolid.qml +22 -0
  214. package/ui/GridArea.qml +664 -0
  215. package/ui/GridTile.qml +325 -0
  216. package/ui/GvfsBridge.qml +164 -0
  217. package/ui/Header.qml +433 -0
  218. package/ui/HoverTimers.qml +39 -0
  219. package/ui/HtmlClip.qml +30 -0
  220. package/ui/HtmlPane.qml +63 -0
  221. package/ui/HyprlandMark.qml +45 -0
  222. package/ui/Ipc.qml +1005 -0
  223. package/ui/IpcControls.qml +7 -0
  224. package/ui/IpcMenuState.qml +48 -0
  225. package/ui/IpcPreviewState.qml +185 -0
  226. package/ui/JumpPath.qml +51 -0
  227. package/ui/KeymapSheet.qml +561 -0
  228. package/ui/KeymapSheetResult.qml +89 -0
  229. package/ui/KindStack.qml +40 -0
  230. package/ui/LangMark.qml +51 -0
  231. package/ui/LangMarkLoader.qml +26 -0
  232. package/ui/List.qml +480 -0
  233. package/ui/LoadingState.qml +54 -0
  234. package/ui/LocalSend.qml +56 -0
  235. package/ui/LocalSendMark.qml +73 -0
  236. package/ui/MarkdownBlockView.qml +311 -0
  237. package/ui/MarkdownBlocks.qml +35 -0
  238. package/ui/MarkdownFigure.qml +254 -0
  239. package/ui/MarkdownImages.qml +72 -0
  240. package/ui/MarkdownList.qml +81 -0
  241. package/ui/MarkdownMathsText.qml +97 -0
  242. package/ui/MarkdownPane.qml +83 -0
  243. package/ui/MarkdownPictures.qml +41 -0
  244. package/ui/MarkdownQuote.qml +52 -0
  245. package/ui/MarkdownRemote.qml +82 -0
  246. package/ui/MarkdownSource.qml +155 -0
  247. package/ui/MarkdownTable.qml +222 -0
  248. package/ui/MarkdownTableScroll.qml +64 -0
  249. package/ui/MarkdownTableWheel.qml +126 -0
  250. package/ui/MarkdownText.qml +87 -0
  251. package/ui/MarkdownWorker.js +132 -0
  252. package/ui/MatchText.qml +99 -0
  253. package/ui/MediaSession.qml +38 -0
  254. package/ui/MediaSound.qml +26 -0
  255. package/ui/MediaStrip.qml +191 -0
  256. package/ui/MediaTrack.qml +36 -0
  257. package/ui/MediaVolume.qml +54 -0
  258. package/ui/MenuActionDialog.qml +396 -0
  259. package/ui/MenuEdgeFade.qml +24 -0
  260. package/ui/MenuHost.qml +227 -0
  261. package/ui/MenuRow.qml +351 -0
  262. package/ui/MountListing.qml +100 -0
  263. package/ui/NetworkDialog.qml +435 -0
  264. package/ui/NetworkForm.qml +426 -0
  265. package/ui/NetworkMounts.qml +913 -0
  266. package/ui/NetworkPlaces.qml +127 -0
  267. package/ui/OpenWithDialog.qml +613 -0
  268. package/ui/Opener.qml +115 -0
  269. package/ui/Pane.qml +1296 -0
  270. package/ui/PaneMenuActions.qml +527 -0
  271. package/ui/PanePath.qml +90 -0
  272. package/ui/PaneRail.qml +239 -0
  273. package/ui/PaneStates.qml +88 -0
  274. package/ui/PaneSwap.qml +339 -0
  275. package/ui/PaneWire.qml +605 -0
  276. package/ui/ParkedWait.qml +9 -0
  277. package/ui/PathEntry.qml +186 -0
  278. package/ui/PathJump.qml +400 -0
  279. package/ui/PdfViewer.qml +297 -0
  280. package/ui/PermissionsDialog.qml +658 -0
  281. package/ui/PermissionsHeadings.qml +31 -0
  282. package/ui/PhoneMounts.qml +67 -0
  283. package/ui/PickerAnswers.qml +75 -0
  284. package/ui/PickerBox.qml +26 -0
  285. package/ui/PickerChrome.qml +295 -0
  286. package/ui/PickerFilter.qml +97 -0
  287. package/ui/PickerGrid.qml +308 -0
  288. package/ui/PickerHeader.qml +28 -0
  289. package/ui/PickerLifecycle.qml +27 -0
  290. package/ui/PickerList.qml +218 -0
  291. package/ui/PickerListing.qml +149 -0
  292. package/ui/PickerPathBar.qml +127 -0
  293. package/ui/PickerPlaces.qml +189 -0
  294. package/ui/PickerRecent.qml +92 -0
  295. package/ui/PickerSave.qml +101 -0
  296. package/ui/PickerSelection.qml +155 -0
  297. package/ui/PickerWindow.qml +846 -0
  298. package/ui/PowerSectorsReader.qml +10 -0
  299. package/ui/PrebuildDriver.qml +146 -0
  300. package/ui/Preview.qml +1004 -0
  301. package/ui/PreviewAnimation.qml +24 -0
  302. package/ui/PreviewArchive.qml +66 -0
  303. package/ui/PreviewColumn.qml +551 -0
  304. package/ui/PreviewEdge.qml +63 -0
  305. package/ui/PreviewFacts.qml +30 -0
  306. package/ui/PreviewFactsLine.qml +34 -0
  307. package/ui/PreviewHtml.qml +233 -0
  308. package/ui/PreviewImage.qml +253 -0
  309. package/ui/PreviewImagePan.qml +13 -0
  310. package/ui/PreviewImageSharp.qml +32 -0
  311. package/ui/PreviewLines.qml +106 -0
  312. package/ui/PreviewMarkdown.qml +706 -0
  313. package/ui/PreviewMedia.qml +250 -0
  314. package/ui/PreviewPdf.qml +482 -0
  315. package/ui/PreviewScroll.qml +201 -0
  316. package/ui/PreviewSwap.qml +335 -0
  317. package/ui/PreviewText.qml +149 -0
  318. package/ui/ProtocolChip.qml +18 -0
  319. package/ui/QuickLookArchive.qml +38 -0
  320. package/ui/QuickLookBar.qml +103 -0
  321. package/ui/QuickLookPrepare.qml +355 -0
  322. package/ui/QuickLookSwap.qml +10 -0
  323. package/ui/QuickLookWarm.qml +65 -0
  324. package/ui/RailDrop.qml +155 -0
  325. package/ui/RailHeading.qml +19 -0
  326. package/ui/RailPlaces.qml +38 -0
  327. package/ui/RailTrashRow.qml +67 -0
  328. package/ui/RenameField.qml +353 -0
  329. package/ui/RendererRetry.qml +11 -0
  330. package/ui/ResizeHandle.qml +52 -0
  331. package/ui/Reveal.qml +54 -0
  332. package/ui/Row.qml +476 -0
  333. package/ui/RowDrag.qml +213 -0
  334. package/ui/RowDropTarget.qml +27 -0
  335. package/ui/RunCard.qml +286 -0
  336. package/ui/Scripts.qml +65 -0
  337. package/ui/SearchStrip.qml +232 -0
  338. package/ui/SelectionBand.qml +140 -0
  339. package/ui/SelectionPreview.qml +370 -0
  340. package/ui/SettingsFavourite.qml +122 -0
  341. package/ui/SettingsGroup.qml +96 -0
  342. package/ui/SettingsHint.qml +12 -0
  343. package/ui/SettingsMetrics.qml +29 -0
  344. package/ui/SettingsPane.qml +178 -0
  345. package/ui/SettingsPanel.qml +547 -0
  346. package/ui/SettingsRail.qml +128 -0
  347. package/ui/SettingsRow.qml +409 -0
  348. package/ui/SettingsRuler.qml +85 -0
  349. package/ui/SettingsSegment.qml +85 -0
  350. package/ui/ShareBrowser.qml +113 -0
  351. package/ui/ShareLink.qml +67 -0
  352. package/ui/ShelfPins.qml +101 -0
  353. package/ui/Sidebar.qml +732 -0
  354. package/ui/SidebarRow.qml +358 -0
  355. package/ui/Spinner.qml +60 -0
  356. package/ui/StableModel.qml +89 -0
  357. package/ui/StateMessage.qml +84 -0
  358. package/ui/StatusBar.qml +512 -0
  359. package/ui/StatusFileName.qml +27 -0
  360. package/ui/TabBar.qml +673 -0
  361. package/ui/TabDragGeometry.qml +60 -0
  362. package/ui/TagMenuMark.qml +52 -0
  363. package/ui/TagRailSection.qml +53 -0
  364. package/ui/TagSlot.qml +40 -0
  365. package/ui/TagState.qml +150 -0
  366. package/ui/TagSwatch.qml +28 -0
  367. package/ui/Taildrop.qml +86 -0
  368. package/ui/TailscaleMark.qml +50 -0
  369. package/ui/Theme.qml +460 -0
  370. package/ui/ThemeColors.qml +43 -0
  371. package/ui/TransferCard.qml +239 -0
  372. package/ui/TrashConfirm.qml +129 -0
  373. package/ui/TrashDropHandler.qml +11 -0
  374. package/ui/TrashHost.qml +202 -0
  375. package/ui/TrashMonitor.qml +30 -0
  376. package/ui/TrashToolbar.qml +19 -0
  377. package/ui/TrashView.qml +492 -0
  378. package/ui/UnlockDialog.qml +450 -0
  379. package/ui/UnlockHost.qml +206 -0
  380. package/ui/UpdateCheck.qml +97 -0
  381. package/ui/UserDirs.qml +25 -0
  382. package/ui/ViewState.qml +565 -0
  383. package/ui/ViewportScrollBar.qml +195 -0
  384. package/ui/ViewportScrollBars.qml +25 -0
  385. package/ui/WebPreview.qml +33 -0
  386. package/ui/WebPreviewView.qml +26 -0
  387. package/ui/WindowBody.qml +827 -0
  388. package/ui/WindowDrag.qml +35 -0
  389. package/ui/boot/Commons/AudioNodes.qml +50 -0
  390. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  391. package/ui/boot/Commons/Border.qml +243 -0
  392. package/ui/boot/Commons/BorderGeometry.js +373 -0
  393. package/ui/boot/Commons/Color.qml +258 -0
  394. package/ui/boot/Commons/I18n.qml +118 -0
  395. package/ui/boot/Commons/InputDevices.qml +112 -0
  396. package/ui/boot/Commons/InputMethodState.qml +38 -0
  397. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  398. package/ui/boot/Commons/Numbers.js +19 -0
  399. package/ui/boot/Commons/Plural.js +91 -0
  400. package/ui/boot/Commons/ShellIpc.qml +12 -0
  401. package/ui/boot/Commons/Style.qml +561 -0
  402. package/ui/boot/Commons/Translations.qml +100 -0
  403. package/ui/boot/Commons/UntypedInput.qml +152 -0
  404. package/ui/boot/Commons/Util.qml +188 -0
  405. package/ui/boot/Commons/qmldir +13 -0
  406. package/ui/boot/LICENSE-omarchy +20 -0
  407. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  408. package/ui/boot/Ui/BarIconButton.qml +106 -0
  409. package/ui/boot/Ui/BarIconContent.qml +38 -0
  410. package/ui/boot/Ui/BarIndicator.qml +53 -0
  411. package/ui/boot/Ui/BarWidget.qml +50 -0
  412. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  413. package/ui/boot/Ui/BorderSurface.qml +40 -0
  414. package/ui/boot/Ui/Button.qml +224 -0
  415. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  416. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  417. package/ui/boot/Ui/CursorSurface.qml +42 -0
  418. package/ui/boot/Ui/Dropdown.qml +245 -0
  419. package/ui/boot/Ui/FilterInput.qml +26 -0
  420. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  421. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  422. package/ui/boot/Ui/MultiSelect.qml +624 -0
  423. package/ui/boot/Ui/NumberField.qml +86 -0
  424. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  425. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  426. package/ui/boot/Ui/Panel.qml +60 -0
  427. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  428. package/ui/boot/Ui/PanelController.qml +16 -0
  429. package/ui/boot/Ui/PanelHero.qml +112 -0
  430. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  431. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  432. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  433. package/ui/boot/Ui/PanelSlider.qml +156 -0
  434. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  435. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  436. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  437. package/ui/boot/Ui/PopupCard.qml +181 -0
  438. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  439. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  440. package/ui/boot/Ui/SizedImage.qml +125 -0
  441. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  442. package/ui/boot/Ui/TextField.qml +58 -0
  443. package/ui/boot/Ui/Toggle.qml +118 -0
  444. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  445. package/ui/boot/Ui/WidgetButton.qml +172 -0
  446. package/ui/boot/Ui/qmldir +41 -0
  447. package/ui/boot/fleatab.qml +50 -0
  448. package/ui/boot/picker.qml +39 -0
  449. package/ui/boot/shell.qml +122 -0
  450. package/ui/boot/tabtearoff.qml +51 -0
  451. package/ui/js/Anchor.js +379 -0
  452. package/ui/js/AnchorHold.js +162 -0
  453. package/ui/js/Archive.js +157 -0
  454. package/ui/js/ArchiveCard.js +44 -0
  455. package/ui/js/ArchiveOps.js +44 -0
  456. package/ui/js/BackReturn.js +118 -0
  457. package/ui/js/BulkRename.js +100 -0
  458. package/ui/js/Buttons.js +54 -0
  459. package/ui/js/ChromeKeys.js +116 -0
  460. package/ui/js/ClipMarks.js +73 -0
  461. package/ui/js/ClipSelection.js +67 -0
  462. package/ui/js/Clipboard.js +179 -0
  463. package/ui/js/Cloud.js +97 -0
  464. package/ui/js/CodeRuns.js +72 -0
  465. package/ui/js/Collide.js +212 -0
  466. package/ui/js/ColumnFit.js +28 -0
  467. package/ui/js/ColumnMenu.js +111 -0
  468. package/ui/js/Columns.js +298 -0
  469. package/ui/js/ColumnsCarry.js +103 -0
  470. package/ui/js/Compare.js +74 -0
  471. package/ui/js/Contrast.js +157 -0
  472. package/ui/js/ControlReader.js +42 -0
  473. package/ui/js/Convert.js +52 -0
  474. package/ui/js/CopyAs.js +52 -0
  475. package/ui/js/Crumbs.js +178 -0
  476. package/ui/js/Csv.js +231 -0
  477. package/ui/js/Density.js +44 -0
  478. package/ui/js/Devices.js +440 -0
  479. package/ui/js/DirSizes.js +180 -0
  480. package/ui/js/Drag.js +364 -0
  481. package/ui/js/DragOut.js +166 -0
  482. package/ui/js/DropDev.js +52 -0
  483. package/ui/js/Dropbox.js +41 -0
  484. package/ui/js/Eject.js +217 -0
  485. package/ui/js/Errors.js +188 -0
  486. package/ui/js/ExtThumbs.js +115 -0
  487. package/ui/js/Facts.js +402 -0
  488. package/ui/js/FigureDate.js +14 -0
  489. package/ui/js/Filter.js +285 -0
  490. package/ui/js/Find.js +46 -0
  491. package/ui/js/Flash.js +184 -0
  492. package/ui/js/FlashCard.js +79 -0
  493. package/ui/js/FlashEndings.js +125 -0
  494. package/ui/js/FlashPane.js +116 -0
  495. package/ui/js/FleaBin.js +12 -0
  496. package/ui/js/Focus.js +502 -0
  497. package/ui/js/FolderSorts.js +108 -0
  498. package/ui/js/FolderViews.js +44 -0
  499. package/ui/js/Format.js +305 -0
  500. package/ui/js/Fuzzy.js +141 -0
  501. package/ui/js/GlassTag.js +72 -0
  502. package/ui/js/Grid.js +33 -0
  503. package/ui/js/GridGeometry.js +90 -0
  504. package/ui/js/GridNames.js +153 -0
  505. package/ui/js/GvfsBridge.js +200 -0
  506. package/ui/js/HostClipboard.js +29 -0
  507. package/ui/js/Icons.js +218 -0
  508. package/ui/js/Input.js +50 -0
  509. package/ui/js/Jump.js +200 -0
  510. package/ui/js/KeyName.js +64 -0
  511. package/ui/js/Keymap.js +401 -0
  512. package/ui/js/Kinds.js +164 -0
  513. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  514. package/ui/js/LangMarkPaths.js +47 -0
  515. package/ui/js/LangMarks.js +120 -0
  516. package/ui/js/Lists.js +11 -0
  517. package/ui/js/LocalSend.js +39 -0
  518. package/ui/js/LockedMenu.js +63 -0
  519. package/ui/js/MakeDefault.js +186 -0
  520. package/ui/js/MarkBox.js +7 -0
  521. package/ui/js/Markdown.js +119 -0
  522. package/ui/js/MarkdownLists.js +86 -0
  523. package/ui/js/MarkdownMaths.js +58 -0
  524. package/ui/js/MarkdownPictures.js +32 -0
  525. package/ui/js/MarkdownPrepared.js +139 -0
  526. package/ui/js/MarkdownTableFit.js +34 -0
  527. package/ui/js/Marks.js +148 -0
  528. package/ui/js/Marquee.js +96 -0
  529. package/ui/js/Match.js +93 -0
  530. package/ui/js/Material.js +109 -0
  531. package/ui/js/MdBlocks.js +437 -0
  532. package/ui/js/MdBreak.js +71 -0
  533. package/ui/js/MdChunks.js +70 -0
  534. package/ui/js/MdContainer.js +137 -0
  535. package/ui/js/MdDocument.js +337 -0
  536. package/ui/js/MdEmph.js +97 -0
  537. package/ui/js/MdEntity.js +108 -0
  538. package/ui/js/MdEntityTable.js +223 -0
  539. package/ui/js/MdEscape.js +59 -0
  540. package/ui/js/MdFront.js +37 -0
  541. package/ui/js/MdHold.js +32 -0
  542. package/ui/js/MdHtml.js +307 -0
  543. package/ui/js/MdHtmlBlock.js +290 -0
  544. package/ui/js/MdHtmlHeading.js +53 -0
  545. package/ui/js/MdHtmlImage.js +179 -0
  546. package/ui/js/MdInline.js +154 -0
  547. package/ui/js/MdItems.js +212 -0
  548. package/ui/js/MdLeaf.js +309 -0
  549. package/ui/js/MdLink.js +196 -0
  550. package/ui/js/MdMath.js +67 -0
  551. package/ui/js/MdParagraphs.js +138 -0
  552. package/ui/js/MdRefs.js +232 -0
  553. package/ui/js/MdResolve.js +226 -0
  554. package/ui/js/MdRun.js +300 -0
  555. package/ui/js/MdUrl.js +226 -0
  556. package/ui/js/Menu.js +514 -0
  557. package/ui/js/MenuContext.js +85 -0
  558. package/ui/js/MenuFit.js +13 -0
  559. package/ui/js/MenuRefresh.js +74 -0
  560. package/ui/js/MenuWheel.js +35 -0
  561. package/ui/js/Messages.js +186 -0
  562. package/ui/js/Motion.js +16 -0
  563. package/ui/js/Mounts.js +320 -0
  564. package/ui/js/MouseNav.js +13 -0
  565. package/ui/js/Nav.js +329 -0
  566. package/ui/js/NetFs.js +26 -0
  567. package/ui/js/OpenWith.js +83 -0
  568. package/ui/js/Ops.js +585 -0
  569. package/ui/js/Palette.js +33 -0
  570. package/ui/js/PathBar.js +260 -0
  571. package/ui/js/Permissions.js +179 -0
  572. package/ui/js/Phones.js +112 -0
  573. package/ui/js/Picker.js +353 -0
  574. package/ui/js/PlaceMenu.js +96 -0
  575. package/ui/js/Places.js +265 -0
  576. package/ui/js/PreviewAsk.js +21 -0
  577. package/ui/js/PreviewKeys.js +155 -0
  578. package/ui/js/PreviewSettle.js +29 -0
  579. package/ui/js/PreviewSwap.js +101 -0
  580. package/ui/js/PropertyFacts.js +144 -0
  581. package/ui/js/Protocols.js +296 -0
  582. package/ui/js/QuickLookKinds.js +27 -0
  583. package/ui/js/QuickLookRect.js +24 -0
  584. package/ui/js/RailDrop.js +51 -0
  585. package/ui/js/RailKeys.js +87 -0
  586. package/ui/js/RailMenu.js +133 -0
  587. package/ui/js/Recent.js +135 -0
  588. package/ui/js/RecentMode.js +156 -0
  589. package/ui/js/Reload.js +60 -0
  590. package/ui/js/Renderer.js +9 -0
  591. package/ui/js/RunFile.js +143 -0
  592. package/ui/js/Scripts.js +28 -0
  593. package/ui/js/Scroll.js +304 -0
  594. package/ui/js/ScrollOff.js +69 -0
  595. package/ui/js/Search.js +478 -0
  596. package/ui/js/Selection.js +97 -0
  597. package/ui/js/Settings.js +476 -0
  598. package/ui/js/SettingsAbout.js +97 -0
  599. package/ui/js/SettingsShelf.js +52 -0
  600. package/ui/js/SheetKeys.js +42 -0
  601. package/ui/js/SheetQuery.js +281 -0
  602. package/ui/js/ShelfPile.js +27 -0
  603. package/ui/js/SlowClick.js +53 -0
  604. package/ui/js/SlowOp.js +6 -0
  605. package/ui/js/Sort.js +134 -0
  606. package/ui/js/Startup.js +63 -0
  607. package/ui/js/StateWords.js +17 -0
  608. package/ui/js/Status.js +238 -0
  609. package/ui/js/Swap.js +166 -0
  610. package/ui/js/TabMove.js +39 -0
  611. package/ui/js/Tabs.js +736 -0
  612. package/ui/js/Tags.js +134 -0
  613. package/ui/js/Taildrop.js +128 -0
  614. package/ui/js/Tap.js +174 -0
  615. package/ui/js/TextSize.js +107 -0
  616. package/ui/js/Thumbs.js +158 -0
  617. package/ui/js/Transfer.js +172 -0
  618. package/ui/js/Trash.js +81 -0
  619. package/ui/js/TrashDates.js +47 -0
  620. package/ui/js/TrashDrop.js +64 -0
  621. package/ui/js/UiState.js +438 -0
  622. package/ui/js/Unlock.js +152 -0
  623. package/ui/js/Update.js +130 -0
  624. package/ui/js/UserDirs.js +56 -0
  625. package/ui/qmldir +228 -0
@@ -0,0 +1,239 @@
1
+ import QtQuick
2
+ import "." as Flea
3
+ import "js/Focus.js" as Focus
4
+ import "js/Drag.js" as DragOps
5
+ import "js/Eject.js" as Eject
6
+ import "js/RailKeys.js" as RailKeys
7
+ import "js/RailMenu.js" as RailMenu
8
+ import "js/Motion.js" as Motion
9
+
10
+ // The pane's rail, and the two ways it comes and goes. With Auto-hide sidebar off it is part of the
11
+ // pane and Show sidebar governs it, which is 0.2.1's behaviour. Directive 77, the requirement:
12
+ // hovering over the left side shows it, like every other autohide feature in every other file
13
+ // manager. So with the switch on the rail is withdrawn at every width, the pointer at the
14
+ // window's left edge reveals it over the pane rather than reflowing it, and it withdraws a moment
15
+ // after the pointer or the keyboard leaves. It slides in and out on x, and is built once.
16
+ Item {
17
+ id: root
18
+
19
+ property var pane: null
20
+ // The window-long network host from ui/WindowBody.qml, outliving the Sidebar loader so a late mount answer still lands.
21
+ property var service: null
22
+ readonly property bool overlay: ViewState.railAutoHide
23
+ readonly property bool hidden: root.overlay ? !root.revealed : ViewState.railHidden
24
+ // An overlay owes the pane no width, so the listing never reflows as the rail comes and goes.
25
+ // This item is that width, and the rail itself draws past it.
26
+ readonly property real inset: root.overlay ? 0 : rail.width
27
+ // Zero whenever the rail is withdrawn, though an auto-hide Sidebar stays built through the slide and after it.
28
+ readonly property real railWidth: root.hidden ? 0 : rail.width
29
+ readonly property var item: root.hidden ? null : rail.item
30
+ // Where the sliding rail stands, which tests and railState read.
31
+ readonly property real railX: rail.x
32
+
33
+ function focusPress(p) {
34
+ var pane = root.pane
35
+ if (p.x < 0 || p.y < 0 || p.x >= pane.width || p.y >= pane.height) return
36
+ // The shown Sidebar can extend past its zero-width overlay host.
37
+ var sidebar = root.item
38
+ var r = sidebar ? sidebar.mapFromItem(pane, p.x, p.y) : null
39
+ if (!root.hidden && r && r.x >= 0 && r.y >= 0 && r.x < sidebar.width && r.y < sidebar.height) {
40
+ if (root.overlay) pane.railPane.focusView = Focus.RAIL
41
+ } else { pane.focusView = Focus.LIST; pane.focusRequested() }
42
+ }
43
+
44
+ // Long enough that crossing the edge on the way somewhere else does not flash the rail, and that
45
+ // leaving it by a pixel on the way to a row does not drop it.
46
+ readonly property int settleMs: 220
47
+ property bool revealed: false
48
+ // The rail's own context menu takes the pointer with it, so without this the rail withdraws out
49
+ // from under the menu it just opened.
50
+ readonly property bool menuHere: root.pane !== null && root.pane.contextMenu().opened
51
+ && root.pane.contextMenu().forRail
52
+ // The rail stays up while the pointer is on it, which is the other half of the reveal.
53
+ readonly property bool over: railHover.hovered
54
+ // A release in flight keeps a revealed overlay rail up; the hold below is what keeps the Sidebar when it hides anyway.
55
+ readonly property bool releasing: rail.item !== null && (rail.item as Flea.Sidebar).releasing === true
56
+ // Keeps the Sidebar and its gio, undrawn, through a hide mid-release; latched from the release's edges, as a binding through hidden would loop.
57
+ property bool releaseHold: false
58
+ readonly property bool held: root.releaseHold && root.hidden
59
+ function holdRelease(on) {
60
+ if (on) { root.releaseHold = true; return }
61
+ // Past the signal that ended it, where the unload the hold was keeping would destroy the sender mid-emission.
62
+ Qt.callLater(function () { if (!root.releasing) root.releaseHold = false })
63
+ }
64
+ // The pointer has rested on the edge for the dwell, and stays so until it leaves the strip.
65
+ property bool edgeShown: false
66
+ // The Sidebar is built by the first strip entry or Tab and then kept, so later reveals spawn nothing and keep cursor and scroll.
67
+ property bool built: false
68
+ readonly property bool dragHere: dragReveal.containsDrag || root.rowDragOver(rail.item)
69
+ readonly property bool pointerWanted: root.edgeShown || root.over || root.menuHere || root.releasing || root.dragHere
70
+ readonly property bool focusWanted: root.pane !== null && root.pane.focusView === Focus.RAIL
71
+ readonly property bool wanted: root.pointerWanted || root.focusWanted
72
+ // 0 withdrawn to 1 shown; the rail's x follows it, and a reversal starts from where it stands.
73
+ property real slide: root.revealed ? 1 : 0
74
+ Behavior on slide {
75
+ enabled: !Theme.reducedMotion
76
+ NumberAnimation { duration: root.revealed ? Motion.durMs.open : Motion.durMs.close; easing.type: Easing.OutCubic }
77
+ }
78
+
79
+ onWantedChanged: {
80
+ if (root.wanted) { settle.stop(); root.revealed = true }
81
+ else if (root.revealed) settle.restart()
82
+ }
83
+ onRevealedChanged: if (root.revealed) root.built = true
84
+ // Tab out has no pointer to wait for, so the rail slides away at once; the pointer on the rail keeps it up through pointerWanted.
85
+ onFocusWantedChanged: if (!root.focusWanted && !root.pointerWanted && root.revealed) { settle.stop(); root.revealed = false }
86
+ // The switch itself is a fresh start either way: nothing is revealed until the pointer asks.
87
+ onOverlayChanged: { dwell.stop(); root.edgeShown = false; root.built = false; root.revealed = false }
88
+
89
+ anchors { top: parent.top; bottom: parent.bottom }
90
+ width: root.inset
91
+
92
+ Timer {
93
+ id: settle
94
+ interval: root.settleMs
95
+ onTriggered: root.revealed = false
96
+ }
97
+ Timer {
98
+ id: dwell
99
+ interval: Motion.dwellMs
100
+ onTriggered: root.edgeShown = true
101
+ }
102
+
103
+ // The strip that answers the pointer, at the window's own left edge and only while the switch is
104
+ // on. It holds no MouseArea, so a click on the listing under it still lands on the listing.
105
+ Item {
106
+ id: strip
107
+ anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
108
+ width: Theme.spacing.rowPaddingX
109
+ enabled: root.overlay
110
+ HoverHandler {
111
+ id: edge
112
+ enabled: root.overlay
113
+ // Entering also builds the Sidebar, so the dwell covers the build; a rail already up needs no dwell.
114
+ onHoveredChanged: {
115
+ if (!edge.hovered) { dwell.stop(); root.edgeShown = false; return }
116
+ root.built = true
117
+ if (root.revealed) root.edgeShown = true
118
+ else dwell.restart()
119
+ }
120
+ }
121
+ }
122
+
123
+ // Below the rail so Trash answers first; refusing here keeps an overlay rail drop from falling through to the listing row under it.
124
+ DropArea {
125
+ id: dragReveal
126
+ objectName: "dragReveal"
127
+ anchors { left: parent.left; top: parent.top; bottom: parent.bottom }
128
+ width: Math.max(strip.width, rail.width)
129
+ enabled: root.overlay
130
+ keys: [DragOps.ROWS_MIME]
131
+ onDropped: function (drop) { drop.accepted = false }
132
+ }
133
+
134
+ Loader {
135
+ id: rail
136
+ anchors { top: parent.top; bottom: parent.bottom }
137
+ // The slide moves x only, so the rail's width and rows never change with the motion; visible drops when the hide slide ends.
138
+ x: root.overlay ? (root.slide - 1) * width : 0
139
+ width: item && !root.held ? item.implicitWidth : 0
140
+ visible: !root.held && (!root.overlay || root.slide > 0)
141
+ active: root.pane !== null && !root.pane.listOnly && root.pane.sharedSidebar === null
142
+ && (root.overlay ? (root.built || root.releaseHold) : (!root.hidden || root.releaseHold))
143
+ // A Sidebar that went for another reason took its release with it, so there is nothing left to hold.
144
+ onItemChanged: if (item === null) root.releaseHold = false
145
+ sourceComponent: Flea.Sidebar {
146
+ id: sidebar
147
+ backend: root.pane.backend
148
+ navigationPane: root.pane.railPane
149
+ service: root.service
150
+ polling: !root.overlay || root.revealed
151
+ focused: root.pane.railPane.focusView === Focus.RAIL
152
+ trashActive: root.pane.railPane.trash.browsing
153
+ onOpened: function(path) { RailKeys.openFrom(root.pane.railPane, path, sidebar) }
154
+ // Sidebar040: the rail's Recent row answers with the history's own paths, which the
155
+ // pane lists with listpaths; focus follows into the folder the way a mount's open does.
156
+ onRecentRequested: function (paths, requester, visits) { var target = requester || root.pane.railPane; target.openRecent(paths, visits); RailKeys.landed(target, sidebar) }
157
+ onTrashRequested: root.pane.railPane.trash.open()
158
+ onMessage: function(text, isError) { RailKeys.messaged(sidebar, isError); root.pane.message(text, isError) }
159
+ onOperationResult: function(headline, detail, isError) { RailKeys.messaged(sidebar, isError); root.pane.operationResult(headline, detail, isError) }
160
+ onForgetMessage: function(text) { root.pane.forgetMessage(text) }
161
+ menu: root.pane.railPane.contextMenu()
162
+ onRenameFinished: root.pane.railPane.listArea.forceActiveFocus()
163
+ // The pane's own PointHandler never sees a press a row grabs, so an overlay rail claims the keyboard itself; a docked rail leaves it.
164
+ onPressed: if (root.overlay) root.pane.railPane.focusView = Focus.RAIL
165
+ onReleasingChanged: root.holdRelease(sidebar.releasing)
166
+ }
167
+ // Disabled with the Sidebar, so an unload under the pointer clears it; a handler inside the Sidebar dies without reporting.
168
+ HoverHandler { id: railHover; enabled: rail.active && !root.held }
169
+ }
170
+
171
+ // The rail's Sidebar renders the window-long service below; the service's answers
172
+ // route once by origin in ui/WindowBody.qml, so this file carries no Connections of its
173
+ // own and a dual-view open cannot fire twice.
174
+
175
+ // Ctrl+E from a listing with the rail hidden. The rail Loader above unloads the Sidebar with
176
+ // its DeviceMounts poll, eject verdict state and releaseChosen, so the key has nothing to read
177
+ // and nothing to release through. It spins this transient instead: the same DeviceMounts
178
+ // component the rail uses, never a second poller, alive only for this flow. Its first listing
179
+ // resolves the holding volume through the same Mounts.holding and Mounts.railMenu verdict and
180
+ // releases through the same RailMenu.release and devices.eject path with the same messages;
181
+ // the verdict lands, the host unloads, and while nothing is pressed nothing here exists.
182
+ property bool hiddenEjectArmed: false
183
+ function unloadHiddenEject() {
184
+ // Past the signal that asked, which a synchronous unload would destroy mid-emission; a release in flight keeps the host, whose end kills its gio.
185
+ Qt.callLater(function () {
186
+ if (!hiddenEject.item || !hiddenEject.item.releasing)
187
+ hiddenEject.active = false
188
+ })
189
+ }
190
+ function ejectHidden() {
191
+ if (!root.pane)
192
+ return
193
+ if (!hiddenEject.active) {
194
+ hiddenEjectArmed = true
195
+ hiddenEject.active = true
196
+ return
197
+ }
198
+ // A flow is already waiting on its first listing, or an eject is running: re-resolve
199
+ // against the freshest entries, where the service refuses a concurrent eject itself.
200
+ hiddenEjectArmed = true
201
+ if (hiddenEject.item && hiddenEject.item.firstAnswered)
202
+ root.hiddenEjectReady()
203
+ }
204
+ function hiddenEjectReady() {
205
+ var device = hiddenEject.item
206
+ if (!device || !hiddenEjectArmed)
207
+ return
208
+ hiddenEjectArmed = false
209
+ // Sidebar shape with no network entries: only a volume resolves; a share keeps the pane's own answer.
210
+ var adapter = { entries: device.entries, deviceEntries: device.entries, networkEntries: [] }
211
+ adapter.releaseChosen = function (action, key) { RailMenu.release(action, key, device, null, adapter) }
212
+ Eject.release(root.pane, adapter, false)
213
+ root.unloadHiddenEject()
214
+ }
215
+ Loader {
216
+ id: hiddenEject
217
+ active: false
218
+ sourceComponent: Flea.DeviceMounts {
219
+ showUnmounted: ((ViewState.state.places || {}).showUnmounted !== false)
220
+ onOpened: function (path) { root.pane.open(path) }
221
+ onMessage: function (text, isError) { root.pane.message(text, isError) }
222
+ onOperationResult: function (headline, detail, isError) { root.pane.operationResult(headline, detail, isError) }
223
+ onReleasingChanged: root.unloadHiddenEject()
224
+ onForgetMessage: function (text) { root.pane.forgetMessage(text) }
225
+ onQuiesce: function (path) { root.pane.quiesceVolume(path) }
226
+ onFirstAnsweredChanged: root.hiddenEjectReady()
227
+ }
228
+ }
229
+
230
+ // A child row DropArea takes the drag from dragReveal; its live containsDrag binding keeps the rail up.
231
+ function rowDragOver(item) {
232
+ if (!item) return false
233
+ if (item.containsDrag === true) return true
234
+ for (var i = 0; i < item.children.length; i++) {
235
+ if (root.rowDragOver(item.children[i])) return true
236
+ }
237
+ return false
238
+ }
239
+ }
@@ -0,0 +1,88 @@
1
+ import QtQuick
2
+ import "." as Flea
3
+ import "js/Swap.js" as Swap
4
+ import "js/Search.js" as Search
5
+ import "js/KeyName.js" as KeyName
6
+
7
+ // What a pane draws instead of rows: the empty hero, the reading spiral and the failure block. All
8
+ // three sit over the same slot and are gated on the same listing state, so they live together and
9
+ // ui/Pane.qml carries one child rather than three.
10
+ Item {
11
+ id: root
12
+
13
+ required property var pane
14
+ property bool trashOpen: false
15
+
16
+ readonly property alias emptyItem: emptyState
17
+ readonly property alias messageItem: paneMessage
18
+
19
+ // Every Columns hero centres on the pane while it clears the listed columns and their rules, else on the free area right of them.
20
+ readonly property var columns: root.pane.viewMode === "columns" ? root.pane.columnsArea : null
21
+ readonly property real heroWidth: emptyState.visible ? emptyState.naturalWidth
22
+ : loadingState.visible ? loadingState.naturalWidth : paneMessage.naturalWidth
23
+ readonly property bool overActive: !!root.columns && root.columns.activeX > 0
24
+ && root.pane.listSlot.width / 2 - root.heroWidth / 2 < root.columns.activeX
25
+ readonly property real heroX: root.pane.listSlot.x + (root.overActive ? root.columns.activeX : 0)
26
+ readonly property real heroSpan: root.overActive ? root.pane.listSlot.width - root.columns.activeX : root.pane.listSlot.width
27
+
28
+ Flea.EmptyState {
29
+ id: emptyState
30
+ x: root.heroX
31
+ y: root.pane.listSlot.y
32
+ width: root.heroSpan
33
+ height: root.pane.listSlot.height
34
+ visible: !root.trashOpen && root.pane.listingState === "empty"
35
+ caption: Search.live(root.pane) ? Search.nothingMatches(root.pane.swap.answeredQuery) : ""
36
+ mark: "search"
37
+ hint: Search.live(root.pane) ? "Press Escape to clear."
38
+ : ViewState.keyHints ? KeyName.hinted("Press %1 for a new folder.", "newFolder") : ""
39
+ }
40
+
41
+ Flea.LoadingState {
42
+ id: loadingState
43
+ x: root.heroX
44
+ y: root.pane.listSlot.y
45
+ width: root.heroSpan
46
+ height: root.pane.listSlot.height
47
+ // A listing past its wait is the Error state, so the crawl yields to the sentence.
48
+ visible: !root.trashOpen && root.pane.listingState === "loading"
49
+ heldOff: root.pane.swap.fellBack
50
+ holdOffMs: Swap.markHoldOff(root.pane.searchMode)
51
+ onShownChanged: root.pane.swap.markShown(shown)
52
+ }
53
+
54
+ // While a listing is out a press or wheel notch over the header, filter or rows lands on nothing; the handlers gate it, not enabled.
55
+ MouseArea {
56
+ x: root.pane.listSlot.x
57
+ y: root.pane.header.y
58
+ width: root.pane.listSlot.width
59
+ height: root.pane.listSlot.y + root.pane.listSlot.height - root.pane.header.y
60
+ enabled: !root.trashOpen
61
+ acceptedButtons: Qt.AllButtons
62
+ onPressed: function (mouse) {
63
+ mouse.accepted = root.pane.listInFlight
64
+ if (mouse.accepted) root.pane.message(Swap.LOADING, false)
65
+ }
66
+ onWheel: function (wheel) { wheel.accepted = root.pane.listInFlight }
67
+ // Passive, above the rows, so it sees a row's press and its release even after the row is rebuilt; a platform drag takes the release, so ui/FileDrag.qml liftEnded clears the hold.
68
+ PointHandler {
69
+ acceptedButtons: Qt.AllButtons
70
+ onActiveChanged: root.pane.pointerHeld = active
71
+ }
72
+ }
73
+
74
+ Flea.StateMessage {
75
+ id: paneMessage
76
+ active: !root.trashOpen
77
+ x: root.heroX + Theme.spacing.rowPaddingX
78
+ y: root.pane.listSlot.y
79
+ width: root.heroSpan - 2 * Theme.spacing.rowPaddingX
80
+ height: root.pane.listSlot.height
81
+ message: root.pane.stateMessage
82
+ listingState: root.pane.listingState
83
+ lockedMode: root.pane.lockedMode
84
+ total: root.pane.total
85
+ filterQuery: root.pane.filterQuery
86
+ shown: root.pane.filterQuery.length > 0 ? root.pane.shownTotal : -1
87
+ }
88
+ }
@@ -0,0 +1,339 @@
1
+ import QtQuick
2
+ import "js/RecentMode.js" as RecentMode
3
+ import "js/Anchor.js" as Anchor
4
+ import "js/BackReturn.js" as BackReturn
5
+ import "js/DirSizes.js" as DirSizes
6
+ import "js/Nav.js" as Nav
7
+ import "js/Reload.js" as Reload
8
+ import "js/Search.js" as Search
9
+ import "js/Startup.js" as Startup
10
+ import "js/Swap.js" as Swap
11
+ import "js/Tabs.js" as Tabs
12
+ import "js/Thumbs.js" as Thumbs
13
+
14
+ // Where a listing's listed and rows replies land, holding the old rows until they do; see AGENTS.md "The listing swap".
15
+ Item {
16
+ id: root
17
+
18
+ property var pane: null
19
+ // ui/PaneWire.qml, which owns the anchor, the retry and the rename a landing listing settles.
20
+ property var wire: null
21
+ // The cursor a Back or Forward owes the asked listing, or null; hold() arms each listing's own.
22
+ property var restore: null
23
+ // The first window a landing set aside while its locate is out, or null.
24
+ property var parked: null
25
+ // A locate that never answers is a listing that never answers, so past Startup's listing wait the parked rows land at the top.
26
+ property int parkedWaitMs: Startup.LISTING_WAIT_MS
27
+ // Built on the first park, so a window that never parks a Back starts no object for it.
28
+ property var parkedWait: null
29
+ onParkedChanged: {
30
+ if (!root.parked) {
31
+ if (root.parkedWait !== null) root.parkedWait.stop()
32
+ return
33
+ }
34
+ if (root.parkedWait === null) {
35
+ var made = Qt.createComponent("ParkedWait.qml")
36
+ if (made.status !== Component.Ready) throw new Error("ParkedWait.qml did not load: " + made.errorString())
37
+ root.parkedWait = made.createObject(root, { swap: root })
38
+ }
39
+ root.parkedWait.restart()
40
+ }
41
+ property var phase: Swap.idle()
42
+ readonly property bool holding: root.phase.holding
43
+ readonly property bool fellBack: root.phase.fellBack
44
+
45
+ // What tests/ui.sh noblank reads through ui/Ipc.qml swapState: how each hold ended and every empty frame.
46
+ property int holds: 0
47
+ property int fallbacks: 0
48
+ // Rows lines that waited out the loading mark's minimum, which a rows line landing late does not count.
49
+ property int waits: 0
50
+ property int blankFrames: 0
51
+ // The gen whose rows last landed: a new gen parks the cursor on its best match, a re-answer keeps the file.
52
+ property int landedGen: 0
53
+ // The query the drawn rows answered, set in the turn they land; rows read it, never the live text.
54
+ property string answeredQuery: ""
55
+ property int loadingFrames: 0
56
+ property var last: ({ ms: 0, end: "" })
57
+ // When ui/PaneStates.qml drew the loading mark, 0 while none is drawn, and how long the last one stood.
58
+ property double markSince: 0
59
+ property int shownMs: 0
60
+ // Rows lines that came while the mark still owed time, landed in arrival order once it has stood its minimum.
61
+ property var waiting: []
62
+ // True from the turn after the search strip's first frame until the session closes: the views build what an answer draws then, unseen.
63
+ property bool searchWarm: false
64
+
65
+ // ui/js/Nav.js asks for every listing here; false means nothing is held, and one after a live answer holds the walk's state unless clearAtOnce.
66
+ function hold(ask) {
67
+ root.restore = ask.restore ? Object.assign({}, ask.restore) : null
68
+ root.parked = null
69
+ var now = Date.now()
70
+ root.phase = Swap.begin(root.phase, root.pane.listingState, root.pane.searchMode, ask, now)
71
+ if (!root.holding)
72
+ return false
73
+ root.holds += 1
74
+ cap.interval = Swap.capLeft(root.phase, now)
75
+ cap.restart()
76
+ return true
77
+ }
78
+
79
+ function markShown(shown) {
80
+ if (!shown && root.markSince > 0)
81
+ root.shownMs = Date.now() - root.markSince
82
+ root.markSince = shown ? Date.now() : 0
83
+ }
84
+
85
+ // The listed line held or fallen-back rows, or a drawn mark, wait for is kept for them, an earlier request's is dropped, any other applied.
86
+ function takeListed(total, readMs, sortMs, path, changed, gen) {
87
+ if (Swap.dropSessionLine(root.pane.searchMode, gen, root.pane.searchGen, root.pane.searchLeaving === true, root.pane.searchOpenedGen)) {
88
+ return
89
+ }
90
+ var marked = Swap.marked(root.markSince, root.pane.listInFlight, root.pane.searchMode, root.phase.walk)
91
+ var action = Swap.onListed(root.phase, root.pane.listInFlight, path, root.pane.listingPath, marked)
92
+ if (action === Swap.DROP) {
93
+ return
94
+ }
95
+ if (Swap.keepLive(root.pane.searchMode, root.pane.listInFlight) || action === Swap.KEEP) {
96
+ root.phase = Swap.kept(root.phase, { total: total, readMs: readMs, sortMs: sortMs, path: path, changed: changed })
97
+ root.pane.listedSeen = true
98
+ return
99
+ }
100
+ root.phase = Swap.heard(root.phase, root.pane.searchMode === Search.RESULTS)
101
+ root.applyListed(total, readMs, sortMs, path, changed)
102
+ }
103
+
104
+ // A rows line lands with the listed line kept for it, or alone; one ahead of its listed line is dropped as it always was.
105
+ function takeRows(start, items, kinds, listing, gen) {
106
+ if (Swap.dropSessionLine(root.pane.searchMode, gen, root.pane.searchGen, root.pane.searchLeaving === true, root.pane.searchOpenedGen)) {
107
+ return
108
+ }
109
+ var marked = Swap.marked(root.markSince, root.pane.listInFlight, root.pane.searchMode, root.phase.walk)
110
+ var left = marked ? Swap.showLeft(root.markSince, Date.now()) : 0
111
+ var action = Swap.onRows(root.phase, root.pane.listInFlight, root.pane.listedSeen, left)
112
+ if (action === Swap.DROP) {
113
+ return
114
+ }
115
+ if (action === Swap.WAIT) {
116
+ root.waiting.push({ start: start, items: items, kinds: kinds, listing: listing, gen: gen })
117
+ root.waits += 1
118
+ minShow.interval = left
119
+ minShow.restart()
120
+ return
121
+ }
122
+ if (gen > 0 && gen === root.pane.searchGen && root.pane.searchFirstRowsAt === 0) {
123
+ root.pane.searchFirstRowsAt = Date.now()
124
+ }
125
+ // A re-sort renumbers every row, so a selection made while it was in flight names other files under the new numbering.
126
+ if (Swap.clearsSelection(root.pane.backend.heldListing, listing) && root.pane.selectionCount() > 0)
127
+ root.pane.clearSelection()
128
+ root.pane.backend.heldListing = listing
129
+ root.stampTags(items, action === Swap.LAND || root.pane.listInFlight)
130
+ // Only a live answer names its query; a plain window reply answers none, so a scroll keeps the marks.
131
+ if (gen > 0)
132
+ root.answeredQuery = root.pane.searchQuery
133
+ if (action === Swap.LAND) {
134
+ // The owed name sits outside this window, so these rows wait for the window its locate names.
135
+ if (BackReturn.deferForLocate(root.pane, root.restore, items, root.phase.listed)) {
136
+ root.parked = { start: start, items: items, kinds: kinds }
137
+ return
138
+ }
139
+ root.parked = null
140
+ root.land(start, items, kinds, gen > 0 && gen !== root.landedGen && (root.pane.searchQuery || "").length > 0)
141
+ root.landedGen = gen
142
+ return
143
+ }
144
+ root.pane.held = start
145
+ root.pane.rows = RecentMode.stampRows(root.pane, items)
146
+ root.pane.kindNames = kinds
147
+ root.rowsLanded()
148
+ }
149
+
150
+ // A rows reply is the held window: a tagged row builds the tag state, which stamps each row with its folder's slot.
151
+ function stampTags(items, fresh) {
152
+ if (root.pane.tagState === null && !items.some(function (row) { return row.tg !== undefined }))
153
+ return
154
+ root.pane.tags().take(items, fresh)
155
+ }
156
+
157
+ // Reset, path and rows land in one turn (past the cap the reset already ran); a re-answer keeps the cursor's file, a new query takes row 0.
158
+ function land(start, items, kinds, fresh) {
159
+ var reply = root.phase.listed
160
+ var keep = root.pane.searchMode.length > 0 && start === 0 && !fresh && root.pane.rowFor(root.pane.cursorIndex)
161
+ // A live answer on the base the rows already stand on keeps the folder sizes and thumbnails it lists again; the first answer and an exit start clean.
162
+ var again = root.pane.searchLeaving !== true && root.pane.searchBase === reply.path
163
+ && (root.pane.searchMode === Search.TYPING || root.pane.searchMode === Search.RESULTS)
164
+ var sizes = DirSizes.inlined(again ? DirSizes.carried(root.pane.dirSizeState, root.pane.rows, root.pane.held, items, start) : null, items, start)
165
+ var thumbs = again ? Thumbs.carried(root.pane.thumbState, root.pane.rows, root.pane.held, items, start) : null
166
+ if (root.holding)
167
+ root.release(Swap.landed(root.phase), "landed")
168
+ else
169
+ root.phase = Swap.landed(root.phase)
170
+ // The rows go in before the count, so each delegate the count builds is built on its own row.
171
+ root.pane.held = start
172
+ root.pane.rows = RecentMode.stampRows(root.pane, items)
173
+ root.pane.kindNames = kinds
174
+ root.applyListed(reply.total, reply.readMs, reply.sortMs, reply.path, reply.changed)
175
+ if (sizes)
176
+ root.pane.dirSizeState = sizes
177
+ if (thumbs)
178
+ root.pane.thumbState = thumbs
179
+ if (keep) {
180
+ root.pane.setCursor(Search.keptIndex(keep.n, items, start))
181
+ } else if (fresh) {
182
+ root.pane.setCursor(start)
183
+ }
184
+ root.rowsLanded()
185
+ }
186
+
187
+ // A refused located window, any other failure or the wait running out lands the rows set aside, at the top, so the listing still ends.
188
+ function landParked() {
189
+ var parked = root.parked
190
+ if (!parked || root.phase.listed === null)
191
+ return false
192
+ root.parked = null
193
+ root.restore = null
194
+ root.land(parked.start, parked.items, parked.kinds, false)
195
+ return true
196
+ }
197
+
198
+ // A failed or refused listing, or a kept live answer, lets the held rows go at once; the failure draws what it always did.
199
+ function drop() {
200
+ if (root.holding)
201
+ root.release(Swap.dropped(root.phase), "dropped")
202
+ else
203
+ root.phase = Swap.dropped(root.phase)
204
+ }
205
+
206
+ function release(next, end) {
207
+ var query = root.phase.query
208
+ root.last = { ms: Date.now() - root.phase.since, end: end }
209
+ cap.stop()
210
+ root.phase = next
211
+ Nav.forget(root.pane, query)
212
+ }
213
+
214
+ function stopWaiting() {
215
+ minShow.stop()
216
+ root.waiting = []
217
+ }
218
+
219
+ function applyListed(total, readMs, sortMs, path, changed) {
220
+ var pane = root.pane
221
+ pane.thumbState = Thumbs.empty()
222
+ pane.dirSizeState = DirSizes.empty()
223
+ pane.reloadChanged = (changed === undefined || changed === null) ? -1 : changed
224
+ if (!pane.dualMode && !pane.listInFlight && pane.searchMode.length === 0) {
225
+ // With remember on each folder keeps its own order, so the fallback every other folder opens with stays untouched.
226
+ if (ViewState.state.rememberSort === false)
227
+ ViewState.changeLeaf("sort", { key: pane.backend.sortBy === "mtime" ? "date" : pane.backend.sortBy,
228
+ reverse: pane.backend.sortDesc })
229
+ pane.appliedListingPreferences = pane.listingPreferences
230
+ }
231
+ if (pane.listInFlight) {
232
+ pane.listedSeen = true
233
+ }
234
+ pane.total = total
235
+ // A live answer replaces rows in place with listed.path as the base in the same turn.
236
+ if (pane.searchMode === Search.TYPING || pane.searchMode === Search.RESULTS) {
237
+ pane.searchBase = path
238
+ pane.listingState = Search.listingState(pane, total)
239
+ pane.stateMessage = ""
240
+ return
241
+ }
242
+ if (path.length > 0) pane.path = path // the listing landed, so this is where the pane moves
243
+ pane.listingState = total === 0 ? "empty" : "ready"
244
+ pane.stateMessage = total === 0 ? "This folder is empty." : ""
245
+ pane.opened(pane.path)
246
+ }
247
+
248
+ // Everything a rows reply does once its rows are in the pane, whether they came alone or with a swap.
249
+ function rowsLanded() {
250
+ // A landed live answer marks the phase a search, so an exit re-list forgets at once.
251
+ if (root.pane.searchMode.length > 0) {
252
+ root.phase = Swap.heard(root.phase, true)
253
+ }
254
+ var pane = root.pane
255
+ if (pane.rowsAt === 0 && pane.inputAt > 0 && pane.rowFor(pane.cursorIndex))
256
+ pane.rowsAt = Date.now()
257
+ pane.applyPendingSelect()
258
+ root.wire.anchor = Anchor.apply(pane, root.wire.anchor, Theme.fileRowHeight)
259
+ if (pane.preferenceAnchor)
260
+ pane.preferenceAnchor = Anchor.applyPreference(pane, pane.preferenceAnchor)
261
+ // A manual reload's notice, said only when rows changed; every other listing owes none.
262
+ Reload.landed(pane)
263
+ Tabs.applyPending(pane)
264
+ // The Back or Forward cursor lands with the rows, in this same turn, or not at all.
265
+ BackReturn.landRestore(pane, root.restore)
266
+ root.restore = null
267
+ pane.listArea.restartSettle()
268
+ if (pane.listInFlight) {
269
+ pane.listInFlight = false
270
+ pane.listedSeen = false
271
+ }
272
+ root.wire.locateRetry()
273
+ root.wire.openRenameOnArrival()
274
+ }
275
+
276
+ function describe() {
277
+ return { holding: root.holding, fellBack: root.fellBack, holds: root.holds, fallbacks: root.fallbacks, waits: root.waits,
278
+ blankFrames: root.blankFrames, loadingFrames: root.loadingFrames, last: root.last, shownMs: root.shownMs }
279
+ }
280
+
281
+ // A slow listing falls back to the loading state, which stands until its rows land with their listed line.
282
+ Timer {
283
+ id: cap
284
+ repeat: false
285
+ onTriggered: {
286
+ if (!root.holding)
287
+ return
288
+ root.fallbacks += 1
289
+ root.release(Swap.expired(root.phase), "expired")
290
+ }
291
+ }
292
+
293
+ // The mark has stood its minimum, so the rows that came meanwhile land now.
294
+ Timer {
295
+ id: minShow
296
+ repeat: false
297
+ onTriggered: {
298
+ var rows = root.waiting
299
+ root.waiting = []
300
+ // Every line that arrived inside the minimum lands now, in the order it arrived.
301
+ for (var i = 0; i < rows.length; i++)
302
+ root.takeRows(rows[i].start, rows[i].items, rows[i].kinds, rows[i].listing, rows[i].gen)
303
+ }
304
+ }
305
+
306
+ Connections {
307
+ target: root.pane
308
+ function onListInFlightChanged() {
309
+ if (root.pane.listInFlight)
310
+ return
311
+ cap.stop()
312
+ root.stopWaiting()
313
+ root.restore = null
314
+ root.parked = null
315
+ root.phase = Swap.ended(root.phase)
316
+ }
317
+ function onSearchModeChanged() { if (root.pane.searchMode.length === 0) root.searchWarm = false }
318
+ }
319
+
320
+ // Runs one turn after the frame that drew the strip, so the strip never waits for what it builds.
321
+ function warmSearch() { root.searchWarm = root.pane.searchMode.length > 0 }
322
+
323
+ // afterAnimating runs on this thread just before each frame is synchronised, so it sees what that frame draws.
324
+ Connections {
325
+ target: root.Window.window
326
+ function onAfterAnimating() {
327
+ // Search and "/" never put a list in flight, so their blank is no row drawn while the count still names rows.
328
+ var live = Search.live(root.pane) || root.pane.filterQuery.length > 0
329
+ var kind = Swap.frameKind(root.pane.listInFlight, root.pane.listingState, root.fellBack,
330
+ root.pane.rows.length, root.pane.total, live)
331
+ if (kind === "blank")
332
+ root.blankFrames += 1
333
+ else if (kind === "loading")
334
+ root.loadingFrames += 1
335
+ if (root.pane.searchMode.length > 0 && !root.searchWarm)
336
+ Qt.callLater(root.warmSearch)
337
+ }
338
+ }
339
+ }