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
package/ui/js/Eject.js ADDED
@@ -0,0 +1,217 @@
1
+ .pragma library
2
+
3
+ .import "Mounts.js" as Mounts
4
+ .import "Format.js" as Format
5
+ .import "Protocols.js" as Protocols
6
+ .import "NetFs.js" as NetFs
7
+
8
+ // Whole sentences with "%1" for the drive and "%2" for the drives still mounted under it, filled by Format.fill; the first three are what Ctrl+E answers with when it releases nothing.
9
+ var WORDS = {
10
+ systemDrive: "%1 is the system drive, which Flea never unmounts",
11
+ systemVolume: "This folder is on a system volume, which Flea never unmounts",
12
+ noVolume: "This folder is not on a volume Flea can unmount",
13
+ safe: "Ejected %1. It is safe to unplug.",
14
+ safeImage: "Ejected %1.",
15
+ stillMounted: "%1 is still mounted; close what is using it.",
16
+ blockedOne: "%1 could not be ejected because %2 on the same drive is still mounted. Eject that instead.",
17
+ blockedMany: "%1 could not be ejected because %2 on the same drive are still mounted. Eject those instead.",
18
+ unconfirmed: "Could not confirm %1 was ejected; do not unplug it yet."
19
+ }
20
+
21
+ // Sample input: the lsblk --json body ui/js/Mounts.js "parseDevices" reads, taken again after
22
+ // gio mount -e returned for /dev/sda1:
23
+ // {"blockdevices":[
24
+ // {"name":"sda","path":"/dev/sda","label":null,"mountpoints":[null],"rm":true,"size":"116.1G","type":"disk","model":"USB Flash Disk",
25
+ // "children":[{"name":"sda1","path":"/dev/sda1","label":"128GB","mountpoints":[null],"rm":true,"size":"116.1G","type":"part","model":null}]}]}
26
+ // The sda entry is then either gone, or still listed with every mountpoint under it null.
27
+ // gio's exit code is never the witness: it has been 0 over a volume that stayed mounted.
28
+ // "safe" when nothing under the disk that carries the device is mounted, "mounted" when anything
29
+ // under it still is, "unknown" when the body is not a listing this can judge.
30
+ function verdict(body, device) {
31
+ var disk = diskOf(body, device)
32
+ if (disk === undefined)
33
+ return "unknown"
34
+ if (disk === null)
35
+ return "safe"
36
+ return anyMounted(disk) ? "mounted" : "safe"
37
+ }
38
+
39
+ // The top-level lsblk node that holds the device: undefined when the body is not a listing, null
40
+ // when no node holds it, which is the powered-off shape. The whole disk is what gets judged: gvfs
41
+ // ejects the drive, so a sibling partition or a crypt child still mounted means still in use.
42
+ function diskOf(body, device) {
43
+ var tree
44
+ try {
45
+ tree = JSON.parse(String(body || ""))
46
+ } catch (e) {
47
+ return undefined
48
+ }
49
+ var nodes = tree && tree.blockdevices
50
+ // A box with no block devices at all is not running, so an empty listing is a broken one.
51
+ if (!Array.isArray(nodes) || nodes.length === 0)
52
+ return undefined
53
+ var name = kernelName(device)
54
+ if (name.length === 0)
55
+ return undefined
56
+ for (var i = 0; i < nodes.length; i++) {
57
+ if (holds(nodes[i], name))
58
+ return nodes[i]
59
+ }
60
+ return null
61
+ }
62
+
63
+ // Both sides of every comparison here come from lsblk's own NAME column. volumeRow carries lsblk's
64
+ // PATH now, and a device-mapper volume's path is /dev/mapper/<name>, so the leaf is what is read.
65
+ function kernelName(device) {
66
+ var text = String(device || "")
67
+ var cut = text.lastIndexOf("/")
68
+ return cut < 0 ? text : text.substring(cut + 1)
69
+ }
70
+
71
+ function holds(node, name) {
72
+ if (String(node.name) === name)
73
+ return true
74
+ var kids = node.children || []
75
+ for (var i = 0; i < kids.length; i++) {
76
+ if (holds(kids[i], name))
77
+ return true
78
+ }
79
+ return false
80
+ }
81
+
82
+ // lsblk writes "[SWAP]" as a mountpoint of active swap, which is in use like any other mount. This
83
+ // asks whether anything holds the volume at all, which is not Mounts.js mountOf's question: that one
84
+ // answers where to browse, and swap is nowhere.
85
+ function inUse(node) {
86
+ var points = node.mountpoints || []
87
+ for (var i = 0; i < points.length; i++) {
88
+ if (points[i] !== null && String(points[i]).length > 0)
89
+ return true
90
+ }
91
+ return false
92
+ }
93
+
94
+ function anyMounted(node) {
95
+ if (inUse(node))
96
+ return true
97
+ var kids = node.children || []
98
+ for (var i = 0; i < kids.length; i++) {
99
+ if (anyMounted(kids[i]))
100
+ return true
101
+ }
102
+ return false
103
+ }
104
+
105
+ // What the refusal has to name. Empty when the row itself is still mounted, because a re-press on
106
+ // it is then the right next step; otherwise the labels of what is still mounted under the same disk,
107
+ // because a re-press on a row lsblk now shows unmounted would silently do nothing.
108
+ function blockers(body, device) {
109
+ var disk = diskOf(body, device)
110
+ if (!disk)
111
+ return []
112
+ var mounted = []
113
+ collectMounted(disk, mounted)
114
+ var name = kernelName(device)
115
+ for (var i = 0; i < mounted.length; i++) {
116
+ if (mounted[i].name === name)
117
+ return []
118
+ }
119
+ return mounted.map(function (m) { return m.label })
120
+ }
121
+
122
+ function collectMounted(node, out) {
123
+ if (inUse(node))
124
+ out.push({ name: String(node.name), label: node.label ? String(node.label) : String(node.name) })
125
+ var kids = node.children || []
126
+ for (var i = 0; i < kids.length; i++)
127
+ collectMounted(kids[i], out)
128
+ }
129
+
130
+ // Only a real drive's safe verdict may claim it is safe to unplug; an image has nothing to unplug.
131
+ // Other verdicts keep their warnings and name a next step only when pressing it does something.
132
+ function sentence(v, label, others, loop) {
133
+ if (v === "safe")
134
+ return { text: Format.fill(loop === true ? WORDS.safeImage : WORDS.safe, label), isError: false }
135
+ if (v === "attached") // A loop row unmounted, but its loop device could not be detached.
136
+ return { text: "Unmounted " + label + ", but its loop device is still attached.", isError: true }
137
+ if (v === "mounted") {
138
+ var rest = others || []
139
+ if (rest.length === 0)
140
+ return { text: Format.fill(WORDS.stillMounted, label), isError: true }
141
+ return { text: Format.fill(rest.length > 1 ? WORDS.blockedMany : WORDS.blockedOne, label, rest.join(", ")), isError: true }
142
+ }
143
+ return { text: Format.fill(WORDS.unconfirmed, label), isError: true }
144
+ }
145
+
146
+ // Only a releasable mounted row draws the mark in place of the square.
147
+ // Sample input: {label:"NAS", group:"network", kind:"share", uri:"smb://h/data/", mounted:true}.
148
+ function releasable(entry) {
149
+ if (!entry || entry.mounted !== true)
150
+ return false
151
+ if (entry.group === "device" && entry.kind === "volume")
152
+ return entry.removable === true || entry.loop === true
153
+ if (entry.group === "device" && entry.kind === "phone")
154
+ return true
155
+ if (entry.group === "network" && entry.kind === "share") {
156
+ // A kernel row names its fstype for the mark split; a gvfs share's scheme decides it.
157
+ if (entry.fstype)
158
+ return NetFs.isCifsFstype(entry.fstype)
159
+ return Protocols.schemeOf(entry.uri) !== "nfs"
160
+ }
161
+ return false
162
+ }
163
+
164
+ // The one-click release mirrors the rows Mounts.railMenu offers.
165
+ function releaseAction(entry) {
166
+ if (!releasable(entry))
167
+ return ""
168
+ if (entry.group === "device" && entry.kind === "phone")
169
+ return "unmountPhone"
170
+ return entry.group === "device" ? "eject" : "unmount"
171
+ }
172
+
173
+ // Picked by name, never position (a volume or phone row leads with Open): Eject, else the row's unmount, else "".
174
+ function releaseFromRows(rows) {
175
+ var list = rows || []
176
+ for (var i = 0; i < list.length; i++) {
177
+ if (list[i].action === "eject")
178
+ return "eject"
179
+ }
180
+ for (var j = 0; j < list.length; j++) {
181
+ if (list[j].action === "unmount" || list[j].action === "unmountVolume" || list[j].action === "unmountPhone")
182
+ return list[j].action
183
+ }
184
+ return ""
185
+ }
186
+
187
+ // Finder's Cmd+E with Cmd read as Ctrl: from the rail it releases the cursor row, from a listing the
188
+ // mounted volume the directory is inside, so the key can never release a volume the operator is
189
+ // not looking at. The verdict is Mounts.railMenu's either way, off the lsblk poll, and the release
190
+ // goes through the same releaseChosen a chosen menu row takes, carrying the row's key. A hidden rail
191
+ // unloads the Sidebar with its poll, verdict state and releaseChosen, so there is nothing to read
192
+ // and nothing to release through: the pane spins a transient DeviceMounts for one one-shot listing
193
+ // (ui/PaneRail.qml), which completes through this same function with its own entries and
194
+ // releaseChosen. A null rail therefore never answers from silence; it hands the key to that flow.
195
+ function release(root, rail, fromRail) {
196
+ if (!rail) {
197
+ root.ejectHidden()
198
+ return
199
+ }
200
+ var entry = fromRail ? rail.entries[rail.cursorIndex] : Mounts.holding(rail.entries, root.path)
201
+ if (fromRail && entry && entry.group === "device" && entry.kind === "disk") {
202
+ root.message(Format.fill(WORDS.systemDrive, entry.label), false)
203
+ return
204
+ }
205
+ if (!entry) {
206
+ // The backend alone names a system volume, off the listing's own device; a Recent history spans mounts, so it names none.
207
+ root.message(root.systemVolume === true && root.recentMode === "" ? WORDS.systemVolume : WORDS.noVolume, false)
208
+ return
209
+ }
210
+ var rows = Mounts.railMenu(entry)
211
+ var pick = releaseFromRows(rows)
212
+ if (pick === "") {
213
+ root.message(Format.fill(Mounts.WORDS.nothingToRelease, entry.label), false)
214
+ return
215
+ }
216
+ rail.releaseChosen(pick, Mounts.railKey(entry))
217
+ }
@@ -0,0 +1,188 @@
1
+ .pragma library
2
+
3
+ .import "Format.js" as Format
4
+
5
+ // Whole sentences, filled by Format.fill where a name goes in; the item is a file or a folder.
6
+ var WORDS = {
7
+ deniedNamed: "%1 cannot be opened: you do not have permission.",
8
+ denied: "This folder cannot be opened: you do not have permission.",
9
+ unreadNamed: "%1 could not be read.",
10
+ unread: "That folder could not be read.",
11
+ taken: "An item with that name is already here.",
12
+ // Whole connection failures, each with its one cause; the mount service says it is a connection failure by flag, never by these words.
13
+ connectRefused: "Could not connect: the server refused the connection.",
14
+ connectNoFolder: "Could not connect: that location has no folder to show.",
15
+ connectMountedAs: "Could not connect: that share is already mounted as %1.",
16
+ connectNoAnswer: "Could not connect: the server did not respond.",
17
+ connectNoPassword: "Could not connect: the server did not accept the password.",
18
+ connectNoPrompt: "Could not connect: Flea could not ask for the password.",
19
+ connectUnopened: "Could not connect: the network location could not be opened."
20
+ }
21
+
22
+ // Errors reach the user as one sentence, never a raw path or errno. StatusBar board rule 4: the
23
+ // sentence names the input and drops advice nobody can act on. named is the failing directory's own
24
+ // leaf, empty when the breadcrumb is already standing on it and there is nothing to add.
25
+ function sentence(where, message, named) {
26
+ if (where === "scan") {
27
+ if (denied(where, message)) {
28
+ return named ? Format.fill(WORDS.deniedNamed, named) : WORDS.denied
29
+ }
30
+ // A dead share names itself: stale, disconnected and silent each read differently.
31
+ if (shareStale(message)) return "That share changed, so this folder is no longer available."
32
+ if (shareGone(message)) return "That share is disconnected; remount it."
33
+ if (shareSilent(message)) return "That share is not responding."
34
+ return named ? Format.fill(WORDS.unreadNamed, named) : WORDS.unread
35
+ }
36
+ if (where === "sort") {
37
+ // Size and mtime are real orders, so the one refusal left is a key the wire never defined.
38
+ return "Sorting by that column is not available."
39
+ }
40
+ if (where === "read") {
41
+ return "Flea stopped responding. Reopen Flea and try again."
42
+ }
43
+ // The write operations say what they were doing, because the operator is about to try it again.
44
+ if (where === "undo" || where === "redo") {
45
+ // The empty journal is the common case and the backend's own sentence is already the right one.
46
+ return capitalised(message)
47
+ }
48
+ if (where === "rename") {
49
+ if (nameRefusal(message)) return capitalised(message)
50
+ if (readOnly(message)) return capitalised(message)
51
+ return exists(message) ? WORDS.taken : "That file could not be renamed."
52
+ }
53
+ // Bulk rename validates the whole batch first, so its sentence is the backend's own.
54
+ if (where === "bulkrename") {
55
+ return capitalised(message)
56
+ }
57
+ // undo reverses a rename through the same call, so this sentence names no direction.
58
+ if (where === "rename-kept") {
59
+ return "Copied to the new name, but the original was only partly removed. Check the original."
60
+ }
61
+ // Deliberately not the capitalised branch: every other mkdir refusal reaches the UI through
62
+ // src/error.rs from_io, which passes std::io::Error::to_string straight through, errno and all.
63
+ if (where === "mkdir") {
64
+ if (nameRefusal(message)) return capitalised(message)
65
+ if (readOnly(message)) return capitalised(message)
66
+ return exists(message) ? WORDS.taken
67
+ : "That folder could not be created."
68
+ }
69
+ // The state file: what was asked for is still on screen, so the sentence says what did not last.
70
+ if (where === "state") {
71
+ return "That setting could not be saved."
72
+ }
73
+ // And the other way round: main() left a ui.json it could not read alone, so none of it is used.
74
+ if (where === "statefile") {
75
+ return "Your saved settings could not be read, so these are the defaults."
76
+ }
77
+ if (where === "duplicate") {
78
+ if (readOnly(message)) return capitalised(message)
79
+ return "That file could not be duplicated."
80
+ }
81
+ if (where === "trash") {
82
+ if (readOnly(message)) return capitalised(message)
83
+ return "That item could not be moved to Trash."
84
+ }
85
+ // The backend refused rows read from a listing it had already replaced, so nothing ran at all.
86
+ if (where === "stale") {
87
+ return "The listing changed before that arrived, so nothing was done."
88
+ }
89
+ // A window past its deadline names its mount, so the backend's own sentence is the one to show.
90
+ if (where === "window") {
91
+ return capitalised(message)
92
+ }
93
+ // A password request refused before the child started names its archive, like the read sentence names Flea.
94
+ if (where === "archive" && String(message) === "the backend is not running") {
95
+ return named ? Format.fill("%1 could not be opened because the Flea backend stopped; reopen Flea and try again.", named)
96
+ : "This archive could not be opened because the Flea backend stopped; reopen Flea and try again."
97
+ }
98
+ if (where === "transfer" || where === "archive" || where === "convert"
99
+ || where === "link" || where === "linktarget" || where === "rename-stranded" || where === "pasteimage") {
100
+ return capitalised(message)
101
+ }
102
+ return "That action could not be completed; try again."
103
+ }
104
+
105
+ // One condition, two spellings: rename gets the errno's "File exists", while src/backend/ops.rs
106
+ // words mkdir's own collision "a folder or file with that name already exists".
107
+ function exists(message) {
108
+ var text = String(message).toLowerCase()
109
+ return text.indexOf("file exists") >= 0 || text.indexOf("already exists") >= 0
110
+ }
111
+
112
+ // A read-only refusal carries its own sentence from src/error.rs, so the write branches keep it.
113
+ function readOnly(message) {
114
+ return String(message).toLowerCase().indexOf("read-only") >= 0
115
+ }
116
+
117
+ // A per-filesystem name refusal carries its own sentence from src/backend/fsname.rs, so rename and mkdir keep it.
118
+ function nameRefusal(message) {
119
+ var text = String(message)
120
+ return text.indexOf("is not allowed in a name") >= 0 || text.indexOf("is reserved on this") >= 0
121
+ || text.indexOf("cannot end in a dot") >= 0 || text.indexOf("cannot end in a space") >= 0
122
+ || text.indexOf("cannot hold control character") >= 0
123
+ }
124
+
125
+ // The backend already writes these as sentences; this only makes one read like one in the bar.
126
+ function capitalised(message) {
127
+ var text = String(message)
128
+ if (text.length === 0) {
129
+ return "That action could not be completed; try again."
130
+ }
131
+ var out = text.charAt(0).toUpperCase() + text.substring(1)
132
+ return out.charAt(out.length - 1) === "." ? out : out + "."
133
+ }
134
+
135
+ // The one place the backend's errno wording is read, so the sentence and the pane state can never
136
+ // disagree about which failure this is.
137
+ function denied(where, message) {
138
+ return where === "scan" && String(message).toLowerCase().indexOf("permission denied") >= 0
139
+ }
140
+
141
+ // ESTALE, ENOTCONN and ETIMEDOUT each read differently; the backend's words are src/error.rs.
142
+ function shareStale(message) {
143
+ var text = String(message).toLowerCase()
144
+ return text.indexOf("stale") >= 0 || text.indexOf("no longer available") >= 0
145
+ }
146
+ function shareGone(message) {
147
+ var text = String(message).toLowerCase()
148
+ return text.indexOf("not connected") >= 0
149
+ }
150
+ function shareSilent(message) {
151
+ var text = String(message).toLowerCase()
152
+ return text.indexOf("timed out") >= 0 || text.indexOf("timeout") >= 0
153
+ }
154
+
155
+ // Which state the listing area reaches when a listing fails. A denial is the canvas's Locked tile on
156
+ // States.dc.html, which draws the lock mark over the directory's own mode string; the rest are Error.
157
+ function listingState(where, message) {
158
+ return denied(where, message) ? "locked" : "error"
159
+ }
160
+
161
+ // That tile's one line, drawn verbatim as "rwx------ · not yours". src/backend/meta.rs answers mode 0
162
+ // when the stat itself failed, and a real st_mode always carries its file-type bits, so a zero here
163
+ // means "I could not look" and earns no line rather than a false "---------".
164
+ function lockedLine(mode) {
165
+ if (!(mode > 0)) {
166
+ return ""
167
+ }
168
+ return Format.permissions(mode) + notYours(mode)
169
+ }
170
+
171
+ // Entering a directory needs its execute bit and listing it needs its read bit, so an owner holding
172
+ // both would not have been denied: a denial on such a directory proves the operator is not the
173
+ // owner. Below that the owner is locked out too, and the line claims nothing it cannot know.
174
+ var OWNER_CAN_LIST = 0o500
175
+
176
+ // A readable, searchable owner mode proves an observed listing denial belongs to another user.
177
+ function notYours(mode) {
178
+ return (mode & OWNER_CAN_LIST) === OWNER_CAN_LIST ? " · not yours" : ""
179
+ }
180
+
181
+ // 124 and 126/127 name a cause and 77 is tools/flea-gio-auth's proof of a refusal; gio's text is suppressed, so no other status names one.
182
+ function connectFailure(exitCode) {
183
+ if (exitCode === 124) return WORDS.connectNoAnswer
184
+ if (exitCode === 126 || exitCode === 127)
185
+ return WORDS.connectNoPrompt
186
+ if (exitCode === 77) return WORDS.connectNoPassword
187
+ return WORDS.connectUnopened
188
+ }
@@ -0,0 +1,115 @@
1
+ .pragma library
2
+
3
+ .import "Thumbs.js" as Thumbs
4
+ .import "Format.js" as Format
5
+
6
+ // The ExtThumbs board's storage-class gate: network shares and phones are off, USB
7
+ // drives are on, and anything else is local and always gets thumbnails. A class is
8
+ // src/backend/extclass.rs's own word, carried beside the fsinfo line once per
9
+ // directory change, so nothing here classifies and there is no second classifier.
10
+
11
+ // Whole sentences with "%1" for a value, filled by fill so each translates as one; one per state.
12
+ var WORDS = {
13
+ hide: "Hide %1 thumbnails",
14
+ show: "Show %1 thumbnails",
15
+ isOn: "%1 thumbnails are on",
16
+ isOff: "%1 thumbnails are off"
17
+ }
18
+
19
+ // Sample input: "network" answers "thumbNetwork", "" answers "".
20
+ function keyForClass(storageClass) {
21
+ if (storageClass === "network") return "thumbNetwork"
22
+ if (storageClass === "phone") return "thumbPhone"
23
+ if (storageClass === "usb") return "thumbUsb"
24
+ return ""
25
+ }
26
+
27
+ // The menu row is present only on network, phone or USB storage, never locally.
28
+ function present(storageClass) {
29
+ return keyForClass(storageClass) !== ""
30
+ }
31
+
32
+ // Sample input: ("usb", {}) answers true, ("network", {}) answers false.
33
+ function classOn(storageClass, preview) {
34
+ var key = keyForClass(storageClass)
35
+ if (key === "") return true
36
+ if (key === "thumbUsb") return !preview || preview.thumbUsb !== false
37
+ return !!preview && preview[key] === true
38
+ }
39
+
40
+ function classOff(storageClass, preview) {
41
+ return present(storageClass) && !classOn(storageClass, preview)
42
+ }
43
+
44
+ // Off is cache-only, not blank: a visible row still asks, and the backend answers from
45
+ // the shared cache with one stat and never starts a decoder. Thumbnails already made
46
+ // still show, because the cache answers them. Sample input: ("network", {}) answers true.
47
+ function cacheOnly(storageClass, preview) {
48
+ if (!classOff(storageClass, preview)) return false
49
+ return !preview || preview.thumbnails !== "off"
50
+ }
51
+
52
+ // The thumbed line's record: a cache-only miss on a generating class returns to unasked, any other reply is remembered.
53
+ function thumbReply(state, row, file, cap, known, storageClass, preview) {
54
+ if (file === "" && state.file[row] === Thumbs.CACHE_ASKED && (!known || !cacheOnly(storageClass, preview)))
55
+ return { state: Thumbs.miss(state, row, true, cap), missed: true }
56
+ return { state: Thumbs.remember(state, row, file, cap), missed: false }
57
+ }
58
+
59
+ // In Columns the preview is manual on a class that is off: one condition, two surfaces.
60
+ function manualHold(storageClass, preview) {
61
+ return cacheOnly(storageClass, preview)
62
+ }
63
+
64
+ // The menu row's own entrance: absent off the three classes, otherwise labelled for
65
+ // the class and its state. Sample input: ({storageClass: "usb"}) answers "Hide USB thumbnails".
66
+ function entry(e, p) {
67
+ if (!present(p.storageClass)) return false
68
+ e.label = label(p.storageClass, classOn(p.storageClass, p.thumbPreview))
69
+ return true
70
+ }
71
+
72
+ // Sample input: ("network", false) answers "Show network thumbnails".
73
+ function label(storageClass, on) {
74
+ var noun = storageClass === "usb" ? "USB" : storageClass
75
+ return Format.fill(on ? WORDS.hide : WORDS.show, noun)
76
+ }
77
+
78
+ // The status bar's own verdict for the toggle, in the board's words.
79
+ // Sample input: ("network", true) answers "Network thumbnails are on".
80
+ function statusLine(storageClass, on) {
81
+ var noun = storageClass === "usb" ? "USB"
82
+ : storageClass.charAt(0).toUpperCase() + storageClass.slice(1)
83
+ return Format.fill(on ? WORDS.isOn : WORDS.isOff, noun)
84
+ }
85
+
86
+ // A class switched on decodes again: only its cache-only marks leave the map, so a
87
+ // real refusal stays answered and is never decoded twice. Sample input:
88
+ // {file: {3: "", 4: "/c/x.png", 5: "cache-missed"}, order: [3, 4, 5]} forgets 5 alone.
89
+ function forgetMisses(state) {
90
+ var kept = []
91
+ for (var i = 0; i < state.order.length; i++) {
92
+ var at = state.order[i]
93
+ if (state.file[at] === Thumbs.CACHE_MISS || state.file[at] === Thumbs.CACHE_ASKED) delete state.file[at]
94
+ else kept.push(at)
95
+ }
96
+ state.order = kept
97
+ return { file: state.file, order: state.order }
98
+ }
99
+
100
+ // Sample input: ("network", {}) answers "only|media"; a zoom step or another class keeps it, so refreshes nothing.
101
+ function verdict(storageClass, preview) {
102
+ var mode = preview && preview.thumbnails ? preview.thumbnails : "media"
103
+ return (cacheOnly(storageClass, preview) ? "only" : "full") + "|" + mode
104
+ }
105
+
106
+ // FileView reads the whole file, so a row over the gate is refused, not truncated, the
107
+ // same rule ui/PreviewText.qml already keeps. Text previews on network and phone storage
108
+ // read at most the first 256 KiB. Sample input: ("phone") answers 262144.
109
+ var TEXT_PREVIEW_MAX = 1048576
110
+ var REMOTE_TEXT_PREVIEW_MAX = 262144
111
+
112
+ function textLimit(storageClass) {
113
+ if (storageClass === "network" || storageClass === "phone") return REMOTE_TEXT_PREVIEW_MAX
114
+ return TEXT_PREVIEW_MAX
115
+ }