buninu-browser 0.3.0 → 0.5.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 (296) hide show
  1. package/LICENSES/jsmdcui.txt +22 -0
  2. package/README.md +40 -4
  3. package/frontends/wayland/LICENSE-land +21 -0
  4. package/frontends/wayland/README.md +152 -0
  5. package/frontends/wayland/casty/LICENSE +21 -0
  6. package/frontends/wayland/casty/bookmarks.js +32 -0
  7. package/frontends/wayland/casty/click.js +96 -0
  8. package/frontends/wayland/casty/commands.js +61 -0
  9. package/frontends/wayland/casty/config.js +45 -0
  10. package/frontends/wayland/casty/download-directory.js +28 -0
  11. package/frontends/wayland/casty/download.js +49 -0
  12. package/frontends/wayland/casty/hints.js +334 -0
  13. package/frontends/wayland/casty/keys.js +56 -0
  14. package/frontends/wayland/casty/page-commands.js +80 -0
  15. package/frontends/wayland/casty/prompt-width.js +148 -0
  16. package/frontends/wayland/casty/urlbar.js +606 -0
  17. package/frontends/wayland/client.js +88 -0
  18. package/frontends/wayland/clipboard.js +80 -0
  19. package/frontends/wayland/index.js +130 -0
  20. package/frontends/wayland/input.js +75 -0
  21. package/frontends/wayland/protocol.js +55 -0
  22. package/frontends/wayland/protocols/wayland.xml +3248 -0
  23. package/frontends/wayland/protocols/xdg-shell.xml +1415 -0
  24. package/frontends/wayland/transport.js +120 -0
  25. package/frontends/wayland/urlbar.js +162 -0
  26. package/index.js +28 -1
  27. package/lib/audio/shared-connection.js +31 -0
  28. package/lib/cdp-server.js +15 -2
  29. package/lib/clipboard-command.js +10 -0
  30. package/lib/clipboard-powershell.js +22 -0
  31. package/lib/clipboard.js +37 -2
  32. package/lib/document-types.js +11 -0
  33. package/lib/headless-shell.js +54 -12
  34. package/lib/layout/text-layout.js +17 -9
  35. package/lib/paint/display-list.js +6 -1
  36. package/lib/qml/README.md +1427 -0
  37. package/lib/qml/builtins.js +771 -0
  38. package/lib/qml/canvas-dom.js +115 -0
  39. package/lib/qml/canvas.js +76 -0
  40. package/lib/qml/document.js +1199 -0
  41. package/lib/qml/fonts.js +69 -0
  42. package/lib/qml/format.js +9 -0
  43. package/lib/qml/host.js +352 -0
  44. package/lib/qml/images.js +199 -0
  45. package/lib/qml/launcher.js +132 -0
  46. package/lib/qml/loader.js +521 -0
  47. package/lib/qml/media-renderer.js +49 -0
  48. package/lib/qml/media.js +86 -0
  49. package/lib/qml/models.js +622 -0
  50. package/lib/qml/parser.js +381 -0
  51. package/lib/qml/qs +5 -0
  52. package/lib/qml/qscasty +3 -0
  53. package/lib/qml/qsheadless +3 -0
  54. package/lib/qml/qswayland +3 -0
  55. package/lib/qml/reactive.js +182 -0
  56. package/lib/qml/runtime.js +3603 -0
  57. package/lib/qml/shader/fragment.js +83 -0
  58. package/lib/qml/shader/qsb.js +27 -0
  59. package/lib/qml/shader/renderer.js +39 -0
  60. package/lib/qml/shader/runtime.js +61 -0
  61. package/lib/qml/shapes.js +251 -0
  62. package/lib/qml/shortcuts.js +65 -0
  63. package/lib/qml/text.js +109 -0
  64. package/lib/qml/textures.js +126 -0
  65. package/lib/qml/wayland-clipboard.js +32 -0
  66. package/lib/qml/window-constraints.js +16 -0
  67. package/lib/qml/worker-thread.js +70 -0
  68. package/lib/render-tree/index.js +16 -1
  69. package/lib/renderer/bindings.js +16 -1
  70. package/lib/renderer/canvas-2d.js +29 -0
  71. package/lib/renderer/document-selection.js +19 -0
  72. package/lib/renderer/host.js +45 -17
  73. package/lib/renderer/main.js +21 -6
  74. package/lib/renderer/page-realm.js +20 -10
  75. package/lib/renderer/page-renderer.js +497 -42
  76. package/lib/renderer/pointer-move.js +5 -3
  77. package/lib/renderer/render-interval.js +9 -0
  78. package/lib/renderer/text-input-paint.js +54 -0
  79. package/lib/renderer/text-input.js +57 -0
  80. package/lib/style/computed-style.js +10 -4
  81. package/package.json +1 -1
  82. package/test/audio-connection-lifetime.test.js +46 -0
  83. package/test/canvas-paint.test.js +8 -0
  84. package/test/clipboard.test.js +19 -0
  85. package/test/computed-style.test.js +12 -0
  86. package/test/document-selection.test.js +27 -0
  87. package/test/double-click.test.js +85 -0
  88. package/test/fixtures/canvas-damage.js +40 -0
  89. package/test/fixtures/fake-renderer.js +1 -0
  90. package/test/fixtures/flea-helpers/Dockerfile +36 -0
  91. package/test/fixtures/flea-helpers/bat.mjs +43 -0
  92. package/test/fixtures/flea-helpers/bin/bat +3 -0
  93. package/test/fixtures/flea-helpers/bin/bwrap +10 -0
  94. package/test/fixtures/flea-helpers/bin/bwrap-real-filter +9 -0
  95. package/test/fixtures/flea-helpers/bin/glow +3 -0
  96. package/test/fixtures/flea-helpers/bin/qs +3 -0
  97. package/test/fixtures/flea-helpers/bin/unsupported +3 -0
  98. package/test/fixtures/flea-helpers/check-highlighting.mjs +47 -0
  99. package/test/fixtures/flea-helpers/check-media.mjs +29 -0
  100. package/test/fixtures/flea-helpers/check-preview.mjs +27 -0
  101. package/test/fixtures/flea-helpers/check.mjs +36 -0
  102. package/test/fixtures/flea-helpers/highlight-cases.json +227 -0
  103. package/test/fixtures/flea-helpers/source-mime.xml +28 -0
  104. package/test/fixtures/flea-helpers/syntax-compat.mjs +23 -0
  105. package/test/fixtures/flea-thumbnail-stub/flea-placeholder.thumbnailer +4 -0
  106. package/test/fixtures/flea-thumbnail-stub/flea-thumbnail-stub +3 -0
  107. package/test/fixtures/omastorm-helpers/Dockerfile +35 -0
  108. package/test/fixtures/omastorm-helpers/check.mjs +56 -0
  109. package/test/fixtures/omastorm-helpers/launch.mjs +24 -0
  110. package/test/fixtures/omastorm-helpers/prepare.mjs +17 -0
  111. package/test/fixtures/omastorm-helpers/runtime.mjs +32 -0
  112. package/test/fixtures/pointer-paint-batching.js +23 -0
  113. package/test/fixtures/qml/flea/EscapeHint.qml +11 -0
  114. package/test/fixtures/qml/flea/LICENSE +21 -0
  115. package/test/fixtures/qml/flea/README.md +25 -0
  116. package/test/fixtures/qml/flea/Theme.qml +8 -0
  117. package/test/fixtures/qml/flea/demo.qml +24 -0
  118. package/test/fixtures/qml/flea/qmldir +3 -0
  119. package/test/fixtures/qml/flea-theme/LICENSE-Flea +21 -0
  120. package/test/fixtures/qml/flea-theme/LICENSE-Omarchy +20 -0
  121. package/test/fixtures/qml/flea-theme/README.md +70 -0
  122. package/test/fixtures/qml/flea-theme/button.qml +17 -0
  123. package/test/fixtures/qml/flea-theme/demo.qml +27 -0
  124. package/test/fixtures/qml/flea-theme/field.qml +21 -0
  125. package/test/fixtures/qml/flea-theme/imports/qs/Commons/BorderGeometry.js +373 -0
  126. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Color.qml +258 -0
  127. package/test/fixtures/qml/flea-theme/imports/qs/Commons/I18n.qml +118 -0
  128. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Numbers.js +19 -0
  129. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Plural.js +91 -0
  130. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Style.qml +561 -0
  131. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Util.qml +188 -0
  132. package/test/fixtures/qml/flea-theme/imports/qs/Commons/qmldir +13 -0
  133. package/test/fixtures/qml/flea-theme/segment.qml +17 -0
  134. package/test/fixtures/qml/flea-theme/settings.qml +58 -0
  135. package/test/fixtures/qml/flea-theme/text-metrics.qml +10 -0
  136. package/test/fixtures/qml/flea-theme/ui/DialogButton.qml +150 -0
  137. package/test/fixtures/qml/flea-theme/ui/DialogField.qml +147 -0
  138. package/test/fixtures/qml/flea-theme/ui/EscapeHint.qml +11 -0
  139. package/test/fixtures/qml/flea-theme/ui/Glass.qml +114 -0
  140. package/test/fixtures/qml/flea-theme/ui/Glyph.qml +54 -0
  141. package/test/fixtures/qml/flea-theme/ui/GlyphSolid.qml +22 -0
  142. package/test/fixtures/qml/flea-theme/ui/Reveal.qml +54 -0
  143. package/test/fixtures/qml/flea-theme/ui/SettingsRuler.qml +85 -0
  144. package/test/fixtures/qml/flea-theme/ui/SettingsSegment.qml +85 -0
  145. package/test/fixtures/qml/flea-theme/ui/StableModel.qml +89 -0
  146. package/test/fixtures/qml/flea-theme/ui/Theme.qml +460 -0
  147. package/test/fixtures/qml/flea-theme/ui/ThemeColors.qml +43 -0
  148. package/test/fixtures/qml/flea-theme/ui/ViewState.qml +565 -0
  149. package/test/fixtures/qml/flea-theme/ui/js/Anchor.js +379 -0
  150. package/test/fixtures/qml/flea-theme/ui/js/AnchorHold.js +162 -0
  151. package/test/fixtures/qml/flea-theme/ui/js/Archive.js +157 -0
  152. package/test/fixtures/qml/flea-theme/ui/js/BackReturn.js +110 -0
  153. package/test/fixtures/qml/flea-theme/ui/js/Buttons.js +54 -0
  154. package/test/fixtures/qml/flea-theme/ui/js/ColumnMenu.js +111 -0
  155. package/test/fixtures/qml/flea-theme/ui/js/Columns.js +298 -0
  156. package/test/fixtures/qml/flea-theme/ui/js/Contrast.js +157 -0
  157. package/test/fixtures/qml/flea-theme/ui/js/Density.js +44 -0
  158. package/test/fixtures/qml/flea-theme/ui/js/DirSizes.js +129 -0
  159. package/test/fixtures/qml/flea-theme/ui/js/ExtThumbs.js +115 -0
  160. package/test/fixtures/qml/flea-theme/ui/js/Filter.js +284 -0
  161. package/test/fixtures/qml/flea-theme/ui/js/FolderSorts.js +108 -0
  162. package/test/fixtures/qml/flea-theme/ui/js/Format.js +305 -0
  163. package/test/fixtures/qml/flea-theme/ui/js/Icons.js +206 -0
  164. package/test/fixtures/qml/flea-theme/ui/js/Input.js +39 -0
  165. package/test/fixtures/qml/flea-theme/ui/js/KeyName.js +64 -0
  166. package/test/fixtures/qml/flea-theme/ui/js/Keymap.js +370 -0
  167. package/test/fixtures/qml/flea-theme/ui/js/Kinds.js +145 -0
  168. package/test/fixtures/qml/flea-theme/ui/js/Lists.js +11 -0
  169. package/test/fixtures/qml/flea-theme/ui/js/MakeDefault.js +186 -0
  170. package/test/fixtures/qml/flea-theme/ui/js/Match.js +27 -0
  171. package/test/fixtures/qml/flea-theme/ui/js/Material.js +109 -0
  172. package/test/fixtures/qml/flea-theme/ui/js/Menu.js +513 -0
  173. package/test/fixtures/qml/flea-theme/ui/js/Motion.js +16 -0
  174. package/test/fixtures/qml/flea-theme/ui/js/Mounts.js +320 -0
  175. package/test/fixtures/qml/flea-theme/ui/js/MouseNav.js +13 -0
  176. package/test/fixtures/qml/flea-theme/ui/js/Nav.js +328 -0
  177. package/test/fixtures/qml/flea-theme/ui/js/Palette.js +33 -0
  178. package/test/fixtures/qml/flea-theme/ui/js/Places.js +236 -0
  179. package/test/fixtures/qml/flea-theme/ui/js/Protocols.js +296 -0
  180. package/test/fixtures/qml/flea-theme/ui/js/RecentMode.js +154 -0
  181. package/test/fixtures/qml/flea-theme/ui/js/Search.js +396 -0
  182. package/test/fixtures/qml/flea-theme/ui/js/Settings.js +476 -0
  183. package/test/fixtures/qml/flea-theme/ui/js/SettingsAbout.js +99 -0
  184. package/test/fixtures/qml/flea-theme/ui/js/SettingsShelf.js +52 -0
  185. package/test/fixtures/qml/flea-theme/ui/js/Sort.js +127 -0
  186. package/test/fixtures/qml/flea-theme/ui/js/StateWords.js +42 -0
  187. package/test/fixtures/qml/flea-theme/ui/js/Swap.js +166 -0
  188. package/test/fixtures/qml/flea-theme/ui/js/TextSize.js +107 -0
  189. package/test/fixtures/qml/flea-theme/ui/js/Thumbs.js +158 -0
  190. package/test/fixtures/qml/flea-theme/ui/js/UiState.js +438 -0
  191. package/test/fixtures/qml/flea-theme/ui/js/Update.js +132 -0
  192. package/test/fixtures/qml/flea-theme/ui/js/UserDirs.js +56 -0
  193. package/test/fixtures/qml/flea-theme/ui/qmldir +227 -0
  194. package/test/fixtures/qml/hello.qml +26 -0
  195. package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabel.qml +2 -0
  196. package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabelNew.qml +2 -0
  197. package/test/fixtures/qml/imports/Demo/Toolkit/qmldir +3 -0
  198. package/test/fixtures/qml/imports/components/Badge.qml +15 -0
  199. package/test/fixtures/qml/imports/logic/base.js +2 -0
  200. package/test/fixtures/qml/imports/logic/counter.js +4 -0
  201. package/test/fixtures/qml/imports/logic/math.js +3 -0
  202. package/test/fixtures/qml/imports/logic/shared.js +3 -0
  203. package/test/fixtures/qml/imports/main.qml +21 -0
  204. package/test/fixtures/qml/imports/theme/Theme.qml +8 -0
  205. package/test/fixtures/qml/imports/theme/qmldir +2 -0
  206. package/test/fixtures/qml/multiply.qml +61 -0
  207. package/test/fixtures/qml-canvas-dom.js +90 -0
  208. package/test/fixtures/qml-fonts/main.qml +10 -0
  209. package/test/fixtures/qml-locale/check.mjs +6 -0
  210. package/test/fixtures/qml-locale/main.qml +8 -0
  211. package/test/fixtures/qml-media-audio.js +39 -0
  212. package/test/fixtures/qml-shader/grab.qml +14 -0
  213. package/test/fixtures/qml-shader/gradient.frag +5 -0
  214. package/test/fixtures/qml-shader/main.qml +14 -0
  215. package/test/fixtures/qml-shader/texture-grab.qml +25 -0
  216. package/test/fixtures/qml-shader/texture-size.frag +5 -0
  217. package/test/fixtures/qml-shader-check.mjs +24 -0
  218. package/test/fixtures/title-no-paint.js +20 -0
  219. package/test/helpers/flea-sandbox.js +48 -0
  220. package/test/helpers/quickshell-fixture.js +125 -0
  221. package/test/html-clipboard-wayland.test.js +47 -0
  222. package/test/index.test.js +13 -0
  223. package/test/manual-casty-without-qml.js +87 -0
  224. package/test/manual-double-click.html +21 -0
  225. package/test/manual-flea-application.js +49 -0
  226. package/test/manual-flea-binary.js +62 -0
  227. package/test/manual-flea-qml.js +9 -0
  228. package/test/manual-flea-settings.js +40 -0
  229. package/test/manual-flea-theme.js +40 -0
  230. package/test/manual-mouse-buttons.html +18 -0
  231. package/test/manual-omastorm-engine.mjs +19 -0
  232. package/test/manual-omastorm-shaders.mjs +28 -0
  233. package/test/manual-omastorm-software.mjs +25 -0
  234. package/test/manual-pointer-scroll.html +43 -0
  235. package/test/manual-qml-removal.js +46 -0
  236. package/test/manual-without-qml.js +45 -0
  237. package/test/pointer-drag.test.js +104 -0
  238. package/test/qml-canvas-dom.test.js +37 -0
  239. package/test/qml-canvas.test.js +21 -0
  240. package/test/qml-clipboard-bridge.test.js +25 -0
  241. package/test/qml-clipboard-wayland.test.js +40 -0
  242. package/test/qml-connections-timing.test.js +95 -0
  243. package/test/qml-controls-window.test.js +182 -0
  244. package/test/qml-dynamic.test.js +401 -0
  245. package/test/qml-flea-app.test.js +146 -0
  246. package/test/qml-flea-backend.test.js +78 -0
  247. package/test/qml-flea-columns-backend.test.js +38 -0
  248. package/test/qml-flea-compile.test.js +78 -0
  249. package/test/qml-flea-launcher.test.js +88 -0
  250. package/test/qml-flea-markdown-backend.test.js +31 -0
  251. package/test/qml-flea-media.test.js +28 -0
  252. package/test/qml-flea-navigation-backend.test.js +35 -0
  253. package/test/qml-flea-navigation-timing.test.js +147 -0
  254. package/test/qml-flea-preview-backend.test.js +53 -0
  255. package/test/qml-flea-previews.test.js +163 -0
  256. package/test/qml-flea-rows.test.js +101 -0
  257. package/test/qml-flea-textures.test.js +59 -0
  258. package/test/qml-flea-view-switch.test.js +33 -0
  259. package/test/qml-flea-wayland.test.js +68 -0
  260. package/test/qml-flea.test.js +34 -0
  261. package/test/qml-fonts.test.js +31 -0
  262. package/test/qml-grid-multiline.test.js +48 -0
  263. package/test/qml-host.test.js +238 -0
  264. package/test/qml-imports.test.js +102 -0
  265. package/test/qml-inherited-handlers.test.js +51 -0
  266. package/test/qml-input-selection.test.js +66 -0
  267. package/test/qml-interactions.test.js +309 -0
  268. package/test/qml-language.test.js +167 -0
  269. package/test/qml-launcher.test.js +54 -0
  270. package/test/qml-layout-text.test.js +205 -0
  271. package/test/qml-locale.test.js +45 -0
  272. package/test/qml-media.test.js +63 -0
  273. package/test/qml-nonvisual-visible.test.js +19 -0
  274. package/test/qml-persisted-delegates.test.js +45 -0
  275. package/test/qml-quickshell-controls.test.js +65 -0
  276. package/test/qml-quickshell-settings.test.js +62 -0
  277. package/test/qml-repeater.test.js +72 -0
  278. package/test/qml-shader-runtime.test.js +6 -0
  279. package/test/qml-shortcuts.test.js +39 -0
  280. package/test/qml-socket.test.js +60 -0
  281. package/test/qml-software-shader.test.js +55 -0
  282. package/test/qml-style-updates.test.js +124 -0
  283. package/test/qml-textures.test.js +66 -0
  284. package/test/qml-views.test.js +332 -0
  285. package/test/qml-window-update.test.js +36 -0
  286. package/test/qml-worker.test.js +69 -0
  287. package/test/qml.test.js +83 -0
  288. package/test/render-interval.test.js +33 -0
  289. package/test/renderer-host.test.js +11 -0
  290. package/test/text-input-pointer.test.js +31 -0
  291. package/test/text-input-scroll.test.js +29 -0
  292. package/test/text-input.test.js +47 -0
  293. package/test/title-no-paint.test.js +6 -0
  294. package/test/wayland-frontend.test.js +83 -0
  295. package/test/wayland-scroll.test.js +40 -0
  296. package/test/wayland-urlbar.test.js +61 -0
