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.
- package/LICENSES/jsmdcui.txt +22 -0
- package/README.md +102 -2
- package/frontends/wayland/LICENSE-land +21 -0
- package/frontends/wayland/README.md +123 -0
- package/frontends/wayland/casty/LICENSE +21 -0
- package/frontends/wayland/casty/bookmarks.js +32 -0
- package/frontends/wayland/casty/click.js +96 -0
- package/frontends/wayland/casty/commands.js +61 -0
- package/frontends/wayland/casty/config.js +45 -0
- package/frontends/wayland/casty/download-directory.js +28 -0
- package/frontends/wayland/casty/download.js +49 -0
- package/frontends/wayland/casty/hints.js +334 -0
- package/frontends/wayland/casty/keys.js +56 -0
- package/frontends/wayland/casty/page-commands.js +80 -0
- package/frontends/wayland/casty/prompt-width.js +148 -0
- package/frontends/wayland/casty/urlbar.js +606 -0
- package/frontends/wayland/client.js +88 -0
- package/frontends/wayland/clipboard.js +80 -0
- package/frontends/wayland/index.js +116 -0
- package/frontends/wayland/input.js +56 -0
- package/frontends/wayland/protocol.js +55 -0
- package/frontends/wayland/protocols/wayland.xml +3248 -0
- package/frontends/wayland/protocols/xdg-shell.xml +1415 -0
- package/frontends/wayland/transport.js +120 -0
- package/frontends/wayland/urlbar.js +158 -0
- package/index.js +28 -1
- package/lib/clipboard-command.js +10 -0
- package/lib/clipboard-powershell.js +22 -0
- package/lib/clipboard.js +57 -0
- package/lib/document-types.js +11 -0
- package/lib/headless-shell.js +78 -13
- package/lib/layout/text-layout.js +17 -9
- package/lib/paint/display-list.js +5 -1
- package/lib/paint/frame.js +35 -3
- package/lib/paint/raw-frames.js +47 -0
- package/lib/qml/README.md +971 -0
- package/lib/qml/builtins.js +700 -0
- package/lib/qml/canvas-dom.js +115 -0
- package/lib/qml/document.js +1176 -0
- package/lib/qml/format.js +9 -0
- package/lib/qml/host.js +324 -0
- package/lib/qml/images.js +199 -0
- package/lib/qml/launcher.js +132 -0
- package/lib/qml/loader.js +517 -0
- package/lib/qml/media-renderer.js +49 -0
- package/lib/qml/media.js +86 -0
- package/lib/qml/models.js +622 -0
- package/lib/qml/parser.js +381 -0
- package/lib/qml/qs +5 -0
- package/lib/qml/qscasty +3 -0
- package/lib/qml/qsheadless +3 -0
- package/lib/qml/qswayland +3 -0
- package/lib/qml/reactive.js +159 -0
- package/lib/qml/runtime.js +3381 -0
- package/lib/qml/shapes.js +243 -0
- package/lib/qml/text.js +109 -0
- package/lib/qml/textures.js +123 -0
- package/lib/qml/wayland-clipboard.js +32 -0
- package/lib/qml/worker-thread.js +70 -0
- package/lib/render-tree/index.js +16 -1
- package/lib/renderer/bindings.js +11 -0
- package/lib/renderer/canvas-2d.js +13 -0
- package/lib/renderer/compositor-channel.js +103 -0
- package/lib/renderer/compositor.js +25 -33
- package/lib/renderer/flags.js +1 -1
- package/lib/renderer/host.js +128 -92
- package/lib/renderer/main.js +38 -4
- package/lib/renderer/page-realm.js +32 -1
- package/lib/renderer/page-renderer.js +250 -21
- package/lib/renderer/text-input-paint.js +54 -0
- package/lib/renderer/text-input.js +57 -0
- package/lib/style/computed-style.js +2 -2
- package/package.json +1 -1
- package/test/clipboard.test.js +54 -0
- package/test/fixtures/browser-raw-demo.html +36 -0
- package/test/fixtures/flea-helpers/Dockerfile +36 -0
- package/test/fixtures/flea-helpers/bat.mjs +43 -0
- package/test/fixtures/flea-helpers/bin/bat +3 -0
- package/test/fixtures/flea-helpers/bin/bwrap +10 -0
- package/test/fixtures/flea-helpers/bin/bwrap-real-filter +9 -0
- package/test/fixtures/flea-helpers/bin/glow +3 -0
- package/test/fixtures/flea-helpers/bin/qs +3 -0
- package/test/fixtures/flea-helpers/bin/unsupported +3 -0
- package/test/fixtures/flea-helpers/check-highlighting.mjs +47 -0
- package/test/fixtures/flea-helpers/check-media.mjs +29 -0
- package/test/fixtures/flea-helpers/check-preview.mjs +27 -0
- package/test/fixtures/flea-helpers/check.mjs +36 -0
- package/test/fixtures/flea-helpers/highlight-cases.json +227 -0
- package/test/fixtures/flea-helpers/source-mime.xml +28 -0
- package/test/fixtures/flea-helpers/syntax-compat.mjs +23 -0
- package/test/fixtures/flea-thumbnail-stub/flea-placeholder.thumbnailer +4 -0
- package/test/fixtures/flea-thumbnail-stub/flea-thumbnail-stub +3 -0
- package/test/fixtures/qml/flea/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea/LICENSE +21 -0
- package/test/fixtures/qml/flea/README.md +25 -0
- package/test/fixtures/qml/flea/Theme.qml +8 -0
- package/test/fixtures/qml/flea/demo.qml +24 -0
- package/test/fixtures/qml/flea/qmldir +3 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Flea +21 -0
- package/test/fixtures/qml/flea-theme/LICENSE-Omarchy +20 -0
- package/test/fixtures/qml/flea-theme/README.md +70 -0
- package/test/fixtures/qml/flea-theme/button.qml +17 -0
- package/test/fixtures/qml/flea-theme/demo.qml +27 -0
- package/test/fixtures/qml/flea-theme/field.qml +21 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/BorderGeometry.js +373 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Color.qml +258 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/I18n.qml +118 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Numbers.js +19 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Plural.js +91 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Style.qml +561 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/Util.qml +188 -0
- package/test/fixtures/qml/flea-theme/imports/qs/Commons/qmldir +13 -0
- package/test/fixtures/qml/flea-theme/segment.qml +17 -0
- package/test/fixtures/qml/flea-theme/settings.qml +58 -0
- package/test/fixtures/qml/flea-theme/text-metrics.qml +10 -0
- package/test/fixtures/qml/flea-theme/ui/DialogButton.qml +150 -0
- package/test/fixtures/qml/flea-theme/ui/DialogField.qml +147 -0
- package/test/fixtures/qml/flea-theme/ui/EscapeHint.qml +11 -0
- package/test/fixtures/qml/flea-theme/ui/Glass.qml +114 -0
- package/test/fixtures/qml/flea-theme/ui/Glyph.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/GlyphSolid.qml +22 -0
- package/test/fixtures/qml/flea-theme/ui/Reveal.qml +54 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsRuler.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/SettingsSegment.qml +85 -0
- package/test/fixtures/qml/flea-theme/ui/StableModel.qml +89 -0
- package/test/fixtures/qml/flea-theme/ui/Theme.qml +460 -0
- package/test/fixtures/qml/flea-theme/ui/ThemeColors.qml +43 -0
- package/test/fixtures/qml/flea-theme/ui/ViewState.qml +565 -0
- package/test/fixtures/qml/flea-theme/ui/js/Anchor.js +379 -0
- package/test/fixtures/qml/flea-theme/ui/js/AnchorHold.js +162 -0
- package/test/fixtures/qml/flea-theme/ui/js/Archive.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/BackReturn.js +110 -0
- package/test/fixtures/qml/flea-theme/ui/js/Buttons.js +54 -0
- package/test/fixtures/qml/flea-theme/ui/js/ColumnMenu.js +111 -0
- package/test/fixtures/qml/flea-theme/ui/js/Columns.js +298 -0
- package/test/fixtures/qml/flea-theme/ui/js/Contrast.js +157 -0
- package/test/fixtures/qml/flea-theme/ui/js/Density.js +44 -0
- package/test/fixtures/qml/flea-theme/ui/js/DirSizes.js +129 -0
- package/test/fixtures/qml/flea-theme/ui/js/ExtThumbs.js +115 -0
- package/test/fixtures/qml/flea-theme/ui/js/Filter.js +284 -0
- package/test/fixtures/qml/flea-theme/ui/js/FolderSorts.js +108 -0
- package/test/fixtures/qml/flea-theme/ui/js/Format.js +305 -0
- package/test/fixtures/qml/flea-theme/ui/js/Icons.js +206 -0
- package/test/fixtures/qml/flea-theme/ui/js/Input.js +39 -0
- package/test/fixtures/qml/flea-theme/ui/js/KeyName.js +64 -0
- package/test/fixtures/qml/flea-theme/ui/js/Keymap.js +370 -0
- package/test/fixtures/qml/flea-theme/ui/js/Kinds.js +145 -0
- package/test/fixtures/qml/flea-theme/ui/js/Lists.js +11 -0
- package/test/fixtures/qml/flea-theme/ui/js/MakeDefault.js +186 -0
- package/test/fixtures/qml/flea-theme/ui/js/Match.js +27 -0
- package/test/fixtures/qml/flea-theme/ui/js/Material.js +109 -0
- package/test/fixtures/qml/flea-theme/ui/js/Menu.js +513 -0
- package/test/fixtures/qml/flea-theme/ui/js/Motion.js +16 -0
- package/test/fixtures/qml/flea-theme/ui/js/Mounts.js +320 -0
- package/test/fixtures/qml/flea-theme/ui/js/MouseNav.js +13 -0
- package/test/fixtures/qml/flea-theme/ui/js/Nav.js +328 -0
- package/test/fixtures/qml/flea-theme/ui/js/Palette.js +33 -0
- package/test/fixtures/qml/flea-theme/ui/js/Places.js +236 -0
- package/test/fixtures/qml/flea-theme/ui/js/Protocols.js +296 -0
- package/test/fixtures/qml/flea-theme/ui/js/RecentMode.js +154 -0
- package/test/fixtures/qml/flea-theme/ui/js/Search.js +396 -0
- package/test/fixtures/qml/flea-theme/ui/js/Settings.js +476 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsAbout.js +99 -0
- package/test/fixtures/qml/flea-theme/ui/js/SettingsShelf.js +52 -0
- package/test/fixtures/qml/flea-theme/ui/js/Sort.js +127 -0
- package/test/fixtures/qml/flea-theme/ui/js/StateWords.js +42 -0
- package/test/fixtures/qml/flea-theme/ui/js/Swap.js +166 -0
- package/test/fixtures/qml/flea-theme/ui/js/TextSize.js +107 -0
- package/test/fixtures/qml/flea-theme/ui/js/Thumbs.js +158 -0
- package/test/fixtures/qml/flea-theme/ui/js/UiState.js +438 -0
- package/test/fixtures/qml/flea-theme/ui/js/Update.js +132 -0
- package/test/fixtures/qml/flea-theme/ui/js/UserDirs.js +56 -0
- package/test/fixtures/qml/flea-theme/ui/qmldir +227 -0
- package/test/fixtures/qml/hello.qml +26 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabel.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/VersionLabelNew.qml +2 -0
- package/test/fixtures/qml/imports/Demo/Toolkit/qmldir +3 -0
- package/test/fixtures/qml/imports/components/Badge.qml +15 -0
- package/test/fixtures/qml/imports/logic/base.js +2 -0
- package/test/fixtures/qml/imports/logic/counter.js +4 -0
- package/test/fixtures/qml/imports/logic/math.js +3 -0
- package/test/fixtures/qml/imports/logic/shared.js +3 -0
- package/test/fixtures/qml/imports/main.qml +21 -0
- package/test/fixtures/qml/imports/theme/Theme.qml +8 -0
- package/test/fixtures/qml/imports/theme/qmldir +2 -0
- package/test/fixtures/qml/multiply.qml +61 -0
- package/test/fixtures/qml-canvas-dom.js +90 -0
- package/test/fixtures/qml-media-audio.js +39 -0
- package/test/fixtures/raw-capture-benchmark.json +76 -0
- package/test/helpers/flea-sandbox.js +48 -0
- package/test/helpers/quickshell-fixture.js +125 -0
- package/test/html-clipboard-wayland.test.js +47 -0
- package/test/index.test.js +13 -0
- package/test/manual-casty-without-qml.js +87 -0
- package/test/manual-flea-application.js +49 -0
- package/test/manual-flea-binary.js +62 -0
- package/test/manual-flea-qml.js +9 -0
- package/test/manual-flea-settings.js +40 -0
- package/test/manual-flea-theme.js +40 -0
- package/test/manual-qml-removal.js +46 -0
- package/test/manual-without-qml.js +45 -0
- package/test/qml-canvas-dom.test.js +37 -0
- package/test/qml-clipboard-bridge.test.js +25 -0
- package/test/qml-clipboard-wayland.test.js +40 -0
- package/test/qml-connections-timing.test.js +73 -0
- package/test/qml-dynamic.test.js +401 -0
- package/test/qml-flea-app.test.js +146 -0
- package/test/qml-flea-backend.test.js +78 -0
- package/test/qml-flea-columns-backend.test.js +38 -0
- package/test/qml-flea-compile.test.js +78 -0
- package/test/qml-flea-launcher.test.js +88 -0
- package/test/qml-flea-markdown-backend.test.js +31 -0
- package/test/qml-flea-media.test.js +28 -0
- package/test/qml-flea-navigation-backend.test.js +35 -0
- package/test/qml-flea-navigation-timing.test.js +147 -0
- package/test/qml-flea-preview-backend.test.js +53 -0
- package/test/qml-flea-previews.test.js +163 -0
- package/test/qml-flea-rows.test.js +101 -0
- package/test/qml-flea-textures.test.js +59 -0
- package/test/qml-flea-view-switch.test.js +33 -0
- package/test/qml-flea-wayland.test.js +68 -0
- package/test/qml-flea.test.js +34 -0
- package/test/qml-host.test.js +238 -0
- package/test/qml-imports.test.js +102 -0
- package/test/qml-inherited-handlers.test.js +51 -0
- package/test/qml-input-selection.test.js +66 -0
- package/test/qml-interactions.test.js +309 -0
- package/test/qml-language.test.js +167 -0
- package/test/qml-launcher.test.js +54 -0
- package/test/qml-layout-text.test.js +205 -0
- package/test/qml-media.test.js +63 -0
- package/test/qml-persisted-delegates.test.js +45 -0
- package/test/qml-quickshell-controls.test.js +65 -0
- package/test/qml-quickshell-settings.test.js +62 -0
- package/test/qml-repeater.test.js +72 -0
- package/test/qml-style-updates.test.js +124 -0
- package/test/qml-textures.test.js +66 -0
- package/test/qml-views.test.js +332 -0
- package/test/qml-worker.test.js +69 -0
- package/test/qml.test.js +83 -0
- package/test/raw-capture.test.js +152 -0
- package/test/renderer-host.test.js +11 -0
- package/test/text-input-pointer.test.js +31 -0
- package/test/text-input-scroll.test.js +29 -0
- package/test/text-input.test.js +47 -0
- package/test/wayland-frontend.test.js +83 -0
- package/test/wayland-urlbar.test.js +38 -0
- package/test/window-resize.test.js +36 -0
- 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
|
+
}
|