buninu-browser 0.2.4 → 0.4.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 (249) hide show
  1. package/LICENSES/jsmdcui.txt +22 -0
  2. package/README.md +102 -2
  3. package/frontends/wayland/LICENSE-land +21 -0
  4. package/frontends/wayland/README.md +123 -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 +116 -0
  20. package/frontends/wayland/input.js +56 -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 +158 -0
  26. package/index.js +28 -1
  27. package/lib/clipboard-command.js +10 -0
  28. package/lib/clipboard-powershell.js +22 -0
  29. package/lib/clipboard.js +57 -0
  30. package/lib/document-types.js +11 -0
  31. package/lib/headless-shell.js +78 -13
  32. package/lib/layout/text-layout.js +17 -9
  33. package/lib/paint/display-list.js +5 -1
  34. package/lib/paint/frame.js +35 -3
  35. package/lib/paint/raw-frames.js +47 -0
  36. package/lib/qml/README.md +971 -0
  37. package/lib/qml/builtins.js +700 -0
  38. package/lib/qml/canvas-dom.js +115 -0
  39. package/lib/qml/document.js +1176 -0
  40. package/lib/qml/format.js +9 -0
  41. package/lib/qml/host.js +324 -0
  42. package/lib/qml/images.js +199 -0
  43. package/lib/qml/launcher.js +132 -0
  44. package/lib/qml/loader.js +517 -0
  45. package/lib/qml/media-renderer.js +49 -0
  46. package/lib/qml/media.js +86 -0
  47. package/lib/qml/models.js +622 -0
  48. package/lib/qml/parser.js +381 -0
  49. package/lib/qml/qs +5 -0
  50. package/lib/qml/qscasty +3 -0
  51. package/lib/qml/qsheadless +3 -0
  52. package/lib/qml/qswayland +3 -0
  53. package/lib/qml/reactive.js +159 -0
  54. package/lib/qml/runtime.js +3381 -0
  55. package/lib/qml/shapes.js +243 -0
  56. package/lib/qml/text.js +109 -0
  57. package/lib/qml/textures.js +123 -0
  58. package/lib/qml/wayland-clipboard.js +32 -0
  59. package/lib/qml/worker-thread.js +70 -0
  60. package/lib/render-tree/index.js +16 -1
  61. package/lib/renderer/bindings.js +11 -0
  62. package/lib/renderer/canvas-2d.js +13 -0
  63. package/lib/renderer/compositor-channel.js +103 -0
  64. package/lib/renderer/compositor.js +25 -33
  65. package/lib/renderer/flags.js +1 -1
  66. package/lib/renderer/host.js +128 -92
  67. package/lib/renderer/main.js +38 -4
  68. package/lib/renderer/page-realm.js +32 -1
  69. package/lib/renderer/page-renderer.js +250 -21
  70. package/lib/renderer/text-input-paint.js +54 -0
  71. package/lib/renderer/text-input.js +57 -0
  72. package/lib/style/computed-style.js +2 -2
  73. package/package.json +1 -1
  74. package/test/clipboard.test.js +54 -0
  75. package/test/fixtures/browser-raw-demo.html +36 -0
  76. package/test/fixtures/flea-helpers/Dockerfile +36 -0
  77. package/test/fixtures/flea-helpers/bat.mjs +43 -0
  78. package/test/fixtures/flea-helpers/bin/bat +3 -0
  79. package/test/fixtures/flea-helpers/bin/bwrap +10 -0
  80. package/test/fixtures/flea-helpers/bin/bwrap-real-filter +9 -0
  81. package/test/fixtures/flea-helpers/bin/glow +3 -0
  82. package/test/fixtures/flea-helpers/bin/qs +3 -0
  83. package/test/fixtures/flea-helpers/bin/unsupported +3 -0
  84. package/test/fixtures/flea-helpers/check-highlighting.mjs +47 -0
  85. package/test/fixtures/flea-helpers/check-media.mjs +29 -0
  86. package/test/fixtures/flea-helpers/check-preview.mjs +27 -0
  87. package/test/fixtures/flea-helpers/check.mjs +36 -0
  88. package/test/fixtures/flea-helpers/highlight-cases.json +227 -0
  89. package/test/fixtures/flea-helpers/source-mime.xml +28 -0
  90. package/test/fixtures/flea-helpers/syntax-compat.mjs +23 -0
  91. package/test/fixtures/flea-thumbnail-stub/flea-placeholder.thumbnailer +4 -0
  92. package/test/fixtures/flea-thumbnail-stub/flea-thumbnail-stub +3 -0
  93. package/test/fixtures/qml/flea/EscapeHint.qml +11 -0
  94. package/test/fixtures/qml/flea/LICENSE +21 -0
  95. package/test/fixtures/qml/flea/README.md +25 -0
  96. package/test/fixtures/qml/flea/Theme.qml +8 -0
  97. package/test/fixtures/qml/flea/demo.qml +24 -0
  98. package/test/fixtures/qml/flea/qmldir +3 -0
  99. package/test/fixtures/qml/flea-theme/LICENSE-Flea +21 -0
  100. package/test/fixtures/qml/flea-theme/LICENSE-Omarchy +20 -0
  101. package/test/fixtures/qml/flea-theme/README.md +70 -0
  102. package/test/fixtures/qml/flea-theme/button.qml +17 -0
  103. package/test/fixtures/qml/flea-theme/demo.qml +27 -0
  104. package/test/fixtures/qml/flea-theme/field.qml +21 -0
  105. package/test/fixtures/qml/flea-theme/imports/qs/Commons/BorderGeometry.js +373 -0
  106. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Color.qml +258 -0
  107. package/test/fixtures/qml/flea-theme/imports/qs/Commons/I18n.qml +118 -0
  108. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Numbers.js +19 -0
  109. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Plural.js +91 -0
  110. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Style.qml +561 -0
  111. package/test/fixtures/qml/flea-theme/imports/qs/Commons/Util.qml +188 -0
  112. package/test/fixtures/qml/flea-theme/imports/qs/Commons/qmldir +13 -0
  113. package/test/fixtures/qml/flea-theme/segment.qml +17 -0
  114. package/test/fixtures/qml/flea-theme/settings.qml +58 -0
  115. package/test/fixtures/qml/flea-theme/text-metrics.qml +10 -0
  116. package/test/fixtures/qml/flea-theme/ui/DialogButton.qml +150 -0
  117. package/test/fixtures/qml/flea-theme/ui/DialogField.qml +147 -0
  118. package/test/fixtures/qml/flea-theme/ui/EscapeHint.qml +11 -0
  119. package/test/fixtures/qml/flea-theme/ui/Glass.qml +114 -0
  120. package/test/fixtures/qml/flea-theme/ui/Glyph.qml +54 -0
  121. package/test/fixtures/qml/flea-theme/ui/GlyphSolid.qml +22 -0
  122. package/test/fixtures/qml/flea-theme/ui/Reveal.qml +54 -0
  123. package/test/fixtures/qml/flea-theme/ui/SettingsRuler.qml +85 -0
  124. package/test/fixtures/qml/flea-theme/ui/SettingsSegment.qml +85 -0
  125. package/test/fixtures/qml/flea-theme/ui/StableModel.qml +89 -0
  126. package/test/fixtures/qml/flea-theme/ui/Theme.qml +460 -0
  127. package/test/fixtures/qml/flea-theme/ui/ThemeColors.qml +43 -0
  128. package/test/fixtures/qml/flea-theme/ui/ViewState.qml +565 -0
  129. package/test/fixtures/qml/flea-theme/ui/js/Anchor.js +379 -0
  130. package/test/fixtures/qml/flea-theme/ui/js/AnchorHold.js +162 -0
  131. package/test/fixtures/qml/flea-theme/ui/js/Archive.js +157 -0
  132. package/test/fixtures/qml/flea-theme/ui/js/BackReturn.js +110 -0
  133. package/test/fixtures/qml/flea-theme/ui/js/Buttons.js +54 -0
  134. package/test/fixtures/qml/flea-theme/ui/js/ColumnMenu.js +111 -0
  135. package/test/fixtures/qml/flea-theme/ui/js/Columns.js +298 -0
  136. package/test/fixtures/qml/flea-theme/ui/js/Contrast.js +157 -0
  137. package/test/fixtures/qml/flea-theme/ui/js/Density.js +44 -0
  138. package/test/fixtures/qml/flea-theme/ui/js/DirSizes.js +129 -0
  139. package/test/fixtures/qml/flea-theme/ui/js/ExtThumbs.js +115 -0
  140. package/test/fixtures/qml/flea-theme/ui/js/Filter.js +284 -0
  141. package/test/fixtures/qml/flea-theme/ui/js/FolderSorts.js +108 -0
  142. package/test/fixtures/qml/flea-theme/ui/js/Format.js +305 -0
  143. package/test/fixtures/qml/flea-theme/ui/js/Icons.js +206 -0
  144. package/test/fixtures/qml/flea-theme/ui/js/Input.js +39 -0
  145. package/test/fixtures/qml/flea-theme/ui/js/KeyName.js +64 -0
  146. package/test/fixtures/qml/flea-theme/ui/js/Keymap.js +370 -0
  147. package/test/fixtures/qml/flea-theme/ui/js/Kinds.js +145 -0
  148. package/test/fixtures/qml/flea-theme/ui/js/Lists.js +11 -0
  149. package/test/fixtures/qml/flea-theme/ui/js/MakeDefault.js +186 -0
  150. package/test/fixtures/qml/flea-theme/ui/js/Match.js +27 -0
  151. package/test/fixtures/qml/flea-theme/ui/js/Material.js +109 -0
  152. package/test/fixtures/qml/flea-theme/ui/js/Menu.js +513 -0
  153. package/test/fixtures/qml/flea-theme/ui/js/Motion.js +16 -0
  154. package/test/fixtures/qml/flea-theme/ui/js/Mounts.js +320 -0
  155. package/test/fixtures/qml/flea-theme/ui/js/MouseNav.js +13 -0
  156. package/test/fixtures/qml/flea-theme/ui/js/Nav.js +328 -0
  157. package/test/fixtures/qml/flea-theme/ui/js/Palette.js +33 -0
  158. package/test/fixtures/qml/flea-theme/ui/js/Places.js +236 -0
  159. package/test/fixtures/qml/flea-theme/ui/js/Protocols.js +296 -0
  160. package/test/fixtures/qml/flea-theme/ui/js/RecentMode.js +154 -0
  161. package/test/fixtures/qml/flea-theme/ui/js/Search.js +396 -0
  162. package/test/fixtures/qml/flea-theme/ui/js/Settings.js +476 -0
  163. package/test/fixtures/qml/flea-theme/ui/js/SettingsAbout.js +99 -0
  164. package/test/fixtures/qml/flea-theme/ui/js/SettingsShelf.js +52 -0
  165. package/test/fixtures/qml/flea-theme/ui/js/Sort.js +127 -0
  166. package/test/fixtures/qml/flea-theme/ui/js/StateWords.js +42 -0
  167. package/test/fixtures/qml/flea-theme/ui/js/Swap.js +166 -0
  168. package/test/fixtures/qml/flea-theme/ui/js/TextSize.js +107 -0
  169. package/test/fixtures/qml/flea-theme/ui/js/Thumbs.js +158 -0
  170. package/test/fixtures/qml/flea-theme/ui/js/UiState.js +438 -0
  171. package/test/fixtures/qml/flea-theme/ui/js/Update.js +132 -0
  172. package/test/fixtures/qml/flea-theme/ui/js/UserDirs.js +56 -0
  173. package/test/fixtures/qml/flea-theme/ui/qmldir +227 -0
  174. package/test/fixtures/qml/hello.qml +26 -0
  175. package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabel.qml +2 -0
  176. package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabelNew.qml +2 -0
  177. package/test/fixtures/qml/imports/Demo/Toolkit/qmldir +3 -0
  178. package/test/fixtures/qml/imports/components/Badge.qml +15 -0
  179. package/test/fixtures/qml/imports/logic/base.js +2 -0
  180. package/test/fixtures/qml/imports/logic/counter.js +4 -0
  181. package/test/fixtures/qml/imports/logic/math.js +3 -0
  182. package/test/fixtures/qml/imports/logic/shared.js +3 -0
  183. package/test/fixtures/qml/imports/main.qml +21 -0
  184. package/test/fixtures/qml/imports/theme/Theme.qml +8 -0
  185. package/test/fixtures/qml/imports/theme/qmldir +2 -0
  186. package/test/fixtures/qml/multiply.qml +61 -0
  187. package/test/fixtures/qml-canvas-dom.js +90 -0
  188. package/test/fixtures/qml-media-audio.js +39 -0
  189. package/test/fixtures/raw-capture-benchmark.json +76 -0
  190. package/test/helpers/flea-sandbox.js +48 -0
  191. package/test/helpers/quickshell-fixture.js +125 -0
  192. package/test/html-clipboard-wayland.test.js +47 -0
  193. package/test/index.test.js +13 -0
  194. package/test/manual-casty-without-qml.js +87 -0
  195. package/test/manual-flea-application.js +49 -0
  196. package/test/manual-flea-binary.js +62 -0
  197. package/test/manual-flea-qml.js +9 -0
  198. package/test/manual-flea-settings.js +40 -0
  199. package/test/manual-flea-theme.js +40 -0
  200. package/test/manual-qml-removal.js +46 -0
  201. package/test/manual-without-qml.js +45 -0
  202. package/test/qml-canvas-dom.test.js +37 -0
  203. package/test/qml-clipboard-bridge.test.js +25 -0
  204. package/test/qml-clipboard-wayland.test.js +40 -0
  205. package/test/qml-connections-timing.test.js +73 -0
  206. package/test/qml-dynamic.test.js +401 -0
  207. package/test/qml-flea-app.test.js +146 -0
  208. package/test/qml-flea-backend.test.js +78 -0
  209. package/test/qml-flea-columns-backend.test.js +38 -0
  210. package/test/qml-flea-compile.test.js +78 -0
  211. package/test/qml-flea-launcher.test.js +88 -0
  212. package/test/qml-flea-markdown-backend.test.js +31 -0
  213. package/test/qml-flea-media.test.js +28 -0
  214. package/test/qml-flea-navigation-backend.test.js +35 -0
  215. package/test/qml-flea-navigation-timing.test.js +147 -0
  216. package/test/qml-flea-preview-backend.test.js +53 -0
  217. package/test/qml-flea-previews.test.js +163 -0
  218. package/test/qml-flea-rows.test.js +101 -0
  219. package/test/qml-flea-textures.test.js +59 -0
  220. package/test/qml-flea-view-switch.test.js +33 -0
  221. package/test/qml-flea-wayland.test.js +68 -0
  222. package/test/qml-flea.test.js +34 -0
  223. package/test/qml-host.test.js +238 -0
  224. package/test/qml-imports.test.js +102 -0
  225. package/test/qml-inherited-handlers.test.js +51 -0
  226. package/test/qml-input-selection.test.js +66 -0
  227. package/test/qml-interactions.test.js +309 -0
  228. package/test/qml-language.test.js +167 -0
  229. package/test/qml-launcher.test.js +54 -0
  230. package/test/qml-layout-text.test.js +205 -0
  231. package/test/qml-media.test.js +63 -0
  232. package/test/qml-persisted-delegates.test.js +45 -0
  233. package/test/qml-quickshell-controls.test.js +65 -0
  234. package/test/qml-quickshell-settings.test.js +62 -0
  235. package/test/qml-repeater.test.js +72 -0
  236. package/test/qml-style-updates.test.js +124 -0
  237. package/test/qml-textures.test.js +66 -0
  238. package/test/qml-views.test.js +332 -0
  239. package/test/qml-worker.test.js +69 -0
  240. package/test/qml.test.js +83 -0
  241. package/test/raw-capture.test.js +152 -0
  242. package/test/renderer-host.test.js +11 -0
  243. package/test/text-input-pointer.test.js +31 -0
  244. package/test/text-input-scroll.test.js +29 -0
  245. package/test/text-input.test.js +47 -0
  246. package/test/wayland-frontend.test.js +83 -0
  247. package/test/wayland-urlbar.test.js +38 -0
  248. package/test/window-resize.test.js +36 -0
  249. package/tools/bench-raw-capture.js +32 -0