@@ -0,0 +1,115 @@
1
+ .pragma library
2
+
3
+ .import "Thumbs.js" as Thumbs
4
+ .import "Format.js" as Format
5
+
6
+ // The ExtThumbs board's storage-class gate: network shares and phones are off, USB
7
+ // drives are on, and anything else is local and always gets thumbnails. A class is
8
+ // src/backend/extclass.rs's own word, carried beside the fsinfo line once per
9
+ // directory change, so nothing here classifies and there is no second classifier.
10
+
11
+ // Whole sentences with "%1" for a value, filled by fill so each translates as one; one per state.
12
+ var WORDS = {
13
+ hide: "Hide %1 thumbnails",
14
+ show: "Show %1 thumbnails",
15
+ isOn: "%1 thumbnails are on",
16
+ isOff: "%1 thumbnails are off"
17
+ }
18
+
19
+ // Sample input: "network" answers "thumbNetwork", "" answers "".
20
+ function keyForClass(storageClass) {
21
+ if (storageClass === "network") return "thumbNetwork"
22
+ if (storageClass === "phone") return "thumbPhone"
23
+ if (storageClass === "usb") return "thumbUsb"
24
+ return ""
25
+ }
26
+
27
+ // The menu row is present only on network, phone or USB storage, never locally.
28
+ function present(storageClass) {
29
+ return keyForClass(storageClass) !== ""
30
+ }
31
+
32
+ // Sample input: ("usb", {}) answers true, ("network", {}) answers false.
33
+ function classOn(storageClass, preview) {
34
+ var key = keyForClass(storageClass)
35
+ if (key === "") return true
36
+ if (key === "thumbUsb") return !preview || preview.thumbUsb !== false
37
+ return !!preview && preview[key] === true
38
+ }
39
+
40
+ function classOff(storageClass, preview) {
41
+ return present(storageClass) && !classOn(storageClass, preview)
42
+ }
43
+
44
+ // Off is cache-only, not blank: a visible row still asks, and the backend answers from
45
+ // the shared cache with one stat and never starts a decoder. Thumbnails already made
46
+ // still show, because the cache answers them. Sample input: ("network", {}) answers true.
47
+ function cacheOnly(storageClass, preview) {
48
+ if (!classOff(storageClass, preview)) return false
49
+ return !preview || preview.thumbnails !== "off"
50
+ }
51
+
52
+ // The thumbed line's record: a cache-only miss on a generating class returns to unasked, any other reply is remembered.
53
+ function thumbReply(state, row, file, cap, known, storageClass, preview) {
54
+ if (file === "" && state.file[row] === Thumbs.CACHE_ASKED && (!known || !cacheOnly(storageClass, preview)))
55
+ return { state: Thumbs.miss(state, row, true, cap), missed: true }
56
+ return { state: Thumbs.remember(state, row, file, cap), missed: false }
57
+ }
58
+
59
+ // In Columns the preview is manual on a class that is off: one condition, two surfaces.
60
+ function manualHold(storageClass, preview) {
61
+ return cacheOnly(storageClass, preview)
62
+ }
63
+
64
+ // The menu row's own entrance: absent off the three classes, otherwise labelled for
65
+ // the class and its state. Sample input: ({storageClass: "usb"}) answers "Hide USB thumbnails".
66
+ function entry(e, p) {
67
+ if (!present(p.storageClass)) return false
68
+ e.label = label(p.storageClass, classOn(p.storageClass, p.thumbPreview))
69
+ return true
70
+ }
71
+
72
+ // Sample input: ("network", false) answers "Show network thumbnails".
73
+ function label(storageClass, on) {
74
+ var noun = storageClass === "usb" ? "USB" : storageClass
75
+ return Format.fill(on ? WORDS.hide : WORDS.show, noun)
76
+ }
77
+
78
+ // The status bar's own verdict for the toggle, in the board's words.
79
+ // Sample input: ("network", true) answers "Network thumbnails are on".
80
+ function statusLine(storageClass, on) {
81
+ var noun = storageClass === "usb" ? "USB"
82
+ : storageClass.charAt(0).toUpperCase() + storageClass.slice(1)
83
+ return Format.fill(on ? WORDS.isOn : WORDS.isOff, noun)
84
+ }
85
+
86
+ // A class switched on decodes again: only its cache-only marks leave the map, so a
87
+ // real refusal stays answered and is never decoded twice. Sample input:
88
+ // {file: {3: "", 4: "/c/x.png", 5: "cache-missed"}, order: [3, 4, 5]} forgets 5 alone.
89
+ function forgetMisses(state) {
90
+ var kept = []
91
+ for (var i = 0; i < state.order.length; i++) {
92
+ var at = state.order[i]
93
+ if (state.file[at] === Thumbs.CACHE_MISS || state.file[at] === Thumbs.CACHE_ASKED) delete state.file[at]
94
+ else kept.push(at)
95
+ }
96
+ state.order = kept
97
+ return { file: state.file, order: state.order }
98
+ }
99
+
100
+ // Sample input: ("network", {}) answers "only|media"; a zoom step or another class keeps it, so refreshes nothing.
101
+ function verdict(storageClass, preview) {
102
+ var mode = preview && preview.thumbnails ? preview.thumbnails : "media"
103
+ return (cacheOnly(storageClass, preview) ? "only" : "full") + "|" + mode
104
+ }
105
+
106
+ // FileView reads the whole file, so a row over the gate is refused, not truncated, the
107
+ // same rule ui/PreviewText.qml already keeps. Text previews on network and phone storage
108
+ // read at most the first 256 KiB. Sample input: ("phone") answers 262144.
109
+ var TEXT_PREVIEW_MAX = 1048576
110
+ var REMOTE_TEXT_PREVIEW_MAX = 262144
111
+
112
+ function textLimit(storageClass) {
113
+ if (storageClass === "network" || storageClass === "phone") return REMOTE_TEXT_PREVIEW_MAX
114
+ return TEXT_PREVIEW_MAX
115
+ }
@@ -0,0 +1,284 @@
1
+ .pragma library
2
+ .import "Input.js" as Input
3
+
4
+ .import "Format.js" as Format
5
+ .import "Match.js" as Match
6
+ .import "Thumbs.js" as Thumbs
7
+
8
+ // The strip's two sentences and the no-match tile's heading, each filled by Format.fill.
9
+ var WORDS = {
10
+ noMatchOne: "No match among 1 item",
11
+ noMatchMany: "No match among %1 items",
12
+ shown: "%1 of %2 shown",
13
+ shownOfFolder: "%1 of %2 shown · of %3 in this folder",
14
+ nothingMatched: "Nothing matched %1"
15
+ }
16
+
17
+ // The filter narrows the listing already on screen: no walk, no round trip, and every row it keeps
18
+ // is one the backend has already sent. ui/js/Search.js is its bigger sibling, which walks the
19
+ // subtree and replaces the listing; the design canvas draws the two as different things and this
20
+ // file never reaches ui/Backend.qml at all.
21
+ //
22
+ // Two index spaces meet here. A "listing row" is what the backend numbers, what a selection holds
23
+ // and what every write operation sends; a "view position" is where a row is drawn. With no filter
24
+ // up the two are the same number, and at() and viewOf() below are the only places that convert.
25
+
26
+ // The listing rows a query leaves standing, in the order the backend sent them, or null when there
27
+ // is no filter at all. Only the rows the pane holds can be tested, so the answer is a subsequence
28
+ // of the held window; scope() below is what says so when the window is not the whole listing.
29
+ // It is a subsequence and never a re-ranking, which is what keeps directories ahead of files.
30
+ function shown(rows, held, query) {
31
+ if (query.length === 0) {
32
+ return null
33
+ }
34
+ var out = []
35
+ for (var i = 0; i < rows.length; i++) {
36
+ // The same run ui/js/Match.js paints in the name, so a row can never match without showing why.
37
+ if (Match.run(rows[i].n, query).start >= 0) {
38
+ out.push(held + i)
39
+ }
40
+ }
41
+ return out
42
+ }
43
+
44
+ function at(list, view) {
45
+ if (list === null) {
46
+ return view
47
+ }
48
+ // A delegate can outlive the view position it was built for by a frame when the match list
49
+ // shrinks, so a position past the end answers -1: a listing row nothing holds and nothing draws.
50
+ return view < list.length ? list[view] : -1
51
+ }
52
+
53
+ // -1 for a row the filter hides, which is what the callers below clamp against.
54
+ function viewOf(list, row) {
55
+ return list === null ? row : list.indexOf(row)
56
+ }
57
+
58
+ // Issue 92, nixfred: a query matching nothing leaves the cursor on a row the filter no longer draws.
59
+ function cursorShown(pane) { return viewOf(pane.shown, pane.cursorIndex) >= 0 }
60
+
61
+ // The strip's own sentence, SearchFilter rules 1 and 2: what the filter kept, out of the rows it
62
+ // could test, and the directory those rows are a window on. The scope is the half that must not be
63
+ // dropped, because zero matches among 350 loaded rows is not zero matches in a 104,812-file
64
+ // directory; the tail goes when the window is the whole listing, which is nothing left to say.
65
+ function summary(list, loaded, total) {
66
+ if (list === null) {
67
+ return ""
68
+ }
69
+ if (total > loaded) {
70
+ return Format.fill(WORDS.shownOfFolder, Format.count(list.length), Format.count(loaded), Format.count(total))
71
+ }
72
+ return Format.fill(WORDS.shown, Format.count(list.length), Format.count(loaded))
73
+ }
74
+
75
+ // States' no-match tile: the pane names the query back and says how many rows it tested, because
76
+ // the query is up in a strip the reader may have looked away from.
77
+ function noMatch(total) {
78
+ return total === 1 ? WORDS.noMatchOne : Format.fill(WORDS.noMatchMany, Format.count(total))
79
+ }
80
+
81
+ // The rows drawn between two ends, which is not the range between them: a plain index range would
82
+ // sweep up every row the filter hid in the gap, and nothing on screen would say it had.
83
+ function between(list, a, b) {
84
+ var lo = Math.min(a, b)
85
+ var hi = Math.max(a, b)
86
+ var out = []
87
+ for (var i = 0; i < list.length; i++) {
88
+ if (list[i] >= lo && list[i] <= hi) {
89
+ out.push(list[i])
90
+ }
91
+ }
92
+ return out
93
+ }
94
+
95
+ // A request cut back to the rows still drawn. A filtered viewport covers a set and not a run, so
96
+ // the range-shaped planners hand back rows the filter hides, and asking for those would fetch work
97
+ // nothing draws.
98
+ function keep(asked, list) {
99
+ if (list === null) {
100
+ return asked
101
+ }
102
+ var drawn = {}
103
+ for (var i = 0; i < list.length; i++) {
104
+ drawn[list[i]] = true
105
+ }
106
+ var out = []
107
+ for (var j = 0; j < asked.length; j++) {
108
+ if (drawn[asked[j]] === true) {
109
+ out.push(asked[j])
110
+ }
111
+ }
112
+ return out
113
+ }
114
+
115
+ // A pending thumbnail can be hidden inside the planner's span as well as outside its viewport.
116
+ function cut(work, list, state) {
117
+ var drop = work.drop.slice()
118
+ if (list !== null) {
119
+ var drawn = {}
120
+ for (var i = 0; i < list.length; i++) drawn[list[i]] = true
121
+ var dropping = {}
122
+ for (var j = 0; j < drop.length; j++) dropping[drop[j]] = true
123
+ for (var key in state.file) {
124
+ var row = Number(key)
125
+ if (Thumbs.pending(state.file[key]) && drawn[row] !== true && dropping[row] !== true)
126
+ drop.push(row)
127
+ }
128
+ }
129
+ return { ask: keep(work.ask, list), drop: drop }
130
+ }
131
+
132
+ // The listing rows a view range covers, for the two planners that take a first and a last.
133
+ function span(list, first, last) {
134
+ if (list === null) {
135
+ return { first: first, last: last }
136
+ }
137
+ if (list.length === 0) {
138
+ return { first: 0, last: -1 }
139
+ }
140
+ var lo = Math.max(0, Math.min(list.length - 1, first))
141
+ var hi = Math.max(0, Math.min(list.length - 1, last))
142
+ return { first: list[lo], last: list[hi] }
143
+ }
144
+
145
+ // The transitions, taking ui/Pane.qml's root the way ui/js/Search.js and ui/js/Sort.js do: the pane
146
+ // holds the state, this holds what the state does.
147
+
148
+ // "/" opens the query line. Unlike the search's, nothing is committed to start a walk: the rows are
149
+ // already here, so the listing narrows on the keystroke itself.
150
+ function start(pane) {
151
+ pane.filterTyping = true
152
+ }
153
+
154
+ function typed(pane, character) {
155
+ apply(pane, pane.filterQuery + character)
156
+ }
157
+
158
+ function backspace(pane) {
159
+ apply(pane, pane.filterQuery.substring(0, pane.filterQuery.length - 1))
160
+ }
161
+
162
+ // Enter hands the keyboard back to the list with the filter standing, which is what makes y, x, d
163
+ // and r work over the narrowed rows; an empty query has nothing to stand, so it closes instead.
164
+ function commit(pane) {
165
+ if (pane.filterQuery.length === 0) {
166
+ close(pane)
167
+ return
168
+ }
169
+ pane.filterTyping = false
170
+ }
171
+
172
+ // Esc, and every fresh listing. The selection is deliberately not cleared: prune() below has already
173
+ // cut it to rows the filter was showing, so what is left is exactly what was on screen.
174
+ function close(pane) {
175
+ pane.filterQuery = ""
176
+ pane.filterTyping = false
177
+ }
178
+
179
+ // The new query is applied here rather than by writing the property alone, because the selection has
180
+ // to lose the rows that just dropped out before anything can act on rows nobody can see. The next
181
+ // match list is computed rather than read back off the pane's binding, so the order is not a guess.
182
+ function apply(pane, query) {
183
+ var next = shown(pane.rows, pane.held, query)
184
+ prune(pane, next)
185
+ if (query !== pane.filterQuery) pane.filterKeyAt = Date.now()
186
+ pane.filterQuery = query
187
+ if (query.length > 0) pane.filterNarrowedAt = Date.now()
188
+ // A cursor the filter just hid takes the first row still standing, so it is never off screen.
189
+ if (next !== null && next.length > 0 && viewOf(next, pane.cursorIndex) < 0) {
190
+ pane.cursorIndex = next[0]
191
+ pane.cursorSeq += 1
192
+ pane.showRow(0)
193
+ }
194
+ }
195
+
196
+ // A selection you cannot see is one you can act on by accident, and trash, cut and copy all take the
197
+ // selection with no confirmation at all: what the filter hides, it takes out of the selection too.
198
+ function prune(pane, list) {
199
+ if (list === null) {
200
+ return
201
+ }
202
+ var drawn = {}
203
+ for (var i = 0; i < list.length; i++) {
204
+ drawn[list[i]] = true
205
+ }
206
+ var was = pane.selectedIndices()
207
+ var dropped = 0
208
+ for (var j = 0; j < was.length; j++) {
209
+ if (drawn[was[j]] !== true) {
210
+ pane.selection.toggle(was[j])
211
+ dropped += 1
212
+ }
213
+ }
214
+ if (dropped > 0) {
215
+ pane.selectionVersion += 1
216
+ }
217
+ }
218
+
219
+ // Cursor is a listing row but moves through drawn rows, so both steps convert.
220
+ function setCursor(pane, index, context) {
221
+ setCursorView(pane, viewOf(pane.shown, index), context)
222
+ }
223
+
224
+ function setCursorView(pane, view, context) {
225
+ if (pane.shownTotal === 0) {
226
+ return
227
+ }
228
+ var to = Math.max(0, Math.min(pane.shownTotal - 1, view))
229
+ pane.cursorIndex = at(pane.shown, to)
230
+ // Every cursor setter bumps cursorSeq, so a plain move ends a Shift gesture.
231
+ pane.cursorSeq += 1
232
+ pane.showRow(to, context)
233
+ }
234
+
235
+ function moveCursor(pane, delta) {
236
+ setCursorView(pane, viewOf(pane.shown, pane.cursorIndex) + delta)
237
+ }
238
+
239
+ // The wheel moved the viewport and the cursor follows it. Both ends are view positions, because
240
+ // under a filter the listing rows they name are not a run and a numeric clamp would be meaningless.
241
+ function clampCursor(pane, first, last) {
242
+ var was = viewOf(pane.shown, pane.cursorIndex)
243
+ var to = Math.max(first, Math.min(last, was))
244
+ if (to !== was) {
245
+ var held = pulledFrom(pane)
246
+ var from = held >= 0 ? held : pane.cursorIndex
247
+ pane.cursorIndex = at(pane.shown, to)
248
+ pane.cursorSeq = (pane.cursorSeq || 0) + 1
249
+ pane.cursorPulled = { from: from, to: pane.cursorIndex, seq: pane.cursorSeq, version: pane.selectionVersion }
250
+ }
251
+ }
252
+
253
+ // The row the cursor held before the wheel pulled it, while no move, mark or new listing came since (issue 286); else -1.
254
+ function pulledFrom(pane) {
255
+ var pull = pane.cursorPulled
256
+ var current = pull && pull.seq === (pane.cursorSeq || 0) && pull.to === pane.cursorIndex
257
+ && pull.version === pane.selectionVersion
258
+ return current ? pull.from : -1
259
+ }
260
+
261
+ // The query line's own keys while it has the caret, the shape ui/js/Search.js typeKey uses for the
262
+ // search's. Enter commits, escape abandons, backspace shortens, every printable character narrows.
263
+ function typeKey(event, pane) {
264
+ // Ctrl+V and Shift+Insert type the clipboard's first line as one narrowing (issue 287).
265
+ if (Input.isPasteKey(event))
266
+ return Input.pasteInto(pane, function (text) { typed(pane, text) })
267
+ if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
268
+ commit(pane)
269
+ return true
270
+ }
271
+ if (event.key === Qt.Key_Escape) {
272
+ close(pane)
273
+ return true
274
+ }
275
+ if (event.key === Qt.Key_Backspace) {
276
+ backspace(pane)
277
+ return true
278
+ }
279
+ if (Input.isPrintable(event.text)) {
280
+ typed(pane, event.text)
281
+ return true
282
+ }
283
+ return true
284
+ }
@@ -0,0 +1,108 @@
1
+ .pragma library
2
+ .import "UserDirs.js" as UserDirs
3
+
4
+ // Owned here so Backend.qml loads no sort closure at startup.
5
+ var ORDERS = ["name", "size", "mtime", "kind"]
6
+
7
+ // Written only on a user sort in that folder, read once per listing, oldest first.
8
+
9
+ // The cap ui.json holds, oldest first; setting past it drops the folders sorted longest ago.
10
+ var MAX = 500
11
+
12
+ // Sample input: XDG_DOWNLOAD_DIR="$HOME/Inbox"; absent PICTURES and VIDEOS use their conventional names.
13
+ function newestPaths(body, home) {
14
+ var roles = ["DOWNLOAD", "PICTURES", "VIDEOS"]
15
+ var names = ["Downloads", "Pictures", "Videos"]
16
+ var paths = []
17
+ if (!home) return paths
18
+ var configured = {}
19
+ var entries = UserDirs.readRoles(body, home)
20
+ for (var e = 0; e < entries.length; e++)
21
+ configured[entries[e].role] = entries[e].path
22
+ for (var i = 0; i < roles.length; i++) {
23
+ var path = configured[roles[i]] === undefined ? home + "/" + names[i] : configured[roles[i]]
24
+ if (path && path !== home && paths.indexOf(path) < 0)
25
+ paths.push(path)
26
+ }
27
+ return paths
28
+ }
29
+
30
+ function defaultOrder(path, newest) {
31
+ var folder = String(path || "").replace(/\/+$/, "")
32
+ return newest && newest.indexOf(folder) >= 0 ? { key: "mtime", reverse: true } : null
33
+ }
34
+
35
+ // Settings stores Modified as "date" while ORDERS and the backend spell it "mtime".
36
+ function liveKey(key) {
37
+ return key === "date" ? "mtime" : key
38
+ }
39
+
40
+ // Sample input: {"/home/gm/Work": {"key": "size", "reverse": true}}.
41
+ function get(sorts, path) {
42
+ var entry = sorts ? sorts[path] : null
43
+ if (!entry || ORDERS.indexOf(liveKey(entry.key)) < 0)
44
+ return null
45
+ return { key: liveKey(entry.key), reverse: entry.reverse === true }
46
+ }
47
+
48
+ function has(sorts, path) {
49
+ return get(sorts, path) !== null
50
+ }
51
+
52
+ function count(sorts) {
53
+ return sorts ? Object.keys(sorts).length : 0
54
+ }
55
+
56
+ // A re-sort moves its folder to the most recent end; past the cap the oldest entries go.
57
+ function set(sorts, path, key, reverse) {
58
+ var next = {}
59
+ if (sorts) {
60
+ for (var k in sorts) {
61
+ if (k !== path)
62
+ next[k] = sorts[k]
63
+ }
64
+ }
65
+ next[path] = { key: key, reverse: reverse === true }
66
+ var keys = Object.keys(next)
67
+ while (keys.length > MAX)
68
+ delete next[keys.shift()]
69
+ return next
70
+ }
71
+
72
+ function forget(sorts, path) {
73
+ var next = {}
74
+ if (sorts) {
75
+ for (var k in sorts) {
76
+ if (k !== path)
77
+ next[k] = sorts[k]
78
+ }
79
+ }
80
+ return next
81
+ }
82
+
83
+ // A user sort writes its folder only while remembering is on.
84
+ function shouldRemember(remember, path) {
85
+ return remember !== false && !!path
86
+ }
87
+
88
+ // Columns pass keep: a saved order wins over XDG defaults; remembering off uses the global order, except kind, unsupported by peeks.
89
+ function peekOrder(sorts, path, remember, keep, newest, fallback) {
90
+ if (!Array.isArray(keep)) return {}
91
+ var own = remember !== false ? get(sorts, path) || defaultOrder(path, newest)
92
+ : fallback ? orderFor(null, path, fallback, false) : null
93
+ if (remember === false && own && own.key === "name" && !own.reverse) return {}
94
+ return own && own.key !== "kind" ? { by: own.key, desc: own.reverse } : {}
95
+ }
96
+
97
+ // An unknown stored key reads as the default, so a newer Flea never wedges this one.
98
+ function orderFor(sorts, path, fallback, remember, newest) {
99
+ if (remember !== false) {
100
+ var own = get(sorts, path) || defaultOrder(path, newest)
101
+ if (own)
102
+ return own
103
+ }
104
+ fallback = fallback || {}
105
+ var key = liveKey(fallback.key)
106
+ return { key: ORDERS.indexOf(key) >= 0 ? key : "name",
107
+ reverse: fallback.reverse === true }
108
+ }