flea-qml 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (625) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +478 -2
  3. package/THIRD-PARTY-NOTICES.md +29 -0
  4. package/demos/App.java +5 -0
  5. package/demos/Dockerfile +4 -0
  6. package/demos/Main.kt +3 -0
  7. package/demos/Program.cs +1 -0
  8. package/demos/README.md +12 -0
  9. package/demos/app.dart +3 -0
  10. package/demos/app.deb +0 -0
  11. package/demos/app.js +1 -0
  12. package/demos/backup.tar +0 -0
  13. package/demos/budget.xlsx +0 -0
  14. package/demos/build.sh +2 -0
  15. package/demos/bundle.zip +0 -0
  16. package/demos/clip.mp4 +0 -0
  17. package/demos/data.sqlite +0 -0
  18. package/demos/disk.iso +0 -0
  19. package/demos/engine.cpp +5 -0
  20. package/demos/firmware.bin +0 -0
  21. package/demos/flake.nix +4 -0
  22. package/demos/folder/inside.txt +1 -0
  23. package/demos/font.ttf +0 -0
  24. package/demos/index.html +5 -0
  25. package/demos/index.php +2 -0
  26. package/demos/init.lua +1 -0
  27. package/demos/letter.docx +0 -0
  28. package/demos/lib.rs +3 -0
  29. package/demos/main.c +6 -0
  30. package/demos/main.h +3 -0
  31. package/demos/math-and-diagrams.md +200 -0
  32. package/demos/notes.tar.gz +0 -0
  33. package/demos/notes.txt +3 -0
  34. package/demos/photo.png +0 -0
  35. package/demos/program +2 -0
  36. package/demos/report.pdf +32 -0
  37. package/demos/script.py +1 -0
  38. package/demos/server.go +7 -0
  39. package/demos/settings.json +4 -0
  40. package/demos/slides.pptx +0 -0
  41. package/demos/song.mp3 +0 -0
  42. package/demos/style.css +4 -0
  43. package/demos/tool.rb +1 -0
  44. package/demos/types.ts +2 -0
  45. package/docs/pele.md +23 -0
  46. package/docs/protocol.md +3420 -0
  47. package/docs/temml.md +63 -0
  48. package/index.js +107 -0
  49. package/package.json +34 -4
  50. package/src/archive.js +402 -0
  51. package/src/archiveops.js +697 -0
  52. package/src/backend.js +369 -0
  53. package/src/bulkrename.js +95 -0
  54. package/src/clip-host.js +76 -0
  55. package/src/clip.js +462 -0
  56. package/src/code.js +168 -0
  57. package/src/compare.js +74 -0
  58. package/src/create.js +128 -0
  59. package/src/dirsize.js +147 -0
  60. package/src/figure-svg.js +530 -0
  61. package/src/figure-thread.js +23 -0
  62. package/src/figure.js +132 -0
  63. package/src/find.js +55 -0
  64. package/src/flea.js +39 -0
  65. package/src/fsinfo.js +39 -0
  66. package/src/highlight.js +141 -0
  67. package/src/html.js +52 -0
  68. package/src/kinds.js +89 -0
  69. package/src/listing.js +130 -0
  70. package/src/markdown-html.js +317 -0
  71. package/src/mathml.js +186 -0
  72. package/src/menu.js +180 -0
  73. package/src/meta.js +129 -0
  74. package/src/office.js +810 -0
  75. package/src/order.js +100 -0
  76. package/src/peek.js +49 -0
  77. package/src/pele-wrapper.js +695 -0
  78. package/src/permissions.js +180 -0
  79. package/src/places.js +313 -0
  80. package/src/preview-language.js +10 -0
  81. package/src/transfer.js +669 -0
  82. package/src/trash-record.js +41 -0
  83. package/src/trash.js +210 -0
  84. package/src/trashbrowse.js +326 -0
  85. package/src/uistate.js +393 -0
  86. package/src/uistore.js +168 -0
  87. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  88. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  89. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  90. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  91. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  92. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  93. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  94. package/src/vendor/pele.js +145 -0
  95. package/src/vendor/temml.mjs +14708 -0
  96. package/src/watch.js +98 -0
  97. package/src/wayland/client.js +109 -0
  98. package/src/wayland/protocol.js +55 -0
  99. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  100. package/src/wayland/protocols/wayland.xml +3248 -0
  101. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  102. package/src/wayland/resource-io.js +6 -0
  103. package/src/wayland/socket-discovery.js +26 -0
  104. package/src/wayland/transport-posix.js +120 -0
  105. package/src/wayland/transport-win32.js +11 -0
  106. package/src/wayland/transport.js +3 -0
  107. package/src/wayland/win32-resources.js +80 -0
  108. package/src/wayland/win32-wire.js +62 -0
  109. package/test/archive.test.js +80 -0
  110. package/test/bench-markdown-scroll.mjs +190 -0
  111. package/test/clip-host.test.js +113 -0
  112. package/test/code-preview.test.js +35 -0
  113. package/test/compare-ui-paths.test.js +24 -0
  114. package/test/compare.test.js +54 -0
  115. package/test/delete-trash.test.js +67 -0
  116. package/test/dirsize.test.js +138 -0
  117. package/test/file-ops.test.js +241 -0
  118. package/test/find-contents.test.js +161 -0
  119. package/test/find-input.test.js +66 -0
  120. package/test/fixtures/pele-parity.json +1006 -0
  121. package/test/highlight-launcher.test.js +35 -0
  122. package/test/html-preview.test.js +22 -0
  123. package/test/input.test.js +15 -0
  124. package/test/keymap.test.js +26 -0
  125. package/test/language-icons.test.js +36 -0
  126. package/test/launcher.test.js +51 -0
  127. package/test/manual-code-scroll.mjs +28 -0
  128. package/test/manual-column-scroll.mjs +24 -0
  129. package/test/manual-compare-ui.mjs +20 -0
  130. package/test/manual-find-status.mjs +37 -0
  131. package/test/manual-html-security.mjs +23 -0
  132. package/test/manual-html-ui.mjs +30 -0
  133. package/test/manual-syntax-preview.mjs +26 -0
  134. package/test/manual-trash-ui.mjs +86 -0
  135. package/test/markdown-diagrams.test.js +93 -0
  136. package/test/markdown-html.test.js +105 -0
  137. package/test/mathml.test.js +92 -0
  138. package/test/menu-transfer.test.js +57 -0
  139. package/test/office-preview.test.js +154 -0
  140. package/test/pele-parity.test.js +28 -0
  141. package/test/places.test.js +134 -0
  142. package/test/preview-keys.test.js +60 -0
  143. package/test/preview-language.test.js +22 -0
  144. package/test/status-filename.test.js +20 -0
  145. package/test/transfer-replace-merge.test.js +187 -0
  146. package/test/trash-address.test.js +23 -0
  147. package/test/trash-view.test.js +37 -0
  148. package/test/trashbrowse.test.js +135 -0
  149. package/ui/AboutFacts.qml +78 -0
  150. package/ui/ArchiveCards.qml +68 -0
  151. package/ui/ArchiveCompressDialog.qml +378 -0
  152. package/ui/ArchivePasswordDialog.qml +257 -0
  153. package/ui/Backend.qml +771 -0
  154. package/ui/BulkOldNames.qml +47 -0
  155. package/ui/BulkRenameDialog.qml +380 -0
  156. package/ui/CardFrame.qml +67 -0
  157. package/ui/CardScroll.qml +79 -0
  158. package/ui/CheckBox.qml +73 -0
  159. package/ui/ChromeBar.qml +360 -0
  160. package/ui/ChromeButton.qml +131 -0
  161. package/ui/ChromeModes.qml +43 -0
  162. package/ui/CodeLines.qml +256 -0
  163. package/ui/CodeRefusal.qml +52 -0
  164. package/ui/CollideConfirm.qml +147 -0
  165. package/ui/CollideHost.qml +167 -0
  166. package/ui/ColumnPane.qml +523 -0
  167. package/ui/ColumnRow.qml +380 -0
  168. package/ui/ColumnsArea.qml +594 -0
  169. package/ui/ColumnsLanding.qml +147 -0
  170. package/ui/CompareView.qml +321 -0
  171. package/ui/ContextMenu.qml +743 -0
  172. package/ui/ConvertDialog.qml +456 -0
  173. package/ui/CountedWorker.qml +7 -0
  174. package/ui/Crumb.qml +64 -0
  175. package/ui/CrumbDrop.qml +25 -0
  176. package/ui/CsvTable.qml +394 -0
  177. package/ui/CursorMark.qml +41 -0
  178. package/ui/DefaultClaim.qml +130 -0
  179. package/ui/DeviceLock.qml +47 -0
  180. package/ui/DeviceMounts.qml +540 -0
  181. package/ui/DialogButton.qml +150 -0
  182. package/ui/DialogField.qml +147 -0
  183. package/ui/DialogHeader.qml +31 -0
  184. package/ui/DialogScrim.qml +20 -0
  185. package/ui/DialogTitle.qml +37 -0
  186. package/ui/Divider.qml +9 -0
  187. package/ui/DropInto.qml +126 -0
  188. package/ui/DropTarget.qml +35 -0
  189. package/ui/DropboxMark.qml +60 -0
  190. package/ui/EmptyState.qml +150 -0
  191. package/ui/EscapeHint.qml +11 -0
  192. package/ui/FactsTable.qml +54 -0
  193. package/ui/FastScrollHandler.qml +476 -0
  194. package/ui/FatNamesConfirm.qml +157 -0
  195. package/ui/Favourites.qml +143 -0
  196. package/ui/FigureService.qml +120 -0
  197. package/ui/FileDrag.qml +267 -0
  198. package/ui/FilterStrip.qml +133 -0
  199. package/ui/FitMetrics.qml +22 -0
  200. package/ui/FlashDriveRows.qml +137 -0
  201. package/ui/FlashHost.qml +72 -0
  202. package/ui/FlashImageRow.qml +104 -0
  203. package/ui/FlashRunner.qml +119 -0
  204. package/ui/FlashStepRow.qml +172 -0
  205. package/ui/FlashStrip.qml +38 -0
  206. package/ui/FlashView.qml +397 -0
  207. package/ui/FleaMark.qml +88 -0
  208. package/ui/FlyoutFade.qml +23 -0
  209. package/ui/FontPane.qml +170 -0
  210. package/ui/Glass.qml +114 -0
  211. package/ui/GlassTagger.qml +59 -0
  212. package/ui/Glyph.qml +64 -0
  213. package/ui/GlyphSolid.qml +22 -0
  214. package/ui/GridArea.qml +664 -0
  215. package/ui/GridTile.qml +325 -0
  216. package/ui/GvfsBridge.qml +164 -0
  217. package/ui/Header.qml +433 -0
  218. package/ui/HoverTimers.qml +39 -0
  219. package/ui/HtmlClip.qml +30 -0
  220. package/ui/HtmlPane.qml +63 -0
  221. package/ui/HyprlandMark.qml +45 -0
  222. package/ui/Ipc.qml +1005 -0
  223. package/ui/IpcControls.qml +7 -0
  224. package/ui/IpcMenuState.qml +48 -0
  225. package/ui/IpcPreviewState.qml +185 -0
  226. package/ui/JumpPath.qml +51 -0
  227. package/ui/KeymapSheet.qml +561 -0
  228. package/ui/KeymapSheetResult.qml +89 -0
  229. package/ui/KindStack.qml +40 -0
  230. package/ui/LangMark.qml +51 -0
  231. package/ui/LangMarkLoader.qml +26 -0
  232. package/ui/List.qml +480 -0
  233. package/ui/LoadingState.qml +54 -0
  234. package/ui/LocalSend.qml +56 -0
  235. package/ui/LocalSendMark.qml +73 -0
  236. package/ui/MarkdownBlockView.qml +311 -0
  237. package/ui/MarkdownBlocks.qml +35 -0
  238. package/ui/MarkdownFigure.qml +254 -0
  239. package/ui/MarkdownImages.qml +72 -0
  240. package/ui/MarkdownList.qml +81 -0
  241. package/ui/MarkdownMathsText.qml +97 -0
  242. package/ui/MarkdownPane.qml +83 -0
  243. package/ui/MarkdownPictures.qml +41 -0
  244. package/ui/MarkdownQuote.qml +52 -0
  245. package/ui/MarkdownRemote.qml +82 -0
  246. package/ui/MarkdownSource.qml +155 -0
  247. package/ui/MarkdownTable.qml +222 -0
  248. package/ui/MarkdownTableScroll.qml +64 -0
  249. package/ui/MarkdownTableWheel.qml +126 -0
  250. package/ui/MarkdownText.qml +87 -0
  251. package/ui/MarkdownWorker.js +132 -0
  252. package/ui/MatchText.qml +99 -0
  253. package/ui/MediaSession.qml +38 -0
  254. package/ui/MediaSound.qml +26 -0
  255. package/ui/MediaStrip.qml +191 -0
  256. package/ui/MediaTrack.qml +36 -0
  257. package/ui/MediaVolume.qml +54 -0
  258. package/ui/MenuActionDialog.qml +396 -0
  259. package/ui/MenuEdgeFade.qml +24 -0
  260. package/ui/MenuHost.qml +227 -0
  261. package/ui/MenuRow.qml +351 -0
  262. package/ui/MountListing.qml +100 -0
  263. package/ui/NetworkDialog.qml +435 -0
  264. package/ui/NetworkForm.qml +426 -0
  265. package/ui/NetworkMounts.qml +913 -0
  266. package/ui/NetworkPlaces.qml +127 -0
  267. package/ui/OpenWithDialog.qml +613 -0
  268. package/ui/Opener.qml +115 -0
  269. package/ui/Pane.qml +1296 -0
  270. package/ui/PaneMenuActions.qml +527 -0
  271. package/ui/PanePath.qml +90 -0
  272. package/ui/PaneRail.qml +239 -0
  273. package/ui/PaneStates.qml +88 -0
  274. package/ui/PaneSwap.qml +339 -0
  275. package/ui/PaneWire.qml +605 -0
  276. package/ui/ParkedWait.qml +9 -0
  277. package/ui/PathEntry.qml +186 -0
  278. package/ui/PathJump.qml +400 -0
  279. package/ui/PdfViewer.qml +297 -0
  280. package/ui/PermissionsDialog.qml +658 -0
  281. package/ui/PermissionsHeadings.qml +31 -0
  282. package/ui/PhoneMounts.qml +67 -0
  283. package/ui/PickerAnswers.qml +75 -0
  284. package/ui/PickerBox.qml +26 -0
  285. package/ui/PickerChrome.qml +295 -0
  286. package/ui/PickerFilter.qml +97 -0
  287. package/ui/PickerGrid.qml +308 -0
  288. package/ui/PickerHeader.qml +28 -0
  289. package/ui/PickerLifecycle.qml +27 -0
  290. package/ui/PickerList.qml +218 -0
  291. package/ui/PickerListing.qml +149 -0
  292. package/ui/PickerPathBar.qml +127 -0
  293. package/ui/PickerPlaces.qml +189 -0
  294. package/ui/PickerRecent.qml +92 -0
  295. package/ui/PickerSave.qml +101 -0
  296. package/ui/PickerSelection.qml +155 -0
  297. package/ui/PickerWindow.qml +846 -0
  298. package/ui/PowerSectorsReader.qml +10 -0
  299. package/ui/PrebuildDriver.qml +146 -0
  300. package/ui/Preview.qml +1004 -0
  301. package/ui/PreviewAnimation.qml +24 -0
  302. package/ui/PreviewArchive.qml +66 -0
  303. package/ui/PreviewColumn.qml +551 -0
  304. package/ui/PreviewEdge.qml +63 -0
  305. package/ui/PreviewFacts.qml +30 -0
  306. package/ui/PreviewFactsLine.qml +34 -0
  307. package/ui/PreviewHtml.qml +233 -0
  308. package/ui/PreviewImage.qml +253 -0
  309. package/ui/PreviewImagePan.qml +13 -0
  310. package/ui/PreviewImageSharp.qml +32 -0
  311. package/ui/PreviewLines.qml +106 -0
  312. package/ui/PreviewMarkdown.qml +706 -0
  313. package/ui/PreviewMedia.qml +250 -0
  314. package/ui/PreviewPdf.qml +482 -0
  315. package/ui/PreviewScroll.qml +201 -0
  316. package/ui/PreviewSwap.qml +335 -0
  317. package/ui/PreviewText.qml +149 -0
  318. package/ui/ProtocolChip.qml +18 -0
  319. package/ui/QuickLookArchive.qml +38 -0
  320. package/ui/QuickLookBar.qml +103 -0
  321. package/ui/QuickLookPrepare.qml +355 -0
  322. package/ui/QuickLookSwap.qml +10 -0
  323. package/ui/QuickLookWarm.qml +65 -0
  324. package/ui/RailDrop.qml +155 -0
  325. package/ui/RailHeading.qml +19 -0
  326. package/ui/RailPlaces.qml +38 -0
  327. package/ui/RailTrashRow.qml +67 -0
  328. package/ui/RenameField.qml +353 -0
  329. package/ui/RendererRetry.qml +11 -0
  330. package/ui/ResizeHandle.qml +52 -0
  331. package/ui/Reveal.qml +54 -0
  332. package/ui/Row.qml +476 -0
  333. package/ui/RowDrag.qml +213 -0
  334. package/ui/RowDropTarget.qml +27 -0
  335. package/ui/RunCard.qml +286 -0
  336. package/ui/Scripts.qml +65 -0
  337. package/ui/SearchStrip.qml +232 -0
  338. package/ui/SelectionBand.qml +140 -0
  339. package/ui/SelectionPreview.qml +370 -0
  340. package/ui/SettingsFavourite.qml +122 -0
  341. package/ui/SettingsGroup.qml +96 -0
  342. package/ui/SettingsHint.qml +12 -0
  343. package/ui/SettingsMetrics.qml +29 -0
  344. package/ui/SettingsPane.qml +178 -0
  345. package/ui/SettingsPanel.qml +547 -0
  346. package/ui/SettingsRail.qml +128 -0
  347. package/ui/SettingsRow.qml +409 -0
  348. package/ui/SettingsRuler.qml +85 -0
  349. package/ui/SettingsSegment.qml +85 -0
  350. package/ui/ShareBrowser.qml +113 -0
  351. package/ui/ShareLink.qml +67 -0
  352. package/ui/ShelfPins.qml +101 -0
  353. package/ui/Sidebar.qml +732 -0
  354. package/ui/SidebarRow.qml +358 -0
  355. package/ui/Spinner.qml +60 -0
  356. package/ui/StableModel.qml +89 -0
  357. package/ui/StateMessage.qml +84 -0
  358. package/ui/StatusBar.qml +512 -0
  359. package/ui/StatusFileName.qml +27 -0
  360. package/ui/TabBar.qml +673 -0
  361. package/ui/TabDragGeometry.qml +60 -0
  362. package/ui/TagMenuMark.qml +52 -0
  363. package/ui/TagRailSection.qml +53 -0
  364. package/ui/TagSlot.qml +40 -0
  365. package/ui/TagState.qml +150 -0
  366. package/ui/TagSwatch.qml +28 -0
  367. package/ui/Taildrop.qml +86 -0
  368. package/ui/TailscaleMark.qml +50 -0
  369. package/ui/Theme.qml +460 -0
  370. package/ui/ThemeColors.qml +43 -0
  371. package/ui/TransferCard.qml +239 -0
  372. package/ui/TrashConfirm.qml +129 -0
  373. package/ui/TrashDropHandler.qml +11 -0
  374. package/ui/TrashHost.qml +202 -0
  375. package/ui/TrashMonitor.qml +30 -0
  376. package/ui/TrashToolbar.qml +19 -0
  377. package/ui/TrashView.qml +492 -0
  378. package/ui/UnlockDialog.qml +450 -0
  379. package/ui/UnlockHost.qml +206 -0
  380. package/ui/UpdateCheck.qml +97 -0
  381. package/ui/UserDirs.qml +25 -0
  382. package/ui/ViewState.qml +565 -0
  383. package/ui/ViewportScrollBar.qml +195 -0
  384. package/ui/ViewportScrollBars.qml +25 -0
  385. package/ui/WebPreview.qml +33 -0
  386. package/ui/WebPreviewView.qml +26 -0
  387. package/ui/WindowBody.qml +827 -0
  388. package/ui/WindowDrag.qml +35 -0
  389. package/ui/boot/Commons/AudioNodes.qml +50 -0
  390. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  391. package/ui/boot/Commons/Border.qml +243 -0
  392. package/ui/boot/Commons/BorderGeometry.js +373 -0
  393. package/ui/boot/Commons/Color.qml +258 -0
  394. package/ui/boot/Commons/I18n.qml +118 -0
  395. package/ui/boot/Commons/InputDevices.qml +112 -0
  396. package/ui/boot/Commons/InputMethodState.qml +38 -0
  397. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  398. package/ui/boot/Commons/Numbers.js +19 -0
  399. package/ui/boot/Commons/Plural.js +91 -0
  400. package/ui/boot/Commons/ShellIpc.qml +12 -0
  401. package/ui/boot/Commons/Style.qml +561 -0
  402. package/ui/boot/Commons/Translations.qml +100 -0
  403. package/ui/boot/Commons/UntypedInput.qml +152 -0
  404. package/ui/boot/Commons/Util.qml +188 -0
  405. package/ui/boot/Commons/qmldir +13 -0
  406. package/ui/boot/LICENSE-omarchy +20 -0
  407. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  408. package/ui/boot/Ui/BarIconButton.qml +106 -0
  409. package/ui/boot/Ui/BarIconContent.qml +38 -0
  410. package/ui/boot/Ui/BarIndicator.qml +53 -0
  411. package/ui/boot/Ui/BarWidget.qml +50 -0
  412. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  413. package/ui/boot/Ui/BorderSurface.qml +40 -0
  414. package/ui/boot/Ui/Button.qml +224 -0
  415. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  416. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  417. package/ui/boot/Ui/CursorSurface.qml +42 -0
  418. package/ui/boot/Ui/Dropdown.qml +245 -0
  419. package/ui/boot/Ui/FilterInput.qml +26 -0
  420. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  421. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  422. package/ui/boot/Ui/MultiSelect.qml +624 -0
  423. package/ui/boot/Ui/NumberField.qml +86 -0
  424. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  425. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  426. package/ui/boot/Ui/Panel.qml +60 -0
  427. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  428. package/ui/boot/Ui/PanelController.qml +16 -0
  429. package/ui/boot/Ui/PanelHero.qml +112 -0
  430. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  431. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  432. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  433. package/ui/boot/Ui/PanelSlider.qml +156 -0
  434. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  435. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  436. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  437. package/ui/boot/Ui/PopupCard.qml +181 -0
  438. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  439. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  440. package/ui/boot/Ui/SizedImage.qml +125 -0
  441. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  442. package/ui/boot/Ui/TextField.qml +58 -0
  443. package/ui/boot/Ui/Toggle.qml +118 -0
  444. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  445. package/ui/boot/Ui/WidgetButton.qml +172 -0
  446. package/ui/boot/Ui/qmldir +41 -0
  447. package/ui/boot/fleatab.qml +50 -0
  448. package/ui/boot/picker.qml +39 -0
  449. package/ui/boot/shell.qml +122 -0
  450. package/ui/boot/tabtearoff.qml +51 -0
  451. package/ui/js/Anchor.js +379 -0
  452. package/ui/js/AnchorHold.js +162 -0
  453. package/ui/js/Archive.js +157 -0
  454. package/ui/js/ArchiveCard.js +44 -0
  455. package/ui/js/ArchiveOps.js +44 -0
  456. package/ui/js/BackReturn.js +118 -0
  457. package/ui/js/BulkRename.js +100 -0
  458. package/ui/js/Buttons.js +54 -0
  459. package/ui/js/ChromeKeys.js +116 -0
  460. package/ui/js/ClipMarks.js +73 -0
  461. package/ui/js/ClipSelection.js +67 -0
  462. package/ui/js/Clipboard.js +179 -0
  463. package/ui/js/Cloud.js +97 -0
  464. package/ui/js/CodeRuns.js +72 -0
  465. package/ui/js/Collide.js +212 -0
  466. package/ui/js/ColumnFit.js +28 -0
  467. package/ui/js/ColumnMenu.js +111 -0
  468. package/ui/js/Columns.js +298 -0
  469. package/ui/js/ColumnsCarry.js +103 -0
  470. package/ui/js/Compare.js +74 -0
  471. package/ui/js/Contrast.js +157 -0
  472. package/ui/js/ControlReader.js +42 -0
  473. package/ui/js/Convert.js +52 -0
  474. package/ui/js/CopyAs.js +52 -0
  475. package/ui/js/Crumbs.js +178 -0
  476. package/ui/js/Csv.js +231 -0
  477. package/ui/js/Density.js +44 -0
  478. package/ui/js/Devices.js +440 -0
  479. package/ui/js/DirSizes.js +180 -0
  480. package/ui/js/Drag.js +364 -0
  481. package/ui/js/DragOut.js +166 -0
  482. package/ui/js/DropDev.js +52 -0
  483. package/ui/js/Dropbox.js +41 -0
  484. package/ui/js/Eject.js +217 -0
  485. package/ui/js/Errors.js +188 -0
  486. package/ui/js/ExtThumbs.js +115 -0
  487. package/ui/js/Facts.js +402 -0
  488. package/ui/js/FigureDate.js +14 -0
  489. package/ui/js/Filter.js +285 -0
  490. package/ui/js/Find.js +46 -0
  491. package/ui/js/Flash.js +184 -0
  492. package/ui/js/FlashCard.js +79 -0
  493. package/ui/js/FlashEndings.js +125 -0
  494. package/ui/js/FlashPane.js +116 -0
  495. package/ui/js/FleaBin.js +12 -0
  496. package/ui/js/Focus.js +502 -0
  497. package/ui/js/FolderSorts.js +108 -0
  498. package/ui/js/FolderViews.js +44 -0
  499. package/ui/js/Format.js +305 -0
  500. package/ui/js/Fuzzy.js +141 -0
  501. package/ui/js/GlassTag.js +72 -0
  502. package/ui/js/Grid.js +33 -0
  503. package/ui/js/GridGeometry.js +90 -0
  504. package/ui/js/GridNames.js +153 -0
  505. package/ui/js/GvfsBridge.js +200 -0
  506. package/ui/js/HostClipboard.js +29 -0
  507. package/ui/js/Icons.js +218 -0
  508. package/ui/js/Input.js +50 -0
  509. package/ui/js/Jump.js +200 -0
  510. package/ui/js/KeyName.js +64 -0
  511. package/ui/js/Keymap.js +401 -0
  512. package/ui/js/Kinds.js +164 -0
  513. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  514. package/ui/js/LangMarkPaths.js +47 -0
  515. package/ui/js/LangMarks.js +120 -0
  516. package/ui/js/Lists.js +11 -0
  517. package/ui/js/LocalSend.js +39 -0
  518. package/ui/js/LockedMenu.js +63 -0
  519. package/ui/js/MakeDefault.js +186 -0
  520. package/ui/js/MarkBox.js +7 -0
  521. package/ui/js/Markdown.js +119 -0
  522. package/ui/js/MarkdownLists.js +86 -0
  523. package/ui/js/MarkdownMaths.js +58 -0
  524. package/ui/js/MarkdownPictures.js +32 -0
  525. package/ui/js/MarkdownPrepared.js +139 -0
  526. package/ui/js/MarkdownTableFit.js +34 -0
  527. package/ui/js/Marks.js +148 -0
  528. package/ui/js/Marquee.js +96 -0
  529. package/ui/js/Match.js +93 -0
  530. package/ui/js/Material.js +109 -0
  531. package/ui/js/MdBlocks.js +437 -0
  532. package/ui/js/MdBreak.js +71 -0
  533. package/ui/js/MdChunks.js +70 -0
  534. package/ui/js/MdContainer.js +137 -0
  535. package/ui/js/MdDocument.js +337 -0
  536. package/ui/js/MdEmph.js +97 -0
  537. package/ui/js/MdEntity.js +108 -0
  538. package/ui/js/MdEntityTable.js +223 -0
  539. package/ui/js/MdEscape.js +59 -0
  540. package/ui/js/MdFront.js +37 -0
  541. package/ui/js/MdHold.js +32 -0
  542. package/ui/js/MdHtml.js +307 -0
  543. package/ui/js/MdHtmlBlock.js +290 -0
  544. package/ui/js/MdHtmlHeading.js +53 -0
  545. package/ui/js/MdHtmlImage.js +179 -0
  546. package/ui/js/MdInline.js +154 -0
  547. package/ui/js/MdItems.js +212 -0
  548. package/ui/js/MdLeaf.js +309 -0
  549. package/ui/js/MdLink.js +196 -0
  550. package/ui/js/MdMath.js +67 -0
  551. package/ui/js/MdParagraphs.js +138 -0
  552. package/ui/js/MdRefs.js +232 -0
  553. package/ui/js/MdResolve.js +226 -0
  554. package/ui/js/MdRun.js +300 -0
  555. package/ui/js/MdUrl.js +226 -0
  556. package/ui/js/Menu.js +514 -0
  557. package/ui/js/MenuContext.js +85 -0
  558. package/ui/js/MenuFit.js +13 -0
  559. package/ui/js/MenuRefresh.js +74 -0
  560. package/ui/js/MenuWheel.js +35 -0
  561. package/ui/js/Messages.js +186 -0
  562. package/ui/js/Motion.js +16 -0
  563. package/ui/js/Mounts.js +320 -0
  564. package/ui/js/MouseNav.js +13 -0
  565. package/ui/js/Nav.js +329 -0
  566. package/ui/js/NetFs.js +26 -0
  567. package/ui/js/OpenWith.js +83 -0
  568. package/ui/js/Ops.js +585 -0
  569. package/ui/js/Palette.js +33 -0
  570. package/ui/js/PathBar.js +260 -0
  571. package/ui/js/Permissions.js +179 -0
  572. package/ui/js/Phones.js +112 -0
  573. package/ui/js/Picker.js +353 -0
  574. package/ui/js/PlaceMenu.js +96 -0
  575. package/ui/js/Places.js +265 -0
  576. package/ui/js/PreviewAsk.js +21 -0
  577. package/ui/js/PreviewKeys.js +155 -0
  578. package/ui/js/PreviewSettle.js +29 -0
  579. package/ui/js/PreviewSwap.js +101 -0
  580. package/ui/js/PropertyFacts.js +144 -0
  581. package/ui/js/Protocols.js +296 -0
  582. package/ui/js/QuickLookKinds.js +27 -0
  583. package/ui/js/QuickLookRect.js +24 -0
  584. package/ui/js/RailDrop.js +51 -0
  585. package/ui/js/RailKeys.js +87 -0
  586. package/ui/js/RailMenu.js +133 -0
  587. package/ui/js/Recent.js +135 -0
  588. package/ui/js/RecentMode.js +156 -0
  589. package/ui/js/Reload.js +60 -0
  590. package/ui/js/Renderer.js +9 -0
  591. package/ui/js/RunFile.js +143 -0
  592. package/ui/js/Scripts.js +28 -0
  593. package/ui/js/Scroll.js +304 -0
  594. package/ui/js/ScrollOff.js +69 -0
  595. package/ui/js/Search.js +478 -0
  596. package/ui/js/Selection.js +97 -0
  597. package/ui/js/Settings.js +476 -0
  598. package/ui/js/SettingsAbout.js +97 -0
  599. package/ui/js/SettingsShelf.js +52 -0
  600. package/ui/js/SheetKeys.js +42 -0
  601. package/ui/js/SheetQuery.js +281 -0
  602. package/ui/js/ShelfPile.js +27 -0
  603. package/ui/js/SlowClick.js +53 -0
  604. package/ui/js/SlowOp.js +6 -0
  605. package/ui/js/Sort.js +134 -0
  606. package/ui/js/Startup.js +63 -0
  607. package/ui/js/StateWords.js +17 -0
  608. package/ui/js/Status.js +238 -0
  609. package/ui/js/Swap.js +166 -0
  610. package/ui/js/TabMove.js +39 -0
  611. package/ui/js/Tabs.js +736 -0
  612. package/ui/js/Tags.js +134 -0
  613. package/ui/js/Taildrop.js +128 -0
  614. package/ui/js/Tap.js +174 -0
  615. package/ui/js/TextSize.js +107 -0
  616. package/ui/js/Thumbs.js +158 -0
  617. package/ui/js/Transfer.js +172 -0
  618. package/ui/js/Trash.js +81 -0
  619. package/ui/js/TrashDates.js +47 -0
  620. package/ui/js/TrashDrop.js +64 -0
  621. package/ui/js/UiState.js +438 -0
  622. package/ui/js/Unlock.js +152 -0
  623. package/ui/js/Update.js +130 -0
  624. package/ui/js/UserDirs.js +56 -0
  625. package/ui/qmldir +228 -0
