flea-qml 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +511 -2
  3. package/THIRD-PARTY-NOTICES.md +29 -0
  4. package/demos/App.java +5 -0
  5. package/demos/Dockerfile +4 -0
  6. package/demos/Main.kt +3 -0
  7. package/demos/Program.cs +1 -0
  8. package/demos/README.md +12 -0
  9. package/demos/app.dart +3 -0
  10. package/demos/app.deb +0 -0
  11. package/demos/app.js +1 -0
  12. package/demos/backup.tar +0 -0
  13. package/demos/budget.xlsx +0 -0
  14. package/demos/build.sh +2 -0
  15. package/demos/bundle.zip +0 -0
  16. package/demos/clip.mp4 +0 -0
  17. package/demos/data.sqlite +0 -0
  18. package/demos/disk.iso +0 -0
  19. package/demos/engine.cpp +5 -0
  20. package/demos/firmware.bin +0 -0
  21. package/demos/flake.nix +4 -0
  22. package/demos/folder/inside.txt +1 -0
  23. package/demos/font.ttf +0 -0
  24. package/demos/index.html +5 -0
  25. package/demos/index.php +2 -0
  26. package/demos/init.lua +1 -0
  27. package/demos/letter.docx +0 -0
  28. package/demos/lib.rs +3 -0
  29. package/demos/main.c +6 -0
  30. package/demos/main.h +3 -0
  31. package/demos/math-and-diagrams.md +200 -0
  32. package/demos/notes.tar.gz +0 -0
  33. package/demos/notes.txt +3 -0
  34. package/demos/photo.png +0 -0
  35. package/demos/program +2 -0
  36. package/demos/report.pdf +32 -0
  37. package/demos/script.py +1 -0
  38. package/demos/server.go +7 -0
  39. package/demos/settings.json +4 -0
  40. package/demos/slides.pptx +0 -0
  41. package/demos/song.mp3 +0 -0
  42. package/demos/style.css +4 -0
  43. package/demos/tool.rb +1 -0
  44. package/demos/types.ts +2 -0
  45. package/docs/pele.md +23 -0
  46. package/docs/protocol.md +3420 -0
  47. package/docs/temml.md +63 -0
  48. package/index.js +107 -0
  49. package/package.json +34 -4
  50. package/src/archive.js +407 -0
  51. package/src/archiveops.js +728 -0
  52. package/src/backend.js +369 -0
  53. package/src/bulkrename.js +95 -0
  54. package/src/clip-host.js +76 -0
  55. package/src/clip.js +462 -0
  56. package/src/code.js +168 -0
  57. package/src/compare.js +74 -0
  58. package/src/create.js +128 -0
  59. package/src/dirsize.js +147 -0
  60. package/src/figure-svg.js +530 -0
  61. package/src/figure-thread.js +23 -0
  62. package/src/figure.js +132 -0
  63. package/src/find.js +55 -0
  64. package/src/flea.js +39 -0
  65. package/src/fsinfo.js +39 -0
  66. package/src/highlight.js +141 -0
  67. package/src/html.js +52 -0
  68. package/src/kinds.js +89 -0
  69. package/src/listing.js +130 -0
  70. package/src/markdown-html.js +317 -0
  71. package/src/mathml.js +186 -0
  72. package/src/menu.js +180 -0
  73. package/src/meta.js +135 -0
  74. package/src/office.js +810 -0
  75. package/src/order.js +100 -0
  76. package/src/peek.js +49 -0
  77. package/src/pele-wrapper.js +695 -0
  78. package/src/permissions.js +180 -0
  79. package/src/places.js +313 -0
  80. package/src/preview-language.js +10 -0
  81. package/src/terminal-server.js +95 -0
  82. package/src/terminal-shell.js +26 -0
  83. package/src/transfer.js +669 -0
  84. package/src/trash-record.js +41 -0
  85. package/src/trash.js +210 -0
  86. package/src/trashbrowse.js +327 -0
  87. package/src/uistate.js +393 -0
  88. package/src/uistore.js +168 -0
  89. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  90. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  91. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  92. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  93. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  94. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  95. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  96. package/src/vendor/pele.js +145 -0
  97. package/src/vendor/temml.mjs +14708 -0
  98. package/src/watch.js +98 -0
  99. package/src/wayland/client.js +109 -0
  100. package/src/wayland/protocol.js +55 -0
  101. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  102. package/src/wayland/protocols/wayland.xml +3248 -0
  103. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  104. package/src/wayland/resource-io.js +6 -0
  105. package/src/wayland/socket-discovery.js +26 -0
  106. package/src/wayland/transport-posix.js +120 -0
  107. package/src/wayland/transport-win32.js +11 -0
  108. package/src/wayland/transport.js +3 -0
  109. package/src/wayland/win32-resources.js +80 -0
  110. package/src/wayland/win32-wire.js +62 -0
  111. package/test/archive.test.js +80 -0
  112. package/test/bench-markdown-scroll.mjs +190 -0
  113. package/test/clip-host.test.js +113 -0
  114. package/test/code-preview.test.js +35 -0
  115. package/test/compare-ui-paths.test.js +24 -0
  116. package/test/compare.test.js +54 -0
  117. package/test/delete-trash.test.js +67 -0
  118. package/test/dirsize.test.js +138 -0
  119. package/test/file-ops.test.js +307 -0
  120. package/test/file-url.test.js +24 -0
  121. package/test/find-contents.test.js +161 -0
  122. package/test/find-input.test.js +66 -0
  123. package/test/fixtures/pele-parity.json +1006 -0
  124. package/test/highlight-launcher.test.js +35 -0
  125. package/test/html-preview.test.js +22 -0
  126. package/test/input.test.js +15 -0
  127. package/test/keymap.test.js +26 -0
  128. package/test/language-icons.test.js +36 -0
  129. package/test/launcher.test.js +51 -0
  130. package/test/manual-archive-here-ui.mjs +47 -0
  131. package/test/manual-code-scroll.mjs +28 -0
  132. package/test/manual-column-scroll.mjs +24 -0
  133. package/test/manual-compare-ui.mjs +20 -0
  134. package/test/manual-find-status.mjs +37 -0
  135. package/test/manual-folder-focus.mjs +63 -0
  136. package/test/manual-html-security.mjs +23 -0
  137. package/test/manual-html-ui.mjs +30 -0
  138. package/test/manual-syntax-preview.mjs +26 -0
  139. package/test/manual-terminal-lifecycle.mjs +42 -0
  140. package/test/manual-terminal-navigation.mjs +48 -0
  141. package/test/manual-terminal-ui.mjs +47 -0
  142. package/test/manual-trash-ui.mjs +86 -0
  143. package/test/markdown-diagrams.test.js +93 -0
  144. package/test/markdown-html.test.js +105 -0
  145. package/test/mathml.test.js +92 -0
  146. package/test/menu-transfer.test.js +57 -0
  147. package/test/office-preview.test.js +154 -0
  148. package/test/pele-parity.test.js +28 -0
  149. package/test/places.test.js +134 -0
  150. package/test/preview-keys.test.js +60 -0
  151. package/test/preview-language.test.js +22 -0
  152. package/test/status-filename.test.js +20 -0
  153. package/test/transfer-replace-merge.test.js +187 -0
  154. package/test/trash-address.test.js +23 -0
  155. package/test/trash-view.test.js +37 -0
  156. package/test/trashbrowse.test.js +135 -0
  157. package/ui/AboutFacts.qml +78 -0
  158. package/ui/ArchiveCards.qml +95 -0
  159. package/ui/ArchiveCompressDialog.qml +378 -0
  160. package/ui/ArchiveHereConfirm.qml +127 -0
  161. package/ui/ArchivePasswordDialog.qml +257 -0
  162. package/ui/Backend.qml +775 -0
  163. package/ui/BulkOldNames.qml +47 -0
  164. package/ui/BulkRenameDialog.qml +380 -0
  165. package/ui/CardFrame.qml +67 -0
  166. package/ui/CardScroll.qml +79 -0
  167. package/ui/CheckBox.qml +73 -0
  168. package/ui/ChromeBar.qml +360 -0
  169. package/ui/ChromeButton.qml +131 -0
  170. package/ui/ChromeModes.qml +43 -0
  171. package/ui/CodeLines.qml +256 -0
  172. package/ui/CodeRefusal.qml +52 -0
  173. package/ui/CollideConfirm.qml +147 -0
  174. package/ui/CollideHost.qml +167 -0
  175. package/ui/ColumnPane.qml +525 -0
  176. package/ui/ColumnRow.qml +380 -0
  177. package/ui/ColumnsArea.qml +594 -0
  178. package/ui/ColumnsLanding.qml +147 -0
  179. package/ui/CompareView.qml +321 -0
  180. package/ui/ContextMenu.qml +743 -0
  181. package/ui/ConvertDialog.qml +456 -0
  182. package/ui/CountedWorker.qml +7 -0
  183. package/ui/Crumb.qml +64 -0
  184. package/ui/CrumbDrop.qml +25 -0
  185. package/ui/CsvTable.qml +394 -0
  186. package/ui/CursorMark.qml +41 -0
  187. package/ui/DefaultClaim.qml +130 -0
  188. package/ui/DeviceLock.qml +47 -0
  189. package/ui/DeviceMounts.qml +540 -0
  190. package/ui/DialogButton.qml +150 -0
  191. package/ui/DialogField.qml +147 -0
  192. package/ui/DialogHeader.qml +31 -0
  193. package/ui/DialogScrim.qml +20 -0
  194. package/ui/DialogTitle.qml +37 -0
  195. package/ui/Divider.qml +9 -0
  196. package/ui/DropInto.qml +126 -0
  197. package/ui/DropTarget.qml +35 -0
  198. package/ui/DropboxMark.qml +60 -0
  199. package/ui/EmptyState.qml +150 -0
  200. package/ui/EscapeHint.qml +11 -0
  201. package/ui/FactsTable.qml +54 -0
  202. package/ui/FastScrollHandler.qml +476 -0
  203. package/ui/FatNamesConfirm.qml +157 -0
  204. package/ui/Favourites.qml +143 -0
  205. package/ui/FigureService.qml +120 -0
  206. package/ui/FileDrag.qml +267 -0
  207. package/ui/FilterStrip.qml +133 -0
  208. package/ui/FitMetrics.qml +22 -0
  209. package/ui/FlashDriveRows.qml +137 -0
  210. package/ui/FlashHost.qml +72 -0
  211. package/ui/FlashImageRow.qml +104 -0
  212. package/ui/FlashRunner.qml +119 -0
  213. package/ui/FlashStepRow.qml +172 -0
  214. package/ui/FlashStrip.qml +38 -0
  215. package/ui/FlashView.qml +397 -0
  216. package/ui/FleaMark.qml +88 -0
  217. package/ui/FlyoutFade.qml +23 -0
  218. package/ui/FontPane.qml +170 -0
  219. package/ui/Glass.qml +114 -0
  220. package/ui/GlassTagger.qml +59 -0
  221. package/ui/Glyph.qml +64 -0
  222. package/ui/GlyphSolid.qml +22 -0
  223. package/ui/GridArea.qml +664 -0
  224. package/ui/GridTile.qml +325 -0
  225. package/ui/GvfsBridge.qml +164 -0
  226. package/ui/Header.qml +433 -0
  227. package/ui/HoverTimers.qml +39 -0
  228. package/ui/HtmlClip.qml +30 -0
  229. package/ui/HtmlPane.qml +63 -0
  230. package/ui/HyprlandMark.qml +45 -0
  231. package/ui/Ipc.qml +1005 -0
  232. package/ui/IpcControls.qml +7 -0
  233. package/ui/IpcMenuState.qml +48 -0
  234. package/ui/IpcPreviewState.qml +185 -0
  235. package/ui/JumpPath.qml +51 -0
  236. package/ui/KeymapSheet.qml +561 -0
  237. package/ui/KeymapSheetResult.qml +89 -0
  238. package/ui/KindStack.qml +40 -0
  239. package/ui/LangMark.qml +51 -0
  240. package/ui/LangMarkLoader.qml +26 -0
  241. package/ui/List.qml +480 -0
  242. package/ui/LoadingState.qml +54 -0
  243. package/ui/LocalSend.qml +56 -0
  244. package/ui/LocalSendMark.qml +73 -0
  245. package/ui/MarkdownBlockView.qml +311 -0
  246. package/ui/MarkdownBlocks.qml +35 -0
  247. package/ui/MarkdownFigure.qml +254 -0
  248. package/ui/MarkdownImages.qml +72 -0
  249. package/ui/MarkdownList.qml +81 -0
  250. package/ui/MarkdownMathsText.qml +97 -0
  251. package/ui/MarkdownPane.qml +83 -0
  252. package/ui/MarkdownPictures.qml +41 -0
  253. package/ui/MarkdownQuote.qml +52 -0
  254. package/ui/MarkdownRemote.qml +82 -0
  255. package/ui/MarkdownSource.qml +155 -0
  256. package/ui/MarkdownTable.qml +222 -0
  257. package/ui/MarkdownTableScroll.qml +64 -0
  258. package/ui/MarkdownTableWheel.qml +126 -0
  259. package/ui/MarkdownText.qml +87 -0
  260. package/ui/MarkdownWorker.js +132 -0
  261. package/ui/MatchText.qml +99 -0
  262. package/ui/MediaSession.qml +38 -0
  263. package/ui/MediaSound.qml +26 -0
  264. package/ui/MediaStrip.qml +191 -0
  265. package/ui/MediaTrack.qml +36 -0
  266. package/ui/MediaVolume.qml +54 -0
  267. package/ui/MenuActionDialog.qml +396 -0
  268. package/ui/MenuEdgeFade.qml +24 -0
  269. package/ui/MenuHost.qml +227 -0
  270. package/ui/MenuRow.qml +351 -0
  271. package/ui/MountListing.qml +100 -0
  272. package/ui/NetworkDialog.qml +435 -0
  273. package/ui/NetworkForm.qml +426 -0
  274. package/ui/NetworkMounts.qml +913 -0
  275. package/ui/NetworkPlaces.qml +127 -0
  276. package/ui/OpenWithDialog.qml +613 -0
  277. package/ui/Opener.qml +95 -0
  278. package/ui/Pane.qml +1320 -0
  279. package/ui/PaneMenuActions.qml +527 -0
  280. package/ui/PanePath.qml +90 -0
  281. package/ui/PaneRail.qml +268 -0
  282. package/ui/PaneStates.qml +88 -0
  283. package/ui/PaneSwap.qml +339 -0
  284. package/ui/PaneWire.qml +601 -0
  285. package/ui/ParkedWait.qml +9 -0
  286. package/ui/PathEntry.qml +186 -0
  287. package/ui/PathJump.qml +400 -0
  288. package/ui/PdfViewer.qml +297 -0
  289. package/ui/PermissionsDialog.qml +658 -0
  290. package/ui/PermissionsHeadings.qml +31 -0
  291. package/ui/PhoneMounts.qml +67 -0
  292. package/ui/PickerAnswers.qml +75 -0
  293. package/ui/PickerBox.qml +26 -0
  294. package/ui/PickerChrome.qml +295 -0
  295. package/ui/PickerFilter.qml +97 -0
  296. package/ui/PickerGrid.qml +308 -0
  297. package/ui/PickerHeader.qml +28 -0
  298. package/ui/PickerLifecycle.qml +27 -0
  299. package/ui/PickerList.qml +218 -0
  300. package/ui/PickerListing.qml +149 -0
  301. package/ui/PickerPathBar.qml +127 -0
  302. package/ui/PickerPlaces.qml +189 -0
  303. package/ui/PickerRecent.qml +92 -0
  304. package/ui/PickerSave.qml +101 -0
  305. package/ui/PickerSelection.qml +155 -0
  306. package/ui/PickerWindow.qml +846 -0
  307. package/ui/PowerSectorsReader.qml +10 -0
  308. package/ui/PrebuildDriver.qml +146 -0
  309. package/ui/Preview.qml +1005 -0
  310. package/ui/PreviewAnimation.qml +24 -0
  311. package/ui/PreviewArchive.qml +91 -0
  312. package/ui/PreviewColumn.qml +553 -0
  313. package/ui/PreviewEdge.qml +63 -0
  314. package/ui/PreviewFacts.qml +30 -0
  315. package/ui/PreviewFactsLine.qml +34 -0
  316. package/ui/PreviewHtml.qml +233 -0
  317. package/ui/PreviewImage.qml +253 -0
  318. package/ui/PreviewImagePan.qml +13 -0
  319. package/ui/PreviewImageSharp.qml +32 -0
  320. package/ui/PreviewLines.qml +106 -0
  321. package/ui/PreviewMarkdown.qml +706 -0
  322. package/ui/PreviewMedia.qml +250 -0
  323. package/ui/PreviewPdf.qml +482 -0
  324. package/ui/PreviewScroll.qml +201 -0
  325. package/ui/PreviewSwap.qml +335 -0
  326. package/ui/PreviewText.qml +149 -0
  327. package/ui/ProtocolChip.qml +18 -0
  328. package/ui/QuickLookArchive.qml +41 -0
  329. package/ui/QuickLookBar.qml +103 -0
  330. package/ui/QuickLookPrepare.qml +355 -0
  331. package/ui/QuickLookSwap.qml +10 -0
  332. package/ui/QuickLookWarm.qml +65 -0
  333. package/ui/RailDrop.qml +155 -0
  334. package/ui/RailHeading.qml +19 -0
  335. package/ui/RailPlaces.qml +38 -0
  336. package/ui/RailTrashRow.qml +67 -0
  337. package/ui/RenameField.qml +353 -0
  338. package/ui/RendererRetry.qml +11 -0
  339. package/ui/ResizeHandle.qml +52 -0
  340. package/ui/Reveal.qml +54 -0
  341. package/ui/Row.qml +476 -0
  342. package/ui/RowDrag.qml +213 -0
  343. package/ui/RowDropTarget.qml +27 -0
  344. package/ui/RunCard.qml +286 -0
  345. package/ui/Scripts.qml +65 -0
  346. package/ui/SearchStrip.qml +232 -0
  347. package/ui/SelectionBand.qml +140 -0
  348. package/ui/SelectionPreview.qml +370 -0
  349. package/ui/SettingsFavourite.qml +122 -0
  350. package/ui/SettingsGroup.qml +96 -0
  351. package/ui/SettingsHint.qml +12 -0
  352. package/ui/SettingsMetrics.qml +29 -0
  353. package/ui/SettingsPane.qml +178 -0
  354. package/ui/SettingsPanel.qml +547 -0
  355. package/ui/SettingsRail.qml +128 -0
  356. package/ui/SettingsRow.qml +409 -0
  357. package/ui/SettingsRuler.qml +85 -0
  358. package/ui/SettingsSegment.qml +85 -0
  359. package/ui/ShareBrowser.qml +113 -0
  360. package/ui/ShareLink.qml +67 -0
  361. package/ui/ShelfPins.qml +101 -0
  362. package/ui/Sidebar.qml +728 -0
  363. package/ui/SidebarRow.qml +358 -0
  364. package/ui/Spinner.qml +60 -0
  365. package/ui/StableModel.qml +89 -0
  366. package/ui/StateMessage.qml +84 -0
  367. package/ui/StatusBar.qml +512 -0
  368. package/ui/StatusFileName.qml +27 -0
  369. package/ui/TabBar.qml +673 -0
  370. package/ui/TabDragGeometry.qml +60 -0
  371. package/ui/TagMenuMark.qml +52 -0
  372. package/ui/TagRailSection.qml +53 -0
  373. package/ui/TagSlot.qml +40 -0
  374. package/ui/TagState.qml +150 -0
  375. package/ui/TagSwatch.qml +28 -0
  376. package/ui/Taildrop.qml +86 -0
  377. package/ui/TailscaleMark.qml +50 -0
  378. package/ui/TerminalService.qml +69 -0
  379. package/ui/TerminalTab.qml +48 -0
  380. package/ui/TerminalTabs.qml +27 -0
  381. package/ui/Theme.qml +460 -0
  382. package/ui/ThemeColors.qml +43 -0
  383. package/ui/TransferCard.qml +239 -0
  384. package/ui/TrashConfirm.qml +129 -0
  385. package/ui/TrashDropHandler.qml +11 -0
  386. package/ui/TrashHost.qml +202 -0
  387. package/ui/TrashMonitor.qml +30 -0
  388. package/ui/TrashToolbar.qml +19 -0
  389. package/ui/TrashView.qml +492 -0
  390. package/ui/UnlockDialog.qml +450 -0
  391. package/ui/UnlockHost.qml +206 -0
  392. package/ui/UpdateCheck.qml +97 -0
  393. package/ui/UserDirs.qml +25 -0
  394. package/ui/ViewState.qml +565 -0
  395. package/ui/ViewportScrollBar.qml +195 -0
  396. package/ui/ViewportScrollBars.qml +25 -0
  397. package/ui/WebPreview.qml +33 -0
  398. package/ui/WebPreviewView.qml +26 -0
  399. package/ui/WindowBody.qml +829 -0
  400. package/ui/WindowDrag.qml +35 -0
  401. package/ui/boot/Commons/AudioNodes.qml +50 -0
  402. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  403. package/ui/boot/Commons/Border.qml +243 -0
  404. package/ui/boot/Commons/BorderGeometry.js +373 -0
  405. package/ui/boot/Commons/Color.qml +258 -0
  406. package/ui/boot/Commons/I18n.qml +118 -0
  407. package/ui/boot/Commons/InputDevices.qml +112 -0
  408. package/ui/boot/Commons/InputMethodState.qml +38 -0
  409. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  410. package/ui/boot/Commons/Numbers.js +19 -0
  411. package/ui/boot/Commons/Plural.js +91 -0
  412. package/ui/boot/Commons/ShellIpc.qml +12 -0
  413. package/ui/boot/Commons/Style.qml +561 -0
  414. package/ui/boot/Commons/Translations.qml +100 -0
  415. package/ui/boot/Commons/UntypedInput.qml +152 -0
  416. package/ui/boot/Commons/Util.qml +188 -0
  417. package/ui/boot/Commons/qmldir +13 -0
  418. package/ui/boot/LICENSE-omarchy +20 -0
  419. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  420. package/ui/boot/Ui/BarIconButton.qml +106 -0
  421. package/ui/boot/Ui/BarIconContent.qml +38 -0
  422. package/ui/boot/Ui/BarIndicator.qml +53 -0
  423. package/ui/boot/Ui/BarWidget.qml +50 -0
  424. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  425. package/ui/boot/Ui/BorderSurface.qml +40 -0
  426. package/ui/boot/Ui/Button.qml +224 -0
  427. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  428. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  429. package/ui/boot/Ui/CursorSurface.qml +42 -0
  430. package/ui/boot/Ui/Dropdown.qml +245 -0
  431. package/ui/boot/Ui/FilterInput.qml +26 -0
  432. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  433. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  434. package/ui/boot/Ui/MultiSelect.qml +624 -0
  435. package/ui/boot/Ui/NumberField.qml +86 -0
  436. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  437. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  438. package/ui/boot/Ui/Panel.qml +60 -0
  439. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  440. package/ui/boot/Ui/PanelController.qml +16 -0
  441. package/ui/boot/Ui/PanelHero.qml +112 -0
  442. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  443. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  444. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  445. package/ui/boot/Ui/PanelSlider.qml +156 -0
  446. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  447. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  448. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  449. package/ui/boot/Ui/PopupCard.qml +181 -0
  450. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  451. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  452. package/ui/boot/Ui/SizedImage.qml +125 -0
  453. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  454. package/ui/boot/Ui/TextField.qml +58 -0
  455. package/ui/boot/Ui/Toggle.qml +118 -0
  456. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  457. package/ui/boot/Ui/WidgetButton.qml +172 -0
  458. package/ui/boot/Ui/qmldir +41 -0
  459. package/ui/boot/fleatab.qml +50 -0
  460. package/ui/boot/picker.qml +39 -0
  461. package/ui/boot/shell.qml +131 -0
  462. package/ui/boot/tabtearoff.qml +51 -0
  463. package/ui/js/Anchor.js +379 -0
  464. package/ui/js/AnchorHold.js +162 -0
  465. package/ui/js/Archive.js +157 -0
  466. package/ui/js/ArchiveCard.js +44 -0
  467. package/ui/js/ArchiveOps.js +44 -0
  468. package/ui/js/BackReturn.js +118 -0
  469. package/ui/js/BulkRename.js +100 -0
  470. package/ui/js/Buttons.js +54 -0
  471. package/ui/js/ChromeKeys.js +116 -0
  472. package/ui/js/ClipMarks.js +73 -0
  473. package/ui/js/ClipSelection.js +67 -0
  474. package/ui/js/Clipboard.js +179 -0
  475. package/ui/js/Cloud.js +97 -0
  476. package/ui/js/CodeRuns.js +72 -0
  477. package/ui/js/Collide.js +212 -0
  478. package/ui/js/ColumnFit.js +28 -0
  479. package/ui/js/ColumnMenu.js +111 -0
  480. package/ui/js/Columns.js +298 -0
  481. package/ui/js/ColumnsCarry.js +103 -0
  482. package/ui/js/Compare.js +74 -0
  483. package/ui/js/Contrast.js +157 -0
  484. package/ui/js/ControlReader.js +42 -0
  485. package/ui/js/Convert.js +52 -0
  486. package/ui/js/CopyAs.js +52 -0
  487. package/ui/js/Crumbs.js +178 -0
  488. package/ui/js/Csv.js +231 -0
  489. package/ui/js/Density.js +44 -0
  490. package/ui/js/Devices.js +440 -0
  491. package/ui/js/DirSizes.js +180 -0
  492. package/ui/js/Drag.js +360 -0
  493. package/ui/js/DragOut.js +166 -0
  494. package/ui/js/DropDev.js +52 -0
  495. package/ui/js/Dropbox.js +41 -0
  496. package/ui/js/Eject.js +217 -0
  497. package/ui/js/Errors.js +188 -0
  498. package/ui/js/ExtThumbs.js +115 -0
  499. package/ui/js/Facts.js +402 -0
  500. package/ui/js/FigureDate.js +14 -0
  501. package/ui/js/Filter.js +285 -0
  502. package/ui/js/Find.js +46 -0
  503. package/ui/js/Flash.js +184 -0
  504. package/ui/js/FlashCard.js +79 -0
  505. package/ui/js/FlashEndings.js +125 -0
  506. package/ui/js/FlashPane.js +116 -0
  507. package/ui/js/FleaBin.js +12 -0
  508. package/ui/js/Focus.js +504 -0
  509. package/ui/js/FolderSorts.js +108 -0
  510. package/ui/js/FolderViews.js +44 -0
  511. package/ui/js/Format.js +314 -0
  512. package/ui/js/Fuzzy.js +141 -0
  513. package/ui/js/GlassTag.js +72 -0
  514. package/ui/js/Grid.js +33 -0
  515. package/ui/js/GridGeometry.js +90 -0
  516. package/ui/js/GridNames.js +153 -0
  517. package/ui/js/GvfsBridge.js +200 -0
  518. package/ui/js/HostClipboard.js +29 -0
  519. package/ui/js/Icons.js +218 -0
  520. package/ui/js/Input.js +50 -0
  521. package/ui/js/Jump.js +200 -0
  522. package/ui/js/KeyName.js +64 -0
  523. package/ui/js/Keymap.js +401 -0
  524. package/ui/js/Kinds.js +164 -0
  525. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  526. package/ui/js/LangMarkPaths.js +47 -0
  527. package/ui/js/LangMarks.js +120 -0
  528. package/ui/js/Lists.js +11 -0
  529. package/ui/js/LocalSend.js +39 -0
  530. package/ui/js/LockedMenu.js +63 -0
  531. package/ui/js/MakeDefault.js +186 -0
  532. package/ui/js/MarkBox.js +7 -0
  533. package/ui/js/Markdown.js +119 -0
  534. package/ui/js/MarkdownLists.js +86 -0
  535. package/ui/js/MarkdownMaths.js +58 -0
  536. package/ui/js/MarkdownPictures.js +32 -0
  537. package/ui/js/MarkdownPrepared.js +139 -0
  538. package/ui/js/MarkdownTableFit.js +34 -0
  539. package/ui/js/Marks.js +148 -0
  540. package/ui/js/Marquee.js +96 -0
  541. package/ui/js/Match.js +93 -0
  542. package/ui/js/Material.js +109 -0
  543. package/ui/js/MdBlocks.js +437 -0
  544. package/ui/js/MdBreak.js +71 -0
  545. package/ui/js/MdChunks.js +70 -0
  546. package/ui/js/MdContainer.js +137 -0
  547. package/ui/js/MdDocument.js +337 -0
  548. package/ui/js/MdEmph.js +97 -0
  549. package/ui/js/MdEntity.js +108 -0
  550. package/ui/js/MdEntityTable.js +223 -0
  551. package/ui/js/MdEscape.js +59 -0
  552. package/ui/js/MdFront.js +37 -0
  553. package/ui/js/MdHold.js +32 -0
  554. package/ui/js/MdHtml.js +307 -0
  555. package/ui/js/MdHtmlBlock.js +290 -0
  556. package/ui/js/MdHtmlHeading.js +53 -0
  557. package/ui/js/MdHtmlImage.js +179 -0
  558. package/ui/js/MdInline.js +154 -0
  559. package/ui/js/MdItems.js +212 -0
  560. package/ui/js/MdLeaf.js +309 -0
  561. package/ui/js/MdLink.js +196 -0
  562. package/ui/js/MdMath.js +67 -0
  563. package/ui/js/MdParagraphs.js +138 -0
  564. package/ui/js/MdRefs.js +232 -0
  565. package/ui/js/MdResolve.js +226 -0
  566. package/ui/js/MdRun.js +300 -0
  567. package/ui/js/MdUrl.js +226 -0
  568. package/ui/js/Menu.js +514 -0
  569. package/ui/js/MenuContext.js +85 -0
  570. package/ui/js/MenuFit.js +13 -0
  571. package/ui/js/MenuRefresh.js +74 -0
  572. package/ui/js/MenuWheel.js +35 -0
  573. package/ui/js/Messages.js +186 -0
  574. package/ui/js/Motion.js +16 -0
  575. package/ui/js/Mounts.js +320 -0
  576. package/ui/js/MouseNav.js +13 -0
  577. package/ui/js/Nav.js +329 -0
  578. package/ui/js/NetFs.js +26 -0
  579. package/ui/js/OpenWith.js +83 -0
  580. package/ui/js/Ops.js +585 -0
  581. package/ui/js/Palette.js +33 -0
  582. package/ui/js/PathBar.js +260 -0
  583. package/ui/js/Permissions.js +179 -0
  584. package/ui/js/Phones.js +112 -0
  585. package/ui/js/Picker.js +353 -0
  586. package/ui/js/PlaceMenu.js +96 -0
  587. package/ui/js/Places.js +265 -0
  588. package/ui/js/PreviewAsk.js +21 -0
  589. package/ui/js/PreviewKeys.js +155 -0
  590. package/ui/js/PreviewSettle.js +29 -0
  591. package/ui/js/PreviewSwap.js +101 -0
  592. package/ui/js/PropertyFacts.js +144 -0
  593. package/ui/js/Protocols.js +296 -0
  594. package/ui/js/QuickLookKinds.js +27 -0
  595. package/ui/js/QuickLookRect.js +24 -0
  596. package/ui/js/RailDrop.js +51 -0
  597. package/ui/js/RailKeys.js +87 -0
  598. package/ui/js/RailMenu.js +133 -0
  599. package/ui/js/Recent.js +135 -0
  600. package/ui/js/RecentMode.js +156 -0
  601. package/ui/js/Reload.js +60 -0
  602. package/ui/js/Renderer.js +9 -0
  603. package/ui/js/RunFile.js +143 -0
  604. package/ui/js/Scripts.js +28 -0
  605. package/ui/js/Scroll.js +304 -0
  606. package/ui/js/ScrollOff.js +69 -0
  607. package/ui/js/Search.js +478 -0
  608. package/ui/js/Selection.js +97 -0
  609. package/ui/js/Settings.js +476 -0
  610. package/ui/js/SettingsAbout.js +97 -0
  611. package/ui/js/SettingsShelf.js +52 -0
  612. package/ui/js/SheetKeys.js +42 -0
  613. package/ui/js/SheetQuery.js +281 -0
  614. package/ui/js/ShelfPile.js +27 -0
  615. package/ui/js/SlowClick.js +53 -0
  616. package/ui/js/SlowOp.js +6 -0
  617. package/ui/js/Sort.js +134 -0
  618. package/ui/js/Startup.js +63 -0
  619. package/ui/js/StateWords.js +17 -0
  620. package/ui/js/Status.js +238 -0
  621. package/ui/js/Swap.js +166 -0
  622. package/ui/js/TabMove.js +39 -0
  623. package/ui/js/Tabs.js +751 -0
  624. package/ui/js/Tags.js +134 -0
  625. package/ui/js/Taildrop.js +128 -0
  626. package/ui/js/Tap.js +180 -0
  627. package/ui/js/TextSize.js +107 -0
  628. package/ui/js/Thumbs.js +158 -0
  629. package/ui/js/Transfer.js +172 -0
  630. package/ui/js/Trash.js +84 -0
  631. package/ui/js/TrashDates.js +47 -0
  632. package/ui/js/TrashDrop.js +64 -0
  633. package/ui/js/UiState.js +438 -0
  634. package/ui/js/Unlock.js +152 -0
  635. package/ui/js/Update.js +130 -0
  636. package/ui/js/UserDirs.js +56 -0
  637. package/ui/qmldir +230 -0
