flea-qml 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +511 -2
  3. package/THIRD-PARTY-NOTICES.md +29 -0
  4. package/demos/App.java +5 -0
  5. package/demos/Dockerfile +4 -0
  6. package/demos/Main.kt +3 -0
  7. package/demos/Program.cs +1 -0
  8. package/demos/README.md +12 -0
  9. package/demos/app.dart +3 -0
  10. package/demos/app.deb +0 -0
  11. package/demos/app.js +1 -0
  12. package/demos/backup.tar +0 -0
  13. package/demos/budget.xlsx +0 -0
  14. package/demos/build.sh +2 -0
  15. package/demos/bundle.zip +0 -0
  16. package/demos/clip.mp4 +0 -0
  17. package/demos/data.sqlite +0 -0
  18. package/demos/disk.iso +0 -0
  19. package/demos/engine.cpp +5 -0
  20. package/demos/firmware.bin +0 -0
  21. package/demos/flake.nix +4 -0
  22. package/demos/folder/inside.txt +1 -0
  23. package/demos/font.ttf +0 -0
  24. package/demos/index.html +5 -0
  25. package/demos/index.php +2 -0
  26. package/demos/init.lua +1 -0
  27. package/demos/letter.docx +0 -0
  28. package/demos/lib.rs +3 -0
  29. package/demos/main.c +6 -0
  30. package/demos/main.h +3 -0
  31. package/demos/math-and-diagrams.md +200 -0
  32. package/demos/notes.tar.gz +0 -0
  33. package/demos/notes.txt +3 -0
  34. package/demos/photo.png +0 -0
  35. package/demos/program +2 -0
  36. package/demos/report.pdf +32 -0
  37. package/demos/script.py +1 -0
  38. package/demos/server.go +7 -0
  39. package/demos/settings.json +4 -0
  40. package/demos/slides.pptx +0 -0
  41. package/demos/song.mp3 +0 -0
  42. package/demos/style.css +4 -0
  43. package/demos/tool.rb +1 -0
  44. package/demos/types.ts +2 -0
  45. package/docs/pele.md +23 -0
  46. package/docs/protocol.md +3420 -0
  47. package/docs/temml.md +63 -0
  48. package/index.js +107 -0
  49. package/package.json +34 -4
  50. package/src/archive.js +407 -0
  51. package/src/archiveops.js +728 -0
  52. package/src/backend.js +369 -0
  53. package/src/bulkrename.js +95 -0
  54. package/src/clip-host.js +76 -0
  55. package/src/clip.js +462 -0
  56. package/src/code.js +168 -0
  57. package/src/compare.js +74 -0
  58. package/src/create.js +128 -0
  59. package/src/dirsize.js +147 -0
  60. package/src/figure-svg.js +530 -0
  61. package/src/figure-thread.js +23 -0
  62. package/src/figure.js +132 -0
  63. package/src/find.js +55 -0
  64. package/src/flea.js +39 -0
  65. package/src/fsinfo.js +39 -0
  66. package/src/highlight.js +141 -0
  67. package/src/html.js +52 -0
  68. package/src/kinds.js +89 -0
  69. package/src/listing.js +130 -0
  70. package/src/markdown-html.js +317 -0
  71. package/src/mathml.js +186 -0
  72. package/src/menu.js +180 -0
  73. package/src/meta.js +135 -0
  74. package/src/office.js +810 -0
  75. package/src/order.js +100 -0
  76. package/src/peek.js +49 -0
  77. package/src/pele-wrapper.js +695 -0
  78. package/src/permissions.js +180 -0
  79. package/src/places.js +313 -0
  80. package/src/preview-language.js +10 -0
  81. package/src/terminal-server.js +95 -0
  82. package/src/terminal-shell.js +26 -0
  83. package/src/transfer.js +669 -0
  84. package/src/trash-record.js +41 -0
  85. package/src/trash.js +210 -0
  86. package/src/trashbrowse.js +327 -0
  87. package/src/uistate.js +393 -0
  88. package/src/uistore.js +168 -0
  89. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  90. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  91. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  92. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  93. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  94. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  95. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  96. package/src/vendor/pele.js +145 -0
  97. package/src/vendor/temml.mjs +14708 -0
  98. package/src/watch.js +98 -0
  99. package/src/wayland/client.js +109 -0
  100. package/src/wayland/protocol.js +55 -0
  101. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  102. package/src/wayland/protocols/wayland.xml +3248 -0
  103. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  104. package/src/wayland/resource-io.js +6 -0
  105. package/src/wayland/socket-discovery.js +26 -0
  106. package/src/wayland/transport-posix.js +120 -0
  107. package/src/wayland/transport-win32.js +11 -0
  108. package/src/wayland/transport.js +3 -0
  109. package/src/wayland/win32-resources.js +80 -0
  110. package/src/wayland/win32-wire.js +62 -0
  111. package/test/archive.test.js +80 -0
  112. package/test/bench-markdown-scroll.mjs +190 -0
  113. package/test/clip-host.test.js +113 -0
  114. package/test/code-preview.test.js +35 -0
  115. package/test/compare-ui-paths.test.js +24 -0
  116. package/test/compare.test.js +54 -0
  117. package/test/delete-trash.test.js +67 -0
  118. package/test/dirsize.test.js +138 -0
  119. package/test/file-ops.test.js +307 -0
  120. package/test/file-url.test.js +24 -0
  121. package/test/find-contents.test.js +161 -0
  122. package/test/find-input.test.js +66 -0
  123. package/test/fixtures/pele-parity.json +1006 -0
  124. package/test/highlight-launcher.test.js +35 -0
  125. package/test/html-preview.test.js +22 -0
  126. package/test/input.test.js +15 -0
  127. package/test/keymap.test.js +26 -0
  128. package/test/language-icons.test.js +36 -0
  129. package/test/launcher.test.js +51 -0
  130. package/test/manual-archive-here-ui.mjs +47 -0
  131. package/test/manual-code-scroll.mjs +28 -0
  132. package/test/manual-column-scroll.mjs +24 -0
  133. package/test/manual-compare-ui.mjs +20 -0
  134. package/test/manual-find-status.mjs +37 -0
  135. package/test/manual-folder-focus.mjs +63 -0
  136. package/test/manual-html-security.mjs +23 -0
  137. package/test/manual-html-ui.mjs +30 -0
  138. package/test/manual-syntax-preview.mjs +26 -0
  139. package/test/manual-terminal-lifecycle.mjs +42 -0
  140. package/test/manual-terminal-navigation.mjs +48 -0
  141. package/test/manual-terminal-ui.mjs +47 -0
  142. package/test/manual-trash-ui.mjs +86 -0
  143. package/test/markdown-diagrams.test.js +93 -0
  144. package/test/markdown-html.test.js +105 -0
  145. package/test/mathml.test.js +92 -0
  146. package/test/menu-transfer.test.js +57 -0
  147. package/test/office-preview.test.js +154 -0
  148. package/test/pele-parity.test.js +28 -0
  149. package/test/places.test.js +134 -0
  150. package/test/preview-keys.test.js +60 -0
  151. package/test/preview-language.test.js +22 -0
  152. package/test/status-filename.test.js +20 -0
  153. package/test/transfer-replace-merge.test.js +187 -0
  154. package/test/trash-address.test.js +23 -0
  155. package/test/trash-view.test.js +37 -0
  156. package/test/trashbrowse.test.js +135 -0
  157. package/ui/AboutFacts.qml +78 -0
  158. package/ui/ArchiveCards.qml +95 -0
  159. package/ui/ArchiveCompressDialog.qml +378 -0
  160. package/ui/ArchiveHereConfirm.qml +127 -0
  161. package/ui/ArchivePasswordDialog.qml +257 -0
  162. package/ui/Backend.qml +775 -0
  163. package/ui/BulkOldNames.qml +47 -0
  164. package/ui/BulkRenameDialog.qml +380 -0
  165. package/ui/CardFrame.qml +67 -0
  166. package/ui/CardScroll.qml +79 -0
  167. package/ui/CheckBox.qml +73 -0
  168. package/ui/ChromeBar.qml +360 -0
  169. package/ui/ChromeButton.qml +131 -0
  170. package/ui/ChromeModes.qml +43 -0
  171. package/ui/CodeLines.qml +256 -0
  172. package/ui/CodeRefusal.qml +52 -0
  173. package/ui/CollideConfirm.qml +147 -0
  174. package/ui/CollideHost.qml +167 -0
  175. package/ui/ColumnPane.qml +525 -0
  176. package/ui/ColumnRow.qml +380 -0
  177. package/ui/ColumnsArea.qml +594 -0
  178. package/ui/ColumnsLanding.qml +147 -0
  179. package/ui/CompareView.qml +321 -0
  180. package/ui/ContextMenu.qml +743 -0
  181. package/ui/ConvertDialog.qml +456 -0
  182. package/ui/CountedWorker.qml +7 -0
  183. package/ui/Crumb.qml +64 -0
  184. package/ui/CrumbDrop.qml +25 -0
  185. package/ui/CsvTable.qml +394 -0
  186. package/ui/CursorMark.qml +41 -0
  187. package/ui/DefaultClaim.qml +130 -0
  188. package/ui/DeviceLock.qml +47 -0
  189. package/ui/DeviceMounts.qml +540 -0
  190. package/ui/DialogButton.qml +150 -0
  191. package/ui/DialogField.qml +147 -0
  192. package/ui/DialogHeader.qml +31 -0
  193. package/ui/DialogScrim.qml +20 -0
  194. package/ui/DialogTitle.qml +37 -0
  195. package/ui/Divider.qml +9 -0
  196. package/ui/DropInto.qml +126 -0
  197. package/ui/DropTarget.qml +35 -0
  198. package/ui/DropboxMark.qml +60 -0
  199. package/ui/EmptyState.qml +150 -0
  200. package/ui/EscapeHint.qml +11 -0
  201. package/ui/FactsTable.qml +54 -0
  202. package/ui/FastScrollHandler.qml +476 -0
  203. package/ui/FatNamesConfirm.qml +157 -0
  204. package/ui/Favourites.qml +143 -0
  205. package/ui/FigureService.qml +120 -0
  206. package/ui/FileDrag.qml +267 -0
  207. package/ui/FilterStrip.qml +133 -0
  208. package/ui/FitMetrics.qml +22 -0
  209. package/ui/FlashDriveRows.qml +137 -0
  210. package/ui/FlashHost.qml +72 -0
  211. package/ui/FlashImageRow.qml +104 -0
  212. package/ui/FlashRunner.qml +119 -0
  213. package/ui/FlashStepRow.qml +172 -0
  214. package/ui/FlashStrip.qml +38 -0
  215. package/ui/FlashView.qml +397 -0
  216. package/ui/FleaMark.qml +88 -0
  217. package/ui/FlyoutFade.qml +23 -0
  218. package/ui/FontPane.qml +170 -0
  219. package/ui/Glass.qml +114 -0
  220. package/ui/GlassTagger.qml +59 -0
  221. package/ui/Glyph.qml +64 -0
  222. package/ui/GlyphSolid.qml +22 -0
  223. package/ui/GridArea.qml +664 -0
  224. package/ui/GridTile.qml +325 -0
  225. package/ui/GvfsBridge.qml +164 -0
  226. package/ui/Header.qml +433 -0
  227. package/ui/HoverTimers.qml +39 -0
  228. package/ui/HtmlClip.qml +30 -0
  229. package/ui/HtmlPane.qml +63 -0
  230. package/ui/HyprlandMark.qml +45 -0
  231. package/ui/Ipc.qml +1005 -0
  232. package/ui/IpcControls.qml +7 -0
  233. package/ui/IpcMenuState.qml +48 -0
  234. package/ui/IpcPreviewState.qml +185 -0
  235. package/ui/JumpPath.qml +51 -0
  236. package/ui/KeymapSheet.qml +561 -0
  237. package/ui/KeymapSheetResult.qml +89 -0
  238. package/ui/KindStack.qml +40 -0
  239. package/ui/LangMark.qml +51 -0
  240. package/ui/LangMarkLoader.qml +26 -0
  241. package/ui/List.qml +480 -0
  242. package/ui/LoadingState.qml +54 -0
  243. package/ui/LocalSend.qml +56 -0
  244. package/ui/LocalSendMark.qml +73 -0
  245. package/ui/MarkdownBlockView.qml +311 -0
  246. package/ui/MarkdownBlocks.qml +35 -0
  247. package/ui/MarkdownFigure.qml +254 -0
  248. package/ui/MarkdownImages.qml +72 -0
  249. package/ui/MarkdownList.qml +81 -0
  250. package/ui/MarkdownMathsText.qml +97 -0
  251. package/ui/MarkdownPane.qml +83 -0
  252. package/ui/MarkdownPictures.qml +41 -0
  253. package/ui/MarkdownQuote.qml +52 -0
  254. package/ui/MarkdownRemote.qml +82 -0
  255. package/ui/MarkdownSource.qml +155 -0
  256. package/ui/MarkdownTable.qml +222 -0
  257. package/ui/MarkdownTableScroll.qml +64 -0
  258. package/ui/MarkdownTableWheel.qml +126 -0
  259. package/ui/MarkdownText.qml +87 -0
  260. package/ui/MarkdownWorker.js +132 -0
  261. package/ui/MatchText.qml +99 -0
  262. package/ui/MediaSession.qml +38 -0
  263. package/ui/MediaSound.qml +26 -0
  264. package/ui/MediaStrip.qml +191 -0
  265. package/ui/MediaTrack.qml +36 -0
  266. package/ui/MediaVolume.qml +54 -0
  267. package/ui/MenuActionDialog.qml +396 -0
  268. package/ui/MenuEdgeFade.qml +24 -0
  269. package/ui/MenuHost.qml +227 -0
  270. package/ui/MenuRow.qml +351 -0
  271. package/ui/MountListing.qml +100 -0
  272. package/ui/NetworkDialog.qml +435 -0
  273. package/ui/NetworkForm.qml +426 -0
  274. package/ui/NetworkMounts.qml +913 -0
  275. package/ui/NetworkPlaces.qml +127 -0
  276. package/ui/OpenWithDialog.qml +613 -0
  277. package/ui/Opener.qml +95 -0
  278. package/ui/Pane.qml +1320 -0
  279. package/ui/PaneMenuActions.qml +527 -0
  280. package/ui/PanePath.qml +90 -0
  281. package/ui/PaneRail.qml +268 -0
  282. package/ui/PaneStates.qml +88 -0
  283. package/ui/PaneSwap.qml +339 -0
  284. package/ui/PaneWire.qml +601 -0
  285. package/ui/ParkedWait.qml +9 -0
  286. package/ui/PathEntry.qml +186 -0
  287. package/ui/PathJump.qml +400 -0
  288. package/ui/PdfViewer.qml +297 -0
  289. package/ui/PermissionsDialog.qml +658 -0
  290. package/ui/PermissionsHeadings.qml +31 -0
  291. package/ui/PhoneMounts.qml +67 -0
  292. package/ui/PickerAnswers.qml +75 -0
  293. package/ui/PickerBox.qml +26 -0
  294. package/ui/PickerChrome.qml +295 -0
  295. package/ui/PickerFilter.qml +97 -0
  296. package/ui/PickerGrid.qml +308 -0
  297. package/ui/PickerHeader.qml +28 -0
  298. package/ui/PickerLifecycle.qml +27 -0
  299. package/ui/PickerList.qml +218 -0
  300. package/ui/PickerListing.qml +149 -0
  301. package/ui/PickerPathBar.qml +127 -0
  302. package/ui/PickerPlaces.qml +189 -0
  303. package/ui/PickerRecent.qml +92 -0
  304. package/ui/PickerSave.qml +101 -0
  305. package/ui/PickerSelection.qml +155 -0
  306. package/ui/PickerWindow.qml +846 -0
  307. package/ui/PowerSectorsReader.qml +10 -0
  308. package/ui/PrebuildDriver.qml +146 -0
  309. package/ui/Preview.qml +1005 -0
  310. package/ui/PreviewAnimation.qml +24 -0
  311. package/ui/PreviewArchive.qml +91 -0
  312. package/ui/PreviewColumn.qml +553 -0
  313. package/ui/PreviewEdge.qml +63 -0
  314. package/ui/PreviewFacts.qml +30 -0
  315. package/ui/PreviewFactsLine.qml +34 -0
  316. package/ui/PreviewHtml.qml +233 -0
  317. package/ui/PreviewImage.qml +253 -0
  318. package/ui/PreviewImagePan.qml +13 -0
  319. package/ui/PreviewImageSharp.qml +32 -0
  320. package/ui/PreviewLines.qml +106 -0
  321. package/ui/PreviewMarkdown.qml +706 -0
  322. package/ui/PreviewMedia.qml +250 -0
  323. package/ui/PreviewPdf.qml +482 -0
  324. package/ui/PreviewScroll.qml +201 -0
  325. package/ui/PreviewSwap.qml +335 -0
  326. package/ui/PreviewText.qml +149 -0
  327. package/ui/ProtocolChip.qml +18 -0
  328. package/ui/QuickLookArchive.qml +41 -0
  329. package/ui/QuickLookBar.qml +103 -0
  330. package/ui/QuickLookPrepare.qml +355 -0
  331. package/ui/QuickLookSwap.qml +10 -0
  332. package/ui/QuickLookWarm.qml +65 -0
  333. package/ui/RailDrop.qml +155 -0
  334. package/ui/RailHeading.qml +19 -0
  335. package/ui/RailPlaces.qml +38 -0
  336. package/ui/RailTrashRow.qml +67 -0
  337. package/ui/RenameField.qml +353 -0
  338. package/ui/RendererRetry.qml +11 -0
  339. package/ui/ResizeHandle.qml +52 -0
  340. package/ui/Reveal.qml +54 -0
  341. package/ui/Row.qml +476 -0
  342. package/ui/RowDrag.qml +213 -0
  343. package/ui/RowDropTarget.qml +27 -0
  344. package/ui/RunCard.qml +286 -0
  345. package/ui/Scripts.qml +65 -0
  346. package/ui/SearchStrip.qml +232 -0
  347. package/ui/SelectionBand.qml +140 -0
  348. package/ui/SelectionPreview.qml +370 -0
  349. package/ui/SettingsFavourite.qml +122 -0
  350. package/ui/SettingsGroup.qml +96 -0
  351. package/ui/SettingsHint.qml +12 -0
  352. package/ui/SettingsMetrics.qml +29 -0
  353. package/ui/SettingsPane.qml +178 -0
  354. package/ui/SettingsPanel.qml +547 -0
  355. package/ui/SettingsRail.qml +128 -0
  356. package/ui/SettingsRow.qml +409 -0
  357. package/ui/SettingsRuler.qml +85 -0
  358. package/ui/SettingsSegment.qml +85 -0
  359. package/ui/ShareBrowser.qml +113 -0
  360. package/ui/ShareLink.qml +67 -0
  361. package/ui/ShelfPins.qml +101 -0
  362. package/ui/Sidebar.qml +728 -0
  363. package/ui/SidebarRow.qml +358 -0
  364. package/ui/Spinner.qml +60 -0
  365. package/ui/StableModel.qml +89 -0
  366. package/ui/StateMessage.qml +84 -0
  367. package/ui/StatusBar.qml +512 -0
  368. package/ui/StatusFileName.qml +27 -0
  369. package/ui/TabBar.qml +673 -0
  370. package/ui/TabDragGeometry.qml +60 -0
  371. package/ui/TagMenuMark.qml +52 -0
  372. package/ui/TagRailSection.qml +53 -0
  373. package/ui/TagSlot.qml +40 -0
  374. package/ui/TagState.qml +150 -0
  375. package/ui/TagSwatch.qml +28 -0
  376. package/ui/Taildrop.qml +86 -0
  377. package/ui/TailscaleMark.qml +50 -0
  378. package/ui/TerminalService.qml +69 -0
  379. package/ui/TerminalTab.qml +48 -0
  380. package/ui/TerminalTabs.qml +27 -0
  381. package/ui/Theme.qml +460 -0
  382. package/ui/ThemeColors.qml +43 -0
  383. package/ui/TransferCard.qml +239 -0
  384. package/ui/TrashConfirm.qml +129 -0
  385. package/ui/TrashDropHandler.qml +11 -0
  386. package/ui/TrashHost.qml +202 -0
  387. package/ui/TrashMonitor.qml +30 -0
  388. package/ui/TrashToolbar.qml +19 -0
  389. package/ui/TrashView.qml +492 -0
  390. package/ui/UnlockDialog.qml +450 -0
  391. package/ui/UnlockHost.qml +206 -0
  392. package/ui/UpdateCheck.qml +97 -0
  393. package/ui/UserDirs.qml +25 -0
  394. package/ui/ViewState.qml +565 -0
  395. package/ui/ViewportScrollBar.qml +195 -0
  396. package/ui/ViewportScrollBars.qml +25 -0
  397. package/ui/WebPreview.qml +33 -0
  398. package/ui/WebPreviewView.qml +26 -0
  399. package/ui/WindowBody.qml +829 -0
  400. package/ui/WindowDrag.qml +35 -0
  401. package/ui/boot/Commons/AudioNodes.qml +50 -0
  402. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  403. package/ui/boot/Commons/Border.qml +243 -0
  404. package/ui/boot/Commons/BorderGeometry.js +373 -0
  405. package/ui/boot/Commons/Color.qml +258 -0
  406. package/ui/boot/Commons/I18n.qml +118 -0
  407. package/ui/boot/Commons/InputDevices.qml +112 -0
  408. package/ui/boot/Commons/InputMethodState.qml +38 -0
  409. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  410. package/ui/boot/Commons/Numbers.js +19 -0
  411. package/ui/boot/Commons/Plural.js +91 -0
  412. package/ui/boot/Commons/ShellIpc.qml +12 -0
  413. package/ui/boot/Commons/Style.qml +561 -0
  414. package/ui/boot/Commons/Translations.qml +100 -0
  415. package/ui/boot/Commons/UntypedInput.qml +152 -0
  416. package/ui/boot/Commons/Util.qml +188 -0
  417. package/ui/boot/Commons/qmldir +13 -0
  418. package/ui/boot/LICENSE-omarchy +20 -0
  419. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  420. package/ui/boot/Ui/BarIconButton.qml +106 -0
  421. package/ui/boot/Ui/BarIconContent.qml +38 -0
  422. package/ui/boot/Ui/BarIndicator.qml +53 -0
  423. package/ui/boot/Ui/BarWidget.qml +50 -0
  424. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  425. package/ui/boot/Ui/BorderSurface.qml +40 -0
  426. package/ui/boot/Ui/Button.qml +224 -0
  427. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  428. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  429. package/ui/boot/Ui/CursorSurface.qml +42 -0
  430. package/ui/boot/Ui/Dropdown.qml +245 -0
  431. package/ui/boot/Ui/FilterInput.qml +26 -0
  432. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  433. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  434. package/ui/boot/Ui/MultiSelect.qml +624 -0
  435. package/ui/boot/Ui/NumberField.qml +86 -0
  436. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  437. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  438. package/ui/boot/Ui/Panel.qml +60 -0
  439. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  440. package/ui/boot/Ui/PanelController.qml +16 -0
  441. package/ui/boot/Ui/PanelHero.qml +112 -0
  442. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  443. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  444. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  445. package/ui/boot/Ui/PanelSlider.qml +156 -0
  446. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  447. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  448. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  449. package/ui/boot/Ui/PopupCard.qml +181 -0
  450. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  451. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  452. package/ui/boot/Ui/SizedImage.qml +125 -0
  453. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  454. package/ui/boot/Ui/TextField.qml +58 -0
  455. package/ui/boot/Ui/Toggle.qml +118 -0
  456. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  457. package/ui/boot/Ui/WidgetButton.qml +172 -0
  458. package/ui/boot/Ui/qmldir +41 -0
  459. package/ui/boot/fleatab.qml +50 -0
  460. package/ui/boot/picker.qml +39 -0
  461. package/ui/boot/shell.qml +131 -0
  462. package/ui/boot/tabtearoff.qml +51 -0
  463. package/ui/js/Anchor.js +379 -0
  464. package/ui/js/AnchorHold.js +162 -0
  465. package/ui/js/Archive.js +157 -0
  466. package/ui/js/ArchiveCard.js +44 -0
  467. package/ui/js/ArchiveOps.js +44 -0
  468. package/ui/js/BackReturn.js +118 -0
  469. package/ui/js/BulkRename.js +100 -0
  470. package/ui/js/Buttons.js +54 -0
  471. package/ui/js/ChromeKeys.js +116 -0
  472. package/ui/js/ClipMarks.js +73 -0
  473. package/ui/js/ClipSelection.js +67 -0
  474. package/ui/js/Clipboard.js +179 -0
  475. package/ui/js/Cloud.js +97 -0
  476. package/ui/js/CodeRuns.js +72 -0
  477. package/ui/js/Collide.js +212 -0
  478. package/ui/js/ColumnFit.js +28 -0
  479. package/ui/js/ColumnMenu.js +111 -0
  480. package/ui/js/Columns.js +298 -0
  481. package/ui/js/ColumnsCarry.js +103 -0
  482. package/ui/js/Compare.js +74 -0
  483. package/ui/js/Contrast.js +157 -0
  484. package/ui/js/ControlReader.js +42 -0
  485. package/ui/js/Convert.js +52 -0
  486. package/ui/js/CopyAs.js +52 -0
  487. package/ui/js/Crumbs.js +178 -0
  488. package/ui/js/Csv.js +231 -0
  489. package/ui/js/Density.js +44 -0
  490. package/ui/js/Devices.js +440 -0
  491. package/ui/js/DirSizes.js +180 -0
  492. package/ui/js/Drag.js +360 -0
  493. package/ui/js/DragOut.js +166 -0
  494. package/ui/js/DropDev.js +52 -0
  495. package/ui/js/Dropbox.js +41 -0
  496. package/ui/js/Eject.js +217 -0
  497. package/ui/js/Errors.js +188 -0
  498. package/ui/js/ExtThumbs.js +115 -0
  499. package/ui/js/Facts.js +402 -0
  500. package/ui/js/FigureDate.js +14 -0
  501. package/ui/js/Filter.js +285 -0
  502. package/ui/js/Find.js +46 -0
  503. package/ui/js/Flash.js +184 -0
  504. package/ui/js/FlashCard.js +79 -0
  505. package/ui/js/FlashEndings.js +125 -0
  506. package/ui/js/FlashPane.js +116 -0
  507. package/ui/js/FleaBin.js +12 -0
  508. package/ui/js/Focus.js +504 -0
  509. package/ui/js/FolderSorts.js +108 -0
  510. package/ui/js/FolderViews.js +44 -0
  511. package/ui/js/Format.js +314 -0
  512. package/ui/js/Fuzzy.js +141 -0
  513. package/ui/js/GlassTag.js +72 -0
  514. package/ui/js/Grid.js +33 -0
  515. package/ui/js/GridGeometry.js +90 -0
  516. package/ui/js/GridNames.js +153 -0
  517. package/ui/js/GvfsBridge.js +200 -0
  518. package/ui/js/HostClipboard.js +29 -0
  519. package/ui/js/Icons.js +218 -0
  520. package/ui/js/Input.js +50 -0
  521. package/ui/js/Jump.js +200 -0
  522. package/ui/js/KeyName.js +64 -0
  523. package/ui/js/Keymap.js +401 -0
  524. package/ui/js/Kinds.js +164 -0
  525. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  526. package/ui/js/LangMarkPaths.js +47 -0
  527. package/ui/js/LangMarks.js +120 -0
  528. package/ui/js/Lists.js +11 -0
  529. package/ui/js/LocalSend.js +39 -0
  530. package/ui/js/LockedMenu.js +63 -0
  531. package/ui/js/MakeDefault.js +186 -0
  532. package/ui/js/MarkBox.js +7 -0
  533. package/ui/js/Markdown.js +119 -0
  534. package/ui/js/MarkdownLists.js +86 -0
  535. package/ui/js/MarkdownMaths.js +58 -0
  536. package/ui/js/MarkdownPictures.js +32 -0
  537. package/ui/js/MarkdownPrepared.js +139 -0
  538. package/ui/js/MarkdownTableFit.js +34 -0
  539. package/ui/js/Marks.js +148 -0
  540. package/ui/js/Marquee.js +96 -0
  541. package/ui/js/Match.js +93 -0
  542. package/ui/js/Material.js +109 -0
  543. package/ui/js/MdBlocks.js +437 -0
  544. package/ui/js/MdBreak.js +71 -0
  545. package/ui/js/MdChunks.js +70 -0
  546. package/ui/js/MdContainer.js +137 -0
  547. package/ui/js/MdDocument.js +337 -0
  548. package/ui/js/MdEmph.js +97 -0
  549. package/ui/js/MdEntity.js +108 -0
  550. package/ui/js/MdEntityTable.js +223 -0
  551. package/ui/js/MdEscape.js +59 -0
  552. package/ui/js/MdFront.js +37 -0
  553. package/ui/js/MdHold.js +32 -0
  554. package/ui/js/MdHtml.js +307 -0
  555. package/ui/js/MdHtmlBlock.js +290 -0
  556. package/ui/js/MdHtmlHeading.js +53 -0
  557. package/ui/js/MdHtmlImage.js +179 -0
  558. package/ui/js/MdInline.js +154 -0
  559. package/ui/js/MdItems.js +212 -0
  560. package/ui/js/MdLeaf.js +309 -0
  561. package/ui/js/MdLink.js +196 -0
  562. package/ui/js/MdMath.js +67 -0
  563. package/ui/js/MdParagraphs.js +138 -0
  564. package/ui/js/MdRefs.js +232 -0
  565. package/ui/js/MdResolve.js +226 -0
  566. package/ui/js/MdRun.js +300 -0
  567. package/ui/js/MdUrl.js +226 -0
  568. package/ui/js/Menu.js +514 -0
  569. package/ui/js/MenuContext.js +85 -0
  570. package/ui/js/MenuFit.js +13 -0
  571. package/ui/js/MenuRefresh.js +74 -0
  572. package/ui/js/MenuWheel.js +35 -0
  573. package/ui/js/Messages.js +186 -0
  574. package/ui/js/Motion.js +16 -0
  575. package/ui/js/Mounts.js +320 -0
  576. package/ui/js/MouseNav.js +13 -0
  577. package/ui/js/Nav.js +329 -0
  578. package/ui/js/NetFs.js +26 -0
  579. package/ui/js/OpenWith.js +83 -0
  580. package/ui/js/Ops.js +585 -0
  581. package/ui/js/Palette.js +33 -0
  582. package/ui/js/PathBar.js +260 -0
  583. package/ui/js/Permissions.js +179 -0
  584. package/ui/js/Phones.js +112 -0
  585. package/ui/js/Picker.js +353 -0
  586. package/ui/js/PlaceMenu.js +96 -0
  587. package/ui/js/Places.js +265 -0
  588. package/ui/js/PreviewAsk.js +21 -0
  589. package/ui/js/PreviewKeys.js +155 -0
  590. package/ui/js/PreviewSettle.js +29 -0
  591. package/ui/js/PreviewSwap.js +101 -0
  592. package/ui/js/PropertyFacts.js +144 -0
  593. package/ui/js/Protocols.js +296 -0
  594. package/ui/js/QuickLookKinds.js +27 -0
  595. package/ui/js/QuickLookRect.js +24 -0
  596. package/ui/js/RailDrop.js +51 -0
  597. package/ui/js/RailKeys.js +87 -0
  598. package/ui/js/RailMenu.js +133 -0
  599. package/ui/js/Recent.js +135 -0
  600. package/ui/js/RecentMode.js +156 -0
  601. package/ui/js/Reload.js +60 -0
  602. package/ui/js/Renderer.js +9 -0
  603. package/ui/js/RunFile.js +143 -0
  604. package/ui/js/Scripts.js +28 -0
  605. package/ui/js/Scroll.js +304 -0
  606. package/ui/js/ScrollOff.js +69 -0
  607. package/ui/js/Search.js +478 -0
  608. package/ui/js/Selection.js +97 -0
  609. package/ui/js/Settings.js +476 -0
  610. package/ui/js/SettingsAbout.js +97 -0
  611. package/ui/js/SettingsShelf.js +52 -0
  612. package/ui/js/SheetKeys.js +42 -0
  613. package/ui/js/SheetQuery.js +281 -0
  614. package/ui/js/ShelfPile.js +27 -0
  615. package/ui/js/SlowClick.js +53 -0
  616. package/ui/js/SlowOp.js +6 -0
  617. package/ui/js/Sort.js +134 -0
  618. package/ui/js/Startup.js +63 -0
  619. package/ui/js/StateWords.js +17 -0
  620. package/ui/js/Status.js +238 -0
  621. package/ui/js/Swap.js +166 -0
  622. package/ui/js/TabMove.js +39 -0
  623. package/ui/js/Tabs.js +751 -0
  624. package/ui/js/Tags.js +134 -0
  625. package/ui/js/Taildrop.js +128 -0
  626. package/ui/js/Tap.js +180 -0
  627. package/ui/js/TextSize.js +107 -0
  628. package/ui/js/Thumbs.js +158 -0
  629. package/ui/js/Transfer.js +172 -0
  630. package/ui/js/Trash.js +84 -0
  631. package/ui/js/TrashDates.js +47 -0
  632. package/ui/js/TrashDrop.js +64 -0
  633. package/ui/js/UiState.js +438 -0
  634. package/ui/js/Unlock.js +152 -0
  635. package/ui/js/Update.js +130 -0
  636. package/ui/js/UserDirs.js +56 -0
  637. package/ui/qmldir +230 -0
