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,658 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ import "." as Flea
4
+ import "js/Format.js" as Format
5
+ import "js/Permissions.js" as Permissions
6
+
7
+ // One item, one mode; the backend owns its reviewed descriptor for this dialog's lifetime.
8
+ FocusScope {
9
+ id: root
10
+ anchors.fill: parent
11
+ visible: opened || opacity > 0
12
+ // Closed means no input, even while the fade-out is still drawn.
13
+ enabled: opened
14
+ opacity: 0
15
+ Flea.Reveal { target: root; shown: root.opened; lift: card }
16
+ property bool opened: false
17
+ property int requestId: 0
18
+ // Inspect ids are request blocks of this width; a wider selection reserves whole blocks.
19
+ readonly property int inspectStride: 1000
20
+ property int multiBase: 0
21
+ property var facts: ({})
22
+ property string path: ""
23
+ property string modeText: ""
24
+ property string errorText: ""
25
+ property bool busy: false
26
+ property bool transportFailed: false
27
+ property Item focusHolder: null
28
+ // The selection's paths with modes in the same order, the first inspect refusal, and the explicit bits.
29
+ property var multiPaths: []
30
+ // The live inspect store noteMode owns; multiModes is its one last-reply snapshot.
31
+ property var multiStore: ({ modes: [], reasons: [], skipped: [] })
32
+ property var multiModes: []
33
+ property int multiPending: 0
34
+ property int explicitSet: 0
35
+ property int explicitClear: 0
36
+ property bool applyingMany: false
37
+ // A failed batch's own error until its re-read lands, so the held note may replace it when it names that refusal.
38
+ property string failedBatchError: ""
39
+ // The Apply skips, carried to the applyMany reply for the final message.
40
+ property var multiApplySkipped: []
41
+ readonly property bool isMulti: multiPaths.length > 1
42
+ readonly property var multiSummary: isMulti ? Permissions.summarize(multiModes, multiStore.reasons) : null
43
+ // Several items: a selection where no file can change shows the files' bits and is not editable, as one unchangeable file is.
44
+ readonly property bool editable: isMulti ? !busy && !transportFailed && !!multiSummary && multiSummary.changeable
45
+ : facts.ok === true && !facts.reason && !busy && !transportFailed
46
+ readonly property int modeValue: Permissions.parse(modeText)
47
+ readonly property bool applying: busy && facts.ok === true
48
+ // Permissions040: a multi Apply cannot be cancelled either.
49
+ readonly property bool applyLocked: applying || applyingMany
50
+ // One Apply condition for the button and the probe, which reads the button.
51
+ readonly property bool canApply: isMulti ? editable : editable && modeValue >= 0
52
+ readonly property real labelWidth: Math.round(96 * root.boardScale)
53
+ // ButtonSystem040 A: a text field is DialogField's height (ui/DialogField.qml's box), centred in its row.
54
+ readonly property int fieldHeight: Theme.rowHeight - Theme.spacing.rowPaddingY
55
+ readonly property int controlHeight: Math.max(Theme.rowHeight, Math.ceil(Theme.font.body * Theme.lineBoxRatio) + 2 * Theme.spacing.rowPaddingY)
56
+ // The board is drawn at base size 14, whose bodySmall is 13; every board pixel below scales from it and lands whole.
57
+ readonly property real boardScale: Theme.font.bodySmall / 13
58
+ readonly property int boardCardWidth: 480
59
+ readonly property int bodyInset: Math.round(16 * root.boardScale) + Theme.spacing.hairline
60
+ readonly property int headingHeight: Math.round(26 * root.boardScale)
61
+ // Permissions040, several items: the surface's one 8 px gap, and the 6 px its button row adds above itself.
62
+ readonly property int multiGap: Theme.spacing.rowPaddingY + Theme.spacing.hairline
63
+ // The board's three flex:1 columns are exact thirds of this span; whole-number arithmetic rounds each start half up, as the board's paint does.
64
+ readonly property real bitSpan: body.holderWidth - root.labelWidth
65
+ function bitStart(column) { return Math.floor((2 * column * root.bitSpan + 3) / 6) }
66
+ function bitWidth(column) { return root.bitStart(column + 1) - root.bitStart(column) }
67
+ // The box sits at the rounded exact centre of its third, so it lands on whole pixels where the board's does.
68
+ function boxLead(column, box) { return Math.floor((2 * column * root.bitSpan + root.bitSpan - 3 * box + 3) / 6) - root.bitStart(column) }
69
+ // lib.py note(): a caption's line box is 1.5 x its size and its glyphs sit centred in it.
70
+ readonly property int captionLead: Math.round((SettingsMetrics.hintLine - noteFont.height) / 2)
71
+ // Permissions040's strip draws the title and "esc" glyphs one row above where Qt's line box seats them (board rows 7-16 and 10-16, the build's 8-17 and 11-17 once centred above the rule).
72
+ readonly property int stripTextRise: Theme.spacing.hairline
73
+ readonly property int railHalf: Math.round(Theme.settings.railPaddingY / 2)
74
+ readonly property int buttonLead: root.railHalf + Theme.spacing.hairline
75
+ readonly property var cardItem: card
76
+ // The title strip's four marks, so a probe reads where each sits against the board's rows.
77
+ readonly property var stripItems: ({ lock: lockMark, title: title, esc: escHint, close: closeMark })
78
+ readonly property var noteItem: scopeLabel
79
+ readonly property var octalFrame: octalBox
80
+ readonly property var bodyItem: body
81
+ readonly property string displayedError: errorLabel.text
82
+ readonly property string displayedSummary: (isMulti ? "" : changeSummary.text + "\n") + scopeLabel.text
83
+ // One section rule for the four drawn below; each site sets its own place and visibility.
84
+ component Rule: Rectangle {
85
+ width: parent.width
86
+ height: Theme.spacing.hairline
87
+ color: Theme.color.mutedFrame
88
+ opacity: 0.4
89
+ }
90
+ // No enabled keys: each item.enabled already carries every mode's answer, a copy could disagree.
91
+ function controls() {
92
+ var result = [{name: "Close", item: closeMark}, {name: "Octal", item: octal},
93
+ {name: "Cancel", item: cancelButton}, {name: "Apply", item: applyButton}]
94
+ for (var row = 0; row < permissionRows.count; row++) {
95
+ var group = permissionRows.itemAt(row)
96
+ for (var column = 0; column < group.checks.count; column++) {
97
+ var checkbox = group.checks.itemAt(column)
98
+ result.push({name: checkbox.Accessible.name, item: checkbox, checked: checkbox.checked, bit: checkbox.bit})
99
+ }
100
+ }
101
+ return result
102
+ }
103
+ readonly property string scopeText: facts.directory
104
+ ? "Scope this folder only · enclosed items unchanged · ownership unchanged"
105
+ : "Scope this item only · ownership unchanged"
106
+ signal requested(var message)
107
+ signal changed(string note)
108
+ signal refreshNeeded()
109
+ signal closed()
110
+
111
+ // One reset for both openers, so a field added to one cannot be missed in the other.
112
+ function reset(itemPath, paths, holder) {
113
+ multiPaths = paths.slice()
114
+ multiStore = ({ modes: [], reasons: [], skipped: [], pending: paths.length })
115
+ multiModes = []
116
+ multiPending = paths.length
117
+ multiApplySkipped = []
118
+ failedBatchError = ""
119
+ explicitSet = 0
120
+ explicitClear = 0
121
+ applyingMany = false
122
+ path = itemPath
123
+ focusHolder = holder
124
+ facts = ({})
125
+ modeText = ""
126
+ errorText = ""
127
+ transportFailed = false
128
+ busy = true
129
+ opened = true
130
+ body.contentY = 0
131
+ cancelButton.forceActiveFocus()
132
+ }
133
+ // One inspect fan-out for openMany and the failed-batch re-read: the same requests.
134
+ function inspectAll() {
135
+ multiStore = ({ modes: [], reasons: [], skipped: [], pending: multiPaths.length })
136
+ multiModes = []
137
+ multiPending = multiPaths.length
138
+ for (var i = 0; i < multiPaths.length; i++)
139
+ requested({ c: "permissions", op: "inspect", id: multiBase * inspectStride + i, path: multiPaths[i] })
140
+ }
141
+
142
+ function open(itemPath, holder) {
143
+ requestId += 1
144
+ reset(itemPath, [], holder)
145
+ requested({ c: "permissions", op: "inspect", id: requestId, path: path })
146
+ }
147
+ // One Entry holds every path the Apply changed, so one undo restores them all.
148
+ function openMany(paths, holder) {
149
+ requestId += 1
150
+ multiBase = requestId
151
+ reset("", paths, holder)
152
+ inspectAll()
153
+ // A wider selection reserves whole strides so the next open's block cannot overlap this one.
154
+ requestId += Math.max(0, Math.floor((paths.length - 1) / inspectStride))
155
+ }
156
+ function receive(message) {
157
+ if (isMulti) { receiveMany(message); return }
158
+ if (!opened || transportFailed || message.id !== requestId || message.op === "close") return
159
+ busy = false
160
+ cancelButton.forceActiveFocus()
161
+ if (!message.ok) { errorText = message.error || "Could not change permissions."; return }
162
+ if (message.op === "apply") { changed(""); close(); return }
163
+ facts = message
164
+ modeText = message.mode
165
+ }
166
+ // The grid over several files; an untouched box takes the explicit opposite of what it shows.
167
+ function multiBit(bit) {
168
+ if (!multiSummary) return { on: false, mixed: false }
169
+ for (var i = 0; i < multiSummary.bits.length; i++) {
170
+ if (multiSummary.bits[i].mask === bit)
171
+ return multiSummary.bits[i]
172
+ }
173
+ return { on: false, mixed: false }
174
+ }
175
+ function multiChecked(bit) {
176
+ if ((explicitSet & bit) !== 0) return true
177
+ if ((explicitClear & bit) !== 0) return false
178
+ return multiBit(bit).on
179
+ }
180
+ function multiValue(bit) {
181
+ if ((explicitSet & bit) !== 0) return "on"
182
+ if ((explicitClear & bit) !== 0) return "off"
183
+ var found = multiBit(bit)
184
+ if (found.mixed) return "some"
185
+ return found.on ? "on" : "off"
186
+ }
187
+ // A set box lets go unless mixed, so a uniform-off box reads off, on, off, on.
188
+ function multiToggle(bit) {
189
+ if ((explicitSet & bit) !== 0) {
190
+ explicitSet &= ~bit
191
+ if (multiBit(bit).mixed) explicitClear |= bit
192
+ } else if ((explicitClear & bit) !== 0) {
193
+ explicitClear &= ~bit
194
+ } else if (multiBit(bit).mixed || !multiBit(bit).on) {
195
+ explicitSet |= bit
196
+ explicitClear &= ~bit
197
+ } else {
198
+ explicitClear |= bit
199
+ explicitSet &= ~bit
200
+ }
201
+ }
202
+ function receiveMany(message) {
203
+ if (!opened || transportFailed || (message.op === "applyMany" && (!applyingMany || message.id !== requestId))) return
204
+ if (message.op === "applyMany") {
205
+ applyingMany = false
206
+ if (message.ok === true) {
207
+ changed(Permissions.multiResult(multiApplySkipped))
208
+ close()
209
+ return
210
+ }
211
+ errorText = message.error || "Could not change permissions."
212
+ if (multiApplySkipped.length > 0)
213
+ errorText += "\n" + Permissions.skipNote(multiApplySkipped)
214
+ // A failed batch re-reads every mode so the grid and a retry start from disk.
215
+ failedBatchError = message.error || ""
216
+ inspectAll()
217
+ busy = true
218
+ cancelButton.forceActiveFocus()
219
+ refreshNeeded()
220
+ return
221
+ }
222
+ if (message.op !== "inspect") return
223
+ var at = (message.id || 0) - multiBase * inspectStride
224
+ if (at < 0 || at >= multiPaths.length) return
225
+ // Accumulated in place through noteMode, which answers true once per selection.
226
+ if (Permissions.noteMode(multiStore, at, multiPaths[at], message)) {
227
+ multiPending = 0
228
+ // The single assignment lands with the last reply, before busy clears, so editable never reads the previous selection's summary.
229
+ multiModes = multiStore.modes.slice()
230
+ busy = false
231
+ var note = Permissions.inspectNote(multiStore, multiPaths)
232
+ // A batch refused for a file the re-read cannot inspect is named by the held note, which quotes that refusal; any other batch error stays.
233
+ var refused = failedBatchError.length > 0 && multiStore.skipped.some(function (skip) { return skip.why === failedBatchError })
234
+ if (note.length > 0 && (errorText.length === 0 || refused)) errorText = note
235
+ failedBatchError = ""
236
+ cancelButton.forceActiveFocus()
237
+ } else {
238
+ multiPending = multiStore.pending
239
+ }
240
+ }
241
+ function backendFailed() {
242
+ if (!opened || transportFailed) return
243
+ // A multi Apply counts as applying with an unknown outcome, and ends so Cancel and close work.
244
+ var applying = applyLocked
245
+ applyingMany = false
246
+ transportFailed = true
247
+ busy = false
248
+ cancelButton.forceActiveFocus()
249
+ errorText = applying
250
+ ? "Flea stopped responding, so the permission change may not have finished. Restart Flea and reopen Permissions to check."
251
+ : "Permissions is unavailable because Flea stopped responding. Restart Flea and reopen Permissions."
252
+ }
253
+ function close() {
254
+ // An issued fchmod cannot be cancelled; retain its result before allowing dismissal.
255
+ if (!opened || applyLocked) return
256
+ if (isMulti) {
257
+ for (var i = 0; i < multiPaths.length; i++)
258
+ requested({ c: "permissions", op: "close", id: multiBase * inspectStride + i })
259
+ } else {
260
+ requested({ c: "permissions", op: "close", id: requestId })
261
+ }
262
+ opened = false
263
+ closed()
264
+ if (focusHolder) focusHolder.forceActiveFocus()
265
+ }
266
+ function apply() {
267
+ if (isMulti) { applyMany(); return }
268
+ if (!editable || modeValue < 0) return
269
+ root.forceActiveFocus()
270
+ busy = true
271
+ errorText = ""
272
+ requested({ c: "permissions", op: "apply", id: requestId, mode: modeText })
273
+ }
274
+ // Apply is one undo step; unchangeable items are counted and an empty batch answers at once.
275
+ function applyMany() {
276
+ if (!editable) return
277
+ root.forceActiveFocus()
278
+ busy = true
279
+ applyingMany = true
280
+ errorText = ""
281
+ // Built fresh from the per-row reasons, so every skip is counted exactly once.
282
+ var skipped = []
283
+ var paths = []
284
+ var modes = []
285
+ for (var i = 0; i < multiPaths.length; i++) {
286
+ var why = multiStore.reasons[i] || ""
287
+ var base = Permissions.parse(multiStore.modes[i])
288
+ if (why.length === 0 && base < 0)
289
+ why = Permissions.specialReason(multiStore.modes[i])
290
+ if (why.length > 0 || base < 0) {
291
+ skipped.push({ path: multiPaths[i],
292
+ why: why.length > 0 ? why : "Could not change permissions." })
293
+ continue
294
+ }
295
+ var target = (base & ~explicitClear) | explicitSet
296
+ paths.push(multiPaths[i])
297
+ modes.push(Permissions.octal(target))
298
+ }
299
+ if (paths.length === 0) {
300
+ applyingMany = false
301
+ busy = false
302
+ errorText = Permissions.multiResult(skipped)
303
+ cancelButton.forceActiveFocus()
304
+ return
305
+ }
306
+ multiApplySkipped = skipped
307
+ requested({ c: "permissionsBatch", paths: paths, modes: modes, id: requestId })
308
+ }
309
+ function focusItems(item, result) {
310
+ if (!item.visible || !item.enabled) return
311
+ if (item.activeFocusOnTab) result.push(item)
312
+ for (var i = 0; i < item.children.length; i++) focusItems(item.children[i], result)
313
+ }
314
+ function stepFocus(back) {
315
+ var items = []
316
+ focusItems(card, items)
317
+ if (!items.length) return
318
+ var current = -1
319
+ for (var i = 0; i < items.length; i++) if (items[i].activeFocus) current = i
320
+ var next = current < 0 ? (back ? items.length - 1 : 0)
321
+ : (current + (back ? -1 : 1) + items.length) % items.length
322
+ items[next].forceActiveFocus()
323
+ body.reveal(items[next])
324
+ }
325
+ FontMetrics { id: noteFont; font { family: Theme.font.family; pixelSize: Theme.font.caption } }
326
+ Keys.onTabPressed: function(event) { root.stepFocus((event.modifiers & Qt.ShiftModifier) !== 0); event.accepted = true }
327
+ Keys.onBacktabPressed: function(event) { root.stepFocus(true); event.accepted = true }
328
+ Keys.onPressed: function(event) { event.accepted = true }
329
+ Keys.onEscapePressed: function(event) { root.close(); event.accepted = true }
330
+ Rectangle {
331
+ anchors.fill: parent
332
+ color: Theme.color.background
333
+ opacity: 0.5
334
+ MouseArea {
335
+ anchors.fill: parent
336
+ hoverEnabled: true
337
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
338
+ onClicked: root.close()
339
+ onWheel: function(wheel) { wheel.accepted = true }
340
+ }
341
+ }
342
+ Rectangle {
343
+ id: card
344
+ x: Theme.cardOrigin(root.width, width)
345
+ y: Theme.cardOrigin(root.height, height)
346
+ width: Theme.cardSpan(Math.round(root.boardCardWidth * root.boardScale), root.width - 2 * Theme.spacing.gap)
347
+ height: Theme.cardSpan(Theme.spacing.hairline + chrome.height + body.wanted + Theme.spacing.rowPaddingX + root.bodyInset, root.height - 2 * Theme.spacing.gap)
348
+ color: Theme.color.surface
349
+ border.color: Theme.color.mutedFrame
350
+ border.width: Theme.spacing.hairline
351
+ radius: Style.cornerRadius
352
+ clip: true
353
+ MouseArea {
354
+ anchors.fill: parent
355
+ hoverEnabled: true
356
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
357
+ onWheel: function(wheel) { wheel.accepted = true }
358
+ }
359
+ Item {
360
+ id: chrome
361
+ // The strip sits inside the card's border, as the board's title does, so its rule and marks start a hairline in.
362
+ anchors.left: parent.left
363
+ anchors.right: parent.right
364
+ anchors.top: parent.top
365
+ anchors.margins: Theme.spacing.hairline
366
+ height: Theme.chromeHeight
367
+ // The board centres the marks and title in the strip above its rule, so the rule's row is never part of the centring band.
368
+ Item { id: titleBand; anchors.left: parent.left; anchors.right: parent.right; anchors.top: parent.top; height: parent.height - Theme.spacing.hairline }
369
+ Flea.Glyph {
370
+ id: lockMark
371
+ x: Theme.spacing.rowPaddingX
372
+ y: Math.floor((titleBand.height - height) / 2)
373
+ width: Theme.chromeMarkSize; height: title.height; name: "lock"; color: Theme.color.accent
374
+ }
375
+ Text {
376
+ id: title
377
+ x: lockMark.x + lockMark.width + Theme.spacing.gap
378
+ y: Math.floor((titleBand.height - height) / 2) - root.stripTextRise
379
+ text: root.isMulti ? Format.fill(Permissions.WORDS.titleMany, root.multiPaths.length) : "Permissions"
380
+ color: Theme.color.foreground; textFormat: Text.PlainText; font { family: Theme.font.family; pixelSize: Theme.font.caption; bold: true }
381
+ }
382
+ // Dialogs rule 7: the way out is named beside the mark that performs it, the settings panel's own corner.
383
+ Flea.EscapeHint {
384
+ id: escHint
385
+ anchors.right: closeMark.left
386
+ anchors.rightMargin: Theme.spacing.gap
387
+ y: Math.floor((titleBand.height - height) / 2) - root.stripTextRise
388
+ }
389
+
390
+ Flea.ChromeButton {
391
+ id: closeMark
392
+ ruleRows: Theme.spacing.hairline
393
+ anchors.right: parent.right
394
+ anchors.rightMargin: Theme.spacing.rowPaddingX
395
+ anchors.verticalCenter: parent.verticalCenter
396
+ glyph: "x"; gesturePolicy: TapHandler.ReleaseWithinBounds
397
+ // Muted at rest as the board draws the strip; the keyboard adds ChromeButton's own frame.
398
+ restingColor: Theme.color.mutedFrame
399
+ enabled: !root.applyLocked
400
+ accessName: "Close permissions"
401
+ activeFocusOnTab: true
402
+ keyboardFocused: activeFocus
403
+ Keys.onTabPressed: function(event) { root.stepFocus((event.modifiers & Qt.ShiftModifier) !== 0) }
404
+ Keys.onBacktabPressed: root.stepFocus(true)
405
+ onActivated: root.close()
406
+ }
407
+ Rule { anchors.bottom: parent.bottom }
408
+ }
409
+ Flea.CardFrame {
410
+ id: body
411
+ anchors.left: parent.left
412
+ anchors.right: parent.right
413
+ anchors.top: chrome.bottom
414
+ anchors.bottom: parent.bottom
415
+ bleed: Theme.focusClearance
416
+ anchors.leftMargin: root.bodyInset - Theme.focusClearance
417
+ anchors.rightMargin: root.bodyInset - Theme.focusClearance
418
+ anchors.topMargin: Theme.spacing.rowPaddingX - Theme.focusClearance
419
+ anchors.bottomMargin: root.bodyInset - Theme.focusClearance
420
+ spacing: 0
421
+ Column {
422
+ width: body.holderWidth
423
+ spacing: 0
424
+ Row {
425
+ width: parent.width
426
+ height: root.controlHeight
427
+ spacing: Theme.spacing.gap
428
+ // For several items the grid and Apply are the whole card, so the name row drops out.
429
+ visible: !root.isMulti
430
+ Flea.Glyph { width: Theme.markSize; height: nameLabel.height; anchors.verticalCenter: parent.verticalCenter; name: root.facts.directory ? "folder" : "file"; color: Theme.color.mutedFrame }
431
+ Text { id: nameLabel; width: parent.width - Theme.markSize - kindLabel.width - 2 * parent.spacing; anchors.verticalCenter: parent.verticalCenter; text: root.path.split("/").pop(); elide: Text.ElideMiddle; textFormat: Text.PlainText; color: Theme.color.foreground; font { family: Theme.font.family; pixelSize: Theme.font.body } }
432
+ Text { id: kindLabel; anchors.verticalCenter: parent.verticalCenter; text: root.facts.ok ? (root.facts.directory ? "directory" : "file") : ""; textFormat: Text.PlainText; color: Theme.color.foreground; font { family: Theme.font.family; pixelSize: Theme.font.caption } }
433
+ }
434
+ Rule { visible: !root.isMulti }
435
+ Flea.PermissionsHeadings { width: parent.width; card: root; enter: !root.isMulti && root.facts.directory === true }
436
+ Repeater {
437
+ id: permissionRows
438
+ model: ["Owner", "Group", "Everyone"]
439
+ Row {
440
+ id: permissionRow
441
+ required property string modelData
442
+ required property int index
443
+ readonly property alias checks: checks
444
+ width: body.holderWidth
445
+ height: root.controlHeight
446
+ Text { width: root.labelWidth; anchors.verticalCenter: parent.verticalCenter; text: permissionRow.modelData; textFormat: Text.PlainText; color: Theme.color.foreground; font { family: Theme.font.family; pixelSize: Theme.font.body } }
447
+ Repeater {
448
+ id: checks
449
+ model: 3
450
+ FocusScope {
451
+ id: checkbox
452
+ required property int index
453
+ readonly property int bit: 1 << (8 - permissionRow.index * 3 - index)
454
+ readonly property bool checked: root.isMulti ? root.multiChecked(bit)
455
+ : (root.modeValue >= 0 ? root.modeValue : parseInt(root.facts.mode || "0", 8)) & bit
456
+ width: root.bitWidth(index)
457
+ height: permissionRow.height
458
+ activeFocusOnTab: true
459
+ enabled: root.editable
460
+ // The pointer's own state, read by the native capture harness before it shoots a hover or a press.
461
+ readonly property bool hovered: boxHover.hovered
462
+ readonly property bool pressed: boxTap.pressed
463
+ Accessible.role: Accessible.CheckBox
464
+ Accessible.name: permissionRow.modelData + " " + ["read", "write", root.facts.directory ? "enter" : "execute"][index]
465
+ Accessible.checked: checked
466
+ Accessible.onPressAction: toggle()
467
+ Accessible.onToggleAction: toggle()
468
+ function toggle() {
469
+ if (!root.editable) return
470
+ if (root.isMulti) root.multiToggle(bit)
471
+ else root.modeText = Permissions.toggle(root.modeText, bit)
472
+ forceActiveFocus()
473
+ }
474
+ Keys.onSpacePressed: toggle()
475
+ Keys.onTabPressed: function(event) { root.stepFocus((event.modifiers & Qt.ShiftModifier) !== 0) }
476
+ Keys.onBacktabPressed: root.stepFocus(true)
477
+ Flea.CheckBox {
478
+ x: root.boxLead(checkbox.index, width)
479
+ y: Math.round((parent.height - height) / 2)
480
+ // A bit differing across the files shows a bar until it is clicked.
481
+ value: root.isMulti ? root.multiValue(bit) : checkbox.checked ? "on" : "off"
482
+ focused: checkbox.activeFocus
483
+ // A disabled row stays checked, so the box dims and keeps its value.
484
+ available: root.editable
485
+ }
486
+ HoverHandler { id: boxHover }
487
+ TapHandler { id: boxTap; gesturePolicy: TapHandler.ReleaseWithinBounds; onTapped: checkbox.toggle() }
488
+ }
489
+ }
490
+ }
491
+ }
492
+ Item {
493
+ width: parent.width
494
+ height: Theme.spacing.rowPaddingY + root.railHalf + Theme.spacing.hairline
495
+ visible: !root.isMulti
496
+ Rule { y: root.railHalf }
497
+ }
498
+ Row {
499
+ width: parent.width
500
+ height: root.controlHeight
501
+ spacing: Theme.spacing.gap
502
+ // Permissions040: for several items Octal drops out.
503
+ visible: !root.isMulti
504
+ Text { width: root.labelWidth; anchors.verticalCenter: parent.verticalCenter; text: "Octal"; textFormat: Text.PlainText; color: Theme.color.foreground; font { family: Theme.font.family; pixelSize: Theme.font.body } }
505
+ Rectangle {
506
+ id: octalBox
507
+ width: body.holderWidth - root.labelWidth - parent.spacing
508
+ height: root.fieldHeight
509
+ y: Math.round((parent.height - height) / 2)
510
+ color: Theme.color.background
511
+ // A focused field is its own frame in the accent, and the error role where its line reports an error, as RenameField draws it.
512
+ border.color: octal.activeFocus ? (errorLabel.visible && !root.busy ? Theme.color.error : Theme.color.accent) : Theme.color.mutedFrame
513
+ TextInput {
514
+ id: octal
515
+ anchors.fill: parent
516
+ anchors.leftMargin: Theme.spacing.gap
517
+ anchors.rightMargin: Theme.spacing.gap
518
+ verticalAlignment: TextInput.AlignVCenter
519
+ text: root.modeText
520
+ readOnly: !root.editable
521
+ activeFocusOnTab: !root.isMulti
522
+ enabled: root.editable && !root.isMulti
523
+ Accessible.name: "Octal mode"
524
+ color: root.editable ? Theme.color.foreground : Theme.color.muted
525
+ font { family: Theme.font.family; pixelSize: Theme.font.body }
526
+ clip: true
527
+ selectByMouse: true
528
+ Keys.onTabPressed: function(event) { root.stepFocus((event.modifiers & Qt.ShiftModifier) !== 0) }
529
+ Keys.onBacktabPressed: root.stepFocus(true)
530
+ onTextEdited: root.modeText = text
531
+ onAccepted: if (root.editable && root.modeValue >= 0) applyButton.forceActiveFocus()
532
+ }
533
+ }
534
+ }
535
+ Repeater {
536
+ model: ["Owner", "Group"]
537
+ Row {
538
+ required property string modelData
539
+ required property int index
540
+ // Permissions040: for several items Owner and Group drop out.
541
+ visible: !root.isMulti
542
+ width: body.holderWidth
543
+ height: root.controlHeight
544
+ spacing: Theme.spacing.gap
545
+ Text { width: root.labelWidth; anchors.verticalCenter: parent.verticalCenter; text: parent.modelData; textFormat: Text.PlainText; color: Theme.color.foreground; font { family: Theme.font.family; pixelSize: Theme.font.body } }
546
+ Text { width: body.holderWidth - root.labelWidth - identity.width - 2 * parent.spacing; anchors.verticalCenter: parent.verticalCenter; text: root.facts.ok ? ((parent.index === 0 ? root.facts.owner : root.facts.group) || "Unknown") + " · read-only" : ""; textFormat: Text.PlainText; elide: Text.ElideRight; color: Theme.color.foreground; font { family: Theme.font.family; pixelSize: Theme.font.body } }
547
+ Text { id: identity; anchors.verticalCenter: parent.verticalCenter; text: root.facts.ok ? (parent.index === 0 ? "uid " + root.facts.uid : "gid " + root.facts.gid) : ""; textFormat: Text.PlainText; color: Theme.color.foreground; font { family: Theme.font.family; pixelSize: Theme.font.caption } }
548
+ }
549
+ }
550
+ Rectangle {
551
+ width: parent.width
552
+ height: directoryScope.implicitHeight + 2 * Theme.spacing.gap
553
+ // Permissions040: for several items the scope box drops out.
554
+ visible: root.facts.directory === true && !root.isMulti
555
+ color: Theme.color.background
556
+ border.width: Theme.spacing.hairline
557
+ border.color: Theme.color.mutedFrame
558
+ Flea.Glyph { id: scopeMark; x: Theme.spacing.gap; y: Theme.spacing.gap; width: Theme.font.bodySmall; height: width; name: "check"; color: Theme.color.accent }
559
+ Text {
560
+ id: directoryScope
561
+ x: scopeMark.x + scopeMark.width + Theme.spacing.gap
562
+ y: Theme.spacing.gap
563
+ width: parent.width - x - Theme.spacing.gap
564
+ text: "Scope: this folder only. Items inside it keep their permissions."
565
+ textFormat: Text.PlainText
566
+ wrapMode: Text.Wrap
567
+ color: Theme.color.foreground
568
+ font { family: Theme.font.family; pixelSize: Theme.font.caption }
569
+ }
570
+ }
571
+ Text {
572
+ id: errorLabel
573
+ width: parent.width
574
+ visible: text.length > 0
575
+ topPadding: Theme.spacing.gap
576
+ text: root.errorText || root.facts.reason || (root.busy
577
+ ? (root.isMulti ? (root.multiPending > 0 ? "Reading permissions…" : "Applying permissions…")
578
+ : (root.facts.ok ? "Applying permissions…" : "Reading permissions…"))
579
+ : root.facts.ok && root.modeValue < 0 ? "Enter three octal digits or a leading-zero four-digit mode." : "")
580
+ textFormat: Text.PlainText
581
+ wrapMode: Text.Wrap
582
+ color: root.busy ? Theme.color.muted : Theme.color.errorOnSurface
583
+ font { family: Theme.font.family; pixelSize: Theme.font.caption }
584
+ }
585
+ Item {
586
+ width: parent.width
587
+ height: root.isMulti ? root.multiGap : Theme.settings.railPaddingY + Theme.spacing.gap + Theme.spacing.hairline
588
+ Rule { y: Theme.settings.railPaddingY; visible: !root.isMulti }
589
+ }
590
+ Text {
591
+ text: "WILL CHANGE"
592
+ visible: !root.isMulti
593
+ bottomPadding: Math.round(Theme.spacing.rowPaddingY / 2)
594
+ textFormat: Text.PlainText
595
+ color: Theme.color.foreground
596
+ font { family: Theme.font.family; pixelSize: Theme.font.caption; letterSpacing: Theme.font.caption / 10 }
597
+ }
598
+ Text {
599
+ id: changeSummary
600
+ width: parent.width
601
+ // For several items the grid and Apply are the whole card, so the preview drops out.
602
+ visible: !root.isMulti
603
+ text: (root.facts.ok && !root.facts.reason && root.modeValue >= 0
604
+ ? Format.fill(Permissions.WORDS.requestedMode, Permissions.octal(root.modeValue)) : "No changes to apply")
605
+ + "\nPath " + root.path.replace(/\//g, "/\u200b")
606
+ + (root.facts.reason ? "\n" + Format.fill(Permissions.WORDS.currentMode, root.facts.mode) : "")
607
+ textFormat: Text.PlainText
608
+ wrapMode: Text.WordWrap
609
+ color: Theme.color.accent
610
+ font { family: Theme.font.family; pixelSize: Theme.font.caption }
611
+ }
612
+ Text {
613
+ id: scopeLabel
614
+ width: parent.width
615
+ // Fixed line boxes put the spare height under the glyphs, so the top padding centres them as CSS line-height does.
616
+ height: lineCount * SettingsMetrics.hintLine
617
+ lineHeight: SettingsMetrics.hintLine
618
+ lineHeightMode: Text.FixedHeight
619
+ topPadding: root.captionLead
620
+ // A box whose bit differs across the files shows a bar until it is clicked.
621
+ text: root.isMulti ? Permissions.mixedNote() : root.scopeText
622
+ textFormat: Text.PlainText
623
+ wrapMode: Text.Wrap
624
+ color: Theme.color.foreground
625
+ font { family: Theme.font.family; pixelSize: Theme.font.caption }
626
+ }
627
+ }
628
+ buttons: Column {
629
+ width: parent.width
630
+ Item { width: parent.width; height: root.isMulti ? root.multiGap + root.buttonLead : Theme.settings.railPaddingY + Theme.spacing.hairline }
631
+ Row {
632
+ anchors.right: parent.right
633
+ spacing: Theme.spacing.gap
634
+ Flea.DialogButton {
635
+ id: cancelButton
636
+ label: "Cancel"
637
+ available: !root.applyLocked
638
+ tabHandle: true
639
+ tabStop: true
640
+ onTabbed: function(from, back) { root.stepFocus(back) }
641
+ onActivated: root.close()
642
+ }
643
+ Flea.DialogButton {
644
+ id: applyButton
645
+ label: "Apply"
646
+ primary: true
647
+ // The multi card has no octal to validate, so Apply enables on the grid alone.
648
+ available: root.canApply
649
+ tabHandle: true
650
+ tabStop: true
651
+ onTabbed: function(from, back) { root.stepFocus(back) }
652
+ onActivated: root.apply()
653
+ }
654
+ }
655
+ }
656
+ }
657
+ }
658
+ }