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,1199 @@
1
+ import {createQmlShaderEffects} from './shader/runtime.js';
2
+ import { createQmlShortcuts } from './shortcuts.js';
3
+ import { createQmlCanvas } from './canvas.js';
4
+ import { nonvisualTypes, typeProperties, createQt } from './builtins.js';
5
+ import { createInertWindow } from '../happy-dom/parser.js';
6
+ import { parseQml } from './parser.js';
7
+ import { createQmlTextures } from './textures.js';
8
+ import { createQmlMedia } from './media.js';
9
+ import { createQmlImages } from './images.js';
10
+ import { createQmlShapes } from './shapes.js';
11
+ import { createQmlText } from './text.js';
12
+ import { createQmlModels } from './models.js';
13
+ import { createReactive } from './reactive.js';
14
+ import { createQmlRuntime } from './runtime.js';
15
+ import { builtinTypes, loadQmlGraph } from './loader.js';
16
+
17
+ const types = builtinTypes;
18
+ const defaults = [
19
+ 'width',
20
+ 'height',
21
+ 'implicitWidth',
22
+ 'implicitHeight',
23
+ 'x',
24
+ 'y',
25
+ 'color',
26
+ 'radius',
27
+ 'containmentMask',
28
+ 'Layout',
29
+ 'Keys',
30
+ 'opacity',
31
+ 'smooth',
32
+ 'antialiasing',
33
+ 'visible',
34
+ 'enabled',
35
+ 'clip',
36
+ 'text',
37
+ 'title',
38
+ 'font',
39
+ 'border',
40
+ 'anchors',
41
+ 'horizontalAlignment',
42
+ 'textFormat',
43
+ 'spacing',
44
+ 'placeholderText',
45
+ 'readOnly',
46
+ 'selectByMouse',
47
+ 'Accessible',
48
+ 'objectName',
49
+ 'activeFocus',
50
+ 'focus',
51
+ 'activeFocusOnTab',
52
+ 'baseline',
53
+ 'horizontalCenter',
54
+ 'verticalCenter',
55
+ 'left',
56
+ 'right',
57
+ 'top',
58
+ 'bottom',
59
+ 'childrenRect',
60
+ 'children',
61
+ 'data',
62
+ 'parent',
63
+ 'Drag',
64
+ 'gradient',
65
+ 'scale',
66
+ 'rotation',
67
+ 'transform',
68
+ 'z',
69
+ 'baselineOffset',
70
+ 'topPadding',
71
+ 'bottomPadding',
72
+ 'leftPadding',
73
+ 'rightPadding',
74
+ 'linkColor',
75
+ 'hoveredLink',
76
+ 'truncated',
77
+ 'contentWidth',
78
+ 'contentHeight',
79
+ 'maximumLineCount',
80
+ 'lineCount',
81
+ 'renderType',
82
+ 'verticalAlignment',
83
+ 'wrapMode',
84
+ 'elide',
85
+ 'lineHeight',
86
+ 'lineHeightMode',
87
+ 'grabToImage',
88
+ 'forceActiveFocus',
89
+ 'contains',
90
+ 'mapToItem',
91
+ 'mapFromItem',
92
+ ];
93
+ const specializedProperties = {
94
+ lineCount: ['Text', 'Label', 'TextEdit'],
95
+ text: ['Text', 'Label', 'TextField', 'TextInput', 'TextEdit', 'Button'],
96
+ font: ['Text', 'Label', 'TextField', 'TextInput', 'TextEdit', 'Button'],
97
+ color: [
98
+ 'Text',
99
+ 'Label',
100
+ 'TextField',
101
+ 'TextInput',
102
+ 'TextEdit',
103
+ 'Button',
104
+ 'Rectangle',
105
+ 'FloatingWindow',
106
+ ],
107
+ title: ['FloatingWindow'],
108
+ border: ['Rectangle'],
109
+ radius: ['Rectangle'],
110
+ textFormat: ['Text', 'Label', 'TextEdit'],
111
+ placeholderText: ['TextField'],
112
+ readOnly: ['TextField', 'TextInput', 'TextEdit'],
113
+ selectByMouse: ['TextField', 'TextInput', 'TextEdit'],
114
+ horizontalAlignment: [
115
+ 'Text',
116
+ 'Label',
117
+ 'TextField',
118
+ 'TextInput',
119
+ 'TextEdit',
120
+ 'Button',
121
+ ],
122
+ };
123
+ const itemProperties = (type) =>
124
+ defaults.filter(
125
+ (key) =>
126
+ !specializedProperties[key] || specializedProperties[key].includes(type),
127
+ );
128
+ const groups = new Set([
129
+ 'Layout.fillWidth',
130
+ 'Layout.fillHeight',
131
+ 'Layout.minimumWidth',
132
+ 'Layout.minimumHeight',
133
+ 'Layout.maximumWidth',
134
+ 'Layout.maximumHeight',
135
+ 'Layout.preferredWidth',
136
+ 'Layout.preferredHeight',
137
+ 'Layout.alignment',
138
+ 'Layout.margins',
139
+ 'Layout.leftMargin',
140
+ 'Layout.rightMargin',
141
+ 'Layout.topMargin',
142
+ 'Layout.bottomMargin',
143
+ 'Keys.enabled',
144
+ 'Keys.forwardTo',
145
+ 'font.pixelSize',
146
+ 'font.family',
147
+ 'font.bold',
148
+ 'border.width',
149
+ 'border.color',
150
+ 'font.capitalization',
151
+ 'font.letterSpacing',
152
+ 'font.italic',
153
+ 'font.features',
154
+ 'anchors.fill',
155
+ 'anchors.centerIn',
156
+ 'anchors.horizontalCenter',
157
+ 'anchors.verticalCenter',
158
+ 'anchors.left',
159
+ 'anchors.right',
160
+ 'anchors.baseline',
161
+ 'anchors.baselineOffset',
162
+ 'anchors.top',
163
+ 'anchors.bottom',
164
+ 'anchors.margins',
165
+ 'anchors.leftMargin',
166
+ 'anchors.rightMargin',
167
+ 'anchors.topMargin',
168
+ 'anchors.bottomMargin',
169
+ 'anchors.horizontalCenterOffset',
170
+ 'anchors.verticalCenterOffset',
171
+ 'Accessible.selected',
172
+ 'Accessible.checkable',
173
+ 'Accessible.name',
174
+ 'Accessible.role',
175
+ 'Accessible.checked',
176
+ 'Accessible.description',
177
+ 'Accessible.focusable',
178
+ 'Accessible.focused',
179
+ 'Accessible.ignored',
180
+ 'xAxis.enabled',
181
+ 'xAxis.minimum',
182
+ 'xAxis.maximum',
183
+ 'yAxis.enabled',
184
+ 'yAxis.minimum',
185
+ 'yAxis.maximum',
186
+ 'sourceSize.width',
187
+ 'sourceSize.height',
188
+ 'Drag.active',
189
+ 'Drag.source',
190
+ 'Drag.keys',
191
+ 'Drag.mimeData',
192
+ 'Drag.supportedActions',
193
+ 'Drag.proposedAction',
194
+ 'Drag.dragType',
195
+ 'Drag.imageSource',
196
+ 'Drag.hotSpot.x',
197
+ 'Drag.hotSpot.y',
198
+ 'origin.x',
199
+ 'origin.y',
200
+ 'easing.type',
201
+ 'easing.amplitude',
202
+ 'easing.period',
203
+ 'easing.overshoot',
204
+ ]);
205
+
206
+ // Rewrite only QML-resolved names; all other identifiers retain page JS meaning.
207
+ // Function/block-local names shadow QML properties. No eval in the host process.
208
+ function rewriteExpression(property, scope, runtimeRef) {
209
+ const edits = [];
210
+ function walk(node, parent, key, locals = new Set()) {
211
+ if (!node || typeof node !== 'object') return;
212
+ if (Array.isArray(node)) {
213
+ for (const child of node) walk(child, parent, key, locals);
214
+ return;
215
+ }
216
+ if (
217
+ [
218
+ 'FunctionExpression',
219
+ 'ArrowFunctionExpression',
220
+ 'FunctionDeclaration',
221
+ 'BlockStatement',
222
+ 'Program',
223
+ 'ForStatement',
224
+ 'ForInStatement',
225
+ 'ForOfStatement',
226
+ 'CatchClause',
227
+ ].includes(node.type)
228
+ ) {
229
+ locals = new Set(locals);
230
+ const addPattern = (n) => {
231
+ if (!n) return;
232
+ if (n.type === 'Identifier') locals.add(n.name);
233
+ else if (n.type === 'AssignmentPattern') addPattern(n.left);
234
+ else if (n.type === 'RestElement') addPattern(n.argument);
235
+ else if (n.type === 'ArrayPattern') n.elements.forEach(addPattern);
236
+ else if (n.type === 'ObjectPattern')
237
+ n.properties.forEach((p) =>
238
+ addPattern(p.type === 'RestElement' ? p : p.value),
239
+ );
240
+ };
241
+ const collectVars = (n) => {
242
+ if (!n || typeof n !== 'object') return;
243
+ if (Array.isArray(n)) {
244
+ n.forEach(collectVars);
245
+ return;
246
+ }
247
+ if (
248
+ [
249
+ 'FunctionExpression',
250
+ 'ArrowFunctionExpression',
251
+ 'FunctionDeclaration',
252
+ ].includes(n.type)
253
+ )
254
+ return;
255
+ if (n.type === 'VariableDeclaration' && n.kind === 'var')
256
+ n.declarations.forEach((d) => addPattern(d.id));
257
+ for (const [name, child] of Object.entries(n))
258
+ if (name !== 'type' && child && typeof child === 'object')
259
+ collectVars(child);
260
+ };
261
+ if (
262
+ node.type === 'Program' ||
263
+ node.type.includes('Function') ||
264
+ (node.type === 'BlockStatement' && !parent)
265
+ )
266
+ collectVars(node.body);
267
+ const loop = node.init ?? node.left;
268
+ if (loop?.type === 'VariableDeclaration')
269
+ loop.declarations.forEach((d) => addPattern(d.id));
270
+ if (node.type === 'CatchClause') addPattern(node.param);
271
+ node.params?.forEach(addPattern);
272
+ addPattern(node.id);
273
+ const statements = Array.isArray(node.body)
274
+ ? node.body
275
+ : node.body?.type === 'BlockStatement'
276
+ ? node.body.body
277
+ : [];
278
+ for (const statement of statements) {
279
+ if (statement.type === 'VariableDeclaration')
280
+ statement.declarations.forEach((d) => addPattern(d.id));
281
+ if (statement.type === 'FunctionDeclaration') addPattern(statement.id);
282
+ }
283
+ }
284
+ // Replacing a namespace identifier with a parenthesized object must not
285
+ // turn the following line into a call on the previous expression (ASI).
286
+ if (
287
+ [
288
+ 'ExpressionStatement',
289
+ 'ReturnStatement',
290
+ 'ThrowStatement',
291
+ 'BreakStatement',
292
+ 'ContinueStatement',
293
+ 'VariableDeclaration',
294
+ ].includes(node.type) &&
295
+ !(
296
+ ['ForStatement', 'ForInStatement', 'ForOfStatement'].includes(
297
+ parent?.type,
298
+ ) && ['init', 'left'].includes(key)
299
+ ) &&
300
+ property.code[node.end - 1] !== ';'
301
+ )
302
+ edits.push([node.end, node.end, ';']);
303
+ if (
304
+ node.type === 'BinaryExpression' &&
305
+ node.operator === 'in' &&
306
+ /\bas\b/.test(property.code.slice(node.left.end, node.right.start))
307
+ ) {
308
+ edits.push(
309
+ [node.start, node.start, runtimeRef + '.cast('],
310
+ [node.left.end, node.right.start, ','],
311
+ [
312
+ node.right.start,
313
+ node.right.end,
314
+ JSON.stringify(property.code.slice(node.right.start, node.right.end)),
315
+ ],
316
+ [node.end, node.end, ')'],
317
+ );
318
+ walk(node.left, node, 'left', locals);
319
+ return;
320
+ }
321
+ if (
322
+ node.type === 'MemberExpression' &&
323
+ !node.computed &&
324
+ !node.optional &&
325
+ node.property.name === 'arg' &&
326
+ parent?.type === 'CallExpression' &&
327
+ key === 'callee'
328
+ ) {
329
+ edits.push(
330
+ [node.start, node.start, runtimeRef + '.argFor('],
331
+ [node.object.end, node.end, ')'],
332
+ );
333
+ walk(node.object, node, 'object', locals);
334
+ return;
335
+ }
336
+ if (
337
+ node.type === 'Identifier' &&
338
+ scope.has(node.name) &&
339
+ !locals.has(node.name)
340
+ ) {
341
+ if (
342
+ (parent?.type === 'MemberExpression' &&
343
+ key === 'property' &&
344
+ !parent.computed) ||
345
+ (parent?.type === 'Property' && key === 'key') ||
346
+ (parent?.type === 'VariableDeclarator' && key === 'id')
347
+ )
348
+ return;
349
+ const replacement = scope.get(node.name);
350
+ edits.push([
351
+ node.start,
352
+ node.end,
353
+ parent?.type === 'Property' && parent.shorthand
354
+ ? `${node.name}: ${replacement}`
355
+ : replacement,
356
+ ]);
357
+ return;
358
+ }
359
+ for (const [k, v] of Object.entries(node))
360
+ if (k !== 'type' && v && typeof v === 'object') walk(v, node, k, locals);
361
+ }
362
+ walk(property.ast, null, null);
363
+ let code = property.code;
364
+ for (const [start, end, value] of edits.sort(
365
+ (a, b) => b[0] - a[0] || b[1] - a[1],
366
+ ))
367
+ code = code.slice(0, start) + value + code.slice(end);
368
+ return property.block ? `(()=>${code})()` : code;
369
+ }
370
+
371
+ /** Asynchronous import preparation uses the browser's existing resource bridge. */
372
+ export async function loadQmlDocument(source, url, options = {}) {
373
+ try {
374
+ if (options.scripting === false)
375
+ return createQmlDocument(source, url, options);
376
+ const graph = await loadQmlGraph(
377
+ source,
378
+ url,
379
+ options.fetchResource,
380
+ options,
381
+ );
382
+ return createQmlDocument(source, url, { ...options, graph });
383
+ } catch (error) {
384
+ return createQmlDocument(source, url, { ...options, error });
385
+ }
386
+ }
387
+
388
+ /** Build actual DOM nodes. Import ASTs are discarded after compiling the page. */
389
+ export function createQmlDocument(
390
+ source,
391
+ url,
392
+ {
393
+ scripting = true,
394
+ graph = null,
395
+ error = null,
396
+ component = false,
397
+ externalSingletons = [],
398
+ } = {},
399
+ ) {
400
+ const window = createInertWindow(url),
401
+ document = window.document;
402
+ document.body.style.cssText = 'margin:0;overflow:hidden';
403
+ try {
404
+ if (error) throw error;
405
+ if (!scripting)
406
+ throw Error('QML rendering requires page scripting; source shown below.');
407
+ if (!graph) {
408
+ const ast = parseQml(source);
409
+ for (const imp of ast.imports)
410
+ if (
411
+ !/^(QtQuick(?:\.Controls)?|Quickshell)(?:\s+\d+(?:\.\d+)?)?$/.test(
412
+ imp,
413
+ )
414
+ )
415
+ throw Error(`Unsupported QML import: ${imp}`);
416
+ graph = {
417
+ entry: url,
418
+ documents: new Map([
419
+ [
420
+ url,
421
+ {
422
+ url,
423
+ ast,
424
+ types: new Map(),
425
+ values: new Map(),
426
+ builtins: new Map(),
427
+ },
428
+ ],
429
+ ]),
430
+ scripts: new Map(),
431
+ };
432
+ }
433
+ // Generated references must not be captured by application locals such as
434
+ // `for (var r = 0; ...)` inside a binding that reads a shared Theme.
435
+ const applicationCode =
436
+ JSON.stringify([...graph.documents.values()].map((d) => d.ast)) +
437
+ [...graph.scripts.values()].map((s) => s.source).join('\n');
438
+ let generatedPrefix = '$qml$';
439
+ while (applicationCode.includes(generatedPrefix)) generatedPrefix += '$';
440
+ const runtimeRef = generatedPrefix + 'runtime';
441
+ const expression = (property, scope) =>
442
+ rewriteExpression(property, scope, runtimeRef);
443
+ const records = [],
444
+ contexts = [],
445
+ singletons = new Map(),
446
+ scripts = [];
447
+ for (const url of externalSingletons)
448
+ singletons.set(url, {
449
+ external: `${runtimeRef}.shared(${JSON.stringify(url)})`,
450
+ });
451
+ let template = null;
452
+ const constants =
453
+ 'const Locale={LongFormat:0,ShortFormat:1,NarrowFormat:2,MetricSystem:0,ImperialUSSystem:1,ImperialUKSystem:2};const StandardKey={Copy:9,Paste:10,Cut:8,SelectAll:26,Undo:11,Redo:12};const ShaderEffect={Uncompiled:0,Compiled:1,Error:2,NoCulling:0,BackFaceCulling:1,FrontFaceCulling:2};const MediaPlayer={NoError:0,ResourceError:1,NetworkError:2,FormatError:3,AccessDeniedError:4,StoppedState:0,PlayingState:1,PausedState:2,NoMedia:0,LoadingMedia:1,LoadedMedia:2,StalledMedia:3,BufferingMedia:4,BufferedMedia:5,EndOfMedia:6,InvalidMedia:7,Infinite:-1};const VideoOutput={Stretch:0,PreserveAspectFit:1,PreserveAspectCrop:2};const XmlListModel={Null:0,Ready:1,Loading:2,Error:3};const LoggingCategory={Debug:0,Info:4,Warning:1,Critical:2,Fatal:3};const Flow={LeftToRight:0,TopToBottom:1};const ListView={Beginning:0,Center:1,End:2,Visible:3,Contain:4,SnapPosition:5,Vertical:2,Horizontal:1,NoHighlightRange:0};const GridView={...ListView,FlowLeftToRight:0,FlowTopToBottom:1};const Binding={RestoreNone:0,RestoreBinding:1,RestoreValue:2,RestoreBindingOrValue:3};const Gradient={Vertical:0,Horizontal:1};const PointerHandler={CanTakeOverFromAnything:31,ApprovesTakeOverByAnything:511,TakeOverForbidden:0};const Drag={None:0,Automatic:1,Internal:2};const Application={styleHints:{wheelScrollLines:3,mouseDoubleClickInterval:400}};const Flickable={AutoFlickDirection:0,HorizontalFlick:1,VerticalFlick:2,HorizontalAndVerticalFlick:3,StopAtBounds:0,DragOverBounds:1,OvershootBounds:2,DragAndOvershootBounds:3};const Image={Null:0,Ready:1,Loading:2,Error:3,Stretch:0,PreserveAspectFit:1,PreserveAspectCrop:2,Tile:3,Pad:6};const TextInput={AlignLeft:1,AlignRight:2,AlignHCenter:4,AlignTop:32,AlignBottom:64,AlignVCenter:128,Normal:0,Password:2,NoEcho:1,PasswordEchoOnEdit:3,NativeRendering:1};const TextEdit=TextInput;const Text={AlignLeft:1,AlignRight:2,AlignHCenter:4,AlignTop:32,AlignBottom:64,AlignVCenter:128,PlainText:0,RichText:1,AutoText:2,StyledText:3,MarkdownText:4,NoWrap:0,WordWrap:1,WrapAnywhere:2,Wrap:3,WrapAtWordBoundaryOrAnywhere:3,ElideNone:0,ElideLeft:1,ElideRight:2,ElideMiddle:3,ProportionalHeight:0,FixedHeight:1};const Font={MixedCase:0,AllUppercase:1,AllLowercase:2,SmallCaps:3,Capitalize:4};const Accessible={Button:43,CheckBox:44};const Shape={CurveRenderer:1};const ShapePath={SquareCap:16,FlatCap:0,RoundCap:32,MiterJoin:0,BevelJoin:64,RoundJoin:128,DashLine:2,WindingFill:1,OddEvenFill:0};const Easing={Linear:0,InQuad:1,OutQuad:2,InOutQuad:3,InCubic:5,OutCubic:6,InOutCubic:7,OutQuart:10,OutQuint:14,OutSine:18,OutExpo:22,OutBack:34};const Animation={Infinite:-1};const Component={Null:0,Ready:1,Loading:2,Error:3};const Loader=Component;const TapHandler={ReleaseWithinBounds:1};const Qt=r.Qt,Quickshell=r.Quickshell,Hyprland=r.Hyprland,FileViewError={FileNotFound:"ENOENT"};';
454
+ const ref = (record) =>
455
+ record.external ?? `${generatedPrefix}n${record.index}`;
456
+ function context(
457
+ doc,
458
+ parent = null,
459
+ chain = [],
460
+ singletonUrl = null,
461
+ inline = null,
462
+ outer = null,
463
+ ) {
464
+ const ctx = {
465
+ doc,
466
+ ids: new Map(),
467
+ values: new Map(),
468
+ index: contexts.length,
469
+ root: null,
470
+ singletonUrl,
471
+ template,
472
+ outer,
473
+ };
474
+ contexts.push(ctx);
475
+ build(inline ?? doc.ast.root, parent, ctx, chain);
476
+ if (singletonUrl) singletons.set(singletonUrl, ctx.root);
477
+ for (const [name, value] of doc.values) {
478
+ if (value.kind === 'singleton') {
479
+ if (!singletons.has(value.url)) {
480
+ const savedTemplate = template;
481
+ template = null;
482
+ const child = context(
483
+ graph.documents.get(value.url),
484
+ null,
485
+ [],
486
+ value.url,
487
+ );
488
+ template = savedTemplate;
489
+ const record = child.root;
490
+ record.element.style.display = 'none';
491
+ record.nonvisual = true;
492
+ singletons.set(value.url, record);
493
+ }
494
+ ctx.values.set(name, ref(singletons.get(value.url)));
495
+ } else ctx.values.set(name, script(value.url, ctx));
496
+ }
497
+ for (const alias of doc.builtins.keys())
498
+ ctx.values.set(`${alias}.Text`, 'Text');
499
+ return ctx;
500
+ }
501
+ const libraryScripts = new Map();
502
+ function script(href, ctx, chain = [], libraryParent = false) {
503
+ if (chain.includes(href)) throw Error(`Cyclic JS import: ${href}`);
504
+ const item = graph.scripts.get(href);
505
+ if (!item) throw Error(`Missing JS resource ${href}`);
506
+ const library = item.library || libraryParent;
507
+ const cache = library ? libraryScripts : (ctx.scriptCache ??= new Map());
508
+ if (cache.has(href)) return cache.get(href);
509
+ const deps = item.deps
510
+ .map(
511
+ (d) =>
512
+ `const ${d.alias}=${script(d.url, ctx, [...chain, href], library)};`,
513
+ )
514
+ .join('\n');
515
+ const name = `${generatedPrefix}j${scripts.length}`;
516
+ scripts.push({ name, deps, item, ctx, library, href });
517
+ cache.set(href, name);
518
+ return name;
519
+ }
520
+ function addProperties(record, object, ctx) {
521
+ for (const p of object.properties) {
522
+ if (p.key === 'id') {
523
+ if (!/^[A-Za-z_][\w]*$/.test(p.code) || ctx.ids.has(p.code))
524
+ throw Error(`Invalid or duplicate QML id: ${p.code}`);
525
+ ctx.ids.set(p.code, ref(record));
526
+ record.element.id =
527
+ ctx.index === 0 && !template
528
+ ? p.code
529
+ : `qml-${ctx.index}-${p.code}`;
530
+ continue;
531
+ }
532
+ if (['__proto__', 'constructor', 'prototype'].includes(p.key))
533
+ throw Error(`Invalid QML property: ${p.key}`);
534
+ if (p.custom || p.method || p.signal) {
535
+ if (
536
+ p.custom &&
537
+ ![
538
+ 'int',
539
+ 'real',
540
+ 'bool',
541
+ 'string',
542
+ 'var',
543
+ 'color',
544
+ 'alias',
545
+ 'double',
546
+ 'url',
547
+ 'point','vector2d','vector3d','vector4d',
548
+ 'size',
549
+ 'rect',
550
+ ].includes(p.propertyType) &&
551
+ !/^[A-Z]/.test(p.propertyType)
552
+ )
553
+ throw Error(`Unsupported QML property type: ${p.propertyType}`);
554
+ if (
555
+ record.properties.has(p.key) &&
556
+ record.properties.get(p.key).p.custom
557
+ )
558
+ throw Error(`Duplicate QML property: ${p.key}`);
559
+ record.names.add(p.key);
560
+ } else if (
561
+ !record.names.has(p.key) &&
562
+ !groups.has(p.key) &&
563
+ !/^(?:Keys\.|Accessible\.|Drag\.|Window\.|ListView\.|GridView\.)?on[A-Z_]/.test(
564
+ p.key,
565
+ ) &&
566
+ !['Component.onCompleted', 'Component.onDestruction'].includes(p.key)
567
+ )
568
+ throw Error(
569
+ `Unsupported QML property: ${record.type}.${p.key} (${ctx.doc.url})`,
570
+ );
571
+ if (p.propertyType === 'alias') record.aliases.set(p.key, { p, ctx });
572
+ const previous = record.properties.get(p.key);
573
+ // Each component layer connects its own signal handler. Overriding a
574
+ // property binding must not discard the base component's connections.
575
+ if (previous && !p.method && !p.signal &&
576
+ (/^(?:Keys\.|Accessible\.|Drag\.|Window\.|ListView\.|GridView\.)?on[A-Z_]/.test(p.key) ||
577
+ ['Component.onCompleted', 'Component.onDestruction'].includes(p.key)))
578
+ (record.inheritedHandlers ??= []).push(previous);
579
+ if (previous?.p.readonly && !p.custom)
580
+ throw Error(`Cannot override readonly QML property: ${p.key}`);
581
+ record.properties.set(p.key, {
582
+ p:
583
+ record.aliases.has(p.key) && p.propertyType !== 'alias'
584
+ ? { ...p, aliasOverride: true }
585
+ : previous?.p.custom && !p.custom
586
+ ? {
587
+ ...p,
588
+ custom: true,
589
+ propertyType: previous.p.propertyType,
590
+ defaultProperty: previous.p.defaultProperty,
591
+ }
592
+ : p,
593
+ ctx,
594
+ });
595
+ }
596
+ }
597
+ function aliasSlot(record, key, seen = new Set()) {
598
+ const token = record.index + ':' + key;
599
+ if (seen.has(token)) throw Error('Cyclic property alias');
600
+ seen.add(token);
601
+ const entry = record.aliases.get(key);
602
+ if (!entry) return null;
603
+ const { p, ctx } = entry,
604
+ ast = p.ast;
605
+ if (
606
+ ast.type !== 'MemberExpression' ||
607
+ ast.object.type !== 'Identifier' ||
608
+ ast.computed
609
+ )
610
+ return null;
611
+ const target = records.find(
612
+ (r) => ref(r) === ctx.ids.get(ast.object.name),
613
+ );
614
+ if (!target) return null;
615
+ if (['data', 'children'].includes(ast.property.name)) return target;
616
+ return aliasSlot(target, ast.property.name, seen);
617
+ }
618
+ function defaultSlot(record) {
619
+ const entry = [...record.aliases.values()].find(
620
+ ({ p }) => p.defaultProperty,
621
+ );
622
+ if (!entry) return record;
623
+ const target = aliasSlot(record, entry.p.key);
624
+ if (!target)
625
+ throw Error('Default property must reference a child collection');
626
+ return target;
627
+ }
628
+ function buildPropertyObject(p, record, ctx, chain) {
629
+ const slot = aliasSlot(record, p.key);
630
+ const child = build(
631
+ ['sourceComponent','cursorDelegate'].includes(p.key) && p.object.type !== 'Component'
632
+ ? { type: 'Component', properties: [], children: [p.object] }
633
+ : p.object,
634
+ slot ?? record,
635
+ ctx,
636
+ chain,
637
+ );
638
+ record.properties.set(p.key, {
639
+ p: {
640
+ ...record.properties.get(p.key).p,
641
+ reference: child,
642
+ collectionSlot: !!slot,
643
+ },
644
+ ctx,
645
+ });
646
+ }
647
+ function build(object, parent, ctx, chain) {
648
+ const inline = ctx.doc.inlineScopes?.get(object)?.get(object.type);
649
+ const resolution = inline
650
+ ? { url: ctx.doc.url + '#inline-' + object.type }
651
+ : ctx.doc.types.get(object.type);
652
+ if (resolution?.url) {
653
+ if (chain.includes(resolution.url) || chain.length > 48)
654
+ throw Error(
655
+ `Recursive QML component: ${[...chain, resolution.url].join(' -> ')}`,
656
+ );
657
+ const inner = context(
658
+ inline ? ctx.doc : graph.documents.get(resolution.url),
659
+ parent,
660
+ [...chain, resolution.url],
661
+ null,
662
+ inline,
663
+ inline ? ctx : null,
664
+ );
665
+ const record = inner.root;
666
+ record.tags.add(object.type.split('.').at(-1));
667
+ if (!ctx.root) ctx.root = record;
668
+ addProperties(record, object, ctx);
669
+ for (const p of object.properties)
670
+ if (
671
+ p.object &&
672
+ !(
673
+ ['Instantiator', 'Repeater', 'ListView', 'GridView'].includes(record.type) &&
674
+ p.key === 'delegate'
675
+ )
676
+ )
677
+ buildPropertyObject(p, record, ctx, chain);
678
+ const slot = defaultSlot(record);
679
+ for (const child of object.children) build(child, slot, ctx, chain);
680
+ return record;
681
+ }
682
+ const type = resolution?.builtin ?? object.type;
683
+ if (!types.has(type)) throw Error(`Unsupported QML type: ${object.type}`);
684
+ if (records.length >= 2000) throw Error('QML expanded object limit');
685
+ const svgTag = { ShapePath: 'g', PathMultiline: 'path', PathSvg: 'path', PathRectangle: 'rect' }[
686
+ type
687
+ ];
688
+ const element = svgTag
689
+ ? document.createElementNS('http://www.w3.org/2000/svg', svgTag)
690
+ : document.createElement(
691
+ ['TextField', 'TextInput'].includes(type)
692
+ ? 'input'
693
+ : type === 'TextEdit'
694
+ ? 'textarea'
695
+ : type === 'Image'
696
+ ? 'div'
697
+ : type === 'Button'
698
+ ? 'button'
699
+ : 'div',
700
+ );
701
+ const record = {
702
+ index: records.length,
703
+ type,
704
+ parent,
705
+ ctx,
706
+ element,
707
+ template,
708
+ bindingTarget: object.bindingTarget,
709
+ tags: new Set([
710
+ type,
711
+ ...(!nonvisualTypes.has(type) ? ['Item'] : []),
712
+ 'QtObject',
713
+ ]),
714
+ names: new Set([
715
+ 'objectName',
716
+ ...(nonvisualTypes.has(type) ? [] : itemProperties(type)),
717
+ ...(typeProperties[type] ?? []),
718
+ ]),
719
+ properties: new Map(),
720
+ aliases: new Map(),
721
+ nonvisual: nonvisualTypes.has(type),
722
+ };
723
+ records.push(record);
724
+ if (!ctx.root) {
725
+ ctx.root = record;
726
+ if (ctx.singletonUrl) singletons.set(ctx.singletonUrl, record);
727
+ }
728
+ if (record.nonvisual)
729
+ element.style.display = type === 'ShellRoot' ? 'contents' : 'none';
730
+ element.setAttribute('data-qml-node', String(record.index));
731
+ element.setAttribute('data-qml-type', type);
732
+ if (type === 'TextField') element.setAttribute('type', 'text');
733
+ if (type === 'Button') element.setAttribute('type', 'button');
734
+ (parent?.element ?? document.body).appendChild(element);
735
+ addProperties(record, object, ctx);
736
+ if (['Instantiator', 'Repeater', 'ListView', 'GridView', 'Component'].includes(type)) {
737
+ const delegate =
738
+ type === 'Component'
739
+ ? object.children[0]
740
+ : (record.properties.get('delegate')?.p.object ??
741
+ (['Repeater', 'Instantiator'].includes(type) ? object.children[0] : null));
742
+ if (!delegate) throw Error(type + ' requires an object delegate');
743
+ if (type === 'Component' && object.children.length !== 1)
744
+ throw Error('Component requires exactly one root');
745
+ record.properties.delete('delegate');
746
+ const outer = template;
747
+ const group = {
748
+ owner: record,
749
+ outer,
750
+ root: null,
751
+ kind:
752
+ type === 'Component'
753
+ ? 'component'
754
+ : type === 'Instantiator'
755
+ ? 'instantiator'
756
+ : type === 'Repeater'
757
+ ? 'repeater'
758
+ : 'view',
759
+ };
760
+ template = group;
761
+ const delegateContext = {
762
+ ...ctx,
763
+ ids: new Map(),
764
+ outer: ctx,
765
+ template: group,
766
+ };
767
+ // Delegate IDs are local to each factory invocation; outer IDs remain visible.
768
+ for (const p of delegate.properties)
769
+ if (p.key === 'id') delegateContext.ids.delete(p.code);
770
+ group.root = build(
771
+ delegate,
772
+ ['Instantiator', 'ListView', 'GridView'].includes(type) ? record : parent,
773
+ delegateContext,
774
+ chain,
775
+ );
776
+ group.root.element.remove();
777
+ group.root.names.add('index');
778
+ group.root.names.add('modelData');
779
+ record.delegate = group;
780
+ template = outer;
781
+ }
782
+ for (const p of object.properties)
783
+ if (
784
+ p.object &&
785
+ !(
786
+ ['Instantiator', 'Repeater', 'ListView', 'GridView'].includes(record.type) &&
787
+ p.key === 'delegate'
788
+ )
789
+ )
790
+ buildPropertyObject(p, record, ctx, chain);
791
+ if (
792
+ type !== 'Component' &&
793
+ !(
794
+ ['Repeater', 'Instantiator'].includes(type) &&
795
+ !object.properties.some((p) => p.key === 'delegate')
796
+ )
797
+ )
798
+ for (const child of object.children) build(child, record, ctx, chain);
799
+ return record;
800
+ }
801
+ let entryContext;
802
+ entryContext = context(graph.documents.get(graph.entry), null, [
803
+ graph.entry,
804
+ ]);
805
+ for (const record of records)
806
+ for (const { p } of record.properties.values())
807
+ if (
808
+ p.required &&
809
+ !(component && record === entryContext.root) &&
810
+ !(
811
+ record.template?.kind === 'component' &&
812
+ record.template.root === record
813
+ ) &&
814
+ !(
815
+ record.template?.root === record && record.template.kind !== 'component'
816
+ )
817
+ )
818
+ throw Error(`Missing required QML property: ${p.key}`);
819
+ // Local createComponent factories already belong to a running QML page.
820
+ // Do not parse a second copy of the entire runtime for every lazy pane.
821
+ const runtimeSource = component
822
+ ? 'globalThis.__qmlRuntime'
823
+ : `globalThis.__qmlRuntime??=(${createQmlRuntime.toString()})((${createReactive.toString()})(),(${createQt.toString()})(),(${createQmlModels.toString()}),(${createQmlText.toString()}),(${createQmlShapes.toString()}),(${createQmlImages.toString()}),(${createQmlTextures.toString()}),(${createQmlMedia.toString()}),(${createQmlCanvas.toString()}),(${createQmlShortcuts.toString()}),(${createQmlShaderEffects.toString()}))`;
824
+ const code = [
825
+ `(()=>{const r=${runtimeSource};if(!r)throw Error('QML component requires an initialized page');const ${runtimeRef}=r;`,
826
+ constants,
827
+ ];
828
+
829
+ function scopeFor(record, ctx) {
830
+ const scope = new Map();
831
+ const namespaces = new Map();
832
+ for (const [name, value] of ctx.values) {
833
+ const parts = name.split('.');
834
+ if (parts.length === 1) scope.set(name, value);
835
+ else {
836
+ if (!namespaces.has(parts[0])) namespaces.set(parts[0], []);
837
+ namespaces
838
+ .get(parts[0])
839
+ .push(`[${JSON.stringify(parts[1])}]:${value}`);
840
+ }
841
+ }
842
+ for (const [name, parts] of namespaces)
843
+ scope.set(name, `({${parts.join(',')}})`);
844
+ const idScopes = [];
845
+ for (let c = ctx; c; c = c.outer) idScopes.unshift(c);
846
+ for (const c of idScopes)
847
+ for (const [name, value] of c.ids) scope.set(name, value);
848
+ // A local component instance can acquire properties at its use site;
849
+ // children declared there must resolve those enclosing properties too.
850
+ const ancestors = [];
851
+ for (let p = record; p; p = p.parent)
852
+ if (p.ctx === ctx || p === record ||
853
+ [...p.properties.values()].some(entry => entry.ctx === ctx)) ancestors.unshift(p);
854
+ if (!ancestors.includes(ctx.root)) ancestors.unshift(ctx.root);
855
+ for (const p of ancestors)
856
+ for (const key of p.names) scope.set(key, `${ref(p)}.${key}`);
857
+ // Component IDs win over same-named properties (e.g. a caption string and caption Text).
858
+ for (const c of idScopes)
859
+ for (const [name, value] of c.ids) scope.set(name, value);
860
+ scope.set(
861
+ 'parent',
862
+ record.parent || record.template || component
863
+ ? `${ref(record)}.parent`
864
+ : runtimeRef + '.viewport',
865
+ );
866
+ for (const name of ['ListView', 'GridView'])
867
+ scope.set(name, `({...${name},view:${ref(record)}.${name}.view})`);
868
+ scope.set('console', runtimeRef + '.console');
869
+ scope.set('Qt', `${runtimeRef}.qt(${JSON.stringify(ctx.doc.url)})`);
870
+ return scope;
871
+ }
872
+ const scriptCode = new Map();
873
+ for (const { name, deps, item, ctx, library, href } of scripts) {
874
+ const scope = library ? new Map() : scopeFor(ctx.root, ctx);
875
+ // Library scripts have no component IDs, but still receive Qt services.
876
+ scope.set('Qt', `${runtimeRef}.qt(${JSON.stringify(href)})`);
877
+ scope.set('console', runtimeRef + '.console');
878
+ for (const dep of item.deps) scope.delete(dep.alias);
879
+ const body = expression(
880
+ { code: item.source, ast: item.ast, block: false },
881
+ scope,
882
+ );
883
+ const scriptGroup = library ? null : ctx.template;
884
+ if (!scriptCode.has(scriptGroup)) scriptCode.set(scriptGroup, []);
885
+ scriptCode
886
+ .get(scriptGroup)
887
+ .push(
888
+ `const ${name}=(()=>{${deps}\n${body}\nreturn Object.defineProperties({}, {${item.exports.map((e) => `[${JSON.stringify(e.name)}]:{enumerable:true,get:()=>${e.name}${e.writable ? `,set:v=>{${e.name}=v;}` : ''}}`).join(',')}});})();`,
889
+ );
890
+ }
891
+ function generate(group) {
892
+ const members = records.filter((record) => record.template === group);
893
+ for (const record of members) {
894
+ if (
895
+ group &&
896
+ ![
897
+ 'Canvas',
898
+ 'ShaderEffect',
899
+ 'ShaderEffectSource',
900
+ 'LinearGradient',
901
+ 'XmlListModel',
902
+ 'XmlListModelRole',
903
+ 'WorkerScript',
904
+ 'LoggingCategory',
905
+ 'Flow',
906
+ 'GridLayout', 'RowLayout',
907
+ 'ColumnLayout',
908
+ 'ListView',
909
+ 'GridView',
910
+ 'ListModel',
911
+ 'DelegateModel',
912
+ 'DelegateModelGroup',
913
+ 'PointHandler',
914
+ 'Binding',
915
+ 'Gradient',
916
+ 'GradientStop',
917
+ 'PathMultiline', 'PathRectangle',
918
+ 'IpcHandler',
919
+ 'DropArea',
920
+ 'DragHandler',
921
+ 'FrameAnimation',
922
+ 'Image',
923
+ 'Flickable',
924
+ 'TextInput',
925
+ 'TextEdit',
926
+ 'Behavior',
927
+ 'NumberAnimation',
928
+ 'PropertyAnimation',
929
+ 'PauseAnimation',
930
+ 'SequentialAnimation',
931
+ 'ParallelAnimation',
932
+ 'ScriptAction',
933
+ 'PropertyAction',
934
+ 'Timer',
935
+ 'Connections',
936
+ 'Socket',
937
+ 'Process',
938
+ 'StdioCollector',
939
+ 'SplitParser',
940
+ 'FileView',
941
+ 'TextMetrics',
942
+ 'FontMetrics',
943
+ 'QtObject',
944
+ 'Item',
945
+ 'Rectangle',
946
+ 'Text',
947
+ 'Label',
948
+ 'TextField',
949
+ 'Button',
950
+ 'MouseArea',
951
+ 'Column',
952
+ 'Row',
953
+ 'Grid',
954
+ 'Shape',
955
+ 'ShapePath',
956
+ 'PathSvg',
957
+ 'Scale',
958
+ 'Translate',
959
+ 'FocusScope',
960
+ 'Loader',
961
+ 'Component',
962
+ 'Shortcut', 'Instantiator', 'Repeater',
963
+ 'TapHandler',
964
+ 'HoverHandler',
965
+ ].includes(record.type)
966
+ )
967
+ throw Error(
968
+ 'Unsupported service inside Repeater delegate: ' + record.type,
969
+ );
970
+ let persistent = false;
971
+ for (let a = record; a; a = a.parent)
972
+ if (a.ctx.singletonUrl) persistent = true;
973
+ const parent =
974
+ group?.kind === 'component' && record === group.root
975
+ ? 'mount'
976
+ : record.parent
977
+ ? ref(record.parent)
978
+ : component && record === entryContext.root
979
+ ? 'mount'
980
+ : 'null';
981
+ if (group || component)
982
+ code.push(
983
+ `const ${ref(record)}=r.create(${JSON.stringify(record.type)},${record.index},${parent},${record === group?.root && group.kind === 'repeater' ? ref(group.owner) : 'null'},${persistent});`,
984
+ );
985
+ else
986
+ code.push(
987
+ `const ${ref(record)}=r.make(document.querySelector('[data-qml-node="${record.index}"]'),${JSON.stringify(record.type)},${parent});`,
988
+ );
989
+ }
990
+ for (const record of members)
991
+ for (const { p } of record.properties.values())
992
+ if (p.custom && p.propertyType !== 'alias') {
993
+ const initial = ['int', 'real', 'double'].includes(p.propertyType)
994
+ ? '0'
995
+ : p.propertyType === 'bool'
996
+ ? 'false'
997
+ : ['string', 'url'].includes(p.propertyType)
998
+ ? '""'
999
+ : p.propertyType === 'color'
1000
+ ? '"transparent"'
1001
+ : p.propertyType === 'size' ? '({width:0,height:0})'
1002
+ : p.propertyType === 'point' ? '({x:0,y:0})'
1003
+ : p.propertyType === 'rect' ? '({x:0,y:0,width:0,height:0})'
1004
+ : p.propertyType === 'vector2d' ? '({x:0,y:0})'
1005
+ : p.propertyType === 'vector3d' ? '({x:0,y:0,z:0})'
1006
+ : p.propertyType === 'vector4d' ? '({x:0,y:0,z:0,w:0})'
1007
+ : p.propertyType === 'var'
1008
+ ? 'undefined'
1009
+ : 'null';
1010
+ code.push(`${ref(record)}[${JSON.stringify(p.key)}]=${initial};`);
1011
+ }
1012
+ for (const record of members)
1013
+ code.push(
1014
+ `r.describe(${ref(record)},${JSON.stringify([...record.tags])},${JSON.stringify([...record.properties.keys()])},${JSON.stringify(record.ctx.doc.url)},${JSON.stringify([...record.properties.values()].filter(({ p }) => p.readonly).map(({ p }) => p.key))});`,
1015
+ );
1016
+ for (const record of members)
1017
+ if (record.type === 'Behavior') {
1018
+ const animation = members.find((child) => child.parent === record);
1019
+ if (!record.parent || !record.bindingTarget || !animation)
1020
+ throw Error('Behavior needs a target property and animation');
1021
+ code.push(
1022
+ `r.behavior(${ref(record.parent)},${JSON.stringify(record.bindingTarget)},${ref(record)},${ref(animation)});`,
1023
+ );
1024
+ }
1025
+ if (!group)
1026
+ for (const [url, record] of singletons)
1027
+ if (!record.external)
1028
+ code.push(`r.share(${JSON.stringify(url)},${ref(record)});`);
1029
+ code.push(...(scriptCode.get(group) ?? []));
1030
+ if (group && group.kind !== 'component')
1031
+ code.push(
1032
+ `${ref(group.root)}.index=index;${ref(group.root)}.modelData=modelData;r.attachDelegate(${ref(group.root)},index,${ref(group.owner)});`,
1033
+ );
1034
+ if(group && group.kind!=='component'){
1035
+ for(const {p} of group.root.properties.values())if(p.required&&!['index','modelData'].includes(p.key)){
1036
+ code.push(`if(modelData==null||!Object.hasOwn(Object(modelData),${JSON.stringify(p.key)}))throw Error('Missing model role: '+${JSON.stringify(p.key)});`);
1037
+ code.push(`r.bind(${ref(group.root)},${JSON.stringify(p.key)},()=>modelData[${JSON.stringify(p.key)}],false,${JSON.stringify(p.propertyType)});`);
1038
+ }
1039
+ }
1040
+ for (const record of members)
1041
+ if (record.delegate) {
1042
+ code.push(
1043
+ record.delegate.kind === 'component'
1044
+ ? `r.component(${ref(record)},(mount,properties={})=>{`
1045
+ : `r.delegate(${ref(record)},(index,modelData)=>{`,
1046
+ );
1047
+ generate(record.delegate);
1048
+ code.push(`return ${ref(record.delegate.root)};});`);
1049
+ }
1050
+ // Methods/aliases exist before any property bindings read them.
1051
+ for (const record of members)
1052
+ for (const { p, ctx } of record.properties.values()) {
1053
+ const scope = scopeFor(record, ctx);
1054
+ if (p.signal) code.push(`${ref(record)}.${p.key}=r.signal();`);
1055
+ if (p.reference && !p.collectionSlot && !p.aliasOverride)
1056
+ code.push(`${ref(record)}.${p.key}=${ref(p.reference)};`);
1057
+ if (p.method) {
1058
+ const expr = expression(p, scope);
1059
+ if (
1060
+ record.type === 'Connections' &&
1061
+ /^(?:Keys\.|Accessible\.|Drag\.|Window\.|ListView\.|GridView\.)?on[A-Z_]/.test(
1062
+ p.key,
1063
+ )
1064
+ )
1065
+ code.push(
1066
+ `r.on(${ref(record)},${JSON.stringify(p.key)},(${expr}));`,
1067
+ );
1068
+ else code.push(`${ref(record)}.${p.key}=(${expr});`);
1069
+ }
1070
+ }
1071
+ for (const record of members)
1072
+ for (const { p, ctx } of record.aliases.values()) {
1073
+ const scope = scopeFor(record, ctx);
1074
+ if (p.propertyType === 'alias') {
1075
+ if (!['Identifier', 'MemberExpression'].includes(p.ast.type))
1076
+ throw Error(`Invalid QML alias ${p.key}`);
1077
+ const aliasScope = new Map(scope);
1078
+ for (let c = ctx; c; c = c.outer)
1079
+ for (const [name, id] of c.ids)
1080
+ if (!aliasScope.has(name) || ctx.ids.has(name))
1081
+ aliasScope.set(name, id);
1082
+ const value = expression(p, aliasScope);
1083
+ code.push(
1084
+ `r.alias(${ref(record)},${JSON.stringify(p.key)},()=>(${value}),v=>{${value}=v;},${!!p.readonly});`,
1085
+ );
1086
+ }
1087
+ }
1088
+ const completed = [];
1089
+ for (const record of members) {
1090
+ const n = ref(record),
1091
+ override =
1092
+ (component && record === entryContext.root) ||
1093
+ (group?.kind === 'component' && record === group.root);
1094
+ for (const dimension of ['width', 'height'])
1095
+ if (!record.nonvisual && !record.properties.has(dimension)) {
1096
+ const implicit =
1097
+ dimension === 'width' ? 'implicitWidth' : 'implicitHeight';
1098
+ code.push(
1099
+ `${override ? `if(!Object.hasOwn(properties,'${dimension}')) ` : ''}r.bind(${n},'${dimension}',()=>r.dimension(${n},${JSON.stringify(dimension)},${!record.parent || record.type === 'FloatingWindow'}));`,
1100
+ );
1101
+ }
1102
+ for (const axis of ['x', 'y'])
1103
+ if (!record.properties.has(axis) && !record.nonvisual)
1104
+ code.push(
1105
+ `${override ? `if(!Object.hasOwn(properties,'${axis}')) ` : ''}r.bind(${n},'${axis}',()=>r.position(${n},'${axis}'));`,
1106
+ );
1107
+ for (const { p, ctx } of [...(record.inheritedHandlers ?? []), ...record.properties.values()]) {
1108
+ if (p.method || p.signal || p.propertyType === 'alias' || p.required)
1109
+ continue;
1110
+ if (p.collectionSlot) continue;
1111
+ if (p.reference) {
1112
+ code.push(
1113
+ `r.${p.aliasOverride ? 'bindAlias' : 'bind'}(${n},${JSON.stringify(p.key)},()=>${ref(p.reference)},${!!p.readonly});`,
1114
+ );
1115
+ continue;
1116
+ }
1117
+ const value = expression(p, scopeFor(record, ctx));
1118
+ if (p.aliasOverride) {
1119
+ code.push(
1120
+ `r.bindAlias(${n},${JSON.stringify(p.key)},()=>(${value}));`,
1121
+ );
1122
+ continue;
1123
+ }
1124
+ if (record.type === 'ScriptAction' && p.key === 'script') {
1125
+ code.push(`r.on(${n},'script',()=>{${value};});`);
1126
+ continue;
1127
+ }
1128
+ if (p.key === 'Component.onDestruction') {
1129
+ code.push(`r.on(${n},'Component.onDestruction',()=>{${value};});`);
1130
+ continue;
1131
+ }
1132
+ if (p.key === 'Component.onCompleted') {
1133
+ completed.push(`${value};`);
1134
+ continue;
1135
+ }
1136
+ if (
1137
+ /^(?:Keys\.|Accessible\.|Drag\.|Window\.|ListView\.|GridView\.)?on[A-Z_]/.test(
1138
+ p.key,
1139
+ ) &&
1140
+ p.key !== 'onClicked'
1141
+ ) {
1142
+ const handler = [
1143
+ 'FunctionExpression',
1144
+ 'ArrowFunctionExpression',
1145
+ ].includes(p.ast?.type)
1146
+ ? value
1147
+ : `function(event,button){const mouse=event,wheel=event;${value};}`;
1148
+ code.push(`r.on(${n},${JSON.stringify(p.key)},${handler});`);
1149
+ continue;
1150
+ }
1151
+ if (p.key === 'onClicked') {
1152
+ const handler = [
1153
+ 'FunctionExpression',
1154
+ 'ArrowFunctionExpression',
1155
+ ].includes(p.ast?.type)
1156
+ ? value
1157
+ : `function(event){const mouse=event;${value};}`;
1158
+ code.push(`r.on(${n},'onClicked',${handler});`);
1159
+ continue;
1160
+ }
1161
+ const parts = p.key.split('.'),
1162
+ key = parts.pop(),
1163
+ target = n + (parts.length ? '.' + parts.join('.') : '');
1164
+ code.push(
1165
+ `${override ? `if(!Object.hasOwn(properties,${JSON.stringify(p.key)})) ` : ''}r.bind(${target},${JSON.stringify(key)},()=>(${value}),${!!p.readonly},${JSON.stringify(p.propertyType)});`,
1166
+ );
1167
+ }
1168
+ // Nonvisual DOM nodes are already hidden. Their QML visible property
1169
+ // may be an application-defined readonly value; never overwrite it.
1170
+ }
1171
+ if ((component && !group) || group?.kind === 'component') {
1172
+ const root = group?.root ?? entryContext.root;
1173
+ for (const { p } of root.properties.values())
1174
+ if (p.required)
1175
+ code.push(
1176
+ `if(!Object.hasOwn(properties,${JSON.stringify(p.key)}))throw Error('Missing required QML property: '+${JSON.stringify(p.key)});`,
1177
+ );
1178
+ code.push(`r.apply(${ref(root)},properties);`);
1179
+ }
1180
+ code.push('r.start();', ...completed);
1181
+ }
1182
+ generate(null);
1183
+ code.push(`return ${ref(entryContext.root)};})();`);
1184
+ const scriptElement = document.createElement('script');
1185
+ scriptElement.textContent = code.join('\n');
1186
+ document.body.appendChild(scriptElement);
1187
+ } catch (error) {
1188
+ document.body.replaceChildren();
1189
+ document.body.style.cssText = 'margin:16px';
1190
+ const heading = document.createElement('h1');
1191
+ heading.textContent = 'QML experiment';
1192
+ const message = document.createElement('p');
1193
+ message.textContent = String(error.message);
1194
+ const pre = document.createElement('pre');
1195
+ pre.textContent = source;
1196
+ document.body.append(heading, message, pre);
1197
+ }
1198
+ return { window, document };
1199
+ }