@@ -0,0 +1,378 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ import "." as Flea
4
+ import "js/Archive.js" as Archive
5
+ import "js/ArchiveCard.js" as ArchiveCard
6
+
7
+ // The With-password compress card: format, name, the password pair and the header switch, on the shipped dialog controls.
8
+ Item {
9
+ id: root
10
+
11
+ property bool opened: false
12
+ property var owner: null
13
+ property var source: null
14
+ property int count: 0
15
+ property string parentLeaf: ""
16
+ property var formats: ["7z", "zip"]
17
+ property string format: "7z"
18
+ property string stem: ""
19
+ property bool encryptNames: true
20
+ property string errorText: ""
21
+ property int focusPart: 0
22
+
23
+ signal accepted(var source, string format, string name, string password, bool encryptNames)
24
+
25
+ readonly property string password: passwordField.text
26
+ readonly property string confirm: confirmField.text
27
+ readonly property bool matched: root.password === root.confirm
28
+ // A password card compresses only with a password typed twice, so an empty pair never reaches the wire.
29
+ readonly property bool canCompress: root.opened && root.matched && root.password.length > 0 && root.name.length > 0
30
+ readonly property string name: nameField.text
31
+ readonly property var cancelItem: cancelButton
32
+ readonly property var submitItem: compressButton
33
+ readonly property var bodyItem: body
34
+ readonly property int dialogWidth: 448
35
+ readonly property int clampMargin: 8
36
+ readonly property var cardItem: card
37
+
38
+ // What a capture reads instead of the fields: the eye, which field wears the error frame, never a typed character.
39
+ function captureState() {
40
+ return { encrypt: root.encryptNames, encryptShown: encryptRow.visible, revealed: passwordField.revealed, eye: passwordField.eyeCentre(),
41
+ passwordError: passwordField.error, confirmError: confirmField.error }
42
+ }
43
+
44
+ anchors.fill: parent
45
+ visible: root.opened || root.opacity > 0
46
+ // Closed means no input, even while the fade-out is still drawn.
47
+ enabled: root.opened
48
+ opacity: 0
49
+ z: 2
50
+
51
+ Flea.Reveal { target: root; shown: root.opened; lift: card }
52
+ // The name clears once the fade ended, so the card stays still while it fades out; open() sets it again.
53
+ onOpacityChanged: if (root.opacity === 0 && !root.opened) nameField.text = ""
54
+
55
+ // The tab order; a disabled stop is skipped, so Tab never lands on a button that does nothing.
56
+ function tabStops() {
57
+ var stops = [formatRow, nameField.input, passwordField.input, confirmField.input]
58
+ if (root.format === "7z") stops.push(encryptRow)
59
+ return stops.concat([cancelButton, compressButton]).filter(function (s) { return s.enabled })
60
+ }
61
+
62
+ function stepTab(back) {
63
+ var stops = root.tabStops()
64
+ var at = -1
65
+ for (var i = 0; i < stops.length; i++) {
66
+ if (stops[i].activeFocus) { at = i; break }
67
+ }
68
+ var next = at < 0 ? (back ? stops.length - 1 : 0)
69
+ : (at + (back ? stops.length - 1 : 1)) % stops.length
70
+ // The rings read focusPart, so a ring follows the tab stop.
71
+ if (stops[next] === cancelButton) root.focusPart = 5
72
+ else if (stops[next] === compressButton) root.focusPart = 6
73
+ else if (stops[next] === encryptRow) root.focusPart = 4
74
+ else if (stops[next] === formatRow) root.focusPart = 1
75
+ else root.focusPart = 0
76
+ stops[next].forceActiveFocus()
77
+ }
78
+
79
+ Keys.onEscapePressed: function (event) { root.close(); event.accepted = true }
80
+ Keys.onTabPressed: function (event) { root.stepTab((event.modifiers & Qt.ShiftModifier) !== 0); event.accepted = true }
81
+ Keys.onBacktabPressed: function (event) { root.stepTab(true); event.accepted = true }
82
+ Keys.onPressed: function (event) {
83
+ if (formatRow.activeFocus
84
+ && (event.key === Qt.Key_Left || event.key === Qt.Key_Right)) {
85
+ var at = root.formats.indexOf(root.format)
86
+ root.chooseFormat(at + (event.key === Qt.Key_Right ? 1 : -1))
87
+ event.accepted = true
88
+ return
89
+ }
90
+ if (encryptRow.activeFocus && event.key === Qt.Key_Space) {
91
+ root.encryptNames = !root.encryptNames
92
+ event.accepted = true
93
+ return
94
+ }
95
+ if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter)
96
+ && (formatRow.activeFocus || encryptRow.activeFocus)) {
97
+ root.commit()
98
+ event.accepted = true
99
+ }
100
+ }
101
+
102
+ // Test seam for the threat walk: types into the pair the way keys would, the confirm apart when asked.
103
+ function takeSecretForTest(secret, confirm) {
104
+ passwordField.text = secret
105
+ confirmField.text = confirm === undefined ? secret : confirm
106
+ }
107
+
108
+ // The source is { paths, menuId }, kept whole so the menu id reaches the wire with the submit.
109
+ function open(source, leaf, holder) {
110
+ if (root.opened || !holder || !source || !source.paths || source.paths.length === 0) return
111
+ var paths = source.paths
112
+ root.owner = holder
113
+ var leaves = ArchiveCard.pathLeaves(paths)
114
+ root.source = source
115
+ root.count = paths.length
116
+ root.parentLeaf = leaf || ""
117
+ // The board's order, 7z first, whatever order the backend reports its formats in.
118
+ var have = holder.backend.archiveFormats || []
119
+ var offered = ["7z", "zip"].filter(function (f) { return have.indexOf(f) >= 0 })
120
+ root.formats = offered.length > 0 ? offered : ["7z"]
121
+ root.format = root.formats.indexOf("7z") >= 0 ? "7z" : root.formats[0]
122
+ root.stem = Archive.archiveStem(leaves, root.parentLeaf)
123
+ root.encryptNames = true
124
+ root.errorText = ""
125
+ root.focusPart = 2
126
+ root.opened = true
127
+ body.contentY = 0
128
+ nameField.text = root.stem + "." + root.format
129
+ passwordField.text = ""
130
+ confirmField.text = ""
131
+ passwordField.takeFocus()
132
+ }
133
+
134
+ // The fields clear on close, so a secret never outlives the card that took it.
135
+ function close() {
136
+ if (!root.opened)
137
+ return
138
+ passwordField.text = ""
139
+ confirmField.text = ""
140
+ root.stem = ""
141
+ root.source = null
142
+ root.opened = false
143
+ if (root.owner && root.owner.listArea)
144
+ root.owner.listArea.forceActiveFocus()
145
+ }
146
+
147
+ function chooseFormat(index) {
148
+ var next = root.formats[Math.max(0, Math.min(root.formats.length - 1, index))]
149
+ if (next === root.format) return
150
+ root.format = next
151
+ nameField.text = root.stem + "." + next
152
+ }
153
+
154
+ function commit() {
155
+ if (!root.opened) return
156
+ if (!root.canCompress) {
157
+ if (!root.matched) root.errorText = "Passwords do not match."
158
+ else if (root.password.length === 0) root.errorText = "Type a password first."
159
+ // Only an empty name is left: the old sentence goes and the focus moves to that field.
160
+ else { root.errorText = ""; root.focusPart = 0; nameField.takeFocus() }
161
+ return
162
+ }
163
+ root.errorText = ""
164
+ // Accepted before close, so the owner's submit still sees the source; close clears the fields.
165
+ root.accepted(root.source, root.format, root.name, root.password,
166
+ root.encryptNames && root.format === "7z")
167
+ root.close()
168
+ }
169
+
170
+ Flea.DialogScrim {
171
+ onDismissed: root.close()
172
+ }
173
+
174
+ Rectangle {
175
+ id: card
176
+ x: Theme.cardOrigin(root.width, width)
177
+ y: Theme.cardOrigin(root.height, height)
178
+ width: Theme.cardSpan(Theme.space(root.dialogWidth) * Theme.dialogWidthRatio, root.width - 2 * root.clampMargin)
179
+ // Clamped to the window; the body scrolls whatever the clamp cut, see ui/CardScroll.qml.
180
+ height: Theme.cardSpan(body.wanted + 2 * Theme.spacing.rowPaddingX, root.height - 2 * root.clampMargin)
181
+ color: Theme.color.surface
182
+ border.width: Theme.spacing.hairline
183
+ border.color: Theme.color.mutedFrame
184
+ radius: Style.cornerRadius
185
+
186
+ MouseArea {
187
+ anchors.fill: parent
188
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
189
+ hoverEnabled: true
190
+ onClicked: {}
191
+ onWheel: function (wheel) { wheel.accepted = true }
192
+ }
193
+
194
+ Flea.CardFrame {
195
+ id: body
196
+ bleedY: Theme.focusClearance
197
+ anchors.fill: parent
198
+ anchors.topMargin: Theme.spacing.rowPaddingX - Theme.focusClearance
199
+ anchors.bottomMargin: Theme.spacing.rowPaddingX - Theme.focusClearance
200
+ spacing: Theme.spacing.rowPaddingX
201
+
202
+ title: Item {
203
+ width: parent.width
204
+ height: header.implicitHeight + Theme.spacing.gap
205
+
206
+ Flea.DialogTitle {
207
+ id: header
208
+ anchors.left: parent.left
209
+ anchors.right: parent.right
210
+ anchors.margins: Theme.spacing.rowPaddingX
211
+ text: ArchiveCard.compressTitle(root.count)
212
+ elide: Text.ElideMiddle
213
+ }
214
+
215
+ Rectangle {
216
+ anchors.bottom: parent.bottom
217
+ x: Theme.spacing.rowPaddingX
218
+ width: parent.width - 2 * Theme.spacing.rowPaddingX
219
+ height: Theme.spacing.hairline
220
+ color: Theme.color.mutedFrame
221
+ opacity: 0.4
222
+ }
223
+ }
224
+
225
+ buttons: Item {
226
+ width: parent.width
227
+ height: Math.max(cancelButton.implicitHeight, compressButton.implicitHeight)
228
+ Row {
229
+ anchors.bottom: parent.bottom
230
+ anchors.right: parent.right
231
+ anchors.rightMargin: Theme.spacing.rowPaddingX
232
+ spacing: Theme.spacing.gap
233
+
234
+ Flea.DialogButton {
235
+ id: cancelButton
236
+ label: "Cancel"
237
+ focused: root.focusPart === 5
238
+ onActivated: root.close()
239
+ onTabbed: function (from, back) { root.stepTab(back) }
240
+ }
241
+
242
+ Flea.DialogButton {
243
+ id: compressButton
244
+ label: "Compress"
245
+ primary: true
246
+ focused: root.focusPart === 6
247
+ onActivated: root.commit()
248
+ onTabbed: function (from, back) { root.stepTab(back) }
249
+ }
250
+ }
251
+ }
252
+
253
+ Column {
254
+ width: parent.width
255
+ spacing: Theme.spacing.rowPaddingX
256
+
257
+ // The segment's own caption, in the same capitals and at the same distance DialogField draws above its box.
258
+ Column {
259
+ x: Theme.spacing.rowPaddingX
260
+ width: parent.width - 2 * Theme.spacing.rowPaddingX
261
+ spacing: Theme.spacing.hairline * 4
262
+
263
+ Text {
264
+ width: parent.width
265
+ text: "FORMAT"
266
+ color: Theme.color.muted
267
+ font.family: Theme.font.family
268
+ font.pixelSize: Theme.font.caption
269
+ font.letterSpacing: Theme.font.caption * nameField.labelSpacing
270
+ textFormat: Text.PlainText
271
+ }
272
+
273
+ Flea.SettingsSegment {
274
+ id: formatRow
275
+ tabStop: true
276
+ options: root.formats
277
+ value: root.format
278
+ onPicked: function (index) { root.chooseFormat(index) }
279
+ }
280
+ }
281
+
282
+ Flea.DialogField {
283
+ id: nameField
284
+ x: Theme.spacing.rowPaddingX
285
+ width: parent.width - 2 * Theme.spacing.rowPaddingX
286
+ label: "Name"
287
+ placeholder: "Archive.7z"
288
+ onAccepted: root.commit()
289
+ onTabbed: function (from, back) { root.stepTab(back) }
290
+ }
291
+
292
+ Flea.DialogField {
293
+ id: passwordField
294
+ x: Theme.spacing.rowPaddingX
295
+ width: parent.width - 2 * Theme.spacing.rowPaddingX
296
+ label: "Password"
297
+ secret: true
298
+ error: root.errorText.length > 0 && root.password.length === 0
299
+ onEdited: root.errorText = ""
300
+ onAccepted: root.commit()
301
+ onTabbed: function (from, back) { root.stepTab(back) }
302
+ }
303
+
304
+ // The extract card's recipe: the refused field in the error frame, its bare sentence directly under the pair.
305
+ Column {
306
+ x: Theme.spacing.rowPaddingX
307
+ width: parent.width - 2 * Theme.spacing.rowPaddingX
308
+ spacing: Theme.spacing.gap
309
+
310
+ Flea.DialogField {
311
+ id: confirmField
312
+ width: parent.width
313
+ label: "Confirm"
314
+ secret: true
315
+ error: root.errorText.length > 0 && !root.matched
316
+ onEdited: root.errorText = ""
317
+ onAccepted: root.commit()
318
+ onTabbed: function (from, back) { root.stepTab(back) }
319
+ }
320
+
321
+ Text {
322
+ width: parent.width
323
+ visible: root.errorText.length > 0
324
+ text: root.errorText
325
+ color: Theme.color.errorOnSurface
326
+ font { family: Theme.font.family; pixelSize: Theme.font.caption }
327
+ textFormat: Text.PlainText
328
+ wrapMode: Text.Wrap
329
+ }
330
+ }
331
+
332
+ Item {
333
+ id: encryptRow
334
+ width: parent.width
335
+ height: Theme.rowHeight
336
+ visible: root.format === "7z"
337
+ Accessible.role: Accessible.CheckBox
338
+ Accessible.name: "Encrypt file names"
339
+ Accessible.checkable: true
340
+ Accessible.checked: root.encryptNames
341
+
342
+ // An Item, not a Rectangle: a Rectangle with no color paints white behind the unchecked box.
343
+ Item {
344
+ id: box
345
+ anchors.left: parent.left
346
+ anchors.leftMargin: Theme.spacing.rowPaddingX
347
+ anchors.verticalCenter: parent.verticalCenter
348
+ width: ruleBox.implicitWidth
349
+ height: ruleBox.implicitHeight
350
+
351
+ Flea.CheckBox {
352
+ id: ruleBox
353
+ value: root.encryptNames ? "on" : "off"
354
+ focused: root.focusPart === 4
355
+ }
356
+ }
357
+
358
+ Text {
359
+ anchors.left: box.right
360
+ anchors.leftMargin: Theme.spacing.gap
361
+ anchors.verticalCenter: parent.verticalCenter
362
+ text: "Encrypt file names"
363
+ color: Theme.color.foreground
364
+ font.family: Theme.font.family
365
+ font.pixelSize: Theme.font.body
366
+ textFormat: Text.PlainText
367
+ }
368
+
369
+ TapHandler {
370
+ acceptedButtons: Qt.LeftButton
371
+ gesturePolicy: TapHandler.ReleaseWithinBounds
372
+ onTapped: root.encryptNames = !root.encryptNames
373
+ }
374
+ }
375
+ }
376
+ }
377
+ }
378
+ }
@@ -0,0 +1,257 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ import "." as Flea
4
+ import "js/Archive.js" as Archive
5
+ import "js/ArchiveCard.js" as ArchiveCard
6
+
7
+ // The encrypted archive's card: its name, one masked field with the eye, and Extract; a wrong answer reopens it with its sentence and as many bullets as were typed.
8
+ Item {
9
+ id: root
10
+
11
+ property bool opened: false
12
+ property var owner: null
13
+ property var source: null
14
+ property string leaf: ""
15
+ property string errorText: ""
16
+ property int focusPart: 0
17
+ // The archive and destination a submit sent, and how many characters it held; the secret itself is cleared on submit.
18
+ property string retryKey: ""
19
+ property int retryLength: 0
20
+
21
+ signal accepted(var source, string password)
22
+
23
+ readonly property string password: passwordField.text
24
+ readonly property bool canExtract: root.opened && root.password.length > 0
25
+ // How many bullets stand for a refused answer; a count, never a character of it.
26
+ readonly property int heldMarks: passwordField.heldMask
27
+ readonly property var cancelItem: cancelButton
28
+ readonly property var submitItem: extractButton
29
+ readonly property var bodyItem: body
30
+ readonly property int dialogWidth: 448
31
+ readonly property int clampMargin: 8
32
+ readonly property var cardItem: card
33
+
34
+ anchors.fill: parent
35
+ visible: root.opened || root.opacity > 0
36
+ // Closed means no input, even while the fade-out is still drawn.
37
+ enabled: root.opened
38
+ opacity: 0
39
+ z: 2
40
+
41
+ Flea.Reveal { target: root; shown: root.opened; lift: card }
42
+ // The sentence and the title clear once the fade ended, so the card stays still while it fades out; open() sets both again.
43
+ onOpacityChanged: if (root.opacity === 0 && !root.opened) { root.errorText = ""; root.leaf = "" }
44
+
45
+ // The tab order, answered at the root because the field owns Tab as a signal; a disabled stop is skipped.
46
+ function tabStops() {
47
+ return [passwordField.input, cancelButton, extractButton].filter(function (s) { return s.enabled })
48
+ }
49
+
50
+ function stepTab(back) {
51
+ var stops = root.tabStops()
52
+ var at = -1
53
+ for (var i = 0; i < stops.length; i++) {
54
+ if (stops[i].activeFocus) { at = i; break }
55
+ }
56
+ var next = at < 0 ? (back ? stops.length - 1 : 0)
57
+ : (at + (back ? stops.length - 1 : 1)) % stops.length
58
+ root.focusPart = stops[next] === cancelButton ? 1 : stops[next] === extractButton ? 2 : 0
59
+ stops[next].forceActiveFocus()
60
+ }
61
+
62
+ Keys.onEscapePressed: function (event) { root.close(); event.accepted = true }
63
+ Keys.onTabPressed: function (event) { root.stepTab((event.modifiers & Qt.ShiftModifier) !== 0); event.accepted = true }
64
+ Keys.onBacktabPressed: function (event) { root.stepTab(true); event.accepted = true }
65
+
66
+ // Test seam for the threat walk: types into the field the way keys would.
67
+ function takeSecretForTest(secret) {
68
+ passwordField.text = secret
69
+ }
70
+
71
+ // What a capture reads instead of the field: whether bullets are drawn, the error frame and the eye, never a typed character.
72
+ function captureState() {
73
+ return { filled: passwordField.text.length > 0 || passwordField.heldMask > 0, fieldError: passwordField.error,
74
+ revealed: passwordField.revealed, eye: passwordField.eyeCentre() }
75
+ }
76
+
77
+ function keyOf(path, dest) {
78
+ return String(path) + "\n" + String(dest)
79
+ }
80
+
81
+ // The source is { path, dest } and err the backend's code; a first ask has no sentence, and a wrong password on this card's own submit draws as many bullets as were typed.
82
+ function open(source, holder, err) {
83
+ if (root.opened || !holder || !source) return
84
+ var again = Archive.wrongPassword(err) && root.retryKey === root.keyOf(source.path, source.dest)
85
+ passwordField.text = ""
86
+ passwordField.heldMask = again ? root.retryLength : 0
87
+ root.retryKey = ""
88
+ root.retryLength = 0
89
+ root.owner = holder
90
+ root.source = source
91
+ root.leaf = ArchiveCard.sourceLeaf(source)
92
+ root.errorText = Archive.wrongPassword(err) ? ArchiveCard.wrongPasswordSentence(root.leaf) : ""
93
+ root.focusPart = 0
94
+ root.opened = true
95
+ body.contentY = 0
96
+ passwordField.takeFocus()
97
+ }
98
+
99
+ // The field clears on submit, cancel, esc and the ground, so a secret never outlives the card that took it.
100
+ function close() {
101
+ if (!root.opened)
102
+ return
103
+ passwordField.text = ""
104
+ passwordField.heldMask = 0
105
+ root.retryKey = ""
106
+ root.retryLength = 0
107
+ root.source = null
108
+ root.opened = false
109
+ if (root.owner && root.owner.listArea)
110
+ root.owner.listArea.forceActiveFocus()
111
+ root.owner = null
112
+ }
113
+
114
+ function commit() {
115
+ if (!root.opened) return
116
+ // Extract stays live, as the board draws it and a primary is never the dimmer button; an empty field asks for the password.
117
+ if (!root.canExtract) {
118
+ root.errorText = "Type a password first."
119
+ root.focusPart = 0
120
+ passwordField.takeFocus()
121
+ return
122
+ }
123
+ // Accepted before close, so the owner's submit still sees it; close clears the field and only the count stays.
124
+ var key = root.keyOf(root.source.path, root.source.dest)
125
+ var typed = root.password.length
126
+ root.accepted(root.source, root.password)
127
+ root.close()
128
+ root.retryKey = key
129
+ root.retryLength = typed
130
+ }
131
+
132
+ // A dimmed ground, and a click on it is a cancel, the same shape the convert dialog uses.
133
+ Rectangle {
134
+ anchors.fill: parent
135
+ color: Theme.color.background
136
+ opacity: 0.5
137
+
138
+ MouseArea {
139
+ anchors.fill: parent
140
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
141
+ hoverEnabled: true
142
+ onClicked: root.close()
143
+ onWheel: function (wheel) { wheel.accepted = true }
144
+ }
145
+ }
146
+
147
+ Rectangle {
148
+ id: card
149
+ x: Theme.cardOrigin(root.width, width)
150
+ y: Theme.cardOrigin(root.height, height)
151
+ width: Theme.cardSpan(Theme.space(root.dialogWidth) * Theme.dialogWidthRatio, root.width - 2 * root.clampMargin)
152
+ // Clamped to the window; the body scrolls whatever the clamp cut, see ui/CardScroll.qml.
153
+ height: Theme.cardSpan(body.wanted + 2 * Theme.spacing.rowPaddingX, root.height - 2 * root.clampMargin)
154
+ color: Theme.color.surface
155
+ border.width: Theme.spacing.hairline
156
+ border.color: Theme.color.mutedFrame
157
+ radius: Style.cornerRadius
158
+
159
+ MouseArea {
160
+ anchors.fill: parent
161
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
162
+ hoverEnabled: true
163
+ onClicked: {}
164
+ onWheel: function (wheel) { wheel.accepted = true }
165
+ }
166
+
167
+ Flea.CardFrame {
168
+ id: body
169
+ bleedY: Theme.focusClearance
170
+ anchors.fill: parent
171
+ anchors.topMargin: Theme.spacing.rowPaddingX - Theme.focusClearance
172
+ anchors.bottomMargin: Theme.spacing.rowPaddingX - Theme.focusClearance
173
+ spacing: Theme.spacing.rowPaddingX
174
+
175
+ title: Item {
176
+ width: parent.width
177
+ height: header.implicitHeight + Theme.spacing.gap
178
+
179
+ Flea.DialogTitle {
180
+ id: header
181
+ anchors.left: parent.left
182
+ anchors.right: parent.right
183
+ anchors.margins: Theme.spacing.rowPaddingX
184
+ text: ArchiveCard.encryptedTitle(root.leaf)
185
+ elide: Text.ElideMiddle
186
+ }
187
+
188
+ Rectangle {
189
+ anchors.bottom: parent.bottom
190
+ x: Theme.spacing.rowPaddingX
191
+ width: parent.width - 2 * Theme.spacing.rowPaddingX
192
+ height: Theme.spacing.hairline
193
+ color: Theme.color.mutedFrame
194
+ opacity: 0.4
195
+ }
196
+ }
197
+
198
+ buttons: Item {
199
+ width: parent.width
200
+ height: Math.max(cancelButton.implicitHeight, extractButton.implicitHeight)
201
+ Row {
202
+ anchors.bottom: parent.bottom
203
+ anchors.right: parent.right
204
+ anchors.rightMargin: Theme.spacing.rowPaddingX
205
+ spacing: Theme.spacing.gap
206
+
207
+ Flea.DialogButton {
208
+ id: cancelButton
209
+ label: "Cancel"
210
+ focused: root.focusPart === 1
211
+ available: true
212
+ enabled: true
213
+ onActivated: root.close()
214
+ onTabbed: function (from, back) { root.stepTab(back) }
215
+ }
216
+
217
+ Flea.DialogButton {
218
+ id: extractButton
219
+ label: "Extract"
220
+ primary: true
221
+ focused: root.focusPart === 2
222
+ onActivated: root.commit()
223
+ onTabbed: function (from, back) { root.stepTab(back) }
224
+ }
225
+ }
226
+ }
227
+
228
+ // The wrong-password specimen: the field in the error frame, then its bare sentence directly under it.
229
+ Column {
230
+ x: Theme.spacing.rowPaddingX
231
+ width: parent.width - 2 * Theme.spacing.rowPaddingX
232
+ spacing: Theme.spacing.gap
233
+
234
+ Flea.DialogField {
235
+ id: passwordField
236
+ width: parent.width
237
+ label: "Password"
238
+ secret: true
239
+ error: root.errorText.length > 0
240
+ onEdited: root.errorText = ""
241
+ onAccepted: root.commit()
242
+ onTabbed: function (from, back) { root.stepTab(back) }
243
+ }
244
+
245
+ Text {
246
+ width: parent.width
247
+ visible: root.errorText.length > 0
248
+ text: root.errorText
249
+ color: Theme.color.errorOnSurface
250
+ font { family: Theme.font.family; pixelSize: Theme.font.caption }
251
+ textFormat: Text.PlainText
252
+ wrapMode: Text.Wrap
253
+ }
254
+ }
255
+ }
256
+ }
257
+ }