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,114 @@
1
+ pragma Singleton
2
+ import QtQuick
3
+ import Quickshell
4
+ import Quickshell.Io
5
+ import qs.Commons
6
+ import qs.Commons as Commons
7
+ import "js/Material.js" as Material
8
+
9
+ // shell.toml's [flea] background-alpha through Omarchy's reader, and Omarchy's Window Transparency mode: one plane per pixel, content opaque; AGENTS.md "Theme roles and sources".
10
+ QtObject {
11
+ id: root
12
+
13
+ readonly property string key: "flea.background-alpha"
14
+ // Pick candidate A: match Nautilus's 0.75 ground, lifting text rather than fading it like window opacity does.
15
+ property real glassAlpha: root.mode === "glassy" && root.usesDefault ? Material.floor(Theme.color.opaquePalette, Theme.textContrast) / 255 : 1
16
+ property bool focused: Qt.application.active
17
+ readonly property bool split: true
18
+ readonly property bool usesDefault: root.valueIn(Commons.Color.userShellValues) < 0 && root.valueIn(Commons.Color.themeShellValues) < 0
19
+ // "opaque", "glassy" or "transparent" (Omarchy's default), read from its toggle files by reread().
20
+ property string mode: "transparent"
21
+ property bool hasOpaque: false
22
+ property bool hasGlassy: false
23
+ // Set once by launch(), before the window exists, because a Wayland surface's format is fixed at creation.
24
+ property bool clear: false
25
+ // In whole 8-bit steps, so a value that rounds to 1 is 1; a launch a [flea] value pins opaque holds 1 until the next launch.
26
+ readonly property real alpha: root.clear ? root.stepped(root.wanted()) : 1
27
+ // False keeps every plane on its own opaque role, so a stock theme draws exactly what it drew before.
28
+ readonly property bool on: root.alpha < 1
29
+ readonly property color background: root.on ? Util.alpha(Theme.color.background, root.alpha) : Theme.color.background
30
+ readonly property color surface: root.on ? Util.alpha(Theme.color.surface, root.alpha) : Theme.color.surface
31
+ readonly property real listAlpha: root.on && root.usesDefault && root.mode === "glassy" && root.split ? Math.max(root.alpha, Material.listFloor(Theme.color.opaquePalette, Theme.textContrast) / 255) : root.alpha
32
+ readonly property color listBackground: root.on ? Util.alpha(Theme.color.background, root.listAlpha) : Theme.color.background
33
+
34
+ readonly property string toggleDir: (Quickshell.env("XDG_STATE_HOME") || Quickshell.env("HOME") + "/.local/state") + "/omarchy/toggles/hypr"
35
+ // A file test and not omarchy-hyprland-window-transparency, which needs OMARCHY_PATH that ssh and some launchers lack.
36
+ property FileView opaqueFile: FileView {
37
+ path: root.toggleDir + "/window-opaque.lua"
38
+ printErrors: false
39
+ onLoaded: root.hasOpaque = true
40
+ onLoadFailed: root.hasOpaque = false
41
+ }
42
+ property FileView glassyFile: FileView {
43
+ path: root.toggleDir + "/window-glassy.lua"
44
+ printErrors: false
45
+ onLoaded: root.hasGlassy = true
46
+ onLoadFailed: root.hasGlassy = false
47
+ }
48
+ // Hyprland reloads its config for a Window Transparency change, with no theme switch.
49
+ property Connections reloads: Connections {
50
+ target: Theme
51
+ function onConfigReloaded() { root.reread(); root.retag() }
52
+ function onFleaWindowOpened() { root.startTagger() }
53
+ }
54
+ // Only inside Hyprland, as ui/Theme.qml's event hookup: a headless run has no compositor to ask.
55
+ readonly property bool inHyprland: Quickshell.env("HYPRLAND_INSTANCE_SIGNATURE") !== null
56
+ // ui/GlassTagger.qml, made when Hyprland opens the first Flea window, so it costs nothing before the first rows.
57
+ property QtObject tagger: null
58
+ onModeChanged: root.retag()
59
+ onUsesDefaultChanged: root.retag()
60
+
61
+ // Sample input: 0.34 answers 87/255, 0.999 answers 1.
62
+ function stepped(value) { return Math.round(value * 255) / 255 }
63
+
64
+ // One shell.toml's value: -1 when it sets none, the clamped number, or 1 for what is not a number.
65
+ function valueIn(values) {
66
+ var v = values ? values[root.key] : undefined
67
+ if (typeof v !== "string" || v.length === 0) return -1
68
+ var n = Number(v)
69
+ return isFinite(n) ? Util.clampAlpha(n) : 1
70
+ }
71
+ // User and theme values keep their precedence; bare Transparent is opaque: no shipped theme sets an opacity of its own, and no toggle file means transparency was never chosen.
72
+ function wanted() {
73
+ var user = root.valueIn(Commons.Color.userShellValues)
74
+ if (user >= 0) return user
75
+ if (root.mode === "opaque") return 1
76
+ var theme = root.valueIn(Commons.Color.themeShellValues)
77
+ if (theme >= 0) return theme
78
+ return root.mode === "glassy" ? root.glassAlpha : 1
79
+ }
80
+
81
+ function reread() {
82
+ root.opaqueFile.reload()
83
+ root.glassyFile.reload()
84
+ root.opaqueFile.waitForJob()
85
+ root.glassyFile.waitForJob()
86
+ root.mode = root.hasGlassy ? "glassy" : root.hasOpaque ? "opaque" : "transparent"
87
+ }
88
+
89
+ // A [flea] value that pins 1 keeps an opaque surface; every other launch is made clear, so each Window Transparency switch shows live.
90
+ function pinsOpaque() {
91
+ var user = root.valueIn(Commons.Color.userShellValues)
92
+ if (user >= 0) return root.stepped(user) >= 1
93
+ var theme = root.valueIn(Commons.Color.themeShellValues)
94
+ return theme >= 0 && root.stepped(theme) >= 1
95
+ }
96
+
97
+ // Color's two shell readers load asynchronously; waiting here lands both before the decision.
98
+ function launch() {
99
+ Commons.Color.shellFile.waitForJob()
100
+ Commons.Color.userShellFile.waitForJob()
101
+ root.reread()
102
+ root.clear = !root.pinsOpaque()
103
+ }
104
+
105
+ function startTagger() {
106
+ if (!root.inHyprland) return
107
+ if (!root.tagger) root.tagger = Qt.createComponent("GlassTagger.qml").createObject(root, {
108
+ mode: Qt.binding(function () { return root.mode }),
109
+ usesDefault: Qt.binding(function () { return root.usesDefault })
110
+ })
111
+ root.retag()
112
+ }
113
+ function retag() { if (root.tagger) root.tagger.retag() }
114
+ }
@@ -0,0 +1,54 @@
1
+ import QtQuick
2
+ import QtQuick.Shapes
3
+ import qs.Commons
4
+ import "js/Icons.js" as Icons
5
+
6
+ // One row or sidebar mark, chosen by name. Lucide's grid is 24 units at stroke 2, scaled to the slot.
7
+ Item {
8
+ id: root
9
+
10
+ property string name: "file"
11
+ property color color: "transparent"
12
+ // Lucide draws on a 24 unit grid, so every path in Icons.js is in those units.
13
+ readonly property real grid: 24
14
+ // The mark's ceiling. Default is the row and menu mark; a surface drawing a mark alone passes its own.
15
+ property real maxSize: Theme.markSize
16
+ // The min() against the slot is the icon spec's own rule, so a smaller caller slot still wins
17
+ // (the Network "+" at the caption token needs it) and no container can ever enlarge a mark.
18
+ readonly property real markSize: Math.min(root.maxSize, root.width, root.height)
19
+ // Its own name: "scale" would shadow Item.scale, which qmllint flagged.
20
+ readonly property real gridScale: root.markSize / root.grid
21
+ property real strokeWidth: Theme.strokeWidth
22
+ // The solid part of the mark (Icons.js FILLED), empty for every mark but the few that have one.
23
+ readonly property string filledPath: Icons.filledPathFor(root.name)
24
+ // Built the first time a mark names a solid part: a plain glyph owns no object for it, which tests/rowcost.qml counts.
25
+ property Item solid: null
26
+ function syncSolid() {
27
+ if (root.solid === null && root.filledPath.length > 0)
28
+ root.solid = Qt.createComponent("GlyphSolid.qml").createObject(root, { glyph: root })
29
+ }
30
+ // filledPath starts empty, so its first non-empty value, on a build or a reuse, lands here.
31
+ onFilledPathChanged: root.syncSolid()
32
+
33
+ // The Shape is sized in grid units pre-transform; Scale's origin defaults to (0,0), so this
34
+ // offset is what re-centers the scaled-down box in root's slot instead of pinning it top-left.
35
+ Shape {
36
+ width: root.grid
37
+ height: root.grid
38
+ x: (root.width - root.markSize) / 2
39
+ y: (root.height - root.markSize) / 2
40
+ preferredRendererType: Shape.CurveRenderer
41
+ transform: Scale { xScale: root.gridScale; yScale: root.gridScale }
42
+
43
+ ShapePath {
44
+ strokeColor: root.color
45
+ fillColor: "transparent"
46
+ strokeWidth: root.strokeWidth
47
+ // The Omarchy cut: square caps and mitered joins, the edge of the brand spiral; Icons.js paths are redrawn sharp to match.
48
+ capStyle: ViewState.hyprlandIcons && Style.cornerRadius > 0 ? ShapePath.RoundCap : ShapePath.SquareCap
49
+ joinStyle: ViewState.hyprlandIcons && Style.cornerRadius > 0 ? ShapePath.RoundJoin : ShapePath.MiterJoin
50
+ // A mark with more than one stroke is one multi-subpath SVG string; see Icons.js "Lucide path data".
51
+ PathSvg { path: Icons.pathFor(root.name) }
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,22 @@
1
+ import QtQuick
2
+ import QtQuick.Shapes
3
+
4
+ // The solid part of a mark, drawn over its stroke in the same ink; ui/Glyph.qml builds it only for a mark that names one.
5
+ Shape {
6
+ id: root
7
+
8
+ required property Glyph glyph
9
+
10
+ width: root.glyph.grid
11
+ height: root.glyph.grid
12
+ x: (root.glyph.width - root.glyph.markSize) / 2
13
+ y: (root.glyph.height - root.glyph.markSize) / 2
14
+ preferredRendererType: Shape.CurveRenderer
15
+ transform: Scale { xScale: root.glyph.gridScale; yScale: root.glyph.gridScale }
16
+
17
+ ShapePath {
18
+ strokeColor: "transparent"
19
+ fillColor: root.glyph.color
20
+ PathSvg { path: root.glyph.filledPath }
21
+ }
22
+ }
@@ -0,0 +1,54 @@
1
+ import QtQuick
2
+ import "js/Motion.js" as Motion
3
+
4
+ // The one fade every dialog and menu shares: OutCubic, Motion.durMs.open in and .close out, snapped under reduced motion.
5
+ QtObject {
6
+ id: root
7
+
8
+ // The item that fades; it declares a plain `opacity: 0` and never binds it.
9
+ property Item target: null
10
+ property bool shown: false
11
+ // A dialog's card: it rises Motion.translateUpPx into place on a fresh open only, and never moves while it fades out.
12
+ property Item lift: null
13
+
14
+ // Drives target.opacity from where it stands, so a reopen during a close fades up from there and never replays the old frame.
15
+ function drive() {
16
+ if (root.target === null) return
17
+ fade.stop()
18
+ risen.stop()
19
+ if (Theme.reducedMotion) {
20
+ root.target.opacity = root.shown ? 1 : 0
21
+ rise.y = 0
22
+ return
23
+ }
24
+ if (root.shown && root.lift !== null) {
25
+ if (root.target.opacity === 0) rise.y = Motion.translateUpPx
26
+ if (rise.y !== 0) risen.start()
27
+ }
28
+ fade.to = root.shown ? 1 : 0
29
+ fade.duration = root.shown ? Motion.durMs.open : Motion.durMs.close
30
+ fade.start()
31
+ }
32
+
33
+ onShownChanged: root.drive()
34
+ Component.onCompleted: {
35
+ if (root.lift !== null) root.lift.transform = rise
36
+ if (root.shown) root.drive()
37
+ }
38
+
39
+ property Translate rise: Translate {}
40
+ property NumberAnimation fade: NumberAnimation {
41
+ target: root.target
42
+ property: "opacity"
43
+ easing.type: Easing.OutCubic
44
+ // A close cut inside the rise leaves the card where it stood until it is gone, then rests it.
45
+ onFinished: if (!root.shown) root.rise.y = 0
46
+ }
47
+ property NumberAnimation risen: NumberAnimation {
48
+ target: root.rise
49
+ property: "y"
50
+ to: 0
51
+ duration: Motion.durMs.open
52
+ easing.type: Easing.OutCubic
53
+ }
54
+ }
@@ -0,0 +1,85 @@
1
+ import QtQuick
2
+
3
+ // The Display board's seven-stop ruler: one tick per size Omarchy offers, each carrying its own
4
+ // number, with the stop in force marked by a taller tick under a foreground number rather than by a
5
+ // fill nobody could read (SettingsRest rule 2). The marked tick takes the accent once an override
6
+ // makes the ruler the control, which is the Blueprint's own rule for an active control against a
7
+ // quiet one, and stays muted while Omarchy owns the size and the ruler is only reporting it.
8
+ Item {
9
+ id: root
10
+
11
+ property var stops: []
12
+ // The last filled tick. Omarchy can be on a size that is not a stop, so the caller fills to the
13
+ // nearest one rather than leaving the ruler blank.
14
+ property int index: -1
15
+ property bool active: false
16
+
17
+ signal picked(int stop)
18
+
19
+ readonly property int tickGap: Theme.spacing.hairline * 2
20
+ readonly property int tickHeight: Theme.spacing.hairline * 3
21
+ // The board's own 7 against 11: the stop in force stands taller, so the mark is never a colour alone.
22
+ readonly property int markedHeight: Theme.spacing.hairline * 5
23
+ // And the board's own 2 against 3 across; the tap target is the slot behind the ink, so a mark this narrow still answers a click anywhere on its stop.
24
+ readonly property int tickInk: Theme.spacing.hairline * 2
25
+ readonly property int markedInk: Theme.spacing.hairline * 3
26
+ // The hairline treatment the panel already uses for a quiet edge, so an unmarked stop reads as one.
27
+ readonly property real restOpacity: 0.4
28
+ readonly property real tickWidth: root.stops.length > 0
29
+ ? (root.width - root.tickGap * (root.stops.length - 1)) / root.stops.length : 0
30
+ readonly property real numberHeight: Theme.font.caption * 1.5
31
+ // WCAG 2.5.8's floor under the tap target, whatever height the numbers and ticks come to.
32
+ readonly property int hitHeight: Math.max(Theme.hitMin, Math.round(root.numberHeight + root.markedHeight))
33
+
34
+ implicitHeight: root.hitHeight
35
+
36
+ Repeater {
37
+ model: root.stops
38
+
39
+ delegate: Item {
40
+ id: tick
41
+ required property var modelData
42
+ required property int index
43
+ readonly property bool marked: tick.index === root.index
44
+
45
+ // The hit box is taller than the drawn tick and centred on the ruler, so it moves nothing.
46
+ x: tick.index * (root.tickWidth + root.tickGap)
47
+ y: Math.round((root.height - tick.height) / 2)
48
+ width: root.tickWidth
49
+ height: root.hitHeight
50
+
51
+ Text {
52
+ id: number
53
+ anchors.horizontalCenter: parent.horizontalCenter
54
+ y: Math.round((root.numberHeight - implicitHeight) / 2)
55
+ text: tick.modelData
56
+ color: tick.marked ? Theme.color.foreground : Theme.color.muted
57
+ opacity: tick.marked ? 1 : root.restOpacity
58
+ font.family: Theme.font.family
59
+ font.pixelSize: Theme.font.caption
60
+ textFormat: Text.PlainText
61
+ }
62
+
63
+ Rectangle {
64
+ anchors.horizontalCenter: parent.horizontalCenter
65
+ y: Math.round(root.numberHeight)
66
+ width: tick.marked ? root.markedInk : root.tickInk
67
+ height: tick.marked ? root.markedHeight : root.tickHeight
68
+ color: root.active && tick.marked ? Theme.color.accent : Theme.color.mutedFrame
69
+ opacity: tick.marked ? 1 : root.restOpacity
70
+ }
71
+
72
+ HoverHandler {
73
+ enabled: root.active
74
+ cursorShape: Qt.PointingHandCursor
75
+ }
76
+
77
+ TapHandler {
78
+ enabled: root.active
79
+ acceptedButtons: Qt.LeftButton
80
+ gesturePolicy: TapHandler.ReleaseWithinBounds
81
+ onTapped: root.picked(tick.modelData)
82
+ }
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,85 @@
1
+ import QtQuick
2
+ import "." as Flea
3
+ import "js/Buttons.js" as Buttons
4
+
5
+ // The Display board's two-value control: both names side by side, the current one bright, rather than
6
+ // the chevron walk a longer list needs. ui/SettingsRow.qml draws it for any row whose model names its
7
+ // options, and the row above owns the writer, so nothing here holds state.
8
+ Row {
9
+ id: root
10
+
11
+ property var options: []
12
+ property string value: ""
13
+ property var glyphs: []
14
+ // A host that puts the row in its Tab order (a dialog's Format) sets this; Settings never focuses it.
15
+ property bool tabStop: false
16
+ // The hit minimum in Settings; a chrome strip hosts the same cells at its control height.
17
+ property int cellHeight: Theme.hitMin
18
+
19
+ // The option a pointer chose, which is never the one already showing.
20
+ signal picked(int index)
21
+
22
+ spacing: Theme.spacing.hairline * Buttons.SEGMENT_GAP
23
+
24
+ Repeater {
25
+ model: root.options
26
+
27
+ delegate: Rectangle {
28
+ id: segment
29
+ required property var modelData
30
+ required property int index
31
+
32
+ readonly property bool current: segment.modelData === root.value
33
+ readonly property string glyph: root.glyphs[segment.index] || ""
34
+ // A segment that takes the keyboard (a dialog's Tab stop) marks its current cell as DialogButton marks a focused set member.
35
+ readonly property bool focusedHere: root.tabStop && segment.current && root.activeFocus && segment.glyph.length === 0
36
+ // Accent where it has hue, else RING wide in the foreground, both inside the cell like DialogButton's set member.
37
+ readonly property string focusFrame: Buttons.focusFrame(Theme.color.accentHasHue, true, false)
38
+
39
+ // The WCAG floor the Blueprint sets for a compact control, which this row has room for.
40
+ width: segment.glyph ? Math.max(Theme.hitMin, Theme.railIconSize + 2 * (Theme.spacing.gap - Theme.spacing.hairline))
41
+ : name.implicitWidth + 2 * Theme.spacing.gap
42
+ height: root.cellHeight
43
+ // Containers rule 4: a segmented cell is never filled, so the choice reads as brightness.
44
+ color: "transparent"
45
+ // Rule 3: words in a set are Tier B and every member wears the box; glyphs in a strip are
46
+ // Tier A, where a box around four marks would be furniture.
47
+ border.width: segment.glyph ? 0 : segment.focusedHere && segment.focusFrame === "thick" ? Buttons.RING : Theme.spacing.hairline
48
+ border.color: segment.focusedHere && segment.focusFrame === "accent" ? Theme.color.accent
49
+ : segment.current ? Theme.color.foreground : Theme.color.mutedFrame
50
+ Accessible.role: Accessible.Button
51
+ Accessible.name: String(segment.modelData)
52
+ Accessible.onPressAction: root.picked(segment.index)
53
+
54
+ Flea.Glyph {
55
+ anchors.centerIn: parent
56
+ visible: segment.glyph.length > 0
57
+ width: Theme.railIconSize
58
+ height: width
59
+ name: segment.glyph || "file"
60
+ color: segment.current ? Theme.color.foreground : Theme.color.mutedFrame
61
+ }
62
+
63
+ Text {
64
+ id: name
65
+ visible: segment.glyph.length === 0
66
+ anchors.centerIn: parent
67
+ text: segment.modelData
68
+ color: segment.current ? Theme.color.foreground : Theme.color.muted
69
+ font.family: Theme.font.family
70
+ font.pixelSize: Theme.font.caption
71
+ textFormat: Text.PlainText
72
+ }
73
+
74
+ HoverHandler {
75
+ cursorShape: Qt.PointingHandCursor
76
+ }
77
+
78
+ TapHandler {
79
+ acceptedButtons: Qt.LeftButton
80
+ gesturePolicy: TapHandler.ReleaseWithinBounds
81
+ onTapped: if (!segment.current) root.picked(segment.index)
82
+ }
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,89 @@
1
+ import QtQuick
2
+ import QtQml.Models
3
+
4
+ // A view's row count as a model that never changes identity, so a new count keeps the delegates that stay drawn.
5
+ DelegateModel {
6
+ id: root
7
+
8
+ required property Item view
9
+ property int total: 0
10
+ // The compositor is filled in componentComplete, so a group change before onCompleted is silently dropped.
11
+ property bool ready: false
12
+
13
+ // A move asked for inside the change's own layout is dropped, so a view that places itself does it here.
14
+ signal resized()
15
+
16
+ // Qt refuses an integer model larger than 100,000,000 rows.
17
+ readonly property int cap: 100000000
18
+ model: root.cap
19
+ groups: DelegateModelGroup { id: shown; name: "shown"; includeByDefault: false }
20
+ filterOnGroup: "shown"
21
+
22
+ // While true a view short of a viewport keeps a full layout's delegates in its reuse pool, so a count that grows takes them instead of building.
23
+ property bool warm: false
24
+ // The rows a full layout from the top builds, its cache buffer's included: the fill lays out no more, so none is left incubating.
25
+ property int warmRows: 0
26
+
27
+ // While true a count change leaves the current delegate current: Qt focuses each new current delegate, which takes the keys from an editor inside the old one.
28
+ property bool holdCurrent: false
29
+
30
+ onTotalChanged: root.resize()
31
+ onWarmChanged: if (root.warm) root.fill()
32
+ Component.onCompleted: { root.ready = true; root.resize() }
33
+
34
+ // Grows the count and shrinks it back in one turn, so no frame sees the rows and what they laid out waits pooled; a geometry change drains it.
35
+ function fill() {
36
+ var from = shown.count
37
+ var to = Math.min(root.warmRows, root.cap)
38
+ if (!root.ready || !root.view.reuseItems || to <= from)
39
+ return
40
+ // The buffer's rows lay out with the viewport's for this pass, built at once where a buffer pass would incubate them later.
41
+ var margin = root.view.displayMarginEnd
42
+ root.view.displayMarginEnd = margin + root.view.cacheBuffer + root.view.height
43
+ root.items.addGroups(from, to - from, "shown")
44
+ root.view.forceLayout()
45
+ shown.removeGroups(from, to - from, "shown")
46
+ root.view.forceLayout()
47
+ root.view.displayMarginEnd = margin
48
+ }
49
+
50
+ // Moves to the top and, unless holdCurrent, row 0 as a reset does, before the change, so only top rows lay out and a view placing itself after keeps its place.
51
+ function resize() {
52
+ var to = Math.min(Math.max(root.total, 0), root.cap)
53
+ var from = shown.count
54
+ if (!root.ready || to === from)
55
+ return
56
+ root.view.contentY = root.view.originY - root.view.topMargin
57
+ if (from > 0 && to > 0 && !root.holdCurrent)
58
+ root.view.currentIndex = 0
59
+ if (to > from)
60
+ root.items.addGroups(from, to - from, "shown")
61
+ else
62
+ shown.removeGroups(to, from - to, "shown")
63
+ // Lays out now: Qt pools a layout's removed rows only after building its inserted ones, so the next count reuses them.
64
+ root.view.forceLayout()
65
+ if (to > 0 && root.view.currentIndex < 0)
66
+ root.view.currentIndex = 0
67
+ root.resized()
68
+ if (to === 0)
69
+ root.drainHidden()
70
+ }
71
+
72
+ // True where a hidden count is rows kept for the return rather than an edit in flight, so the hide drains the pool beside them.
73
+ property bool keptRows: false
74
+ // A hidden view holds no delegates, pooled ones included; a shown one keeps its pool, parked, for the next listing.
75
+ function drainHidden() {
76
+ if (root.view.visible || shown.count > 0 && !root.keptRows || !root.view.reuseItems)
77
+ return
78
+ // Unpersist first so the pool toggle below frees the parked tiles too.
79
+ if (persistedItems.count > 0)
80
+ persistedItems.removeGroups(0, persistedItems.count, "persistedItems")
81
+ root.view.reuseItems = false
82
+ root.view.reuseItems = true
83
+ }
84
+ // A view hidden after it emptied drains here, since its count does not change again.
85
+ property Connections hiding: Connections {
86
+ target: root.view
87
+ function onVisibleChanged() { root.drainHidden() }
88
+ }
89
+ }