buninu-browser 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES/jsmdcui.txt +22 -0
- package/README.md +40 -4
- package/frontends/wayland/LICENSE-land +21 -0
- package/frontends/wayland/README.md +152 -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 +130 -0
- package/frontends/wayland/input.js +75 -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 +162 -0
- package/index.js +28 -1
- package/lib/audio/shared-connection.js +31 -0
- package/lib/cdp-server.js +15 -2
- 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 +54 -12
- package/lib/layout/text-layout.js +17 -9
- package/lib/paint/display-list.js +6 -1
- package/lib/qml/README.md +1427 -0
- package/lib/qml/builtins.js +771 -0
- package/lib/qml/canvas-dom.js +115 -0
- package/lib/qml/canvas.js +76 -0
- package/lib/qml/document.js +1199 -0
- package/lib/qml/fonts.js +69 -0
- package/lib/qml/format.js +9 -0
- package/lib/qml/host.js +352 -0
- package/lib/qml/images.js +199 -0
- package/lib/qml/launcher.js +132 -0
- package/lib/qml/loader.js +521 -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 +182 -0
- package/lib/qml/runtime.js +3603 -0
- package/lib/qml/shader/fragment.js +83 -0
- package/lib/qml/shader/qsb.js +27 -0
- package/lib/qml/shader/renderer.js +39 -0
- package/lib/qml/shader/runtime.js +61 -0
- package/lib/qml/shapes.js +251 -0
- package/lib/qml/shortcuts.js +65 -0
- package/lib/qml/text.js +109 -0
- package/lib/qml/textures.js +126 -0
- package/lib/qml/wayland-clipboard.js +32 -0
- package/lib/qml/window-constraints.js +16 -0
- package/lib/qml/worker-thread.js +70 -0
- package/lib/render-tree/index.js +16 -1
- package/lib/renderer/bindings.js +16 -1
- package/lib/renderer/canvas-2d.js +29 -0
- package/lib/renderer/document-selection.js +19 -0
- package/lib/renderer/host.js +45 -17
- package/lib/renderer/main.js +21 -6
- package/lib/renderer/page-realm.js +20 -10
- package/lib/renderer/page-renderer.js +497 -42
- package/lib/renderer/pointer-move.js +5 -3
- package/lib/renderer/render-interval.js +9 -0
- package/lib/renderer/text-input-paint.js +54 -0
- package/lib/renderer/text-input.js +57 -0
- package/lib/style/computed-style.js +10 -4
- package/package.json +1 -1
- package/test/audio-connection-lifetime.test.js +46 -0
- package/test/canvas-paint.test.js +8 -0
- package/test/clipboard.test.js +19 -0
- package/test/computed-style.test.js +12 -0
- package/test/document-selection.test.js +27 -0
- package/test/double-click.test.js +85 -0
- package/test/fixtures/canvas-damage.js +40 -0
- package/test/fixtures/fake-renderer.js +1 -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/omastorm-helpers/Dockerfile +35 -0
- package/test/fixtures/omastorm-helpers/check.mjs +56 -0
- package/test/fixtures/omastorm-helpers/launch.mjs +24 -0
- package/test/fixtures/omastorm-helpers/prepare.mjs +17 -0
- package/test/fixtures/omastorm-helpers/runtime.mjs +32 -0
- package/test/fixtures/pointer-paint-batching.js +23 -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-fonts/main.qml +10 -0
- package/test/fixtures/qml-locale/check.mjs +6 -0
- package/test/fixtures/qml-locale/main.qml +8 -0
- package/test/fixtures/qml-media-audio.js +39 -0
- package/test/fixtures/qml-shader/grab.qml +14 -0
- package/test/fixtures/qml-shader/gradient.frag +5 -0
- package/test/fixtures/qml-shader/main.qml +14 -0
- package/test/fixtures/qml-shader/texture-grab.qml +25 -0
- package/test/fixtures/qml-shader/texture-size.frag +5 -0
- package/test/fixtures/qml-shader-check.mjs +24 -0
- package/test/fixtures/title-no-paint.js +20 -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-double-click.html +21 -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-mouse-buttons.html +18 -0
- package/test/manual-omastorm-engine.mjs +19 -0
- package/test/manual-omastorm-shaders.mjs +28 -0
- package/test/manual-omastorm-software.mjs +25 -0
- package/test/manual-pointer-scroll.html +43 -0
- package/test/manual-qml-removal.js +46 -0
- package/test/manual-without-qml.js +45 -0
- package/test/pointer-drag.test.js +104 -0
- package/test/qml-canvas-dom.test.js +37 -0
- package/test/qml-canvas.test.js +21 -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 +95 -0
- package/test/qml-controls-window.test.js +182 -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-fonts.test.js +31 -0
- package/test/qml-grid-multiline.test.js +48 -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-locale.test.js +45 -0
- package/test/qml-media.test.js +63 -0
- package/test/qml-nonvisual-visible.test.js +19 -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-shader-runtime.test.js +6 -0
- package/test/qml-shortcuts.test.js +39 -0
- package/test/qml-socket.test.js +60 -0
- package/test/qml-software-shader.test.js +55 -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-window-update.test.js +36 -0
- package/test/qml-worker.test.js +69 -0
- package/test/qml.test.js +83 -0
- package/test/render-interval.test.js +33 -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/title-no-paint.test.js +6 -0
- package/test/wayland-frontend.test.js +83 -0
- package/test/wayland-scroll.test.js +40 -0
- package/test/wayland-urlbar.test.js +61 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
pragma Singleton
|
|
2
|
+
import QtQuick
|
|
3
|
+
import Quickshell
|
|
4
|
+
import Quickshell.Io
|
|
5
|
+
import "BorderGeometry.js" as Geometry
|
|
6
|
+
|
|
7
|
+
// Color surfaces for the shell. Foundational palette (foreground, background,
|
|
8
|
+
// accent, urgent) comes from theme/colors.toml. Per-surface roles come from
|
|
9
|
+
// theme/shell.toml — generated per theme from default/themed/shell.toml.tpl,
|
|
10
|
+
// or shipped directly by a theme to replace the generated file. Surfaces that
|
|
11
|
+
// don't appear in shell.toml fall back to the foundational palette.
|
|
12
|
+
QtObject {
|
|
13
|
+
id: root
|
|
14
|
+
|
|
15
|
+
readonly property string home: Quickshell.env("HOME")
|
|
16
|
+
readonly property string stateHome: home + "/.local/state"
|
|
17
|
+
readonly property string currentThemePath: stateHome + "/omarchy/current/theme"
|
|
18
|
+
|
|
19
|
+
property color foreground: "#cacccc"
|
|
20
|
+
property color background: "#101315"
|
|
21
|
+
property color accent: "#cacccc"
|
|
22
|
+
property color urgent: "#a55555"
|
|
23
|
+
property color muted: "#707880"
|
|
24
|
+
|
|
25
|
+
// Flat dictionary of "section.key" -> raw string from shell.toml.
|
|
26
|
+
// Reassigning this whole property is what makes surface bindings below
|
|
27
|
+
// re-evaluate when the theme swaps; mutating it in place would not.
|
|
28
|
+
property var shellValues: ({})
|
|
29
|
+
|
|
30
|
+
function pick(key, fallback) {
|
|
31
|
+
var v = shellValues[key]
|
|
32
|
+
return (typeof v === "string" && v.length > 0) ? v : fallback
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function pickAlpha(key, fallback) {
|
|
36
|
+
var v = shellValues[key]
|
|
37
|
+
if (typeof v !== "string" || v.length === 0) return fallback
|
|
38
|
+
var n = Number(v)
|
|
39
|
+
if (!isFinite(n)) return fallback
|
|
40
|
+
return Util.clampAlpha(n)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function firstColorToken(value) {
|
|
44
|
+
var parts = String(value || "").replace(/^\s+|\s+$/g, "").split(/\s+/)
|
|
45
|
+
for (var i = 0; i < parts.length; i++) {
|
|
46
|
+
if (!parts[i].match(/^-?\d+(?:\.\d+)?deg$/)) return parts[i]
|
|
47
|
+
}
|
|
48
|
+
return value
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function flatColor(value, fallback) {
|
|
52
|
+
var seen = Object.create(null)
|
|
53
|
+
var token = firstColorToken(value)
|
|
54
|
+
var role = String(token || "").replace(/^\s+|\s+$/g, "").toLowerCase()
|
|
55
|
+
while (root.shellValues[role] && root.shellValues[role] !== token) {
|
|
56
|
+
if (seen[role]) return fallback
|
|
57
|
+
seen[role] = true
|
|
58
|
+
token = firstColorToken(root.shellValues[role])
|
|
59
|
+
role = String(token || "").replace(/^\s+|\s+$/g, "").toLowerCase()
|
|
60
|
+
}
|
|
61
|
+
if (role === "foreground" || role === "text") return root.foreground
|
|
62
|
+
if (role === "accent") return root.accent
|
|
63
|
+
if (role === "urgent") return root.urgent
|
|
64
|
+
if (role === "muted") return root.muted
|
|
65
|
+
if (role === "background") return root.background
|
|
66
|
+
if (role === "transparent") return Qt.rgba(0, 0, 0, 0)
|
|
67
|
+
|
|
68
|
+
var color = Geometry.canonicalColor(token, 1)
|
|
69
|
+
if (typeof color === "string" && color === token && token.charAt(0) !== "#") return fallback
|
|
70
|
+
return color
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Compose a color from a base-color key and its `-alpha` companion. If the
|
|
74
|
+
// base token is a gradient, color-only consumers use the first stop.
|
|
75
|
+
function composed(colorKey, alphaKey, colorFallback, alphaFallback) {
|
|
76
|
+
return Util.alpha(flatColor(pick(colorKey, colorFallback), colorFallback), pickAlpha(alphaKey, alphaFallback))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
readonly property QtObject bar: QtObject {
|
|
80
|
+
property color background: root.composed("bar.background", "bar.background-alpha", root.background, 1.0)
|
|
81
|
+
property color text: root.pick("bar.text", root.foreground)
|
|
82
|
+
property color active: root.pick("bar.active", root.urgent)
|
|
83
|
+
}
|
|
84
|
+
readonly property QtObject popups: QtObject {
|
|
85
|
+
property color background: root.composed("popups.background", "popups.background-alpha", root.background, 1.0)
|
|
86
|
+
property color text: root.pick("popups.text", root.foreground)
|
|
87
|
+
property color border: root.composed("popups.border", "popups.border-alpha", root.accent, 1.0)
|
|
88
|
+
}
|
|
89
|
+
readonly property QtObject tooltip: QtObject {
|
|
90
|
+
property color background: root.composed("tooltip.background", "tooltip.background-alpha", root.background, 1.0)
|
|
91
|
+
property color text: root.pick("tooltip.text", root.foreground)
|
|
92
|
+
property color border: root.composed("tooltip.border", "tooltip.border-alpha", root.foreground, 1.0)
|
|
93
|
+
}
|
|
94
|
+
readonly property QtObject notifications: QtObject {
|
|
95
|
+
property color background: root.composed("notifications.background", "notifications.background-alpha", root.background, 1.0)
|
|
96
|
+
property color text: root.pick("notifications.text", root.foreground)
|
|
97
|
+
property color border: root.composed("notifications.border", "notifications.border-alpha", root.accent, 1.0)
|
|
98
|
+
property color countdown: root.pick("notifications.countdown", root.accent)
|
|
99
|
+
}
|
|
100
|
+
readonly property QtObject menu: QtObject {
|
|
101
|
+
property color background: root.composed("menu.background", "menu.background-alpha", root.background, 1.0)
|
|
102
|
+
property color text: root.pick("menu.text", root.foreground)
|
|
103
|
+
property color border: root.composed("menu.border", "menu.border-alpha", root.foreground, 1.0)
|
|
104
|
+
property color scrim: root.composed("menu.scrim", "menu.scrim-alpha", root.background, 0.5)
|
|
105
|
+
property color selectedBackground: root.composed("menu.selected-background", "menu.selected-background-alpha", root.foreground, 0.08)
|
|
106
|
+
property color selectedText: root.pick("menu.selected-text", root.accent)
|
|
107
|
+
property color selectedBorder: root.composed("menu.selected-border", "menu.selected-border-alpha", root.foreground, 0.0)
|
|
108
|
+
}
|
|
109
|
+
// polkit + lock share a single border-alpha across border / border-active /
|
|
110
|
+
// border-error: the three states are mutually exclusive in time, so one
|
|
111
|
+
// companion is enough.
|
|
112
|
+
readonly property QtObject polkit: QtObject {
|
|
113
|
+
property color background: root.composed("polkit.background", "polkit.background-alpha", root.background, 1.0)
|
|
114
|
+
property color text: root.pick("polkit.text", root.foreground)
|
|
115
|
+
property color textError: root.pick("polkit.text-error", root.urgent)
|
|
116
|
+
property color border: root.composed("polkit.border", "polkit.border-alpha", root.accent, 1.0)
|
|
117
|
+
property color borderError: root.composed("polkit.border-error", "polkit.border-alpha", root.urgent, 1.0)
|
|
118
|
+
property color accent: root.pick("polkit.accent", root.accent)
|
|
119
|
+
property color scrim: root.composed("polkit.scrim", "polkit.scrim-alpha", root.background, 0.5)
|
|
120
|
+
}
|
|
121
|
+
readonly property QtObject lock: QtObject {
|
|
122
|
+
property color background: root.composed("lock.background", "lock.background-alpha", root.background, 0.8)
|
|
123
|
+
property color text: root.pick("lock.text", root.foreground)
|
|
124
|
+
property color placeholder: root.shellValues["lock.placeholder"] ? root.flatColor(root.shellValues["lock.placeholder"], Util.alpha(root.foreground, 0.66)) : Util.alpha(root.foreground, 0.66)
|
|
125
|
+
property color textError: root.pick("lock.text-error", root.urgent)
|
|
126
|
+
property color border: root.composed("lock.border", "lock.border-alpha", root.foreground, 1.0)
|
|
127
|
+
property color borderActive: root.composed("lock.border-active", "lock.border-alpha", root.accent, 1.0)
|
|
128
|
+
property color borderError: root.composed("lock.border-error", "lock.border-alpha", root.urgent, 1.0)
|
|
129
|
+
property color selection: root.composed("lock.selection", "lock.selection-alpha", root.accent, 0.45)
|
|
130
|
+
}
|
|
131
|
+
// The image picker has no card surface or backdrop; per-slice dim
|
|
132
|
+
// overlays / text outlines use the foundational `background` color directly.
|
|
133
|
+
readonly property QtObject imagePicker: QtObject {
|
|
134
|
+
property color text: root.pick("image-picker.text", root.foreground)
|
|
135
|
+
property color selectedBorder: root.composed("image-picker.selected-border", "image-picker.selected-border-alpha", root.accent, 1.0)
|
|
136
|
+
property color unselectedBorder: root.composed("image-picker.unselected-border", "image-picker.unselected-border-alpha", root.foreground, 0.28)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function loadColors(raw) {
|
|
140
|
+
var lines = String(raw || "").split("\n")
|
|
141
|
+
var foundAccent = false
|
|
142
|
+
var foundMuted = false
|
|
143
|
+
var loadedForeground = false
|
|
144
|
+
var loadedBackground = false
|
|
145
|
+
var color0Value = ""
|
|
146
|
+
var color4Value = ""
|
|
147
|
+
var color7Value = ""
|
|
148
|
+
var color8Value = ""
|
|
149
|
+
for (var i = 0; i < lines.length; i++) {
|
|
150
|
+
var match = lines[i].match(/^\s*([A-Za-z0-9_-]+)\s*=\s*["']?(#[0-9A-Fa-f]{6})/)
|
|
151
|
+
if (!match) continue
|
|
152
|
+
if (match[1] === "foreground") { foreground = match[2]; loadedForeground = true }
|
|
153
|
+
else if (match[1] === "background") { background = match[2]; loadedBackground = true }
|
|
154
|
+
// Prefer the explicit `accent` key; only fall back to color4 when the
|
|
155
|
+
// theme doesn't define a separate accent. color4 appears later in the
|
|
156
|
+
// file so the old single-property approach clobbered accent with it.
|
|
157
|
+
else if (match[1] === "accent") { accent = match[2]; foundAccent = true }
|
|
158
|
+
else if (match[1] === "muted") { muted = match[2]; foundMuted = true }
|
|
159
|
+
else if (match[1] === "color0") color0Value = match[2]
|
|
160
|
+
else if (match[1] === "color4") color4Value = match[2]
|
|
161
|
+
else if (match[1] === "color7") color7Value = match[2]
|
|
162
|
+
else if (match[1] === "color8") color8Value = match[2]
|
|
163
|
+
else if (match[1] === "red" || match[1] === "color1") urgent = match[2]
|
|
164
|
+
}
|
|
165
|
+
if (!loadedBackground && color0Value.length > 0) background = color0Value
|
|
166
|
+
if (!loadedForeground && color7Value.length > 0) foreground = color7Value
|
|
167
|
+
if (!foundAccent && color4Value.length > 0) accent = color4Value
|
|
168
|
+
if (!foundMuted) muted = color8Value.length > 0 ? color8Value : foreground
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Last theme-supplied and user-supplied shell.toml dicts, kept separate so
|
|
172
|
+
// either can be reloaded without re-reading the other. `shellValues` is
|
|
173
|
+
// always the merge of theme (base) and user (override) — see mergeShell.
|
|
174
|
+
property var themeShellValues: ({})
|
|
175
|
+
property var userShellValues: ({})
|
|
176
|
+
|
|
177
|
+
// Single TOML walker for shell.toml. Both Color (surface roles) and Style
|
|
178
|
+
// (typography, spacing, bar, control states) consume the resulting dict.
|
|
179
|
+
// Accepts quoted strings, bare numeric values, bare width lists, and bare
|
|
180
|
+
// role names; tolerates inline comments. Numbers are kept as strings here —
|
|
181
|
+
// readers coerce when they pull a value.
|
|
182
|
+
function parseShell(raw) {
|
|
183
|
+
var parsed = {}
|
|
184
|
+
var text = String(raw || "")
|
|
185
|
+
if (text) {
|
|
186
|
+
var lines = text.split("\n")
|
|
187
|
+
var section = ""
|
|
188
|
+
for (var i = 0; i < lines.length; i++) {
|
|
189
|
+
var line = lines[i].replace(/^\s+|\s+$/g, "")
|
|
190
|
+
if (!line || line.charAt(0) === "#") continue
|
|
191
|
+
var sectionMatch = line.match(/^\[([A-Za-z0-9_-]+)\]\s*(#.*)?$/)
|
|
192
|
+
if (sectionMatch) { section = sectionMatch[1]; continue }
|
|
193
|
+
var stringKv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*["']([^"']+)["']\s*(#.*)?$/)
|
|
194
|
+
var numKv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*(-?\d+(?:\.\d+)?)\s*(#.*)?$/)
|
|
195
|
+
var widthKv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*(-?\d+(?:\.\d+)?(?:\s+-?\d+(?:\.\d+)?){1,3})\s*(#.*)?$/)
|
|
196
|
+
var bareKv = line.match(/^([A-Za-z0-9_-]+)\s*=\s*([A-Za-z][A-Za-z0-9_-]*)\s*(#.*)?$/)
|
|
197
|
+
var kv = stringKv || numKv || widthKv || bareKv
|
|
198
|
+
if (!kv || !section) continue
|
|
199
|
+
parsed[section + "." + kv[1]] = kv[2]
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return parsed
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Re-derive `shellValues` from theme base + user override and push it to
|
|
206
|
+
// Style. User keys win, so a machine-level `~/.config/omarchy/shell.toml`
|
|
207
|
+
// survives theme switches (which replace only themeShellValues).
|
|
208
|
+
function mergeShell() {
|
|
209
|
+
var merged = {}
|
|
210
|
+
for (var tk in themeShellValues) merged[tk] = themeShellValues[tk]
|
|
211
|
+
for (var uk in userShellValues) merged[uk] = userShellValues[uk]
|
|
212
|
+
shellValues = merged
|
|
213
|
+
Style.applyShellValues(merged)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function loadShell(raw) {
|
|
217
|
+
themeShellValues = parseShell(raw)
|
|
218
|
+
mergeShell()
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function loadUserShell(raw) {
|
|
222
|
+
userShellValues = parseShell(raw)
|
|
223
|
+
mergeShell()
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Startup load only. Runtime theme switches push the payload explicitly
|
|
227
|
+
// through shell IPC.
|
|
228
|
+
property FileView colorsFile: FileView {
|
|
229
|
+
id: colorsFile
|
|
230
|
+
path: root.currentThemePath + "/colors.toml"
|
|
231
|
+
watchChanges: false
|
|
232
|
+
printErrors: false
|
|
233
|
+
onLoaded: root.loadColors(text())
|
|
234
|
+
}
|
|
235
|
+
property FileView shellFile: FileView {
|
|
236
|
+
id: shellFile
|
|
237
|
+
path: root.currentThemePath + "/shell.toml"
|
|
238
|
+
watchChanges: false
|
|
239
|
+
printErrors: false
|
|
240
|
+
onLoaded: root.loadShell(text())
|
|
241
|
+
onLoadFailed: root.loadShell("")
|
|
242
|
+
}
|
|
243
|
+
// Machine-level override, layered on top of whatever theme is active. This
|
|
244
|
+
// is where `omarchy display text size` writes `[font] base-size`. Watched so the
|
|
245
|
+
// CLI takes effect live without restarting the shell; absent by default.
|
|
246
|
+
property FileView userShellFile: FileView {
|
|
247
|
+
id: userShellFile
|
|
248
|
+
path: root.home + "/.config/omarchy/shell.toml"
|
|
249
|
+
watchChanges: true
|
|
250
|
+
printErrors: false
|
|
251
|
+
onLoaded: root.loadUserShell(text())
|
|
252
|
+
// Re-read on change (including first creation) before loading — `text()`
|
|
253
|
+
// is stale in the change signal itself, so route both paths through reload
|
|
254
|
+
// → onLoaded to always parse fresh content.
|
|
255
|
+
onFileChanged: reload()
|
|
256
|
+
onLoadFailed: root.loadUserShell("")
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
pragma Singleton
|
|
2
|
+
import QtQuick
|
|
3
|
+
import Quickshell
|
|
4
|
+
import Quickshell.Io
|
|
5
|
+
import "Plural.js" as Plural
|
|
6
|
+
import "Numbers.js" as Numbers
|
|
7
|
+
|
|
8
|
+
// Display-language strings for the shell. The selected code lives in
|
|
9
|
+
// ~/.config/omarchy/language (omarchy-language owns it); a missing file
|
|
10
|
+
// means English. Strings come from default/i18n/<code>/shell.json layered
|
|
11
|
+
// over the English catalog, so a missing translation falls back to English
|
|
12
|
+
// per string. Use as I18n.t("common.search"), with .arg(...) for %1 holes,
|
|
13
|
+
// and I18n.tn(key, count).arg(I18n.formatNumber(count)) for an
|
|
14
|
+
// entry with plural forms.
|
|
15
|
+
// The language file is watched, so switching language under Setup
|
|
16
|
+
// re-renders the shell without a restart.
|
|
17
|
+
QtObject {
|
|
18
|
+
id: root
|
|
19
|
+
|
|
20
|
+
readonly property string omarchyPath: Quickshell.env("OMARCHY_PATH")
|
|
21
|
+
readonly property string configHome: Quickshell.env("XDG_CONFIG_HOME") || (Quickshell.env("HOME") + "/.config")
|
|
22
|
+
|
|
23
|
+
readonly property string language: languageCode(languageFile.text())
|
|
24
|
+
readonly property var localeSettings: parseCatalog(registryFile.text())[root.language] || ({})
|
|
25
|
+
readonly property string formatLocale: localeSettings.formatLocale || "en-US"
|
|
26
|
+
// From the registry: how numbers are written and which way text runs.
|
|
27
|
+
readonly property string decimal: localeSettings.decimal || "."
|
|
28
|
+
readonly property string zeroDigit: localeSettings.zeroDigit || "0"
|
|
29
|
+
readonly property bool rightToLeft: localeSettings.direction === "rtl"
|
|
30
|
+
|
|
31
|
+
// Reading text() makes blockLoading take effect on the first lookup.
|
|
32
|
+
// These bindings also rebuild the maps when a watched catalog changes.
|
|
33
|
+
readonly property var englishStrings: parseCatalog(englishCatalogFile.text())
|
|
34
|
+
readonly property var languageStrings: parseCatalog(languageCatalogFile.text())
|
|
35
|
+
readonly property var strings: {
|
|
36
|
+
var merged = ({})
|
|
37
|
+
for (var k in root.englishStrings) merged[k] = root.englishStrings[k]
|
|
38
|
+
for (var k2 in root.languageStrings) merged[k2] = root.languageStrings[k2]
|
|
39
|
+
return merged
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The optional fallback is what imperative callers (the JS models) pass
|
|
43
|
+
// as their English text: for a key the catalogs do not carry, they keep
|
|
44
|
+
// sensible English instead of a raw key.
|
|
45
|
+
function t(key, fallback) {
|
|
46
|
+
var value = root.strings[key]
|
|
47
|
+
if (value && typeof value === "object") value = value.other
|
|
48
|
+
if (typeof value === "string" && value.length > 0) return value
|
|
49
|
+
return fallback !== undefined ? fallback : key
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The form of a plural entry for count, by the language's plural rules
|
|
53
|
+
// (Plural.js). The English entry answers when the translation has none.
|
|
54
|
+
function tn(key, count, fallback) {
|
|
55
|
+
var value = Plural.pick(root.languageStrings[key], root.language, count)
|
|
56
|
+
if (typeof value !== "string" || value.length === 0) value = Plural.pick(root.englishStrings[key], "en", count)
|
|
57
|
+
if (typeof value === "string" && value.length > 0) return value
|
|
58
|
+
return fallback !== undefined ? fallback : key
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// A number with the language's decimal separator and digits, ungrouped:
|
|
62
|
+
// formatNumber(13.14, 1) is "13,1" in Danish. Pass decimals to round;
|
|
63
|
+
// without them the value is written as it is.
|
|
64
|
+
function formatNumber(value, decimals) {
|
|
65
|
+
return Numbers.format(value, decimals, root.decimal, root.zeroDigit)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseCatalog(raw) {
|
|
69
|
+
try {
|
|
70
|
+
var parsed = JSON.parse(raw || "{}")
|
|
71
|
+
return (parsed && typeof parsed === "object" && !Array.isArray(parsed)) ? parsed : ({})
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return ({})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function languageCode(raw) {
|
|
78
|
+
var code = String(raw || "").trim()
|
|
79
|
+
if (!/^[a-z]{2,3}(-[A-Za-z]{2})?$/.test(code)) code = "en"
|
|
80
|
+
return code
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// The catalogs are small local files, read synchronously so the first
|
|
84
|
+
// binding to call t() already gets the string rather than the raw key.
|
|
85
|
+
property FileView languageFile: FileView {
|
|
86
|
+
// OMARCHY_LANGUAGE_FILE overrides the location, as it does for
|
|
87
|
+
// omarchy-i18n, so tests can pin a language whatever the machine uses.
|
|
88
|
+
path: Quickshell.env("OMARCHY_LANGUAGE_FILE") || (root.configHome + "/omarchy/language")
|
|
89
|
+
watchChanges: true
|
|
90
|
+
printErrors: false
|
|
91
|
+
blockLoading: true
|
|
92
|
+
onFileChanged: reload()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
property FileView englishCatalogFile: FileView {
|
|
96
|
+
path: root.omarchyPath + "/default/i18n/en/shell.json"
|
|
97
|
+
watchChanges: true
|
|
98
|
+
printErrors: false
|
|
99
|
+
blockLoading: true
|
|
100
|
+
onFileChanged: reload()
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
property FileView languageCatalogFile: FileView {
|
|
104
|
+
path: root.omarchyPath + "/default/i18n/" + root.language + "/shell.json"
|
|
105
|
+
watchChanges: true
|
|
106
|
+
printErrors: false
|
|
107
|
+
blockLoading: true
|
|
108
|
+
onFileChanged: reload()
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
property FileView registryFile: FileView {
|
|
112
|
+
path: root.omarchyPath + "/default/i18n/locales.json"
|
|
113
|
+
watchChanges: true
|
|
114
|
+
printErrors: false
|
|
115
|
+
blockLoading: true
|
|
116
|
+
onFileChanged: reload()
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// A number written with a language's decimal separator and digits from the
|
|
2
|
+
// registry, ungrouped: format(13.14, 1, ",", "0") is "13,1". Without
|
|
3
|
+
// decimals the value is written as it is. default/i18n/plural.jq's
|
|
4
|
+
// localize_number does the same for omarchy-i18n.
|
|
5
|
+
function format(value, decimals, decimal, zeroDigit) {
|
|
6
|
+
var number = Number(value)
|
|
7
|
+
if (!isFinite(number)) return String(value)
|
|
8
|
+
var text = decimals === undefined ? String(number) : number.toFixed(decimals)
|
|
9
|
+
if (decimal && decimal !== ".") text = text.replace(".", decimal)
|
|
10
|
+
if (zeroDigit && zeroDigit !== "0") {
|
|
11
|
+
var zero = zeroDigit.charCodeAt(0)
|
|
12
|
+
text = text.replace(/[0-9]/g, function(digit) { return String.fromCharCode(zero + digit.charCodeAt(0) - 48) })
|
|
13
|
+
}
|
|
14
|
+
return text
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (typeof module !== "undefined") {
|
|
18
|
+
module.exports = { format: format }
|
|
19
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// CLDR cardinal plural categories for whole counts, per Omarchy language
|
|
2
|
+
// code. A plural catalog entry is an object keyed by these categories
|
|
3
|
+
// ({ "one": "%1 file", "other": "%1 files" }); every language needs "other",
|
|
4
|
+
// plus whichever categories its counts can reach. default/i18n/plural.jq
|
|
5
|
+
// holds the same rules for omarchy-i18n, and i18n-test.sh keeps the two
|
|
6
|
+
// in agreement.
|
|
7
|
+
|
|
8
|
+
function within(value, low, high) {
|
|
9
|
+
return value >= low && value <= high
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function category(code, count) {
|
|
13
|
+
var n = Math.abs(Math.trunc(Number(count) || 0))
|
|
14
|
+
var n10 = n % 10
|
|
15
|
+
var n100 = n % 100
|
|
16
|
+
var millions = n !== 0 && n % 1000000 === 0
|
|
17
|
+
|
|
18
|
+
switch (String(code || "")) {
|
|
19
|
+
case "ja": case "ko": case "zh-CN": case "zh-TW": case "th": case "vi": case "id": case "ms":
|
|
20
|
+
return "other"
|
|
21
|
+
case "hi": case "bn": case "fa": case "si":
|
|
22
|
+
return n <= 1 ? "one" : "other"
|
|
23
|
+
case "fr": case "pt-BR":
|
|
24
|
+
return n <= 1 ? "one" : millions ? "many" : "other"
|
|
25
|
+
case "ca": case "es-MX": case "it": case "pt":
|
|
26
|
+
return n === 1 ? "one" : millions ? "many" : "other"
|
|
27
|
+
case "fil":
|
|
28
|
+
return (n10 === 4 || n10 === 6 || n10 === 9) ? "other" : "one"
|
|
29
|
+
case "is":
|
|
30
|
+
return n10 === 1 && n100 !== 11 ? "one" : "other"
|
|
31
|
+
case "cs": case "sk":
|
|
32
|
+
return n === 1 ? "one" : within(n, 2, 4) ? "few" : "other"
|
|
33
|
+
case "pl":
|
|
34
|
+
if (n === 1) return "one"
|
|
35
|
+
return within(n10, 2, 4) && !within(n100, 12, 14) ? "few" : "many"
|
|
36
|
+
case "ru": case "uk":
|
|
37
|
+
if (n10 === 1 && n100 !== 11) return "one"
|
|
38
|
+
return within(n10, 2, 4) && !within(n100, 12, 14) ? "few" : "many"
|
|
39
|
+
case "hr": case "sr":
|
|
40
|
+
if (n10 === 1 && n100 !== 11) return "one"
|
|
41
|
+
return within(n10, 2, 4) && !within(n100, 12, 14) ? "few" : "other"
|
|
42
|
+
case "sl":
|
|
43
|
+
if (n100 === 1) return "one"
|
|
44
|
+
if (n100 === 2) return "two"
|
|
45
|
+
return within(n100, 3, 4) ? "few" : "other"
|
|
46
|
+
case "lt":
|
|
47
|
+
if (within(n100, 11, 19)) return "other"
|
|
48
|
+
return n10 === 1 ? "one" : n10 >= 2 ? "few" : "other"
|
|
49
|
+
case "lv":
|
|
50
|
+
if (n10 === 0 || within(n100, 11, 19)) return "zero"
|
|
51
|
+
return n10 === 1 ? "one" : "other"
|
|
52
|
+
case "ro":
|
|
53
|
+
if (n === 1) return "one"
|
|
54
|
+
return n === 0 || within(n100, 1, 19) ? "few" : "other"
|
|
55
|
+
case "ar":
|
|
56
|
+
if (n <= 2) return ["zero", "one", "two"][n]
|
|
57
|
+
return within(n100, 3, 10) ? "few" : within(n100, 11, 99) ? "many" : "other"
|
|
58
|
+
case "he":
|
|
59
|
+
return n === 1 ? "one" : n === 2 ? "two" : "other"
|
|
60
|
+
case "ga":
|
|
61
|
+
if (n <= 2) return n === 0 ? "other" : n === 1 ? "one" : "two"
|
|
62
|
+
return within(n, 3, 6) ? "few" : within(n, 7, 10) ? "many" : "other"
|
|
63
|
+
default:
|
|
64
|
+
return n === 1 ? "one" : "other"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
var ORDER = ["zero", "one", "two", "few", "many", "other"]
|
|
69
|
+
|
|
70
|
+
// The categories a translation of a plural entry must provide.
|
|
71
|
+
function categories(code) {
|
|
72
|
+
var seen = { other: true }
|
|
73
|
+
for (var n = 0; n <= 1000; n++) seen[category(code, n)] = true
|
|
74
|
+
seen[category(code, 1000000)] = true
|
|
75
|
+
return ORDER.filter(function(name) { return seen[name] })
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// The string for a count from a plural entry, or the entry itself when it
|
|
79
|
+
// is a plain string. A translation missing a category falls back to its
|
|
80
|
+
// "other" form.
|
|
81
|
+
function pick(entry, code, count) {
|
|
82
|
+
if (typeof entry === "string") return entry
|
|
83
|
+
if (!entry || typeof entry !== "object") return undefined
|
|
84
|
+
var form = entry[category(code, count)]
|
|
85
|
+
if (typeof form === "string" && form.length > 0) return form
|
|
86
|
+
return typeof entry.other === "string" ? entry.other : undefined
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (typeof module !== "undefined") {
|
|
90
|
+
module.exports = { category: category, categories: categories, pick: pick }
|
|
91
|
+
}
|