@@ -0,0 +1,157 @@
1
+ .pragma library
2
+
3
+ .import "Format.js" as Format
4
+
5
+ // Which rows get an Extract, and what a new archive is called; the backend sends no is-archive flag, so this is the mechanism.
6
+
7
+ // The widened tail of EXTENSIONS, which 7z reads; tar and the zip class keep their own rules.
8
+ var SEVEN_TAILS = [".appimage", ".cpio", ".cab", ".jar", ".apk", ".rpm", ".deb", ".iso"]
9
+
10
+ // Longest form first, so ".tar.gz" matches before ".gz"; .rar is read here and never offered to write (src/backend/archive.rs).
11
+ var EXTENSIONS = [".tar.zst", ".tar.bz2", ".tar.gz", ".tar.xz", ".tgz", ".tar", ".zip", ".7z", ".rar"].concat(SEVEN_TAILS)
12
+
13
+ // A disk image lists in the archive preview and is never extracted.
14
+ var LIST_ONLY = [".iso"]
15
+
16
+ function isArchive(name) {
17
+ var lower = String(name).toLowerCase()
18
+ for (var i = 0; i < EXTENSIONS.length; i++) {
19
+ if (lower.length > EXTENSIONS[i].length && lower.indexOf(EXTENSIONS[i], lower.length - EXTENSIONS[i].length) !== -1) {
20
+ return true
21
+ }
22
+ }
23
+ return false
24
+ }
25
+
26
+ // The name an extract unpacks into: the archive's own, with every archive extension taken off.
27
+ function extractDir(name) {
28
+ var text = String(name)
29
+ var lower = text.toLowerCase()
30
+ for (var i = 0; i < EXTENSIONS.length; i++) {
31
+ if (lower.length > EXTENSIONS[i].length && lower.indexOf(EXTENSIONS[i], lower.length - EXTENSIONS[i].length) !== -1) {
32
+ return text.substring(0, text.length - EXTENSIONS[i].length)
33
+ }
34
+ }
35
+ return text
36
+ }
37
+
38
+ // One row compresses under its own name; several compress under the directory holding them.
39
+ function archiveStem(names, parentLeaf) {
40
+ if (names.length === 1) {
41
+ return stripExtension(names[0])
42
+ }
43
+ return parentLeaf.length > 0 ? parentLeaf : "archive"
44
+ }
45
+
46
+ function stripExtension(name) {
47
+ var text = String(name)
48
+ var cut = text.lastIndexOf(".")
49
+ return cut > 0 ? text.substring(0, cut) : text
50
+ }
51
+
52
+ var SHEET_COMPRESS = "Compress to .%1"
53
+
54
+ // The compress submenu is the probed table, so no 7zip means no .7z; the sheet reads each leaf "Compress to .zip" beside the board's file mark.
55
+ function formatEntries(formats) {
56
+ var out = []
57
+ for (var i = 0; i < formats.length; i++) {
58
+ out.push({ id: formats[i], label: "." + formats[i], sheetLabel: SHEET_COMPRESS.replace("%1", formats[i]), glyph: "file" })
59
+ }
60
+ return out
61
+ }
62
+
63
+ // A disk image by name, longest form first so ".img.xz" wins over ".img"; a zip needs one such member.
64
+ function imageKind(name) {
65
+ var lower = String(name || "").toLowerCase()
66
+ var table = [[".img.xz", "xz"], [".img.gz", "gz"], [".img.zst", "zst"], [".img.bz2", "bz2"],
67
+ [".iso", "iso"], [".img", "img"], [".raw", "raw"], [".zip", "zip"]]
68
+ for (var i = 0; i < table.length; i++) {
69
+ var suffix = table[i][0]
70
+ if (lower.length > suffix.length && lower.lastIndexOf(suffix) === lower.length - suffix.length)
71
+ return table[i][1]
72
+ }
73
+ return ""
74
+ }
75
+
76
+ function isDiskImage(name, zipMember) {
77
+ var kind = imageKind(name)
78
+ if (kind === "" || kind === "zip")
79
+ return kind === "zip" && imageKind(zipMember) !== "" && imageKind(zipMember) !== "zip"
80
+ return true
81
+ }
82
+
83
+ // Whether an installed tool reads this archive: .7z needs 7z, .zip and .rar either tool, tar bsdtar, a disk image none.
84
+ function canExtract(name, extraction) {
85
+ var lower = String(name).toLowerCase()
86
+ var caps = extraction || ({})
87
+ if (isListOnly(name)) return false
88
+ if (hasSuffix(lower, ".7z")) return caps.sevenZip === true
89
+ if (hasSuffix(lower, ".zip") || hasSuffix(lower, ".rar")) return caps.zip === true
90
+ if (isSevenSuffix(lower)) return caps.sevenZip === true
91
+ return caps.archive === true
92
+ }
93
+
94
+ // A disk image lists in the archive preview and is never extracted.
95
+ function isListOnly(name) {
96
+ var lower = String(name).toLowerCase()
97
+ for (var i = 0; i < LIST_ONLY.length; i++) {
98
+ if (hasSuffix(lower, LIST_ONLY[i])) return true
99
+ }
100
+ return false
101
+ }
102
+
103
+ // The seven-tail membership, read off the one table above.
104
+ function isSevenSuffix(lower) {
105
+ for (var i = 0; i < SEVEN_TAILS.length; i++) {
106
+ if (hasSuffix(lower, SEVEN_TAILS[i])) return true
107
+ }
108
+ return false
109
+ }
110
+
111
+ // The missing program for a disabled Extract: only bsdtar offers "tar" and only 7z offers "7z".
112
+ function extractHint(formats) {
113
+ var table = formats || []
114
+ if (table.indexOf("7z") >= 0) return "bsdtar is not installed"
115
+ if (table.indexOf("tar") >= 0) return "7-Zip is not installed"
116
+ return "No archive tool is installed"
117
+ }
118
+
119
+ // Menu.js's Extract row: absent for none and for a disk image, disabled with its reason for no reader.
120
+ function extractEntry(entry, p, count) {
121
+ if (!p.rowIsArchive || count !== 1) return false
122
+ if (isListOnly(p.archiveName || "")) return false
123
+ if (p.canExtract !== true) {
124
+ entry.disabled = true
125
+ entry.hint = extractHint(p.archiveFormats)
126
+ entry.hintWrap = true
127
+ }
128
+ return true
129
+ }
130
+
131
+ function hasSuffix(lower, suffix) {
132
+ return lower.length >= suffix.length && lower.indexOf(suffix, lower.length - suffix.length) !== -1
133
+ }
134
+
135
+ // The compress submenu's last row, which opens the password dialog; hidden where 7z is missing, since bsdtar writes no encryption.
136
+ function passwordEntry(formats) {
137
+ var table = formats || []
138
+ if (table.indexOf("7z") < 0) return []
139
+ return [{ separator: true },
140
+ { id: "compressPassword", label: "With password…", glyph: "key" }]
141
+ }
142
+
143
+ // The backend's two password codes, matched exactly the way "cancelled" routes, never as sentences.
144
+ function needsPassword(err) {
145
+ return String(err) === "encrypted"
146
+ }
147
+
148
+ function wrongPassword(err) {
149
+ return String(err) === "wrong-password"
150
+ }
151
+
152
+ // What an extract's terminal line does: "password" opens the card, "wrong" reopens it with its sentence, "" messages.
153
+ function doneAction(ok, err) {
154
+ if (!ok && needsPassword(err)) return "password"
155
+ if (!ok && wrongPassword(err)) return "wrong"
156
+ return ""
157
+ }
@@ -0,0 +1,110 @@
1
+ .pragma library
2
+
3
+ // Back and Forward entries and the landing ui/PaneSwap.qml runs in the rows' turn.
4
+
5
+ // A stack entry names the directory and the row its cursor stood on; bare paths read as empty rows.
6
+ function makeEntry(path, name, index) {
7
+ return { path: String(path), name: String(name || ""), index: Math.max(0, Math.floor(Number(index) || 0)) }
8
+ }
9
+
10
+ function pathOf(entry) {
11
+ return typeof entry === "string" ? entry : String(entry.path || "")
12
+ }
13
+
14
+ function asEntry(target) {
15
+ return typeof target === "string" ? makeEntry(target, "", 0) : target
16
+ }
17
+
18
+ // The place a listing still out asked for, with the cursor its Back or Forward owes it; null when none is out or it re-reads the drawn folder.
19
+ function pending(pane) {
20
+ if (!pane.listInFlight || !pane.listingPath || pane.listingPath === pane.path)
21
+ return null
22
+ var owed = pane.swap ? pane.swap.restore : null
23
+ if (owed && owed.path === pane.listingPath)
24
+ return makeEntry(owed.path, owed.name, owed.index)
25
+ return makeEntry(pane.listingPath, "", 0)
26
+ }
27
+
28
+ // A tab switch left its cursor, order and located row to its landing; the listing let go never lands, so a later one must not take them.
29
+ function dropOwed(pane) {
30
+ if (pane.tabs)
31
+ Object.assign(pane.tabs, { pendingCursor: -1, pendingCursorName: null, pendingLocatePath: "", pendingSortBy: "", pendingSortDesc: false })
32
+ }
33
+
34
+ // The stack a Back or Forward still out pushed the drawn place onto, or "" when the listing out is any other.
35
+ function pushedOnto(pane) {
36
+ var owed = pending(pane) && pane.swap ? pane.swap.restore : null
37
+ return owed && owed.pushedOnto ? owed.pushedOnto : ""
38
+ }
39
+
40
+ // The drawn folder with the cursor row it stands on; leaving a walk names no row, so the landing stays at the top.
41
+ function drawn(pane) {
42
+ var walk = pane.searchMode === "results" || (pane.searchFrom || "").length > 0
43
+ var row = !walk && pane.rowFor ? pane.rowFor(pane.cursorIndex) : null
44
+ return makeEntry(pane.path, row ? row.n : "", walk ? 0 : pane.cursorIndex)
45
+ }
46
+
47
+ // The departing cursor, or the pending place while a listing is out.
48
+ function capture(pane) {
49
+ return pending(pane) || drawn(pane)
50
+ }
51
+
52
+ // One step through the stacks after Nav's guard, popping before the open pushes back.
53
+ function travel(pane, from, to) {
54
+ var stack = pane[from]
55
+ var target = asEntry(stack[stack.length - 1])
56
+ pane[to] = (pane[to] || []).concat([capture(pane)])
57
+ pane[from] = stack.slice(0, -1)
58
+ pane.openWithoutHistory(pathOf(target), { restore: Object.assign({}, target, { pushedOnto: to }), supersede: true })
59
+ }
60
+
61
+ // The landing in the rows reply's turn: name, then clamped index, then the top.
62
+ function landRestore(pane, entry) {
63
+ if (!entry)
64
+ return
65
+ if (pane.path !== entry.path || (pane.searchMode || "").length > 0 || pane.total === 0)
66
+ return
67
+ var name = String(entry.name || "")
68
+ if (name.length > 0) {
69
+ for (var i = 0; i < pane.rows.length; i++) {
70
+ if (String(pane.rows[i].n) === name) {
71
+ pane.setCursor(pane.held + i)
72
+ return
73
+ }
74
+ }
75
+ }
76
+ pane.setCursor(Math.min(entry.index, pane.total - 1))
77
+ }
78
+
79
+ // True when a Back listing's first window lacks the owed name: the existing locate is sent and the rows wait for its window.
80
+ function deferForLocate(pane, entry, items, listed) {
81
+ if (!entry || entry.pendingPath || !listed || listed.path !== entry.path)
82
+ return false
83
+ var name = String(entry.name || "")
84
+ if (name.length === 0 || !(listed.total > items.length) || (pane.searchMode || "").length > 0)
85
+ return false
86
+ for (var i = 0; i < items.length; i++) {
87
+ if (String(items[i].n) === name)
88
+ return false
89
+ }
90
+ if (!(pane.windowSize > 0) || typeof pane.backend.window !== "function")
91
+ return false
92
+ entry.pendingPath = pane.join(entry.path, name)
93
+ entry.total = listed.total
94
+ pane.backend.send({ c: "locate", path: entry.pendingPath })
95
+ return true
96
+ }
97
+
98
+ // The locate reply for a waiting landing fetches the window holding the row, or the clamped index on a miss.
99
+ function takeLocated(pane, message) {
100
+ var entry = pane.swap ? pane.swap.restore : null
101
+ if (!entry || !entry.pendingPath || entry.fetched || !message
102
+ || message.path !== entry.pendingPath || message.directory !== entry.path)
103
+ return false
104
+ var index = Number(message.index)
105
+ if (!(index >= 0) || index >= entry.total)
106
+ index = Math.min(entry.index, entry.total - 1)
107
+ entry.fetched = true
108
+ pane.backend.window(index, pane.windowSize)
109
+ return true
110
+ }
@@ -0,0 +1,54 @@
1
+ .pragma library
2
+
3
+ // Sample input: primaryFor("collide") === "keep", isDestructive("trash", "delete") === true.
4
+ // Variant A: one control for every dialog button, sized by Theme, primary is the safe Enter action.
5
+
6
+ // The members of a set (the protocols) sit this many hairlines apart, 4 px at base size 14.
7
+ var SET_GAP = 4
8
+ // The segments of a settings chooser sit this many hairlines apart, 2 px at base size 14.
9
+ var SEGMENT_GAP = 2
10
+ // A focused control whose foreground hairline would match its rest draws its own frame this wide, inward.
11
+ var RING = 2
12
+ var DISABLED_OPACITY = 0.55
13
+ // The pointer's wash and the press wash, laid in the control's own ink.
14
+ var WASH_HOVER = 0.08
15
+ var WASH_PRESS = 0.14
16
+ // A primary's resting accent wash, at the press wash's weight.
17
+ var WASH_PRIMARY = WASH_PRESS
18
+ // Chrome marks scale by this factor; dialog buttons inset only their frame and wash by whole pixels.
19
+ var PRESS_SCALE = 0.96
20
+ var PRESS_MS = 150
21
+
22
+ // One primary per dialog, fixed: it never moves with focus.
23
+ var PRIMARY = {
24
+ trash: "cancel",
25
+ collide: "keep",
26
+ openWith: "open",
27
+ convert: "convert",
28
+ newFile: "create",
29
+ moveTo: "move",
30
+ copyTo: "copy",
31
+ network: "action",
32
+ permissions: "apply",
33
+ picker: "accept"
34
+ }
35
+
36
+ // A destructive action rests as the error label in a muted frame, never primary.
37
+ var DESTRUCTIVE = {
38
+ trash: { "delete": true },
39
+ collide: { "replace": true },
40
+ picker: { "replace": true }
41
+ }
42
+
43
+ // Sample input: focusFrame(true, false, false) === "foreground", focusFrame(true, false, true) === "thick", focusFrame(false, false, true) === "thick", focusFrame(true, true, true) === "accent", focusFrame(false, true, false) === "thick".
44
+ // What a focused control's own frame draws: one foreground hairline, one accent hairline for a set member where the accent has hue, or "thick", RING wide in the foreground, where a hairline would match the rest.
45
+ function focusFrame(accentHasHue, setMember, restIsForeground) { return setMember !== true ? (restIsForeground === true ? "thick" : "foreground") : accentHasHue === true ? "accent" : "thick" }
46
+
47
+ function primaryFor(dialog) {
48
+ return PRIMARY[dialog] || ""
49
+ }
50
+
51
+ function isDestructive(dialog, name) {
52
+ var set = DESTRUCTIVE[dialog] || {}
53
+ return set[name] === true
54
+ }
@@ -0,0 +1,111 @@
1
+ .pragma library
2
+
3
+ // Neighbour-column background menus: a peek's empty space navigates to its DRAWN directory (parent/ancestor or shownChildPath, never pending childPath) and opens the background menu once its rows land.
4
+
5
+ // A trailing slash trimmed except at the root itself.
6
+ // Sample input: trimSlash("/home/gm/") is "/home/gm".
7
+ function trimSlash(path) {
8
+ var text = String(path)
9
+ return text.length > 1 && text.charAt(text.length - 1) === "/" ? text.substring(0, text.length - 1) : text
10
+ }
11
+
12
+ // True when two paths name the same directory, trailing slash aside.
13
+ // Sample input: samePath("/a/", "/a") is true.
14
+ function samePath(a, b) {
15
+ return trimSlash(a) === trimSlash(b)
16
+ }
17
+
18
+ // "" when a background intent may arm, else the busy reason: rejects before any intent is stored, so a refused open never leaves one behind.
19
+ // Sample input: canArm({listInFlight: true}) is "loading".
20
+ function canArm(pane) {
21
+ if (!pane)
22
+ return "no pane"
23
+ if (pane.listInFlight === true)
24
+ return "loading"
25
+ if (pane.trash && (pane.trash.opened === true || pane.trash.confirming === true))
26
+ return "trash"
27
+ if (pane.searchMode && pane.searchMode.length > 0)
28
+ return "search"
29
+ if (pane.collide && (pane.collide.opened === true || pane.collide.pending !== null))
30
+ return "collide"
31
+ if (pane.menuVisible === true)
32
+ return "menu"
33
+ if (pane.menuActions && pane.menuActions.opened === true)
34
+ return "menu"
35
+ if (pane.renamingIndex >= 0 || pane.renamePending === true)
36
+ return "rename"
37
+ if (pane.filterTyping === true)
38
+ return "filter"
39
+ if (pane.selectionBand !== null && pane.selectionBand !== undefined)
40
+ return "band"
41
+ return ""
42
+ }
43
+
44
+ // True when the target is already shown: open the menu where it stands, no navigation.
45
+ // Sample input: directOpen("/a", "/a/") is true.
46
+ function directOpen(target, panePath) {
47
+ return target.length > 0 && samePath(target, panePath)
48
+ }
49
+
50
+ // True when landed rows may open the deferred menu: the pending target names this listing and it succeeded; empty counts because an empty directory is where the menu matters most, while locked, error and loading drop the intent.
51
+ // Sample input: shouldOpen("/a", "/a", "ready") is true.
52
+ function shouldOpen(panePath, pending, listingState) {
53
+ if (!pending || pending.length === 0)
54
+ return false
55
+ if (!samePath(panePath, pending))
56
+ return false
57
+ return listingState === "ready" || listingState === "empty"
58
+ }
59
+
60
+ // A failed listing drops the deferred menu, so no later rows open it.
61
+ function clearPendingBackground(pane) {
62
+ if (!pane)
63
+ return
64
+ pane.pendingBackground = ""
65
+ pane.pendingBackgroundAt = null
66
+ }
67
+
68
+ // The deferred neighbour background, consumed either way so later rows never open it: opens only a successful listing of the drawn target, at the stored point.
69
+ function applyPendingBackground(pane) {
70
+ if (!pane || !pane.pendingBackground || pane.pendingBackground.length === 0)
71
+ return
72
+ var target = pane.pendingBackground
73
+ var at = pane.pendingBackgroundAt
74
+ pane.pendingBackground = ""
75
+ pane.pendingBackgroundAt = null
76
+ if (!shouldOpen(pane.path, target, pane.listingState))
77
+ return
78
+ if (pane.searchMode && pane.searchMode.length > 0)
79
+ return
80
+ if (pane.trash && pane.trash.opened)
81
+ return
82
+ if (at === null || at === undefined)
83
+ return
84
+ pane.openBackgroundMenu(at)
85
+ }
86
+
87
+ // Routes one neighbour background for the columns view: busy refuses before any intent is stored, the shown folder opens its menu where it stands, and any other target navigates with the menu waiting on its rows. Answers opened, navigating, ignored or refused:<reason>.
88
+ // Sample input: routeBackground({path: "/a"}, "/a", {x: 1}, {openBackground: function () {}}) is "opened".
89
+ function routeBackground(pane, base, scenePoint, menu) {
90
+ if (!pane || !base || base.length === 0 || !scenePoint)
91
+ return "ignored"
92
+ var busy = canArm(pane)
93
+ if (busy.length > 0) {
94
+ if (busy === "loading")
95
+ pane.message("A directory is already loading.", false)
96
+ return "refused:" + busy
97
+ }
98
+ if (directOpen(base, pane.path)) {
99
+ menu.openBackground(scenePoint)
100
+ return "opened"
101
+ }
102
+ pane.pendingBackground = base
103
+ pane.pendingBackgroundAt = scenePoint
104
+ pane.open(base)
105
+ if (!pane.listInFlight) {
106
+ pane.pendingBackground = ""
107
+ pane.pendingBackgroundAt = null
108
+ return "refused:not-started"
109
+ }
110
+ return "navigating"
111
+ }