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,482 @@
1
+ // Bound: the page Components below read this file's root and document, and are only built by its own Loaders and Repeater.
2
+ pragma ComponentBehavior: Bound
3
+
4
+ import QtQuick
5
+ import QtQuick.Pdf
6
+ import qs.Commons
7
+ import "js/Format.js" as Format
8
+ import "js/PreviewSettle.js" as PreviewSettle
9
+ import "js/PreviewSwap.js" as PreviewSwap
10
+ import "js/Swap.js" as Swap
11
+
12
+ // A PDF in the preview column's frame, or a page of it in Quick Look. QtPdf ships inside the already
13
+ // installed qt6-webengine and needs no package of its own; proven to import and render under
14
+ // Quickshell itself on this box, not only under qml6.
15
+ Item {
16
+ id: root
17
+
18
+ property string path: ""
19
+ property bool active: false
20
+ // The backend a slow document is fetched through, null where no fetch runs.
21
+ property var backend: null
22
+ // Which viewer this is, so the backend's copy cleanup never touches the other's.
23
+ property string viewerSlot: ""
24
+ // True on a storage class that can hang: the document loads from the fetched copy.
25
+ property bool fetchFirst: false
26
+ // The fetch in flight, the local copy it handed back, and what its failure says.
27
+ property int fetchId: 0
28
+ property string localCopy: ""
29
+ property string copyError: ""
30
+ // Decided once per opened document, so a later class change neither blanks nor reloads it.
31
+ property bool fetchedCopy: false
32
+ // The backend's flag for a fetch that never answered; any other copy failure could not be read.
33
+ property bool copyStalled: false
34
+ readonly property string failSentence: root.copyStalled ? "This file is not responding."
35
+ : "This file could not be read."
36
+ // Which page the frame is showing, zero-based, always inside the document it belongs to; the page under the frame's top when continuous.
37
+ property int page: 0
38
+ // The Flickable the page scrolls in, so the loading mark stands in the visible frame at any zoom.
39
+ property Item viewport: null
40
+ // The column's mode: every page at this item's width, one gap apart, inside the viewport; Quick Look shows one page.
41
+ property bool continuous: false
42
+
43
+ // What the facts table shows as Pages; 0 until the document is ready, or if it never becomes so.
44
+ readonly property int pageCount: doc.status === PdfDocument.Ready ? doc.pageCount : 0
45
+ // A render error fails the column only before its first draw and on the page under the frame's top; every other page error leaves only its own slot empty.
46
+ property bool pageFailed: false
47
+ readonly property bool failed: doc.status === PdfDocument.Error || root.copyError.length > 0 || root.pageFailed
48
+
49
+ // The page whose render is on screen, -1 until one has landed for this document.
50
+ property int shownPage: -1
51
+ // A render slower than the listing swap's cap stops passing the old page off as the one asked for.
52
+ property bool fellBack: false
53
+ // When the loading state went up, 0 while none stands; the page that replaces it waits behind it for Swap.MIN_SHOW_MS.
54
+ property double fellBackAt: 0
55
+ // What the chrome counts: the page on screen, or the one asked for once nothing else stands in for it.
56
+ readonly property int drawnPage: root.shownPage >= 0 && !root.fellBack ? root.shownPage : root.page
57
+
58
+ // The page's proportions come from the document, never from the rendered image. Reading the
59
+ // image's implicit size closed a loop through the width binding below: sourceSize changed the
60
+ // implicit size, which re-evaluated width, which re-set sourceSize. Qt broke that binding, and a
61
+ // broken width binding is a page that never resizes when the frame or the zoom changes.
62
+ // corner: a document mixing page shapes draws the old page in the new page's box until its render lands.
63
+ readonly property real pageAspect: {
64
+ if (root.continuous || doc.status !== PdfDocument.Ready || root.pageCount <= 0)
65
+ return 1
66
+ var box = doc.pagePointSize(Math.min(root.page, root.pageCount - 1))
67
+ return box.height > 0 ? box.width / box.height : 1
68
+ }
69
+
70
+ // Each page's height over its width, read from the document once when it becomes ready; the sizes read are counted for the tests.
71
+ property var pageRatios: []
72
+ property int sizeReads: 0
73
+ Connections {
74
+ target: doc
75
+ function onStatusChanged() {
76
+ var ratios = []
77
+ for (var i = 0; root.continuous && doc.status === PdfDocument.Ready && i < doc.pageCount; i++) {
78
+ var box = doc.pagePointSize(i)
79
+ ratios.push(box.width > 0 ? box.height / box.width : 0)
80
+ }
81
+ root.sizeReads += ratios.length
82
+ root.pageRatios = ratios
83
+ }
84
+ }
85
+ // Each page's top and height at this item's width, so the stack is as tall as it will be before any page renders.
86
+ readonly property var layout: {
87
+ var tops = [], heights = [], y = 0
88
+ for (var i = 0; root.width > 0 && i < root.pageRatios.length; i++) {
89
+ tops.push(y)
90
+ heights.push(Math.round(root.width * root.pageRatios[i]))
91
+ y += heights[i] + Theme.spacing.gap
92
+ }
93
+ return { tops: tops, heights: heights, height: Math.max(0, y - Theme.spacing.gap) }
94
+ }
95
+ readonly property real stackHeight: root.layout.height
96
+ readonly property real viewTop: root.viewport ? root.viewport.contentY : 0
97
+ readonly property real viewHeight: root.viewport ? root.viewport.height : 0
98
+ // Pages within half a frame of the frame hold an image; the others hold none.
99
+ readonly property int nearFirst: root.pageAt(root.viewTop - root.viewHeight / 2)
100
+ readonly property int nearLast: root.pageAt(root.viewTop + root.viewHeight * 1.5)
101
+ // The Qt render width in device pixels, held through small resizes so a drag does not render every pixel.
102
+ property int renderWidth: 0
103
+ onWidthChanged: root.fitRenderWidth()
104
+ onLayoutChanged: { root.pageFromOffset(); root.syncWindow() }
105
+ onNearFirstChanged: root.syncWindow()
106
+ onNearLastChanged: root.syncWindow()
107
+ onViewTopChanged: root.pageFromOffset()
108
+
109
+ // The page whose band, its height and the gap after it, holds the offset.
110
+ function pageAt(y) {
111
+ var tops = root.layout.tops
112
+ var lo = 0, hi = tops.length - 1
113
+ while (lo < hi) {
114
+ var mid = (lo + hi + 1) >> 1
115
+ if (tops[mid] <= y) lo = mid
116
+ else hi = mid - 1
117
+ }
118
+ return Math.max(0, lo)
119
+ }
120
+ function pageSlot(index) {
121
+ for (var i = 0; i < pages.count; i++)
122
+ if (pages.itemAt(i).pageNo === index) return pages.itemAt(i)
123
+ return null
124
+ }
125
+ function landedPages() {
126
+ var out = []
127
+ for (var i = 0; i < pages.count; i++)
128
+ if (pages.itemAt(i).landed) out.push(pages.itemAt(i).pageNo)
129
+ return out
130
+ }
131
+ // The slots are the pages of the near window and any page still rendering; a slot is added or dropped without moving the others.
132
+ function syncWindow() {
133
+ var last = Math.min(root.nearLast, root.layout.tops.length - 1)
134
+ var have = {}
135
+ for (var i = windowPages.count - 1; i >= 0; i--) {
136
+ var no = windowPages.get(i).pageNo
137
+ var busy = pages.itemAt(i) !== null && pages.itemAt(i).rendering
138
+ if (no > root.layout.tops.length - 1 || ((no < root.nearFirst || no > last) && !busy))
139
+ windowPages.remove(i)
140
+ else
141
+ have[no] = true
142
+ }
143
+ for (var n = root.nearFirst; n <= last; n++)
144
+ if (!have[n]) windowPages.append({ pageNo: n })
145
+ }
146
+ function pageFromOffset() { if (root.continuous && root.pageCount > 0) root.page = root.pageAt(root.viewTop) }
147
+ function fitRenderWidth() {
148
+ var target = Math.round(root.width * Screen.devicePixelRatio)
149
+ var ratio = root.renderWidth > 0 ? target / root.renderWidth : 0
150
+ if (target > 0 && (ratio > 1.1 || ratio < 0.9))
151
+ root.renderWidth = target
152
+ }
153
+ // The first frame holds when every page it shows has landed; later pages are never waited on, so a scroll never blanks the column.
154
+ function firstDrawn() {
155
+ if (!root.continuous || root.shownPage >= 0 || root.pageCount <= 0)
156
+ return
157
+ for (var i = root.pageAt(root.viewTop); i <= root.pageAt(root.viewTop + root.viewHeight); i++) {
158
+ var slot = root.pageSlot(i)
159
+ if (!slot || !slot.settled)
160
+ return
161
+ }
162
+ root.shownPage = root.pageAt(root.viewTop)
163
+ }
164
+ function renderBusy() {
165
+ if (!root.continuous)
166
+ return root.pageItem !== null && root.pageItem.status === Image.Loading
167
+ for (var i = 0; i < pages.count; i++)
168
+ if (pages.itemAt(i).rendering)
169
+ return true
170
+ return false
171
+ }
172
+
173
+ // A new document starts at its first page; an idle press opens on the next frame, a walk or a render in flight waits for the settle.
174
+ onPathChanged: { root.page = 0; root.requestOpen() }
175
+ onOpenedChanged: root.fetchForOpened()
176
+
177
+ // A hangable document is fetched once per opening; anything else loads in place.
178
+ function fetchForOpened() {
179
+ root.localCopy = ""
180
+ root.copyError = ""
181
+ root.copyStalled = false
182
+ root.fetchedCopy = root.opened.length > 0 && root.fetchFirst === true && root.backend !== null
183
+ if (!root.fetchedCopy)
184
+ return
185
+ root.fetchId = root.backend.pdfCopy(root.opened, root.viewerSlot)
186
+ }
187
+
188
+ Connections {
189
+ target: root.backend
190
+ function onPdfCopied(id, path, err, stalled) {
191
+ if (id !== root.fetchId || root.fetchedCopy !== true)
192
+ return
193
+ if (err.length > 0) {
194
+ root.copyError = err
195
+ root.copyStalled = stalled
196
+ } else {
197
+ root.localCopy = path
198
+ }
199
+ }
200
+ }
201
+
202
+ // The copy, once it lands; in place while no fetch runs, and nothing while one is out.
203
+ function docSource() {
204
+ if (root.opened.length === 0)
205
+ return ""
206
+ if (root.fetchedCopy !== true)
207
+ return Format.fileUri(root.opened)
208
+ return root.localCopy.length > 0 ? Format.fileUri(root.localCopy) : ""
209
+ }
210
+ onPageCountChanged: if (root.pageCount > 0) root.page = Math.min(root.page, root.pageCount - 1)
211
+ // A turn arms the cap for a page other than the one on screen; with no page shown yet it does nothing, and turning back onto the shown page stops the cap instead.
212
+ onPageChanged: {
213
+ if (!root.pageItem || root.pageItem.status !== Image.Loading) return
214
+ if (root.shownPage < 0) return
215
+ if (root.page !== root.shownPage) renderCap.start()
216
+ else { renderCap.stop(); root.endFallBack() }
217
+ }
218
+
219
+ // The loading state leaves once it has stood Swap.MIN_SHOW_MS; until then the page waits behind it.
220
+ function endFallBack() {
221
+ var left = root.fellBack ? Swap.showLeft(root.fellBackAt, Date.now()) : 0
222
+ if (left > 0) {
223
+ renderMin.interval = left
224
+ renderMin.restart()
225
+ return
226
+ }
227
+ renderMin.stop()
228
+ root.fellBack = false
229
+ root.fellBackAt = 0
230
+ }
231
+
232
+ function turn(delta) {
233
+ if (root.pageCount <= 0)
234
+ return
235
+ var next = Math.max(0, Math.min(root.pageCount - 1, root.page + delta))
236
+ if (!root.continuous)
237
+ root.page = next
238
+ else if (root.viewport)
239
+ root.viewport.contentY = Math.max(0, Math.min(root.layout.tops[next], root.viewport.contentHeight - root.viewport.height))
240
+ }
241
+
242
+ // Loading a page other than the one on screen starts the cap; a landed or failed render ends it.
243
+ function renderChanged() {
244
+ var page = root.pageItem
245
+ if (page.status === Image.Loading) {
246
+ if (root.shownPage >= 0 && page.currentFrame !== root.shownPage) {
247
+ renderMin.stop()
248
+ renderCap.restart()
249
+ }
250
+ return
251
+ }
252
+ renderCap.stop()
253
+ root.endFallBack()
254
+ // A document switch reports Ready for loads that drew nothing, or that drew the last file.
255
+ var landed = page.status === Image.Ready && page.implicitWidth > 0
256
+ && page.source.toString() === doc.source.toString()
257
+ root.shownPage = landed ? page.currentFrame : -1
258
+ }
259
+
260
+ // Issue 117, vianney-g: a source change while an async page render is in flight can destroy the
261
+ // carrier device under Qt's own reader thread, which aborts the process, so a walk through a
262
+ // folder of PDFs settles before a document is opened rather than opening one per cursor step.
263
+ property string opened: ""
264
+ readonly property int settleMs: PreviewSwap.DOCUMENT_SETTLE_MS
265
+ // The last path or active change, so an isolated press opens at once and a walk still settles.
266
+ property double lastRequestAt: 0
267
+ onActiveChanged: root.requestOpen()
268
+
269
+ // One opener for the timer and the idle fast path, so both switch the same way.
270
+ function openPending() {
271
+ var next = root.active ? root.path : ""
272
+ if (next === root.opened)
273
+ return
274
+ // Another document's page is never this one's: it is dropped, and kept by no render, before the switch.
275
+ renderCap.stop()
276
+ renderMin.stop()
277
+ root.fellBack = false
278
+ root.fellBackAt = 0
279
+ root.shownPage = -1
280
+ root.pageFailed = false
281
+ root.opened = next
282
+ if (root.continuous && root.viewport)
283
+ root.viewport.contentY = 0
284
+ }
285
+
286
+ // An idle press opens on the next presented frame, so that frame shows the page Quick Look has; the settle is the backstop.
287
+ property bool fastArmed: false
288
+ // Opens taken by the fast path, so a test tells them from the settle by count, never by time.
289
+ property int fastOpens: 0
290
+ function runFast() {
291
+ if (!root.fastArmed)
292
+ return
293
+ root.fastArmed = false
294
+ pdfSettle.stop()
295
+ root.fastOpens += 1
296
+ root.openPending()
297
+ }
298
+ Connections {
299
+ target: root.fastArmed ? root.Window.window : null
300
+ function onFrameSwapped() { root.runFast() }
301
+ }
302
+
303
+ // An idle press with no render in flight opens at once, and a change that opens nothing stamps nothing.
304
+ function requestOpen() {
305
+ if ((root.active ? root.path : "") === root.opened)
306
+ return
307
+ var now = Date.now()
308
+ root.fastArmed = PreviewSettle.pdfOpenNow(now, root.lastRequestAt, root.settleMs, root.renderBusy())
309
+ root.lastRequestAt = now
310
+ pdfSettle.restart()
311
+ }
312
+ Timer {
313
+ id: pdfSettle
314
+ interval: root.settleMs
315
+ onTriggered: { root.fastArmed = false; root.openPending() }
316
+ }
317
+
318
+ // The cap draws the loading state by 100 ms, as the listing swap's does, and it then stands Swap.MIN_SHOW_MS.
319
+ Timer {
320
+ id: renderCap
321
+ objectName: "renderCap"
322
+ interval: Swap.CAP_MS
323
+ onTriggered: {
324
+ if (!root.fellBack)
325
+ root.fellBackAt = Date.now()
326
+ root.fellBack = true
327
+ }
328
+ }
329
+
330
+ // The loading state has stood its minimum, so the page that landed meanwhile shows now.
331
+ Timer {
332
+ id: renderMin
333
+ objectName: "renderMin"
334
+ onTriggered: { root.fellBack = false; root.fellBackAt = 0 }
335
+ }
336
+
337
+ PdfDocument {
338
+ id: doc
339
+ // Format.fileUri, not a concatenation: a path can carry a # or a ? and either one truncates
340
+ // a hand-built URI at that character. A document is only opened while the column shows one.
341
+ source: root.docSource()
342
+ }
343
+
344
+ // Quick Look's one page: built only when this is not the column's stack, so the stack holds no image of its own besides its pages.
345
+ Loader {
346
+ id: single
347
+ anchors.fill: parent
348
+ active: !root.continuous
349
+ sourceComponent: singlePage
350
+ }
351
+ // The drawn page itself, for the geometry gate: sized to the contain of the viewport at the
352
+ // zoom, so its rect is the picture and not the frame it is centred in.
353
+ readonly property Item pageItem: single.item ? single.item.image : null
354
+
355
+ Component {
356
+ id: singlePage
357
+
358
+ Item {
359
+ readonly property Item image: page
360
+
361
+ // The page's own paper under the raster: on this box a rendered page can arrive with text drawn
362
+ // and no background, and the dark frame showed through it. Paper is the document's, not a theme
363
+ // role, so the colour is a constant; visible only with the page, so a loading document draws none.
364
+ Rectangle {
365
+ anchors.centerIn: parent
366
+ visible: page.visible
367
+ width: page.width
368
+ height: page.height
369
+ color: "#ffffff"
370
+ }
371
+
372
+ // The page is the only light surface in the app, which is exactly what the canvas draws.
373
+ PdfPageImage {
374
+ id: page
375
+ anchors.centerIn: parent
376
+ // Drawn once a render has landed: the paper alone before it was the white flash on every turn.
377
+ visible: root.shownPage >= 0 && !root.fellBack
378
+ document: doc
379
+ // Qt ignores a frame at or past frameCount, which is 0 until the first render lands, so the binding reads it to reapply a turn made during that render.
380
+ currentFrame: page.frameCount > 0 ? Math.min(root.page, page.frameCount - 1) : 0
381
+ fillMode: Image.PreserveAspectFit
382
+ asynchronous: true
383
+ // Qt 6.8's double buffer: the page on screen stays until the next render is ready, where
384
+ // without it Qt dropped the old pixmap at the turn and the paper showed white for the render.
385
+ // Off until this document has a page on screen, so no render can keep another document's.
386
+ retainWhileLoading: root.shownPage >= 0
387
+ onStatusChanged: root.renderChanged()
388
+ // Fit inside the frame without ever upscaling past the page's own resolution.
389
+ width: Math.min(parent.width, parent.height * root.pageAspect)
390
+ height: Math.min(parent.height, parent.width / root.pageAspect)
391
+
392
+ // Rasterised at the size it is drawn at, so a zoomed page sharpens instead of scaling up a
393
+ // page-point-sized bitmap; the deadband and the assignment (not a binding) are both Qt's
394
+ // own PdfPageView.qml reRenderIfNecessary, without which a resize re-renders every frame.
395
+ onWidthChanged: page.rerenderIfNeeded()
396
+ // PdfPageImage copies the document's source into its own inherited source property only as
397
+ // the document turns Ready, and a re-render that beats that copy makes Qt warn that the two
398
+ // are in conflict; the guard below waits for the copy and this re-renders once it lands.
399
+ onSourceChanged: page.rerenderIfNeeded()
400
+
401
+ function rerenderIfNeeded() {
402
+ // source is a url, so it is converted before comparing: a bare === against a
403
+ // string is false forever and would stop this guard from ever firing. Opening the
404
+ // next document leaves the last one's source here until the copy, and the page box
405
+ // resizing meanwhile (pageAspect is 1 while it loads) was that warning, twice a switch.
406
+ if (page.source.toString() === "" || page.source.toString() !== doc.source.toString())
407
+ return
408
+ var target = Math.round(page.width)
409
+ if (target <= 0)
410
+ return
411
+ var ratio = page.sourceSize.width > 0 ? target / page.sourceSize.width : 0
412
+ if (ratio > 1.1 || ratio < 0.9)
413
+ page.sourceSize = Qt.size(target, 0)
414
+ }
415
+ }
416
+ }
417
+ }
418
+
419
+ // The continuous stack: a slot per page of the near window, each holding an image while its page is near the frame or still rendering.
420
+ ListModel { id: windowPages }
421
+ Repeater {
422
+ id: pages
423
+ model: windowPages
424
+ delegate: Item {
425
+ id: slot
426
+ required property int pageNo
427
+ readonly property bool near: slot.pageNo >= root.nearFirst && slot.pageNo <= root.nearLast
428
+ readonly property bool landed: pageLoader.item !== null && pageLoader.item.landed
429
+ // A failed render counts as settled, so the first frame is never held for a page that will not come.
430
+ property bool errored: false
431
+ readonly property bool settled: slot.landed || slot.errored
432
+ // A page that left the frame keeps its image until the render in flight lands, so no render is cut off by its own page going away.
433
+ property bool rendering: false
434
+ y: root.layout.tops[slot.pageNo] || 0
435
+ width: root.width
436
+ height: root.layout.heights[slot.pageNo] || 0
437
+ onSettledChanged: root.firstDrawn()
438
+ onRenderingChanged: if (!slot.rendering) Qt.callLater(root.syncWindow)
439
+
440
+ function imageStatus(status) {
441
+ var drawn = root.shownPage >= 0
442
+ slot.rendering = status === Image.Loading
443
+ slot.errored = status === Image.Error
444
+ if (slot.errored && !drawn && slot.pageNo === root.pageAt(root.viewTop))
445
+ root.pageFailed = true
446
+ }
447
+
448
+ Loader {
449
+ id: pageLoader
450
+ anchors.fill: parent
451
+ active: slot.near || slot.rendering
452
+ sourceComponent: Item {
453
+ readonly property bool landed: image.status === Image.Ready && image.implicitWidth > 0
454
+ Rectangle {
455
+ anchors.fill: parent
456
+ visible: image.status === Image.Ready
457
+ color: "#ffffff"
458
+ }
459
+ PdfPageImage {
460
+ id: image
461
+ anchors.fill: parent
462
+ document: doc
463
+ currentFrame: slot.pageNo
464
+ fillMode: Image.Stretch
465
+ asynchronous: true
466
+ retainWhileLoading: true
467
+ sourceSize: Qt.size(root.renderWidth, 0)
468
+ onStatusChanged: slot.imageStatus(image.status)
469
+ }
470
+ }
471
+ }
472
+ }
473
+ }
474
+
475
+ // Past the cap the loading mark takes the frame, the fallback ui/PaneSwap.qml gives a slow listing.
476
+ LoadingState {
477
+ parent: root.viewport ? root.viewport : root
478
+ anchors.fill: parent
479
+ visible: root.fellBack
480
+ heldOff: true
481
+ }
482
+ }
@@ -0,0 +1,201 @@
1
+ import QtQuick
2
+ import "." as Flea
3
+ import "js/Scroll.js" as Scroll
4
+
5
+ // The column's scroll route and bounded session memory; no position is written to settings.
6
+ Item {
7
+ id: root
8
+ objectName: "previewScroll"
9
+ required property var column
10
+ parent: root.column.frameItem
11
+ anchors.fill: parent
12
+
13
+ readonly property var code: root.html && root.html.sourceView ? root.html.sourceCode : root.column.linesItem.code
14
+ readonly property var markdown: root.column.isMarkdownRow ? root.column.markdown : null
15
+ readonly property var html: root.column.isHtmlRow ? root.column.markdown : null
16
+ readonly property var viewport: root.column.linesItem.visible ? root.column.linesItem.viewport
17
+ : root.markdown ? root.markdown.viewport : root.html ? root.html.viewport : null
18
+ readonly property string subject: root.column.row && root.column.path.length > 0
19
+ ? JSON.stringify([root.column.path, root.column.row.s, root.column.row.m]) : ""
20
+ readonly property bool contentReady: root.html ? root.html.contentReady : root.markdown ? root.markdown.contentReady : !!root.code && root.code.settled
21
+ readonly property bool loading: root.scrollPending || (root.column.isDocumentRow ? !root.column.markdown || root.column.markdown.loading || (root.contentReady && root.pendingRestore)
22
+ : root.column.linesItem.loading || (!!root.code && root.code.settled && (!root.code.firstScreen || root.pendingRestore))
23
+ )
24
+ property var places: ({})
25
+ property var order: []
26
+ readonly property int capacity: 256
27
+ property string identity: ""
28
+ property string filePath: ""
29
+ property var wanted: ({ y: 0 })
30
+ property bool pendingRestore: false
31
+ property bool applying: false
32
+ property string scrollKey: ""
33
+ readonly property bool scrollPending: root.scrollKey.length > 0 && !!root.column.swap
34
+ && root.column.swap.capturing && root.column.swap.key === root.scrollKey
35
+ property real drawnY: 0
36
+
37
+ function store(path, fingerprint, place) {
38
+ var at = root.order.indexOf(path)
39
+ if (at >= 0) root.order.splice(at, 1)
40
+ root.order.push(path)
41
+ root.places[path] = { fingerprint: fingerprint, place: place }
42
+ while (root.order.length > root.capacity)
43
+ delete root.places[root.order.shift()]
44
+ }
45
+
46
+ function remember() {
47
+ var v = root.viewport
48
+ if (!v || root.applying || root.pendingRestore || !root.contentReady
49
+ || !root.identity || !root.column.row || root.column.path !== root.filePath
50
+ || root.identity !== JSON.stringify([root.column.path, root.column.row.s, root.column.row.m]))
51
+ return
52
+ var place = { y: v.contentY - Scroll.limitsY(v).min }
53
+ if (root.markdown) {
54
+ var item = root.markdown.topBlockItem()
55
+ place.index = item ? item.blockIndex : -1
56
+ place.offset = item ? v.contentY - item.y : 0
57
+ }
58
+ root.store(root.filePath, root.identity, place)
59
+ }
60
+
61
+ function follow() {
62
+ root.pendingRestore = root.subject.length > 0
63
+ root.scrollKey = ""
64
+ root.identity = root.subject
65
+ root.filePath = root.identity ? root.column.path : ""
66
+ var saved = root.places[root.filePath]
67
+ root.wanted = saved && saved.fingerprint === root.identity ? saved.place : { y: 0 }
68
+ Qt.callLater(root.restore)
69
+ }
70
+
71
+ // Layout and the saved place land before the preview hold releases its first live frame.
72
+ function restore() {
73
+ var v = root.viewport
74
+ if (!root.pendingRestore || root.applying || !v || !root.contentReady)
75
+ return
76
+ root.applying = true
77
+ v.forceLayout()
78
+ if (root.markdown && root.wanted.index !== undefined) {
79
+ root.markdown.savedY = Scroll.limitsY(v).min + root.wanted.y
80
+ root.markdown.savedIndex = root.wanted.index
81
+ root.markdown.savedOffset = root.wanted.offset
82
+ root.markdown.keepScroll = true
83
+ root.markdown.restoreScroll()
84
+ } else {
85
+ var bounds = Scroll.limitsY(v)
86
+ v.currentIndex = -1
87
+ v.contentY = Math.max(bounds.min, Math.min(bounds.max, bounds.min + root.wanted.y))
88
+ }
89
+ v.forceLayout()
90
+ if (!root.markdown && !(root.html && !root.html.sourceView) && (!root.code || !root.code.firstScreen)) {
91
+ root.applying = false
92
+ return
93
+ }
94
+ root.pendingRestore = false
95
+ root.applying = false
96
+ root.drawnY = v.contentY
97
+ root.remember()
98
+ }
99
+
100
+ // A cold code window moves under the existing preview hold, so no frame draws missing lines.
101
+ function moved() {
102
+ var v = root.viewport
103
+ if (!v || root.applying || root.pendingRestore || !root.contentReady)
104
+ return
105
+ root.applying = true
106
+ v.forceLayout()
107
+ root.applying = false
108
+ if (!root.markdown && !(root.html && !root.html.sourceView) && root.code && !root.code.firstScreen && root.column.swap
109
+ && !root.column.swap.holding && !root.column.swap.capturing) {
110
+ var target = v.contentY
111
+ var identity = root.identity
112
+ var key = "scroll:" + identity
113
+ root.scrollKey = key
114
+ root.applying = true
115
+ v.contentY = root.drawnY
116
+ root.applying = false
117
+ root.column.swap.hold(function () {
118
+ if (root.scrollKey !== key) return
119
+ if (root.identity === identity) {
120
+ root.applying = true
121
+ v.contentY = target
122
+ v.forceLayout()
123
+ root.applying = false
124
+ root.remember()
125
+ root.column.swap.start(false)
126
+ }
127
+ root.scrollKey = ""
128
+ }, key, true)
129
+ return
130
+ }
131
+ if (root.markdown || (root.html && !root.html.sourceView) || (root.code && root.code.firstScreen))
132
+ root.drawnY = v.contentY
133
+ root.remember()
134
+ }
135
+
136
+ function pageBy(direction) {
137
+ var v = root.viewport
138
+ if (!v || !root.column.visible || root.column.overlayOpen || root.column.manualHold
139
+ || root.column.pending || root.column.selectionCount > 1 || root.pendingRestore || root.scrollPending)
140
+ return
141
+ var bounds = Scroll.limitsY(v)
142
+ v.cancelFlick()
143
+ Scroll.stopTail(v)
144
+ v.contentY = Math.max(bounds.min, Math.min(bounds.max, v.contentY + direction * v.height))
145
+ v.forceLayout()
146
+ root.remember()
147
+ }
148
+
149
+ function scrollPage(direction, viewport) {
150
+ viewport.contentY = Math.max(0, Math.min(viewport.contentHeight - viewport.height,
151
+ viewport.contentY + direction * Flea.Theme.rowHeight))
152
+ }
153
+
154
+ onSubjectChanged: root.follow()
155
+ onContentReadyChanged: Qt.callLater(root.restore)
156
+ onViewportChanged: {
157
+ if (root.viewport) bars.targetView = root.viewport
158
+ Qt.callLater(root.restore)
159
+ }
160
+ Connections {
161
+ target: root.code
162
+ function onFirstScreenChanged() {
163
+ if (root.pendingRestore) Qt.callLater(root.restore)
164
+ if (root.code.firstScreen && root.viewport && !root.pendingRestore)
165
+ root.drawnY = root.viewport.contentY
166
+ else
167
+ root.moved()
168
+ }
169
+ }
170
+ Connections {
171
+ target: root.viewport
172
+ // ListView has count; the rendered page's Flickable supplies only geometry and scrolling signals.
173
+ ignoreUnknownSignals: true
174
+ function onCountChanged() { Qt.callLater(root.restore) }
175
+ function onContentHeightChanged() { Qt.callLater(root.restore) }
176
+ function onOriginYChanged() { Qt.callLater(root.restore) }
177
+ function onContentYChanged() { root.moved() }
178
+ }
179
+ Flea.FastScrollHandler {
180
+ id: wheel
181
+ flickable: root.html ? root.viewport : root.column.linesItem.viewport
182
+ visible: (root.html !== null || root.column.linesItem.visible) && flickable !== null
183
+ onWheel: function (event) {
184
+ if (root.scrollPending) {
185
+ event.accepted = true
186
+ return
187
+ }
188
+ // This handler replaces FastScrollHandler.onWheel; explicitly run
189
+ // its shared scrolling logic once the preview handoff is complete.
190
+ wheel.wheeled()
191
+ wheel.handleWheel(event)
192
+ }
193
+ }
194
+ Loader {
195
+ id: bars
196
+ anchors.fill: parent
197
+ property var targetView: null
198
+ active: root.viewport !== null && targetView !== null
199
+ sourceComponent: Flea.ViewportScrollBars { flickable: bars.targetView }
200
+ }
201
+ }