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,913 @@
1
+ import QtQuick
2
+ import Quickshell
3
+ import Quickshell.Io
4
+ import "js/Errors.js" as Errors
5
+ import "js/Cloud.js" as Cloud
6
+ import "js/Crumbs.js" as Crumbs
7
+ import "js/Format.js" as Format
8
+ import "js/Mounts.js" as Mounts
9
+ import "js/Protocols.js" as Protocols
10
+ import "js/Dropbox.js" as Dropbox
11
+
12
+ // OEM-shaped Network service: nothing but this file, its two children and ui/PhoneMounts.qml, which
13
+ // only unmounts off this listing, touches gio or the saved places file; Sidebar renders their rows.
14
+ Item {
15
+ id: root
16
+
17
+ property string bookmarksText: ""
18
+ property var savedFavourites: []
19
+ onSavedFavouritesChanged: root.rebuild()
20
+ property var backend: null
21
+ property var entries: []
22
+ // Retained across unmounts: the same GVFS path denotes the same share; navigation still uses the real path.
23
+ property var pathAliases: []
24
+ property var _aliasLabels: ({})
25
+ // A bridge wait outlives _pending*, so the identity it opened under waits here by path until the check lands.
26
+ property var _bridgeIdentities: ({})
27
+ function pathAlias(path) { return Crumbs.aliasAt(path, root.pathAliases) }
28
+ function rememberPathAlias(path, label, uri) {
29
+ var key = Mounts.normalize(uri)
30
+ root.pathAliases = Crumbs.rememberAlias(root.pathAliases, path, root._aliasLabels[key] || label, key)
31
+ }
32
+ // Secrets live only here for this QML process lifetime; the map is never serialized or exposed.
33
+ property var _passwords: ({})
34
+ property string result: "idle"
35
+ property Item origin: null
36
+ property Item _pendingOrigin: null
37
+
38
+ signal opened(string path, var origin)
39
+ // A FUSE path that is a file, not a folder: the opener takes it, so a typed network URL
40
+ // naming a file is never listed as a folder.
41
+ signal openFileRequested(string path, var origin)
42
+ signal message(string text, bool isError)
43
+ // The bridge wait's own sticky line, cleared with "" when the folder lands or fails.
44
+ signal sticky(string text, var origin)
45
+ // The GVFS bridge's Starting line, which the status bar marks busy; a clear still goes through sticky.
46
+ signal startingBridge(string text, var origin)
47
+ // Client-side only, see "listShares" below: ui/ShareBrowser.qml renders these as pane rows.
48
+ signal sharesListed(string baseUri, string baseLabel, var names, var origin)
49
+ // Fired once ui/NetworkPlaces.qml's write has actually landed, so a caller's reload reads it.
50
+ signal renamed()
51
+ signal retryRequested(string uri, string label, string password, string reason, bool failedConnect, var origin)
52
+ // connectFailed says the connection itself failed (refused, unanswered, no folder), so the dialog marks the location unacknowledged without reading the sentence.
53
+ signal completed(string requestId, string uri, bool success, string reason, bool connectFailed)
54
+
55
+ // Hyprland has no auth portal here, so "gio mount" on a share that wants a credential prompt
56
+ // hangs forever with no stdin to answer it, and "gio info" on a location gvfs cannot reach does
57
+ // the same. Every gio leg of an open gets this bound; the helper leg has its own below.
58
+ readonly property int mountTimeoutMs: 15000
59
+ // The frozen helper has its own 30 s deadline; this outer bound also contains a broken test override.
60
+ readonly property int authTimeoutSeconds: 35
61
+ // Issue #36: every gio call this Service starts is pinned to C, so the wording of the ones it
62
+ // reads output from (info, list, and the listing in ui/MountListing.qml) cannot be translated.
63
+ // gvfsd composes a mount's own label and its refusals, and no client locale reaches those, which
64
+ // is why nothing below decides anything on one.
65
+ readonly property var gioEnvironment: ({ "LC_ALL": "C" })
66
+ property string _mountListing: ""
67
+ // What the rail waits for before it draws NETWORK: the shared gio listing's own answer, timed out or not.
68
+ readonly property bool listingAnswered: listing.answered
69
+ // ui/PhoneMounts.qml builds its rows off the same five second poll rather than walking the gvfs volume monitors a second time.
70
+ readonly property alias mountListing: root._mountListing
71
+ property string _pendingUri: ""
72
+ // OEM collectors cache finished output because onExited can race their text property.
73
+ property string _infoOutput: ""
74
+ property string _listSharesOutput: ""
75
+ // Set right before mountTimeout terminates that leg's process, so its own onExited does not
76
+ // also report a second, redundant failure for the exact same open attempt.
77
+ property bool _mountTimedOut: false
78
+ property bool _infoTimedOut: false
79
+ property bool _listSharesTimedOut: false
80
+ // Set from "gio mount"'s own exit code and consumed by the "gio info" that follows it, which is
81
+ // what actually decides whether the location is mounted; this only colours the failure message.
82
+ property bool _mountFailed: false
83
+ property string _pendingUnmountLabel: ""
84
+ // The entry's own label at activation time, carried through to the sharesListed signal.
85
+ property string _pendingLabel: ""
86
+ property string _pendingPassword: ""
87
+ property bool _authAwaitingStart: false
88
+ property bool _authCancelled: false
89
+ property string _requestId: ""
90
+ property string _requestPassword: ""
91
+ // Issue 194's repair: gio info answered with a folder that is not the requested share.
92
+ // Its own path is peeked first, then the FUSE root it names, all through backend.peek,
93
+ // so no new poll is added and a second open waits on the same single-flight guard.
94
+ // Only a root that peeks empty or unreadable starts the bridge for the gvfs root, then
95
+ // peeks once under the same deadline; the #194 folder itself does not exist, so waiting
96
+ // on it first costs 15 s before failing. _repairWaiting covers that bridge leg,
97
+ // _repairActive the peek legs, _repairBridged the one bridge start a repair may spend.
98
+ property bool _repairWaiting: false
99
+ property string _repairUri: ""
100
+ property string _repairPath: ""
101
+ property string _repairRoot: ""
102
+ property bool _repairFailed: false
103
+ property bool _repairActive: false
104
+ property bool _repairBridged: false
105
+
106
+ onBookmarksTextChanged: root.rebuild()
107
+
108
+ property string dropboxPath: ""
109
+ onDropboxPathChanged: root.rebuild()
110
+ property string dropboxReason: "Checking Dropbox"
111
+ // What state the reason is, for logic: ui/js/Dropbox.js names the codes.
112
+ property string dropboxCode: ""
113
+ property bool dropboxChecking: false
114
+ property bool _dropboxAwaitingStart: false
115
+ property string _dropboxOutput: ""
116
+ property string _dropboxError: ""
117
+ // OEM dropbox/status.py uses a four-second daemon status deadline.
118
+ readonly property int dropboxStatusTimeoutSeconds: 4
119
+ signal dropboxRefreshed()
120
+ // Any finished status check answers, whatever it said; the providers' determination is only the startup answer.
121
+ onDropboxRefreshed: root.dropboxAnswered = true
122
+ // What the rail waits for from Dropbox: the providers' determination, or a finished status check.
123
+ property bool dropboxAnswered: false
124
+ readonly property bool dropboxReady: dropboxPath.length > 0 && dropboxReason.length === 0 && !dropboxChecking
125
+ property int _dropboxMetadataRequest: 0
126
+ property bool _dropboxMetadataAgain: false
127
+
128
+ function readDropboxAccount(facts) {
129
+ if (!facts || facts.dropboxInfo === undefined) return
130
+ root.dropboxAnswered = true
131
+ var account = Dropbox.account(facts.dropboxInfo, facts.dropboxError)
132
+ if (dropboxPath !== account.path || account.reason) {
133
+ dropboxReason = account.reason || "Checking Dropbox"
134
+ dropboxCode = account.code
135
+ }
136
+ dropboxPath = account.path
137
+ }
138
+
139
+ function refreshDropboxAccount() {
140
+ if (!backend) return
141
+ if (_dropboxMetadataRequest) { _dropboxMetadataAgain = true; return }
142
+ _dropboxMetadataRequest = backend.askFormats()
143
+ }
144
+
145
+ function rearmDropboxAccount() {
146
+ dropboxAccountFile.path = Quickshell.env("HOME") + "/.dropbox/info.json"
147
+ root.refreshDropboxAccount()
148
+ }
149
+
150
+ onBackendChanged: if (backend) root.readDropboxAccount(backend.providers)
151
+ Connections {
152
+ target: root.backend
153
+ function onProvidersChanged() { root.readDropboxAccount(root.backend.providers) }
154
+ function onFormatsResult(message) {
155
+ if (message.id !== root._dropboxMetadataRequest) return
156
+ root._dropboxMetadataRequest = 0
157
+ if (root._dropboxMetadataAgain) {
158
+ root._dropboxMetadataAgain = false
159
+ root.refreshDropboxAccount()
160
+ }
161
+ }
162
+ // Issue 194's repair answers here. The chrome answers only the Tab request its own key
163
+ // names and the columns view only ever reads the ancestors it asked for, so sharing the
164
+ // peek wire costs those readers nothing they would otherwise notice.
165
+ function onPeeked(path, hidden, total, rows, readFailed, mode) { root.repairPeeked(path, rows, readFailed) }
166
+ }
167
+
168
+ function refreshDropbox(facts) {
169
+ if (dropboxChecking) return false
170
+ var provider = facts.dropbox || {}
171
+ root.readDropboxAccount(facts)
172
+ if (provider.reason || dropboxPath) dropboxCode = ""
173
+ dropboxReason = provider.reason || (dropboxPath ? "Checking Dropbox" : dropboxReason)
174
+ if (!provider.command || !dropboxPath) { root.dropboxAnswered = true; return true }
175
+ dropboxChecking = true
176
+ _dropboxAwaitingStart = true
177
+ _dropboxOutput = ""
178
+ _dropboxError = ""
179
+ dropboxStatus.command = ["timeout", "--signal=KILL", String(dropboxStatusTimeoutSeconds), provider.command, "status"]
180
+ dropboxStatus.running = true
181
+ return true
182
+ }
183
+
184
+ Process {
185
+ id: dropboxStatus
186
+ environment: root.gioEnvironment
187
+ stdout: StdioCollector { id: dropboxOut; waitForEnd: true; onStreamFinished: root._dropboxOutput = text }
188
+ stderr: StdioCollector { id: dropboxErr; waitForEnd: true; onStreamFinished: root._dropboxError = text }
189
+ onStarted: root._dropboxAwaitingStart = false
190
+ onRunningChanged: {
191
+ if (root._dropboxAwaitingStart && !running) {
192
+ root._dropboxAwaitingStart = false
193
+ root.dropboxChecking = false
194
+ root.dropboxReason = "Dropbox could not be checked"
195
+ root.dropboxCode = ""
196
+ root.dropboxRefreshed()
197
+ }
198
+ }
199
+ onExited: function(exitCode) {
200
+ root._dropboxAwaitingStart = false
201
+ var answer = exitCode === 137 || exitCode === 9 ? { reason: "Dropbox did not answer in time", code: "statusFailed" }
202
+ : Dropbox.status(dropboxOut.text || root._dropboxOutput, exitCode, dropboxErr.text || root._dropboxError)
203
+ root.dropboxReason = answer.reason
204
+ root.dropboxCode = answer.code
205
+ root.dropboxChecking = false
206
+ root.dropboxRefreshed()
207
+ }
208
+ }
209
+
210
+ FileView {
211
+ id: dropboxAccountFile
212
+ path: Quickshell.env("HOME") + "/.dropbox/info.json"
213
+ // Watch only; providers.rs owns the regular-file check and bounded metadata read.
214
+ preload: false
215
+ watchChanges: true
216
+ printErrors: false
217
+ onFileChanged: Qt.callLater(root.refreshDropboxAccount)
218
+ }
219
+
220
+ FileView {
221
+ // A FileView directory watch does not report its own removal; its parent does.
222
+ path: Quickshell.env("HOME")
223
+ preload: false
224
+ watchChanges: true
225
+ printErrors: false
226
+ onFileChanged: {
227
+ // FileView must observe the empty path for an event turn before it can re-arm a new parent.
228
+ dropboxAccountFile.path = ""
229
+ Qt.callLater(root.rearmDropboxAccount)
230
+ }
231
+ }
232
+
233
+ // The five second "gio mount -li" poll is ui/MountListing.qml's: this Service reads its listing and asks for a re-read through pollMounts() below.
234
+ // How many rails are loaded now; an open or a bridge wait also holds the poll, so a
235
+ // hidden rail costs no gio while a mount in flight still refreshes behind it.
236
+ property int _rails: 0
237
+ // The listing's timer polls on start when this arrival activates it, so only an already active one is asked again.
238
+ function railArrived() { var wasActive = listing.active; root._rails++; if (wasActive) root.pollMounts() }
239
+ function railLeft() { root._rails = Math.max(0, root._rails - 1) }
240
+ MountListing {
241
+ id: listing
242
+ environment: root.gioEnvironment
243
+ active: root._rails > 0 || mountProcess.running || authProcess.running || infoProcess.running
244
+ || listSharesProcess.running || root._repairActive || root._repairWaiting || root.bridgeWaiting
245
+ // Cloud rows ride the same five second rhythm: the mount table is a file read, so
246
+ // nothing new runs. The read is asynchronous: /proc has no write of ours to wait for,
247
+ // and a blocking read every tick stalled the GUI thread mid-scroll.
248
+ onListed: { root._polledListing = listing.text; root._listedOnce = true; cloudFile.reload() }
249
+ }
250
+
251
+ // The last texts a rebuild parsed; see ui/js/Mounts.js pollDecision for the change gate.
252
+ property string _polledListing: ""
253
+ property bool _listedOnce: false
254
+ property string _lastMountListing: ""
255
+ property string _lastMountinfo: ""
256
+ function pollAnswered() {
257
+ var infoText = cloudFile.text()
258
+ if (Mounts.pollDecision(root._listedOnce, root._polledListing, root._lastMountListing, infoText, root._lastMountinfo) !== "rebuild")
259
+ return
260
+ root._lastMountListing = root._polledListing
261
+ root._lastMountinfo = infoText
262
+ root._mountListing = root._polledListing
263
+ root.rebuild()
264
+ }
265
+
266
+ // /proc/self/mountinfo, read on the listing's own poll for ui/js/Cloud.js's FUSE rows.
267
+ // A FileView watch is not set up: proc files report no change events, so the poll drives it.
268
+ FileView {
269
+ id: cloudFile
270
+ path: "/proc/self/mountinfo"
271
+ printErrors: false
272
+ onLoaded: root.pollAnswered()
273
+ onLoadFailed: root.pollAnswered()
274
+ }
275
+
276
+ // The saved places file is ui/NetworkPlaces.qml's, the only writer of it in this Service.
277
+ NetworkPlaces {
278
+ id: places
279
+ entries: root.entries
280
+ onMessage: function (text, isError) { root.message(text, isError) }
281
+ onWrote: root.renamed()
282
+ }
283
+
284
+ // Phones and shares open through the GVFS FUSE bridge, hosted window-long rather than
285
+ // in the rail: hiding the rail mid-wait kills no wait, and the ready, the failure and the
286
+ // Starting line it showed all still land. Files land on openFileRequested, so a typed
287
+ // network URL naming a file is never listed as a folder.
288
+ Loader {
289
+ id: bridgeLoader
290
+ active: false // built on first ensure, so launch pays no Timers, Process or compile
291
+ source: "GvfsBridge.qml"
292
+ }
293
+ // Null until the first ensure; every reader below guards it.
294
+ readonly property var bridge: bridgeLoader.item
295
+ readonly property bool bridgeBuilt: bridgeLoader.active
296
+ // Whether a bridge wait is in flight, so the listing poll holds while one stands.
297
+ readonly property bool bridgeWaiting: root.bridge !== null && root.bridge.flow.waiter !== null
298
+ // Synchronous: a local source: URL answers item on the same call that sets active.
299
+ function ensureBridge() {
300
+ if (!bridgeLoader.active)
301
+ bridgeLoader.active = true
302
+ return bridgeLoader.item
303
+ }
304
+ // A null bridge is a missing component, not a refused mount, so the reason says Flea did not load.
305
+ function bridgeMissingReason() {
306
+ return Format.fill(Mounts.WORDS.bridgeMissing, root._pendingLabel || root._pendingUri)
307
+ }
308
+ // The log warning names the file and the Loader status, and the wait ends with no retry.
309
+ function failBridgeMissing() {
310
+ console.warn("GvfsBridge.qml did not load: Loader status " + bridgeLoader.status)
311
+ if (root._requestPassword.length > 0) root.remember(root._pendingUri, root._requestPassword)
312
+ mountTimeout.stop()
313
+ root._repairActive = false
314
+ root._repairWaiting = false
315
+ root._repairBridged = false
316
+ root._repairRoot = ""
317
+ root._repairPath = ""
318
+ root.result = "failed"
319
+ var reason = root.bridgeMissingReason()
320
+ root.message(reason, true)
321
+ root.finishRequest(false, reason)
322
+ }
323
+ Connections {
324
+ target: root.bridge
325
+ function onReady(path, origin, isDir) {
326
+ if (root._repairWaiting) {
327
+ root._repairWaiting = false
328
+ root.sticky("", origin)
329
+ root._repairActive = true
330
+ mountTimeout.restart()
331
+ root.backend.peek(root._repairRoot, 512, false)
332
+ return
333
+ }
334
+ root.sticky("", origin)
335
+ var held = root._bridgeIdentities[path]
336
+ delete root._bridgeIdentities[path]
337
+ if (!isDir) {
338
+ root.openFileRequested(path, origin)
339
+ return
340
+ }
341
+ if (held) root.rememberPathAlias(path, held.label, held.uri)
342
+ root.opened(path, origin)
343
+ }
344
+ function onStarting(text, origin) { root.startingBridge(text, origin) }
345
+ function onFailed(text, origin) {
346
+ if (root._repairWaiting) {
347
+ root._repairWaiting = false
348
+ root.sticky("", origin)
349
+ root.repairFailed()
350
+ return
351
+ }
352
+ root.result = "failed"
353
+ root.sticky("", origin)
354
+ root.message(text, true)
355
+ }
356
+ // The bridge's busy refusal reaches a repair the same way: a second bridge is never
357
+ // started, so the repair ends instead of waiting on a wait it did not start.
358
+ function onNotice(text, origin) {
359
+ if (root._repairWaiting) {
360
+ root._repairWaiting = false
361
+ root.sticky("", origin)
362
+ root.repairFailed()
363
+ return
364
+ }
365
+ root.message(text, false)
366
+ }
367
+ }
368
+
369
+ // Three sources, deduped on the normalized uri (see ui/js/Mounts.js "normalize"): a live gio mount wins over a bookmark for the same share even when the trailing slash differs.
370
+ // The bookmark's own label wins on that merged row, or a rename of a mounted share would be written to the file and never drawn again; see ui/js/Mounts.js "railLabel".
371
+ function rebuild(mountinfoText) {
372
+ var out = []
373
+ var seen = {}
374
+ var mounts = Mounts.parseMounts(root._mountListing)
375
+ var marks = Mounts.nonFileBookmarks(root.bookmarksText)
376
+ for (var i = 0; i < mounts.length; i++) {
377
+ var covered = false
378
+ for (var j = 0; j < marks.length; j++) {
379
+ if (!Mounts.addressMountCovers(mounts[i].uri, marks[j].uri)) continue
380
+ covered = true
381
+ var coveredKey = Mounts.normalize(marks[j].uri)
382
+ if (seen[coveredKey]) continue
383
+ seen[coveredKey] = true
384
+ out.push({ path: "", label: marks[j].label, group: "network", kind: "share", uri: marks[j].uri, mountUri: mounts[i].uri, mounted: true, glyph: "server" })
385
+ }
386
+ if (covered) continue
387
+ var mkey = Mounts.normalize(mounts[i].uri)
388
+ if (seen[mkey]) continue
389
+ seen[mkey] = true
390
+ out.push({ path: "", label: Mounts.railLabel(mounts[i], marks), group: "network", kind: "share", uri: mounts[i].uri, mounted: true, glyph: "server" })
391
+ }
392
+ for (var k = 0; k < marks.length; k++) {
393
+ var bkey = Mounts.normalize(marks[k].uri)
394
+ if (seen[bkey]) continue
395
+ seen[bkey] = true
396
+ out.push({ path: "", label: marks[k].label, group: "network", kind: "share", uri: marks[k].uri, mounted: false, glyph: "server" })
397
+ }
398
+ if (root.dropboxPath.length > 0) {
399
+ out.push({ path: root.dropboxPath, label: "Dropbox", group: "network", kind: "dropbox", uri: "", mounted: true, glyph: "" })
400
+ }
401
+ // Cloud rows show a FUSE mount under its folder's name with no menu; mountinfoText is the tests' seam, live callers pass nothing.
402
+ var infoText = mountinfoText === undefined ? cloudFile.text() : mountinfoText
403
+ var clouds = Cloud.parseCloudMounts(infoText, Quickshell.env("HOME"))
404
+ var seenPath = {}
405
+ for (var c = 0; c < clouds.length; c++) {
406
+ seenPath[clouds[c].path] = true
407
+ out.push({ path: clouds[c].path, label: Mounts.leaf(clouds[c].path), group: "network",
408
+ kind: "cloud", uri: "", mounted: true, glyph: "server" })
409
+ }
410
+ // Every mountinfo kernel row stays; a gio line at its mountpoint lends name and release spelling, the rest keep the leaf and unmount by path.
411
+ var kernels = Cloud.parseNetworkMounts(infoText)
412
+ var gioRows = Mounts.gioKernelMounts(root._mountListing)
413
+ var gioByPath = {}
414
+ for (var g = 0; g < gioRows.length; g++) {
415
+ if (!gioByPath[gioRows[g].path])
416
+ gioByPath[gioRows[g].path] = gioRows[g]
417
+ }
418
+ for (var v = 0; v < kernels.length; v++) {
419
+ if (seenPath[kernels[v].path])
420
+ continue
421
+ seenPath[kernels[v].path] = true
422
+ var lent = gioByPath[kernels[v].path]
423
+ out.push({ path: kernels[v].path, label: lent ? lent.label : Mounts.leaf(kernels[v].path),
424
+ group: "network", kind: "share", uri: lent ? lent.uri : kernels[v].path,
425
+ fstype: kernels[v].fstype, mounted: true, glyph: "server", editable: false })
426
+ }
427
+ // Only persisted names may relabel an alias; live gio labels are deliberately excluded.
428
+ var labelSources = []
429
+ for (var s = 0; s < marks.length; s++)
430
+ labelSources.push({ uri: marks[s].uri, label: marks[s].label, saved: true })
431
+ for (var f = 0; root.savedFavourites && f < root.savedFavourites.length; f++) {
432
+ var favourite = root.savedFavourites[f]
433
+ if (favourite && favourite.path && favourite.label)
434
+ labelSources.push({ path: favourite.path, label: favourite.label, saved: true })
435
+ }
436
+ root._aliasLabels = Crumbs.labelsByKey(labelSources, Mounts.normalize)
437
+ root.pathAliases = Crumbs.relabelAliases(root.pathAliases, root._aliasLabels)
438
+ // Every five seconds forever, so an unchanged poll must not assign: see Mounts.sameEntries.
439
+ if (!Mounts.sameEntries(root.entries, out))
440
+ root.entries = out
441
+ }
442
+
443
+ // A favourite's path is already real; a share needs mounting (if not live) then resolving.
444
+ // A cloud row's path is already real too: its mount is the tool that made it, not Flea's.
445
+ // origin rides along explicitly, because this service outlives the rail that renders it and
446
+ // the rail's own origin is gone by the time a hidden-rail answer lands.
447
+ function activate(index, origin) {
448
+ var e = root.entries[index]
449
+ if (!e) return
450
+ if (e.kind === "cloud") {
451
+ root.opened(e.path, origin === undefined ? root.origin : origin)
452
+ return
453
+ }
454
+ if (e.kind === "share") {
455
+ // A kernel mount's path is already real, so it opens with no gio round trip; a gvfs share carries no path and rides openShare below.
456
+ if (e.path && e.path.length > 0) {
457
+ root.opened(e.path, origin === undefined ? root.origin : origin)
458
+ return
459
+ }
460
+ root.openShare(e.uri, e.mounted, e.label, false, { origin: origin === undefined ? root.origin : origin })
461
+ return
462
+ }
463
+ root.opened(e.path, origin === undefined ? root.origin : origin)
464
+ }
465
+
466
+ function passwordFor(uri) {
467
+ return root._passwords[Mounts.normalize(uri)] || ""
468
+ }
469
+
470
+ function remember(uri, password) {
471
+ if (password.length === 0) return
472
+ var next = Object.assign({}, root._passwords)
473
+ next[Mounts.normalize(uri)] = password
474
+ root._passwords = next
475
+ }
476
+
477
+ // forgetPassword, not forget: forget(uri) below is the bookmark writer, and one name for both
478
+ // would have made a refused connect delete the saved place.
479
+ function forgetPassword(uri) {
480
+ var key = Mounts.normalize(uri)
481
+ if (root._passwords[key] === undefined) return
482
+ var next = Object.assign({}, root._passwords)
483
+ delete next[key]
484
+ root._passwords = next
485
+ }
486
+
487
+ function saveLocation(uri, label, password, requestId, origin) {
488
+ return root.openShare(uri, false, label, password.length > 0, { id: requestId, password: password, origin: origin })
489
+ }
490
+
491
+ function openChildShare(uri, label, origin) {
492
+ var password = root.passwordFor(root._pendingUri)
493
+ root.remember(uri, password)
494
+ root.openShare(uri, false, label, password.length > 0, { origin: origin })
495
+ }
496
+
497
+ function openShare(uri, alreadyMounted, label, authenticated, request) {
498
+ request = request || ({})
499
+ // An open is single flight over four children, the bridge wait and the repair peek,
500
+ // the share listing included, so a new one must not start over the running leg and hand
501
+ // that leg's deadline to itself; see "listShares". A refusal names the moment and
502
+ // touches no flight state.
503
+ if (mountProcess.running || authProcess.running || infoProcess.running || listSharesProcess.running || root._repairActive || root._repairWaiting) {
504
+ // A guard that returns in silence names nothing at all, and a leg can hold it 15 s.
505
+ var reason = Mounts.WORDS.anotherOpening
506
+ if (request.id) root.completed(request.id, Mounts.normalize(uri), false, reason, false)
507
+ else root.message(reason, false)
508
+ return
509
+ }
510
+ if (root.result === "failed") root.message("", false)
511
+ // One canonical spelling from here: tests/network-open-share.sh pins the info leg to it.
512
+ root._pendingUri = Mounts.normalize(uri)
513
+ root._pendingLabel = label || ""
514
+ root._requestId = request.id || ""
515
+ root._requestPassword = request.password || ""
516
+ root._pendingOrigin = request.origin === undefined ? root.origin : request.origin
517
+ root._mountFailed = false
518
+ if (alreadyMounted) {
519
+ root.result = "resolving"
520
+ root.runInfo(uri)
521
+ return
522
+ }
523
+ var password = request.password || root.passwordFor(uri)
524
+ // A remembered password is a fallback for sftp, never a first resort: Mounts.keyless.
525
+ if (authenticated === true || (!Mounts.keyless(uri)
526
+ && (password.length > 0 || Mounts.credentialed(uri)))) {
527
+ if (password.length === 0) {
528
+ root.result = "missing-credential"
529
+ if (!root.finishRequest(false, "Enter the password to mount this location."))
530
+ root.retryRequested(uri, root._pendingLabel, "", "Enter the password to mount this location.", false, root._pendingOrigin)
531
+ return
532
+ }
533
+ root._pendingPassword = password
534
+ password = ""
535
+ root._authAwaitingStart = true
536
+ root.result = "mounting"
537
+ authProcess.command = ["timeout", String(root.authTimeoutSeconds),
538
+ Quickshell.env("FLEA_GIO_AUTH") || "/usr/lib/flea/flea-gio-auth", root._pendingUri]
539
+ authProcess.running = true
540
+ return
541
+ }
542
+ root.result = "mounting"
543
+ mountProcess.command = /^smb:\/\//i.test(uri)
544
+ ? ["gio", "mount", "--anonymous", uri] : ["gio", "mount", uri]
545
+ mountProcess.running = true
546
+ mountTimeout.restart()
547
+ }
548
+
549
+ function runInfo(uri) {
550
+ infoProcess.command = ["gio", "info", uri]
551
+ root._infoOutput = ""
552
+ infoProcess.running = true
553
+ mountTimeout.restart()
554
+ }
555
+
556
+ // refused says the server itself turned the credential down. Only that invalidates it: a helper
557
+ // that could not start and a host that never answered say nothing about the password, and the
558
+ // reopened dialog has to be populated from it exactly as 0.1.6 populated it.
559
+ function failMount(reason, password, refused, missing) {
560
+ root._pendingPassword = ""
561
+ root.result = missing === true ? "missing-credential" : "failed"
562
+ root.message(reason, true)
563
+ var attempted = password || root._requestPassword
564
+ // Keeping a refused secret meant every later click on that location replayed it with no
565
+ // prompt at all, which is how a domain account locks itself out.
566
+ if (refused === true) root.forgetPassword(root._pendingUri)
567
+ else if (attempted.length > 0) root.remember(root._pendingUri, attempted)
568
+ if (!root.finishRequest(false, reason, missing !== true))
569
+ root.retryRequested(root._pendingUri, root._pendingLabel, attempted, reason,
570
+ missing !== true, root._pendingOrigin)
571
+ }
572
+
573
+ // Only when nothing in the FUSE root answers for the request: the mount fails with the
574
+ // reason gio gave, never as an unreadable directory.
575
+ function repairFailed() {
576
+ var uri = root._repairUri
577
+ var refused = root._repairFailed
578
+ root._repairActive = false
579
+ root._repairWaiting = false
580
+ root._repairBridged = false
581
+ root._repairRoot = ""
582
+ root._repairPath = ""
583
+ if (refused) root.failMount(Errors.WORDS.connectRefused, root.passwordFor(uri))
584
+ else root.failMount(Errors.WORDS.connectNoFolder, root.passwordFor(uri))
585
+ }
586
+
587
+ // Issue 194's repair, answered through backend.peek: gio's own folder first, then every
588
+ // entry of the FUSE root it names, matched by ui/js/Protocols.js smbResolve. A real
589
+ // directory always opens; several users prefer the desktop login and otherwise name
590
+ // themselves; anything else is the mount failing with the reason gio gave. Only a root
591
+ // that peeks empty or unreadable starts the bridge for the gvfs root, then peeks that
592
+ // root once under the same deadline.
593
+ function repairPeeked(path, rows, readFailed) {
594
+ if (!root._repairActive) return
595
+ if (path === root._repairPath) {
596
+ if (!readFailed) {
597
+ var openPath = root._repairPath
598
+ var openOrigin = root._pendingOrigin
599
+ mountTimeout.stop()
600
+ root._repairActive = false
601
+ root._repairBridged = false
602
+ root._repairPath = ""
603
+ root.result = "mounted"
604
+ root.rememberPathAlias(openPath, root._pendingLabel, root._pendingUri)
605
+ root.finishRequest(true, "")
606
+ root.opened(openPath, openOrigin)
607
+ return
608
+ }
609
+ var cut = root._repairPath.indexOf("/gvfs/")
610
+ if (cut < 0 || !root.backend) {
611
+ mountTimeout.stop()
612
+ root.repairFailed()
613
+ return
614
+ }
615
+ root._repairRoot = root._repairPath.substring(0, cut + 5)
616
+ mountTimeout.restart()
617
+ root.backend.peek(root._repairRoot, 512, false)
618
+ return
619
+ }
620
+ if (root._repairRoot.length === 0 || path !== root._repairRoot) return
621
+ // The bridge down reads as an empty root, so only then does the repair spend its one
622
+ // bridge start; a root that answered with entries but no match fails without one.
623
+ if ((readFailed || rows.length === 0) && !root._repairBridged && root.backend) {
624
+ root._repairBridged = true
625
+ root._repairWaiting = true
626
+ mountTimeout.restart()
627
+ var repairBridge = root.ensureBridge()
628
+ if (repairBridge)
629
+ repairBridge.ensure(root._repairRoot, root._pendingLabel, root._pendingOrigin)
630
+ else
631
+ root.failBridgeMissing()
632
+ return
633
+ }
634
+ mountTimeout.stop()
635
+ var candidates = []
636
+ for (var i = 0; i < rows.length; i++) candidates.push(root._repairRoot + "/" + rows[i].n)
637
+ var login = Quickshell.env("USER") || Quickshell.env("LOGNAME") || ""
638
+ var picked = Protocols.smbResolve(root._repairUri, candidates, login)
639
+ root._repairActive = false
640
+ root._repairBridged = false
641
+ root._repairRoot = ""
642
+ root._repairPath = ""
643
+ if (picked.path.length > 0) {
644
+ var pickedOrigin = root._pendingOrigin
645
+ root.result = "mounted"
646
+ root.rememberPathAlias(picked.path, root._pendingLabel, root._pendingUri)
647
+ root.finishRequest(true, "")
648
+ root.opened(picked.path, pickedOrigin)
649
+ return
650
+ }
651
+ if (picked.users.length > 0) {
652
+ root.failMount(Format.fill(Errors.WORDS.connectMountedAs, picked.users.join(", ")),
653
+ root.passwordFor(root._repairUri))
654
+ return
655
+ }
656
+ root.repairFailed()
657
+ }
658
+
659
+ function finishRequest(success, reason, connectFailed) {
660
+ var requestId = root._requestId
661
+ if (!requestId) return false
662
+ root._requestId = ""
663
+ if (success) root.remember(root._pendingUri, root._requestPassword)
664
+ root._requestPassword = ""
665
+ root.completed(requestId, root._pendingUri, success, reason || "", connectFailed === true)
666
+ return true
667
+ }
668
+
669
+ function cancelLocation(requestId) {
670
+ if (!requestId || requestId !== root._requestId) return
671
+ var origin = root._pendingOrigin
672
+ root._requestId = ""
673
+ root._requestPassword = ""
674
+ root._pendingPassword = ""
675
+ root._authAwaitingStart = false
676
+ root._repairActive = false
677
+ root._repairWaiting = false
678
+ root._repairBridged = false
679
+ // Only this flight's own bridge waiter ends; another flight's wait is never cancelled,
680
+ // and with no bridge built no wait can stand.
681
+ if (root.bridge)
682
+ root.bridge.cancelFor(origin)
683
+ root.sticky("", origin)
684
+ mountTimeout.stop()
685
+ if (mountProcess.running) { root._mountTimedOut = true; mountProcess.running = false }
686
+ if (infoProcess.running) { root._infoTimedOut = true; infoProcess.running = false }
687
+ if (listSharesProcess.running) { root._listSharesTimedOut = true; listSharesProcess.running = false }
688
+ if (authProcess.running) { root._authCancelled = true; authProcess.running = false }
689
+ root.result = "cancelled"
690
+ }
691
+
692
+ // A server root with no share segment mounts but has no FUSE path of its own.
693
+ function isBareRoot(uri) {
694
+ return /^[a-z][a-z0-9+.-]*:\/\/[^\/]+\/?$/i.test(uri)
695
+ }
696
+
697
+ // Client-side only, never writes bookmarks; see AGENTS.md "The share browser overlay". This is
698
+ // the third leg of an open and takes the same bound: "gio list" on a server gvfs cannot reach
699
+ // hangs exactly the way "gio info" does, with nothing else in the chain left to end it.
700
+ function listShares(uri) {
701
+ listSharesProcess.command = ["gio", "list", uri]
702
+ root._listSharesOutput = ""
703
+ listSharesProcess.running = true
704
+ mountTimeout.restart()
705
+ }
706
+
707
+ // One shared ContextMenu preserves keyboard focus; this service performs its Unmount action.
708
+ function unmount(index) {
709
+ var e = root.entries[index]
710
+ if (!e || e.kind !== "share" || !e.mounted || unmountProcess.running) return
711
+ root._pendingUnmountLabel = e.label
712
+ // A user FUSE mount answers to its own unmounter; the exit status below decides.
713
+ if (e.fstype && String(e.fstype).indexOf("fuse.") === 0)
714
+ unmountProcess.command = ["fusermount3", "-u", e.path]
715
+ else
716
+ unmountProcess.command = ["gio", "mount", "-u", e.mountUri || e.uri]
717
+ unmountProcess.running = true
718
+ }
719
+
720
+ // What the rail and the Processes below still call by name; the work is in the two children above.
721
+ function rename(uri, name) { places.rename(uri, name) }
722
+ function forget(uri) { places.forget(uri) }
723
+ function replacePlace(oldUri) { places.replace(oldUri, root._pendingUri, root._pendingLabel) }
724
+ function pollMounts() { listing.poll() }
725
+
726
+ Timer {
727
+ id: mountTimeout
728
+ interval: root.mountTimeoutMs
729
+ repeat: false
730
+ onTriggered: {
731
+ // Whichever leg of the open is still running is the one that missed the deadline.
732
+ if (mountProcess.running) {
733
+ root._mountTimedOut = true
734
+ mountProcess.running = false
735
+ } else if (infoProcess.running) {
736
+ root._infoTimedOut = true
737
+ infoProcess.running = false
738
+ } else if (listSharesProcess.running) {
739
+ root._listSharesTimedOut = true
740
+ listSharesProcess.running = false
741
+ } else if (root._repairActive) {
742
+ root._repairActive = false
743
+ root._repairWaiting = false
744
+ root._repairBridged = false
745
+ root._repairRoot = ""
746
+ root._repairPath = ""
747
+ } else {
748
+ return
749
+ }
750
+ // Not failMount: that offers a Retry over the rail, and the deadline's own arm in
751
+ // tests/ui.sh presses l on the rail the instant this fires. An address that never
752
+ // answered has no credential to correct anyway.
753
+ root.result = "failed"
754
+ root.message(Errors.WORDS.connectNoAnswer, true)
755
+ root.finishRequest(false, Errors.WORDS.connectNoAnswer, true)
756
+ }
757
+ }
758
+
759
+ // The credentialed leg: "timeout" bounds it rather than mountTimeout, so a hung helper answers
760
+ // 124 and Errors.connectFailure names it, and the C locale above reaches the gio the helper runs.
761
+ Process {
762
+ id: authProcess
763
+ environment: root.gioEnvironment
764
+ stdinEnabled: true
765
+ stderr: StdioCollector { waitForEnd: true }
766
+ onStarted: {
767
+ root._authAwaitingStart = false
768
+ var password = root._pendingPassword
769
+ root._pendingPassword = ""
770
+ authProcess.write(password + "\n")
771
+ password = ""
772
+ }
773
+ onRunningChanged: {
774
+ if (root._authAwaitingStart && !authProcess.running) {
775
+ root._authAwaitingStart = false
776
+ root.failMount(Errors.WORDS.connectNoPrompt,
777
+ root.passwordFor(root._pendingUri))
778
+ }
779
+ }
780
+ onExited: function (exitCode) {
781
+ if (root._authCancelled) { root._authCancelled = false; return }
782
+ root._pendingPassword = ""
783
+ if (exitCode === 0) {
784
+ root.runInfo(root._pendingUri)
785
+ return
786
+ }
787
+ // 124 is a host that never answered and 126/127 a helper that could not start; neither
788
+ // is the server turning the credential down, so neither invalidates it.
789
+ var refused = exitCode !== 124 && exitCode !== 126 && exitCode !== 127
790
+ root.failMount(Errors.connectFailure(exitCode),
791
+ root.passwordFor(root._pendingUri), refused)
792
+ }
793
+ }
794
+
795
+ Process {
796
+ id: mountProcess
797
+ environment: root.gioEnvironment
798
+ onExited: function (exitCode) {
799
+ mountTimeout.stop()
800
+ var timedOut = root._mountTimedOut
801
+ root._mountTimedOut = false
802
+ if (timedOut) return
803
+ // A refusal for a location that is already mounted and a refusal for one that does not
804
+ // exist differ only in a translated sentence, so neither is read: the info call below
805
+ // answers with a FUSE path when the location really is mounted, whatever this code was.
806
+ root._mountFailed = exitCode !== 0
807
+ root.runInfo(root._pendingUri)
808
+ }
809
+ }
810
+
811
+ // The FUSE path is ui/js/Mounts.js "localPath"'s to find, one resolver for the product and for
812
+ // tests/js/network.js, and the C locale above is what keeps gio's own wording stable for it.
813
+ Process {
814
+ id: infoProcess
815
+ environment: root.gioEnvironment
816
+ stdout: StdioCollector { id: infoOut; waitForEnd: true; onStreamFinished: root._infoOutput = text }
817
+ onExited: function (exitCode) {
818
+ mountTimeout.stop()
819
+ root.pollMounts()
820
+ var timedOut = root._infoTimedOut
821
+ root._infoTimedOut = false
822
+ var failed = root._mountFailed
823
+ root._mountFailed = false
824
+ if (timedOut) return
825
+ var path = Mounts.localPath(String(infoOut.text || root._infoOutput || ""))
826
+ if (exitCode === 0 && path.length > 0 && root.backend && Protocols.schemeOf(root._pendingUri) === "smb"
827
+ && !Protocols.smbFuseMatches(root._pendingUri, path)) {
828
+ // Issue 194: gio answered with a folder that is not this share (a user its line
829
+ // drops above all). Whether it exists is the backend's own answer to say: the
830
+ // folder peeks first, then the FUSE root, and only an empty or unreadable root
831
+ // starts the bridge for the gvfs root under the same deadline.
832
+ root._repairUri = root._pendingUri
833
+ root._repairPath = path
834
+ root._repairFailed = failed
835
+ root._repairRoot = ""
836
+ root._repairWaiting = false
837
+ root._repairBridged = false
838
+ root._repairActive = true
839
+ mountTimeout.restart()
840
+ root.backend.peek(path, 1, false)
841
+ return
842
+ }
843
+ if (exitCode === 0 && path.length > 0) {
844
+ var openBridge = root.ensureBridge()
845
+ if (!openBridge) {
846
+ root.failBridgeMissing()
847
+ return
848
+ }
849
+ // Capture before emitting completion or waiting: a later open may replace _pending* before onReady.
850
+ var bridgeLabel = root._pendingLabel
851
+ var bridgeUri = root._pendingUri
852
+ var bridgeOrigin = root._pendingOrigin
853
+ root._bridgeIdentities[path] = { label: bridgeLabel, uri: bridgeUri }
854
+ root.result = "mounted"
855
+ root.finishRequest(true, "")
856
+ openBridge.ensure(path, bridgeLabel, bridgeOrigin)
857
+ return
858
+ }
859
+ // A refused keyless sftp attempt is a missing credential and not a refused location, sftp only.
860
+ if (failed && Mounts.keyless(root._pendingUri) && Mounts.credentialed(root._pendingUri)) {
861
+ root.failMount("Enter the password to mount this location.",
862
+ root.passwordFor(root._pendingUri), false, true)
863
+ return
864
+ }
865
+ // A server root lists its shares whatever the exit code said, so this stays ahead of the refusal.
866
+ if (root.isBareRoot(root._pendingUri)) {
867
+ root.result = "resolving"
868
+ root.listShares(root._pendingUri)
869
+ return
870
+ }
871
+ if (failed) {
872
+ root.failMount(Errors.WORDS.connectRefused,
873
+ root.passwordFor(root._pendingUri))
874
+ return
875
+ }
876
+ root.failMount(Errors.WORDS.connectNoFolder, root.passwordFor(root._pendingUri))
877
+ }
878
+ }
879
+
880
+ Process {
881
+ id: listSharesProcess
882
+ environment: root.gioEnvironment
883
+ stdout: StdioCollector { id: listSharesOut; waitForEnd: true; onStreamFinished: root._listSharesOutput = text }
884
+ onExited: function (exitCode) {
885
+ mountTimeout.stop()
886
+ var timedOut = root._listSharesTimedOut
887
+ root._listSharesTimedOut = false
888
+ if (timedOut) return
889
+ var body = String(listSharesOut.text || root._listSharesOutput || "")
890
+ var names = body.split("\n").map(function (s) { return s.trim() }).filter(function (s) { return s.length > 0 })
891
+ if (exitCode !== 0 || names.length === 0) {
892
+ root.failMount(Errors.WORDS.connectNoFolder, root.passwordFor(root._pendingUri))
893
+ return
894
+ }
895
+ root.result = "mounted"
896
+ root.finishRequest(true, "")
897
+ root.sharesListed(root._pendingUri, root._pendingLabel, names, root._pendingOrigin)
898
+ }
899
+ }
900
+
901
+ Process {
902
+ id: unmountProcess
903
+ environment: root.gioEnvironment
904
+ onExited: function (exitCode) {
905
+ root.pollMounts()
906
+ root.result = exitCode === 0 ? "unmounted" : "failed"
907
+ // Replace the arm prompt immediately after successful unmount.
908
+ root.message(exitCode === 0
909
+ ? Format.fill(Mounts.WORDS.unmountedLine, root._pendingUnmountLabel)
910
+ : "That share could not be unmounted; it may still be in use.", exitCode !== 0)
911
+ }
912
+ }
913
+ }