flea-qml 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (637) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +511 -2
  3. package/THIRD-PARTY-NOTICES.md +29 -0
  4. package/demos/App.java +5 -0
  5. package/demos/Dockerfile +4 -0
  6. package/demos/Main.kt +3 -0
  7. package/demos/Program.cs +1 -0
  8. package/demos/README.md +12 -0
  9. package/demos/app.dart +3 -0
  10. package/demos/app.deb +0 -0
  11. package/demos/app.js +1 -0
  12. package/demos/backup.tar +0 -0
  13. package/demos/budget.xlsx +0 -0
  14. package/demos/build.sh +2 -0
  15. package/demos/bundle.zip +0 -0
  16. package/demos/clip.mp4 +0 -0
  17. package/demos/data.sqlite +0 -0
  18. package/demos/disk.iso +0 -0
  19. package/demos/engine.cpp +5 -0
  20. package/demos/firmware.bin +0 -0
  21. package/demos/flake.nix +4 -0
  22. package/demos/folder/inside.txt +1 -0
  23. package/demos/font.ttf +0 -0
  24. package/demos/index.html +5 -0
  25. package/demos/index.php +2 -0
  26. package/demos/init.lua +1 -0
  27. package/demos/letter.docx +0 -0
  28. package/demos/lib.rs +3 -0
  29. package/demos/main.c +6 -0
  30. package/demos/main.h +3 -0
  31. package/demos/math-and-diagrams.md +200 -0
  32. package/demos/notes.tar.gz +0 -0
  33. package/demos/notes.txt +3 -0
  34. package/demos/photo.png +0 -0
  35. package/demos/program +2 -0
  36. package/demos/report.pdf +32 -0
  37. package/demos/script.py +1 -0
  38. package/demos/server.go +7 -0
  39. package/demos/settings.json +4 -0
  40. package/demos/slides.pptx +0 -0
  41. package/demos/song.mp3 +0 -0
  42. package/demos/style.css +4 -0
  43. package/demos/tool.rb +1 -0
  44. package/demos/types.ts +2 -0
  45. package/docs/pele.md +23 -0
  46. package/docs/protocol.md +3420 -0
  47. package/docs/temml.md +63 -0
  48. package/index.js +107 -0
  49. package/package.json +34 -4
  50. package/src/archive.js +407 -0
  51. package/src/archiveops.js +728 -0
  52. package/src/backend.js +369 -0
  53. package/src/bulkrename.js +95 -0
  54. package/src/clip-host.js +76 -0
  55. package/src/clip.js +462 -0
  56. package/src/code.js +168 -0
  57. package/src/compare.js +74 -0
  58. package/src/create.js +128 -0
  59. package/src/dirsize.js +147 -0
  60. package/src/figure-svg.js +530 -0
  61. package/src/figure-thread.js +23 -0
  62. package/src/figure.js +132 -0
  63. package/src/find.js +55 -0
  64. package/src/flea.js +39 -0
  65. package/src/fsinfo.js +39 -0
  66. package/src/highlight.js +141 -0
  67. package/src/html.js +52 -0
  68. package/src/kinds.js +89 -0
  69. package/src/listing.js +130 -0
  70. package/src/markdown-html.js +317 -0
  71. package/src/mathml.js +186 -0
  72. package/src/menu.js +180 -0
  73. package/src/meta.js +135 -0
  74. package/src/office.js +810 -0
  75. package/src/order.js +100 -0
  76. package/src/peek.js +49 -0
  77. package/src/pele-wrapper.js +695 -0
  78. package/src/permissions.js +180 -0
  79. package/src/places.js +313 -0
  80. package/src/preview-language.js +10 -0
  81. package/src/terminal-server.js +95 -0
  82. package/src/terminal-shell.js +26 -0
  83. package/src/transfer.js +669 -0
  84. package/src/trash-record.js +41 -0
  85. package/src/trash.js +210 -0
  86. package/src/trashbrowse.js +327 -0
  87. package/src/uistate.js +393 -0
  88. package/src/uistore.js +168 -0
  89. package/src/vendor/LICENSES/Apache-2.0.txt +202 -0
  90. package/src/vendor/LICENSES/MIT-katex-2015.txt +27 -0
  91. package/src/vendor/LICENSES/MIT-katex.txt +21 -0
  92. package/src/vendor/LICENSES/MIT-mermaid.txt +21 -0
  93. package/src/vendor/LICENSES/MIT-pele.txt +40 -0
  94. package/src/vendor/LICENSES/MIT-perseus.txt +18 -0
  95. package/src/vendor/LICENSES/MIT-temml.txt +21 -0
  96. package/src/vendor/pele.js +145 -0
  97. package/src/vendor/temml.mjs +14708 -0
  98. package/src/watch.js +98 -0
  99. package/src/wayland/client.js +109 -0
  100. package/src/wayland/protocol.js +55 -0
  101. package/src/wayland/protocols/ext-data-control-v1.xml +276 -0
  102. package/src/wayland/protocols/wayland.xml +3248 -0
  103. package/src/wayland/protocols/wlr-data-control-unstable-v1.xml +278 -0
  104. package/src/wayland/resource-io.js +6 -0
  105. package/src/wayland/socket-discovery.js +26 -0
  106. package/src/wayland/transport-posix.js +120 -0
  107. package/src/wayland/transport-win32.js +11 -0
  108. package/src/wayland/transport.js +3 -0
  109. package/src/wayland/win32-resources.js +80 -0
  110. package/src/wayland/win32-wire.js +62 -0
  111. package/test/archive.test.js +80 -0
  112. package/test/bench-markdown-scroll.mjs +190 -0
  113. package/test/clip-host.test.js +113 -0
  114. package/test/code-preview.test.js +35 -0
  115. package/test/compare-ui-paths.test.js +24 -0
  116. package/test/compare.test.js +54 -0
  117. package/test/delete-trash.test.js +67 -0
  118. package/test/dirsize.test.js +138 -0
  119. package/test/file-ops.test.js +307 -0
  120. package/test/file-url.test.js +24 -0
  121. package/test/find-contents.test.js +161 -0
  122. package/test/find-input.test.js +66 -0
  123. package/test/fixtures/pele-parity.json +1006 -0
  124. package/test/highlight-launcher.test.js +35 -0
  125. package/test/html-preview.test.js +22 -0
  126. package/test/input.test.js +15 -0
  127. package/test/keymap.test.js +26 -0
  128. package/test/language-icons.test.js +36 -0
  129. package/test/launcher.test.js +51 -0
  130. package/test/manual-archive-here-ui.mjs +47 -0
  131. package/test/manual-code-scroll.mjs +28 -0
  132. package/test/manual-column-scroll.mjs +24 -0
  133. package/test/manual-compare-ui.mjs +20 -0
  134. package/test/manual-find-status.mjs +37 -0
  135. package/test/manual-folder-focus.mjs +63 -0
  136. package/test/manual-html-security.mjs +23 -0
  137. package/test/manual-html-ui.mjs +30 -0
  138. package/test/manual-syntax-preview.mjs +26 -0
  139. package/test/manual-terminal-lifecycle.mjs +42 -0
  140. package/test/manual-terminal-navigation.mjs +48 -0
  141. package/test/manual-terminal-ui.mjs +47 -0
  142. package/test/manual-trash-ui.mjs +86 -0
  143. package/test/markdown-diagrams.test.js +93 -0
  144. package/test/markdown-html.test.js +105 -0
  145. package/test/mathml.test.js +92 -0
  146. package/test/menu-transfer.test.js +57 -0
  147. package/test/office-preview.test.js +154 -0
  148. package/test/pele-parity.test.js +28 -0
  149. package/test/places.test.js +134 -0
  150. package/test/preview-keys.test.js +60 -0
  151. package/test/preview-language.test.js +22 -0
  152. package/test/status-filename.test.js +20 -0
  153. package/test/transfer-replace-merge.test.js +187 -0
  154. package/test/trash-address.test.js +23 -0
  155. package/test/trash-view.test.js +37 -0
  156. package/test/trashbrowse.test.js +135 -0
  157. package/ui/AboutFacts.qml +78 -0
  158. package/ui/ArchiveCards.qml +95 -0
  159. package/ui/ArchiveCompressDialog.qml +378 -0
  160. package/ui/ArchiveHereConfirm.qml +127 -0
  161. package/ui/ArchivePasswordDialog.qml +257 -0
  162. package/ui/Backend.qml +775 -0
  163. package/ui/BulkOldNames.qml +47 -0
  164. package/ui/BulkRenameDialog.qml +380 -0
  165. package/ui/CardFrame.qml +67 -0
  166. package/ui/CardScroll.qml +79 -0
  167. package/ui/CheckBox.qml +73 -0
  168. package/ui/ChromeBar.qml +360 -0
  169. package/ui/ChromeButton.qml +131 -0
  170. package/ui/ChromeModes.qml +43 -0
  171. package/ui/CodeLines.qml +256 -0
  172. package/ui/CodeRefusal.qml +52 -0
  173. package/ui/CollideConfirm.qml +147 -0
  174. package/ui/CollideHost.qml +167 -0
  175. package/ui/ColumnPane.qml +525 -0
  176. package/ui/ColumnRow.qml +380 -0
  177. package/ui/ColumnsArea.qml +594 -0
  178. package/ui/ColumnsLanding.qml +147 -0
  179. package/ui/CompareView.qml +321 -0
  180. package/ui/ContextMenu.qml +743 -0
  181. package/ui/ConvertDialog.qml +456 -0
  182. package/ui/CountedWorker.qml +7 -0
  183. package/ui/Crumb.qml +64 -0
  184. package/ui/CrumbDrop.qml +25 -0
  185. package/ui/CsvTable.qml +394 -0
  186. package/ui/CursorMark.qml +41 -0
  187. package/ui/DefaultClaim.qml +130 -0
  188. package/ui/DeviceLock.qml +47 -0
  189. package/ui/DeviceMounts.qml +540 -0
  190. package/ui/DialogButton.qml +150 -0
  191. package/ui/DialogField.qml +147 -0
  192. package/ui/DialogHeader.qml +31 -0
  193. package/ui/DialogScrim.qml +20 -0
  194. package/ui/DialogTitle.qml +37 -0
  195. package/ui/Divider.qml +9 -0
  196. package/ui/DropInto.qml +126 -0
  197. package/ui/DropTarget.qml +35 -0
  198. package/ui/DropboxMark.qml +60 -0
  199. package/ui/EmptyState.qml +150 -0
  200. package/ui/EscapeHint.qml +11 -0
  201. package/ui/FactsTable.qml +54 -0
  202. package/ui/FastScrollHandler.qml +476 -0
  203. package/ui/FatNamesConfirm.qml +157 -0
  204. package/ui/Favourites.qml +143 -0
  205. package/ui/FigureService.qml +120 -0
  206. package/ui/FileDrag.qml +267 -0
  207. package/ui/FilterStrip.qml +133 -0
  208. package/ui/FitMetrics.qml +22 -0
  209. package/ui/FlashDriveRows.qml +137 -0
  210. package/ui/FlashHost.qml +72 -0
  211. package/ui/FlashImageRow.qml +104 -0
  212. package/ui/FlashRunner.qml +119 -0
  213. package/ui/FlashStepRow.qml +172 -0
  214. package/ui/FlashStrip.qml +38 -0
  215. package/ui/FlashView.qml +397 -0
  216. package/ui/FleaMark.qml +88 -0
  217. package/ui/FlyoutFade.qml +23 -0
  218. package/ui/FontPane.qml +170 -0
  219. package/ui/Glass.qml +114 -0
  220. package/ui/GlassTagger.qml +59 -0
  221. package/ui/Glyph.qml +64 -0
  222. package/ui/GlyphSolid.qml +22 -0
  223. package/ui/GridArea.qml +664 -0
  224. package/ui/GridTile.qml +325 -0
  225. package/ui/GvfsBridge.qml +164 -0
  226. package/ui/Header.qml +433 -0
  227. package/ui/HoverTimers.qml +39 -0
  228. package/ui/HtmlClip.qml +30 -0
  229. package/ui/HtmlPane.qml +63 -0
  230. package/ui/HyprlandMark.qml +45 -0
  231. package/ui/Ipc.qml +1005 -0
  232. package/ui/IpcControls.qml +7 -0
  233. package/ui/IpcMenuState.qml +48 -0
  234. package/ui/IpcPreviewState.qml +185 -0
  235. package/ui/JumpPath.qml +51 -0
  236. package/ui/KeymapSheet.qml +561 -0
  237. package/ui/KeymapSheetResult.qml +89 -0
  238. package/ui/KindStack.qml +40 -0
  239. package/ui/LangMark.qml +51 -0
  240. package/ui/LangMarkLoader.qml +26 -0
  241. package/ui/List.qml +480 -0
  242. package/ui/LoadingState.qml +54 -0
  243. package/ui/LocalSend.qml +56 -0
  244. package/ui/LocalSendMark.qml +73 -0
  245. package/ui/MarkdownBlockView.qml +311 -0
  246. package/ui/MarkdownBlocks.qml +35 -0
  247. package/ui/MarkdownFigure.qml +254 -0
  248. package/ui/MarkdownImages.qml +72 -0
  249. package/ui/MarkdownList.qml +81 -0
  250. package/ui/MarkdownMathsText.qml +97 -0
  251. package/ui/MarkdownPane.qml +83 -0
  252. package/ui/MarkdownPictures.qml +41 -0
  253. package/ui/MarkdownQuote.qml +52 -0
  254. package/ui/MarkdownRemote.qml +82 -0
  255. package/ui/MarkdownSource.qml +155 -0
  256. package/ui/MarkdownTable.qml +222 -0
  257. package/ui/MarkdownTableScroll.qml +64 -0
  258. package/ui/MarkdownTableWheel.qml +126 -0
  259. package/ui/MarkdownText.qml +87 -0
  260. package/ui/MarkdownWorker.js +132 -0
  261. package/ui/MatchText.qml +99 -0
  262. package/ui/MediaSession.qml +38 -0
  263. package/ui/MediaSound.qml +26 -0
  264. package/ui/MediaStrip.qml +191 -0
  265. package/ui/MediaTrack.qml +36 -0
  266. package/ui/MediaVolume.qml +54 -0
  267. package/ui/MenuActionDialog.qml +396 -0
  268. package/ui/MenuEdgeFade.qml +24 -0
  269. package/ui/MenuHost.qml +227 -0
  270. package/ui/MenuRow.qml +351 -0
  271. package/ui/MountListing.qml +100 -0
  272. package/ui/NetworkDialog.qml +435 -0
  273. package/ui/NetworkForm.qml +426 -0
  274. package/ui/NetworkMounts.qml +913 -0
  275. package/ui/NetworkPlaces.qml +127 -0
  276. package/ui/OpenWithDialog.qml +613 -0
  277. package/ui/Opener.qml +95 -0
  278. package/ui/Pane.qml +1320 -0
  279. package/ui/PaneMenuActions.qml +527 -0
  280. package/ui/PanePath.qml +90 -0
  281. package/ui/PaneRail.qml +268 -0
  282. package/ui/PaneStates.qml +88 -0
  283. package/ui/PaneSwap.qml +339 -0
  284. package/ui/PaneWire.qml +601 -0
  285. package/ui/ParkedWait.qml +9 -0
  286. package/ui/PathEntry.qml +186 -0
  287. package/ui/PathJump.qml +400 -0
  288. package/ui/PdfViewer.qml +297 -0
  289. package/ui/PermissionsDialog.qml +658 -0
  290. package/ui/PermissionsHeadings.qml +31 -0
  291. package/ui/PhoneMounts.qml +67 -0
  292. package/ui/PickerAnswers.qml +75 -0
  293. package/ui/PickerBox.qml +26 -0
  294. package/ui/PickerChrome.qml +295 -0
  295. package/ui/PickerFilter.qml +97 -0
  296. package/ui/PickerGrid.qml +308 -0
  297. package/ui/PickerHeader.qml +28 -0
  298. package/ui/PickerLifecycle.qml +27 -0
  299. package/ui/PickerList.qml +218 -0
  300. package/ui/PickerListing.qml +149 -0
  301. package/ui/PickerPathBar.qml +127 -0
  302. package/ui/PickerPlaces.qml +189 -0
  303. package/ui/PickerRecent.qml +92 -0
  304. package/ui/PickerSave.qml +101 -0
  305. package/ui/PickerSelection.qml +155 -0
  306. package/ui/PickerWindow.qml +846 -0
  307. package/ui/PowerSectorsReader.qml +10 -0
  308. package/ui/PrebuildDriver.qml +146 -0
  309. package/ui/Preview.qml +1005 -0
  310. package/ui/PreviewAnimation.qml +24 -0
  311. package/ui/PreviewArchive.qml +91 -0
  312. package/ui/PreviewColumn.qml +553 -0
  313. package/ui/PreviewEdge.qml +63 -0
  314. package/ui/PreviewFacts.qml +30 -0
  315. package/ui/PreviewFactsLine.qml +34 -0
  316. package/ui/PreviewHtml.qml +233 -0
  317. package/ui/PreviewImage.qml +253 -0
  318. package/ui/PreviewImagePan.qml +13 -0
  319. package/ui/PreviewImageSharp.qml +32 -0
  320. package/ui/PreviewLines.qml +106 -0
  321. package/ui/PreviewMarkdown.qml +706 -0
  322. package/ui/PreviewMedia.qml +250 -0
  323. package/ui/PreviewPdf.qml +482 -0
  324. package/ui/PreviewScroll.qml +201 -0
  325. package/ui/PreviewSwap.qml +335 -0
  326. package/ui/PreviewText.qml +149 -0
  327. package/ui/ProtocolChip.qml +18 -0
  328. package/ui/QuickLookArchive.qml +41 -0
  329. package/ui/QuickLookBar.qml +103 -0
  330. package/ui/QuickLookPrepare.qml +355 -0
  331. package/ui/QuickLookSwap.qml +10 -0
  332. package/ui/QuickLookWarm.qml +65 -0
  333. package/ui/RailDrop.qml +155 -0
  334. package/ui/RailHeading.qml +19 -0
  335. package/ui/RailPlaces.qml +38 -0
  336. package/ui/RailTrashRow.qml +67 -0
  337. package/ui/RenameField.qml +353 -0
  338. package/ui/RendererRetry.qml +11 -0
  339. package/ui/ResizeHandle.qml +52 -0
  340. package/ui/Reveal.qml +54 -0
  341. package/ui/Row.qml +476 -0
  342. package/ui/RowDrag.qml +213 -0
  343. package/ui/RowDropTarget.qml +27 -0
  344. package/ui/RunCard.qml +286 -0
  345. package/ui/Scripts.qml +65 -0
  346. package/ui/SearchStrip.qml +232 -0
  347. package/ui/SelectionBand.qml +140 -0
  348. package/ui/SelectionPreview.qml +370 -0
  349. package/ui/SettingsFavourite.qml +122 -0
  350. package/ui/SettingsGroup.qml +96 -0
  351. package/ui/SettingsHint.qml +12 -0
  352. package/ui/SettingsMetrics.qml +29 -0
  353. package/ui/SettingsPane.qml +178 -0
  354. package/ui/SettingsPanel.qml +547 -0
  355. package/ui/SettingsRail.qml +128 -0
  356. package/ui/SettingsRow.qml +409 -0
  357. package/ui/SettingsRuler.qml +85 -0
  358. package/ui/SettingsSegment.qml +85 -0
  359. package/ui/ShareBrowser.qml +113 -0
  360. package/ui/ShareLink.qml +67 -0
  361. package/ui/ShelfPins.qml +101 -0
  362. package/ui/Sidebar.qml +728 -0
  363. package/ui/SidebarRow.qml +358 -0
  364. package/ui/Spinner.qml +60 -0
  365. package/ui/StableModel.qml +89 -0
  366. package/ui/StateMessage.qml +84 -0
  367. package/ui/StatusBar.qml +512 -0
  368. package/ui/StatusFileName.qml +27 -0
  369. package/ui/TabBar.qml +673 -0
  370. package/ui/TabDragGeometry.qml +60 -0
  371. package/ui/TagMenuMark.qml +52 -0
  372. package/ui/TagRailSection.qml +53 -0
  373. package/ui/TagSlot.qml +40 -0
  374. package/ui/TagState.qml +150 -0
  375. package/ui/TagSwatch.qml +28 -0
  376. package/ui/Taildrop.qml +86 -0
  377. package/ui/TailscaleMark.qml +50 -0
  378. package/ui/TerminalService.qml +69 -0
  379. package/ui/TerminalTab.qml +48 -0
  380. package/ui/TerminalTabs.qml +27 -0
  381. package/ui/Theme.qml +460 -0
  382. package/ui/ThemeColors.qml +43 -0
  383. package/ui/TransferCard.qml +239 -0
  384. package/ui/TrashConfirm.qml +129 -0
  385. package/ui/TrashDropHandler.qml +11 -0
  386. package/ui/TrashHost.qml +202 -0
  387. package/ui/TrashMonitor.qml +30 -0
  388. package/ui/TrashToolbar.qml +19 -0
  389. package/ui/TrashView.qml +492 -0
  390. package/ui/UnlockDialog.qml +450 -0
  391. package/ui/UnlockHost.qml +206 -0
  392. package/ui/UpdateCheck.qml +97 -0
  393. package/ui/UserDirs.qml +25 -0
  394. package/ui/ViewState.qml +565 -0
  395. package/ui/ViewportScrollBar.qml +195 -0
  396. package/ui/ViewportScrollBars.qml +25 -0
  397. package/ui/WebPreview.qml +33 -0
  398. package/ui/WebPreviewView.qml +26 -0
  399. package/ui/WindowBody.qml +829 -0
  400. package/ui/WindowDrag.qml +35 -0
  401. package/ui/boot/Commons/AudioNodes.qml +50 -0
  402. package/ui/boot/Commons/AudioNodesModel.js +96 -0
  403. package/ui/boot/Commons/Border.qml +243 -0
  404. package/ui/boot/Commons/BorderGeometry.js +373 -0
  405. package/ui/boot/Commons/Color.qml +258 -0
  406. package/ui/boot/Commons/I18n.qml +118 -0
  407. package/ui/boot/Commons/InputDevices.qml +112 -0
  408. package/ui/boot/Commons/InputMethodState.qml +38 -0
  409. package/ui/boot/Commons/IpcRegistry.qml +77 -0
  410. package/ui/boot/Commons/Numbers.js +19 -0
  411. package/ui/boot/Commons/Plural.js +91 -0
  412. package/ui/boot/Commons/ShellIpc.qml +12 -0
  413. package/ui/boot/Commons/Style.qml +561 -0
  414. package/ui/boot/Commons/Translations.qml +100 -0
  415. package/ui/boot/Commons/UntypedInput.qml +152 -0
  416. package/ui/boot/Commons/Util.qml +188 -0
  417. package/ui/boot/Commons/qmldir +13 -0
  418. package/ui/boot/LICENSE-omarchy +20 -0
  419. package/ui/boot/Ui/BackgroundMedia.qml +49 -0
  420. package/ui/boot/Ui/BarIconButton.qml +106 -0
  421. package/ui/boot/Ui/BarIconContent.qml +38 -0
  422. package/ui/boot/Ui/BarIndicator.qml +53 -0
  423. package/ui/boot/Ui/BarWidget.qml +50 -0
  424. package/ui/boot/Ui/BorderOverlay.qml +54 -0
  425. package/ui/boot/Ui/BorderSurface.qml +40 -0
  426. package/ui/boot/Ui/Button.qml +224 -0
  427. package/ui/boot/Ui/ButtonGroup.qml +131 -0
  428. package/ui/boot/Ui/ConfirmDialog.qml +134 -0
  429. package/ui/boot/Ui/CursorSurface.qml +42 -0
  430. package/ui/boot/Ui/Dropdown.qml +245 -0
  431. package/ui/boot/Ui/FilterInput.qml +26 -0
  432. package/ui/boot/Ui/IconContentBounds.qml +141 -0
  433. package/ui/boot/Ui/KeyboardPanel.qml +453 -0
  434. package/ui/boot/Ui/MultiSelect.qml +624 -0
  435. package/ui/boot/Ui/NumberField.qml +86 -0
  436. package/ui/boot/Ui/OpticalGlyph.qml +68 -0
  437. package/ui/boot/Ui/OverlayWindow.qml +102 -0
  438. package/ui/boot/Ui/Panel.qml +60 -0
  439. package/ui/boot/Ui/PanelActionButton.qml +105 -0
  440. package/ui/boot/Ui/PanelController.qml +16 -0
  441. package/ui/boot/Ui/PanelHero.qml +112 -0
  442. package/ui/boot/Ui/PanelKeyCatcher.qml +100 -0
  443. package/ui/boot/Ui/PanelSectionHeader.qml +31 -0
  444. package/ui/boot/Ui/PanelSeparator.qml +19 -0
  445. package/ui/boot/Ui/PanelSlider.qml +156 -0
  446. package/ui/boot/Ui/PanelToolTip.qml +50 -0
  447. package/ui/boot/Ui/PluginBarApi.qml +89 -0
  448. package/ui/boot/Ui/PointerMoveGate.qml +54 -0
  449. package/ui/boot/Ui/PopupCard.qml +181 -0
  450. package/ui/boot/Ui/ScreenMoveRemap.qml +43 -0
  451. package/ui/boot/Ui/SearchableDropdown.qml +354 -0
  452. package/ui/boot/Ui/SizedImage.qml +125 -0
  453. package/ui/boot/Ui/SpeedTestOverlay.qml +412 -0
  454. package/ui/boot/Ui/TextField.qml +58 -0
  455. package/ui/boot/Ui/Toggle.qml +118 -0
  456. package/ui/boot/Ui/ToggleSwitch.qml +115 -0
  457. package/ui/boot/Ui/WidgetButton.qml +172 -0
  458. package/ui/boot/Ui/qmldir +41 -0
  459. package/ui/boot/fleatab.qml +50 -0
  460. package/ui/boot/picker.qml +39 -0
  461. package/ui/boot/shell.qml +131 -0
  462. package/ui/boot/tabtearoff.qml +51 -0
  463. package/ui/js/Anchor.js +379 -0
  464. package/ui/js/AnchorHold.js +162 -0
  465. package/ui/js/Archive.js +157 -0
  466. package/ui/js/ArchiveCard.js +44 -0
  467. package/ui/js/ArchiveOps.js +44 -0
  468. package/ui/js/BackReturn.js +118 -0
  469. package/ui/js/BulkRename.js +100 -0
  470. package/ui/js/Buttons.js +54 -0
  471. package/ui/js/ChromeKeys.js +116 -0
  472. package/ui/js/ClipMarks.js +73 -0
  473. package/ui/js/ClipSelection.js +67 -0
  474. package/ui/js/Clipboard.js +179 -0
  475. package/ui/js/Cloud.js +97 -0
  476. package/ui/js/CodeRuns.js +72 -0
  477. package/ui/js/Collide.js +212 -0
  478. package/ui/js/ColumnFit.js +28 -0
  479. package/ui/js/ColumnMenu.js +111 -0
  480. package/ui/js/Columns.js +298 -0
  481. package/ui/js/ColumnsCarry.js +103 -0
  482. package/ui/js/Compare.js +74 -0
  483. package/ui/js/Contrast.js +157 -0
  484. package/ui/js/ControlReader.js +42 -0
  485. package/ui/js/Convert.js +52 -0
  486. package/ui/js/CopyAs.js +52 -0
  487. package/ui/js/Crumbs.js +178 -0
  488. package/ui/js/Csv.js +231 -0
  489. package/ui/js/Density.js +44 -0
  490. package/ui/js/Devices.js +440 -0
  491. package/ui/js/DirSizes.js +180 -0
  492. package/ui/js/Drag.js +360 -0
  493. package/ui/js/DragOut.js +166 -0
  494. package/ui/js/DropDev.js +52 -0
  495. package/ui/js/Dropbox.js +41 -0
  496. package/ui/js/Eject.js +217 -0
  497. package/ui/js/Errors.js +188 -0
  498. package/ui/js/ExtThumbs.js +115 -0
  499. package/ui/js/Facts.js +402 -0
  500. package/ui/js/FigureDate.js +14 -0
  501. package/ui/js/Filter.js +285 -0
  502. package/ui/js/Find.js +46 -0
  503. package/ui/js/Flash.js +184 -0
  504. package/ui/js/FlashCard.js +79 -0
  505. package/ui/js/FlashEndings.js +125 -0
  506. package/ui/js/FlashPane.js +116 -0
  507. package/ui/js/FleaBin.js +12 -0
  508. package/ui/js/Focus.js +504 -0
  509. package/ui/js/FolderSorts.js +108 -0
  510. package/ui/js/FolderViews.js +44 -0
  511. package/ui/js/Format.js +314 -0
  512. package/ui/js/Fuzzy.js +141 -0
  513. package/ui/js/GlassTag.js +72 -0
  514. package/ui/js/Grid.js +33 -0
  515. package/ui/js/GridGeometry.js +90 -0
  516. package/ui/js/GridNames.js +153 -0
  517. package/ui/js/GvfsBridge.js +200 -0
  518. package/ui/js/HostClipboard.js +29 -0
  519. package/ui/js/Icons.js +218 -0
  520. package/ui/js/Input.js +50 -0
  521. package/ui/js/Jump.js +200 -0
  522. package/ui/js/KeyName.js +64 -0
  523. package/ui/js/Keymap.js +401 -0
  524. package/ui/js/Kinds.js +164 -0
  525. package/ui/js/LICENSES/ISC-lucide.txt +43 -0
  526. package/ui/js/LangMarkPaths.js +47 -0
  527. package/ui/js/LangMarks.js +120 -0
  528. package/ui/js/Lists.js +11 -0
  529. package/ui/js/LocalSend.js +39 -0
  530. package/ui/js/LockedMenu.js +63 -0
  531. package/ui/js/MakeDefault.js +186 -0
  532. package/ui/js/MarkBox.js +7 -0
  533. package/ui/js/Markdown.js +119 -0
  534. package/ui/js/MarkdownLists.js +86 -0
  535. package/ui/js/MarkdownMaths.js +58 -0
  536. package/ui/js/MarkdownPictures.js +32 -0
  537. package/ui/js/MarkdownPrepared.js +139 -0
  538. package/ui/js/MarkdownTableFit.js +34 -0
  539. package/ui/js/Marks.js +148 -0
  540. package/ui/js/Marquee.js +96 -0
  541. package/ui/js/Match.js +93 -0
  542. package/ui/js/Material.js +109 -0
  543. package/ui/js/MdBlocks.js +437 -0
  544. package/ui/js/MdBreak.js +71 -0
  545. package/ui/js/MdChunks.js +70 -0
  546. package/ui/js/MdContainer.js +137 -0
  547. package/ui/js/MdDocument.js +337 -0
  548. package/ui/js/MdEmph.js +97 -0
  549. package/ui/js/MdEntity.js +108 -0
  550. package/ui/js/MdEntityTable.js +223 -0
  551. package/ui/js/MdEscape.js +59 -0
  552. package/ui/js/MdFront.js +37 -0
  553. package/ui/js/MdHold.js +32 -0
  554. package/ui/js/MdHtml.js +307 -0
  555. package/ui/js/MdHtmlBlock.js +290 -0
  556. package/ui/js/MdHtmlHeading.js +53 -0
  557. package/ui/js/MdHtmlImage.js +179 -0
  558. package/ui/js/MdInline.js +154 -0
  559. package/ui/js/MdItems.js +212 -0
  560. package/ui/js/MdLeaf.js +309 -0
  561. package/ui/js/MdLink.js +196 -0
  562. package/ui/js/MdMath.js +67 -0
  563. package/ui/js/MdParagraphs.js +138 -0
  564. package/ui/js/MdRefs.js +232 -0
  565. package/ui/js/MdResolve.js +226 -0
  566. package/ui/js/MdRun.js +300 -0
  567. package/ui/js/MdUrl.js +226 -0
  568. package/ui/js/Menu.js +514 -0
  569. package/ui/js/MenuContext.js +85 -0
  570. package/ui/js/MenuFit.js +13 -0
  571. package/ui/js/MenuRefresh.js +74 -0
  572. package/ui/js/MenuWheel.js +35 -0
  573. package/ui/js/Messages.js +186 -0
  574. package/ui/js/Motion.js +16 -0
  575. package/ui/js/Mounts.js +320 -0
  576. package/ui/js/MouseNav.js +13 -0
  577. package/ui/js/Nav.js +329 -0
  578. package/ui/js/NetFs.js +26 -0
  579. package/ui/js/OpenWith.js +83 -0
  580. package/ui/js/Ops.js +585 -0
  581. package/ui/js/Palette.js +33 -0
  582. package/ui/js/PathBar.js +260 -0
  583. package/ui/js/Permissions.js +179 -0
  584. package/ui/js/Phones.js +112 -0
  585. package/ui/js/Picker.js +353 -0
  586. package/ui/js/PlaceMenu.js +96 -0
  587. package/ui/js/Places.js +265 -0
  588. package/ui/js/PreviewAsk.js +21 -0
  589. package/ui/js/PreviewKeys.js +155 -0
  590. package/ui/js/PreviewSettle.js +29 -0
  591. package/ui/js/PreviewSwap.js +101 -0
  592. package/ui/js/PropertyFacts.js +144 -0
  593. package/ui/js/Protocols.js +296 -0
  594. package/ui/js/QuickLookKinds.js +27 -0
  595. package/ui/js/QuickLookRect.js +24 -0
  596. package/ui/js/RailDrop.js +51 -0
  597. package/ui/js/RailKeys.js +87 -0
  598. package/ui/js/RailMenu.js +133 -0
  599. package/ui/js/Recent.js +135 -0
  600. package/ui/js/RecentMode.js +156 -0
  601. package/ui/js/Reload.js +60 -0
  602. package/ui/js/Renderer.js +9 -0
  603. package/ui/js/RunFile.js +143 -0
  604. package/ui/js/Scripts.js +28 -0
  605. package/ui/js/Scroll.js +304 -0
  606. package/ui/js/ScrollOff.js +69 -0
  607. package/ui/js/Search.js +478 -0
  608. package/ui/js/Selection.js +97 -0
  609. package/ui/js/Settings.js +476 -0
  610. package/ui/js/SettingsAbout.js +97 -0
  611. package/ui/js/SettingsShelf.js +52 -0
  612. package/ui/js/SheetKeys.js +42 -0
  613. package/ui/js/SheetQuery.js +281 -0
  614. package/ui/js/ShelfPile.js +27 -0
  615. package/ui/js/SlowClick.js +53 -0
  616. package/ui/js/SlowOp.js +6 -0
  617. package/ui/js/Sort.js +134 -0
  618. package/ui/js/Startup.js +63 -0
  619. package/ui/js/StateWords.js +17 -0
  620. package/ui/js/Status.js +238 -0
  621. package/ui/js/Swap.js +166 -0
  622. package/ui/js/TabMove.js +39 -0
  623. package/ui/js/Tabs.js +751 -0
  624. package/ui/js/Tags.js +134 -0
  625. package/ui/js/Taildrop.js +128 -0
  626. package/ui/js/Tap.js +180 -0
  627. package/ui/js/TextSize.js +107 -0
  628. package/ui/js/Thumbs.js +158 -0
  629. package/ui/js/Transfer.js +172 -0
  630. package/ui/js/Trash.js +84 -0
  631. package/ui/js/TrashDates.js +47 -0
  632. package/ui/js/TrashDrop.js +64 -0
  633. package/ui/js/UiState.js +438 -0
  634. package/ui/js/Unlock.js +152 -0
  635. package/ui/js/Update.js +130 -0
  636. package/ui/js/UserDirs.js +56 -0
  637. package/ui/qmldir +230 -0
