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
package/ui/Sidebar.qml ADDED
@@ -0,0 +1,728 @@
1
+ import QtQuick
2
+ import Quickshell
3
+ import Quickshell.Io
4
+ import qs.Commons
5
+ import "." as Flea
6
+ import "js/Eject.js" as Eject
7
+ import "js/Mounts.js" as Mounts
8
+ import "js/Places.js" as Places
9
+ import "js/PlaceMenu.js" as PlaceMenu
10
+ import "js/RailMenu.js" as RailMenu
11
+ import "js/RailKeys.js" as RailKeys
12
+
13
+ // Places, Favorites, Network and Devices share one flat cursor in visual order.
14
+ Item {
15
+ id: root
16
+
17
+ property bool focused: false
18
+ property var backend: null
19
+ property Item navigationPane: null
20
+ property bool trashActive: false
21
+ // Set by Enter on a row that mounts first, so the open that lands afterwards takes focus into the folder.
22
+ property bool focusOnOpen: false
23
+ // Right Arrow from a row that releases puts the keyboard on its eject mark; a move, a lost focus or a vanished mark ends its frame.
24
+ property bool markFocused: false
25
+ // A pointer press on a focused rail returns the highlight to the current place until the next rail key.
26
+ property bool pointerFocus: false
27
+ onFocusedChanged: if (!root.focused) { root.markFocused = false; root.pointerFocus = false }
28
+ // ui/Pane.qml's one ui/ContextMenu.qml, handed in rather than built here: a second instance in
29
+ // this tree took the keyboard away from the list, see ui/SidebarRow.qml's own note.
30
+ property var menu: null
31
+ property int cursorIndex: 0
32
+ property var cursorEntries: []
33
+ // The row that holds what the rail pane shows. It is settled only once no listing is out, so it moves in the frame the listing swaps.
34
+ property int litIndex: -1
35
+ // The menu sets cursorIndex on open; the keyboard highlight stays off while that menu is open.
36
+ readonly property bool cursorDrawn: RailKeys.cursorDrawn(root.focused, root.menu && root.menu.opened && root.menu.forRail, root.pointerFocus)
37
+ readonly property int highlightedIndex: root.cursorDrawn ? root.cursorIndex : root.litIndex
38
+ function settleLit() {
39
+ var pane = root.navigationPane
40
+ if (pane === null || pane.listInFlight)
41
+ return
42
+ root.litIndex = Places.heldBy(root.entries, pane.path, Places.litView(pane.trash.opened, pane.recentMode, pane.searchMode, pane.swap.answeredQuery, pane.pathAlias))
43
+ }
44
+ onNavigationPaneChanged: root.settleLit()
45
+ Connections {
46
+ target: root.navigationPane
47
+ function onPathChanged() { root.settleLit() }
48
+ function onRecentModeChanged() { root.settleLit() }
49
+ function onSearchModeChanged() { root.settleLit() }
50
+ function onListInFlightChanged() { root.settleLit() }
51
+ }
52
+ Connections {
53
+ target: root.navigationPane ? root.navigationPane.trash : null
54
+ function onOpenedChanged() { root.settleLit() }
55
+ }
56
+ Connections {
57
+ target: root.navigationPane ? root.navigationPane.swap : null
58
+ function onAnsweredQueryChanged() { root.settleLit() }
59
+ }
60
+ readonly property var placesState: ViewState.state.places || ({})
61
+ readonly property var userFavouriteEntries: RailPlaces.favouriteEntries
62
+ readonly property var homeEntries: RailPlaces.homeEntries
63
+ // Recent sits under Home and carries a location token.
64
+ readonly property var homeLead: root.homeEntries.slice(0, 1)
65
+ readonly property var homeRest: root.homeEntries.slice(1)
66
+ readonly property var recentEntries: RailPlaces.recentEntries
67
+ readonly property var placesEntries: root.homeLead.concat(root.recentEntries, root.homeRest, root.trashEntries, root.userFavouriteEntries, root.tagRows)
68
+ // The TAGS section (ui/TagRailSection.qml) is built once the setting is on and a tag is in use, since it ships off; its rows end the places group.
69
+ property var tagSection: null
70
+ readonly property var tagRows: root.tagSection !== null && root.placesState.showTags === true ? root.tagSection.entries : []
71
+ readonly property bool tagsListed: root.placesState.showTags === true && root.navigationPane !== null && root.navigationPane.tagState !== null
72
+ && Object.keys(root.navigationPane.tagState.inUse).length > 0
73
+ onTagsListedChanged: {
74
+ if (!root.tagsListed || root.tagSection !== null) return
75
+ root.tagSection = Qt.createComponent("TagRailSection.qml").createObject(netGap, { sidebar: root })
76
+ }
77
+ readonly property int trashCount: trashMonitor.count
78
+ signal trashChanged()
79
+ function refreshTrash() { trashMonitor.refresh() }
80
+ TrashMonitor {
81
+ id: trashMonitor
82
+ backend: root.navigationPane ? root.navigationPane.backend : null
83
+ watching: root.trashActive || root.placesState.showTrash !== false
84
+ onChanged: root.trashChanged()
85
+ // A background poll the operator never asked for must not take the status bar: the count is
86
+ // read for the rail Trash menu even with the badge off, so only a shown count reports it.
87
+ onFailed: function(text) { if (root.trashActive || root.placesState.trashCount === true) root.message(text, true) }
88
+ }
89
+ readonly property var trashEntries: RailPlaces.trashEntries.map(function (entry) {
90
+ return Object.assign({}, entry, { count: root.trashCount })
91
+ })
92
+ signal trashRequested()
93
+ // Folder drops share one receiver below the rows; Trash's own area answers above it.
94
+ RailDrop { id: fileDrop; sidebar: root; viewport: scroller }
95
+
96
+ // The saved place an Edit is rewriting, "" when none is; ui/js/RailMenu.js editPlace sets it.
97
+ property string editingPlace: ""
98
+ // And the request that Edit's own attempt went out with, so no other mount answers for it.
99
+ property string editingRequest: ""
100
+ // PaneRail injects the window-long host, so hiding the rail preserves mounts, waits and dialog answers.
101
+ property var service: null
102
+ readonly property var networkEntries: root.placesState.showNetwork === false || !root.railGate.showNetwork || !root.service ? [] : root.service.entries
103
+ // The poll rebinds its delegates in place, so a rename left standing would edit a different share.
104
+ onNetworkEntriesChanged: root.cancelRename()
105
+ // Phones ride the DEVICES group behind the block devices: a plugged phone is a device to the person holding it, whatever transport gvfs reaches it over.
106
+ readonly property var deviceEntries: root.placesState.showDevices === false || !root.railGate.showDevices ? [] : devices.entries.concat(phones.entries, root.volumeEntries)
107
+ // flea-qml: the drives the backend found by looking close the group: every root a path can
108
+ // start from and what is mounted where drives are mounted, on systems lsblk says nothing about.
109
+ readonly property var volumeEntries: Places.volumeEntries(RailPlaces.probedVolumes, devices.entries)
110
+ // The eject chain's guard state, read fresh at ipc time, so a failed eject names its guard.
111
+ function ejectChainState() { return devices.ejectState() }
112
+ // A release in flight, which PaneRail holds the rail open for.
113
+ readonly property bool releasing: devices.releasing
114
+ // False parks the DEVICES poll; PaneRail drops it while an auto-hide rail is withdrawn, and a reveal polls at once.
115
+ property bool polling: true
116
+ function pollDevices() { devices.poll() }
117
+ // flea-qml: the backend looks for places again every five seconds only while a rail asks it
118
+ // to (src/places.js), so a parked or unloaded rail takes its request back.
119
+ property var placesWatcher: null
120
+ function watchPlaces(shown) {
121
+ var want = shown && root.polling ? root.backend : null
122
+ if (want === root.placesWatcher) return
123
+ if (root.placesWatcher) root.placesWatcher.send({ c: "places", watch: false })
124
+ root.placesWatcher = want
125
+ if (want) want.send({ c: "places", watch: true })
126
+ }
127
+ onPollingChanged: root.watchPlaces(true)
128
+ onBackendChanged: root.watchPlaces(true)
129
+ readonly property var entries: root.placesEntries.concat(root.networkEntries, root.deviceEntries)
130
+
131
+ // The rail lands in one step by gating the entries themselves, so cursor, IPC and menus match only drawn rows.
132
+ readonly property int railSettleMs: 800
133
+ property bool railDeadlineElapsed: false
134
+ property bool bookmarksReady: false
135
+ // Favourite-relative insertion boundary, count past the last row, -1 when idle; a rebuild clears it.
136
+ property int reorderLine: -1
137
+ readonly property var railGate: Mounts.railGroupsReady(root.bookmarksReady && root.service !== null && root.service.listingAnswered && root.service.dropboxAnswered, devices.firstAnswered && phones.firstDone, root.railDeadlineElapsed ? root.railSettleMs : 0, root.railSettleMs)
138
+ Timer { interval: root.railSettleMs; running: true; repeat: false; onTriggered: root.railDeadlineElapsed = true }
139
+
140
+ // Reconcile only the aggregate; evaluating entries from a group's change handler re-enters its binding.
141
+ onEntriesChanged: {
142
+ root.reorderLine = -1
143
+ var next = Places.railCursorAfter(root.cursorEntries, root.entries, root.cursorIndex)
144
+ var lit = Places.railCursorAfter(root.cursorEntries, root.entries, root.litIndex)
145
+ if (root.renamingIndex >= 0
146
+ && Places.railCursorAfter(root.cursorEntries, root.entries, root.renamingIndex) !== root.renamingIndex)
147
+ root.cancelRename()
148
+ root.cursorEntries = root.entries
149
+ root.cursorIndex = next
150
+ // A listing still out keeps its row through the rebuild; an idle pane re-reads it.
151
+ root.litIndex = lit
152
+ root.settleLit()
153
+ root.markFocused = root.markFocused && Eject.releasable(root.entries[next])
154
+ availability.restart()
155
+ }
156
+
157
+ signal opened(string path)
158
+ // A locked LUKS row opens the unlock dialog instead of running gio mount.
159
+ signal unlockRequested(string device, string label, double size, string fstype)
160
+ // Recent answers bounded newest-first history paths, which the pane opens through listpaths.
161
+ signal recentRequested(var paths, var requester, var visits)
162
+ signal addRequested()
163
+ // The rail's Edit row asks the window to open the dialog over the saved place.
164
+ signal editRequested(string uri, string label, string password, string reason, bool failedConnect, var origin)
165
+ signal message(string text, bool isError)
166
+ signal operationResult(string headline, string detail, bool isError)
167
+ signal forgetMessage(string text)
168
+
169
+ // The entry index mid-rename, or -1; ui/SidebarRow.qml swaps its Text for a field on it, and ui/Pane.qml holds its keys off while it stands.
170
+ property int renamingIndex: -1
171
+ // Fires once, on both commit and cancel, so ui/Pane.qml has one place to hand focus back.
172
+ signal renameFinished()
173
+ // Any press inside the rail, so an overlay rail can take the keyboard though a row or the Flickable below holds the grab: the handler's item is above every child.
174
+ signal pressed()
175
+
176
+ // Sized in characters, because a monospace makes that exact where a pixel constant would be an accident.
177
+ readonly property int widthChars: 18
178
+ // The mark and its gap count too, because ui/SidebarRow.qml draws them before the label: without them an 18-character entry elided at 15.
179
+ implicitWidth: Places.sidebarWidth(root.placesState.sidebarWidth)
180
+
181
+ TextMetrics {
182
+ id: metrics
183
+ font.family: Theme.font.family
184
+ font.pixelSize: Theme.font.body
185
+ text: "0"
186
+ }
187
+
188
+ FileView {
189
+ id: bookmarksFile
190
+ path: Quickshell.env("HOME") + "/.config/gtk-3.0/bookmarks"
191
+ watchChanges: true
192
+ printErrors: false
193
+ onFileChanged: reload()
194
+ onLoaded: { root.bookmarksReady = true; root.pushBookmarks() }
195
+ onLoadFailed: { root.bookmarksReady = true; root.pushBookmarks() }
196
+ }
197
+
198
+ // The host keeps its own copy of this text: it outlives this rail, so a rail unload
199
+ // mid-mount leaves the mount's labels and dedup exactly where they were. Pushed only
200
+ // after the FileView loaded, so a reveal never blanks the host with an unread "".
201
+ function pushBookmarks() {
202
+ if (!root.bookmarksReady || !root.service) return
203
+ root.service.bookmarksText = bookmarksFile.text()
204
+ }
205
+
206
+ // The window-long poll runs while a rail is loaded; arrival waits until the pane's overlay parent is bound.
207
+ Component.onCompleted: { Qt.callLater(root.meetHost); root.watchPlaces(true) }
208
+ function meetHost() { root.arrive() } // never builds the host: ui/WindowBody.qml orders it after the first rows
209
+ // Arrival counts whenever the service appears, so a host built after this rail still starts the poll.
210
+ property bool arrived: false
211
+ function arrive() { if (root.arrived || !root.service) return; root.arrived = true; root.service.railArrived() }
212
+ onServiceChanged: { root.arrive(); root.pushBookmarks() }
213
+ // Departure hands the timer back to a flight, if any, and the last listing stands while it is off.
214
+ Component.onDestruction: { if (root.service && root.arrived) root.service.railLeft(); root.watchPlaces(false) }
215
+
216
+ // The desktop's own history, read and never written, kept across opens.
217
+ property var recentPaths: []
218
+ property var recentVisits: ({})
219
+ property bool recentKept: false
220
+ property bool recentReading: false
221
+ property int recentChanges: 0
222
+ property int recentReadAt: -1
223
+ // Every asker waiting on the read in flight, null meaning the rail pane itself.
224
+ property var recentRequesters: []
225
+ // How many times the history has been parsed; the seam reads it the way it reads the jump's.
226
+ property int recentReads: 0
227
+ function readRecent(requester) {
228
+ // One read at a time: every asker waits on it, so each pane opens once it lands.
229
+ if (root.recentReading) {
230
+ root.recentRequesters = recentReader.item.joinRequesters(root.recentRequesters, requester)
231
+ return
232
+ }
233
+ if (root.recentKept && root.recentReadAt === root.recentChanges) {
234
+ root.recentRequested(root.recentPaths, requester || null, root.recentVisits)
235
+ return
236
+ }
237
+ // Build the helper with the reader, only after an uncached Recent action.
238
+ recentReader.active = true
239
+ root.recentRequesters = recentReader.item.joinRequesters([], requester)
240
+ root.recentReading = true
241
+ root.recentReadAt = root.recentChanges
242
+ recentWatcher.path = recentReader.item.file
243
+ recentReader.item.refresh()
244
+ }
245
+ // Watching only: it never loads the file, and it reports a rename over it, a delete and a re-create alike.
246
+ FileView {
247
+ id: recentWatcher
248
+ preload: false
249
+ watchChanges: true
250
+ onFileChanged: root.recentChanges += 1
251
+ }
252
+ // The history reader and its helpers load on a Recent action, then drop after the read.
253
+ Loader {
254
+ id: recentReader
255
+ active: false
256
+ source: "PickerRecent.qml"
257
+ }
258
+ Connections {
259
+ target: recentReader.item
260
+ function onRefreshed() {
261
+ root.recentPaths = recentReader.item.paths
262
+ root.recentVisits = recentReader.item.visits
263
+ root.recentKept = true
264
+ root.recentReading = false
265
+ root.recentReads += 1
266
+ // Drop the bounded parsed model later, after the reader finishes emitting this signal.
267
+ Qt.callLater(function () { if (!root.recentReading) recentReader.active = false })
268
+ var askers = root.recentRequesters
269
+ root.recentRequesters = []
270
+ for (var i = 0; i < askers.length; i++) root.recentRequested(root.recentPaths, askers[i], root.recentVisits)
271
+ }
272
+ }
273
+
274
+ // The context menu's gate for the two Dropbox rows, so the pane never reaches into the rail.
275
+ readonly property bool dropboxReady: root.service !== null && root.service.dropboxReady
276
+ readonly property var providerService: root.service
277
+
278
+ DeviceMounts {
279
+ id: devices
280
+ polling: root.polling
281
+ showUnmounted: root.placesState.showUnmounted !== false
282
+ onOpened: function (path) { root.opened(path) }
283
+ onMessage: function (text, isError) { root.message(text, isError) }
284
+ onOperationResult: function (headline, detail, isError) { root.operationResult(headline, detail, isError) }
285
+ onForgetMessage: function (text) { root.forgetMessage(text) }
286
+ // An eject releases the volume, so readers on it stop first and panes on it go Home.
287
+ onQuiesce: function (path) { if (root.navigationPane) root.navigationPane.quiesceVolume(path) }
288
+ onUnlockRequested: function (device, label, size, fstype) { root.unlockRequested(device, label, size, fstype) }
289
+ }
290
+
291
+ // Lists and unmounts only: activate() below routes a phone's mount-and-open through the same openShare leg a share rides.
292
+ PhoneMounts {
293
+ id: phones
294
+ listingText: root.service !== null ? root.service.mountListing : ""
295
+ listingAnswered: root.service !== null && root.service.listingAnswered
296
+ onMessage: function (text, isError) { root.message(text, isError) }
297
+ onReleased: if (root.service) root.service.pollMounts()
298
+ }
299
+
300
+ // The mount service lives window-long in ui/WindowBody.qml's network host, injected through
301
+ // ui/PaneRail.qml: every call below reaches the same instance whether this rail is shown or
302
+ // has been unloaded mid-mount, and the answers land through ui/PaneRail.qml's own
303
+ // connections rather than through this rail.
304
+
305
+ // Home is in neither file, so it is prepended; the merge and its first-position-wins rule are Places.favorites'.
306
+ Connections {
307
+ target: Favourites
308
+ function onFailed(message) { root.message(message, true) }
309
+ }
310
+
311
+ // NetworkDialog's saved() drives this reload because a watch set up before its parent directory existed never fires, and it blocks because "forget" derives its body from this text: measured here, an asynchronous reload put a removed line back.
312
+ function reloadBookmarks() {
313
+ bookmarksFile.reload()
314
+ bookmarksFile.waitForJob()
315
+ }
316
+
317
+ function networkResult() {
318
+ return root.service !== null ? root.service.result : ""
319
+ }
320
+
321
+ // The eject mark's one click names its release row, never rows[0], which a volume's Open would win; Ctrl+E keys its release by Mounts.railKey too.
322
+ function ejectRow(index) {
323
+ var entry = root.entries[index]
324
+ if (!entry)
325
+ return
326
+ var action = Eject.releaseAction(entry)
327
+ if (action.length === 0)
328
+ return
329
+ root.releaseChosen(action, Mounts.railKey(entry))
330
+ }
331
+
332
+ // Right click is the whole affordance, and which rows offer what is ui/js/Mounts.js "rowMenu"'s: a row with nothing to offer opens no menu at all.
333
+ function openRailMenu(index, scenePosition) {
334
+ root.cancelRename()
335
+ var entry = root.entries[index]
336
+ if (!entry || !root.menu) {
337
+ return
338
+ }
339
+ root.cursorIndex = index
340
+ RailMenu.railMenuFor(root, entry, scenePosition, ViewState.menuHidden)
341
+ }
342
+
343
+ // The keyboard's entrance to the same menu: ui/js/Mounts.js "raiseMenu" has already asked whether the row releases anything, so this only turns the cursor into a point.
344
+ function openCursorMenu() {
345
+ var row = root.railItemFor(root.cursorIndex)
346
+ if (!row)
347
+ return
348
+ root.openRailMenu(root.cursorIndex, row.mapToItem(null, Style.spacing.rowPaddingX, row.height))
349
+ }
350
+
351
+ // A chosen row arrives with its key rather than its position, and RailMenu.release names the row.
352
+ function releaseChosen(action, key) {
353
+ if (key === "trash") return
354
+ // A place and a favourite are both a path, and ui/js/PlaceMenu.js owns what their rows do.
355
+ if (key.indexOf("place:") === 0 || key.indexOf("favourite:") === 0) {
356
+ PlaceMenu.perform(action, key, root, Favourites)
357
+ return
358
+ }
359
+ RailMenu.release(action, key, devices, root.service, root)
360
+ }
361
+
362
+ Connections {
363
+ target: root.menu
364
+ function onRailChosen(action, key) { root.releaseChosen(action, key) }
365
+ }
366
+
367
+ // The host exists from creation, so a row pressed before the rail arrives still answers.
368
+ function networkHost() {
369
+ if (root.service) return root.service
370
+ if (root.navigationPane) return root.navigationPane.ensureNetworkService()
371
+ return null
372
+ }
373
+
374
+ // The favourites store persists reorders; a refused move keeps its row and reports the failure.
375
+ function moveFavourite(from, to) {
376
+ root.reorderLine = -1
377
+ if (to !== from)
378
+ Favourites.move(from, to)
379
+ }
380
+ // A favourite's path is already real and opens directly; a share or a volume may need its Service.
381
+ function openFavourite(index) {
382
+ Places.openEntry(root.userFavouriteEntries[index], {
383
+ pane: root.navigationPane, opened: root.opened, message: root.message,
384
+ networkHost: root.networkHost, trash: root.trashRequested
385
+ })
386
+ }
387
+
388
+ function activate(index) {
389
+ root.cancelRename()
390
+ root.cursorIndex = index
391
+ var entry = root.entries[index]
392
+ if (!entry) return
393
+ if (entry.kind === "favourite") { root.openFavourite(entry.favouriteIndex); return }
394
+ if (entry.kind === "home") { root.opened(entry.path); return }
395
+ if (entry.kind === "recent") { root.readRecent(); return }
396
+ if (entry.kind === "trash") { root.trashRequested(); return }
397
+ if (entry.kind === "tag") { root.navigationPane.tags().openTag(entry.tag); return }
398
+ var rest = index - root.placesEntries.length
399
+ if (rest < root.networkEntries.length) root.service.activate(rest, root.navigationPane)
400
+ // A phone mounts, resolves and opens the way a share does.
401
+ else if (entry.kind === "phone") { var phoneHost = root.networkHost(); if (phoneHost) phoneHost.openShare(entry.uri, entry.mounted, entry.label, false, { origin: root.navigationPane }) }
402
+ else devices.activate(rest - root.networkEntries.length)
403
+ }
404
+
405
+ // RailMenu.release hands the phone action back here, because the phone Service is this rail's own child.
406
+ function releasePhone(key) { phones.release(key) }
407
+ // Its Mount and Open rows are the row's own activation, resolved by key because the poll renumbers.
408
+ function openPhone(key) {
409
+ var e = phones.entries[Mounts.rowByKey(phones.entries, key)]
410
+ var host = e ? root.networkHost() : null
411
+ if (e && host) host.openShare(e.uri, e.mounted, e.label, false, { origin: root.navigationPane })
412
+ }
413
+
414
+ // Network only: neither a favourite nor a device has a bookmark line of its own shape for
415
+ // Places.relabel to find, and a volume's label lives on the filesystem, not in a rail file.
416
+ function startRename(index) {
417
+ if (index < root.placesEntries.length)
418
+ return
419
+ if (index >= root.placesEntries.length + root.networkEntries.length)
420
+ return
421
+ // A kernel share row has no saved place to rename, and relabelling it would append a file:// bookmark the network rail never draws.
422
+ var entry = root.networkEntries[index - root.placesEntries.length]
423
+ if (entry && entry.editable === false)
424
+ return
425
+ root.renamingIndex = index
426
+ }
427
+
428
+ // The rail's own answer to ui/Pane.qml's renameEditor: the live editor row, or null.
429
+ function renameEditor() {
430
+ if (root.renamingIndex < 0)
431
+ return null
432
+ var item = netRepeater.itemAt(root.renamingIndex - root.placesEntries.length)
433
+ return item && item.renaming ? item : null
434
+ }
435
+
436
+ function cancelRename() {
437
+ if (root.renamingIndex < 0)
438
+ return
439
+ root.renamingIndex = -1
440
+ root.renameFinished()
441
+ }
442
+
443
+ // An empty submitted name reverts rather than writing an empty label.
444
+ function commitRename(index, name) {
445
+ if (root.renamingIndex !== index) return
446
+ root.renamingIndex = -1
447
+ root.renameFinished()
448
+ var trimmed = String(name || "").trim()
449
+ if (trimmed.length === 0)
450
+ return
451
+ var entry = root.networkEntries[index - root.placesEntries.length]
452
+ if (!entry)
453
+ return
454
+ root.service.rename(entry.uri, trimmed)
455
+ }
456
+
457
+ // Auto-hide reveals the rail over the listing, so on glass it hides the rows under it the way a menu does.
458
+ Rectangle {
459
+ anchors.fill: parent
460
+ color: ViewState.railAutoHide ? Theme.color.surface : Glass.surface
461
+ }
462
+
463
+ onCursorIndexChanged: { root.markFocused = false; root.revealCursor() }
464
+
465
+ // Anchored inside the Flickable's contentItem, so a row's y is already the scroll coordinate.
466
+ function revealCursor() {
467
+ var row = root.railItemFor(root.cursorIndex)
468
+ if (!row)
469
+ return
470
+ var p = row.mapToItem(scroller.contentItem, 0, 0)
471
+ if (p.y < scroller.contentY)
472
+ scroller.contentY = p.y
473
+ else if (p.y + row.height > scroller.contentY + scroller.height)
474
+ scroller.contentY = p.y + row.height - scroller.height
475
+ }
476
+
477
+ // The rows live in a viewport: a rail taller than its own height could show no bottom row at all.
478
+ Timer {
479
+ id: availability
480
+ interval: 120
481
+ onTriggered: {
482
+ var indices = []
483
+ for (var i = 0; i < favRepeater.count; i++) {
484
+ var item = favRepeater.itemAt(i)
485
+ var point = item ? item.mapToItem(scroller.contentItem, 0, 0) : null
486
+ if (point && point.y + item.height > scroller.contentY && point.y < scroller.contentY + scroller.height)
487
+ indices.push(i)
488
+ }
489
+ Favourites.inspect(indices)
490
+ }
491
+ }
492
+ Flickable {
493
+ id: scroller
494
+ onContentYChanged: { availability.restart(); fileDrop.reaim() }
495
+ anchors.fill: parent
496
+ clip: true
497
+ contentWidth: width
498
+ contentHeight: rail.height + 2 * Style.spacing.rowPaddingX
499
+ boundsBehavior: Flickable.StopAtBounds
500
+
501
+ FastScrollHandler {
502
+ parent: scroller
503
+ flickable: scroller
504
+ }
505
+ // No bar and no lane: rows fill the rail and still scroll by wheel, touchpad and keys.
506
+ Column {
507
+ id: rail
508
+ anchors.top: parent.top
509
+ anchors.topMargin: Style.spacing.rowPaddingX
510
+ anchors.left: parent.left
511
+ anchors.right: parent.right
512
+
513
+ RailHeading {
514
+ id: placesHeading
515
+ visible: root.homeEntries.length + root.trashEntries.length > 0
516
+ text: "PLACES"
517
+ }
518
+ Repeater {
519
+ id: homeLeadRepeater
520
+ model: root.homeLead
521
+ delegate: SidebarRow {
522
+ lit: index === root.litIndex
523
+ highlighted: index === root.highlightedIndex
524
+ cursor: root.cursorDrawn && index === root.cursorIndex
525
+ focused: root.focused
526
+ onActivated: function (idx) { root.activate(idx) }
527
+ onMenuRequested: function(idx, pos) { root.openRailMenu(idx, pos) }
528
+ onTabRequested: function (idx) { PlaceMenu.openTabAt(root, idx) }
529
+ }
530
+ }
531
+ Repeater {
532
+ id: recentRepeater
533
+ model: root.recentEntries
534
+ delegate: SidebarRow {
535
+ lit: index + root.homeLead.length === root.litIndex
536
+ highlighted: index + root.homeLead.length === root.highlightedIndex
537
+ cursor: root.cursorDrawn && index + root.homeLead.length === root.cursorIndex
538
+ focused: root.focused
539
+ onActivated: function (idx) { root.activate(idx + root.homeLead.length) }
540
+ onMenuRequested: function(idx, pos) { root.openRailMenu(idx + root.homeLead.length, pos) }
541
+ }
542
+ }
543
+ Repeater {
544
+ id: homeRestRepeater
545
+ model: root.homeRest
546
+ delegate: SidebarRow {
547
+ lit: index + root.homeLead.length + root.recentEntries.length === root.litIndex
548
+ highlighted: index + root.homeLead.length + root.recentEntries.length === root.highlightedIndex
549
+ cursor: root.cursorDrawn && index + root.homeLead.length + root.recentEntries.length === root.cursorIndex
550
+ focused: root.focused
551
+ onActivated: function (idx) { root.activate(idx + root.homeLead.length + root.recentEntries.length) }
552
+ onMenuRequested: function(idx, pos) { root.openRailMenu(idx + root.homeLead.length + root.recentEntries.length, pos) }
553
+ onTabRequested: function (idx) { PlaceMenu.openTabAt(root, idx + root.homeLead.length + root.recentEntries.length) }
554
+ }
555
+ }
556
+ Repeater {
557
+ id: trashRepeater
558
+ model: root.trashEntries
559
+ delegate: RailTrashRow { rail: root }
560
+ }
561
+
562
+ RailHeading {
563
+ id: favouritesHeading
564
+ visible: root.userFavouriteEntries.length > 0
565
+ text: "FAVORITES"
566
+ topExtra: placesHeading.visible ? Style.spacing.panelGap : 0
567
+ }
568
+ Repeater {
569
+ id: favRepeater
570
+ model: root.userFavouriteEntries
571
+ delegate: SidebarRow {
572
+ lit: index + root.homeLead.length + root.recentEntries.length + root.homeRest.length + root.trashEntries.length === root.litIndex
573
+ highlighted: index + root.homeLead.length + root.recentEntries.length + root.homeRest.length + root.trashEntries.length === root.highlightedIndex
574
+ cursor: root.cursorDrawn && index + root.homeLead.length + root.recentEntries.length + root.homeRest.length + root.trashEntries.length === root.cursorIndex
575
+ focused: root.focused
576
+ // The rail's own reorder drag, persisted through the favourites store.
577
+ dragFrom: modelData.favouriteIndex
578
+ lineCount: root.userFavouriteEntries.length
579
+ onMoved: function (to) { root.moveFavourite(dragFrom, to) }
580
+ onReorderAt: function (line) { root.reorderLine = line }
581
+ onActivated: function (idx) { root.activate(idx + root.homeLead.length + root.recentEntries.length + root.homeRest.length + root.trashEntries.length) }
582
+ onMenuRequested: function (idx, pos) { root.openRailMenu(idx + root.homeLead.length + root.recentEntries.length + root.homeRest.length + root.trashEntries.length, pos) }
583
+ onTabRequested: function (idx) { PlaceMenu.openTabAt(root, idx + root.homeLead.length + root.recentEntries.length + root.homeRest.length + root.trashEntries.length) }
584
+ }
585
+ }
586
+
587
+ // The OEM panel idiom's own group gap, not the tighter row-to-row rhythm rows keep inside a group.
588
+ Item {
589
+ id: netGap
590
+ // The TAGS section lives inside this gap, so its rows end the places group by construction.
591
+ visible: root.networkEntries.length > 0 || (root.tagSection !== null && root.placesState.showTags === true)
592
+ width: rail.width
593
+ height: ((root.tagSection !== null && root.placesState.showTags === true) ? root.tagSection.height : 0) + (root.networkEntries.length > 0 ? Style.spacing.panelGap : 0)
594
+ }
595
+
596
+ // Self-hides with its list below when gio, the bookmarks file and Dropbox all have nothing to say.
597
+ Item {
598
+ visible: root.networkEntries.length > 0
599
+ width: rail.width
600
+ height: netHeading.implicitHeight + Style.spacing.rowGap
601
+
602
+ RailHeading {
603
+ id: netHeading
604
+ noBottom: true
605
+ text: "NETWORK"
606
+ }
607
+
608
+ // A hand-drawn plus, not a Text "+": at caption size the font glyph read as a Christian cross, not a plus. Sized off the heading's own font token.
609
+ Glyph {
610
+ id: addGlyph
611
+ // The rail's trailing indicator slot: caption wide, inset by rowPaddingX, anchored exactly as ui/SidebarRow.qml's dot is.
612
+ anchors.right: parent.right
613
+ anchors.rightMargin: Style.spacing.rowPaddingX
614
+ anchors.verticalCenter: netHeading.verticalCenter
615
+ name: "plus"
616
+ color: Theme.color.mutedFrame
617
+ width: Theme.font.caption
618
+ height: width
619
+ }
620
+
621
+ // Bigger than the ink it covers, so it centres on the ink's own box, in real pixels: a centre anchor quantises an odd size difference and leaves the two centres half a pixel apart.
622
+ Item {
623
+ id: addMark
624
+ width: Math.max(Theme.hitMin, Theme.font.caption)
625
+ height: width
626
+ x: addGlyph.x + (addGlyph.width - width) / 2
627
+ y: addGlyph.y + (addGlyph.height - height) / 2
628
+ Accessible.role: Accessible.Button
629
+ Accessible.name: "Add network location"
630
+ Accessible.onPressAction: root.addRequested()
631
+ TapHandler { onTapped: root.addRequested() }
632
+ }
633
+ }
634
+
635
+ Repeater {
636
+ id: netRepeater
637
+ model: root.networkEntries
638
+ delegate: SidebarRow {
639
+ lit: index + root.placesEntries.length === root.litIndex
640
+ highlighted: index + root.placesEntries.length === root.highlightedIndex
641
+ cursor: root.cursorDrawn && index + root.placesEntries.length === root.cursorIndex
642
+ markFocused: root.markFocused && cursor
643
+ focused: root.focused
644
+ renaming: (index + root.placesEntries.length) === root.renamingIndex
645
+ onActivated: function (idx) { root.activate(idx + root.placesEntries.length) }
646
+ onEjectRequested: function (idx) { root.ejectRow(idx + root.placesEntries.length) }
647
+ onMenuRequested: function (idx, pos) { root.openRailMenu(idx + root.placesEntries.length, pos) }
648
+ onRenameCommitted: function (idx, text) { root.commitRename(idx + root.placesEntries.length, text) }
649
+ onRenameCancelled: root.cancelRename()
650
+ }
651
+ }
652
+
653
+ Item {
654
+ visible: root.deviceEntries.length > 0
655
+ width: rail.width
656
+ height: Style.spacing.panelGap
657
+ }
658
+
659
+ // Self-hides with its list where lsblk reports no disk and no drive was found by looking, and carries no add mark: nothing here is bookmarked.
660
+ RailHeading {
661
+ id: devHeading
662
+ visible: root.deviceEntries.length > 0
663
+ text: "DEVICES"
664
+ }
665
+
666
+ Repeater {
667
+ id: devRepeater
668
+ model: root.deviceEntries
669
+ delegate: SidebarRow {
670
+ lit: index + root.placesEntries.length + root.networkEntries.length === root.litIndex
671
+ highlighted: index + root.placesEntries.length + root.networkEntries.length === root.highlightedIndex
672
+ cursor: root.cursorDrawn && index + root.placesEntries.length + root.networkEntries.length === root.cursorIndex
673
+ markFocused: root.markFocused && cursor
674
+ focused: root.focused
675
+ onActivated: function (idx) { root.activate(idx + root.placesEntries.length + root.networkEntries.length) }
676
+ onEjectRequested: function (idx) { root.ejectRow(idx + root.placesEntries.length + root.networkEntries.length) }
677
+ onMenuRequested: function (idx, pos) { root.openRailMenu(idx + root.placesEntries.length + root.networkEntries.length, pos) }
678
+ }
679
+ }
680
+ }
681
+ // Sidebar040 specimen 1: the bar lies over the boundary, one hairline in the row above and the rest under it.
682
+ Rectangle {
683
+ readonly property var row: favRepeater.itemAt(Math.min(root.reorderLine, favRepeater.count - 1))
684
+ readonly property real boundary: row ? row.y + (root.reorderLine === favRepeater.count ? row.height : 0) : 0
685
+ visible: root.reorderLine >= 0 && root.reorderLine <= favRepeater.count && row !== null
686
+ width: rail.width
687
+ height: Theme.accentEdge * Theme.spacing.hairline
688
+ y: boundary - Theme.spacing.hairline + rail.y
689
+ color: Theme.color.accent
690
+ z: 1
691
+ }
692
+ }
693
+
694
+ // The "+" ink, its hit target and the rail's own indicator dot: the three boxes that share one centre.
695
+ function networkMarkItems() { var netRow = netRepeater.itemAt(0); return [addGlyph, addMark, netRow ? netRow.indicatorSlot : null] }
696
+ function headingItems() { return [placesHeading, favouritesHeading, netHeading, devHeading] }
697
+ // The rail has no ListView virtualization, so every row already exists; the same itemFor idiom ui/Pane.qml uses for the list, so a test can find a rail row's on-screen box.
698
+ function railItemFor(index) {
699
+ if (index < root.homeLead.length) return homeLeadRepeater.itemAt(index)
700
+ var rest = index - root.homeLead.length
701
+ if (rest < root.recentEntries.length) return recentRepeater.itemAt(rest)
702
+ rest -= root.recentEntries.length
703
+ if (rest < root.homeRest.length) return homeRestRepeater.itemAt(rest)
704
+ rest -= root.homeRest.length
705
+ if (rest < root.trashEntries.length) return trashRepeater.itemAt(rest)
706
+ rest -= root.trashEntries.length
707
+ if (rest < root.userFavouriteEntries.length) return favRepeater.itemAt(rest)
708
+ rest -= root.userFavouriteEntries.length
709
+ if (rest < root.tagRows.length) return root.tagSection.itemAt(rest)
710
+ rest -= root.tagRows.length
711
+ if (rest < root.networkEntries.length)
712
+ return netRepeater.itemAt(rest)
713
+ rest -= root.networkEntries.length
714
+ return devRepeater.itemAt(rest)
715
+ }
716
+ // Observe the whole sidebar, including its empty background. A sibling
717
+ // overlay does not receive DOM events dispatched to rows beneath it.
718
+ PointHandler {
719
+ acceptedButtons: Qt.AllButtons
720
+ onActiveChanged: if (active) { root.pressed(); if (root.focused) root.pointerFocus = true }
721
+ }
722
+ // The rail edge, one Divider shared with the column edges.
723
+ Flea.Divider {
724
+ anchors.right: parent.right
725
+ anchors.top: parent.top
726
+ anchors.bottom: parent.bottom
727
+ }
728
+ }