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/js/Tabs.js ADDED
@@ -0,0 +1,736 @@
1
+ .pragma library
2
+
3
+ .import "Filter.js" as Filter
4
+ .import "Format.js" as Format
5
+ .import "Nav.js" as Nav
6
+ .import "RecentMode.js" as RecentMode
7
+ .import "Search.js" as Search
8
+ .import "Sort.js" as Sort
9
+ .import "Startup.js" as Startup
10
+ .import "TabMove.js" as TabMove
11
+
12
+ // The sentence a digit key answers with when no tab sits there, filled by Format.fill.
13
+ var WORDS = {
14
+ noTab: "No tab %1",
15
+ lastTab: "The last tab cannot be closed"
16
+ }
17
+
18
+ // Hidden tabs are snapshots, so the pane and backend still own only one listing.
19
+ // The nine-tab cap matches TUI's direct digit selection; GUI shortcuts cycle through the same state.
20
+
21
+ var MAX = 9
22
+
23
+ // Where a snapshotted tab reopens: the folder a search or a history was opened over, else the path.
24
+ function restingPath(pane) {
25
+ if (pane.searchMode === "results" && (pane.searchFrom || "").length > 0)
26
+ return pane.searchFrom
27
+ return RecentMode.restingPath(pane) || Nav.place(pane)
28
+ }
29
+
30
+ var nextTabIdentity = 0
31
+
32
+ // Only the held cursor row can name a file; a search's row never belongs to its resting folder.
33
+ function cursorFile(pane) {
34
+ var row = pane.rowFor && pane.cursorIndex >= 0 ? pane.rowFor(pane.cursorIndex) : null
35
+ return row && typeof row.n === "string" ? row.n : ""
36
+ }
37
+
38
+ function snapshot(pane, path, identity, dropped) {
39
+ var where = path === undefined ? restingPath(pane) : path
40
+ // A cursor and a selection read off a search's own listing name nothing in the directory the
41
+ // tab records, so a tab stepping back out of a search starts at its first row with none.
42
+ var elsewhere = where !== pane.path || Search.live(pane) || dropped === true
43
+ return {
44
+ tabIdentity: identity || ++nextTabIdentity,
45
+ path: where,
46
+ history: pane.history.slice(),
47
+ forwardHistory: (pane.forwardHistory || []).slice(),
48
+ cursorIndex: elsewhere ? 0 : pane.cursorIndex,
49
+ cursorName: elsewhere ? "" : cursorFile(pane),
50
+ viewMode: pane.viewMode,
51
+ showHidden: pane.showHidden,
52
+ selected: elsewhere ? [] : pane.selectedIndices().slice(),
53
+ // A lone row restores with only(), so a tab switch never re-arms the row a tap left behind.
54
+ follows: elsewhere ? false : pane.selection.follows(),
55
+ sortBy: pane.backend.sortBy,
56
+ sortDesc: pane.backend.sortDesc,
57
+ // Issue 94, nixfred: the counter every list bumps, so a selection only returns to its own rows.
58
+ listRequests: pane.backend.listRequests,
59
+ // The directory's filesystem, so a drop on this tab while another shows decides move against copy.
60
+ dev: elsewhere ? 0 : pane.backend.dirDev
61
+ }
62
+ }
63
+
64
+ // keep is the tabs object whose landing the current tab still owes, for a pack that leaves the current tab's listing out.
65
+ function pack(items, index, keep) {
66
+ var tabs = {
67
+ items: items,
68
+ index: index,
69
+ pendingCursor: -1,
70
+ pendingCursorName: null,
71
+ pendingLocatePath: "",
72
+ pendingSortBy: "",
73
+ pendingSortDesc: false
74
+ }
75
+ if (keep) {
76
+ tabs.pendingCursor = keep.pendingCursor
77
+ tabs.pendingCursorName = keep.pendingCursorName
78
+ tabs.pendingLocatePath = keep.pendingLocatePath
79
+ tabs.pendingSortBy = keep.pendingSortBy
80
+ tabs.pendingSortDesc = keep.pendingSortDesc
81
+ }
82
+ return tabs
83
+ }
84
+
85
+ function label(path, home) {
86
+ if (!path || path === "/")
87
+ return "/"
88
+ if (home && path === home)
89
+ return "Home"
90
+ var leaf = Format.leafPart(Format.tilde(path, home || ""))
91
+ return leaf.length > 0 ? leaf : path
92
+ }
93
+
94
+ // The current tab's path lives on the pane, not in the snapshot, so a navigate does not wait for a switch.
95
+ function labelAt(pane, i) {
96
+ return label(pathAt(pane.tabs, currentIndex(pane), i, pane.path), pane.home)
97
+ }
98
+
99
+ // The path tab i draws: the live pane path for the current tab, the snapshot's for a hidden one.
100
+ // Takes the values rather than the pane so ui/TabBar.qml's binding can read each one by name.
101
+ function pathAt(tabs, index, i, currentPath) {
102
+ if (i === index)
103
+ return currentPath
104
+ return tabs && tabs.items && tabs.items[i] ? tabs.items[i].path : ""
105
+ }
106
+
107
+ // The tab's filesystem the same way, 0 when unknown, which ui/js/Drag.js verbFor reads as copy.
108
+ function devAt(tabs, index, i, currentDev) {
109
+ if (i === index)
110
+ return currentDev
111
+ return tabs && tabs.items && tabs.items[i] ? Number(tabs.items[i].dev) || 0 : 0
112
+ }
113
+
114
+ function labels(pane) {
115
+ var n = count(pane)
116
+ var out = []
117
+ for (var i = 0; i < n; i++)
118
+ out.push(labelAt(pane, i))
119
+ return out
120
+ }
121
+
122
+ function count(pane) {
123
+ return pane.tabs && pane.tabs.items && pane.tabs.items.length > 0 ? pane.tabs.items.length : 1
124
+ }
125
+
126
+ function currentIndex(pane) {
127
+ return pane.tabs ? pane.tabs.index : 0
128
+ }
129
+
130
+ // Search and Recent replace the listing; Trash only covers it, so closing Trash alone needs no re-list.
131
+ function dropOverlay(pane) {
132
+ var dropped = Search.live(pane) || Search.owesRelist(pane) || RecentMode.dropOverlay(pane)
133
+ if (pane.trash) pane.trash.close()
134
+ Search.leaveWalk(pane)
135
+ Filter.close(pane)
136
+ return dropped
137
+ }
138
+
139
+ function closePreview(pane) {
140
+ if (pane.preview && pane.preview.active)
141
+ pane.preview.close()
142
+ }
143
+
144
+ // The mark names a standing notice's serial, never its sentence; a count change withdraws it.
145
+ function sayCount(pane, text) {
146
+ pane.message(text, false)
147
+ pane.tabCountNotice = pane.statusBar ? pane.statusBar.noticeSerial : -1
148
+ }
149
+ // Any other verdict from here replaces a standing count one, so no later change withdraws it.
150
+ function sayPlain(pane, text) {
151
+ pane.tabCountNotice = -1
152
+ pane.message(text, false)
153
+ }
154
+ // The count changed under a standing count verdict, so it is withdrawn; anything else stands.
155
+ function dropCount(pane) {
156
+ if (pane.tabCountNotice !== -1 && pane.statusBar) pane.statusBar.withdraw(pane.tabCountNotice)
157
+ pane.tabCountNotice = -1
158
+ }
159
+
160
+ // Only ever called for a switch that re-listed nothing; the clamp is the belt on top of that, since
161
+ // an index past the end would select a row that is not there at all.
162
+ function restoreSelection(pane, selected, follows) {
163
+ pane.clearSelection()
164
+ if (!selected || selected.length === 0)
165
+ return
166
+ if (follows === true && selected.length === 1 && selected[0] < pane.total) {
167
+ pane.selection.only(selected[0])
168
+ pane.selectionVersion++
169
+ return
170
+ }
171
+ var kept = 0
172
+ for (var i = 0; i < selected.length; i++) {
173
+ if (selected[i] < pane.total) {
174
+ pane.selection.toggle(selected[i])
175
+ kept++
176
+ }
177
+ }
178
+ if (kept > 0)
179
+ pane.selectionVersion++
180
+ }
181
+
182
+ function apply(pane, item, dropped, cursor) {
183
+ // Issue 93: rows from a search dropped onto its scope, or a listing still out, are not the rows this tab left.
184
+ var same = pane.path === item.path && pane.showHidden === item.showHidden && dropped !== true && cursor === undefined && !pane.listInFlight
185
+ if (pane.tabs) pane.tabs.pendingCursorName = cursor === undefined ? null : cursor
186
+ var shown = pane.viewMode
187
+ pane.history = item.history.slice()
188
+ pane.forwardHistory = (item.forwardHistory || []).slice()
189
+ pane.viewMode = item.viewMode
190
+ // A tab's view becomes the standing one unless its folder keeps its own; ui/Pane.qml adoptView decides.
191
+ if (pane.adoptView)
192
+ pane.adoptView(item.path, item.viewMode)
193
+ pane.showHidden = item.showHidden
194
+ if (same) {
195
+ if (pane.backend && (pane.backend.sortBy !== item.sortBy || pane.backend.sortDesc !== item.sortDesc)) {
196
+ // Issue 94: the one reset a reorder takes, ui/js/Sort.js's, which this branch half repeated.
197
+ Sort.resort(pane, item.sortBy, item.sortDesc)
198
+ pane.tabs.pendingCursor = item.cursorIndex
199
+ return
200
+ }
201
+ // The switch that re-reads nothing, unless something else did: the watch and a write both list.
202
+ pane.setCursor(item.cursorIndex)
203
+ if (pane.backend && item.listRequests === pane.backend.listRequests) {
204
+ restoreSelection(pane, item.selected, item.follows)
205
+ return
206
+ }
207
+ pane.clearSelection()
208
+ return
209
+ }
210
+ // The listing keeps the tab's own dotfile answer restored above; a tab in another view clears at once, as its rows were never listed there.
211
+ pane.openWithoutHistory(item.path, { keepHidden: true, clearAtOnce: pane.viewMode !== shown, supersede: true })
212
+ // After the request: letting a listing go clears what an earlier switch owed, and this one is owed to the listing just asked for.
213
+ pane.tabs.pendingCursorName = cursor === undefined ? null : cursor
214
+ pane.tabs.pendingCursor = cursor === undefined ? item.cursorIndex : 0
215
+ pane.tabs.pendingSortBy = item.sortBy
216
+ pane.tabs.pendingSortDesc = item.sortDesc
217
+ }
218
+
219
+ function applyPending(pane) {
220
+ if (!pane.tabs)
221
+ return
222
+ var t = pane.tabs
223
+ // Issue 91, nixfred: spent on the first reply either way, or an order already in force never was.
224
+ if (t.pendingSortBy && t.pendingSortBy.length > 0 && pane.backend) {
225
+ var by = t.pendingSortBy
226
+ var desc = t.pendingSortDesc
227
+ t.pendingSortBy = ""
228
+ if (pane.backend.sortBy !== by || pane.backend.sortDesc !== desc) {
229
+ pane.backend.sort(by, desc)
230
+ pane.backend.sortBy = by
231
+ pane.backend.sortDesc = desc
232
+ pane.backend.window(0, pane.windowSize)
233
+ return
234
+ }
235
+ }
236
+ if (t.pendingCursorName) {
237
+ var name = t.pendingCursorName
238
+ t.pendingCursorName = null
239
+ var first = pane.held || 0
240
+ var end = Math.min(pane.total, first + (pane.rows ? pane.rows.length : pane.windowSize))
241
+ var found = false
242
+ for (var i = first; i < end; i++) {
243
+ var row = pane.rowFor(i)
244
+ if (row && row.n === name) {
245
+ t.pendingCursor = i
246
+ found = true
247
+ break
248
+ }
249
+ }
250
+ if (!found && pane.total > 0 && pane.backend && pane.backend.send) {
251
+ t.pendingLocatePath = (pane.path === "/" ? "/" : pane.path + "/") + name
252
+ t.pendingCursor = -1
253
+ pane.backend.send({ c: "locate", path: t.pendingLocatePath })
254
+ return
255
+ }
256
+ }
257
+ if (t.pendingCursor >= 0) {
258
+ var last = pane.total > 0 ? pane.total - 1 : 0
259
+ pane.setCursor(Math.min(t.pendingCursor, last))
260
+ t.pendingCursor = -1
261
+ }
262
+ }
263
+
264
+ // A fresh receiver index comes from its own name-only listing, never from the source window.
265
+ function locatedCursor(pane, message) {
266
+ var t = pane.tabs
267
+ if (!t || !t.pendingLocatePath || message.path !== t.pendingLocatePath || message.directory !== pane.path)
268
+ return
269
+ t.pendingLocatePath = ""
270
+ if (pane.listInFlight) return
271
+ var index = message.index >= 0 && message.index < pane.total ? message.index : 0
272
+ pane.setCursor(index)
273
+ if (pane.total > 0 && !pane.rowFor(index)) pane.backend.window(index, pane.windowSize)
274
+ }
275
+
276
+ // The desktop launch carries the same filename through the existing environment seam.
277
+ function prepareCursor(pane, cursor) {
278
+ if (!cursor) return
279
+ if (!pane.tabs) pane.tabs = pack([snapshot(pane)], 0)
280
+ pane.tabs.pendingCursor = 0
281
+ pane.tabs.pendingCursorName = cursor
282
+ pane.tabs.pendingLocatePath = ""
283
+ }
284
+
285
+ function currentItems(pane, here) {
286
+ if (pane.tabs && pane.tabs.items && pane.tabs.items.length > 0)
287
+ return pane.tabs.items.slice()
288
+ return [snapshot(pane, here)]
289
+ }
290
+
291
+ // A caller may name where the new tab lands, which is what the Places row menu's own row does;
292
+ // without one it is Settings, View, Opening that decides, and that still defaults to this folder.
293
+ function openNew(pane, where) {
294
+ // Before the preview and the search go: a refused tenth tab must cost the user nothing.
295
+ if (count(pane) >= MAX) {
296
+ sayCount(pane, "Nine tabs is the most.")
297
+ return
298
+ }
299
+ var here = restingPath(pane)
300
+ closePreview(pane)
301
+ var dropped = dropOverlay(pane)
302
+ // Settings > View > Opening decides where the new tab lands; it cloned the current folder before
303
+ // 0.2.1 and that is still the default. The tab the operator leaves keeps the path it was on.
304
+ var target = where || Startup.newTabPath(pane.uiState, here, pane.home)
305
+ var items = currentItems(pane, here)
306
+ var index = currentIndex(pane)
307
+ items[index] = snapshot(pane, here, items[index].tabIdentity, dropped)
308
+ items.push(snapshot(pane, target, undefined, dropped))
309
+ pane.tabs = pack(items, items.length - 1)
310
+ dropCount(pane)
311
+ // dropOverlay clears the search while its rows still stand, and a listing still out belongs to the tab being left, so both list the target again, the one out let go.
312
+ if (pane.path !== target || dropped || pane.listInFlight)
313
+ pane.openWithoutHistory(target, { supersede: true })
314
+ }
315
+
316
+ // Ctrl+Return on the cursor row, the keyboard twin of Tap.tappedTab's middle
317
+ // click: that directory in a new tab, leaving the cursor and selection alone.
318
+ function openCursorTab(pane) {
319
+ var row = pane.rowFor ? pane.rowFor(pane.cursorIndex) : null
320
+ if (!row || !row.d || typeof row.n !== "string") {
321
+ sayPlain(pane, "Only a folder opens in a new tab.")
322
+ return
323
+ }
324
+ var base = Search.baseOf(pane)
325
+ openNew(pane, base === "/" ? "/" + row.n : base + "/" + row.n)
326
+ }
327
+
328
+ function selectAt(pane, i) {
329
+ var here = restingPath(pane)
330
+ var items = currentItems(pane, here)
331
+ if (i < 0 || i >= items.length) {
332
+ sayCount(pane, Format.fill(WORDS.noTab, i + 1))
333
+ return
334
+ }
335
+ var index = currentIndex(pane)
336
+ if (i === index)
337
+ return
338
+ closePreview(pane)
339
+ var dropped = dropOverlay(pane)
340
+ items[index] = snapshot(pane, here, items[index].tabIdentity, dropped)
341
+ pane.tabs = pack(items, i)
342
+ apply(pane, items[i], dropped)
343
+ }
344
+
345
+ function closeAt(pane, i) {
346
+ var items = currentItems(pane)
347
+ if (items.length <= 1) {
348
+ sayCount(pane, WORDS.lastTab)
349
+ return
350
+ }
351
+ if (i < 0 || i >= items.length) {
352
+ sayCount(pane, Format.fill(WORDS.noTab, i + 1))
353
+ return
354
+ }
355
+ var index = currentIndex(pane)
356
+ items.splice(i, 1)
357
+ var next = index
358
+ if (i < index)
359
+ next = index - 1
360
+ else if (i === index)
361
+ next = Math.min(i, items.length - 1)
362
+ if (i === index) {
363
+ closePreview(pane)
364
+ var dropped = dropOverlay(pane)
365
+ pane.tabs = pack(items, next)
366
+ apply(pane, items[next], dropped)
367
+ } else {
368
+ pane.tabs = pack(items, next, pane.tabs)
369
+ }
370
+ dropCount(pane)
371
+ }
372
+
373
+ // A move lists nothing, so the listing out lands for the tab it belongs to and that tab keeps what the landing owes.
374
+ function move(pane, from, to) {
375
+ var items = currentItems(pane)
376
+ if (items.length < 2)
377
+ return
378
+ var index = currentIndex(pane)
379
+ items[index] = snapshot(pane, restingPath(pane), items[index].tabIdentity)
380
+ pane.tabs = pack(items, TabMove.reorder(items, from, to, index), pane.tabs)
381
+ }
382
+
383
+ // Tabs040 callout 1: { and } move the current tab with no re-list.
384
+ function moveCurrent(pane, delta) {
385
+ var total = count(pane)
386
+ if (total < 2)
387
+ return
388
+ var from = currentIndex(pane)
389
+ move(pane, from, TabMove.step(from, delta, total))
390
+ }
391
+
392
+ function act(action, pane) {
393
+ if (action === "tabMoveLeft" || action === "tabMoveRight") {
394
+ moveCurrent(pane, action === "tabMoveRight" ? 1 : -1)
395
+ return
396
+ }
397
+ if (action === "tabNext" || action === "tabPrevious") {
398
+ var total = count(pane)
399
+ var direction = action === "tabNext" ? 1 : -1
400
+ selectAt(pane, (currentIndex(pane) + total + direction) % total)
401
+ return
402
+ }
403
+ if (action === "tabNew") {
404
+ openNew(pane)
405
+ return
406
+ }
407
+ if (action === "tabClose") {
408
+ closeAt(pane, currentIndex(pane))
409
+ return
410
+ }
411
+ if (action.length === 4 && action.indexOf("tab") === 0 && action.charAt(3) >= "1" && action.charAt(3) <= "9")
412
+ selectAt(pane, parseInt(action.charAt(3), 10) - 1)
413
+ }
414
+
415
+ // Last folder reopens every tab; a missing folder stays for the listing error to name.
416
+ function remembered(pane) {
417
+ // The write lands before the pane moves, so the current tab reads dropPath.
418
+ var here = restingPath(pane)
419
+ if (here === pane.path && pane.dropPath)
420
+ here = pane.dropPath
421
+ var items = pane.tabs && pane.tabs.items && pane.tabs.items.length > 0 ? pane.tabs.items : null
422
+ if (!items)
423
+ return { paths: [here], index: 0 }
424
+ var index = currentIndex(pane)
425
+ var out = []
426
+ for (var i = 0; i < items.length; i++)
427
+ out.push(i === index ? here : String((items[i] || {}).path || ""))
428
+ return { paths: out, index: index }
429
+ }
430
+
431
+ // Without a Last-folder strip or with a named path there is no plan.
432
+ function restorePlan(state, argvPath) {
433
+ if (argvPath && String(argvPath).length > 0)
434
+ return null
435
+ var data = state || {}
436
+ if ((data.startIn || "home") !== "last")
437
+ return null
438
+ var stored = data.lastTabs || null
439
+ var kept = stored && Array.isArray(stored.paths) ? stored.paths : []
440
+ for (var i = 0; i < kept.length; i++) {
441
+ if (typeof kept[i] !== "string" || kept[i].length === 0)
442
+ return null
443
+ }
444
+ var paths = []
445
+ for (var j = 0; j < kept.length && paths.length < MAX; j++)
446
+ paths.push(kept[j])
447
+ if (paths.length === 0)
448
+ return null
449
+ var index = stored && stored.index === Math.floor(stored.index) ? stored.index : 0
450
+ if (index < 0 || index >= paths.length)
451
+ index = Math.min(Math.max(index, 0), paths.length - 1)
452
+ return { paths: paths, index: index }
453
+ }
454
+
455
+ // A restored tab snapshots like a fresh tab, so view, sort and hidden come from the pane.
456
+ function restoreItems(pane, paths) {
457
+ var out = []
458
+ for (var i = 0; i < paths.length; i++)
459
+ out.push(snapshot(pane, String(paths[i])))
460
+ return out
461
+ }
462
+
463
+ // A lift offers only private tab MIME; JSON preserves folder paths and cursor names verbatim.
464
+ var TAB_MIME = "application/x-flea-tab"
465
+
466
+ var TAB_VIEWS = ["list", "grid", "columns"]
467
+
468
+ // An unset process id never owns a tab drag.
469
+ var ownPid = ""
470
+
471
+ function setOwnPid(pid) {
472
+ ownPid = String(pid || "")
473
+ }
474
+
475
+ // One lift's token: the pid names the window, the token names the lift inside it.
476
+ var TOKEN_RANDOM_RANGE = 1000000000 // Spread simultaneous lifts across a billion random suffixes.
477
+ function newToken() {
478
+ return String(Date.now()) + "-" + String(Math.floor(Math.random() * TOKEN_RANDOM_RANGE))
479
+ }
480
+
481
+ // The hand-off a tear-off gives the one window it starts; src/tearoff.rs drops the same names for Rust launches.
482
+ var TEAR_OFF_ENV = ["FLEA_TAB_SOURCE_PID", "FLEA_TAB_CURSOR", "FLEA_TAB_TOKEN"]
483
+
484
+ // Sample input: ["flea", "/tmp"] gives ["env", "-u", "FLEA_TAB_SOURCE_PID", "-u", "FLEA_TAB_CURSOR", "-u", "FLEA_TAB_TOKEN", "flea", "/tmp"].
485
+ function freshLaunch(argv) {
486
+ var unset = ["env"]
487
+ for (var i = 0; i < TEAR_OFF_ENV.length; i++)
488
+ unset.push("-u", TEAR_OFF_ENV[i])
489
+ return unset.concat(argv)
490
+ }
491
+
492
+ // A lift names the live current tab or stored hidden tab, with the cursor name when available; null if absent.
493
+ function tabInfo(pane, index) {
494
+ if (!pane)
495
+ return null
496
+ var total = count(pane)
497
+ if (index < 0 || index >= total)
498
+ return null
499
+ var here = restingPath(pane)
500
+ var current = currentIndex(pane)
501
+ var path, view, cursor
502
+ cursor = ""
503
+ if (index === current) {
504
+ path = here
505
+ view = pane.viewMode
506
+ if (here === pane.path) cursor = cursorFile(pane)
507
+ } else {
508
+ var items = currentItems(pane, here)
509
+ var item = items[index] || {}
510
+ path = String(item.path || "")
511
+ view = String(item.viewMode || "")
512
+ if (typeof item.cursorName === "string")
513
+ cursor = item.cursorName
514
+ }
515
+ if (!path || path.charAt(0) !== "/")
516
+ return null
517
+ return { path: path, view: view, cursor: cursor }
518
+ }
519
+
520
+ // JSON carries every path byte, including CR and LF.
521
+ function tabPayload(pane, index, pid, token) {
522
+ var info = tabInfo(pane, index)
523
+ if (!info)
524
+ return ""
525
+ var who = pid === undefined ? ownPid : pid
526
+ var lift = token === undefined ? "" : token
527
+ return JSON.stringify([String(who), String(lift), String(info.path), String(info.view), String(info.cursor)])
528
+ }
529
+
530
+ // A tab drag offers only the private tab MIME.
531
+ function tabDragMime(pane, index, pid, token) {
532
+ var payload = tabPayload(pane, index, pid, token)
533
+ if (!payload)
534
+ return {}
535
+ var mime = {}
536
+ mime[TAB_MIME] = payload
537
+ return mime
538
+ }
539
+
540
+ // Sample input: ["111","lift-1","/tmp/folder","list","note.txt"].
541
+ function parseTabMime(payload) {
542
+ var fields = null
543
+ try {
544
+ fields = JSON.parse(String(payload))
545
+ } catch (error) {
546
+ return null
547
+ }
548
+ if (!Array.isArray(fields) || fields.length !== 5)
549
+ return null
550
+ var pid = String(fields[0] || "")
551
+ var token = String(fields[1] || "")
552
+ var path = String(fields[2] || "")
553
+ if (!/^[0-9]+$/.test(pid))
554
+ return null
555
+ if (token.length === 0 || /[\x00-\x1f\x7f]/.test(token))
556
+ return null
557
+ if (path.length === 0 || path.charAt(0) !== "/" || path.indexOf("\u0000") >= 0)
558
+ return null
559
+ return { pid: pid, token: token, path: path,
560
+ view: String(fields[3] || ""), cursor: String(fields[4] || "") }
561
+ }
562
+
563
+ // An own-window drag belongs to reorder and must not duplicate its tab.
564
+ function isOwnTab(info, pid) {
565
+ var self = pid === undefined ? ownPid : String(pid)
566
+ return !!info && self.length > 0 && info.pid === self
567
+ }
568
+
569
+ function hasTabFormat(formats) {
570
+ return !!formats && formats.indexOf(TAB_MIME) >= 0
571
+ }
572
+
573
+ function enterAccepts(formats, payload, selfPid, canRecv, outActive) {
574
+ var text = String(payload || "")
575
+ if (text.length === 0)
576
+ return hasTabFormat(formats) && canRecv === true
577
+ var info = parseTabMime(text)
578
+ if (!info)
579
+ return false
580
+ var self = selfPid === undefined ? ownPid : String(selfPid)
581
+ if (self.length > 0 && info.pid === self)
582
+ return outActive === true
583
+ return canRecv === true
584
+ }
585
+
586
+ // Return the on-strip slot under x; off-strip drops never reach this helper, and the caller passes -1.
587
+ function dropIndexAt(x, tabWidth, tabCount) {
588
+ return TabMove.insertionAt(x, tabWidth, tabCount)
589
+ }
590
+
591
+ // A catcher must compare the drop's global coordinates with the source window before tearing off.
592
+ function catcherOutcome(rect, strip, x, y, tabWidth, tabCount) {
593
+ if (!rect || !(rect.width > 0) || !(rect.height > 0) || !isFinite(x) || !isFinite(y))
594
+ return { outcome: "cancel", at: -1 }
595
+ var localX = x - rect.x, localY = y - rect.y
596
+ if (localX < 0 || localY < 0 || localX >= rect.width || localY >= rect.height)
597
+ return { outcome: "tearoff", at: -1 }
598
+ if (strip && localX >= strip.x && localX < strip.x + strip.width
599
+ && localY >= strip.y && localY < strip.y + strip.height)
600
+ return { outcome: "return", at: dropIndexAt(localX - strip.x, tabWidth, tabCount) }
601
+ return { outcome: "cancel", at: -1 }
602
+ }
603
+
604
+ // A full strip refuses incoming tabs; a listing out is let go by the tab received.
605
+ function canReceive(pane) {
606
+ if (!pane)
607
+ return false
608
+ return count(pane) < MAX
609
+ }
610
+
611
+ // A received foreign tab opens at the requested drop position.
612
+ function receiveTab(pane, payload, at) {
613
+ if (!pane)
614
+ return false
615
+ var info = parseTabMime(payload)
616
+ if (!info || isOwnTab(info))
617
+ return false
618
+ if (count(pane) >= MAX) {
619
+ sayCount(pane, "Nine tabs is the most.")
620
+ return false
621
+ }
622
+ closePreview(pane)
623
+ var dropped = dropOverlay(pane)
624
+ var here = restingPath(pane)
625
+ var items = currentItems(pane, here)
626
+ var index = currentIndex(pane)
627
+ items[index] = snapshot(pane, here, items[index].tabIdentity, dropped)
628
+ var snap = snapshot(pane, info.path, undefined, dropped)
629
+ if (TAB_VIEWS.indexOf(info.view) >= 0)
630
+ snap.viewMode = info.view
631
+ if (info.cursor.length > 0)
632
+ snap.cursorName = info.cursor
633
+ var place = at >= 0 && at <= items.length ? at : items.length
634
+ items.splice(place, 0, snap)
635
+ pane.tabs = pack(items, place)
636
+ dropCount(pane)
637
+ apply(pane, snap, dropped, info.cursor)
638
+ return true
639
+ }
640
+
641
+ // A window's lone tab cannot be lifted out of its hidden strip.
642
+ function canLift(pane) {
643
+ return count(pane) >= 2
644
+ }
645
+
646
+ // Only the outstanding lift's token may acknowledge that lift.
647
+ function takeToken(stored, token) {
648
+ return !!stored && stored.length > 0 && stored === String(token || "")
649
+ }
650
+
651
+ // An unacknowledged lift expires after one network leg's 15 s bound (ui/NetworkMounts.qml mountTimeout).
652
+ var ACK_WAIT_MS = 15000
653
+
654
+ function ackCloses(stored, token, liftedAt, now) {
655
+ if (!takeToken(stored, token))
656
+ return false
657
+ if (liftedAt > 0 && now - liftedAt > ACK_WAIT_MS)
658
+ return false
659
+ return true
660
+ }
661
+
662
+ // B's synchronous take decision at drop time; the async peek may still refuse, and then no ack goes out.
663
+ var DROP_TAKE = "move"
664
+ var DROP_IGNORE = "ignore"
665
+ function dropDecision(info, selfPid, outActive, canRecv) {
666
+ if (!info)
667
+ return DROP_IGNORE
668
+ if (isOwnTab(info, selfPid))
669
+ return outActive === true ? DROP_TAKE : DROP_IGNORE
670
+ return canRecv === true ? DROP_TAKE : DROP_IGNORE
671
+ }
672
+
673
+ // Moving a tab must never close the window's only remaining tab.
674
+ function closeTabAfterMove(pane, index) {
675
+ if (!pane)
676
+ return "kept"
677
+ var total = count(pane)
678
+ if (index < 0 || index >= total)
679
+ return "kept"
680
+ if (total <= 1)
681
+ return "kept"
682
+ closeAt(pane, index)
683
+ return "closed"
684
+ }
685
+
686
+ // A rename editor prevents its tab from leaving the window.
687
+ function tearRefusal(pane) {
688
+ if (!pane)
689
+ return ""
690
+ if (pane.listInFlight)
691
+ return "A directory is already loading."
692
+ if (pane.renamePending === true)
693
+ return "Finish the rename before dragging a tab out."
694
+ if (typeof pane.renameEditor === "function" && pane.renameEditor())
695
+ return "Finish the rename before dragging a tab out."
696
+ return ""
697
+ }
698
+
699
+ // Tab lookup uses the live path for the current tab and stored paths for hidden tabs.
700
+ function indexOfPath(pane, path) {
701
+ if (!pane || !path)
702
+ return -1
703
+ var here = restingPath(pane)
704
+ var items = currentItems(pane, here)
705
+ var current = currentIndex(pane)
706
+ for (var i = 0; i < items.length; i++) {
707
+ var named = i === current ? here : String((items[i] || {}).path || "")
708
+ if (named === path)
709
+ return i
710
+ }
711
+ return -1
712
+ }
713
+
714
+ // A lift captures pane and identity; indices and paths may change while its ack travels.
715
+ function captureLift(pane, index, token, now) {
716
+ var items = currentItems(pane)
717
+ if (index < 0 || index >= items.length) return null
718
+ for (var i = 0; i < items.length; i++)
719
+ if (!items[i].tabIdentity) items[i].tabIdentity = ++nextTabIdentity
720
+ if (!pane.tabs) pane.tabs = pack(items, 0)
721
+ return { token: token, pane: pane, identity: items[index].tabIdentity, liftedAt: now, taken: false }
722
+ }
723
+
724
+ function resolveMovedTab(pane, identity) {
725
+ if (!pane || !pane.tabs || !identity) return -1
726
+ var items = pane.tabs.items
727
+ for (var i = 0; i < items.length; i++)
728
+ if (items[i].tabIdentity === identity) return i
729
+ return -1
730
+ }
731
+
732
+ function liftFor(lifts, token) {
733
+ for (var i = 0; i < lifts.length; i++)
734
+ if (takeToken(lifts[i].token, token)) return lifts[i]
735
+ return null
736
+ }