@@ -0,0 +1,157 @@
1
+ import QtQuick
2
+ import qs.Commons
3
+ import "." as Flea
4
+ import "js/Collide.js" as Collide
5
+ import "js/Icons.js" as Icons
6
+
7
+ // FAT-family names, on CollideConfirm's card, keys and look.
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
+ property bool opened: false
16
+ property string titleText: ""
17
+ property var renames: []
18
+ property string noteText: ""
19
+ property string focusName: Collide.FAT_DEFAULT
20
+ // The card geometry lives in ui/js/Collide.js, shared with CollideConfirm.
21
+ readonly property real referenceScale: Collide.cardReferenceScale(Theme.font.bodySmall)
22
+ readonly property real cardPadding: Collide.cardPadding(referenceScale)
23
+ readonly property real cardBottomPadding: Theme.spacing.rowPaddingX
24
+ // The card grows with the labels rather than wrapping one, and Rename and copy keeps the ring.
25
+ readonly property real contentWidth: Collide.cardContentWidth(referenceScale, Theme.dialogWidthRatio, buttons.implicitWidth)
26
+ signal chosen(string choice)
27
+ readonly property var cardItem: card
28
+ readonly property bool buttonsFit: buttons.width <= body.holderWidth
29
+ function buttonItem(name) { return ({cancel: cancelButton, skip: skipButton, rename: renameButton})[name] || null }
30
+ function open(heading, list, note) {
31
+ titleText = heading
32
+ renames = list
33
+ noteText = note
34
+ focusName = Collide.FAT_DEFAULT
35
+ body.contentY = 0
36
+ opened = true
37
+ forceActiveFocus()
38
+ }
39
+ function choose(choice) {
40
+ opened = false
41
+ chosen(choice)
42
+ }
43
+ Flea.Reveal { target: root; shown: root.opened; lift: card }
44
+ // A press lands on its own button first, so the accent never shows one choice while another is taken.
45
+ function press(choice) {
46
+ focusName = choice
47
+ choose(choice)
48
+ }
49
+ Keys.onPressed: function(event) {
50
+ if (event.key === Qt.Key_Escape) root.choose("cancel")
51
+ else if (event.modifiers & (Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier)) { event.accepted = true; return }
52
+ else if (Collide.activates(event.key)) root.choose(root.focusName)
53
+ else root.focusName = Collide.walk(["cancel", "skip", "rename"], root.focusName, event.key, event.modifiers, "cancel")
54
+ if (root.opened) body.reveal(root.buttonItem(root.focusName))
55
+ event.accepted = true
56
+ }
57
+ Flea.DialogScrim {
58
+ onDismissed: root.choose("cancel")
59
+ }
60
+ Rectangle {
61
+ id: card
62
+ x: Theme.cardOrigin(root.width, width)
63
+ y: Theme.cardOrigin(root.height, height)
64
+ width: Theme.cardSpan(root.contentWidth + 2 * root.cardPadding + 2 * Theme.spacing.hairline, root.width - 2 * Theme.spacing.gap)
65
+ height: Theme.cardSpan(body.wanted + root.cardPadding + root.cardBottomPadding + 2 * Theme.spacing.hairline, root.height - 2 * Theme.spacing.gap)
66
+ color: Theme.color.surface
67
+ border.color: Theme.color.mutedFrame
68
+ border.width: Theme.spacing.hairline
69
+ radius: Style.cornerRadius
70
+ MouseArea {
71
+ anchors.fill: parent
72
+ hoverEnabled: true
73
+ acceptedButtons: Qt.LeftButton | Qt.RightButton
74
+ onWheel: function(wheel) { wheel.accepted = true }
75
+ }
76
+ Flea.CardFrame {
77
+ id: body
78
+ bleed: Theme.focusClearance
79
+ anchors.fill: parent
80
+ anchors.leftMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
81
+ anchors.rightMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
82
+ anchors.topMargin: root.cardPadding + Theme.spacing.hairline - Theme.focusClearance
83
+ anchors.bottomMargin: root.cardBottomPadding + Theme.spacing.hairline - Theme.focusClearance
84
+ title: Column {
85
+ width: parent.width
86
+ spacing: Theme.spacing.gap
87
+ Flea.DialogTitle {
88
+ width: parent.width
89
+ text: root.titleText
90
+ }
91
+ Rectangle { width: parent.width; height: Theme.spacing.hairline; color: Theme.color.mutedFrame; opacity: 0.4 }
92
+ }
93
+ buttons: Row {
94
+ id: buttons
95
+ anchors.right: parent.right
96
+ spacing: Theme.spacing.gap
97
+ Flea.DialogButton { id: cancelButton; label: Collide.FAT_BUTTONS.cancel; focused: root.focusName === "cancel"; onActivated: root.press("cancel") }
98
+ Flea.DialogButton { id: skipButton; label: Collide.FAT_BUTTONS.skip; focused: root.focusName === "skip"; onActivated: root.press("skip") }
99
+ Flea.DialogButton { id: renameButton; label: Collide.FAT_BUTTONS.rename; primary: true; focused: root.focusName === "rename"; onActivated: root.press("rename") }
100
+ }
101
+ Column {
102
+ width: parent.width
103
+ spacing: Theme.spacing.gap
104
+ Column {
105
+ width: parent.width
106
+ Repeater {
107
+ model: root.renames
108
+ delegate: Row {
109
+ id: nameRow
110
+ required property var modelData
111
+ // The label column: what is left of the row once the mark and its gap are taken.
112
+ readonly property real labelWidth: Math.max(0, width - Theme.markSize - spacing)
113
+ width: parent.width
114
+ height: Theme.fileRowHeight
115
+ spacing: Theme.spacing.gap
116
+ Flea.Glyph { width: Theme.markSize; height: parent.height; name: nameRow.modelData.d ? "folder" : Icons.glyphFor(nameRow.modelData.i); color: Theme.color.foreground }
117
+ Text {
118
+ id: oldName
119
+ width: Math.min(implicitWidth, Math.max(0, nameRow.labelWidth - arrow.implicitWidth))
120
+ anchors.verticalCenter: parent.verticalCenter
121
+ text: nameRow.modelData.old
122
+ textFormat: Text.PlainText
123
+ elide: Text.ElideRight
124
+ color: Theme.color.muted
125
+ font { family: Theme.font.family; pixelSize: Theme.font.body }
126
+ }
127
+ Text {
128
+ id: arrow
129
+ anchors.verticalCenter: parent.verticalCenter
130
+ text: " → "
131
+ color: Theme.color.foreground
132
+ font { family: Theme.font.family; pixelSize: Theme.font.body }
133
+ }
134
+ Text {
135
+ width: Math.max(0, nameRow.labelWidth - oldName.width - arrow.implicitWidth)
136
+ anchors.verticalCenter: parent.verticalCenter
137
+ text: nameRow.modelData.new
138
+ textFormat: Text.PlainText
139
+ elide: Text.ElideRight
140
+ color: Theme.color.foreground
141
+ font { family: Theme.font.family; pixelSize: Theme.font.body }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ Text {
147
+ width: parent.width
148
+ text: root.noteText
149
+ textFormat: Text.PlainText
150
+ wrapMode: Text.Wrap
151
+ color: Theme.color.foreground
152
+ font { family: Theme.font.family; pixelSize: Theme.font.caption }
153
+ }
154
+ }
155
+ }
156
+ }
157
+ }
@@ -0,0 +1,143 @@
1
+ pragma Singleton
2
+ import QtQuick
3
+ import Quickshell
4
+ import Quickshell.Io
5
+ import "js/UiState.js" as UiState
6
+ import "js/Startup.js" as Startup
7
+
8
+ // All callers share one operation writer; the Rust updater locks and re-reads before editing.
9
+ QtObject {
10
+ id: root
11
+ readonly property var records: ((ViewState.state.places || {}).favourites || [])
12
+ readonly property bool busy: writer.running
13
+ property bool operationActive: false
14
+ property int recordCount: 0
15
+ property var statuses: ({})
16
+ onRecordsChanged: {
17
+ var previousCount = root.recordCount
18
+ root.recordCount = root.records.length
19
+ root.statuses = ({})
20
+ if (!root.operationActive) root.externalChanged(previousCount)
21
+ }
22
+ property string lastError: ""
23
+ signal wrote()
24
+ signal failed(string message)
25
+ signal completed(string requestId, bool success, string message)
26
+ signal externalChanged(int previousCount)
27
+
28
+ function apply(operation, requestId) {
29
+ if (root.busy) { root.failed("A Favorites change is still being saved."); return false }
30
+ root.lastError = ""
31
+ writer.answer = ""
32
+ writer.errorText = ""
33
+ writer.requestId = requestId || ""
34
+ writer.beforeRecords = root.records
35
+ writer.expectedRecords = UiState.favouritesAfter(root.records, operation)
36
+ root.operationActive = true
37
+ writer.command = [Quickshell.env("FLEA_BIN") || "flea", "--favourites", JSON.stringify(operation)]
38
+ writer.pending = true
39
+ writer.running = true
40
+ return true
41
+ }
42
+ function add(path, label, requestId) {
43
+ return root.apply({ op: "add", record: { label: label, path: path } }, requestId)
44
+ }
45
+ function remove(index) {
46
+ return root.apply({ op: "remove", index: index, expected: root.records })
47
+ }
48
+ function move(index, to) {
49
+ return root.apply({ op: "move", index: index, to: to, expected: root.records })
50
+ }
51
+ function rename(index, label) {
52
+ return root.apply({ op: "rename", index: index, label: label, expected: root.records })
53
+ }
54
+ // One favourite on a dead mount must not hold the single-flight guard forever.
55
+ readonly property int inspectWaitMs: Startup.INSPECT_WAIT_MS
56
+ property var inspectTimeout: Timer {
57
+ interval: root.inspectWaitMs
58
+ onTriggered: inspector.running = false
59
+ }
60
+
61
+ function inspect(indices) {
62
+ var pending = indices.filter(function (index) { return root.statuses[index] === undefined })
63
+ if (pending.length === 0 || inspector.running) return
64
+ inspector.answer = ""
65
+ inspector.command = [Quickshell.env("FLEA_BIN") || "flea", "--favourites", JSON.stringify({ op: "inspect", indices: pending.slice(0, 128) })]
66
+ inspector.running = true
67
+ inspectTimeout.restart()
68
+ }
69
+ function finish(expected) {
70
+ var changed = JSON.stringify(root.records) !== JSON.stringify(expected)
71
+ root.operationActive = false
72
+ if (changed) root.externalChanged(writer.beforeRecords.length)
73
+ }
74
+ property var stateChanges: Connections {
75
+ target: ViewState
76
+ function onFavouritesReadFailed(message) { root.failed(message) }
77
+ }
78
+ property var inspection: Process {
79
+ id: inspector
80
+ property string answer: ""
81
+ stdout: StdioCollector { onStreamFinished: inspector.answer = this.text }
82
+ onExited: function (code) {
83
+ inspectTimeout.stop()
84
+ if (code !== 0) return
85
+ try {
86
+ var rows = JSON.parse(inspector.answer).statuses
87
+ var next = Object.assign({}, root.statuses)
88
+ for (var i = 0; i < rows.length; i++) {
89
+ if (JSON.stringify(root.records[rows[i].index]) === JSON.stringify(rows[i].record))
90
+ next[rows[i].index] = rows[i].error
91
+ }
92
+ root.statuses = next
93
+ } catch (error) { root.failed("Flea could not check which favorites still exist.") }
94
+ }
95
+ }
96
+
97
+ property var process: Process {
98
+ id: writer
99
+ property string answer: ""
100
+ property string errorText: ""
101
+ property bool pending: false
102
+ property string requestId: ""
103
+ property var beforeRecords: []
104
+ property var expectedRecords: []
105
+ onStarted: writer.pending = false
106
+ onRunningChanged: {
107
+ if (!running && pending) {
108
+ pending = false
109
+ root.finish(writer.beforeRecords)
110
+ root.lastError = "Favorites could not be saved."
111
+ root.failed(root.lastError)
112
+ root.completed(writer.requestId, false, root.lastError)
113
+ }
114
+ }
115
+ stdout: StdioCollector { onStreamFinished: writer.answer = this.text }
116
+ stderr: StdioCollector { onStreamFinished: writer.errorText = this.text.trim() }
117
+ onExited: function (code) {
118
+ writer.pending = false
119
+ if (code !== 0) {
120
+ ViewState.refreshFavourites()
121
+ root.finish(writer.beforeRecords)
122
+ root.lastError = writer.errorText.replace(/^flea: /, "").split("\n")[0] || "Favorites could not be saved."
123
+ root.failed(root.lastError)
124
+ root.completed(writer.requestId, false, root.lastError)
125
+ return
126
+ }
127
+ try {
128
+ var state = JSON.parse(writer.answer)
129
+ if (!state.places || !Array.isArray(state.places.favourites)) throw new Error("missing favorites")
130
+ if (!ViewState.refreshFavourites()) throw new Error("saved favorites could not be read")
131
+ root.finish(writer.expectedRecords)
132
+ root.wrote()
133
+ root.completed(writer.requestId, true, "")
134
+ } catch (error) {
135
+ root.finish(writer.beforeRecords)
136
+ root.lastError = "Favorites were saved, but their new state could not be read."
137
+ root.failed(root.lastError)
138
+ // CLI exit zero means the write committed; a response error must never replay the add.
139
+ root.completed(writer.requestId, true, root.lastError)
140
+ }
141
+ }
142
+ }
143
+ }
@@ -0,0 +1,120 @@
1
+ pragma Singleton
2
+
3
+ import QtQuick
4
+
5
+ // The block view's diagrams, drawn by the backend (src/figure.js, Pele in a Bun Worker) through a
6
+ // window's Backend, which WindowBody adopts and releases. Answers come by ticket through `done`,
7
+ // and recent ones are held here, so a revisit or a theme flip back asks nothing.
8
+ QtObject {
9
+ id: root
10
+
11
+ signal done(int ticket, string svg, string error)
12
+
13
+ // Every open window's Backend, adopted as each window is built and released as it closes; the
14
+ // first still open draws. With none every ask fails, and the figure shows its source.
15
+ property var backends: []
16
+ readonly property var backend: root.backends.length > 0 ? root.backends[0] : null
17
+ function adopt(backend) {
18
+ if (backend && root.backends.indexOf(backend) < 0)
19
+ root.backends = root.backends.concat([backend]);
20
+ }
21
+ function release(backend) {
22
+ var at = root.backends.indexOf(backend);
23
+ if (at < 0)
24
+ return;
25
+ root.backends = root.backends.filter(function (other) { return other !== backend; });
26
+ if (at !== 0)
27
+ return;
28
+ // The drawing backend went with its window: what it still owed fails, and a new ask goes to the next one.
29
+ var owed = Object.keys(root.waiting);
30
+ root.waiting = ({});
31
+ root.byRequest = ({});
32
+ for (var i = 0; i < owed.length; i++)
33
+ root.done(Number(owed[i]), "", "backend closed");
34
+ }
35
+ readonly property int cacheMax: 64
36
+ property int seq: 0
37
+ // ticket -> cache key, and backend request id -> ticket, for the answer that names only its id.
38
+ property var waiting: ({})
39
+ property var byRequest: ({})
40
+ property var answerCache: ({})
41
+ property var answerOrder: []
42
+
43
+ // FNV-1a over the table's comma-joined text, "0" for none.
44
+ function tableDigestOf(table) {
45
+ if (!table || table.length === 0)
46
+ return "0";
47
+ var fnvOffset = 0x811c9dc5;
48
+ var fnvPrime = 0x01000193;
49
+ var hexRadix = 16;
50
+ var text = table.join(",");
51
+ var hash = fnvOffset;
52
+ for (var i = 0; i < text.length; i++)
53
+ hash = Math.imul(hash ^ text.charCodeAt(i), fnvPrime);
54
+ return (hash >>> 0).toString(hexRadix);
55
+ }
56
+
57
+ // The cache key: everything the drawing depends on; a dated figure's day joins it, so the next day's ask draws that day's chart.
58
+ function cacheKeyOf(kind, source, t, display) {
59
+ var key = [t.bg, t.fg, t.accent || "", t.muted || "", t.surface || "", t.font || "", t.bodyPx || 0,
60
+ root.tableDigestOf(t.advances), root.tableDigestOf(t.boldAdvances)].join("|");
61
+ return kind + "\n" + key + (t.today ? "|" + t.today : "") + "\n" + !!display + "\n" + source;
62
+ }
63
+
64
+ function cached(kind, source, theme, display) {
65
+ var key = root.cacheKeyOf(kind, source, theme, display);
66
+ var hit = root.answerCache[key];
67
+ if (hit !== undefined) {
68
+ var at = root.answerOrder.indexOf(key);
69
+ if (at >= 0)
70
+ root.answerOrder.splice(at, 1);
71
+ root.answerOrder.push(key);
72
+ }
73
+ return hit;
74
+ }
75
+
76
+ function store(key, svg) {
77
+ var at = root.answerOrder.indexOf(key);
78
+ if (at >= 0)
79
+ root.answerOrder.splice(at, 1);
80
+ root.answerCache[key] = svg;
81
+ root.answerOrder.push(key);
82
+ while (root.answerOrder.length > root.cacheMax)
83
+ delete root.answerCache[root.answerOrder.shift()];
84
+ }
85
+
86
+ // A ticket for one figure; its answer comes later through done(ticket, svg, error), never during this call.
87
+ function ask(kind, source, display, theme) {
88
+ root.seq++;
89
+ var ticket = root.seq;
90
+ var hit = root.cached(kind, source, theme, display);
91
+ if (hit !== undefined) {
92
+ Qt.callLater(function () { root.done(ticket, hit, ""); });
93
+ return ticket;
94
+ }
95
+ if (kind !== "mermaid" || root.backend === null || typeof root.backend.askFigure !== "function") {
96
+ Qt.callLater(function () { root.done(ticket, "", kind !== "mermaid" ? "unknown figure kind" : "no backend"); });
97
+ return ticket;
98
+ }
99
+ var id = root.backend.askFigure(source, theme);
100
+ root.waiting[ticket] = root.cacheKeyOf(kind, source, theme, display);
101
+ root.byRequest[id] = ticket;
102
+ return ticket;
103
+ }
104
+
105
+ property Connections answers: Connections {
106
+ target: root.backend
107
+ ignoreUnknownSignals: true
108
+ function onFigureResult(message) {
109
+ var ticket = root.byRequest[message.id];
110
+ if (ticket === undefined)
111
+ return;
112
+ delete root.byRequest[message.id];
113
+ var key = root.waiting[ticket];
114
+ delete root.waiting[ticket];
115
+ if (message.svg)
116
+ root.store(key, message.svg);
117
+ root.done(ticket, message.svg || "", message.svg ? "" : (message.error || "render failed"));
118
+ }
119
+ }
120
+ }
@@ -0,0 +1,267 @@
1
+ import QtQuick
2
+ import "js/Drag.js" as DragOps
3
+ import "js/DragOut.js" as DragOut
4
+ import "js/Ops.js" as Ops
5
+ import "js/Search.js" as Search
6
+
7
+ // The platform drag belongs to the view: a tab switch may destroy the pressed delegate inside QDrag.exec().
8
+ Item {
9
+ id: root
10
+
11
+ required property var pane
12
+ // A peek column's {pane, dir, v, w}: its rows are in no listing, so a lift carries the row's own path instead.
13
+ property var peekSource: null
14
+ // The window the gesture speaks in: the listing's pane, or the pane a peek column belongs to.
15
+ readonly property var host: root.pane || (root.peekSource ? root.peekSource.pane : null)
16
+ property var dragRows: []
17
+ // The numbering dragRows were read in, fixed at the lift so a drop after a re-list is refused rather than resolved anew.
18
+ property real dragListing: 0
19
+ property int dropIndex: -1
20
+ // The hovered folder a drop lands in after it opens: a swap may rebuild the lit delegate on another row.
21
+ property string dropDest: ""
22
+ property int dropDestDev: 0
23
+ property bool dragCopy: false
24
+ property bool dragShift: false
25
+ property bool dragLink: false
26
+ property bool awaitingPaths: false
27
+ property bool buttonUp: false
28
+ property var dragMime: ({})
29
+ property var feedback: null
30
+ // Drops callout 2: the folder the pointer rests on and the autoscroll band it rests in; the timers are built on first use.
31
+ property int hoverIndex: -1
32
+ property string hoverPath: ""
33
+ property int scrollDir: 0
34
+ property var hoverTimers: null
35
+
36
+ Drag.dragType: Drag.Automatic
37
+ // Plain lift offers copy alone, narrowed by modifiers (a browser uploader refuses move); Ctrl copy, Shift move, both link, so any receiver takes the lift's verb.
38
+ Drag.supportedActions: root.dragLink ? Qt.LinkAction : root.dragCopy ? Qt.CopyAction : root.dragShift ? Qt.MoveAction : Qt.CopyAction
39
+ Drag.proposedAction: root.dragLink ? Qt.LinkAction : root.dragCopy ? Qt.CopyAction : root.dragShift ? Qt.MoveAction : Qt.CopyAction
40
+ Drag.mimeData: root.dragMime
41
+ // The receiver moves or copies itself: deleting on its acceptance would trash files before Files reads them.
42
+ Drag.onDragFinished: function () { root.liftEnded() }
43
+
44
+ function liftBegan(index, centroid) {
45
+ if (!root.pane || root.pane.listInFlight || index < 0 || !root.pane.rowFor(index)) return
46
+ if (root.pane.cancelSlowClick) root.pane.cancelSlowClick()
47
+ root.buttonUp = false
48
+ root.awaitingPaths = false
49
+ if (root.pane) root.pane.awaitingPaths = false
50
+ root.dragRows = DragOps.carried(root.pane, index)
51
+ root.dragListing = root.pane.backend ? root.pane.backend.heldListing : 0
52
+ root.dropIndex = -1
53
+ root.dropDest = ""
54
+ root.dropDestDev = 0
55
+ // Ctrl is the threshold's, and a paths reply must not sample it again.
56
+ root.dragCopy = DragOps.copying(centroid.modifiers)
57
+ root.dragShift = DragOps.shifting(centroid.modifiers)
58
+ root.dragLink = DragOps.linking(centroid.modifiers)
59
+ root.dragMime = DragOps.mimeFor(root.pane, root.dragRows, root.dragCopy, root.dragShift)
60
+ if (root.dragMime["text/uri-list"]) {
61
+ root.startOffer()
62
+ return
63
+ }
64
+ if (!root.pane.backend || !DragOut.askAllowed(root.pane.pathsPending, root.pane.clipPending)) {
65
+ root.cannotLeave()
66
+ return
67
+ }
68
+ root.awaitingPaths = true
69
+ if (root.pane) root.pane.awaitingPaths = true
70
+ var dev = root.pane.backend.dirDev || 0
71
+ root.feedback = { own: true, copy: root.dragCopy, shift: root.dragShift, dev: dev,
72
+ deletable: DragOps.listingDeletable(root.pane), count: root.dragRows.length, canLeave: true }
73
+ root.say(DragOps.feedbackLine(root.feedback, "", dev))
74
+ var rows = root.dragRows.slice()
75
+ var listing = root.dragListing
76
+ var copy = root.dragCopy
77
+ var shift = root.dragShift
78
+ var link = root.dragLink
79
+ root.pane.pathsPending = {
80
+ kind: "drag", rows: rows, listing: listing, copy: copy, shift: shift, link: link,
81
+ deliver: function (list, pending) { root.deliverPaths(list, pending) }
82
+ }
83
+ root.pane.backend.send({ c: "paths", rows: rows, listing: listing })
84
+ }
85
+
86
+ function liftPeek(row, centroid) {
87
+ if (!root.peekSource || !row) return
88
+ root.buttonUp = false
89
+ root.dragRows = []
90
+ root.dragListing = 0
91
+ root.dropIndex = -1
92
+ root.dropDest = ""
93
+ root.dropDestDev = 0
94
+ root.dragCopy = DragOps.copying(centroid.modifiers)
95
+ root.dragShift = DragOps.shifting(centroid.modifiers)
96
+ root.dragLink = DragOps.linking(centroid.modifiers)
97
+ root.dragMime = root.peekMime(row, root.dragCopy, root.dragShift)
98
+ root.startOffer()
99
+ }
100
+
101
+ // No rows in the marker, so no receiver resolves it by index (DragOps.canDropByIndex needs rows); the uri is the column's directory plus the name.
102
+ function peekMime(row, copy, shift) {
103
+ var src = root.peekSource
104
+ var path = src.pane.join(src.dir, row.n)
105
+ var mime = {}
106
+ mime[DragOps.ROWS_MIME] = DragOps.markerPayload([], copy, src.dir, src.v, src.w, shift === true)
107
+ mime["text/uri-list"] = DragOps.uriFor(path) + "\r\n"
108
+ mime["text/plain"] = path
109
+ return mime
110
+ }
111
+
112
+ function liftReleased() {
113
+ root.buttonUp = true
114
+ }
115
+
116
+ function liftMoved(centroid) {
117
+ if (root.awaitingPaths || root.Drag.active) return
118
+ root.dragCopy = DragOps.copying(centroid.modifiers)
119
+ root.dragShift = DragOps.shifting(centroid.modifiers)
120
+ root.dragLink = DragOps.linking(centroid.modifiers)
121
+ }
122
+
123
+ function startOffer() {
124
+ root.dragMime = DragOut.keys.stamped(root.dragMime)
125
+ root.feedback = DragOps.feedbackFor(root.dragMime[DragOps.ROWS_MIME],
126
+ (root.dragMime["text/uri-list"] || "").split("\r\n"))
127
+ root.showTarget("", root.feedback.dev)
128
+ // Drag.Automatic runs QDrag::exec synchronously, so a finish inside it must not overwrite the hold: arm the watch first.
129
+ if (root.host) root.host.dragHolding = true
130
+ root.Drag.active = true
131
+ }
132
+
133
+ function deliverPaths(list, pending) {
134
+ root.awaitingPaths = false
135
+ if (root.pane) root.pane.awaitingPaths = false
136
+ var held = root.pane && root.pane.backend ? root.pane.backend.heldListing : 0
137
+ if (root.buttonUp || held !== pending.listing || !list || list.length !== pending.rows.length) {
138
+ root.cannotLeave()
139
+ return
140
+ }
141
+ root.dragCopy = pending.copy
142
+ root.dragShift = pending.shift === true
143
+ root.dragLink = pending.link === true || (pending.copy === true && pending.shift === true)
144
+ root.dragMime = root.mimeForPaths(pending.rows, list, pending.copy, root.dragShift)
145
+ root.startOffer()
146
+ }
147
+
148
+ function mimeForPaths(rows, paths, copy, shift) {
149
+ var mime = {}
150
+ var dev = root.pane.backend ? root.pane.backend.dirDev : 0
151
+ mime[DragOps.ROWS_MIME] = DragOps.markerPayload(rows, copy, Search.baseOf(root.pane), dev, DragOps.listingDeletable(root.pane), shift === true)
152
+ var uris = []
153
+ for (var i = 0; i < paths.length; i++) uris.push(DragOps.uriFor(paths[i]))
154
+ mime["text/uri-list"] = uris.join("\r\n") + "\r\n"
155
+ mime["text/plain"] = paths.join("\n")
156
+ return mime
157
+ }
158
+
159
+ // Too wide carries no uri-list, so the gesture ends here and the refusal reads as a pane message.
160
+ function cannotLeave() {
161
+ var line = DragOps.line(root.dragRows.length, "", root.dragCopy, root.dragLink) + DragOps.reachNote(false)
162
+ root.awaitingPaths = false
163
+ root.liftEnded()
164
+ if (root.host) root.host.message(line, false)
165
+ }
166
+
167
+ function verbAt(marker, row, seenCopy, seenShift) {
168
+ return DragOps.verbFor(DragOps.isOwnDrag(marker), DragOps.dropHeld(DragOps.markerCopying(marker), seenCopy),
169
+ DragOps.dropHeld(DragOps.markerShift(marker), seenShift), DragOps.markerDev(marker),
170
+ row ? row.v : 0, DragOps.markerDeletable(marker))
171
+ }
172
+
173
+ function liftEnded() {
174
+ root.Drag.active = false
175
+ if (root.host) { root.host.dragHolding = false; root.host.awaitingPaths = false; root.host.pointerHeld = false }
176
+ root.dragRows = []
177
+ root.dragListing = 0
178
+ root.dragMime = ({})
179
+ root.dropIndex = -1
180
+ root.dropDest = ""
181
+ root.dropDestDev = 0
182
+ root.dragCopy = false
183
+ root.dragShift = false
184
+ root.dragLink = false
185
+ root.feedback = null
186
+ root.hoverIndex = -1
187
+ root.hoverPath = ""
188
+ root.scrollDir = 0
189
+ if (root.hoverTimers) root.hoverTimers.stopRest()
190
+ // A different view may now speak for this gesture; its activity is separate from every transfer.
191
+ var bar = root.host ? root.host.statusBar : null
192
+ if (bar && bar.dragFeedbackOwner) bar.dragFeedbackOwner.say("")
193
+ }
194
+
195
+ // A folder row under the pointer starts the rest that opens it and the band that scrolls; a move restarts both.
196
+ function hoverBegan(index, path, y, height) {
197
+ root.hoverIndex = index
198
+ root.hoverPath = path
199
+ root.scrollDir = DragOut.band(y, height)
200
+ root.ensureHoverTimers()
201
+ root.hoverTimers.rest()
202
+ }
203
+
204
+ function hoverMoved(y, height) {
205
+ root.scrollDir = DragOut.band(y, height)
206
+ if (root.scrollDir !== 0) root.ensureHoverTimers()
207
+ if (root.hoverTimers) root.hoverTimers.rest()
208
+ }
209
+
210
+ function hoverEnded() {
211
+ root.hoverIndex = -1
212
+ root.hoverPath = ""
213
+ root.scrollDir = 0
214
+ if (root.hoverTimers) root.hoverTimers.stopRest()
215
+ }
216
+
217
+ function ensureHoverTimers() {
218
+ if (root.hoverTimers !== null)
219
+ return
220
+ root.hoverTimers = Qt.createComponent("HoverTimers.qml").createObject(root, { session: root })
221
+ }
222
+
223
+ function say(text) {
224
+ var bar = root.host ? root.host.statusBar : null
225
+ if (!bar) return
226
+ if (!text) {
227
+ if (bar.dragFeedbackOwner === root) {
228
+ bar.dragFeedbackOwner = null
229
+ bar.setActivity(root, "", Ops.emptyTransfer())
230
+ }
231
+ return
232
+ }
233
+ if (bar.activities.some(function (entry) { return entry.transfer.running })) return
234
+ if (bar.dragFeedbackOwner && bar.dragFeedbackOwner !== root)
235
+ bar.setActivity(bar.dragFeedbackOwner, "", Ops.emptyTransfer())
236
+ bar.dragFeedbackOwner = root
237
+ bar.setActivity(root, text, Ops.emptyTransfer())
238
+ }
239
+
240
+ function enterTarget(marker, urls, name, destDev, shelf, proposed) {
241
+ root.feedback = DragOps.feedbackFor(marker, urls, shelf, proposed)
242
+ root.showTarget(name, destDev)
243
+ }
244
+
245
+ function showTarget(name, destDev) {
246
+ if (!root.feedback) return
247
+ root.dragCopy = DragOps.copyingFor(root.feedback, destDev)
248
+ root.dragLink = DragOps.linkingFor(root.feedback, destDev)
249
+ root.say(DragOps.feedbackLine(root.feedback, name, destDev))
250
+ }
251
+
252
+ function leaveTarget() {
253
+ var bar = root.host ? root.host.statusBar : null
254
+ if (!bar || bar.dragFeedbackOwner !== root) return
255
+ root.say(root.feedback && root.feedback.own
256
+ ? DragOps.feedbackLine(root.feedback, "", root.feedback.dev) : "")
257
+ }
258
+
259
+ Connections {
260
+ target: root.host ? root.host.statusBar : null
261
+ function onActivitiesChanged() {
262
+ var bar = root.host.statusBar
263
+ if (bar.dragFeedbackOwner === root
264
+ && bar.activities.some(function (entry) { return entry.transfer.running })) root.say("")
265
+ }
266
+ }
267
+ }