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,706 @@
1
+ import QtQuick
2
+ import Quickshell.Io
3
+ import "." as Flea
4
+ import "js/CodeRuns.js" as CodeRuns
5
+ import "js/Markdown.js" as Markdown
6
+ import "js/MarkdownPrepared.js" as Prepared
7
+
8
+ // Rendered and Source previews share document insets, and only images beside the document can load.
9
+ Item {
10
+ id: root
11
+
12
+ property bool active: false
13
+ property string path: ""
14
+ property real size: 0
15
+ readonly property alias viewport: body
16
+ property bool externalScrollBars: false
17
+ // "rendered" or "source"; anything else reads as rendered. Only the Quick Look's r asks for source.
18
+ property string view: Markdown.RENDERED
19
+
20
+ // FileView reads whole files, so refuse above maxBytes, with remote storage keeping its smaller 256 KiB gate.
21
+ property int maxBytes: 1048576
22
+ // Retained for its callers; over-limit rows are refused, never truncated, so it reads nothing.
23
+ property bool truncate: false
24
+ readonly property bool tooLarge: root.size > root.maxBytes
25
+ property bool readFailed: false
26
+ // Quick Look only: the one file whose first read may block the key (a page-cache read beats a lost frame), named by the open.
27
+ property string blockPath: ""
28
+ // Reads that blocked the key for the file blockPath named, so a suite tells them from an async read.
29
+ property int blockedReads: 0
30
+ // Quick Look only: a small document's blocks are taken from, and kept in, the one parsed entry a resting cursor prepares.
31
+ property bool shareParse: false
32
+ // Parses a request took from the shared entry instead of running, so a suite can tell a reuse from a parse.
33
+ property int reusedParses: 0
34
+
35
+ // Set by each load, never a binding on file.text(): the blocking first read loaded inside such a binding and re-entered it.
36
+ property string loadedText: ""
37
+ readonly property string rawText: root.loadedText
38
+ function hexOf(c) {
39
+ return Prepared.hexOf(c)
40
+ }
41
+ // The host supplies the code surface colour for its page.
42
+ property color codeSurface: Theme.color.surface
43
+ readonly property string borderHex: hexOf(Theme.color.mutedFrame)
44
+ readonly property string chromeHex: hexOf(root.codeSurface)
45
+ // The render suite reads the ink it asserts beside the border, same assembly, no coercion.
46
+ readonly property string inkHex: hexOf(Theme.color.foreground)
47
+ readonly property string accentHex: hexOf(Theme.color.accent)
48
+ readonly property string mutedHex: hexOf(Theme.color.muted)
49
+ readonly property string surfaceHex: hexOf(Theme.color.surface)
50
+ readonly property int insetX: Theme.spacing.rowPaddingX + Theme.spacing.rowPaddingY - Theme.spacing.hairline
51
+ readonly property int insetY: Theme.spacing.gap + Theme.spacing.rowPaddingY
52
+ // RenderedPreviews resolves its pixels at body 14, and each one follows the body from there: no spacing token equals them at any size.
53
+ readonly property int boardBody: 14
54
+ readonly property int boardBlockGap: 6
55
+ readonly property int boardFencePadX: 12
56
+ readonly property int boardFencePadY: 8
57
+ // Headings are 20 and 15 px at body 14 in both surfaces, over the body the surface sets; deeper levels are body bold.
58
+ readonly property var boardHeadings: [20, 15]
59
+ function boardPx(px) {
60
+ return Math.round(px * Theme.font.body / root.boardBody)
61
+ }
62
+ readonly property int blockGap: root.boardPx(root.boardBlockGap)
63
+ readonly property int fencePadX: root.boardPx(root.boardFencePadX)
64
+ readonly property int fencePadY: root.boardPx(root.boardFencePadY)
65
+ // The preview column sets the document one token under Quick Look's body (RenderedPreviews 13 against 14).
66
+ property bool compact: false
67
+ readonly property int bodyPx: root.compact ? Theme.font.bodySmall : Theme.font.body
68
+ // Code colours use this backend; Quick Look and the column name their requests separately.
69
+ property var backend: null
70
+ // The backend ask slot of this pane: the column is compact, and Quick Look keeps its own.
71
+ property string slot: root.compact ? "column" : "look"
72
+ // w wraps Source lines the way the code view wraps them.
73
+ property bool wrap: false
74
+ readonly property var inks: CodeRuns.inks(Theme.palette, Theme.fallbackColor, String(Theme.color.foreground))
75
+ // Pending fence replies and a sequence for unique slots within this pane.
76
+ property int fenceWaits: 0
77
+ property int fenceSeq: 0
78
+ property var fenceReplies: ({})
79
+ function fenceSlot(fence) {
80
+ root.fenceSeq += 1
81
+ var slot = root.slot + ":fence:" + root.fenceSeq
82
+ root.fenceReplies[slot] = fence
83
+ return slot
84
+ }
85
+ function fenceWaiting(delta) {
86
+ root.fenceWaits = Math.max(0, root.fenceWaits + delta)
87
+ }
88
+ Connections {
89
+ target: root.backend
90
+ function onCodeResult(message) {
91
+ for (var slot in root.fenceReplies) root.fenceReplies[slot].answered(message)
92
+ }
93
+ }
94
+ // The faint rule wash the Quick Look bar's bottom hairline draws, shared by the table's header and row rules.
95
+ readonly property real ruleOpacity: 0.12
96
+ function headingPx(level) {
97
+ return level >= 1 && level <= root.boardHeadings.length ? root.boardPx(root.boardHeadings[level - 1]) : root.bodyPx
98
+ }
99
+ // A document past the nesting limit parses to one sentinel block, and the pane then shows its source behind a notice.
100
+ readonly property bool tooDeep: root.blockList.length === 1 && root.blockList[0].type === "deep"
101
+ readonly property string shownView: root.tooDeep ? Markdown.SOURCE : root.view
102
+ readonly property Item noticeItem: sourceList.noticeItem
103
+ readonly property Item sourceItem: sourceList.list
104
+ // Only the active file in Rendered view may request figures.
105
+ readonly property bool figuresArmed: root.active && root.shownView !== Markdown.SOURCE
106
+ // The theme the web page's diagrams are drawn under (src/figure.js): an unplaced figure reads the
107
+ // body font's advance tables, so each label is sized from the font that draws it.
108
+ Loader {
109
+ id: themeProbe
110
+ active: root.useWeb
111
+ sourceComponent: Flea.MarkdownFigure {
112
+ visible: false
113
+ askArmed: false
114
+ kind: "mermaid"
115
+ display: true
116
+ bgHex: root.hexOf(Theme.color.background)
117
+ fgHex: root.inkHex
118
+ accentHex: root.accentHex
119
+ mutedHex: root.mutedHex
120
+ surfaceHex: root.surfaceHex
121
+ fontFamily: Theme.font.family
122
+ bodyPx: root.bodyPx
123
+ }
124
+ // A page asked before the probe existed carried no advance tables; it is asked again with them.
125
+ onLoaded: root.askWeb()
126
+ }
127
+ // Parse sequence numbers reject replies for an older file.
128
+ property var blockList: []
129
+ property int parseSeq: 0
130
+ property int appliedSeq: 0
131
+ property bool parsing: false
132
+ // Parses that really ran (worker message or synchronous), so a suite counts one per event.
133
+ property int parseRuns: 0
134
+ // Loads of the shown file that landed, so a suite can tell a save the watcher read from one it never saw.
135
+ property int loadRuns: 0
136
+ // A worker reply landed for this file; the lazy suite asserts the parse left the UI thread.
137
+ property bool parsedOffThread: false
138
+ property string parseError: ""
139
+ // Worker liveness for the quicklook-firstframe watchdog line: last seq of each message kind (-1 none), beats, fallback fires.
140
+ property int ackSeq: -1
141
+ property int beatSeq: -1
142
+ property int beatCount: 0
143
+ property int headSeq: -1
144
+ property int replySeq: -1
145
+ property int fallbackFires: 0
146
+ readonly property bool fallbackRunning: parseFallback.running
147
+ // Give the worker ten seconds to answer before the synchronous recovery parse.
148
+ readonly property int parseFallbackMs: 10000
149
+ // Keep this many pixels of blocks warm beyond the visible ListView window.
150
+ readonly property int blockCachePixels: 600
151
+ readonly property bool blocksReady: root.appliedSeq === root.parseSeq && !root.parsing
152
+ readonly property bool sourceRefused: root.shownView === Markdown.SOURCE && sourceList.refused
153
+ readonly property bool loading: root.active && !root.sourceRefused && !root.readFailed && !root.tooLarge && root.parseError === ""
154
+ && (!file.loaded || !root.blocksReady || root.coloursWaiting)
155
+ readonly property string status: {
156
+ if (root.sourceRefused) return sourceList.status
157
+ if (root.tooLarge) return "This file is too large to preview."
158
+ if (root.readFailed || root.parseError !== "") return "This file could not be read."
159
+ return root.blocksReady && file.loaded && !root.coloursWaiting ? "ready" : "loading"
160
+ }
161
+ readonly property string lineLabel: Markdown.countLine(Markdown.lineCount(root.rawText))
162
+ // The bar's line count reads only once there is a file behind it, never "0 lines" first.
163
+ readonly property bool contentReady: file.loaded && root.blocksReady
164
+ && !root.sourceRefused && !root.tooLarge && !root.readFailed && root.parseError === ""
165
+ // Visible Source rows and fences hold the first screen until their colours arrive.
166
+ readonly property bool sourceWaiting: root.shownView === Markdown.SOURCE && !sourceList.firstScreen
167
+ readonly property bool coloursWaiting: root.fenceWaits > 0 || root.sourceWaiting
168
+ // A first screen is drawn: blocks are in the list, the head of a parse still running or a whole list, and the swap counts either as shown.
169
+ readonly property bool firstScreen: root.sourceRefused || (file.loaded && root.parseError === "" && root.blockList.length > 0 && !root.coloursWaiting)
170
+ // True when the reader has settled with nothing to put in the frame, the way PreviewLines.blank reads.
171
+ readonly property bool blank: root.tooLarge
172
+ || (root.active && !root.readFailed && file.loaded && root.rawText.length === 0)
173
+
174
+ // What the Source text holds, so a suite sees that Rendered lays none out.
175
+ readonly property int sourceChars: sourceList.laidChars
176
+ readonly property Item bodyItem: body
177
+ // The route a sideways stroke takes to a table wider than the document, which each such table joins.
178
+ readonly property Item tableWheel: tableRoute
179
+ // The first live table drawn wider than the document, which Quick Look's IPC reads for the sideways capture.
180
+ function tableScroller() { return tableRoute.scrollers.filter(function (s) { return s })[0] || null }
181
+ // The offset the wheel moved, in whichever view shows, for Quick Look's IPC.
182
+ readonly property real scrollY: root.shownView === Markdown.SOURCE ? (root.sourceItem !== null ? root.sourceItem.contentY : 0) : body.contentY
183
+ // The render suite reads live delegate geometry; only visible blocks plus the cache exist, so offscreen blocks answer null.
184
+ function blockItem(i) {
185
+ var kids = body.contentItem.children
186
+ for (var k = 0; k < kids.length; k++) {
187
+ if (kids[k].blockIndex === i)
188
+ return kids[k]
189
+ }
190
+ return null
191
+ }
192
+ // Instantiated delegates only: the lazy suite asserts this stays bounded.
193
+ function delegateCount() { return body.contentItem.children.length }
194
+ // An absent figure delegate answers null to the render probe.
195
+ function figureInfo(i) {
196
+ var d = blockItem(i)
197
+ if (!d)
198
+ return null
199
+ var box = null
200
+ var kids = d.children
201
+ for (var k = 0; k < kids.length; k++)
202
+ if (kids[k].objectName === "figureBox" && kids[k].visible)
203
+ box = kids[k]
204
+ if (!box)
205
+ return null
206
+ var fig = null
207
+ var inner = box.children
208
+ for (var m = 0; m < inner.length; m++)
209
+ if (inner[m].objectName === "figureItem")
210
+ fig = inner[m]
211
+ if (!fig)
212
+ return null
213
+ return { ready: fig.ready, failed: fig.failed, working: fig.working, boxW: box.width,
214
+ imgW: fig.fitWidth, imgH: fig.fitHeight }
215
+ }
216
+ readonly property real flickContentHeight: sourceList.visible
217
+ ? (root.sourceItem !== null ? root.sourceItem.contentHeight : 0) : body.contentHeight
218
+
219
+ visible: root.active
220
+
221
+ FileView {
222
+ id: file
223
+ printErrors: false
224
+ // The one watcher is on the shown file; a path change re-points it, so an old file never reloads here.
225
+ watchChanges: true
226
+ // The first event opens the window and the rest land inside it: a restart would starve a file written without pause.
227
+ onFileChanged: if (!reloadCoalesce.running) reloadCoalesce.start()
228
+ onLoaded: {
229
+ root.loadedText = file.text()
230
+ // Only the first read of a path blocks; a reload after a save never does, whatever the file grew to.
231
+ file.blockLoading = false
232
+ root.loadRuns++
233
+ // A save that unlinks and recreates the file can fail one reload; the next good load clears it.
234
+ root.readFailed = false
235
+ root.askParse()
236
+ root.askWeb()
237
+ }
238
+ onLoadFailed: {
239
+ root.loadedText = ""
240
+ file.blockLoading = false
241
+ root.keepScroll = false
242
+ root.readFailed = true
243
+ }
244
+ onPathChanged: root.readFailed = false
245
+ }
246
+
247
+ // One step moves the path and the blocking flag together, since a read inside the path change takes the flag as it stands then.
248
+ function pointFile() {
249
+ var want = (root.active && !root.tooLarge) ? root.path : ""
250
+ // A pane built before the path moved sees the last file's path, which an open that named its file must not read first.
251
+ if ((want === file.path) || (want !== "" && root.blockPath !== "" && want !== root.blockPath))
252
+ return
253
+ var blocking = want !== "" && want === root.blockPath
254
+ file.blockLoading = blocking
255
+ if (blocking) {
256
+ root.blockedReads++
257
+ // A view that holds another file takes a new path asynchronously, so the blocking read starts from none.
258
+ file.path = ""
259
+ }
260
+ file.path = want
261
+ // blockLoading gates only text(), so the blocking first read is taken here, in the first frame, not inside a binding.
262
+ if (blocking) root.loadedText = file.text()
263
+ else if (want === "") root.loadedText = ""
264
+ }
265
+ Component.onCompleted: root.pointFile()
266
+ // The list builds its cache asynchronously, so a teardown mid-build drops the model first and no incubation outlives its context.
267
+ Component.onDestruction: body.model = null
268
+ onTooLargeChanged: root.pointFile()
269
+
270
+ // One reload per save: an editor's truncate, write and rename raise events within a few ms, and 50 ms is under what a reader notices.
271
+ readonly property int reloadCoalesceMs: 50
272
+ Timer {
273
+ id: reloadCoalesce
274
+ interval: root.reloadCoalesceMs
275
+ repeat: false
276
+ onTriggered: root.reloadFromDisk()
277
+ }
278
+
279
+ // A reparse of the shown file (disk edit, theme change) puts the reader back where they were, by block or else by pixels.
280
+ property real savedY: 0
281
+ // The place by block: the block at the view's top and the pixels past its top edge, -1 when none is built there.
282
+ property int savedIndex: -1
283
+ property real savedOffset: 0
284
+ property bool keepScroll: false
285
+ // Where a restore left the view when the content was too short for the saved place, NaN when none waits.
286
+ property real heldY: NaN
287
+ // Two positions this close are the same place: the list stores contentY as a float.
288
+ readonly property real samePlacePx: 1
289
+ // True while the model is replaced: the list moves itself to its top then, and restoreScroll puts the view back.
290
+ property bool settingBlocks: false
291
+ // A place waiting for taller content ends the moment the reader moves the view away from it.
292
+ function releaseHeldPlace() {
293
+ if (root.settingBlocks || isNaN(root.heldY) || Math.abs(body.contentY - root.heldY) < root.samePlacePx)
294
+ return
295
+ root.keepScroll = false
296
+ root.heldY = NaN
297
+ }
298
+ // A place waiting for taller content is kept; otherwise the reader's present place is saved (each landing asks first).
299
+ function rememberScroll() {
300
+ if (root.keepScroll)
301
+ return
302
+ root.savedY = body.contentY
303
+ root.keepScroll = true
304
+ // A path change or a failed load ends a hold without clearing its place, so a new place starts with none.
305
+ root.heldY = NaN
306
+ var item = root.topBlockItem()
307
+ root.savedIndex = item === null ? -1 : item.blockIndex
308
+ root.savedOffset = item === null ? 0 : body.contentY - item.y
309
+ }
310
+ // The first built block that ends below the view's top, so a view in a gap or above the first block still has one.
311
+ function topBlockItem() {
312
+ var kids = body.contentItem.children
313
+ var found = null
314
+ for (var k = 0; k < kids.length; k++) {
315
+ var kid = kids[k]
316
+ if (kid.blockIndex === undefined || kid.y + kid.height <= body.contentY)
317
+ continue
318
+ if (found === null || kid.blockIndex < found.blockIndex)
319
+ found = kid
320
+ }
321
+ return found
322
+ }
323
+ // A place by block is restored by block: the list estimates an unbuilt document, and only a built end may clamp it.
324
+ function restoreScroll() {
325
+ if (!root.keepScroll)
326
+ return
327
+ var byBlock = root.savedIndex >= 0 && root.savedIndex < root.blockList.length
328
+ if (byBlock)
329
+ body.positionViewAtIndex(root.savedIndex, ListView.Beginning)
330
+ var item = byBlock ? root.blockItem(root.savedIndex) : null
331
+ var top = body.originY - body.topMargin
332
+ var end = Math.max(top, body.originY + body.contentHeight - body.height + body.bottomMargin)
333
+ var place = item === null ? root.savedY : item.y + root.savedOffset
334
+ var endExact = item === null || root.blockItem(root.blockList.length - 1) !== null
335
+ var at = Math.max(top, endExact ? Math.min(place, end) : place)
336
+ var settled = !endExact || place <= end
337
+ // The hold is recorded before the move, so the move itself reads as the same place.
338
+ root.heldY = settled ? NaN : at
339
+ body.contentY = at
340
+ if (settled)
341
+ root.keepScroll = false
342
+ }
343
+ // Where the list's end last was, origin and height together: a layout that re-estimates the unbuilt blocks moves the two apart.
344
+ property real seenEnd: 0
345
+ // Whether the last block had a delegate at the last end change or move, so the end then was drawn, not estimated.
346
+ property bool endBuilt: false
347
+ // The snap below sets contentY itself, and the end can settle again under it.
348
+ property bool snappingToEnd: false
349
+ function noteEnd() {
350
+ root.endBuilt = root.blockItem(root.blockList.length - 1) !== null
351
+ }
352
+ // A picture decodes after its block was built at no height; only a reader at the drawn end follows it, never one in unbuilt blocks.
353
+ function holdEnd() {
354
+ var was = root.seenEnd
355
+ var builtBefore = root.endBuilt
356
+ root.seenEnd = body.originY + body.contentHeight
357
+ root.noteEnd()
358
+ if (root.snappingToEnd || !builtBefore || !(root.seenEnd > was))
359
+ return
360
+ var wasEnd = was - body.height + body.bottomMargin
361
+ var tallerThanView = wasEnd > body.originY - body.topMargin
362
+ if (!tallerThanView || body.contentY < wasEnd - root.samePlacePx)
363
+ return
364
+ root.snappingToEnd = true
365
+ body.contentY = root.seenEnd - body.height + body.bottomMargin
366
+ root.snappingToEnd = false
367
+ }
368
+ // How far the last block's bottom and the inset under it lie past the viewport, 0 when whole, -1 when the last block is not built.
369
+ function endGap() {
370
+ var last = root.blockItem(root.blockList.length - 1)
371
+ return last === null ? -1 : Math.max(0, Math.round(last.mapToItem(body, 0, last.height).y + body.bottomMargin - body.height))
372
+ }
373
+ function reloadFromDisk() {
374
+ if (!root.active || root.tooLarge)
375
+ return
376
+ file.reload()
377
+ }
378
+ // Link ink and code chrome are parsed into the runs, so a theme change reparses the loaded file.
379
+ function reparseForTheme() {
380
+ if (!root.active || !file.loaded)
381
+ return
382
+ root.askWeb()
383
+ root.askParse()
384
+ }
385
+ // A theme switch moves ink and chrome in one turn, so both ask through one callLater and the parse sees both.
386
+ onInkHexChanged: Qt.callLater(root.reparseForTheme)
387
+ onChromeHexChanged: Qt.callLater(root.reparseForTheme)
388
+
389
+ // Large files require synchronous worker activation before their parse request.
390
+ readonly property int workerThreshold: 65536
391
+ Loader {
392
+ id: parserLoader
393
+ active: false
394
+ sourceComponent: parserComponent
395
+ }
396
+ Component {
397
+ id: parserComponent
398
+ CountedWorker {
399
+ source: "MarkdownWorker.js"
400
+ onMessage: function (messageObject) { root.landed(messageObject) }
401
+ }
402
+ }
403
+ function landed(messageObject) {
404
+ if (messageObject.seq !== root.parseSeq)
405
+ return
406
+ // A live worker proves it with messages, so the fallback below fires only after 10 s of none, never for slowness.
407
+ if (messageObject.yielded === true) {
408
+ // One slice a message: only the live request asks for the next, so a newer request is read first.
409
+ parseFallback.restart()
410
+ parserLoader.item.sendMessage({ seq: messageObject.seq, cont: true })
411
+ return
412
+ }
413
+ if (messageObject.ack === true || messageObject.progress === true) {
414
+ if (messageObject.ack === true) root.ackSeq = messageObject.seq
415
+ else { root.beatSeq = messageObject.seq; root.beatCount++ }
416
+ parseFallback.restart()
417
+ return
418
+ }
419
+ // The head of a first parse draws the first screen; the parse goes on, and the whole list replaces it when it lands.
420
+ if (messageObject.partial === true) {
421
+ root.headSeq = messageObject.seq
422
+ parseFallback.restart()
423
+ root.settingBlocks = true
424
+ root.blockList = messageObject.blocks
425
+ root.settingBlocks = false
426
+ return
427
+ }
428
+ root.parsing = false
429
+ root.appliedSeq = messageObject.seq
430
+ root.replySeq = messageObject.seq
431
+ if (messageObject.error !== "") {
432
+ root.parseError = messageObject.error
433
+ root.askedAny = false
434
+ return
435
+ }
436
+ root.parseError = ""
437
+ // The reader's present place is taken before the model reset moves the list to its top; a place that waits is kept.
438
+ root.rememberScroll()
439
+ root.settingBlocks = true
440
+ root.blockList = messageObject.blocks
441
+ root.settingBlocks = false
442
+ // A reply that changed nothing the list sees raises no model change, so the saved place is released here.
443
+ root.restoreScroll()
444
+ root.parsedOffThread = true
445
+ }
446
+
447
+ // The worker parses only above workerThreshold; this timer recovers a worker request whose reply never settles.
448
+ Timer {
449
+ id: parseFallback
450
+ interval: root.parseFallbackMs
451
+ repeat: false
452
+ onTriggered: {
453
+ if (!root.parsing)
454
+ return
455
+ root.fallbackFires++
456
+ // A late worker reply for the request being recovered is voided by the new number.
457
+ root.parseSeq++
458
+ root.releaseWorker()
459
+ root.parseNow(root.askedText, root.askedDir, root.askedChrome, root.askedInk)
460
+ }
461
+ }
462
+
463
+ // The last parsed request's text, folder, chrome and ink; a reload's second trigger (onRawTextChanged after onLoaded) is skipped.
464
+ property string askedText: ""
465
+ property string askedDir: ""
466
+ property string askedChrome: ""
467
+ property string askedInk: ""
468
+ property bool askedAny: false
469
+ function releaseWorker() {
470
+ // The worker holds a live request's parse between slices; a cancel frees it.
471
+ if (parserLoader.item !== null)
472
+ parserLoader.item.sendMessage({ seq: root.parseSeq, cancel: true })
473
+ }
474
+ // Forget the last request and void any worker reply in flight, so the next ask always parses.
475
+ function dropParse() {
476
+ root.parseSeq++
477
+ root.releaseWorker()
478
+ root.parsing = false
479
+ root.askedAny = false
480
+ root.askedText = ""
481
+ }
482
+
483
+ // The synchronous parse of one request, landed like a worker reply: the small-file path and the worker's recovery both end here.
484
+ function parseNow(text, dir, chrome, ink, deep) {
485
+ var blocks = root.shareParse && !deep ? Prepared.take(root.path, text, dir, chrome, ink) : null
486
+ if (blocks !== null)
487
+ root.reusedParses++
488
+ try {
489
+ if (blocks === null)
490
+ blocks = deep ? Markdown.deepBlocks() : Markdown.blocks(text, dir, chrome, ink)
491
+ } catch (e) {
492
+ root.parseError = String(e.message || e)
493
+ root.appliedSeq = root.parseSeq
494
+ root.parsing = false
495
+ root.askedAny = false
496
+ return
497
+ }
498
+ if (root.shareParse && !deep)
499
+ Prepared.store(root.path, text, dir, chrome, ink, blocks)
500
+ // Taken once the parse is good and before the model reset, like the worker landing: a parse that throws takes no place.
501
+ root.rememberScroll()
502
+ root.settingBlocks = true
503
+ root.blockList = blocks
504
+ root.settingBlocks = false
505
+ root.parseError = ""
506
+ root.appliedSeq = root.parseSeq
507
+ root.parsing = false
508
+ root.restoreScroll()
509
+ }
510
+
511
+ // Resolve images before Qt sees text: remote images become placeholders; only files beside the document load.
512
+ function askParse() {
513
+ if (!root.active || root.tooLarge || !file.loaded) {
514
+ root.parseError = ""
515
+ root.dropParse()
516
+ return
517
+ }
518
+ // Read the file's own text: onLoaded can run before the rawText binding has caught up.
519
+ var text = file.text()
520
+ var dir = Markdown.dirOf(root.path)
521
+ if (root.askedAny && text === root.askedText && dir === root.askedDir
522
+ && root.chromeHex === root.askedChrome && root.inkHex === root.askedInk) {
523
+ return
524
+ }
525
+ // Only a request that goes on to parse clears an error; a skipped one has nothing to replace it with.
526
+ root.parseError = ""
527
+ root.askedAny = true
528
+ root.askedText = text
529
+ root.askedDir = dir
530
+ root.askedChrome = root.chromeHex
531
+ root.askedInk = root.inkHex
532
+ root.parseSeq++
533
+ root.parseRuns++
534
+ root.parsing = true
535
+ // The live text length decides worker activation before bindings update; a big text with a deep head lands the sentinel unparsed.
536
+ var big = text.length > root.workerThreshold
537
+ var deep = big && Markdown.deepHead(text).deep
538
+ var wantWorker = big && !deep
539
+ parserLoader.active = wantWorker
540
+ var w = parserLoader.item
541
+ if (wantWorker && w) {
542
+ parseFallback.restart()
543
+ // Only a file with nothing drawn yet sends a head: a reparse keeps the list and the reader's place until the whole one lands.
544
+ w.sendMessage({ seq: root.parseSeq, source: text, dir: dir, chrome: root.chromeHex, ink: root.inkHex, head: root.blockList.length === 0 ? Markdown.HEAD_BLOCKS : 0 })
545
+ return
546
+ }
547
+ parserLoader.active = false
548
+ parseFallback.stop()
549
+ root.parseNow(text, dir, root.chromeHex, root.inkHex, deep)
550
+ }
551
+
552
+ onRawTextChanged: root.askParse()
553
+ onActiveChanged: {
554
+ root.pointFile()
555
+ root.askParse()
556
+ }
557
+ onPathChanged: {
558
+ // A save's pending reload belongs to the old file.
559
+ reloadCoalesce.stop()
560
+ root.keepScroll = false
561
+ root.parseError = ""
562
+ root.blockList = []
563
+ tableRoute.reset()
564
+ root.parsedOffThread = false
565
+ // The new file's load asks; asking now would parse the old file's text under the new path.
566
+ root.dropParse()
567
+ root.pointFile()
568
+ }
569
+
570
+ // Warming both view heights prevents a contentHeight binding loop on Rendered/Source changes.
571
+ onViewChanged: {
572
+ body.contentHeight
573
+ if (root.sourceItem !== null)
574
+ root.sourceItem.contentHeight
575
+ }
576
+
577
+ // Only the shown Source (a too-deep one too) asks for lines, a screen at a time: an unseen 1 MiB Source held the UI thread 100 ms.
578
+ Flea.MarkdownSource {
579
+ id: sourceList
580
+ anchors.fill: parent
581
+ visible: root.shownView === Markdown.SOURCE && (root.parseError === "" || root.sourceRefused)
582
+ text: root.shownView === Markdown.SOURCE ? root.rawText : ""
583
+ path: root.path
584
+ truncate: root.truncate
585
+ backend: root.backend
586
+ slot: root.slot
587
+ active: root.active && root.shownView === Markdown.SOURCE
588
+ && (file.loaded || root.tooLarge || root.readFailed || (sourceList.engine && !root.truncate))
589
+ tooLarge: root.tooLarge
590
+ readFailed: root.readFailed
591
+ wrap: root.wrap
592
+ notice: root.tooDeep ? Markdown.deepNotice() : ""
593
+ insetX: root.insetX
594
+ insetY: root.insetY
595
+ blockGap: root.blockGap
596
+ bodyPx: root.bodyPx
597
+ }
598
+
599
+ // Where the runtime has a web view, the rendered view is one sandboxed page the backend builds
600
+ // (src/markdown-html.js): formulas are set, and scrolling is the browser's. The block list
601
+ // below stays the view everywhere else, and for the source view.
602
+ readonly property bool useWeb: web.available && !root.tooLarge && !root.readFailed
603
+ && root.shownView === Markdown.RENDERED
604
+ property int webToken: 0
605
+ function askWeb() {
606
+ if (!root.useWeb || !root.active || root.path === "" || root.backend === null
607
+ || typeof root.backend.askMarkdownHtml !== "function")
608
+ return
609
+ var figure = themeProbe.item ? themeProbe.item.hexTheme() : null
610
+ root.webToken = root.backend.askMarkdownHtml("markdown-" + root.slot, root.path, {
611
+ foreground: root.inkHex, background: root.hexOf(Theme.color.background), muted: root.mutedHex,
612
+ surface: root.surfaceHex, accent: root.accentHex, bodyPx: root.bodyPx,
613
+ font: Theme.font.family,
614
+ advances: figure ? figure.advances : [], boldAdvances: figure ? figure.boldAdvances : []
615
+ })
616
+ }
617
+ onUseWebChanged: root.askWeb()
618
+ Connections {
619
+ target: root.backend
620
+ ignoreUnknownSignals: true
621
+ function onMarkdownHtmlResult(message) {
622
+ if (message.token === root.webToken && message.state === "ready")
623
+ web.html = message.html
624
+ }
625
+ }
626
+ Flea.WebPreview {
627
+ id: web
628
+ anchors.fill: parent
629
+ visible: root.useWeb
630
+ // A link opens outside, under the same gate as the block view's links.
631
+ onLinkActivated: function (url) {
632
+ if (Markdown.isExternalLink(url))
633
+ Qt.openUrlExternally(url)
634
+ }
635
+ }
636
+
637
+ // Render only visible blocks and a bounded cache, even for a 1 MiB document.
638
+ ListView {
639
+ id: body
640
+ anchors.fill: parent
641
+ anchors.leftMargin: root.insetX
642
+ anchors.rightMargin: root.insetX
643
+ clip: true
644
+ visible: (!root.tooLarge && !root.readFailed && root.parseError === "")
645
+ && root.shownView !== Markdown.SOURCE && !root.useWeb
646
+ // The web page draws the document then, so no block delegates are built behind it.
647
+ model: root.useWeb ? [] : root.blockList
648
+ // A new model resets the view to its origin, so the saved place is restored once that reset is done.
649
+ onModelChanged: root.restoreScroll()
650
+ onContentYChanged: { root.releaseHeldPlace(); root.noteEnd() }
651
+ onContentHeightChanged: root.holdEnd()
652
+ onOriginYChanged: root.holdEnd()
653
+ spacing: root.blockGap
654
+ topMargin: root.insetY
655
+ bottomMargin: root.insetY
656
+ cacheBuffer: root.blockCachePixels
657
+ focus: false
658
+
659
+ // The wheel and the bar belong to the frame, not to the inset list, so both sit on the root.
660
+ FastScrollHandler {
661
+ parent: root
662
+ flickable: body
663
+ visible: body.visible
664
+ }
665
+
666
+ Flea.ViewportScrollBar {
667
+ parent: root
668
+ visible: !root.externalScrollBars && overflow && body.visible
669
+ anchors { top: parent.top; right: parent.right }
670
+ flickable: body
671
+ }
672
+
673
+ // A sideways stroke over a table wider than the document goes to the table, above the handler that scrolls the document.
674
+ Flea.MarkdownTableWheel {
675
+ id: tableRoute
676
+ parent: root
677
+ visible: body.visible
678
+ }
679
+
680
+ delegate: Flea.MarkdownBlockView {
681
+ block: modelData
682
+ blockIndex: index
683
+ blockCount: root.blockList.length
684
+ siblings: root.blockList
685
+ preview: root
686
+ width: ListView.view.width
687
+ // Only a figure intersecting the viewport may send a render request.
688
+ inView: {
689
+ var v = ListView.view
690
+ if (!v)
691
+ return true
692
+ return (y + height >= v.contentY) && (y <= v.contentY + v.height)
693
+ }
694
+ }
695
+ }
696
+
697
+ Text {
698
+ anchors.centerIn: parent
699
+ visible: false // Failed previews remain blank.
700
+ text: root.status
701
+ color: Theme.color.muted
702
+ font.family: Theme.font.family
703
+ font.pixelSize: Theme.font.body
704
+ textFormat: Text.PlainText
705
+ }
706
+ }