@@ -0,0 +1,144 @@
1
+ .pragma library
2
+ .import "Format.js" as Format
3
+ .import "Facts.js" as Facts
4
+
5
+ // The Properties card's Size and Contains lines for a folder: the walk's floor while it counts, the whole once it is done.
6
+ var TAIL = " · counting…"
7
+ var SIZE = {exact: "%1 (%2 bytes)", floor: "> %1 (%2 bytes)"}
8
+ var CONTAINS = {exact: ["%1 item", "%1 items"], floor: ["> %1 item", "> %1 items"]}
9
+
10
+ // Sample input: {bytes: 4096} from the properties reply; the walk's first floor is the folder's own entry.
11
+ function begin(facts) {
12
+ return {bytes: facts.bytes, items: 0, partial: false, done: false}
13
+ }
14
+
15
+ // A progress or final reply folded into the walk; a cancelled or refused walk ends as the floor already counted, so the card never counts forever.
16
+ function next(walk, reply) {
17
+ if (reply.cancelled)
18
+ return walk.done ? walk : {bytes: walk.bytes, items: walk.items, partial: true, done: true}
19
+ if (!reply.ok)
20
+ return {bytes: walk.bytes, items: walk.items, partial: true, done: true}
21
+ return {bytes: reply.bytes, items: reply.items, partial: reply.partial === true, done: reply.done === true}
22
+ }
23
+
24
+ // Sample input: size(13, null) is a file's own; size(0, walk) reads the walk. A partial walk stays a floor when done.
25
+ function size(bytes, walk) {
26
+ var shown = walk ? walk.bytes : bytes
27
+ var template = walk && (!walk.done || walk.partial) ? SIZE.floor : SIZE.exact
28
+ return {value: template.arg(Format.size(shown)).arg(String(shown)), tail: walk && !walk.done ? TAIL : ""}
29
+ }
30
+
31
+ function contains(walk) {
32
+ if (!walk)
33
+ return null
34
+ var forms = !walk.done || walk.partial ? CONTAINS.floor : CONTAINS.exact
35
+ return {value: forms[walk.items === 1 ? 0 : 1].arg(Format.count(walk.items)), tail: walk.done ? "" : TAIL}
36
+ }
37
+
38
+ function escape(text) {
39
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
40
+ }
41
+
42
+ // StyledText: the value in the Text's own colour and only the tail in `muted`, on the value's line.
43
+ function styled(line, muted) {
44
+ if (!line.tail)
45
+ return escape(line.value)
46
+ return "%1<font color=\"%2\">%3</font>".arg(escape(line.value)).arg(muted).arg(escape(line.tail))
47
+ }
48
+
49
+ // The names ffprobe prints for the codecs a card is likely to meet; one it does not know is shown upper-cased, as ffprobe says it.
50
+ var CODECS = {h264: "H.264", hevc: "H.265", vp8: "VP8", vp9: "VP9", av1: "AV1", mpeg4: "MPEG-4", mpeg2video: "MPEG-2", mpeg1video: "MPEG-1",
51
+ aac: "AAC", mp3: "MP3", opus: "Opus", vorbis: "Vorbis", flac: "FLAC", ac3: "AC-3", eac3: "E-AC-3", dts: "DTS", alac: "ALAC"}
52
+
53
+ function codecName(name) {
54
+ if (!name)
55
+ return ""
56
+ if (name.indexOf("pcm_") === 0)
57
+ return "PCM"
58
+ return CODECS[name] || name.toUpperCase()
59
+ }
60
+
61
+ // Sample input: codecs("h264", "aac") reads "H.264 · AAC"; a file with one stream names that one.
62
+ function codecs(video, audio) {
63
+ return [codecName(video), codecName(audio)].filter(function (name) { return name !== "" }).join(" · ")
64
+ }
65
+
66
+ // Sample input: 8100000 reads "8.1 Mb/s"; ffprobe's bits per second, decimal prefixes, whole kb/s below a megabit.
67
+ function bitRate(bps) {
68
+ if (!(bps > 0))
69
+ return ""
70
+ if (bps < 1000)
71
+ return Math.round(bps) + " b/s"
72
+ var kilo = Math.round(bps / 1000)
73
+ return kilo < 1000 ? kilo + " kb/s" : (bps / 1000000).toFixed(1) + " Mb/s"
74
+ }
75
+
76
+ // A camera writes the make's first word into the model on its own (Canon EOS R6, NIKON CORPORATION and NIKON Z 6) or not (SONY, ILCE-7M4).
77
+ function camera(make, model) {
78
+ if (!make || !model)
79
+ return model || make || ""
80
+ var brand = make.toLowerCase().split(/\s+/)[0]
81
+ return model.toLowerCase().indexOf(brand) === 0 ? model : make + " " + model
82
+ }
83
+
84
+ // A rational as [numerator, denominator], zero when the file named none or a zero denominator.
85
+ function ratio(pair) {
86
+ return pair && pair[0] > 0 && pair[1] > 0 ? pair[0] / pair[1] : 0
87
+ }
88
+
89
+ // Sample input: [1, 250] reads "1/250 s", [10, 600] "1/60 s", [6, 10] "0.6 s", [13, 10] "1.3 s"; a fraction whose numerator divides its denominator stays a 1/n.
90
+ function shutterText(pair, seconds) {
91
+ var num = pair[0], den = pair[1], tenths = Math.round(seconds * 10)
92
+ // A shutter that rounds to a whole second reads like one, so 0.96 s and 1 s both read "1 s".
93
+ if (tenths >= 10)
94
+ return String(tenths / 10) + " s"
95
+ if (den % num === 0 || num === 1)
96
+ return "1/" + (num === 1 ? den : den / num) + " s"
97
+ return tenths > 0 ? (tenths / 10).toFixed(1) + " s" : "1/" + Math.round(1 / seconds) + " s"
98
+ }
99
+
100
+ // Sample input: ([1, 250], [28, 10], 400) reads "1/250 s · f/2.8 · ISO 400"; a part the file lacks is left out.
101
+ function exposure(shutter, fnumber, iso) {
102
+ var seconds = ratio(shutter), stop = ratio(fnumber), parts = []
103
+ if (seconds > 0)
104
+ parts.push(shutterText(shutter, seconds))
105
+ if (stop > 0)
106
+ parts.push("f/" + String(Math.round(stop * 10) / 10))
107
+ if (iso > 0)
108
+ parts.push("ISO " + iso)
109
+ return parts.join(" · ")
110
+ }
111
+
112
+ // Sample input: "2026:09:12 18:31:07", EXIF's own stamp, reads "2026-09-12 18:31" like every other date on the card.
113
+ function taken(stamp) {
114
+ var m = /^(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):\d{2}/.exec(stamp || "")
115
+ return m && m[1] !== "0000" ? m[1] + "-" + m[2] + "-" + m[3] + " " + m[4] + ":" + m[5] : ""
116
+ }
117
+
118
+ // Empty strings, zero ISO and zero rationals are absent; present deferred EXIF fields win a disagreement.
119
+ function mergePhoto(photo, reply) {
120
+ var p = photo || {}, r = reply || {}
121
+ return {
122
+ make: r.make || p.make || "",
123
+ model: r.model || p.model || "",
124
+ lens: r.lens || p.lens || "",
125
+ shutter: ratio(r.shutter) > 0 ? r.shutter : p.exp,
126
+ fnumber: ratio(r.fnumber) > 0 ? r.fnumber : p.fnum,
127
+ iso: r.iso > 0 ? r.iso : p.iso,
128
+ taken: r.taken || p.taken || ""
129
+ }
130
+ }
131
+
132
+ // One merged photo, formatted once in the existing card language; codecs and bit rate stay with the facts reply.
133
+ function extraPairs(reply, photo) {
134
+ if (!reply || !reply.ok)
135
+ return []
136
+ var merged = mergePhoto(photo, reply)
137
+ var shown = photo ? Facts.photoFacts(Object.assign({}, merged, {exp: merged.shutter, fnum: merged.fnumber})) : {
138
+ camera: camera(merged.make, merged.model), lens: merged.lens,
139
+ exposure: exposure(merged.shutter, merged.fnumber, merged.iso), taken: taken(merged.taken)
140
+ }
141
+ return [["Codecs", codecs(reply.vcodec, reply.acodec)], ["Bit rate", bitRate(reply.bps)],
142
+ ["Camera", shown.camera], ["Lens", shown.lens], ["Exposure", shown.exposure], ["Taken", shown.taken]]
143
+ .filter(function (pair) { return pair[1] !== "" })
144
+ }
@@ -0,0 +1,296 @@
1
+ .pragma library
2
+
3
+ // The Add-network-place form is a form over a gvfs URI, never a second mount system: a protocol
4
+ // picks the scheme, prefills the port and swaps the field set, and the Mounts-as line always shows
5
+ // the exact URI the dialog will hand to gio mount.
6
+
7
+ // The five the canvas draws, in the order it draws them.
8
+ var PROTOCOLS = ["SMB", "SFTP", "FTPS", "WebDAV", "NFS"]
9
+
10
+ // Which fields each protocol asks for. "path" is the label the canvas gives that row, which differs
11
+ // per protocol because the thing it names differs: a share, a remote path, an export.
12
+ var FIELDS = {
13
+ "SMB": { pathLabel: "Share", credentials: true, domain: true, tls: false },
14
+ "SFTP": { pathLabel: "Path", credentials: true, domain: false, tls: false },
15
+ "FTPS": { pathLabel: "Path", credentials: true, domain: false, tls: true },
16
+ "WebDAV": { pathLabel: "Path", credentials: true, domain: false, tls: true },
17
+ // NFS has no credentials at all: the export is authorised by the server, not by a password.
18
+ "NFS": { pathLabel: "Export", credentials: false, domain: false, tls: false }
19
+ }
20
+
21
+ // The TLS box flips two schemes and defaults on, which is what the canvas draws ticked.
22
+ function scheme(protocol, tls) {
23
+ switch (protocol) {
24
+ case "SMB": return "smb"
25
+ case "SFTP": return "sftp"
26
+ case "FTPS": return tls ? "ftps" : "ftp"
27
+ case "WebDAV": return tls ? "davs" : "dav"
28
+ case "NFS": return "nfs"
29
+ }
30
+ return ""
31
+ }
32
+
33
+ function fieldsFor(protocol) {
34
+ return FIELDS[protocol] || FIELDS["SMB"]
35
+ }
36
+
37
+ // The port the form prefills. It belongs to the scheme, never to the protocol: WebDAV builds two
38
+ // schemes and SCHEME_PORTS below is the one table both this and the rail's dedup read it out of.
39
+ function defaultPort(protocol, tls) {
40
+ return defaultPortFor(scheme(protocol, tls))
41
+ }
42
+
43
+ // The exact URI gio mount will be handed. Empty when there is not enough to build one, so the
44
+ // Mounts-as line shows nothing rather than a half-formed address.
45
+ function uri(form) {
46
+ var host = String(form.host || "").trim()
47
+ var port = String(form.port || "").trim()
48
+ if (!validHost(host) || !validPort(port)) {
49
+ return ""
50
+ }
51
+ var spec = fieldsFor(form.protocol)
52
+ var head = scheme(form.protocol, form.tls) + "://"
53
+ if (spec.credentials) {
54
+ head += userPart(form, spec)
55
+ }
56
+ head += host
57
+ // Keep canvas defaults in the form, but omit them because GVFS NFS rejects an explicit :2049.
58
+ if (port.length > 0 && Number(port) !== defaultPort(form.protocol, form.tls)) {
59
+ head += ":" + port
60
+ }
61
+ return head + pathPart(form.path)
62
+ }
63
+
64
+ function validHost(host) {
65
+ return /^\[[0-9A-Fa-f:.]+\]$/.test(host) || /^[A-Za-z0-9._-]+$/.test(host)
66
+ }
67
+
68
+ function validPort(port) {
69
+ if (port.length === 0) {
70
+ return true
71
+ }
72
+ if (!/^[0-9]+$/.test(port)) {
73
+ return false
74
+ }
75
+ var number = Number(port)
76
+ return number >= 1 && number <= 65535
77
+ }
78
+
79
+ // smb://DOMAIN;user@host/share is the form a domain takes; without one it is just user@host.
80
+ function userPart(form, spec) {
81
+ var user = String(form.user || "").trim()
82
+ if (user.length === 0) {
83
+ return ""
84
+ }
85
+ var domain = spec.domain ? String(form.domain || "").trim() : ""
86
+ return (domain.length > 0 ? encodeURIComponent(domain) + ";" : "")
87
+ + encodeURIComponent(user) + "@"
88
+ }
89
+
90
+ function pathPart(path) {
91
+ var text = String(path || "").trim()
92
+ if (text.length === 0) {
93
+ return "/"
94
+ }
95
+ var parts = text.split("/")
96
+ for (var i = 0; i < parts.length; i++) {
97
+ parts[i] = encodeURIComponent(parts[i])
98
+ }
99
+ var encoded = parts.join("/")
100
+ return encoded.charAt(0) === "/" ? encoded : "/" + encoded
101
+ }
102
+
103
+ // The label the sidebar row will carry: what the operator typed, or the last part of the path.
104
+ function label(form) {
105
+ var given = String(form.label || "").trim()
106
+ if (given.length > 0) {
107
+ return given
108
+ }
109
+ var path = String(form.path || "").replace(/\/+$/, "")
110
+ var cut = path.lastIndexOf("/")
111
+ var leaf = cut >= 0 ? path.substring(cut + 1) : path
112
+ return leaf.length > 0 ? leaf : String(form.host || "").trim()
113
+ }
114
+
115
+ // A form with nothing to mount cannot be saved, which is what greys the Save row out.
116
+ function complete(form) {
117
+ var host = String(form.host || "").trim()
118
+ var port = String(form.port || "").trim()
119
+ return validHost(host) && validPort(port)
120
+ }
121
+
122
+ // The port each scheme drops, which is a property of the scheme and not of the protocol the form
123
+ // picked: WebDAV builds two schemes whose ports differ, and one number for both stripped a real
124
+ // ":443" off "dav://" and left its real ":80" on, the duplicate rail row this exists to remove.
125
+ // Measured on this box against gvfs 1.60.2 and glib2 2.88.3 by round-tripping each spelling through
126
+ // Gio.File, the uri mapper "gio mount -l" prints through: gio drops smb 445, sftp 22, ftp 21,
127
+ // ftps 21 (not IANA's 990), dav 80 and davs 443, and keeps every other port it is given, ftps 990
128
+ // and dav 443 included. It drops no nfs port at all, so 2049 is here for the other half of the job:
129
+ // the port an nfs client uses when the line omits one, so two spellings of one export make one row.
130
+ var SCHEME_PORTS = {
131
+ "smb": 445, "sftp": 22, "ftp": 21, "ftps": 21, "dav": 80, "davs": 443, "nfs": 2049
132
+ }
133
+
134
+ // A bookmarks line is arbitrary text, so an inherited Object member must never answer as a port.
135
+ function defaultPortFor(uriScheme) {
136
+ var want = String(uriScheme || "").toLowerCase()
137
+ return SCHEME_PORTS.hasOwnProperty(want) ? SCHEME_PORTS[want] : 0
138
+ }
139
+
140
+ // "gio mount -l" never reports a scheme's default port and the add form spells out the one it
141
+ // prefilled, so the same share arrives spelled two ways and must resolve to one rail row. The port
142
+ // is found in the authority alone: a ":" or an "@" further along belongs to the path.
143
+ function stripDefaultPort(uri) {
144
+ var text = String(uri || "")
145
+ var auth = authority(text)
146
+ var tail = ":" + defaultPortFor(schemeOf(text))
147
+ // A bracketed IPv6 literal ends in "]", so its own trailing digits can never match this tail.
148
+ if (auth.length === 0 || tail === ":0" || auth.substring(auth.length - tail.length) !== tail)
149
+ return text
150
+ var mark = text.indexOf("://") + 3
151
+ return text.substring(0, mark) + auth.substring(0, auth.length - tail.length)
152
+ + text.substring(mark + auth.length)
153
+ }
154
+
155
+ // The scheme a built or bookmarked URI carries, lowercased; "" when the text is not a URI at all.
156
+ function schemeOf(uri) {
157
+ var text = String(uri || "")
158
+ var mark = text.indexOf("://")
159
+ return mark < 0 ? "" : text.substring(0, mark).toLowerCase()
160
+ }
161
+
162
+ // Everything between "://" and the next "/", which is the only place a userinfo, a host or a port
163
+ // can live: a scan over the whole URI would read a "@" or a ":" inside the path as one of those.
164
+ function authority(uri) {
165
+ var text = String(uri || "")
166
+ var mark = text.indexOf("://")
167
+ if (mark < 0)
168
+ return ""
169
+ var rest = text.substring(mark + 3)
170
+ var slash = rest.indexOf("/")
171
+ return slash < 0 ? rest : rest.substring(0, slash)
172
+ }
173
+
174
+ // The host alone: userinfo dropped at the authority's own last "@", a bracketed IPv6 literal kept
175
+ // whole because its colons are not a port separator, and any real port dropped.
176
+ function hostOf(uri) {
177
+ var auth = authority(uri)
178
+ var at = auth.lastIndexOf("@")
179
+ var hostPort = at < 0 ? auth : auth.substring(at + 1)
180
+ if (hostPort.charAt(0) === "[") {
181
+ var close = hostPort.indexOf("]")
182
+ return close < 0 ? hostPort : hostPort.substring(0, close + 1)
183
+ }
184
+ var colon = hostPort.indexOf(":")
185
+ return colon < 0 ? hostPort : hostPort.substring(0, colon)
186
+ }
187
+
188
+ // gvfsd renders a network mount as "<share> <word> <host>" and translates that word, so issue #36's
189
+ // rule reads the URI instead: the tail that gets cut is this URI's own host, in any language, and a
190
+ // label that does not end in it is left exactly as gio gave it.
191
+ function shareName(rawLabel, uri) {
192
+ var text = String(rawLabel || "")
193
+ var host = hostOf(uri)
194
+ var head = text.substring(0, text.length - host.length)
195
+ if (host.length === 0 || head.length === 0 || text.substring(head.length) !== host)
196
+ return text
197
+ // The one assumption left after the host check: gvfsd's connector is a single whitespace-delimited
198
+ // word in whatever language it renders. A translation using two would leave one of them on the
199
+ // name, a wrong label and never a wrong destination; a head this does not shorten is left whole.
200
+ var name = head.replace(/\s+\S+\s+$/, "")
201
+ return name.length > 0 && name !== head ? name : text
202
+ }
203
+
204
+ // Sample input: smb://dan@192.168.1.75/test, the reporter's share, and its FUSE folder
205
+ // /run/user/1000/gvfs/smb-share:server=192.168.1.75,share=test,user=dan. A FUSE path for
206
+ // another user, another share or another server must never open as this share's listing:
207
+ // a mount problem answers as one instead of reading like an unreadable directory.
208
+ function decodeSegment(raw) {
209
+ try { return decodeURIComponent(String(raw)) } catch (e) { return String(raw) }
210
+ }
211
+
212
+ // The smb:// request as gio sees it: the user past any DOMAIN; prefix, the host without
213
+ // a port, and the share as the first path segment. Null when not an smb:// URI at all.
214
+ function smbParts(uri) {
215
+ if (schemeOf(uri) !== "smb") return null
216
+ var auth = authority(uri)
217
+ if (auth.length === 0) return null
218
+ var at = auth.lastIndexOf("@")
219
+ var info = at < 0 ? "" : auth.substring(0, at)
220
+ var semi = info.indexOf(";")
221
+ var user = semi < 0 ? info : info.substring(semi + 1)
222
+ var mark = String(uri).indexOf("://") + 3
223
+ var rest = String(uri).substring(mark + auth.length)
224
+ if (rest.charAt(0) === "/") rest = rest.substring(1)
225
+ var cut = rest.indexOf("/")
226
+ var share = cut < 0 ? rest : rest.substring(0, cut)
227
+ return { user: decodeSegment(user), host: hostOf(uri).toLowerCase(), share: decodeSegment(share) }
228
+ }
229
+
230
+ // The smb-share: FUSE directory gio info prints a local path for, or null when the path
231
+ // is no smb FUSE directory at all. Null is undecided, never a mismatch: an unknown shape
232
+ // opens exactly as before rather than refusing a mount over a format this never learned.
233
+ function fuseSmbParts(path) {
234
+ var m = String(path || "").match(/smb-share:server=([^,\/]+),share=([^,\/]+)(?:,user=([^,\/]+))?/)
235
+ if (!m) return null
236
+ return { host: String(m[1]).toLowerCase(), share: m[2], user: m[3] || "" }
237
+ }
238
+
239
+ // Whether gio info's answer belongs to the smb:// request that asked for it. True for any
240
+ // non-smb URI and whenever either side will not parse; false only on a parsed mismatch, a
241
+ // user the answer drops or changes above all, so that answer is refused as a mount failure.
242
+ function smbFuseMatches(uri, fusePath) {
243
+ if (schemeOf(uri) !== "smb") return true
244
+ var want = smbParts(uri)
245
+ var got = fuseSmbParts(fusePath)
246
+ if (!want || !got) return true
247
+ var wantUser = String(want.user || "")
248
+ var gotUser = String(got.user || "")
249
+ var semi = gotUser.indexOf(";")
250
+ if (semi >= 0) gotUser = gotUser.substring(semi + 1)
251
+ return want.host === got.host
252
+ && String(want.share).toLowerCase() === String(got.share).toLowerCase()
253
+ && wantUser.toLowerCase() === gotUser.toLowerCase()
254
+ }
255
+
256
+ // Sample input: uri smb://dan@192.168.1.75/test with gio info printing the path without
257
+ // ,user=dan while only the user=dan folder exists. Answers the entry whose user the URI
258
+ // names; with no user the single share match wins, several prefer the desktop login.
259
+ function smbResolve(uri, fusePaths, login) {
260
+ var found = { path: "", users: [] }
261
+ var want = smbParts(uri)
262
+ if (!want) return found
263
+ var hits = []
264
+ var list = fusePaths || []
265
+ for (var i = 0; i < list.length; i++) {
266
+ var got = fuseSmbParts(list[i])
267
+ if (!got || got.host !== want.host) continue
268
+ if (String(got.share).toLowerCase() !== String(want.share).toLowerCase()) continue
269
+ hits.push({ path: list[i], user: String(got.user || "") })
270
+ }
271
+ if (String(want.user || "").length > 0) {
272
+ for (var j = 0; j < hits.length; j++) {
273
+ if (hits[j].user.toLowerCase() === String(want.user).toLowerCase()) {
274
+ found.path = hits[j].path
275
+ return found
276
+ }
277
+ }
278
+ return found
279
+ }
280
+ if (hits.length === 1) {
281
+ found.path = hits[0].path
282
+ return found
283
+ }
284
+ if (hits.length > 1) {
285
+ for (var k = 0; k < hits.length; k++) {
286
+ if (hits[k].user.toLowerCase() === String(login || "").toLowerCase()) {
287
+ found.path = hits[k].path
288
+ return found
289
+ }
290
+ }
291
+ for (var m = 0; m < hits.length; m++) {
292
+ if (found.users.indexOf(hits[m].user) < 0) found.users.push(hits[m].user)
293
+ }
294
+ }
295
+ return found
296
+ }
@@ -0,0 +1,27 @@
1
+ .pragma library
2
+
3
+ .import "Kinds.js" as Kinds
4
+
5
+ // Which compilation unit Quick Look opens a row's content from, for ui/QuickLookWarm.qml to hold compiled before the first Space.
6
+ var MARKDOWN = "MarkdownPane.qml"
7
+ var PDF = "PdfViewer.qml"
8
+ var MEDIA = "PreviewMedia.qml"
9
+ var IMAGE = "PreviewImage.qml"
10
+ var TEXT = "PreviewText.qml"
11
+
12
+ // Sample input: { n: "a.pdf", i: "x-office-document" } answers "PdfViewer.qml", { n: "a.mp4", i: "video-x-generic" } "PreviewMedia.qml", { n: "a.png", i: "image-x-generic" } "PreviewImage.qml", { n: "a.md", i: "text-x-generic" } "MarkdownPane.qml".
13
+ // A folder, an archive, an unsupported file and null answer "" because no unit of their own is compiled for them.
14
+ function unitOf(row) {
15
+ if (row === null || row === undefined || row.d === true)
16
+ return ""
17
+ if (Kinds.isMarkdown(row.n))
18
+ return MARKDOWN
19
+ switch (Kinds.quickLookKind(row.i, row.n, row.t === true)) {
20
+ case Kinds.PDF: return PDF
21
+ case Kinds.VIDEO:
22
+ case Kinds.AUDIO: return MEDIA
23
+ case Kinds.IMAGE: return IMAGE
24
+ case Kinds.TEXT: return TEXT
25
+ }
26
+ return ""
27
+ }
@@ -0,0 +1,24 @@
1
+ .pragma library
2
+
3
+ // Sample input: surface({ kind: "pdf", known: true, w: 595, h: 842, orient: 1 }, 1200, 760, false, { fraction: 0.82, edge: 24, bar: 27 }) is { w: 484.06, h: 712 }.
4
+ // The want before Theme.cardSpan rounds it. A shape is the kind plus its one fact: a PDF's page 1 size or an image's pixels and EXIF orientation, known false while unasked or unanswered.
5
+ function surface(shape, winW, winH, expanded, tokens) {
6
+ var today = { w: winW * tokens.fraction, h: winH * tokens.fraction }
7
+ if (expanded)
8
+ return { w: winW, h: winH }
9
+ var tallH = winH - 2 * tokens.edge
10
+ var seen = shape.known === true && shape.w > 0 && shape.h > 0
11
+ if (shape.kind === "text" || shape.kind === "archive")
12
+ return { w: today.w, h: tallH }
13
+ if (shape.kind === "pdf" && seen)
14
+ return { w: Math.min(today.w, (tallH - tokens.bar) * shape.w / shape.h), h: tallH }
15
+ if (shape.kind === "image" && seen) {
16
+ var turned = shape.orient >= 5
17
+ var ow = turned ? shape.h : shape.w
18
+ var oh = turned ? shape.w : shape.h
19
+ // A portrait shorter than the tall rect keeps today's frame rather than being stretched to it.
20
+ if (oh > ow && oh >= tallH)
21
+ return { w: Math.min(today.w, tallH * ow / oh), h: tallH }
22
+ }
23
+ return today
24
+ }
@@ -0,0 +1,51 @@
1
+ .pragma library
2
+
3
+ .import "Drag.js" as Drag
4
+ .import "DragOut.js" as DragOut
5
+ .import "DropDev.js" as DropDev
6
+
7
+ var sequence = 0
8
+
9
+ // Sample input: a mounted volume at "/run/media/u/USB/" names "/run/media/u/USB"; a phone root names nothing.
10
+ function destination(entry, marker, urls, shelf, plain) {
11
+ if (!entry || entry.kind === "recent" || entry.kind === "trash" || entry.kind === "tag"
12
+ || entry.kind === "phone" || entry.mounted === false) return ""
13
+ var path = DropDev.normalize(entry.path)
14
+ if (path.charAt(0) !== "/" || /^\/run\/user\/[0-9]+\/gvfs\/(mtp|gphoto2|afc):[^/]+$/i.test(path)) return ""
15
+ if (marker && !Drag.markerFields(marker)) return ""
16
+ var paths = DragOut.sources(urls, plain, marker, shelf)
17
+ if (paths.length === 0 || DragOut.refusal(paths, path, marker, shelf) !== "") return ""
18
+ return path
19
+ }
20
+
21
+ // The rail's existing rows supply geometry, including the scroller's offset; headings name no row.
22
+ function rowAt(sidebar, x, y) {
23
+ for (var i = 0; i < sidebar.entries.length; i++) {
24
+ var row = sidebar.railItemFor(i)
25
+ if (!row || !row.visible) continue
26
+ var at = row.mapFromItem(sidebar, x, y)
27
+ if (at.x >= 0 && at.y >= 0 && at.x < row.width && at.y < row.height) return i
28
+ }
29
+ return -1
30
+ }
31
+
32
+ // A new hover never borrows cached device facts from an earlier mount or gesture.
33
+ function pending(path) {
34
+ sequence += 1
35
+ return { path: path, id: sequence, dev: 0 }
36
+ }
37
+
38
+ // Sample input: answer({path:"/usb",id:7,dev:0}, 7, {"/usb":42}) names device 42; a superseded id leaves it unknown.
39
+ function answer(held, id, devs) {
40
+ if (!held || held.id !== id || !Object.prototype.hasOwnProperty.call(devs || {}, held.path)) return held
41
+ return { path: held.path, id: held.id, dev: DropDev.devFor(devs, held.path) }
42
+ }
43
+
44
+ // The ordinary folder transfer asks the collision host first, which owns the card and the undo path.
45
+ function drop(pane, entry, held, marker, urls, shelf, plain, proposed, ctrl, shift) {
46
+ if (!pane || !pane.backend || pane.listInFlight) return false
47
+ var path = destination(entry, marker, urls, shelf, plain)
48
+ if (!path) return false
49
+ return Drag.dropInto(pane, marker, urls, path, held && held.path === path ? held.dev : 0,
50
+ shelf, plain, proposed, ctrl, shift)
51
+ }
@@ -0,0 +1,87 @@
1
+ .pragma library
2
+
3
+ .import "Eject.js" as Eject
4
+ .import "Mounts.js" as Mounts
5
+ .import "Nav.js" as Nav
6
+
7
+ // What the rail does with a key, split out of Focus.js at its 300-line hard cap the same way
8
+ // ui/js/PreviewKeys.js was: Focus.js decides which surface owns a key, and this is the surface.
9
+
10
+ // A mount-first Enter's open has landed in root: focus follows it into the folder unless the user already left the rail.
11
+ function landed(root, sidebar) {
12
+ if (!sidebar || !sidebar.focusOnOpen) return
13
+ sidebar.focusOnOpen = false
14
+ if (root.focusView === "rail") root.focusView = "list"
15
+ }
16
+
17
+ // A Sidebar open (opened or networkOpened): the pane that asked opens it, and a waiting claim lands; no pane spends the claim.
18
+ function openFrom(pane, path, sidebar) {
19
+ if (!pane) { if (sidebar) sidebar.focusOnOpen = false; return }
20
+ Nav.openPlace(pane, path)
21
+ if (sidebar) landed(pane, sidebar)
22
+ }
23
+
24
+ // A mount that failed has no open to land, so its focus claim goes with the error.
25
+ function messaged(sidebar, isError) {
26
+ if (isError && sidebar) sidebar.focusOnOpen = false
27
+ }
28
+
29
+ // The keyboard cursor takes the rail's highlight only while the keyboard holds it, never under an open rail menu or after a pointer press until a rail key.
30
+ function cursorDrawn(focused, menuOpen, pointerFocus) {
31
+ return Boolean(focused && !menuOpen && !pointerFocus)
32
+ }
33
+
34
+ // Right Arrow from a row whose mark releases moves the keyboard onto that mark, read raw because the table resolves Right to opening the row in every preset.
35
+ function markKey(event, root, sidebar) {
36
+ if (event && (event.modifiers & (Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier)) !== 0) return false
37
+ var key = event ? event.key : 0
38
+ if (!sidebar.markFocused) {
39
+ if (key !== Qt.Key_Right || !Eject.releasable(sidebar.entries[sidebar.cursorIndex])) return false
40
+ sidebar.markFocused = true
41
+ return true
42
+ }
43
+ if (key === Qt.Key_Left || key === Qt.Key_Escape) sidebar.markFocused = false
44
+ else if (key === Qt.Key_Return || key === Qt.Key_Enter || key === Qt.Key_Space) {
45
+ sidebar.markFocused = false
46
+ Eject.release(root, sidebar, true)
47
+ } else if (key !== Qt.Key_Right) return false
48
+ return true
49
+ }
50
+
51
+ // The rail answers ten of the key table's action names and ignores the rest while it has focus; event is the key itself, for the ones the table does not name.
52
+ function act(action, root, sidebar, event) {
53
+ // Any other rail key ends a mount-first open's claim on focus and restores the keyboard highlight a pointer press hid.
54
+ sidebar.focusOnOpen = false
55
+ sidebar.pointerFocus = false
56
+ if (markKey(event, root, sidebar)) return
57
+ // A cursor key leaves the mark for the rows, even on the first or last row where the cursor stays.
58
+ if (action.indexOf("cursor") === 0) sidebar.markFocused = false
59
+ switch (action) {
60
+ case "cursorDown": sidebar.cursorIndex = Math.min(sidebar.entries.length - 1, sidebar.cursorIndex + 1); return
61
+ case "cursorUp": sidebar.cursorIndex = Math.max(0, sidebar.cursorIndex - 1); return
62
+ // The sheet advertises g and G as first and last row, and the rail is a cursored list too, so
63
+ // they answered nothing here while every other cursor key worked.
64
+ case "cursorFirst": sidebar.cursorIndex = 0; return
65
+ case "cursorLast": sidebar.cursorIndex = Math.max(0, sidebar.entries.length - 1); return
66
+ // activate(), not opened(path), so a Network entry mounts first and focus follows the open into the folder.
67
+ case "open":
68
+ if (sidebar.entries.length === 0) return
69
+ // Focus follows the open only when it lands (ui/PaneRail.qml), since a mount or a share's gio info can take long or never answer.
70
+ sidebar.focusOnOpen = true
71
+ sidebar.activate(sidebar.cursorIndex)
72
+ return
73
+ // Focus.LIST's own value, written out because importing Focus.js back would be a cycle.
74
+ case "escape":
75
+ if (root.statusBar && root.statusBar.escapePressed()) return
76
+ root.focusView = "list"; return
77
+ case "addNetwork": sidebar.addRequested(); return
78
+ // Favorites are not offered: Sidebar.startRename ignores an index outside the Network group.
79
+ case "rename": sidebar.startRename(sidebar.cursorIndex); return
80
+ // Eject and Unmount are menu rows, so this opens the menu rather than inventing a second route.
81
+ case "menu":
82
+ if (sidebar.entries[sidebar.cursorIndex] && sidebar.entries[sidebar.cursorIndex].kind === "trash") sidebar.openCursorMenu()
83
+ else Mounts.raiseMenu(root, sidebar)
84
+ return
85
+ case "eject": Eject.release(root, sidebar, true); return
86
+ }
87
+ }