buninu-browser 0.3.0 → 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 +19 -4
- 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 +37 -2
- package/lib/document-types.js +11 -0
- package/lib/headless-shell.js +47 -8
- package/lib/layout/text-layout.js +17 -9
- package/lib/paint/display-list.js +5 -1
- 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/host.js +31 -6
- package/lib/renderer/main.js +19 -6
- package/lib/renderer/page-realm.js +16 -1
- package/lib/renderer/page-renderer.js +230 -13
- 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 +19 -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/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/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
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
.import "TextSize.js" as TextSize
|
|
3
|
+
.import "Places.js" as Places
|
|
4
|
+
.import "Format.js" as Format
|
|
5
|
+
.import "Keymap.js" as Keymap
|
|
6
|
+
.import "KeyName.js" as KeyName
|
|
7
|
+
.import "SettingsShelf.js" as Shelf
|
|
8
|
+
.import "SettingsAbout.js" as About
|
|
9
|
+
|
|
10
|
+
// Whole sentences with "%1" for a value, filled by fill so each translates as one.
|
|
11
|
+
var WORDS = {
|
|
12
|
+
sheetTip: "Press %1 for the keyboard sheet.",
|
|
13
|
+
loadTip: "%1 loads the current selection.",
|
|
14
|
+
textKeys: "%1 and %2 step through them; %3 follows Omarchy again.",
|
|
15
|
+
nearestStop: "Omarchy's own size. The ruler marks %1, the nearest stop."
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Sections follow the current Desktop boards; their state uses the shared ui.json updater.
|
|
19
|
+
var SECTIONS = [
|
|
20
|
+
{ id: "view", label: "View", glyph: "sliders" },
|
|
21
|
+
{ id: "places", label: "Places", glyph: "star" },
|
|
22
|
+
{ id: "shelf", label: "Shelf", glyph: "shelf" },
|
|
23
|
+
{ id: "preview", label: "Preview", glyph: "preview" },
|
|
24
|
+
{ id: "keys", label: "Keys", glyph: "keyboard" },
|
|
25
|
+
{ id: "display", label: "Display", glyph: "maximize" },
|
|
26
|
+
{ id: "menus", label: "Menus", glyph: "list" },
|
|
27
|
+
{ id: "about", label: "About", glyph: "info" }
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
// Where a section id sits in SECTIONS, or 0 for an id no section carries.
|
|
31
|
+
function sectionIndex(id) {
|
|
32
|
+
for (var i = 0; i < SECTIONS.length; i++) {
|
|
33
|
+
if (SECTIONS[i].id === id)
|
|
34
|
+
return i
|
|
35
|
+
}
|
|
36
|
+
return 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The six SettingsMenus.html puts under the Basic file actions heading, and the ids ui/js/Menu.js gives those rows.
|
|
40
|
+
var BASIC = ["cut", "copy", "paste", "duplicate", "rename", "trash"]
|
|
41
|
+
|
|
42
|
+
// Storage ids remain stable while the action dispatcher uses descriptive verbs. SettingsMenus rule 3: the master rides its group's heading, so a group is its own id here and a group of one row has no master to ride it, which is why Destructive carries neither box nor count.
|
|
43
|
+
var MENU_GROUPS = [
|
|
44
|
+
{ id: "basic", label: "Basic file actions", ids: BASIC },
|
|
45
|
+
{ id: "destructive", label: "Destructive", ids: ["delete"] },
|
|
46
|
+
{ id: "openInspect", label: "Open and inspect",
|
|
47
|
+
ids: ["openwith", "run", "openAsText", "openTerminal", "moveto", "copyto", "properties", "permissions", "makeExecutable", "copyAs", "showOriginal", "tags"] },
|
|
48
|
+
{ id: "extras", label: "Extras", features: ["placeMenu"], // features gate a surface, not a row
|
|
49
|
+
ids: ["shelf", "compress", "extract", "convert", "flashDrive", "taildrop", "localsend", "dropbox", "sharelink", "runScript", "placeMenu", "updateFlea", "toggleLangMarks", "extThumbs", "pasteAs", "invertSelection"] }
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
// Open and Show hidden files draw the lock mark instead of a box, and the board says why: a menu that cannot open the row under the cursor is not a menu, and the hidden toggle is the one background row with no keyboard-independent alternative.
|
|
53
|
+
var LOCKED = ["open", "toggleHidden"]
|
|
54
|
+
|
|
55
|
+
var LABELS = {
|
|
56
|
+
cut: "Cut", copy: "Copy", paste: "Paste", duplicate: "Duplicate", rename: "Rename",
|
|
57
|
+
trash: "Move to Trash", openTerminal: "Open in terminal", copyAs: "Copy as", showOriginal: "Show original",
|
|
58
|
+
pasteAs: "Paste as", invertSelection: "Invert selection", permissions: "Permissions", makeExecutable: "Make executable",
|
|
59
|
+
delete: "Delete permanently", openwith: "Open with", moveto: "Move to", copyto: "Copy to", properties: "Properties",
|
|
60
|
+
compress: "Compress", extract: "Extract", localsend: "Send with LocalSend",
|
|
61
|
+
convert: "Convert", flashDrive: "Flash to drive", taildrop: "Send with Taildrop", dropbox: "Move to Dropbox", tags: "Tags",
|
|
62
|
+
sharelink: "Copy share link", open: "Open", toggleHidden: "Show hidden files", shelf: "Enable shelf", placeMenu: "Places row menu", runScript: "Run script",
|
|
63
|
+
updateFlea: "Update Flea", toggleLangMarks: "Show code icons", extThumbs: "Show thumbnails", run: "Run", openAsText: "Open as text"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The four values of the Keys row, in SettingsKeys.html's own chooser order. The first is what a missing or unrecognised stored name resolves to, which that board says is Default.
|
|
67
|
+
var PRESETS = ["default", "vim", "mac", "windows"]
|
|
68
|
+
var PRESET_LABELS = { "default": "Default", vim: "Vim", mac: "Mac", windows: "Windows" }
|
|
69
|
+
|
|
70
|
+
// Every board row carries a left mark, and a switch wears the mark of the row it governs: these are ui/js/Menu.js's own glyphs by action id, which tests/js/settings.js asserts the two agree on.
|
|
71
|
+
var GLYPHS = {
|
|
72
|
+
cut: "scissors", copy: "copy", paste: "clipboard", duplicate: "file-plus", rename: "rename",
|
|
73
|
+
trash: "trash", openTerminal: "terminal", copyAs: "file-text", showOriginal: "symlink",
|
|
74
|
+
pasteAs: "symlink", invertSelection: "contrast", permissions: "lock", makeExecutable: "play", compress: "archive",
|
|
75
|
+
delete: "trash", openwith: "app-window", moveto: "folder-plus", copyto: "copy", properties: "info",
|
|
76
|
+
extract: "archive-out",
|
|
77
|
+
convert: "sliders", flashDrive: "drive", sharelink: "network", open: "folder-open", toggleHidden: "eye", placeMenu: "folder-open", runScript: "terminal",
|
|
78
|
+
updateFlea: "download", toggleLangMarks: "code", extThumbs: "image", shelf: "shelf", run: "play", openAsText: "file-text", tags: "tag"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// The shelf switch wears the cut glyph the menu row draws, never the animated brand mark.
|
|
82
|
+
var MARKS = { taildrop: "tailscale", localsend: "localsend", dropbox: "dropbox", "display.hyprlandIcons": "hyprland" }
|
|
83
|
+
|
|
84
|
+
function label(id) {
|
|
85
|
+
return LABELS[id] || id
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function glyph(id) {
|
|
89
|
+
return GLYPHS[id]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function contains(list, id) {
|
|
93
|
+
for (var i = 0; list && i < list.length; i++) {
|
|
94
|
+
if (list[i] === id)
|
|
95
|
+
return true
|
|
96
|
+
}
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function isHidden(hidden, id) {
|
|
101
|
+
return contains(hidden, id)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The rule: the count is of ENABLED actions, so "5 of 6" means one of the six is switched off.
|
|
105
|
+
function basicEnabled(hidden, list) {
|
|
106
|
+
var on = 0
|
|
107
|
+
for (var i = 0; i < list.length; i++) {
|
|
108
|
+
if (!isHidden(hidden, list[i]))
|
|
109
|
+
on += 1
|
|
110
|
+
}
|
|
111
|
+
return on
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// All, some and none are read off the group's own ids, which is the tri-state the SettingsMenus board draws. A master is derived here and never stored, so there is no second value that could disagree.
|
|
115
|
+
function masterState(hidden, list) {
|
|
116
|
+
var on = basicEnabled(hidden, list)
|
|
117
|
+
if (on === list.length)
|
|
118
|
+
return "all"
|
|
119
|
+
return on === 0 ? "none" : "some"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Activating a checked master switches its whole group off; an unchecked or partial one switches the group on, so the recovering move is always the one keystroke. Ids outside the group are preserved either way.
|
|
123
|
+
function toggleMaster(hidden, list) {
|
|
124
|
+
var enable = masterState(hidden, list) !== "all"
|
|
125
|
+
var next = []
|
|
126
|
+
for (var i = 0; hidden && i < hidden.length; i++) {
|
|
127
|
+
if (!enable || !contains(list, hidden[i]))
|
|
128
|
+
next.push(hidden[i])
|
|
129
|
+
}
|
|
130
|
+
if (!enable) {
|
|
131
|
+
for (var b = 0; b < list.length; b++) {
|
|
132
|
+
if (!contains(next, list[b]))
|
|
133
|
+
next.push(list[b])
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return next
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function toggleId(hidden, id) {
|
|
140
|
+
var next = []
|
|
141
|
+
var had = false
|
|
142
|
+
for (var i = 0; hidden && i < hidden.length; i++) {
|
|
143
|
+
if (hidden[i] === id) {
|
|
144
|
+
had = true
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
next.push(hidden[i])
|
|
148
|
+
}
|
|
149
|
+
if (!had)
|
|
150
|
+
next.push(id)
|
|
151
|
+
return next
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// One row per line of the panel's pane. kind decides what ui/SettingsPanel.qml draws and whether the row is a focus stop: group, hint, fact and lock rows are read-only and the cursor steps over them. A ruler reports the effective size while Omarchy owns it and only becomes a control on an override.
|
|
155
|
+
function focusable(row) {
|
|
156
|
+
// SettingsGrammar rule 7: a dependent greyed by its parent cannot be operated, so it is no stop either.
|
|
157
|
+
if (row.available === false)
|
|
158
|
+
return false
|
|
159
|
+
// SettingsMenus rule 3 and SettingsRest rule 3: a heading carrying its group's master or its one action is a control, and one carrying neither stays a heading.
|
|
160
|
+
if (row.kind === "group")
|
|
161
|
+
return row.master === true || row.action !== undefined
|
|
162
|
+
if (row.kind === "ruler")
|
|
163
|
+
return row.on === true
|
|
164
|
+
return row.kind === "check" || row.kind === "choice" || row.kind === "action" || row.kind === "favourite"
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// state: { textSize, hidden, keyHints, preset, baseSize, monitorScale, cornerRadius, presetKeys }
|
|
168
|
+
function rows(section, state) {
|
|
169
|
+
if (section === "columns")
|
|
170
|
+
return columnRows(state)
|
|
171
|
+
if (section === "places")
|
|
172
|
+
return placesRows(state)
|
|
173
|
+
if (section === "shelf")
|
|
174
|
+
return Shelf.rows(state, choice)
|
|
175
|
+
if (section === "view")
|
|
176
|
+
return viewRows(state)
|
|
177
|
+
if (section === "preview")
|
|
178
|
+
return previewRows(state)
|
|
179
|
+
if (section === "about")
|
|
180
|
+
return About.rows(state)
|
|
181
|
+
if (section === "display")
|
|
182
|
+
return displayRows(state)
|
|
183
|
+
if (section === "menus")
|
|
184
|
+
return menuRows(state.hidden, state.keyHints)
|
|
185
|
+
return keyRows(state)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// The sentence beside Effective has a job only while the ruler cannot state the running size: in Follow the two differ whenever Omarchy's size is not one of the seven, and an override is always a stop.
|
|
189
|
+
function effectiveNote(follows, baseSize) {
|
|
190
|
+
var nearest = TextSize.nearest(baseSize)
|
|
191
|
+
return !follows ? "Your override." : nearest === baseSize ? "Omarchy's own size."
|
|
192
|
+
: Format.fill(WORDS.nearestStop, nearest)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// The SettingsScale board's own division: Flea owns its text override and Omarchy owns the rest. The size follows the desktop until one of TextSize's seven stops is pinned, and the monitor scale and the corner rounding are the compositor's, drawn as the read-only facts they are.
|
|
196
|
+
function displayRows(state) {
|
|
197
|
+
var follows = TextSize.following(state.textSize)
|
|
198
|
+
var out = [
|
|
199
|
+
{ kind: "group", label: "Text size" },
|
|
200
|
+
{ kind: "choice", id: "textMode", label: "Text size", glyph: "type",
|
|
201
|
+
labels: ["Follow Omarchy", "Override"],
|
|
202
|
+
value: follows ? "Follow Omarchy" : "Override" },
|
|
203
|
+
// The board's seven-stop ruler, the override's own control, now carrying the numbers it stands for; a size Omarchy invented that is not a stop marks the nearest one.
|
|
204
|
+
{ kind: "ruler", id: "textStop", stops: TextSize.STOPS, on: !follows,
|
|
205
|
+
index: TextSize.STOPS.indexOf(TextSize.nearest(state.baseSize)) }
|
|
206
|
+
]
|
|
207
|
+
// SettingsRest rule 2 keeps only the chords; the hint wraps onto two lines at this width.
|
|
208
|
+
var textKeys = KeyName.hintedAll(WORDS.textKeys, ["textSizeUp", "textSizeDown", "textSizeReset"])
|
|
209
|
+
if (textKeys.length > 0) out.push({ kind: "hint", label: textKeys })
|
|
210
|
+
// And Effective stays: it is state.baseSize, the size actually running, where the ruler marks TextSize.nearest() and a tie takes the smaller stop, so a base of 13 marks 12.
|
|
211
|
+
out.push({ kind: "fact", id: "textEffective", label: "Effective", role: "live",
|
|
212
|
+
value: state.baseSize + " px", caption: effectiveNote(follows, state.baseSize) })
|
|
213
|
+
out.push({ kind: "group", label: "Scale" })
|
|
214
|
+
out.push({ kind: "fact", label: "Scale", glyph: "maximize",
|
|
215
|
+
value: scaleLabel(state.monitorScale) })
|
|
216
|
+
out.push({ kind: "hint",
|
|
217
|
+
label: "Flea follows Hyprland's scale and does not change it." })
|
|
218
|
+
out.push({ kind: "group", label: "Appearance" })
|
|
219
|
+
out.push({ kind: "check", id: "display.hyprlandIcons", label: "Hyprland-aware icons",
|
|
220
|
+
mark: MARKS["display.hyprlandIcons"],
|
|
221
|
+
on: ((state.data || {}).display || {}).hyprlandIcons === true })
|
|
222
|
+
return out
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// The compositor's own number, as Hyprland writes it: 1.00 and 1.25. An unanswered query says so rather than reading as 1x, because a wrong number here looks exactly like a right one. The row carries no control, which is what says it cannot be changed; SettingsGrammar rule 5.
|
|
226
|
+
function scaleLabel(scale) {
|
|
227
|
+
if (!(scale > 0))
|
|
228
|
+
return "not reported"
|
|
229
|
+
return (Math.round(scale * 100) / 100) + "x"
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// The one row of this section that is not a menu action: it governs how every menu row is drawn rather than whether it exists, so it sits in its own group and never in MENU_GROUPS.
|
|
233
|
+
function menuRows(hidden, keyHints) {
|
|
234
|
+
var out = []
|
|
235
|
+
for (var g = 0; g < MENU_GROUPS.length; g++) {
|
|
236
|
+
var group = MENU_GROUPS[g]
|
|
237
|
+
var master = group.ids.length > 1
|
|
238
|
+
out.push({ kind: "group", label: group.label, id: master ? group.id : "", master: master,
|
|
239
|
+
ids: group.ids, state: master ? masterState(hidden, group.ids) : "",
|
|
240
|
+
value: master ? basicEnabled(hidden, group.ids) + " of " + group.ids.length : "" })
|
|
241
|
+
for (var i = 0; i < group.ids.length; i++) {
|
|
242
|
+
out.push({ kind: "check", id: group.ids[i], label: label(group.ids[i]),
|
|
243
|
+
glyph: GLYPHS[group.ids[i]], mark: MARKS[group.ids[i]],
|
|
244
|
+
role: group.ids[i] === "delete" ? "error" : "",
|
|
245
|
+
value: group.ids[i] === "delete" ? "off by default" : "",
|
|
246
|
+
on: !isHidden(hidden, group.ids[i]) })
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
out.push({ kind: "group", label: "Shortcuts" })
|
|
250
|
+
out.push({ kind: "check", id: "keyHints", label: "Show keyboard hints", glyph: "keyboard",
|
|
251
|
+
on: keyHints === true })
|
|
252
|
+
// HANDOFF rule 8: one short line. The switch's own label says what it draws, so the hint keeps
|
|
253
|
+
// only the part it cannot: turning it off binds nothing differently.
|
|
254
|
+
out.push({ kind: "hint", label: "Every key stays bound either way." })
|
|
255
|
+
out.push({ kind: "group", label: "Always shown" })
|
|
256
|
+
for (var l = 0; l < LOCKED.length; l++)
|
|
257
|
+
out.push({ kind: "lock", id: LOCKED[l], label: label(LOCKED[l]), glyph: GLYPHS[LOCKED[l]] })
|
|
258
|
+
return out
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Settings shows six examples; the keyboard sheet retains the complete generated inventory.
|
|
262
|
+
function keyPreview(preset) {
|
|
263
|
+
var bindings = Keymap.bindingRows(preset, "gui")
|
|
264
|
+
var enter = Keymap.lookupFor(preset, Qt.Key_Return, "", 0, "listing", "gui")
|
|
265
|
+
var items = [
|
|
266
|
+
{ keys: "arrows", label: preset === "mac" ? "move, open, up" : "move cursor", glyph: "" },
|
|
267
|
+
{ keys: "enter", label: enter, glyph: "" },
|
|
268
|
+
{ keys: "space", label: "quick look", glyph: "" }
|
|
269
|
+
]
|
|
270
|
+
var actions = ["copy", "paste", "trash"]
|
|
271
|
+
for (var i = 0; i < actions.length; i++) {
|
|
272
|
+
var action = actions[i]
|
|
273
|
+
var mods = preset === "mac" ? "super" : preset === "windows" ? "ctrl" : "text"
|
|
274
|
+
if (action === "trash" && (preset === "mac" || preset === "windows")) mods = "none"
|
|
275
|
+
for (var j = 0; j < bindings.length; j++) {
|
|
276
|
+
var binding = bindings[j]
|
|
277
|
+
if (Keymap.actionGroup(binding.action) === action && binding.mods === mods) {
|
|
278
|
+
items.push({ keys: binding.keys, label: action, glyph: action === "trash" ? "" : GLYPHS[action] })
|
|
279
|
+
break
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return items
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function keyRows(state) {
|
|
287
|
+
var out = [
|
|
288
|
+
{ kind: "group", label: "Preset" },
|
|
289
|
+
{ kind: "choice", id: "preset", label: "Keybinding preset", glyph: "keyboard",
|
|
290
|
+
labels: PRESETS.map(function (p) { return PRESET_LABELS[p] }),
|
|
291
|
+
value: PRESET_LABELS[state.preset] || state.preset },
|
|
292
|
+
// Rule 3: the hint says what the preset does, where the old one listed the same four labels the control draws.
|
|
293
|
+
{ kind: "hint", label: "Keys change, actions do not." },
|
|
294
|
+
{ kind: "group", label: "This preset" },
|
|
295
|
+
{ kind: "keyPreview", id: "keyPreview", items: keyPreview(state.preset) }
|
|
296
|
+
]
|
|
297
|
+
var sheetTip = KeyName.hinted(WORDS.sheetTip, "keymapSheet")
|
|
298
|
+
if (sheetTip.length > 0) out.push({ kind: "hint", label: sheetTip })
|
|
299
|
+
return out
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// A read-only row is never the cursor, so both key steps and the opening cursor skip over one; the same shape ui/ContextMenu.qml's stepCursor uses, because a settings row and a menu row step alike.
|
|
303
|
+
function stepRow(list, from, delta) {
|
|
304
|
+
var i = from + delta
|
|
305
|
+
while (i >= 0 && i < list.length) {
|
|
306
|
+
if (focusable(list[i]))
|
|
307
|
+
return i
|
|
308
|
+
i += delta
|
|
309
|
+
}
|
|
310
|
+
return from
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function firstRow(list) {
|
|
314
|
+
return list.length > 0 && focusable(list[0]) ? 0 : stepRow(list, 0, 1)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Choice values are stored separately from labels so presentation never becomes a persistence format. Which control a choice gets is decided by fit, in ui/SettingsRow.qml, not declared here: rule 1 of the SettingsGrammar board, after two named values were a segment and three were a chevron.
|
|
318
|
+
function choice(id, label, glyph, values, labels, value) {
|
|
319
|
+
return { kind: "choice", id: id, label: label, glyph: glyph, values: values, labels: labels,
|
|
320
|
+
value: labels[Math.max(0, values.indexOf(value))], selected: value }
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function viewRows(state) {
|
|
324
|
+
var data = state.data || {}
|
|
325
|
+
var sort = data.sort || {}
|
|
326
|
+
var columns = data.columns || ["name", "size", "date"]
|
|
327
|
+
var out = [
|
|
328
|
+
{ kind: "group", label: "View" },
|
|
329
|
+
choice("view", "Last-used view", undefined, ["list", "columns", "grid", "dual"],
|
|
330
|
+
["List", "Columns", "Grid", "Dual pane"], data.view || "list"),
|
|
331
|
+
{ kind: "check", id: "rememberViewPerFolder", label: "Remember each folder's view", glyph: "folder",
|
|
332
|
+
on: data.rememberViewPerFolder === true },
|
|
333
|
+
choice("density", "Row density", "list", ["tight", "compact", "normal", "comfortable"],
|
|
334
|
+
["Tight", "Compact", "Normal", "Comfortable"], data.density || "compact"),
|
|
335
|
+
{ kind: "hint", label: "Compact stays the default." },
|
|
336
|
+
{ kind: "action", id: "columns", label: "Columns", glyph: "columns",
|
|
337
|
+
value: columns.map(function (key) { return key.charAt(0).toUpperCase() + key.slice(1) }).join(", ") },
|
|
338
|
+
choice("addressBar", "Address bar", undefined, ["path", "breadcrumb"],
|
|
339
|
+
["Path", "Breadcrumb"], data.addressBar || "breadcrumb"),
|
|
340
|
+
// ColumnsWidth board, #167 and #69: the count follows the window width, capped here.
|
|
341
|
+
choice("columnsLimit", "Columns view limit", "columns", [2, 3, 4, 5],
|
|
342
|
+
["2", "3", "4", "5"], data.columnsLimit || 3),
|
|
343
|
+
{ kind: "check", id: "highlightToday", label: "Highlight today's dates", glyph: "history",
|
|
344
|
+
on: data.highlightToday === true },
|
|
345
|
+
// Drops callout 2: off until switched on, and the board draws it with no mark, like escapeUp.
|
|
346
|
+
{ kind: "check", id: "openFoldersWhileDragging", label: "Open folders while dragging",
|
|
347
|
+
on: data.openFoldersWhileDragging === true },
|
|
348
|
+
{ kind: "group", label: "Sorting" },
|
|
349
|
+
choice("sort.key", "Sort by", "sort", ["name", "size", "date", "kind"],
|
|
350
|
+
["Name", "Size", "Date", "Kind"], sort.key || "name"),
|
|
351
|
+
{ kind: "check", id: "foldersFirst", label: "Folders first", glyph: "folders-first", on: data.foldersFirst !== false },
|
|
352
|
+
{ kind: "check", id: "groupByKind", label: "Group by kind", caption: "folders, photos, files", glyph: "grid", on: data.groupByKind === true },
|
|
353
|
+
{ kind: "check", id: "hidden", label: "Show hidden files", glyph: "eye", on: data.hidden === true },
|
|
354
|
+
// Issue 70: a dependent greys in place at the same column while hidden files are off, with no hint.
|
|
355
|
+
{ kind: "check", id: "hiddenLast", label: "Hidden files last", glyph: "eye-off",
|
|
356
|
+
on: data.hiddenLast === true, available: data.hidden === true },
|
|
357
|
+
{ kind: "check", id: "langMarks", label: "Show code icons", glyph: "code", on: data.langMarks !== false },
|
|
358
|
+
// Issue 179: the group's last row; off, every folder takes the default sort and nothing is written.
|
|
359
|
+
{ kind: "check", id: "rememberSort", label: "Remember each folder's sort", glyph: "sort",
|
|
360
|
+
on: data.rememberSort !== false },
|
|
361
|
+
{ kind: "group", label: "Cursor" },
|
|
362
|
+
{ kind: "check", id: "wrapAtEnds", label: "Wrap at list ends", caption: "arrow-up at the top", glyph: "arrow-up", on: data.wrapAtEnds === true },
|
|
363
|
+
// Issue 29: Escape climbs to the parent while on, and stays put while off, which is 0.3.4.
|
|
364
|
+
{ kind: "check", id: "escapeUp", label: "Escape goes up a folder", caption: "with nothing to close", on: data.escapeUp === true },
|
|
365
|
+
{ kind: "group", label: "Opening" },
|
|
366
|
+
choice("startIn", "Flea opens in", "house", ["home", "last", "folder"],
|
|
367
|
+
["Home", "Last folder", "Chosen folder"], data.startIn || "home"),
|
|
368
|
+
// The action writes the folder the panel was opened over and selects the mode with it, so the row above never names a chosen folder that was never chosen. The value is the path itself.
|
|
369
|
+
{ kind: "action", id: "startFolder", label: "Chosen folder", glyph: "folder",
|
|
370
|
+
value: data.startFolder || "Use this folder" },
|
|
371
|
+
choice("newTab", "New tabs open in", "columns", ["current", "home", "start"],
|
|
372
|
+
["Current folder", "Home", "Start folder"], data.newTab || "current"),
|
|
373
|
+
// ClickAndRefresh: one tap opens in single mode and selects with a modifier, so the
|
|
374
|
+
// slow-click rename below has nothing to answer and greys in place, the way hiddenLast does.
|
|
375
|
+
choice("openMode", "Open items with", "pointer", ["double", "single"],
|
|
376
|
+
["Double click", "Single click"], data.openMode || "double"),
|
|
377
|
+
{ kind: "check", id: "clickRename", label: "Click a selected name to rename",
|
|
378
|
+
on: data.clickRename !== false, available: data.openMode !== "single" },
|
|
379
|
+
{ kind: "hint", footer: true, label: state.saveStatus || "Saved",
|
|
380
|
+
role: (state.saveStatus || "").indexOf("Could not") === 0 ? "error" : "accent" }
|
|
381
|
+
]
|
|
382
|
+
// Tabs040 callout 2, the board's own sentence, under the control it explains and only there.
|
|
383
|
+
if ((data.startIn || "home") === "last") {
|
|
384
|
+
for (var i = 0; i < out.length; i++) {
|
|
385
|
+
if (out[i].id === "startIn") {
|
|
386
|
+
out.splice(i + 1, 0, { kind: "hint", label: "Last folder reopens every tab you had." })
|
|
387
|
+
break
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return out
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function previewRows(state) {
|
|
395
|
+
var data = (state.data || {}).preview || {}
|
|
396
|
+
var load = choice("preview.loadOn", "Load", "eye", ["automatic", "manual"],
|
|
397
|
+
["Automatic", "Manual"], data.loadOn || "automatic")
|
|
398
|
+
// Rule 7: with the column off, SelectionPreview.canRead is false and both load paths return, so Load is the one real dependent here and it greys rather than lying.
|
|
399
|
+
load.available = data.column !== false
|
|
400
|
+
// One value per row, rule 4: the board drops the figure outright, because "64 px Medium" is one fact twice.
|
|
401
|
+
var size = choice("preview.thumbSize", "Thumbnail size", "maximize", ["small", "medium", "large", "xlarge", "huge", "largest"],
|
|
402
|
+
["Small", "Medium", "Large", "Extra large", "Huge", "Largest"], data.thumbSize || "medium")
|
|
403
|
+
var head = [
|
|
404
|
+
{ kind: "group", label: "Preview column" },
|
|
405
|
+
// Preview board rule 4: the heading names the group, so the row under it names the switch.
|
|
406
|
+
{ kind: "check", id: "preview.column", label: "Show the preview column", glyph: "columns", on: data.column !== false },
|
|
407
|
+
load
|
|
408
|
+
]
|
|
409
|
+
// Rule 3: a hint sits under the control it explains, not two hairlines below it in a footer; none when the preset binds no key to name.
|
|
410
|
+
var loadTip = data.loadOn === "manual" ? KeyName.hinted(WORDS.loadTip, "loadPreview") : "Automatic follows the cursor."
|
|
411
|
+
var tail = [
|
|
412
|
+
{ kind: "group", label: "Thumbnails" },
|
|
413
|
+
choice("preview.thumbnails", "Thumbnails", "image", ["off", "images", "media"],
|
|
414
|
+
["Off", "Images", "Everything"], data.thumbnails || "media"),
|
|
415
|
+
size,
|
|
416
|
+
// ExtThumbs: one switch per storage class, at the same column and never indented.
|
|
417
|
+
// They grey with the segment at 0.55 and keep their values, the way a disabled
|
|
418
|
+
// chrome control keeps its state; grid zoom below never greys with them.
|
|
419
|
+
// No glyph: the board leaves their 19 px slot empty.
|
|
420
|
+
{ kind: "check", id: "preview.thumbNetwork", label: "On network shares",
|
|
421
|
+
on: data.thumbNetwork === true, available: data.thumbnails !== "off" },
|
|
422
|
+
{ kind: "check", id: "preview.thumbPhone", label: "On phones",
|
|
423
|
+
on: data.thumbPhone === true, available: data.thumbnails !== "off" },
|
|
424
|
+
{ kind: "check", id: "preview.thumbUsb", label: "On USB drives",
|
|
425
|
+
on: data.thumbUsb !== false, available: data.thumbnails !== "off" },
|
|
426
|
+
{ kind: "hint", label: "Off still shows thumbnails that were already made.",
|
|
427
|
+
available: data.thumbnails !== "off" },
|
|
428
|
+
// Rule 7: GridArea gates ctrl-scroll on ViewState.ctrlZoom alone and sizes the tiles from it with thumbnails off, so it is grid zoom, it is named that, and it never greys with them.
|
|
429
|
+
{ kind: "check", id: "preview.ctrlZoom", label: "Zoom the grid with ctrl and scroll", glyph: "move-horizontal", on: data.ctrlZoom !== false }
|
|
430
|
+
]
|
|
431
|
+
return head.concat(loadTip.length > 0 ? [{ kind: "hint", label: loadTip }] : [], tail)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function columnRows(state) {
|
|
435
|
+
var columns = (state.data || {}).columns || ["name", "size", "date"]
|
|
436
|
+
var rows = [{ kind: "group", label: "Columns" }, { kind: "lock", label: "Name", glyph: "file" }]
|
|
437
|
+
for (var i = 0; i < 4; i++) {
|
|
438
|
+
var id = ["mode", "size", "date", "kind"][i]
|
|
439
|
+
rows.push({ kind: "check", id: "column:" + id, label: ["Mode", "Size", "Date", "Kind"][i], on: columns.indexOf(id) >= 0 })
|
|
440
|
+
}
|
|
441
|
+
rows.push({ kind: "action", id: "backView", label: "Back to View", value: "Back" })
|
|
442
|
+
return rows
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function placesRows(state) {
|
|
446
|
+
var data = (state.data || {}).places || {}
|
|
447
|
+
var entries = Places.storedEntries(data.favourites || [], state.home || "")
|
|
448
|
+
// SettingsRest rule 3: the one action that governs the whole group rides its heading, the slot the Menus master takes.
|
|
449
|
+
var rows = [{ kind: "group", label: "Favorites", id: "addFavourite", action: "addFavourite", value: "Add this folder" }]
|
|
450
|
+
for (var i = 0; i < entries.length; i++) {
|
|
451
|
+
rows.push({ kind: "favourite", id: "favourite:" + i, label: entries[i].label,
|
|
452
|
+
value: entries[i].storedPath, display: Format.tilde(entries[i].storedPath, state.home || ""), glyph: entries[i].glyph, error: entries[i].error || (state.favouriteStatuses || {})[i] || "", favouriteIndex: i })
|
|
453
|
+
}
|
|
454
|
+
rows.push({ kind: "group", label: "Built in" })
|
|
455
|
+
// Sidebar040: Recent sits beside Home, Network, Devices and Trash, and ships off.
|
|
456
|
+
// The fourth field is what a key the file never stored reads as, the way the Rail group below reads its own.
|
|
457
|
+
var builtins = [["showHome", "Home", "house", true], ["showRecent", "Recent", "history", false],
|
|
458
|
+
["showNetwork", "Network", "network", true],
|
|
459
|
+
["showDevices", "Devices", "drive", true], ["showTrash", "Trash", "trash", true]]
|
|
460
|
+
for (var b = 0; b < builtins.length; b++) rows.push({ kind: "check", id: "places." + builtins[b][0], label: builtins[b][1], glyph: builtins[b][2], on: builtins[b][3] ? data[builtins[b][0]] !== false : data[builtins[b][0]] === true })
|
|
461
|
+
rows.push({ kind: "group", label: "Sidebar" })
|
|
462
|
+
// The fourth field is src/uischema.rs's shipped value, which is what a key the file never stored reads as.
|
|
463
|
+
var rail = [["driveSize", "Show drive size", "drive", false], ["trashCount", "Show Trash count", "trash", false], ["showUnmounted", "Show unmounted drives", "drive", true], ["showTags", "Show tags", "tag", false], ["autoHide", "Auto-hide sidebar", "maximize", false]]
|
|
464
|
+
for (var r = 0; r < rail.length; r++) rows.push({ kind: "check", id: "places." + rail[r][0], label: rail[r][1], glyph: rail[r][2], on: rail[r][3] ? data[rail[r][0]] !== false : data[rail[r][0]] === true })
|
|
465
|
+
// Directive 74: the rail is remembered as a word, so this row reads it as one; ctrl-b writes the same leaf. Directive 77: auto-hide answers the pointer instead, so the remembered choice is greyed while that is on rather than quietly ignored.
|
|
466
|
+
rows.push({ kind: "check", id: "places.rail", label: "Show sidebar", glyph: "columns", on: data.rail !== "hidden", available: data.autoHide !== true })
|
|
467
|
+
rows.push(choice("places.sidebarWidth", "Sidebar width", "maximize", Places.WIDTH_STOPS,
|
|
468
|
+
["160 px", "192 px", "224 px", "256 px"], Places.sidebarWidth(data.sidebarWidth)))
|
|
469
|
+
// Trash lives in Places, and the sweep is off until switched on: permanent deletion is outside the undo journal.
|
|
470
|
+
rows.push({ kind: "group", label: "Trash" })
|
|
471
|
+
// The eyebrow says TRASH, so the label does not repeat it; a fuller one elided the caption to "permanently, once a...".
|
|
472
|
+
rows.push({ kind: "check", id: "trashAutoEmpty", label: "Empty after 30 days",
|
|
473
|
+
caption: "permanently", glyph: "history",
|
|
474
|
+
on: (state.data || {}).trashAutoEmpty === true })
|
|
475
|
+
return rows
|
|
476
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
.import "Update.js" as Update
|
|
3
|
+
.import "MakeDefault.js" as MakeDefault
|
|
4
|
+
|
|
5
|
+
// The Settings panel's About section: what is installed, the one updater, and where help lives; it came out of ui/js/Settings.js whole when the Updates group grew.
|
|
6
|
+
|
|
7
|
+
// The two rows that leave Flea for a web page carry their destination, so ui/SettingsPanel.qml opens whatever a row names.
|
|
8
|
+
var ISSUES_URL = "https://github.com/thisisgm/flea/issues"
|
|
9
|
+
var SPONSORS_URL = "https://github.com/sponsors/thisisgm"
|
|
10
|
+
|
|
11
|
+
// state.about is ui/AboutFacts.qml's facts, the updater's status and the default's claim among them; state.data is ui.json.
|
|
12
|
+
function rows(state) {
|
|
13
|
+
var facts = state.about || {}
|
|
14
|
+
var out = [
|
|
15
|
+
{ kind: "hero", label: "Flea", value: "A file manager for Omarchy" },
|
|
16
|
+
{ kind: "fact", label: "Version", value: facts.version || "Not reported" },
|
|
17
|
+
{ kind: "fact", label: "Built", value: facts.built || "Not recorded in this build" },
|
|
18
|
+
{ kind: "fact", label: "Installed from", value: facts.source || "Not reported" },
|
|
19
|
+
{ kind: "fact", label: "Package", value: facts.package || "Not reported" },
|
|
20
|
+
{ kind: "fact", label: "License", value: "MIT, © 2026 GM" },
|
|
21
|
+
{ kind: "group", label: "Language" },
|
|
22
|
+
{ kind: "fact", label: "Language", glyph: "globe", value: "English" },
|
|
23
|
+
{ kind: "group", label: "Updates" }
|
|
24
|
+
]
|
|
25
|
+
out = out.concat(updateRows(facts.update || Update.idle(), state.data))
|
|
26
|
+
out.push({ kind: "group", label: "This computer" })
|
|
27
|
+
// Rule 5: the identity is in the tail, so a handler too long for the row loses its head instead.
|
|
28
|
+
out.push({ kind: "fact", label: "File manager", glyph: "folder", elide: "head", value: facts.handler || "Not reported" })
|
|
29
|
+
out = out.concat(MakeDefault.rows(facts.handler, facts.claim))
|
|
30
|
+
out.push({ kind: "action", id: "keyboardSheet", label: "Keyboard sheet", glyph: "keyboard", value: "?" })
|
|
31
|
+
out.push({ kind: "action", id: "reportIssue", label: "Report an issue", glyph: "network", value: "Open", url: ISSUES_URL })
|
|
32
|
+
// SettingsGrammar rule 6: a brand is never given a generic glyph, and the mark set holds no GitHub reproduction.
|
|
33
|
+
out.push({ kind: "action", id: "support", label: "Support Flea", value: "GitHub Sponsors", url: SPONSORS_URL })
|
|
34
|
+
return out
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Omarchy installs and Flea may check and launch: the row's value is the check, its chevron says Enter acts, and the note explains the states that need it.
|
|
38
|
+
function updateRows(update, data) {
|
|
39
|
+
var out = [
|
|
40
|
+
{ kind: Update.isFact(update) ? "fact" : "action", id: "updateFlea", label: "Update Flea", glyph: "download",
|
|
41
|
+
value: Update.value(update), role: Update.role(update), inert: !Update.acts(update) },
|
|
42
|
+
{ kind: "check", id: "updates.autoCheck", label: "Check automatically", caption: "when About opens, every 6 h",
|
|
43
|
+
on: autoCheck(data) }
|
|
44
|
+
]
|
|
45
|
+
var note = Update.note(update)
|
|
46
|
+
if (note !== null)
|
|
47
|
+
out.push({ kind: "hint", label: note[0], role: note[1] })
|
|
48
|
+
return out
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// On until switched off, the default src/uischema.rs ships for updates.autoCheck.
|
|
52
|
+
function autoCheck(data) {
|
|
53
|
+
return ((data || {}).updates || {}).autoCheck !== false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// The three names Flea is packaged under, src/update.rs's OPR_PACKAGE, AUR_PACKAGE and GIT_PACKAGE.
|
|
57
|
+
var PACKAGES = { opr: "flea", aur: "flea-bin", git: "flea-git" }
|
|
58
|
+
|
|
59
|
+
// Installed from, one sentence per kind src/update.rs derives, so this row and the Update Flea row name one source.
|
|
60
|
+
var SOURCES = { opr: "Omarchy Package Repository", aur: "AUR, flea-bin", git: "AUR, flea-git",
|
|
61
|
+
local: "Local package", unowned: "Not from a package" }
|
|
62
|
+
|
|
63
|
+
// src/update.rs kind_for, over pacman -Qqo's owner ("" when no package owns Flea); pacman -Si knows no AUR package.
|
|
64
|
+
function installKind(owner, signed) {
|
|
65
|
+
if (!owner)
|
|
66
|
+
return "unowned"
|
|
67
|
+
if (owner === PACKAGES.opr && signed)
|
|
68
|
+
return "opr"
|
|
69
|
+
if (owner === PACKAGES.aur)
|
|
70
|
+
return "aur"
|
|
71
|
+
return owner === PACKAGES.git ? "git" : "local"
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function installedFrom(owner, signed) {
|
|
75
|
+
return SOURCES[installKind(owner, signed)]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// pacman -Qi under LC_ALL=C, read the way src/update.rs package_info reads it; "" for a query that failed.
|
|
79
|
+
// Sample input: "Name : flea-bin\nVersion : 0.3.4-1\nBuild Date : Wed Sep 23 12:40:50 2026\nValidated By : None\n"
|
|
80
|
+
function packageFacts(text) {
|
|
81
|
+
var facts = { package: "", built: "", signed: false }
|
|
82
|
+
var lines = String(text || "").split("\n")
|
|
83
|
+
for (var i = 0; i < lines.length; i++) {
|
|
84
|
+
var cut = lines[i].indexOf(":")
|
|
85
|
+
if (cut < 0)
|
|
86
|
+
continue
|
|
87
|
+
var key = lines[i].substring(0, cut).trim(), value = lines[i].substring(cut + 1).trim()
|
|
88
|
+
if (key === "Name")
|
|
89
|
+
facts.package = value
|
|
90
|
+
else if (key === "Version")
|
|
91
|
+
facts.package = facts.package.length > 0 ? facts.package + " " + value : value
|
|
92
|
+
else if (key === "Build Date")
|
|
93
|
+
facts.built = value
|
|
94
|
+
// One or more methods separated by two spaces, such as "SHA-256 Sum Signature".
|
|
95
|
+
else if (key === "Validated By")
|
|
96
|
+
facts.signed = value.split(/\s+/).indexOf("Signature") >= 0
|
|
97
|
+
}
|
|
98
|
+
return facts
|
|
99
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
.pragma library
|
|
2
|
+
|
|
3
|
+
// The Settings panel's Shelf section, SettingsRest rules 1 to 4 and ledger directive 59. It lives
|
|
4
|
+
// beside Settings.js rather than in it: the section owns the shelf's switches, the bar plugin reads
|
|
5
|
+
// them from ui.json, and the pins are the shelf's own entries the panel is handed.
|
|
6
|
+
|
|
7
|
+
// `choice` is handed in because this library cannot import the one that imports it.
|
|
8
|
+
function rows(state, choice) {
|
|
9
|
+
var data = (state.data || {}).shelf || {}
|
|
10
|
+
var on = data.enabled === true
|
|
11
|
+
var out = [{ kind: "group", label: "Shelf", id: "shelf.enabled", master: true,
|
|
12
|
+
state: on ? "all" : "none", value: on ? "on" : "off" }]
|
|
13
|
+
out.push({ kind: "check", id: "shelf.bar", label: "Show in bar", mark: "flea",
|
|
14
|
+
available: on, on: data.bar !== false })
|
|
15
|
+
out.push(choice("shelf.rail", "Edge rail", "maximize", ["off", "left", "right", "bottom"],
|
|
16
|
+
["Off", "Left", "Right", "Bottom"], railEdge(data.rail)))
|
|
17
|
+
out[out.length - 1].available = on
|
|
18
|
+
// The board's own sentence, second half included: the bind is the user's line in hyprland's
|
|
19
|
+
// config and nothing here installs it.
|
|
20
|
+
out.push({ kind: "hint", label: "Super+D opens it from anywhere once you add the bind from the README." })
|
|
21
|
+
out.push({ kind: "group", label: "Recent captures" })
|
|
22
|
+
out.push({ kind: "check", id: "shelf.screenshots", label: "Screenshots", glyph: "image",
|
|
23
|
+
available: on, on: data.screenshots !== false })
|
|
24
|
+
out.push({ kind: "check", id: "shelf.recordings", label: "Recordings", glyph: "film",
|
|
25
|
+
available: on, on: data.recordings !== false })
|
|
26
|
+
out.push(choice("shelf.recent", "Show", "list", CAPTURE_COUNTS, ["1", "2", "3", "5"],
|
|
27
|
+
captureCount(data.recent)))
|
|
28
|
+
out[out.length - 1].available = on
|
|
29
|
+
out.push({ kind: "group", label: "Pinned", id: "pinFolder", action: "pinFolder",
|
|
30
|
+
value: "Pin this folder" })
|
|
31
|
+
var pins = state.pins || []
|
|
32
|
+
for (var i = 0; i < pins.length; i++) {
|
|
33
|
+
out.push({ kind: "favourite", id: "pin:" + i, label: pins[i].name, value: pins[i].path,
|
|
34
|
+
glyph: pins[i].folder ? "folder" : "file", pinPath: pins[i].path,
|
|
35
|
+
pinIndex: i, pinCount: pins.length,
|
|
36
|
+
available: on, error: pins[i].missing === true ? "missing" : "" })
|
|
37
|
+
}
|
|
38
|
+
return out
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// The four the board offers, and the fallback for a count a hand-edited file left behind.
|
|
42
|
+
var CAPTURE_COUNTS = [1, 2, 3, 5]
|
|
43
|
+
|
|
44
|
+
function captureCount(value) {
|
|
45
|
+
var n = Number(value)
|
|
46
|
+
return CAPTURE_COUNTS.indexOf(n) >= 0 ? n : 3
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function railEdge(value) {
|
|
50
|
+
var edges = ["off", "left", "right", "bottom"]
|
|
51
|
+
return edges.indexOf(String(value)) >= 0 ? String(value) : "off"
|
|
52
|
+
}
|