flea-qml 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (625) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +478 -2
  3. package/THIRD-PARTY-NOTICES.md +29 -0
  4. package/demos/App.java +5 -0
  5. package/demos/Dockerfile +4 -0
  6. package/demos/Main.kt +3 -0
  7. package/demos/Program.cs +1 -0
  8. package/demos/README.md +12 -0
  9. package/demos/app.dart +3 -0
  10. package/demos/app.deb +0 -0
  11. package/demos/app.js +1 -0
  12. package/demos/backup.tar +0 -0
  13. package/demos/budget.xlsx +0 -0
  14. package/demos/build.sh +2 -0
  15. package/demos/bundle.zip +0 -0
  16. package/demos/clip.mp4 +0 -0
  17. package/demos/data.sqlite +0 -0
  18. package/demos/disk.iso +0 -0
  19. package/demos/engine.cpp +5 -0
  20. package/demos/firmware.bin +0 -0
  21. package/demos/flake.nix +4 -0
  22. package/demos/folder/inside.txt +1 -0
  23. package/demos/font.ttf +0 -0
  24. package/demos/index.html +5 -0
  25. package/demos/index.php +2 -0
  26. package/demos/init.lua +1 -0
  27. package/demos/letter.docx +0 -0
  28. package/demos/lib.rs +3 -0
  29. package/demos/main.c +6 -0
  30. package/demos/main.h +3 -0
  31. package/demos/math-and-diagrams.md +200 -0
  32. package/demos/notes.tar.gz +0 -0
  33. package/demos/notes.txt +3 -0
  34. package/demos/photo.png +0 -0
  35. package/demos/program +2 -0
  36. package/demos/report.pdf +32 -0
  37. package/demos/script.py +1 -0
  38. package/demos/server.go +7 -0
  39. package/demos/settings.json +4 -0
  40. package/demos/slides.pptx +0 -0
  41. package/demos/song.mp3 +0 -0
  42. package/demos/style.css +4 -0
  43. package/demos/tool.rb +1 -0
  44. package/demos/types.ts +2 -0
  45. package/docs/pele.md +23 -0
  46. package/docs/protocol.md +3420 -0
  47. package/docs/temml.md +63 -0
  48. package/index.js +107 -0
  49. package/package.json +34 -4
  50. package/src/archive.js +402 -0
  51. package/src/archiveops.js +697 -0
  52. package/src/backend.js +369 -0
  53. package/src/bulkrename.js +95 -0
  54. package/src/clip-host.js +76 -0
  55. package/src/clip.js +462 -0
  56. package/src/code.js +168 -0
  57. package/src/compare.js +74 -0
  58. package/src/create.js +128 -0
  59. package/src/dirsize.js +147 -0
  60. package/src/figure-svg.js +530 -0
  61. package/src/figure-thread.js +23 -0
  62. package/src/figure.js +132 -0
  63. package/src/find.js +55 -0
  64. package/src/flea.js +39 -0
  65. package/src/fsinfo.js +39 -0
  66. package/src/highlight.js +141 -0
  67. package/src/html.js +52 -0
  68. package/src/kinds.js +89 -0
  69. package/src/listing.js +130 -0
  70. package/src/markdown-html.js +317 -0
  71. package/src/mathml.js +186 -0
  72. package/src/menu.js +180 -0
  73. package/src/meta.js +129 -0
  74. package/src/office.js +810 -0
  75. package/src/order.js +100 -0
  76. package/src/peek.js +49 -0
  77. package/src/pele-wrapper.js +695 -0
  78. package/src/permissions.js +180 -0
  79. package/src/places.js +313 -0
  80. package/src/preview-language.js +10 -0
  81. package/src/transfer.js +669 -0
  82. package/src/trash-record.js +41 -0
  83. package/src/trash.js +210 -0
  84. package/src/trashbrowse.js +326 -0
  85. package/src/uistate.js +393 -0
  86. package/src/uistore.js +168 -0
  87. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  88. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  89. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  90. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  91. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  92. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  93. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  94. package/src/vendor/pele.js +145 -0
  95. package/src/vendor/temml.mjs +14708 -0
  96. package/src/watch.js +98 -0
  97. package/src/wayland/client.js +109 -0
  98. package/src/wayland/protocol.js +55 -0
  99. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  100. package/src/wayland/protocols/wayland.xml +3248 -0
  101. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  102. package/src/wayland/resource-io.js +6 -0
  103. package/src/wayland/socket-discovery.js +26 -0
  104. package/src/wayland/transport-posix.js +120 -0
  105. package/src/wayland/transport-win32.js +11 -0
  106. package/src/wayland/transport.js +3 -0
  107. package/src/wayland/win32-resources.js +80 -0
  108. package/src/wayland/win32-wire.js +62 -0
  109. package/test/archive.test.js +80 -0
  110. package/test/bench-markdown-scroll.mjs +190 -0
  111. package/test/clip-host.test.js +113 -0
  112. package/test/code-preview.test.js +35 -0
  113. package/test/compare-ui-paths.test.js +24 -0
  114. package/test/compare.test.js +54 -0
  115. package/test/delete-trash.test.js +67 -0
  116. package/test/dirsize.test.js +138 -0
  117. package/test/file-ops.test.js +241 -0
  118. package/test/find-contents.test.js +161 -0
  119. package/test/find-input.test.js +66 -0
  120. package/test/fixtures/pele-parity.json +1006 -0
  121. package/test/highlight-launcher.test.js +35 -0
  122. package/test/html-preview.test.js +22 -0
  123. package/test/input.test.js +15 -0
  124. package/test/keymap.test.js +26 -0
  125. package/test/language-icons.test.js +36 -0
  126. package/test/launcher.test.js +51 -0
  127. package/test/manual-code-scroll.mjs +28 -0
  128. package/test/manual-column-scroll.mjs +24 -0
  129. package/test/manual-compare-ui.mjs +20 -0
  130. package/test/manual-find-status.mjs +37 -0
  131. package/test/manual-html-security.mjs +23 -0
  132. package/test/manual-html-ui.mjs +30 -0
  133. package/test/manual-syntax-preview.mjs +26 -0
  134. package/test/manual-trash-ui.mjs +86 -0
  135. package/test/markdown-diagrams.test.js +93 -0
  136. package/test/markdown-html.test.js +105 -0
  137. package/test/mathml.test.js +92 -0
  138. package/test/menu-transfer.test.js +57 -0
  139. package/test/office-preview.test.js +154 -0
  140. package/test/pele-parity.test.js +28 -0
  141. package/test/places.test.js +134 -0
  142. package/test/preview-keys.test.js +60 -0
  143. package/test/preview-language.test.js +22 -0
  144. package/test/status-filename.test.js +20 -0
  145. package/test/transfer-replace-merge.test.js +187 -0
  146. package/test/trash-address.test.js +23 -0
  147. package/test/trash-view.test.js +37 -0
  148. package/test/trashbrowse.test.js +135 -0
  149. package/ui/AboutFacts.qml +78 -0
  150. package/ui/ArchiveCards.qml +68 -0
  151. package/ui/ArchiveCompressDialog.qml +378 -0
  152. package/ui/ArchivePasswordDialog.qml +257 -0
  153. package/ui/Backend.qml +771 -0
  154. package/ui/BulkOldNames.qml +47 -0
  155. package/ui/BulkRenameDialog.qml +380 -0
  156. package/ui/CardFrame.qml +67 -0
  157. package/ui/CardScroll.qml +79 -0
  158. package/ui/CheckBox.qml +73 -0
  159. package/ui/ChromeBar.qml +360 -0
  160. package/ui/ChromeButton.qml +131 -0
  161. package/ui/ChromeModes.qml +43 -0
  162. package/ui/CodeLines.qml +256 -0
  163. package/ui/CodeRefusal.qml +52 -0
  164. package/ui/CollideConfirm.qml +147 -0
  165. package/ui/CollideHost.qml +167 -0
  166. package/ui/ColumnPane.qml +523 -0
  167. package/ui/ColumnRow.qml +380 -0
  168. package/ui/ColumnsArea.qml +594 -0
  169. package/ui/ColumnsLanding.qml +147 -0
  170. package/ui/CompareView.qml +321 -0
  171. package/ui/ContextMenu.qml +743 -0
  172. package/ui/ConvertDialog.qml +456 -0
  173. package/ui/CountedWorker.qml +7 -0
  174. package/ui/Crumb.qml +64 -0
  175. package/ui/CrumbDrop.qml +25 -0
  176. package/ui/CsvTable.qml +394 -0
  177. package/ui/CursorMark.qml +41 -0
  178. package/ui/DefaultClaim.qml +130 -0
  179. package/ui/DeviceLock.qml +47 -0
  180. package/ui/DeviceMounts.qml +540 -0
  181. package/ui/DialogButton.qml +150 -0
  182. package/ui/DialogField.qml +147 -0
  183. package/ui/DialogHeader.qml +31 -0
  184. package/ui/DialogScrim.qml +20 -0
  185. package/ui/DialogTitle.qml +37 -0
  186. package/ui/Divider.qml +9 -0
  187. package/ui/DropInto.qml +126 -0
  188. package/ui/DropTarget.qml +35 -0
  189. package/ui/DropboxMark.qml +60 -0
  190. package/ui/EmptyState.qml +150 -0
  191. package/ui/EscapeHint.qml +11 -0
  192. package/ui/FactsTable.qml +54 -0
  193. package/ui/FastScrollHandler.qml +476 -0
  194. package/ui/FatNamesConfirm.qml +157 -0
  195. package/ui/Favourites.qml +143 -0
  196. package/ui/FigureService.qml +120 -0
  197. package/ui/FileDrag.qml +267 -0
  198. package/ui/FilterStrip.qml +133 -0
  199. package/ui/FitMetrics.qml +22 -0
  200. package/ui/FlashDriveRows.qml +137 -0
  201. package/ui/FlashHost.qml +72 -0
  202. package/ui/FlashImageRow.qml +104 -0
  203. package/ui/FlashRunner.qml +119 -0
  204. package/ui/FlashStepRow.qml +172 -0
  205. package/ui/FlashStrip.qml +38 -0
  206. package/ui/FlashView.qml +397 -0
  207. package/ui/FleaMark.qml +88 -0
  208. package/ui/FlyoutFade.qml +23 -0
  209. package/ui/FontPane.qml +170 -0
  210. package/ui/Glass.qml +114 -0
  211. package/ui/GlassTagger.qml +59 -0
  212. package/ui/Glyph.qml +64 -0
  213. package/ui/GlyphSolid.qml +22 -0
  214. package/ui/GridArea.qml +664 -0
  215. package/ui/GridTile.qml +325 -0
  216. package/ui/GvfsBridge.qml +164 -0
  217. package/ui/Header.qml +433 -0
  218. package/ui/HoverTimers.qml +39 -0
  219. package/ui/HtmlClip.qml +30 -0
  220. package/ui/HtmlPane.qml +63 -0
  221. package/ui/HyprlandMark.qml +45 -0
  222. package/ui/Ipc.qml +1005 -0
  223. package/ui/IpcControls.qml +7 -0
  224. package/ui/IpcMenuState.qml +48 -0
  225. package/ui/IpcPreviewState.qml +185 -0
  226. package/ui/JumpPath.qml +51 -0
  227. package/ui/KeymapSheet.qml +561 -0
  228. package/ui/KeymapSheetResult.qml +89 -0
  229. package/ui/KindStack.qml +40 -0
  230. package/ui/LangMark.qml +51 -0
  231. package/ui/LangMarkLoader.qml +26 -0
  232. package/ui/List.qml +480 -0
  233. package/ui/LoadingState.qml +54 -0
  234. package/ui/LocalSend.qml +56 -0
  235. package/ui/LocalSendMark.qml +73 -0
  236. package/ui/MarkdownBlockView.qml +311 -0
  237. package/ui/MarkdownBlocks.qml +35 -0
  238. package/ui/MarkdownFigure.qml +254 -0
  239. package/ui/MarkdownImages.qml +72 -0
  240. package/ui/MarkdownList.qml +81 -0
  241. package/ui/MarkdownMathsText.qml +97 -0
  242. package/ui/MarkdownPane.qml +83 -0
  243. package/ui/MarkdownPictures.qml +41 -0
  244. package/ui/MarkdownQuote.qml +52 -0
  245. package/ui/MarkdownRemote.qml +82 -0
  246. package/ui/MarkdownSource.qml +155 -0
  247. package/ui/MarkdownTable.qml +222 -0
  248. package/ui/MarkdownTableScroll.qml +64 -0
  249. package/ui/MarkdownTableWheel.qml +126 -0
  250. package/ui/MarkdownText.qml +87 -0
  251. package/ui/MarkdownWorker.js +132 -0
  252. package/ui/MatchText.qml +99 -0
  253. package/ui/MediaSession.qml +38 -0
  254. package/ui/MediaSound.qml +26 -0
  255. package/ui/MediaStrip.qml +191 -0
  256. package/ui/MediaTrack.qml +36 -0
  257. package/ui/MediaVolume.qml +54 -0
  258. package/ui/MenuActionDialog.qml +396 -0
  259. package/ui/MenuEdgeFade.qml +24 -0
  260. package/ui/MenuHost.qml +227 -0
  261. package/ui/MenuRow.qml +351 -0
  262. package/ui/MountListing.qml +100 -0
  263. package/ui/NetworkDialog.qml +435 -0
  264. package/ui/NetworkForm.qml +426 -0
  265. package/ui/NetworkMounts.qml +913 -0
  266. package/ui/NetworkPlaces.qml +127 -0
  267. package/ui/OpenWithDialog.qml +613 -0
  268. package/ui/Opener.qml +115 -0
  269. package/ui/Pane.qml +1296 -0
  270. package/ui/PaneMenuActions.qml +527 -0
  271. package/ui/PanePath.qml +90 -0
  272. package/ui/PaneRail.qml +239 -0
  273. package/ui/PaneStates.qml +88 -0
  274. package/ui/PaneSwap.qml +339 -0
  275. package/ui/PaneWire.qml +605 -0
  276. package/ui/ParkedWait.qml +9 -0
  277. package/ui/PathEntry.qml +186 -0
  278. package/ui/PathJump.qml +400 -0
  279. package/ui/PdfViewer.qml +297 -0
  280. package/ui/PermissionsDialog.qml +658 -0
  281. package/ui/PermissionsHeadings.qml +31 -0
  282. package/ui/PhoneMounts.qml +67 -0
  283. package/ui/PickerAnswers.qml +75 -0
  284. package/ui/PickerBox.qml +26 -0
  285. package/ui/PickerChrome.qml +295 -0
  286. package/ui/PickerFilter.qml +97 -0
  287. package/ui/PickerGrid.qml +308 -0
  288. package/ui/PickerHeader.qml +28 -0
  289. package/ui/PickerLifecycle.qml +27 -0
  290. package/ui/PickerList.qml +218 -0
  291. package/ui/PickerListing.qml +149 -0
  292. package/ui/PickerPathBar.qml +127 -0
  293. package/ui/PickerPlaces.qml +189 -0
  294. package/ui/PickerRecent.qml +92 -0
  295. package/ui/PickerSave.qml +101 -0
  296. package/ui/PickerSelection.qml +155 -0
  297. package/ui/PickerWindow.qml +846 -0
  298. package/ui/PowerSectorsReader.qml +10 -0
  299. package/ui/PrebuildDriver.qml +146 -0
  300. package/ui/Preview.qml +1004 -0
  301. package/ui/PreviewAnimation.qml +24 -0
  302. package/ui/PreviewArchive.qml +66 -0
  303. package/ui/PreviewColumn.qml +551 -0
  304. package/ui/PreviewEdge.qml +63 -0
  305. package/ui/PreviewFacts.qml +30 -0
  306. package/ui/PreviewFactsLine.qml +34 -0
  307. package/ui/PreviewHtml.qml +233 -0
  308. package/ui/PreviewImage.qml +253 -0
  309. package/ui/PreviewImagePan.qml +13 -0
  310. package/ui/PreviewImageSharp.qml +32 -0
  311. package/ui/PreviewLines.qml +106 -0
  312. package/ui/PreviewMarkdown.qml +706 -0
  313. package/ui/PreviewMedia.qml +250 -0
  314. package/ui/PreviewPdf.qml +482 -0
  315. package/ui/PreviewScroll.qml +201 -0
  316. package/ui/PreviewSwap.qml +335 -0
  317. package/ui/PreviewText.qml +149 -0
  318. package/ui/ProtocolChip.qml +18 -0
  319. package/ui/QuickLookArchive.qml +38 -0
  320. package/ui/QuickLookBar.qml +103 -0
  321. package/ui/QuickLookPrepare.qml +355 -0
  322. package/ui/QuickLookSwap.qml +10 -0
  323. package/ui/QuickLookWarm.qml +65 -0
  324. package/ui/RailDrop.qml +155 -0
  325. package/ui/RailHeading.qml +19 -0
  326. package/ui/RailPlaces.qml +38 -0
  327. package/ui/RailTrashRow.qml +67 -0
  328. package/ui/RenameField.qml +353 -0
  329. package/ui/RendererRetry.qml +11 -0
  330. package/ui/ResizeHandle.qml +52 -0
  331. package/ui/Reveal.qml +54 -0
  332. package/ui/Row.qml +476 -0
  333. package/ui/RowDrag.qml +213 -0
  334. package/ui/RowDropTarget.qml +27 -0
  335. package/ui/RunCard.qml +286 -0
  336. package/ui/Scripts.qml +65 -0
  337. package/ui/SearchStrip.qml +232 -0
  338. package/ui/SelectionBand.qml +140 -0
  339. package/ui/SelectionPreview.qml +370 -0
  340. package/ui/SettingsFavourite.qml +122 -0
  341. package/ui/SettingsGroup.qml +96 -0
  342. package/ui/SettingsHint.qml +12 -0
  343. package/ui/SettingsMetrics.qml +29 -0
  344. package/ui/SettingsPane.qml +178 -0
  345. package/ui/SettingsPanel.qml +547 -0
  346. package/ui/SettingsRail.qml +128 -0
  347. package/ui/SettingsRow.qml +409 -0
  348. package/ui/SettingsRuler.qml +85 -0
  349. package/ui/SettingsSegment.qml +85 -0
  350. package/ui/ShareBrowser.qml +113 -0
  351. package/ui/ShareLink.qml +67 -0
  352. package/ui/ShelfPins.qml +101 -0
  353. package/ui/Sidebar.qml +732 -0
  354. package/ui/SidebarRow.qml +358 -0
  355. package/ui/Spinner.qml +60 -0
  356. package/ui/StableModel.qml +89 -0
  357. package/ui/StateMessage.qml +84 -0
  358. package/ui/StatusBar.qml +512 -0
  359. package/ui/StatusFileName.qml +27 -0
  360. package/ui/TabBar.qml +673 -0
  361. package/ui/TabDragGeometry.qml +60 -0
  362. package/ui/TagMenuMark.qml +52 -0
  363. package/ui/TagRailSection.qml +53 -0
  364. package/ui/TagSlot.qml +40 -0
  365. package/ui/TagState.qml +150 -0
  366. package/ui/TagSwatch.qml +28 -0
  367. package/ui/Taildrop.qml +86 -0
  368. package/ui/TailscaleMark.qml +50 -0
  369. package/ui/Theme.qml +460 -0
  370. package/ui/ThemeColors.qml +43 -0
  371. package/ui/TransferCard.qml +239 -0
  372. package/ui/TrashConfirm.qml +129 -0
  373. package/ui/TrashDropHandler.qml +11 -0
  374. package/ui/TrashHost.qml +202 -0
  375. package/ui/TrashMonitor.qml +30 -0
  376. package/ui/TrashToolbar.qml +19 -0
  377. package/ui/TrashView.qml +492 -0
  378. package/ui/UnlockDialog.qml +450 -0
  379. package/ui/UnlockHost.qml +206 -0
  380. package/ui/UpdateCheck.qml +97 -0
  381. package/ui/UserDirs.qml +25 -0
  382. package/ui/ViewState.qml +565 -0
  383. package/ui/ViewportScrollBar.qml +195 -0
  384. package/ui/ViewportScrollBars.qml +25 -0
  385. package/ui/WebPreview.qml +33 -0
  386. package/ui/WebPreviewView.qml +26 -0
  387. package/ui/WindowBody.qml +827 -0
  388. package/ui/WindowDrag.qml +35 -0
  389. package/ui/boot/Commons/AudioNodes.qml +50 -0
  390. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  391. package/ui/boot/Commons/Border.qml +243 -0
  392. package/ui/boot/Commons/BorderGeometry.js +373 -0
  393. package/ui/boot/Commons/Color.qml +258 -0
  394. package/ui/boot/Commons/I18n.qml +118 -0
  395. package/ui/boot/Commons/InputDevices.qml +112 -0
  396. package/ui/boot/Commons/InputMethodState.qml +38 -0
  397. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  398. package/ui/boot/Commons/Numbers.js +19 -0
  399. package/ui/boot/Commons/Plural.js +91 -0
  400. package/ui/boot/Commons/ShellIpc.qml +12 -0
  401. package/ui/boot/Commons/Style.qml +561 -0
  402. package/ui/boot/Commons/Translations.qml +100 -0
  403. package/ui/boot/Commons/UntypedInput.qml +152 -0
  404. package/ui/boot/Commons/Util.qml +188 -0
  405. package/ui/boot/Commons/qmldir +13 -0
  406. package/ui/boot/LICENSE-omarchy +20 -0
  407. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  408. package/ui/boot/Ui/BarIconButton.qml +106 -0
  409. package/ui/boot/Ui/BarIconContent.qml +38 -0
  410. package/ui/boot/Ui/BarIndicator.qml +53 -0
  411. package/ui/boot/Ui/BarWidget.qml +50 -0
  412. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  413. package/ui/boot/Ui/BorderSurface.qml +40 -0
  414. package/ui/boot/Ui/Button.qml +224 -0
  415. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  416. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  417. package/ui/boot/Ui/CursorSurface.qml +42 -0
  418. package/ui/boot/Ui/Dropdown.qml +245 -0
  419. package/ui/boot/Ui/FilterInput.qml +26 -0
  420. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  421. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  422. package/ui/boot/Ui/MultiSelect.qml +624 -0
  423. package/ui/boot/Ui/NumberField.qml +86 -0
  424. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  425. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  426. package/ui/boot/Ui/Panel.qml +60 -0
  427. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  428. package/ui/boot/Ui/PanelController.qml +16 -0
  429. package/ui/boot/Ui/PanelHero.qml +112 -0
  430. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  431. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  432. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  433. package/ui/boot/Ui/PanelSlider.qml +156 -0
  434. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  435. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  436. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  437. package/ui/boot/Ui/PopupCard.qml +181 -0
  438. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  439. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  440. package/ui/boot/Ui/SizedImage.qml +125 -0
  441. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  442. package/ui/boot/Ui/TextField.qml +58 -0
  443. package/ui/boot/Ui/Toggle.qml +118 -0
  444. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  445. package/ui/boot/Ui/WidgetButton.qml +172 -0
  446. package/ui/boot/Ui/qmldir +41 -0
  447. package/ui/boot/fleatab.qml +50 -0
  448. package/ui/boot/picker.qml +39 -0
  449. package/ui/boot/shell.qml +122 -0
  450. package/ui/boot/tabtearoff.qml +51 -0
  451. package/ui/js/Anchor.js +379 -0
  452. package/ui/js/AnchorHold.js +162 -0
  453. package/ui/js/Archive.js +157 -0
  454. package/ui/js/ArchiveCard.js +44 -0
  455. package/ui/js/ArchiveOps.js +44 -0
  456. package/ui/js/BackReturn.js +118 -0
  457. package/ui/js/BulkRename.js +100 -0
  458. package/ui/js/Buttons.js +54 -0
  459. package/ui/js/ChromeKeys.js +116 -0
  460. package/ui/js/ClipMarks.js +73 -0
  461. package/ui/js/ClipSelection.js +67 -0
  462. package/ui/js/Clipboard.js +179 -0
  463. package/ui/js/Cloud.js +97 -0
  464. package/ui/js/CodeRuns.js +72 -0
  465. package/ui/js/Collide.js +212 -0
  466. package/ui/js/ColumnFit.js +28 -0
  467. package/ui/js/ColumnMenu.js +111 -0
  468. package/ui/js/Columns.js +298 -0
  469. package/ui/js/ColumnsCarry.js +103 -0
  470. package/ui/js/Compare.js +74 -0
  471. package/ui/js/Contrast.js +157 -0
  472. package/ui/js/ControlReader.js +42 -0
  473. package/ui/js/Convert.js +52 -0
  474. package/ui/js/CopyAs.js +52 -0
  475. package/ui/js/Crumbs.js +178 -0
  476. package/ui/js/Csv.js +231 -0
  477. package/ui/js/Density.js +44 -0
  478. package/ui/js/Devices.js +440 -0
  479. package/ui/js/DirSizes.js +180 -0
  480. package/ui/js/Drag.js +364 -0
  481. package/ui/js/DragOut.js +166 -0
  482. package/ui/js/DropDev.js +52 -0
  483. package/ui/js/Dropbox.js +41 -0
  484. package/ui/js/Eject.js +217 -0
  485. package/ui/js/Errors.js +188 -0
  486. package/ui/js/ExtThumbs.js +115 -0
  487. package/ui/js/Facts.js +402 -0
  488. package/ui/js/FigureDate.js +14 -0
  489. package/ui/js/Filter.js +285 -0
  490. package/ui/js/Find.js +46 -0
  491. package/ui/js/Flash.js +184 -0
  492. package/ui/js/FlashCard.js +79 -0
  493. package/ui/js/FlashEndings.js +125 -0
  494. package/ui/js/FlashPane.js +116 -0
  495. package/ui/js/FleaBin.js +12 -0
  496. package/ui/js/Focus.js +502 -0
  497. package/ui/js/FolderSorts.js +108 -0
  498. package/ui/js/FolderViews.js +44 -0
  499. package/ui/js/Format.js +305 -0
  500. package/ui/js/Fuzzy.js +141 -0
  501. package/ui/js/GlassTag.js +72 -0
  502. package/ui/js/Grid.js +33 -0
  503. package/ui/js/GridGeometry.js +90 -0
  504. package/ui/js/GridNames.js +153 -0
  505. package/ui/js/GvfsBridge.js +200 -0
  506. package/ui/js/HostClipboard.js +29 -0
  507. package/ui/js/Icons.js +218 -0
  508. package/ui/js/Input.js +50 -0
  509. package/ui/js/Jump.js +200 -0
  510. package/ui/js/KeyName.js +64 -0
  511. package/ui/js/Keymap.js +401 -0
  512. package/ui/js/Kinds.js +164 -0
  513. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  514. package/ui/js/LangMarkPaths.js +47 -0
  515. package/ui/js/LangMarks.js +120 -0
  516. package/ui/js/Lists.js +11 -0
  517. package/ui/js/LocalSend.js +39 -0
  518. package/ui/js/LockedMenu.js +63 -0
  519. package/ui/js/MakeDefault.js +186 -0
  520. package/ui/js/MarkBox.js +7 -0
  521. package/ui/js/Markdown.js +119 -0
  522. package/ui/js/MarkdownLists.js +86 -0
  523. package/ui/js/MarkdownMaths.js +58 -0
  524. package/ui/js/MarkdownPictures.js +32 -0
  525. package/ui/js/MarkdownPrepared.js +139 -0
  526. package/ui/js/MarkdownTableFit.js +34 -0
  527. package/ui/js/Marks.js +148 -0
  528. package/ui/js/Marquee.js +96 -0
  529. package/ui/js/Match.js +93 -0
  530. package/ui/js/Material.js +109 -0
  531. package/ui/js/MdBlocks.js +437 -0
  532. package/ui/js/MdBreak.js +71 -0
  533. package/ui/js/MdChunks.js +70 -0
  534. package/ui/js/MdContainer.js +137 -0
  535. package/ui/js/MdDocument.js +337 -0
  536. package/ui/js/MdEmph.js +97 -0
  537. package/ui/js/MdEntity.js +108 -0
  538. package/ui/js/MdEntityTable.js +223 -0
  539. package/ui/js/MdEscape.js +59 -0
  540. package/ui/js/MdFront.js +37 -0
  541. package/ui/js/MdHold.js +32 -0
  542. package/ui/js/MdHtml.js +307 -0
  543. package/ui/js/MdHtmlBlock.js +290 -0
  544. package/ui/js/MdHtmlHeading.js +53 -0
  545. package/ui/js/MdHtmlImage.js +179 -0
  546. package/ui/js/MdInline.js +154 -0
  547. package/ui/js/MdItems.js +212 -0
  548. package/ui/js/MdLeaf.js +309 -0
  549. package/ui/js/MdLink.js +196 -0
  550. package/ui/js/MdMath.js +67 -0
  551. package/ui/js/MdParagraphs.js +138 -0
  552. package/ui/js/MdRefs.js +232 -0
  553. package/ui/js/MdResolve.js +226 -0
  554. package/ui/js/MdRun.js +300 -0
  555. package/ui/js/MdUrl.js +226 -0
  556. package/ui/js/Menu.js +514 -0
  557. package/ui/js/MenuContext.js +85 -0
  558. package/ui/js/MenuFit.js +13 -0
  559. package/ui/js/MenuRefresh.js +74 -0
  560. package/ui/js/MenuWheel.js +35 -0
  561. package/ui/js/Messages.js +186 -0
  562. package/ui/js/Motion.js +16 -0
  563. package/ui/js/Mounts.js +320 -0
  564. package/ui/js/MouseNav.js +13 -0
  565. package/ui/js/Nav.js +329 -0
  566. package/ui/js/NetFs.js +26 -0
  567. package/ui/js/OpenWith.js +83 -0
  568. package/ui/js/Ops.js +585 -0
  569. package/ui/js/Palette.js +33 -0
  570. package/ui/js/PathBar.js +260 -0
  571. package/ui/js/Permissions.js +179 -0
  572. package/ui/js/Phones.js +112 -0
  573. package/ui/js/Picker.js +353 -0
  574. package/ui/js/PlaceMenu.js +96 -0
  575. package/ui/js/Places.js +265 -0
  576. package/ui/js/PreviewAsk.js +21 -0
  577. package/ui/js/PreviewKeys.js +155 -0
  578. package/ui/js/PreviewSettle.js +29 -0
  579. package/ui/js/PreviewSwap.js +101 -0
  580. package/ui/js/PropertyFacts.js +144 -0
  581. package/ui/js/Protocols.js +296 -0
  582. package/ui/js/QuickLookKinds.js +27 -0
  583. package/ui/js/QuickLookRect.js +24 -0
  584. package/ui/js/RailDrop.js +51 -0
  585. package/ui/js/RailKeys.js +87 -0
  586. package/ui/js/RailMenu.js +133 -0
  587. package/ui/js/Recent.js +135 -0
  588. package/ui/js/RecentMode.js +156 -0
  589. package/ui/js/Reload.js +60 -0
  590. package/ui/js/Renderer.js +9 -0
  591. package/ui/js/RunFile.js +143 -0
  592. package/ui/js/Scripts.js +28 -0
  593. package/ui/js/Scroll.js +304 -0
  594. package/ui/js/ScrollOff.js +69 -0
  595. package/ui/js/Search.js +478 -0
  596. package/ui/js/Selection.js +97 -0
  597. package/ui/js/Settings.js +476 -0
  598. package/ui/js/SettingsAbout.js +97 -0
  599. package/ui/js/SettingsShelf.js +52 -0
  600. package/ui/js/SheetKeys.js +42 -0
  601. package/ui/js/SheetQuery.js +281 -0
  602. package/ui/js/ShelfPile.js +27 -0
  603. package/ui/js/SlowClick.js +53 -0
  604. package/ui/js/SlowOp.js +6 -0
  605. package/ui/js/Sort.js +134 -0
  606. package/ui/js/Startup.js +63 -0
  607. package/ui/js/StateWords.js +17 -0
  608. package/ui/js/Status.js +238 -0
  609. package/ui/js/Swap.js +166 -0
  610. package/ui/js/TabMove.js +39 -0
  611. package/ui/js/Tabs.js +736 -0
  612. package/ui/js/Tags.js +134 -0
  613. package/ui/js/Taildrop.js +128 -0
  614. package/ui/js/Tap.js +174 -0
  615. package/ui/js/TextSize.js +107 -0
  616. package/ui/js/Thumbs.js +158 -0
  617. package/ui/js/Transfer.js +172 -0
  618. package/ui/js/Trash.js +81 -0
  619. package/ui/js/TrashDates.js +47 -0
  620. package/ui/js/TrashDrop.js +64 -0
  621. package/ui/js/UiState.js +438 -0
  622. package/ui/js/Unlock.js +152 -0
  623. package/ui/js/Update.js +130 -0
  624. package/ui/js/UserDirs.js +56 -0
  625. package/ui/qmldir +228 -0