@@ -0,0 +1,743 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ import "." as Flea
4
+ import "js/Keymap.js" as Keymap
5
+ import "js/LockedMenu.js" as LockedMenu
6
+ import "js/Menu.js" as Menu
7
+ import "js/MenuContext.js" as MenuContext
8
+ import "js/MenuFit.js" as MenuFit
9
+ import "js/MenuRefresh.js" as MenuRefresh
10
+
11
+ // A plain overlay, not a QQC Popup: the one Controls import cost 10 ms of warm startup.
12
+ Item {
13
+ id: root
14
+
15
+ // Row actions, provider leaves and header choices share this signal, routed by Pane.
16
+ signal chosen(string action)
17
+ signal refused(string reason)
18
+ signal snapshotRequested()
19
+
20
+ property bool opened: false
21
+ property bool preparing: false
22
+ // Driven from ui/Pane.qml's own state, so this file owns no hidden-file logic itself.
23
+ property bool showHidden: false
24
+ // [{id, label}], the reachable Taildrop targets; installed providers keep their disabled reason.
25
+ property var taildropPeers: []
26
+ property bool taildropInstalled: false
27
+ property var localSend: ({ installed: false, peers: [], checking: false })
28
+ property string taildropReason: ""
29
+ property string taildropCode: ""
30
+ property bool providersRefreshing: false
31
+ property bool refreshOwed: false
32
+ property var lastProviderAnswer: null
33
+ // The archive formats this box actually probed, and whether a converter is installed at all.
34
+ property var archiveFormats: []
35
+ property bool canConvert: false
36
+ property bool canExtract: false
37
+ // The cursor row's own name, which is what tells a disk image (lists only) from an archive.
38
+ property string archiveName: ""
39
+ property bool clipboardAvailable: false
40
+ property bool clipboardWatchFailed: false
41
+ // Whether the cursor row is a file, an archive, an image; all decided client-side. Only a file row takes send peers.
42
+ property bool rowIsFile: false
43
+ property bool rowIsArchive: false
44
+ property bool rowIsImage: false
45
+ // Flash to drive reads the name rather than the icon, and a zip's one member once named.
46
+ property string rowName: ""
47
+ property string flashZipMember: ""
48
+ // MenuAdditions040: Show original is visible but only on a symlink.
49
+ property bool rowIsSymlink: false
50
+ // MenuAdditions040 callout 10: the two-byte shebang read of the cursor row at menu open.
51
+ property bool rowHasShebang: false
52
+ // ... and whether the menu's single target is the cursor row itself.
53
+ property bool cursorIsTarget: false
54
+ property int rowMode: 0
55
+ property int selectionCount: 0
56
+ // MenuAdditions040: Permissions takes the whole selection.
57
+ property var selectionModes: []
58
+ // Compare follows held metadata and the matching snapshot while the menu is open.
59
+ property var compareKinds: []
60
+ // OpenWith.html's flyout rows, filled by ui/PaneMenuActions.qml when the registry answers.
61
+ property var openWithApps: []
62
+ property bool openWithLoaded: false
63
+ // The actual account directory is available only after a fresh provider status and identity check.
64
+ property string dropboxPath: ""
65
+ property bool dropboxInstalled: false
66
+ property string dropboxReason: ""
67
+ property string dropboxCode: ""
68
+ // A row already inside the account directory offers a share link instead of another move.
69
+ property bool rowInDropbox: false
70
+ // False on a listing's empty space, where Menus.html's background column is what opens instead.
71
+ // openBackground() is its only writer and openAt() puts it back, because one instance serves both.
72
+ property bool hasRow: true
73
+ // ExtThumbs: src/backend/extclass.rs's word for the directory this opening answers for.
74
+ property string storageClass: ""
75
+ // The Locked tile's folder while one is drawn, "" while none is; bound by ui/Pane.qml. A right
76
+ // click landing on the tile reaches openBackground through the views, which routes it to the
77
+ // locked folder's own menu rather than the parent's background one.
78
+ property string tileTarget: ""
79
+ property int tileMode: 0
80
+ // The folder this opening answers for, captured at open and cleared at close.
81
+ property string lockedPath: ""
82
+ property int lockedMode: 0
83
+ readonly property bool forLocked: root.lockedPath.length > 0
84
+ // The owner's menuSelectionIdentity(), read at open and at a choice only; none, as on the rail, reads "".
85
+ property var identitySource: null
86
+ function selectionIdentity() { return root.identitySource ? root.identitySource.menuSelectionIdentity() : "" }
87
+ property string openedIdentity: ""
88
+ // The owner intersects the live monitor work area with this application's viewport.
89
+ property rect workArea: Qt.rect(0, 0, root.width, root.height)
90
+ readonly property real workAreaInset: Theme.space(4)
91
+
92
+ // The rail's own rows when ui/Sidebar.qml raised this menu, empty when the listing did. One
93
+ // instance serves both: a second one in this tree takes the keyboard from the list, see AGENTS.md.
94
+ property var railEntries: []
95
+ // The key naming the rail row those rows belong to, see ui/js/Mounts.js "railKey": the rail
96
+ // rebuilds on a poll, so an index would name a different row by the time one is chosen.
97
+ property string railKey: ""
98
+ readonly property bool forRail: root.railEntries.length > 0
99
+ signal railChosen(string action, string key)
100
+ // The Locked tile's folder with the row chosen on it, read before close() the way railKey
101
+ // is: close() clears lockedPath, so Pane.qml cannot read it after the menu is gone.
102
+ signal lockedChosen(string action, string path)
103
+
104
+ // ui/Header.qml's own entrance, the third face of this one instance: openForHeader() flips the
105
+ // entries to ui/js/Menu.js headerEntries (the column toggles and the hidden toggle, built from
106
+ // qs module ViewState's hidden columns and the pane's showHidden), and every row flows back
107
+ // through chosen() like the listing's own. A row's chosen verb routes by prefix in ui/Pane.qml.
108
+ property bool forHeader: false
109
+ function openForHeader(scenePoint) {
110
+ root.forHeader = true
111
+ root.place(scenePoint)
112
+ }
113
+
114
+ // The pane keeps its Keys handler on the list, so the menu has to hand focus back on close.
115
+ property Item focusHolder: null
116
+ // The original focus item may disappear while the menu is open; the owning view is the fallback.
117
+ property Item focusOwner: null
118
+ readonly property bool keyboardFocused: keyCatcher.activeFocus
119
+
120
+ // The keyboard-highlighted top-level row, and which row's flyout is open beside it, or -1.
121
+ property int cursor: 0
122
+ property int openSubmenuRow: -1
123
+ property int submenuCursor: 0
124
+ // A hidden row still opens its flyout from the action.
125
+ property string loneFlyoutAction: ""
126
+ readonly property bool submenuOpen: root.openSubmenuRow >= 0 || root.loneFlyoutAction.length > 0
127
+ // What a closing flyout keeps drawing while it fades out; ui/FlyoutFade.qml holds them at their last open values.
128
+ property var heldRows: []
129
+ property real heldOffset: 0
130
+ property string heldAction: ""
131
+ property var flyoutFade: null
132
+ function submenuAction() {
133
+ return root.loneFlyoutAction.length > 0 ? root.loneFlyoutAction
134
+ : root.entries[root.openSubmenuRow] ? root.entries[root.openSubmenuRow].action : ""
135
+ }
136
+ onSubmenuOpenChanged: if (!root.submenuOpen) MenuRefresh.pressChanged(root, false); else root.buildFlyoutFade()
137
+ // Built by the first flyout, so a menu that never opens one holds neither the fade nor the held values.
138
+ function buildFlyoutFade() {
139
+ if (root.flyoutFade === null)
140
+ root.flyoutFade = Qt.createComponent("FlyoutFade.qml").createObject(root, { menu: root })
141
+ }
142
+ // The glyph every open flyout row draws, read back so a test can name it without OCR.
143
+ function submenuGlyphs() {
144
+ if (!root.submenuOpen)
145
+ return ""
146
+ var mark = Menu.submenuGlyph(root.submenuAction())
147
+ var out = []
148
+ for (var i = 0; i < root.submenuEntries.length; i++) {
149
+ // What the row draws, not what the flyout defaults to: an Open with row carries its own
150
+ // glyph or an application icon, and reporting the default made a check measure nothing.
151
+ var row = root.submenuEntries[i]
152
+ out.push(row.separator === true ? "" : row.icon ? "icon"
153
+ : row.glyph !== undefined ? row.glyph : mark)
154
+ }
155
+ return out.join("|")
156
+ }
157
+
158
+ // The open flyout draws the row's entries, or the lone action's leaves.
159
+ readonly property var submenuEntries: root.loneFlyoutAction.length > 0
160
+ ? Menu.flyoutEntries(root.loneFlyoutAction)
161
+ : (root.submenuOpen && root.entries[root.openSubmenuRow]
162
+ ? root.entries[root.openSubmenuRow].submenu : [])
163
+
164
+ // The row list this menu currently offers; a test reads this back through shell.qml's IPC.
165
+ property var entries: []
166
+ // While a card's rows build its fit reads none, then reads them once when they stand; a closed flyout holds nothing.
167
+ property bool rowsBuilding: false
168
+ property bool submenuBuilding: false
169
+ function setEntries(next, flyoutRow) { try { root.rowsBuilding = true; root.submenuBuilding = root.submenuOpen; root.entries = next; if (flyoutRow !== undefined) root.openSubmenuRow = flyoutRow } finally { root.rowsBuilding = false; root.submenuBuilding = false } }
170
+ property bool canTrash: true
171
+ // False in a read-only folder, off the listing's own w flag; true until the pane knows better.
172
+ property bool dirWritable: true
173
+ // A filesystem that holds no links offers no Paste as rows; true until the pane knows better.
174
+ property bool canLink: true
175
+ // The Tags flyout's rows while a menu is live; null where tags cannot be stored, which drops the row.
176
+ property var tagSubmenu: null
177
+ // Issue 179: the background menu's Sort by flyout offers its forget row only for this folder.
178
+ property bool hasFolderSort: false
179
+
180
+ // Menu.js builds entries from the context shared by the row menu and query; includeHidden is the keymap sheet's.
181
+ function buildEntries(flyoutAction, includeHidden) {
182
+ // Rail rows arrive built with their own release verdicts.
183
+ if (root.forRail)
184
+ return root.railEntries
185
+ if (root.forHeader)
186
+ return Menu.headerEntries(ViewState.hiddenCols, root.showHidden)
187
+ // Locked rows address the denied folder rather than its covered parent.
188
+ if (root.forLocked)
189
+ return LockedMenu.lockedEntries({ lockedMode: root.lockedMode, hiddenActions: ViewState.menuHidden })
190
+ return Menu.listingEntries(root.listingContext(flyoutAction, includeHidden))
191
+ }
192
+ function listingContext(flyoutAction, includeHidden) {
193
+ return MenuContext.listingContext(root, flyoutAction, includeHidden, {
194
+ scripts: Flea.Scripts.entries, hiddenActions: ViewState.menuHidden,
195
+ langMarks: ViewState.langMarks,
196
+ thumbPreview: ViewState.preview, updateVersion: UpdateCheck.menuVersion
197
+ })
198
+ }
199
+
200
+ // The row item at an index, for ui/Ipc.qml: a driven test clicks a menu row without deriving its geometry from a row count the Menus settings can now change under it.
201
+ function itemFor(index) { return menuRows.itemAt(index) }
202
+ function submenuItemFor(index) { return subRows.itemAt(index) }
203
+ readonly property var frameItem: frame
204
+ readonly property var submenuFrameItem: flyout
205
+
206
+ // A separator is never the cursor, so both key steps and the opening cursor skip over one.
207
+ function stepCursor(from, delta) { return Menu.stepRow(root.entries, from, delta) }
208
+ // The same rule inside a flyout: OpenWith.html's tail row sits under its own separator.
209
+ function stepSubmenu(from, delta) { return Menu.stepRow(root.submenuEntries, from, delta) }
210
+ // A wheel over the main frame closes the flyout first, the same path moving the pointer
211
+ // onto a plain row takes, and then steps; the highlight never moves where it is not drawn.
212
+ function stepMain(delta) {
213
+ root.openSubmenuRow = -1
214
+ root.loneFlyoutAction = ""
215
+ root.cursor = root.stepCursor(root.cursor, delta)
216
+ }
217
+
218
+ function firstRow() {
219
+ return root.stepCursor(-1, 1)
220
+ }
221
+
222
+ anchors.fill: parent
223
+ // The frames fade out after opened flips (ui/Reveal.qml), so the menu stays drawn until both are gone and takes no input meanwhile.
224
+ visible: root.opened || frame.opacity > 0 || flyout.opacity > 0
225
+ enabled: root.opened
226
+ z: 1
227
+
228
+ // Built by the first open, so a pane that never opens a menu holds no animation (the first frame's cost).
229
+ property var fade: null
230
+ onOpenedChanged: {
231
+ if (root.opened && root.fade === null)
232
+ root.fade = Qt.createComponent("Reveal.qml").createObject(root, { target: frame, shown: Qt.binding(function () { return root.opened }) })
233
+ }
234
+
235
+ // Takes a point in scene coordinates and keeps the whole menu inside the pane it belongs to.
236
+ function openAt(scenePoint) {
237
+ root.clearRail()
238
+ root.forHeader = false
239
+ root.hasRow = true
240
+ root.place(scenePoint)
241
+ }
242
+
243
+ // Empty-space clicks draw background entries, or the Locked tile's own folder menu.
244
+ function openBackground(scenePoint) {
245
+ if (root.tileTarget.length > 0) {
246
+ root.openLocked(root.tileTarget, root.tileMode, scenePoint)
247
+ return
248
+ }
249
+ root.clearRail()
250
+ root.forHeader = false
251
+ root.hasRow = false
252
+ root.place(scenePoint)
253
+ }
254
+
255
+ // The Locked tile's own entrance, capturing the folder this opening answers for; every row
256
+ // below acts on that folder alone, through ui/Pane.qml's locked dispatch rather than the
257
+ // snapshot the cursor rows take.
258
+ function openLocked(path, mode, scenePoint) {
259
+ // An empty locked menu opens no frame and closes a stale one, so a refusal leaves no actionable frame standing.
260
+ var refusal = LockedMenu.lockedRefusal({ lockedMode: mode, hiddenActions: ViewState.menuHidden })
261
+ if (refusal.length > 0) { root.close(); root.refused(refusal); return }
262
+ root.clearRail()
263
+ root.forHeader = false
264
+ root.hasRow = false
265
+ root.lockedPath = path
266
+ root.lockedMode = mode
267
+ root.place(scenePoint)
268
+ }
269
+
270
+ // ui/Sidebar.qml's own entrance to this same menu: the rail hands in its rows and the key that
271
+ // names the row they came from, and a rail row with nothing to release opens no menu at all.
272
+ function openForRail(key, entries, scenePoint) {
273
+ if (!entries || entries.length === 0)
274
+ return
275
+ root.railKey = key
276
+ root.railEntries = entries
277
+ root.place(scenePoint)
278
+ }
279
+
280
+ // Cleared on both ends: a rail entry left standing would put Eject on a listing row's menu,
281
+ // and a locked path left standing would put the old folder's rows on another directory's tile.
282
+ function clearRail() {
283
+ root.railEntries = []
284
+ root.railKey = ""
285
+ root.lockedPath = ""
286
+ root.lockedMode = 0
287
+ }
288
+
289
+ // Where the menu was asked to open, in this item's own coordinates; clampFrame runs twice on it.
290
+ property real placeX: 0
291
+ property real placeY: 0
292
+
293
+ // A Column hands its implicitHeight to the frame one polish after its model changes, so the
294
+ // height place() reads is still the menu that was open before this one. Clamping again on the
295
+ // real height lands before the first paint, so no menu is placed against another's size.
296
+ function clampFrame() {
297
+ frame.x = root.workArea.x + Menu.clamp(root.placeX - root.workArea.x, frame.width, root.workArea.width)
298
+ frame.y = root.workArea.y + root.workAreaInset
299
+ + Menu.clamp(root.placeY - root.workArea.y - root.workAreaInset,
300
+ frame.height, Math.max(0, root.workArea.height - 2 * root.workAreaInset))
301
+ }
302
+
303
+ // The scene point any row or the ground last saw, so a row tells a moving pointer from one it arrived under.
304
+ property point pointerGlobal: Qt.point(-1, -1)
305
+ // Placing to its first frame's afterAnimating: Qt hovers what that frame shows, kept rows too, so a "move" then is the rest point; Qt 6.12 can hover later, which ui/MenuRow.qml reads as rest.
306
+ property bool pointerSettling: false
307
+ Connections {
308
+ target: root.pointerSettling ? root.Window.window : null
309
+ function onAfterAnimating() { root.pointerSettling = false }
310
+ }
311
+
312
+ function place(scenePoint) {
313
+ root.refreshOwed = false
314
+ root.preparing = true
315
+ if (!root.opened)
316
+ root.focusHolder = root.Window.window ? root.Window.window.activeFocusItem : null
317
+ root.pointerGlobal = Qt.point(-1, -1)
318
+ root.pointerSettling = true
319
+ // A place that changes nothing drawn schedules no frame, so it asks for the one that ends the settle.
320
+ if (root.Window.window) root.Window.window.update()
321
+ var point = root.mapFromItem(null, scenePoint)
322
+ root.placeX = point.x
323
+ root.placeY = point.y
324
+ root.setEntries(root.buildEntries())
325
+ root.openedIdentity = root.selectionIdentity()
326
+ root.loneFlyoutAction = ""
327
+ scroll.contentY = 0
328
+ scroll.resetSteps()
329
+ subScroll.resetSteps()
330
+ root.clampFrame()
331
+ root.cursor = root.firstRow()
332
+ root.openSubmenuRow = -1
333
+ root.submenuCursor = 0
334
+ root.opened = true
335
+ root.preparing = false
336
+ if (root.hasRow && !root.forRail && !root.forHeader) root.snapshotRequested()
337
+ keyCatcher.forceActiveFocus()
338
+ }
339
+ onWorkAreaChanged: if (root.opened) root.clampFrame()
340
+ onCursorChanged: scroll.reveal(menuRows.itemAt(root.cursor))
341
+ onSubmenuCursorChanged: subScroll.reveal(subRows.itemAt(root.submenuCursor))
342
+
343
+ // Every wheel scroll calls this, so a shut menu costs nothing and never touches focus.
344
+ function close() {
345
+ root.refreshOwed = false
346
+ if (!root.opened)
347
+ return
348
+ root.opened = false
349
+ root.openSubmenuRow = -1
350
+ root.loneFlyoutAction = ""
351
+ root.clearRail()
352
+ var holder = root.focusHolder && root.focusHolder.visible && root.focusHolder.enabled ? root.focusHolder : root.focusOwner
353
+ if (holder)
354
+ holder.forceActiveFocus()
355
+ }
356
+
357
+ // The menu closes before the action runs, so it never hangs over the listing that action opened.
358
+ function choose(action) {
359
+ if (!root.validateChoice(action, "")) return
360
+ // Both read before close(), which is what clears the rail rows and the locked path.
361
+ var key = root.railKey
362
+ var rail = root.forRail
363
+ var locked = root.lockedPath
364
+ root.close()
365
+ if (rail) {
366
+ root.railChosen(action, key)
367
+ return
368
+ }
369
+ if (locked.length > 0) {
370
+ root.lockedChosen(action, locked)
371
+ return
372
+ }
373
+ root.chosen(action)
374
+ }
375
+
376
+ // One signal covers every submenu: the row's own action, a colon, and the entry chosen inside it.
377
+ function chooseSub(id) {
378
+ var entry = root.entries[root.openSubmenuRow]
379
+ var action = root.loneFlyoutAction || (entry ? entry.action : "")
380
+ if (root.loneFlyoutAction.length > 0) {
381
+ var lonePick = Menu.loneChoice(root.loneFlyoutAction, id, root.forRail, root.forHeader, root.hasRow, root.openedIdentity, root.selectionIdentity())
382
+ if (lonePick.kind !== "fire") {
383
+ root.refuseLone(lonePick.kind)
384
+ return
385
+ }
386
+ }
387
+ if (!action || !root.validateChoice(action, id)) return
388
+ // A Tags choice changes the boxes this flyout draws, so the menu stays up and the answer redraws them in place.
389
+ if (action !== "tags") root.close()
390
+ root.chosen(action + ":" + id)
391
+ }
392
+
393
+ function openSubmenu(index) {
394
+ root.loneFlyoutAction = ""
395
+ if (!Menu.hasSubmenu(root.entries[index]) || root.entries[index].disabled === true) return
396
+ root.submenuBuilding = true
397
+ try { root.openSubmenuRow = index } finally { root.submenuBuilding = false }
398
+ root.submenuCursor = 0
399
+ subScroll.contentY = 0
400
+ }
401
+
402
+ // c and P open Copy as and Paste as with the flyout already open.
403
+ function openSubmenuFor(action) {
404
+ var leaves = Menu.flyoutEntries(action)
405
+ if (!leaves.length) return false
406
+ if (action === "pasteAs" && !root.clipboardAvailable) {
407
+ root.close()
408
+ root.refused(Menu.EMPTY_CLIPBOARD)
409
+ return false
410
+ }
411
+ if (!root.validateChoice(action, leaves[0].id)) return false
412
+ var pick = Menu.submenuFor(action, root.entries, root.clipboardAvailable)
413
+ if (pick.kind === "row") {
414
+ root.cursor = pick.index
415
+ root.openSubmenu(pick.index)
416
+ return true
417
+ }
418
+ if (pick.kind === "lone") {
419
+ root.submenuBuilding = true
420
+ try { root.openSubmenuRow = -1; root.loneFlyoutAction = action } finally { root.submenuBuilding = false }
421
+ root.submenuCursor = 0
422
+ subScroll.contentY = 0
423
+ return true
424
+ }
425
+ if (pick.kind === "refuse") {
426
+ root.close()
427
+ root.refused(Menu.EMPTY_CLIPBOARD)
428
+ return false
429
+ }
430
+ return false
431
+ }
432
+
433
+ // Fresh capabilities use the normal inventory; selection stays on its action and placement uses the existing clamp.
434
+ function refreshProviderRows() {
435
+ if (!root.opened || root.forRail || root.forHeader || root.forLocked) return
436
+ // Replacing a pressed delegate destroys its grab before release can activate it.
437
+ if (MenuRefresh.anyPressed(menuRows, root.submenuOpen ? subRows : null)) {
438
+ root.refreshOwed = true
439
+ return
440
+ }
441
+ root.refreshOwed = false
442
+ var next = root.buildEntries()
443
+ // An answer that changed nothing drawn leaves every row standing: no model reset, no cursor move.
444
+ if (MenuRefresh.unchanged(root.entries, next)) return
445
+ MenuRefresh.apply(root, next)
446
+ }
447
+ // A finished refresh: its answer is what later opens draw while their own refresh runs behind them, see ui/js/MenuRefresh.js.
448
+ function providersSettled() {
449
+ root.lastProviderAnswer = MenuRefresh.settle(MenuRefresh.live(root))
450
+ root.refreshProviderRows()
451
+ }
452
+
453
+ function refuseLone(kind) {
454
+ root.close()
455
+ root.refused(kind === "moved" ? "Selected items changed; reopen the menu."
456
+ : "That action is no longer available; reopen the menu.")
457
+ }
458
+
459
+ // Rebuild only to validate; rows stay fixed while the menu is open under the pointer.
460
+ function validateChoice(action, subId, includeHidden) {
461
+ return MenuContext.validateChoice(root, action, subId, includeHidden)
462
+ }
463
+
464
+ // Offsets are summed, not multiplied; a lone flyout stands at the top.
465
+ function submenuOffset() {
466
+ if (root.loneFlyoutAction.length > 0)
467
+ return 0
468
+ var y = 0
469
+ for (var i = 0; i < root.openSubmenuRow; i++)
470
+ y += root.entries[i].separator === true ? separatorProbe.separatorHeight : Theme.rowHeight
471
+ return y - scroll.contentY
472
+ }
473
+
474
+ // One row off the model, only so the two heights above are read from MenuRow rather than repeated here.
475
+ Flea.MenuRow {
476
+ id: separatorProbe
477
+ visible: false
478
+ entry: ({ separator: true })
479
+ }
480
+
481
+ // The ground owns every pointer event outside the rows: hover stops here, the wheel is swallowed, and the click that closes is taken on release so the row beneath never sees a press the close would have handed it.
482
+ MouseArea {
483
+ id: ground
484
+ anchors.fill: parent
485
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
486
+ hoverEnabled: true
487
+ onEntered: root.pointerGlobal = ground.mapToItem(null, ground.mouseX, ground.mouseY)
488
+ onPositionChanged: function (mouse) { root.pointerGlobal = ground.mapToItem(null, mouse.x, mouse.y) }
489
+ onClicked: root.close()
490
+ onWheel: function (wheel) { wheel.accepted = true }
491
+ }
492
+
493
+ // How far an edge fade reaches past the card's padding, as a share of a row, so it covers the first cut row without washing a whole one.
494
+ readonly property real fadeRowShare: 0.7
495
+ readonly property real fadeReach: Math.round(Theme.rowHeight * root.fadeRowShare)
496
+ // The widest row's wanted width in a card's column; a binding that calls it follows every row's own text.
497
+ function widestRow(column) { return MenuFit.widestWanted(column.children) }
498
+
499
+ Rectangle {
500
+ id: frame
501
+ opacity: 0
502
+ // Theme.menuWidth, or the widest row's own width when a label and its hint need more, never past the work area.
503
+ width: Math.max(0, Math.min(root.workArea.width, root.rowsBuilding ? Theme.menuWidth : Math.max(Theme.menuWidth, root.widestRow(rows))))
504
+ onWidthChanged: if (root.opened) root.clampFrame()
505
+ // The vertical inset keeps the first and last row's square highlight off the rounded corners.
506
+ height: Math.max(0, Math.min(rows.implicitHeight + 2 * Theme.spacing.rowPaddingY,
507
+ root.workArea.height - 2 * root.workAreaInset))
508
+ // The height this menu is actually going to have, arriving after place() has already run.
509
+ onHeightChanged: if (root.opened) root.clampFrame()
510
+ color: Theme.color.surface
511
+ border.width: Theme.spacing.hairline
512
+ border.color: Theme.color.mutedFrame
513
+ // Mirrors hyprland decoration:rounding, same as NetworkDialog; 0 on a stock box stays square.
514
+ radius: Style.cornerRadius
515
+
516
+ // The card takes every press no row takes (padding, separator, disabled row); a stepped body is no Flickable, so the closing ground would.
517
+ MouseArea { anchors.fill: parent; acceptedButtons: Qt.LeftButton | Qt.RightButton }
518
+
519
+ Flea.CardScroll {
520
+ id: scroll
521
+ // A menu steps the highlight, never pixel scrolls: one row a notch, one row per row height of touchpad travel. No bar, no lane.
522
+ highlightSteps: true
523
+ stepBy: function (delta) { root.stepMain(delta) }
524
+ revealClearY: root.fadeReach
525
+ // A rebuilt model keeps the flyout's row, else the cursor's, in view.
526
+ onContentHeightChanged: if (root.opened) scroll.reveal(menuRows.itemAt(root.openSubmenuRow >= 0 ? root.openSubmenuRow : root.cursor))
527
+ anchors.fill: parent
528
+ anchors.topMargin: Theme.spacing.rowPaddingY
529
+ anchors.bottomMargin: Theme.spacing.rowPaddingY
530
+ Column {
531
+ id: rows
532
+ width: parent.width
533
+
534
+ Repeater {
535
+ id: menuRows
536
+ model: root.entries
537
+ delegate: Flea.MenuRow {
538
+ id: row
539
+ required property var modelData
540
+ required property int index
541
+ width: rows.width
542
+ entry: row.modelData
543
+ onPressedChanged: MenuRefresh.pressChanged(root, row.pressed)
544
+ current: !root.submenuOpen && root.cursor === row.index
545
+ lastPointerGlobal: root.pointerGlobal
546
+ onPointerSeen: function (at) { root.pointerGlobal = at }
547
+ onPointerMoved: {
548
+ if (root.pointerSettling) return
549
+ root.cursor = row.index
550
+ if (Menu.hasSubmenu(row.modelData)) root.openSubmenu(row.index)
551
+ else { root.openSubmenuRow = -1; root.loneFlyoutAction = "" }
552
+ }
553
+ onActivated: {
554
+ if (Menu.hasSubmenu(row.modelData))
555
+ root.openSubmenu(row.index)
556
+ else
557
+ root.choose(row.modelData.action)
558
+ }
559
+ }
560
+ }
561
+ }
562
+ }
563
+ Flea.MenuEdgeFade {
564
+ anchors.top: parent.top
565
+ inset: Theme.spacing.rowPaddingY
566
+ reach: root.fadeReach
567
+ visible: scroll.contentY > 0
568
+ }
569
+ Flea.MenuEdgeFade {
570
+ anchors.bottom: parent.bottom
571
+ inset: Theme.spacing.rowPaddingY
572
+ reach: root.fadeReach
573
+ visible: scroll.contentY + scroll.height < scroll.contentHeight
574
+ rotation: 180
575
+ }
576
+ }
577
+
578
+ // The flyout: a second frame beside whichever row opened it, only while one has, at least its parent's width.
579
+ Rectangle {
580
+ id: flyout
581
+ visible: root.submenuOpen || flyout.opacity > 0
582
+ enabled: root.submenuOpen
583
+ opacity: 0
584
+ x: frame.x + frame.width + width <= root.workArea.x + root.workArea.width
585
+ ? frame.x + frame.width : Math.max(root.workArea.x, frame.x - width)
586
+ // peers.y already carries the inset, so the flyout frame itself stays on the row grid.
587
+ y: Math.max(root.workArea.y + root.workAreaInset,
588
+ Math.min(frame.y + root.heldOffset, root.workArea.y + root.workArea.height - root.workAreaInset - height))
589
+ // Its own read, so a change of the parent's width does not read every peer's width again.
590
+ readonly property real widestPeer: root.submenuBuilding ? 0 : root.widestRow(peers)
591
+ width: Math.max(0, Math.min(Math.max(frame.width, Theme.menuWidth, flyout.widestPeer), root.workArea.width))
592
+ height: Math.max(0, Math.min(peers.implicitHeight + 2 * Theme.spacing.rowPaddingY,
593
+ root.workArea.height - 2 * root.workAreaInset))
594
+ color: Theme.color.surface
595
+ border.width: Theme.spacing.hairline
596
+ border.color: Theme.color.mutedFrame
597
+ radius: Style.cornerRadius
598
+
599
+ // The flyout takes its dead presses the same way.
600
+ MouseArea { anchors.fill: parent; acceptedButtons: Qt.LeftButton | Qt.RightButton }
601
+
602
+ Flea.CardScroll {
603
+ id: subScroll
604
+ // The flyout steps like the main frame, through its own cursor and reveal.
605
+ highlightSteps: true
606
+ stepBy: function (delta) { root.submenuCursor = root.stepSubmenu(root.submenuCursor, delta) }
607
+ revealClearY: root.fadeReach
608
+ anchors.fill: parent
609
+ anchors.topMargin: Theme.spacing.rowPaddingY
610
+ anchors.bottomMargin: Theme.spacing.rowPaddingY
611
+ Column {
612
+ id: peers
613
+ width: parent.width
614
+
615
+ Repeater {
616
+ id: subRows
617
+ model: root.heldRows
618
+ delegate: Flea.MenuRow {
619
+ id: subRow
620
+ required property var modelData
621
+ required property int index
622
+ width: peers.width
623
+ // Which mark a whole flyout draws is ui/js/Menu.js submenuGlyph's to say, so the
624
+ // read-back submenuGlyphs() above and the drawn row cannot answer differently.
625
+ // A flyout row may carry its own mark and caption: OpenWith.html rides each
626
+ // application's own Icon in the mark slot and puts "default" in the hint slot,
627
+ // and its tail row sits under a separator. MenuAdditions040 leaves carry a
628
+ // keyHint letter instead, drawn only while key hints are on, like every hint.
629
+ entry: ({ label: subRow.modelData.label, action: "",
630
+ disabled: subRow.modelData.disabled === true,
631
+ separator: subRow.modelData.separator === true,
632
+ hint: subRow.modelData.hint !== undefined ? subRow.modelData.hint
633
+ : subRow.modelData.keyHint !== undefined && ViewState.keyHints
634
+ ? subRow.modelData.keyHint : undefined,
635
+ // flea-qml: the leaf's letter, bold in its label while key hints are on.
636
+ mnemonic: subRow.modelData.keyHint !== undefined && ViewState.keyHints
637
+ ? (subRow.modelData.mark !== undefined ? subRow.modelData.mark : subRow.modelData.keyHint) : undefined,
638
+ icon: subRow.modelData.icon,
639
+ tagRow: subRow.modelData.tagRow,
640
+ glyph: subRow.modelData.glyph !== undefined ? subRow.modelData.glyph
641
+ : Menu.submenuGlyph(root.heldAction) })
642
+ current: root.submenuCursor === subRow.index
643
+ onPressedChanged: MenuRefresh.pressChanged(root, subRow.pressed)
644
+ // A flyout opened by key can land under the resting pointer too, so it reads the same point.
645
+ lastPointerGlobal: root.pointerGlobal
646
+ onPointerSeen: function (at) { root.pointerGlobal = at }
647
+ onPointerMoved: if (!root.pointerSettling && subRow.modelData.separator !== true) root.submenuCursor = subRow.index
648
+ onActivated: root.chooseSub(subRow.modelData.id)
649
+ }
650
+ }
651
+ }
652
+ }
653
+ Flea.MenuEdgeFade {
654
+ anchors.top: parent.top
655
+ inset: Theme.spacing.rowPaddingY
656
+ reach: root.fadeReach
657
+ visible: subScroll.contentY > 0
658
+ }
659
+ Flea.MenuEdgeFade {
660
+ anchors.bottom: parent.bottom
661
+ inset: Theme.spacing.rowPaddingY
662
+ reach: root.fadeReach
663
+ visible: subScroll.contentY + subScroll.height < subScroll.contentHeight
664
+ rotation: 180
665
+ }
666
+ }
667
+
668
+ // One focus catcher for the whole menu: real QML focus never moves into the Repeater rows
669
+ // themselves, so every key lands here regardless of which level is open. They arrive through
670
+ // keys.toml's own table, so j and k step this list the way they step every other one.
671
+ Item {
672
+ id: keyCatcher
673
+ anchors.fill: parent
674
+ focus: true
675
+
676
+ Keys.onPressed: function (event) {
677
+ var action = Keymap.lookup(event.key, event.text, event.modifiers, "menu")
678
+ event.accepted = true
679
+ if (action === "escape") {
680
+ if (root.submenuOpen) {
681
+ root.openSubmenuRow = -1
682
+ root.loneFlyoutAction = ""
683
+ } else
684
+ root.close()
685
+ event.accepted = true
686
+ return
687
+ }
688
+ if (action === "cursorDown") {
689
+ if (root.submenuOpen)
690
+ root.submenuCursor = root.stepSubmenu(root.submenuCursor, 1)
691
+ else
692
+ root.cursor = root.stepCursor(root.cursor, 1)
693
+ event.accepted = true
694
+ return
695
+ }
696
+ if (action === "cursorUp") {
697
+ if (root.submenuOpen)
698
+ root.submenuCursor = root.stepSubmenu(root.submenuCursor, -1)
699
+ else
700
+ root.cursor = root.stepCursor(root.cursor, -1)
701
+ event.accepted = true
702
+ return
703
+ }
704
+ if (action === "parent") {
705
+ root.openSubmenuRow = -1
706
+ root.loneFlyoutAction = ""
707
+ return
708
+ }
709
+ if (action === "menuRight") { root.openSubmenu(root.cursor); return }
710
+ // A flyout letter answers only the flyout it was opened for.
711
+ if (root.submenuOpen) {
712
+ var opener = root.loneFlyoutAction.length > 0
713
+ ? { action: root.loneFlyoutAction } : root.entries[root.openSubmenuRow]
714
+ if (opener && (opener.action === "copyAs" || opener.action === "pasteAs")) {
715
+ var leaves = root.submenuEntries
716
+ for (var l = 0; l < leaves.length; l++) {
717
+ if (leaves[l].separator === true) continue
718
+ if (leaves[l].id === action) { root.chooseSub(leaves[l].id); return }
719
+ }
720
+ }
721
+ // 1 to 7 toggle the Tags flyout row that carries the digit, like a click.
722
+ if (opener && opener.action === "tags" && /^tag[1-7]$/.test(action)) { root.chooseSub(root.submenuEntries[Number(action.charAt(3)) - 1].id); return }
723
+ }
724
+ if (action === "open" || action === "preview") {
725
+ if (root.submenuOpen) {
726
+ var sub = root.submenuEntries[root.submenuCursor]
727
+ if (sub)
728
+ root.chooseSub(sub.id)
729
+ } else {
730
+ var entry = root.entries[root.cursor]
731
+ if (Menu.hasSubmenu(entry))
732
+ root.openSubmenu(root.cursor)
733
+ else if (entry && entry.separator !== true)
734
+ root.choose(entry.action)
735
+ }
736
+ event.accepted = true
737
+ }
738
+ }
739
+ }
740
+ onCompareKindsChanged: root.refreshProviderRows()
741
+ readonly property bool langMarks: ViewState.langMarks
742
+ onLangMarksChanged: root.refreshProviderRows()
743
+ }