package/ui/TabBar.qml ADDED
@@ -0,0 +1,673 @@
1
+ import QtQuick
2
+ import Quickshell
3
+ import Quickshell.Io
4
+ import "." as Flea
5
+ import "js/DragOut.js" as DragOut
6
+ import "js/Tabs.js" as Tabs
7
+ import "js/TabMove.js" as TabMove
8
+
9
+ // The window's tab strip. Hidden with no height until a second tab exists, so the default window
10
+ // keeps the chrome-to-list layout every existing click test and the first-paint path already have.
11
+ Item {
12
+ id: root
13
+
14
+ property var pane: null
15
+ enabled: root.pane !== null && root.pane.enabled
16
+
17
+ // pane.tabs is a replaced JS object, so these bindings have to read it directly; a helper
18
+ // call alone would not re-run when t opens a second tab.
19
+ readonly property var tabs: pane ? pane.tabs : null
20
+ readonly property string path: pane ? pane.path : ""
21
+ readonly property int tabCount: root.tabs && root.tabs.items && root.tabs.items.length > 0 ? root.tabs.items.length : 1
22
+ readonly property int currentIndex: root.tabs ? root.tabs.index : 0
23
+ readonly property bool open: root.tabCount > 1
24
+ // Tab's strip stop: the keyboard is on the tab at focusedIndex, or on New tab one past the last, which draws its own frame; ui/js/ChromeKeys.js moves it.
25
+ property bool stopFocused: false
26
+ property int focusedIndex: 0
27
+ // The keyboard frame lies inside a tab's box, clear of the rule, the current tab's accent edge and the separator.
28
+ readonly property real frameLeft: Theme.chromeControlInset
29
+ readonly property real frameBottom: Theme.accentEdge + Theme.chromeControlInset
30
+ readonly property int tabWidth: {
31
+ var n = Math.max(1, root.tabCount)
32
+ var avail = Math.max(0, root.width - Theme.hitMin)
33
+ var maxW = Math.round(Theme.font.caption * 12) + Theme.hitMin + 2 * Theme.spacing.rowPaddingX
34
+ var minW = Theme.hitMin * 3
35
+ return Math.round(Math.max(minW, Math.min(maxW, avail / n)))
36
+ }
37
+
38
+ // How long a drag rests on a tab before the tab is selected: long enough to cross it on the way elsewhere.
39
+ readonly property int hoverSwitchMs: 400
40
+
41
+ // Tabs040 callout 1: a tab drag reorders the strip. dragFrom is the tab
42
+ // held, dropAt the insertion point (0..tabCount) its pointer names.
43
+ property int dragFrom: -1
44
+ property int dropAt: -1
45
+ readonly property int dragTo: root.dropAt > root.dragFrom ? root.dropAt - 1 : root.dropAt
46
+ readonly property var layoutOrder: {
47
+ var order = []
48
+ for (var i = 0; i < root.tabCount; i++) order.push(i)
49
+ if (root.dragFrom >= 0 && root.dropAt >= 0)
50
+ TabMove.reorder(order, root.dragFrom, root.dragTo, root.currentIndex)
51
+ return order
52
+ }
53
+
54
+ // Gesture state is separate from outstanding lifts awaiting their own ack.
55
+ property var outMime: ({})
56
+ property bool outActive: false
57
+ property bool outOutside: false
58
+ property bool outRefused: false
59
+ property int outIndex: -1
60
+ property string outPath: ""
61
+ property string outPid: ""
62
+ property string outToken: ""
63
+ property bool ownAccepted: false
64
+ property var pendingTab: null
65
+ readonly property int tabDropPeekFirst: 2
66
+ // A receiver cannot wait longer than the source keeps its unacknowledged lift.
67
+ readonly property int tabDropWaitMs: Tabs.ACK_WAIT_MS
68
+ onPaneChanged: root.refuseTabDrop()
69
+ // The lift outlives the drag's own end, until the taken ack or Tabs.ACK_WAIT_MS ends.
70
+ property double ackLiftedAt: 0
71
+ property var takenQueue: []
72
+ property var outstandingLifts: []
73
+ readonly property int ackPollMs: 100 // Settle acknowledged lifts when their captured pane finishes loading.
74
+ // Stage trace, on only with FLEA_TRACE_TABDRAG=1; read once, silent otherwise.
75
+ readonly property bool tabTrace: Quickshell.env("FLEA_TRACE_TABDRAG") === "1"
76
+ function traceTab(stage, detail) { if (root.tabTrace) console.log("TABDRAG " + stage + " pid=" + Quickshell.processId + " " + detail) }
77
+
78
+ Timer {
79
+ id: ackTimer
80
+ interval: root.ackPollMs
81
+ repeat: true
82
+ running: root.outstandingLifts.length > 0
83
+ onTriggered: root.drainLifts()
84
+ }
85
+
86
+ Timer {
87
+ id: tabDropDeadline
88
+ interval: root.tabDropWaitMs
89
+ onTriggered: root.refuseTabDrop()
90
+ }
91
+
92
+ Process {
93
+ id: takenAck
94
+ onExited: function (exitCode, exitStatus) {
95
+ root.traceTab("taken-exit", "code=" + exitCode)
96
+ root.pumpTaken()
97
+ }
98
+ }
99
+
100
+ // QTBUG-64128: a DropArea rejects a drag sourced from its ancestor.
101
+ Item { id: dragOrigin; width: 0; height: 0; visible: false }
102
+ Drag.source: dragOrigin
103
+ Flea.TabDragGeometry { id: sourceGeometry }
104
+
105
+ Drag.dragType: Drag.Automatic
106
+ // Private tab MIME must never offer the folder as a file drag.
107
+ Drag.supportedActions: Qt.MoveAction
108
+ Drag.mimeData: root.outMime
109
+ Drag.onDragFinished: function (dropAction) { root.outFinished(dropAction) }
110
+
111
+ function dragStarted(index) {
112
+ root.dragFrom = index
113
+ root.dropAt = index < 0 ? -1 : index + 1
114
+ }
115
+ function dragMoved(x) {
116
+ if (root.dragFrom < 0)
117
+ return
118
+ root.dropAt = TabMove.insertionAt(x, root.tabWidth, root.tabCount)
119
+ }
120
+ function dragFinished() {
121
+ if (root.dragFrom >= 0 && root.dropAt >= 0 && root.pane) {
122
+ Tabs.move(root.pane, root.dragFrom, root.dragTo)
123
+ }
124
+ root.dragFrom = -1
125
+ root.dropAt = -1
126
+ root.drainLifts()
127
+ }
128
+
129
+ // A lift captures the tab payload before the pane can change.
130
+ function tabLiftBegan(index) {
131
+ if (!Tabs.canLift(root.pane)) {
132
+ root.dragFrom = -1
133
+ root.dropAt = -1
134
+ return
135
+ }
136
+ root.dragStarted(index)
137
+ root.outIndex = index
138
+ var info = Tabs.tabInfo(root.pane, index)
139
+ root.outPath = info ? info.path : ""
140
+ root.outPid = String(Quickshell.processId)
141
+ root.outToken = Tabs.newToken()
142
+ var band = strip.mapToItem(null, 0, 0)
143
+ sourceGeometry.begin(root.outToken, { x: band.x, y: band.y,
144
+ width: root.width - strip.x, height: root.height })
145
+ root.ackLiftedAt = 0
146
+ var lift = Tabs.captureLift(root.pane, index, root.outToken, root.ackLiftedAt)
147
+ if (lift) root.outstandingLifts = root.outstandingLifts.concat([lift])
148
+ root.outMime = Tabs.tabDragMime(root.pane, index, root.outPid, root.outToken)
149
+ root.outOutside = false
150
+ root.outRefused = false
151
+ root.ownAccepted = false
152
+ }
153
+
154
+ // A platform drag freezes the strip reorder until the drop answers.
155
+ function tabLiftMoved(stripX, winX, winY) {
156
+ root.dragMoved(stripX)
157
+ if (root.parent)
158
+ root.outOutside = winX < 0 || winY < 0 || winX > root.parent.width || winY > root.parent.height
159
+ if (root.outOutside && !root.outActive && root.outMime[Tabs.TAB_MIME]) {
160
+ var refusal = Tabs.tearRefusal(root.pane)
161
+ if (refusal.length > 0) {
162
+ if (!root.outRefused && root.pane)
163
+ root.pane.message(refusal, false)
164
+ root.outRefused = true
165
+ return
166
+ }
167
+ root.outActive = true
168
+ root.Drag.active = true
169
+ root.traceTab("drag-start", "index=" + root.outIndex + " path=" + root.outPath + " mime=" + Object.keys(root.outMime).join(",") + " dragType=" + root.Drag.dragType)
170
+ }
171
+ }
172
+
173
+ // A platform gesture owns its release, so the handler must not reorder again.
174
+ function tabLiftEnded() {
175
+ if (root.outActive || root.ownAccepted) {
176
+ root.dragFrom = -1
177
+ root.dropAt = -1
178
+ return
179
+ }
180
+ root.dragFinished()
181
+ if (root.ackLiftedAt === 0) root.clearAck()
182
+ }
183
+
184
+ // The lift must survive the drag ending until its acknowledgment arrives.
185
+ function holdAck() {
186
+ var lift = Tabs.liftFor(root.outstandingLifts, root.outToken)
187
+ root.ackLiftedAt = Date.now()
188
+ if (lift) lift.liftedAt = root.ackLiftedAt
189
+ }
190
+ function clearAck() {
191
+ root.outstandingLifts = root.outstandingLifts.filter(function (lift) { return lift.token !== root.outToken })
192
+ root.outToken = ""
193
+ root.outIndex = -1
194
+ root.outPath = ""
195
+ root.ackLiftedAt = 0
196
+ }
197
+
198
+ function drainLifts() {
199
+ if (root.dragFrom >= 0) return
200
+ var now = Date.now()
201
+ var kept = []
202
+ for (var i = 0; i < root.outstandingLifts.length; i++) {
203
+ var lift = root.outstandingLifts[i]
204
+ if (!lift.taken && !Tabs.ackCloses(lift.token, lift.token, lift.liftedAt, now)) {
205
+ if (root.outToken === lift.token) root.outToken = ""
206
+ continue
207
+ }
208
+ var index = Tabs.resolveMovedTab(lift.pane, lift.identity)
209
+ if (index < 0) continue
210
+ if (lift.taken && !lift.pane.listInFlight) {
211
+ Tabs.closeTabAfterMove(lift.pane, index)
212
+ if (root.outToken === lift.token) root.outToken = ""
213
+ } else kept.push(lift)
214
+ }
215
+ root.outstandingLifts = kept
216
+ }
217
+
218
+ // A cross-process drop closes the source tab only after a matching acknowledgment.
219
+ function outFinished(dropAction) {
220
+ root.traceTab("drag-finished", "action=" + dropAction)
221
+ var consumed = root.ownAccepted
222
+ root.dragFrom = -1
223
+ root.dropAt = -1
224
+ root.outActive = false
225
+ root.ownAccepted = false
226
+ if (consumed)
227
+ root.clearAck()
228
+ else if (root.outToken.length > 0)
229
+ root.holdAck()
230
+ }
231
+
232
+ // Qt's platform Escape filter and explicit cancellation share the same ending.
233
+ function cancelOut() {
234
+ if (!root.outActive)
235
+ return
236
+ root.Drag.cancel()
237
+ if (root.outActive)
238
+ root.outFinished(Qt.IgnoreAction)
239
+ }
240
+
241
+ function returnAt(at) {
242
+ var lift = Tabs.liftFor(root.outstandingLifts, root.outToken)
243
+ var from = lift ? Tabs.resolveMovedTab(lift.pane, lift.identity) : -1
244
+ if (from >= 0)
245
+ Tabs.move(lift.pane, from, at > from ? at - 1 : at)
246
+ root.ownAccepted = true
247
+ }
248
+
249
+ function catcherDrop(x, y) {
250
+ if (!root.outActive)
251
+ return
252
+ var result = Tabs.catcherOutcome(sourceGeometry.rect, sourceGeometry.strip,
253
+ x, y, root.tabWidth, root.tabCount)
254
+ root.traceTab("catcher-drop", "outcome=" + result.outcome + " global=" + x + "," + y
255
+ + (sourceGeometry.rect ? "" : " reason=unknown-source-rectangle"))
256
+ if (result.outcome === "tearoff")
257
+ root.tearOffAt()
258
+ else if (result.outcome === "return")
259
+ root.returnAt(result.at)
260
+ }
261
+
262
+ // The new window acknowledges only after listing; a failed spawn keeps the source tab.
263
+ function tearOffAt() {
264
+ if (root.outPath.length === 0)
265
+ return
266
+ var info = Tabs.parseTabMime(root.outMime[Tabs.TAB_MIME])
267
+ Quickshell.execDetached(["env", "FLEA_TAB_SOURCE_PID=" + root.outPid,
268
+ "FLEA_TAB_CURSOR=" + (info ? info.cursor : ""),
269
+ "FLEA_TAB_TOKEN=" + root.outToken, Quickshell.env("FLEA_BIN") || "flea", root.outPath])
270
+ root.outFinished(Qt.IgnoreAction)
271
+ }
272
+
273
+ // An own-window tab drag reorders its strip instead of opening another tab.
274
+ function tabEnterOk(drag) {
275
+ var ok = Tabs.enterAccepts(drag.formats, drag.getDataAsString(Tabs.TAB_MIME), undefined, Tabs.canReceive(root.pane), root.outActive)
276
+ root.traceTab("enter-strip", "formats=" + String(drag.formats) + " ok=" + ok)
277
+ return ok
278
+ }
279
+
280
+ // A foreign tab opens only after its reserved peek confirms a directory.
281
+ function acceptTabDrop(payload, info, at) {
282
+ if (root.pendingTab || !root.pane || !Tabs.canReceive(root.pane)) {
283
+ root.traceTab("drop-skip", "reason=strip-accept-refused")
284
+ return
285
+ }
286
+ root.pendingTab = { payload: payload, pid: info.pid, token: info.token, path: info.path, at: at, pane: root.pane }
287
+ tabDropDeadline.restart()
288
+ root.traceTab("peek-sent", "path=" + info.path + " first=" + root.tabDropPeekFirst + " hidden=false")
289
+ root.pane.backend.peek(info.path, root.tabDropPeekFirst, false, false)
290
+ }
291
+
292
+ function refuseTabDrop() {
293
+ var pending = root.pendingTab
294
+ if (!pending)
295
+ return
296
+ root.pendingTab = null
297
+ tabDropDeadline.stop()
298
+ var pane = root.pane || pending.pane
299
+ if (pane)
300
+ pane.message("That tab could not be received.", false)
301
+ root.traceTab("receive", "ok=false refused-pending path=" + pending.path)
302
+ }
303
+
304
+ // Send the taken acknowledgment only after the receiving tab opens.
305
+ function sendTaken(pid, token) {
306
+ root.traceTab("taken-sent", "target=" + pid + " token=" + token)
307
+ root.takenQueue = root.takenQueue.concat([{ pid: String(pid), token: String(token) }])
308
+ root.pumpTaken()
309
+ }
310
+ // One Process runs one call, so overlapping acks queue behind it instead of vanishing inside execDetached.
311
+ function pumpTaken() {
312
+ if (takenAck.running || root.takenQueue.length === 0)
313
+ return
314
+ var next = root.takenQueue[0]
315
+ root.takenQueue = root.takenQueue.slice(1)
316
+ takenAck.command = ["qs", "ipc", "--pid", next.pid, "call", "fleatab", "taken", next.token]
317
+ takenAck.running = true
318
+ }
319
+
320
+ Connections {
321
+ target: root.pane ? root.pane.backend : null
322
+ function onPeeked(path, hidden, total, rows, readFailed, mode, hiddenLast, first) {
323
+ var pending = root.pendingTab
324
+ if (!pending || path !== pending.path || hidden !== false || hiddenLast !== false || first !== root.tabDropPeekFirst)
325
+ return
326
+ root.traceTab("peek-answer", "path=" + path + " failed=" + readFailed + " total=" + total)
327
+ root.pendingTab = null
328
+ tabDropDeadline.stop()
329
+ if (readFailed) {
330
+ if (root.pane)
331
+ root.pane.message("That folder is no longer there.", false)
332
+ root.traceTab("receive", "ok=false refused-missing path=" + path)
333
+ return
334
+ }
335
+ var received = root.pane && Tabs.receiveTab(root.pane, pending.payload, pending.at)
336
+ root.traceTab("receive", "ok=" + !!received + " path=" + pending.path + " at=" + pending.at)
337
+ if (received)
338
+ root.sendTaken(pending.pid, pending.token)
339
+ }
340
+ }
341
+
342
+ visible: root.open
343
+ implicitHeight: Theme.chromeHeight
344
+ height: visible ? implicitHeight : 0
345
+
346
+ function itemAt(index) {
347
+ return repeater.itemAt(index)
348
+ }
349
+ // The strip's keyboard frames, so tests/keyreach.qml measures them in their host.
350
+ readonly property Item newTabFrame: addFrame
351
+
352
+ // On glass the strip's ground runs left and right of the current tab's plate, which a dragged tab gives up; see ui/Glass.qml.
353
+ readonly property Item plate: Glass.on && root.dragFrom !== root.currentIndex && repeater.count > root.currentIndex ? root.itemAt(root.currentIndex) : null
354
+ Rectangle {
355
+ width: root.plate ? strip.x + root.plate.x : parent.width
356
+ height: parent.height
357
+ color: Glass.surface
358
+ }
359
+ Rectangle {
360
+ visible: root.plate !== null
361
+ x: root.plate ? strip.x + root.plate.x + root.plate.width : 0
362
+ width: parent.width - x
363
+ height: parent.height
364
+ color: Glass.surface
365
+ }
366
+
367
+ Rectangle {
368
+ anchors.bottom: parent.bottom
369
+ anchors.left: parent.left
370
+ anchors.right: parent.right
371
+ height: Theme.spacing.hairline
372
+ color: Theme.color.foreground
373
+ opacity: 0.12
374
+ }
375
+
376
+ Item {
377
+ id: strip
378
+ anchors.left: parent.left
379
+ anchors.leftMargin: Theme.spacing.rowPaddingX
380
+ anchors.top: parent.top
381
+ anchors.bottom: parent.bottom
382
+ width: root.tabCount * root.tabWidth + Theme.hitMin
383
+
384
+ Repeater {
385
+ id: repeater
386
+ model: root.open ? root.tabCount : 0
387
+ delegate: Item {
388
+ id: tab
389
+ required property int index
390
+ readonly property int slot: root.layoutOrder.indexOf(tab.index)
391
+ x: tab.slot * root.tabWidth
392
+ width: root.tabWidth
393
+ height: strip.height
394
+ // The tab under the pointer draws ghosted while its drag runs.
395
+ opacity: root.dragFrom === tab.index ? Theme.disabledOpacity : 1
396
+
397
+ readonly property bool current: root.currentIndex === tab.index
398
+ readonly property Item focusFrame: tabFrame
399
+ // Every input named, so the label re-reads when a tab opens or the pane navigates.
400
+ readonly property string title: Tabs.label(
401
+ Tabs.pathAt(root.tabs, root.currentIndex, tab.index, root.path),
402
+ pane ? pane.home : "")
403
+
404
+ Accessible.role: Accessible.PageTab
405
+ Accessible.name: tab.title
406
+ Accessible.onPressAction: if (pane) Tabs.selectAt(pane, tab.index)
407
+
408
+ HoverHandler { cursorShape: Qt.PointingHandCursor }
409
+
410
+ // The rule: a drag resting on a tab selects it so the drop can land in that tab's
411
+ // listing, and a drop on the tab itself lands there too, by path, see ui/DropInto.qml.
412
+ Flea.DropInto {
413
+ anchors.fill: parent
414
+ pane: root.pane
415
+ enabled: DragOut.searchTabEnabled(root.pane ? root.pane.searchMode : "", tab.current)
416
+ switchesOnHover: true
417
+ refuseLoading: DragOut.refuseLoading(root.pane && root.pane.listInFlight, true, tab.current)
418
+ // The pane's drop path, not its drawn one: a tab selected by the hover switch is
419
+ // current before its listing lands, and until then pane.path is the tab left behind.
420
+ // Sidebar040: a history is not a directory, so a drop onto the tab standing on it
421
+ // is refused rather than landing in the root it stands on.
422
+ dest: root.pane && root.pane.recentMode.length > 0 && tab.current ? ""
423
+ : Tabs.pathAt(root.tabs, root.currentIndex, tab.index,
424
+ root.pane ? root.pane.dropPath : root.path)
425
+ // Unknown while the listed reply is still out, because dirDev is then the directory a hover switch just left; unknown makes verbFor copy, never a move that turns into a cross-device delete.
426
+ destDev: Tabs.devAt(root.tabs, root.currentIndex, tab.index,
427
+ root.pane && root.pane.backend && !root.pane.listInFlight ? root.pane.backend.dirDev : 0)
428
+ // Only an accepted enter arms the switch: Qt emits entered before it reads accepted,
429
+ // and a refused drag gets no exited, so the timer would otherwise never stop.
430
+ onEntered: function (drag) { if (drag.accepted) hoverSwitch.restart() }
431
+ onExited: hoverSwitch.stop()
432
+ onDropped: hoverSwitch.stop()
433
+ }
434
+ Timer {
435
+ id: hoverSwitch
436
+ interval: root.hoverSwitchMs
437
+ onTriggered: if (root.pane && !tab.current) Tabs.selectAt(root.pane, tab.index)
438
+ }
439
+
440
+ Rectangle {
441
+ anchors.fill: parent
442
+ color: tab.current && (!Glass.on || root.plate === tab) ? Glass.background : "transparent"
443
+ }
444
+
445
+ // Flush on the strip's own bottom edge, replacing it rather than sitting inside the
446
+ // plate: rendered in Quickshell on a low-chroma theme, an inset edge vanishes.
447
+ Rectangle {
448
+ anchors.bottom: parent.bottom
449
+ anchors.left: parent.left
450
+ anchors.right: parent.right
451
+ height: Theme.accentEdge
452
+ color: Theme.color.accent
453
+ visible: tab.current
454
+ }
455
+
456
+ Rectangle {
457
+ anchors.right: parent.right
458
+ anchors.verticalCenter: parent.verticalCenter
459
+ width: Theme.spacing.hairline
460
+ height: parent.height * 0.45
461
+ color: Theme.color.foreground
462
+ opacity: 0.12
463
+ visible: tab.slot < root.tabCount - 1 && !tab.current
464
+ }
465
+
466
+ Rectangle {
467
+ id: tabFrame
468
+ x: root.frameLeft
469
+ y: root.frameLeft
470
+ width: parent.width - 2 * root.frameLeft
471
+ height: parent.height - root.frameLeft - root.frameBottom
472
+ color: "transparent"
473
+ border.width: Theme.spacing.hairline
474
+ border.color: Theme.color.foreground
475
+ visible: root.stopFocused && root.focusedIndex === tab.index
476
+ }
477
+
478
+ Text {
479
+ id: titleText
480
+ anchors.left: parent.left
481
+ anchors.leftMargin: Theme.spacing.rowPaddingX
482
+ anchors.right: closeHit.left
483
+ anchors.verticalCenter: parent.verticalCenter
484
+ text: tab.title
485
+ color: tab.current ? Theme.color.foreground : Theme.color.muted
486
+ font.family: Theme.font.family
487
+ font.pixelSize: Theme.font.caption
488
+ textFormat: Text.PlainText
489
+ elide: Text.ElideRight
490
+ }
491
+
492
+ Item {
493
+ id: closeHit
494
+ anchors.right: parent.right
495
+ anchors.verticalCenter: parent.verticalCenter
496
+ width: Theme.hitMin
497
+ height: parent.height
498
+
499
+ // The mark never moves and its target never shrinks; only the ink answers, so a
500
+ // crowded strip is no harder to hit than a tidy one.
501
+ Flea.Glyph {
502
+ anchors.centerIn: parent
503
+ width: Theme.chromeMarkSize
504
+ height: Theme.chromeMarkSize
505
+ name: "x"
506
+ color: tab.current || closeHover.hovered ? Theme.color.foreground : Theme.color.mutedFrame
507
+ }
508
+
509
+ HoverHandler { id: closeHover }
510
+ }
511
+
512
+ TapHandler {
513
+ acceptedButtons: Qt.LeftButton | Qt.MiddleButton
514
+ onTapped: function (eventPoint, button) {
515
+ if (!pane)
516
+ return
517
+ if (button === Qt.MiddleButton) {
518
+ Tabs.closeAt(pane, tab.index)
519
+ return
520
+ }
521
+ var local = closeHit.mapFromItem(tab, eventPoint.position.x, eventPoint.position.y)
522
+ if (local.x >= 0 && local.x <= closeHit.width && local.y >= 0 && local.y <= closeHit.height)
523
+ Tabs.closeAt(pane, tab.index)
524
+ else
525
+ Tabs.selectAt(pane, tab.index)
526
+ }
527
+ }
528
+
529
+ // A left-button drag reorders; a press without a move still taps above.
530
+ // A file drag never enters here, so the hover switch answers only files.
531
+ DragHandler {
532
+ acceptedButtons: Qt.LeftButton
533
+ target: null
534
+ function updateDrop() {
535
+ // Scene coordinates stay fixed while the preview moves the delegate beneath the pointer.
536
+ var pos = strip.mapFromItem(null, centroid.scenePosition.x, centroid.scenePosition.y)
537
+ var win = root.parent ? root.parent.mapFromItem(null, centroid.scenePosition.x, centroid.scenePosition.y) : pos
538
+ root.tabLiftMoved(pos.x, win.x, win.y)
539
+ }
540
+ onActiveChanged: {
541
+ // The threshold move precedes activation, so its centroid must be sampled here.
542
+ if (active) {
543
+ root.tabLiftBegan(tab.index)
544
+ updateDrop()
545
+ } else if (root.outActive || root.ownAccepted) {
546
+ // A platform drag owns its release, so no grab release reaches this handler to end the lift.
547
+ root.tabLiftEnded()
548
+ }
549
+ }
550
+ onCentroidChanged: if (active) updateDrop()
551
+ onGrabChanged: function (transition, point) {
552
+ if (transition !== PointerDevice.UngrabExclusive) return
553
+ // Qt deactivates before updating the centroid on release; the event point holds the release position.
554
+ var pos = strip.mapFromItem(null, point.scenePosition.x, point.scenePosition.y)
555
+ root.dragMoved(pos.x)
556
+ root.tabLiftEnded()
557
+ }
558
+ onCanceled: {
559
+ root.dragFrom = -1
560
+ root.dropAt = -1
561
+ if (!root.outActive && root.ackLiftedAt === 0) root.clearAck()
562
+ }
563
+ }
564
+ }
565
+ }
566
+
567
+ Item {
568
+ id: addButton
569
+ x: root.tabCount * root.tabWidth
570
+ width: Theme.hitMin
571
+ height: strip.height
572
+ Accessible.role: Accessible.Button
573
+ Accessible.name: "New tab"
574
+ Accessible.onPressAction: if (pane) Tabs.openNew(pane)
575
+ HoverHandler { cursorShape: Qt.PointingHandCursor }
576
+
577
+ Rectangle {
578
+ id: addFrame
579
+ x: root.frameLeft
580
+ y: root.frameLeft
581
+ width: parent.width - 2 * root.frameLeft
582
+ height: parent.height - root.frameLeft - root.frameBottom
583
+ color: "transparent"
584
+ border.width: Theme.spacing.hairline
585
+ border.color: Theme.color.foreground
586
+ visible: root.stopFocused && root.focusedIndex === root.tabCount
587
+ }
588
+
589
+ Flea.Glyph {
590
+ anchors.centerIn: parent
591
+ width: Theme.chromeMarkSize
592
+ height: Theme.chromeMarkSize
593
+ name: "plus"
594
+ color: root.stopFocused && root.focusedIndex === root.tabCount ? Theme.color.foreground : Theme.color.mutedFrame
595
+ }
596
+
597
+ TapHandler {
598
+ acceptedButtons: Qt.LeftButton
599
+ onTapped: if (pane) Tabs.openNew(pane)
600
+ }
601
+ }
602
+ }
603
+
604
+ // The strip catcher accepts tab drags without intercepting pointer input.
605
+ DropArea {
606
+ anchors.left: parent.left
607
+ anchors.leftMargin: Theme.spacing.rowPaddingX
608
+ anchors.top: parent.top
609
+ anchors.bottom: parent.bottom
610
+ anchors.right: parent.right
611
+ keys: [Tabs.TAB_MIME]
612
+ onEntered: function (drag) {
613
+ if (!root.tabEnterOk(drag))
614
+ drag.accepted = false
615
+ }
616
+ onExited: root.traceTab("leave-strip", "")
617
+ // An own drag out and back tracks its insertion while it is over the strip.
618
+ onPositionChanged: function (drag) {
619
+ var info = Tabs.parseTabMime(drag.getDataAsString(Tabs.TAB_MIME))
620
+ if (info && Tabs.isOwnTab(info) && root.outActive)
621
+ root.dragMoved(drag.x)
622
+ }
623
+ onDropped: function (drop) {
624
+ var payload = drop.getDataAsString(Tabs.TAB_MIME)
625
+ root.traceTab("drop-strip", "empty=" + (payload.length === 0) + " len=" + payload.length)
626
+ var info = Tabs.parseTabMime(payload)
627
+ if (!info) {
628
+ root.traceTab("drop-skip", "reason=strip-bad-payload")
629
+ return
630
+ }
631
+ if (Tabs.isOwnTab(info)) {
632
+ // An own-strip return reorders once before the late release clears the gesture.
633
+ if (!root.outActive) {
634
+ root.traceTab("drop-skip", "reason=strip-own-inactive")
635
+ return
636
+ }
637
+ root.returnAt(Tabs.dropIndexAt(drop.x, root.tabWidth, root.tabCount))
638
+ drop.accept(Qt.MoveAction)
639
+ return
640
+ }
641
+ // The take decision answers Move at once; the peek behind it may still refuse, and then no ack goes out.
642
+ if (Tabs.dropDecision(info, undefined, root.outActive, Tabs.canReceive(root.pane)) !== Tabs.DROP_TAKE) {
643
+ root.traceTab("drop-skip", "reason=strip-decision-ignore")
644
+ return
645
+ }
646
+ drop.accept(Qt.MoveAction)
647
+ root.acceptTabDrop(payload, info, Tabs.dropIndexAt(drop.x, root.tabWidth, root.tabCount))
648
+ }
649
+ }
650
+
651
+ // The catcher stays loaded only while an outgoing tab drag is active.
652
+ Loader {
653
+ id: tearPanels
654
+ active: root.outActive
655
+ source: "file://" + Quickshell.shellDir + "/tabtearoff.qml"
656
+ onLoaded: {
657
+ item.tabBar = root
658
+ item.tabMime = Tabs.TAB_MIME
659
+ }
660
+ }
661
+
662
+ Component.onCompleted: Tabs.setOwnPid(Quickshell.processId)
663
+
664
+ // Tabs040 callout 1: the accent bar borders the ghost's leading edge through the strip's height.
665
+ Rectangle {
666
+ visible: root.dragFrom >= 0 && root.dropAt >= 0
667
+ x: strip.x + root.dragTo * root.tabWidth - Theme.spacing.hairline
668
+ width: 2 * Theme.spacing.hairline
669
+ anchors.top: parent.top
670
+ anchors.bottom: parent.bottom
671
+ color: Theme.color.accent
672
+ }
673
+ }