diff --git a/.editorconfig b/.editorconfig index 572b7d2..e948b32 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,7 +6,7 @@ insert_final_newline = true indent_style = space charset = utf-8 -[*.{js,json}] +[*.{js,ts,json}] indent_size = 4 trim_trailing_whitespace = true diff --git a/.eslintignore b/.eslintignore index 2af37ff..524273b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,7 @@ -/docs/panel.js +/docs/panel_master_2021-04-21.js +/docs/panel_42.5_2022-10-22.js +/docs/panel_43.2_2023-01-24.js +/docs/panel_45.0_2023-09-26.js +/docs/panel_46.4_2024-09-11.js +/docs/panel_47.rc_2024-09-12.js +/docs/panel_48.2_2025-06-08.js diff --git a/.eslintrc.yml b/.eslintrc.yml index 12f1a2e..caf7992 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -2,7 +2,8 @@ env: es2021: true extends: 'eslint:recommended' parserOptions: - ecmaVersion: 12 + ecmaVersion: 2022 + sourceType: module rules: indent: - error @@ -17,5 +18,43 @@ rules: semi: - error - always + no-unused-vars: + - error + - argsIgnorePattern: "^_" + curly: + - error + - all + brace-style: + - error + - 1tbs + # Rules from GJS Style Guide regarding whitespace. + # See here: https://gjs.guide/guides/gjs/style-guide.html#whitespace + func-call-spacing: + - error + - never + array-bracket-spacing: + - error + - never + space-before-function-paren: + - error + - never + space-before-blocks: + - error + - always + key-spacing: + - error + - beforeColon: false + afterColon: true + mode: strict + object-curly-spacing: + - error + - always + comma-dangle: + - error + - arrays: always-multiline + objects: always-multiline + imports: always-multiline + exports: always-multiline + functions: never globals: - imports: readonly + log: readonly diff --git a/.gitignore b/.gitignore index eb99e38..9bb16b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /node_modules/ -gschemas.compiled -top-bar-organizer@julian.gse.jsts.xyz.zip +/dist/ +top-bar-organizer@julian.gse.jsts.xyz.shell-extension.zip diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md new file mode 100644 index 0000000..2caef4d --- /dev/null +++ b/.gitlab/issue_templates/Bug.md @@ -0,0 +1,49 @@ +### Bug Description + + + +### Steps to Reproduce + + + +### What behavior did you observe? + + + +### What behavior did you expect? + + + +### Further Information + + + + + +### System Information + +- Operating System: +- GNOME Version: + +Enabled Extensions: + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 72a8f7e..3df8919 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,22 +9,22 @@ For code of this repo in `.js` files, stick to a textwidth of 80. The commit message format is as follows: ``` -Tag: Short description +tag: short description Longer description here if necessary ``` -The `Tag` should be one of the following: +The `tag` should be one of the following: -- `Fix` - for a bug fix. -- `Update` - for a backwards compatible enhancement. -- `Feature` (formerly also `New`) - for a new feature. -- `Breaking` - for a backwards-incompatible enhancement or feature. -- `Perf` - for a code change that improves performance. -- `Refactor` - for a code change that neither fixes a bug nor adds a feature (nor is `Perf`). -- `Build` - for changes that affect the build system or external dependencies. -- `Test` - for adding or correcting tests. -- `Docs` - for changes to documentation only. -- `Other` - for anything that isn't covered by the tags above. +- `fix` - for a bug fix. +- `update` - for a backwards compatible enhancement. +- `feature` (formerly also `New`) - for a new feature. +- `breaking` - for a backwards-incompatible enhancement or feature. +- `perf` - for a code change that improves performance. +- `refactor` - for a code change that neither fixes a bug nor adds a feature (nor is `perf`). +- `build` - for changes that affect the build system or external dependencies. +- `test` - for adding or correcting tests. +- `docs` - for changes to documentation only. +- `other` - for anything that isn't covered by the tags above. Those tags are an adapted version of the tags eslint () and of the tags Angular () uses. diff --git a/README.md b/README.md new file mode 100644 index 0000000..91ea15c --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Top Bar Organizer + +![Screenshot of GNOME Shell 43 with Top Bar Organizer v6 running and its preferences open. The GNOME Shell top bar items aren't all in their default location.](./res/Screenshot%20of%20GNOME%20Shell%2043%20with%20Top%20Bar%20Organizer%20v6%20and%20its%20preferences%20in%20light%20theme%202023-01-30.jpg) + +Top Bar Organizer allows you to organize the items of the GNOME Shell top (menu)bar. + +## Installation + +The extension is available on the [GNOME Extensions website](https://extensions.gnome.org/extension/4356/top-bar-organizer/). +Or you can also manually install a release from the [releases page](https://gitlab.gnome.org/june/top-bar-organizer/-/releases). + +There's also a community-maintained [AUR package](https://aur.archlinux.org/packages/gnome-shell-extension-top-bar-organizer) available. diff --git a/ambient.d.ts b/ambient.d.ts new file mode 100644 index 0000000..842985c --- /dev/null +++ b/ambient.d.ts @@ -0,0 +1,4 @@ +import "@girs/gjs" +import "@girs/gjs/dom" +import "@girs/gnome-shell/ambient" +import "@girs/gnome-shell/extensions/global" diff --git a/data/css/prefs.css b/data/css/prefs.css new file mode 100644 index 0000000..5cafea5 --- /dev/null +++ b/data/css/prefs.css @@ -0,0 +1,9 @@ +/* Taken from: https://gitlab.gnome.org/GNOME/gnome-control-center/-/blob/43.5/shell/style.css */ + +.drag-handle { + color: alpha(@theme_fg_color, 0.4); +} + +.drag-handle:backdrop { + color: alpha(@theme_unfocused_fg_color, 0.4); +} diff --git a/data/org.gnome.shell.extensions.top-bar-organizer.gschema.xml b/data/org.gnome.shell.extensions.top-bar-organizer.gschema.xml new file mode 100644 index 0000000..8b143e7 --- /dev/null +++ b/data/org.gnome.shell.extensions.top-bar-organizer.gschema.xml @@ -0,0 +1,25 @@ + + + + + Order of items in the left box of the top bar. + [] + + + Order of items in the center box of the top bar. + [] + + + Order of items in the right box of the top bar. + [] + + + Top bar items to (forcefully) hide. + [] + + + Top bar items to (forcefully) show. + [] + + + diff --git a/data/ui/prefs-box-order-item-options-dialog.ui b/data/ui/prefs-box-order-item-options-dialog.ui new file mode 100644 index 0000000..d953d24 --- /dev/null +++ b/data/ui/prefs-box-order-item-options-dialog.ui @@ -0,0 +1,38 @@ + + + + diff --git a/data/ui/prefs-box-order-item-row.ui b/data/ui/prefs-box-order-item-row.ui new file mode 100644 index 0000000..dd06b32 --- /dev/null +++ b/data/ui/prefs-box-order-item-row.ui @@ -0,0 +1,62 @@ + + + + +
+ + Move Up + row.move-up + + + Move Down + row.move-down + +
+
+ + Options + row.options + +
+
+ + Forget + row.forget + +
+
+
diff --git a/data/ui/prefs-box-order-list-box.ui b/data/ui/prefs-box-order-list-box.ui new file mode 100644 index 0000000..46c77d2 --- /dev/null +++ b/data/ui/prefs-box-order-list-box.ui @@ -0,0 +1,10 @@ + + + + diff --git a/src/prefs-box-order-list-empty-placeholder.ui b/data/ui/prefs-box-order-list-empty-placeholder.ui similarity index 54% rename from src/prefs-box-order-list-empty-placeholder.ui rename to data/ui/prefs-box-order-list-empty-placeholder.ui index 11803f8..d113107 100644 --- a/src/prefs-box-order-list-empty-placeholder.ui +++ b/data/ui/prefs-box-order-list-empty-placeholder.ui @@ -1,22 +1,4 @@ - diff --git a/data/ui/prefs-page.ui b/data/ui/prefs-page.ui new file mode 100644 index 0000000..7eca8d0 --- /dev/null +++ b/data/ui/prefs-page.ui @@ -0,0 +1,52 @@ + + + + diff --git a/docs/Creating_a_New_Release.md b/docs/Creating_a_New_Release.md new file mode 100644 index 0000000..049afd2 --- /dev/null +++ b/docs/Creating_a_New_Release.md @@ -0,0 +1,51 @@ +# Creating a Release + +## Create a Tag + +To create a new tag, do the following: + +1. Fill out `git_annotated_tag_template`. +2. Run the following command to tag the current commit with `vX`: + + ``` + git tag -a -F git_annotated_tag_template -s --cleanup=verbatim vX + ``` + +3. Restore `git_annotated_tag_template` to its original state: + + ``` + git restore git_annotated_tag_template + ``` + +4. Push the tag: + + ``` + git push --tags + ``` + +## Build a Release-ZIP + +1. Build the release-ZIP: + + ``` + ./package.sh + ``` + +2. Name the release-ZIP after the current version: + + ``` + mv top-bar-organizer@julian.gse.jsts.xyz.shell-extension.zip top-bar-organizer@julian.gse.jsts.xyz.shell-extension_vX.zip + ``` + +## Create a GitLab Release + +1. Go to the [Releases section of the repo](https://gitlab.gnome.org/june/top-bar-organizer/-/releases) and click on the "New Release" button. +2. Select the corresponding tag created earlier. +3. Name the release "Top Bar Organizer vX". +4. Copy the [release notes template](./release_notes_template.md) and fill it out. +5. Drop the release-ZIP created in the previous step at the end of the release notes. +6. Create the release. + +## Uploading to the GNOME Extensions Website + +1. Go to the [upload page of the GNOME extensions website](https://extensions.gnome.org/upload/) and upload the release-ZIP. diff --git a/docs/Creating_a_New_Tag.md b/docs/Creating_a_New_Tag.md deleted file mode 100644 index 9cfb87d..0000000 --- a/docs/Creating_a_New_Tag.md +++ /dev/null @@ -1,22 +0,0 @@ -# Creating a New Tag - -To create a new tag, do the following: - -1. Fill out `git_annotated_tag_template`. -2. Run the following command: - - ``` - git tag -a -F git_annotated_tag_template -s --cleanup=verbatim [] - ``` - -3. Restore `git_annotated_tag_template` to its original state: - - ``` - git restore git_annotated_tag_template - ``` - -4. Push the new tag. - - ``` - git push --tags - ``` diff --git a/docs/Gnome_Shell_Extensions_Development_VM.md b/docs/Gnome_Shell_Extensions_Development_VM.md new file mode 100644 index 0000000..76ca83e --- /dev/null +++ b/docs/Gnome_Shell_Extensions_Development_VM.md @@ -0,0 +1,91 @@ +# Gnome Shell Extensions Development VM + +This document holds some setup instructions and tips for getting the most of your Gnome Shell Extensions Development VM. + +Note on commands in this document: + +- `$` indicates that a command should be run as your normal user. +- `#` indicates that a command should be run as root. + +## GTKInspector + +Enable GTKInspector by running the following command: + +``` +$ gsettings set org.gtk.Settings.Debug enable-inspector-keybinding true +``` + +Now you can inspect GTK Apps by pressing `Ctrl + Shift + D`. + +### Links and Sources + +- + +## Looking Glass + +Looking Glass is Gnome Shells integrated debugger and inspector tool. +You can use it by pressing `Alt + F2`, typing `lg` and pressing Enter. + +If you want to exit Looking Glass, press `Esc` in the Evaluator pane. + +### Links and Sources + +- + +## Sharing a Directory Between the Host and the Guest + +To share a directory between the host and the Gnome Shell Extensions Development VM, do the following. +Note that this guide assumes you're using Virtual Machine Manager (virt-manager) and at least v4.0.0 of it. + +1. Shut down the VM. +2. Go to your VMs hardware details and then to `Memory`. + Check the `Enable shared memory` checkbox there. +3. Go to your VMs hardware details and then to `Add Hardware -> Filesystem`. + Then select `virtiofs` for the driver and an appropriate source path (like `/home/user/gse_dev_vm_shared_folder`) and target path (like `shared_folder`). + Finally click on `Finish`. +4. Power on the VM. +5. Create a mountpoint by running: + + ``` + # mkdir /mnt/shared_host_folder + ``` + +6. Edit `/etc/fstab` and add the following line at the end: + + ``` + TARGET_PATH_YOU_SET_IN_VIRT_MANAGER /mnt/shared_host_folder virtiofs rw,noatime,_netdev 0 0 + ``` + +7. Reboot the VM. + +Now you have a shared folder between your host and your VM, which you can access on your host at the specified source path and in the VM at `/mnt/shared_host_folder`. + +### Links and Sources + +- +- +- + +## Enabling Automatic Login + +Enabling Automatic Login in the Gnome Settings under `Users -> Unlock... -> Automatic Login` saves you from inserting the VM users password after VM startups. + +## Disabling Automatic Screen Lock + +Disabling Automatic Screen Lock in the Gnome Settings under `Privacy -> Screen Lock -> Automatic Screen Lock` saves you from Gnome locking the VM and you having to insert the VM users password. + +## Running Applications Providing Tray Icons Automatically on Startup + +Especially for the development of this extension it is useful to have some applications, which provide tray icons (e.g. Element, Telegram) run automatically on startup. +To make this happen (nicely), you need to do the following: + +1. Make sure the applications get started automatically on log in by using Gnome Tweaks, going to `Startup Applications` and adding them there. +2. If you're using Automatic Login, use the Password and Keys application (seahorse) to set the Login keyrings password to a blank one. + Do this to avoid the Login keyring password prompt after log in, which is triggered by some applications like Element. + Note that this leaves the keyring unencrypted, but since you're in a Dev VM, no important things should be stored in there anyway (hopefully). +3. Use the "Auto Move Windows" extension to move the windows of your applications to a different workspace, so your main one doesn't get cluttered. +4. To see tray icons, install the "AppIndicator and KStatusNotifierItem Support" extension. + +### Links and Sources + +- diff --git a/docs/panel_42.5_2022-10-22.js b/docs/panel_42.5_2022-10-22.js new file mode 100644 index 0000000..9c96e7a --- /dev/null +++ b/docs/panel_42.5_2022-10-22.js @@ -0,0 +1,318 @@ +// My annotated and cut down `js/ui/panel.js` from gnome-shell/42.5. +// All annotations are what I guessed, interpreted and copied while reading the +// code and comparing to other `panel.js` versions and might be wrong. They are +// prefixed with "Annotation:" to indicate that they're my comments, not +// comments that orginally existed. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/42.5/js/ui/panel.js +// On: 2022-10-22 +// License: This code is licensed under GPLv2. + +// Parts taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/42.5/js/ui/sessionMode.js +// On: 2022-10-22 +// License: This code is licensed under GPLv2. + +// I'm using the word "item" to refer to the thing, which gets added to the top +// (menu)bar / panel, where an item has a role/name and an indicator. + + +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* exported Panel */ + +const { Atk, Clutter, GLib, GObject, Meta, Shell, St } = imports.gi; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. +const CtrlAltTab = imports.ui.ctrlAltTab; +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. +const PopupMenu = imports.ui.popupMenu; +const PanelMenu = imports.ui.panelMenu; +const Main = imports.ui.main; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +// Compared to panel_master_2021-04-21.js: +// The "screenRecording" entry got added. +const PANEL_ITEM_IMPLEMENTATIONS = { + 'activities': ActivitiesButton, + 'aggregateMenu': AggregateMenu, + 'appMenu': AppMenuButton, + 'dateMenu': imports.ui.dateMenu.DateMenuButton, + 'a11y': imports.ui.status.accessibility.ATIndicator, + 'keyboard': imports.ui.status.keyboard.InputSourceIndicator, + 'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator, + 'screenRecording': imports.ui.status.remoteAccess.ScreenRecordingIndicator, +}; + +var Panel = GObject.registerClass( +class Panel extends St.Widget { + // Annotation: Initializes the top (menu)bar / panel. + // Does relevant stuff like: + // - Defining `this._leftBox`, `this._centerBox` and `this._rightBox`. + // - Finally calling `this._updatePanel()`. + // Compared to panel_master_2021-04-21.js: + // Didn't really change, except for two events getting connected, which we + // probably don't care about, the corners being gone and some reformatting. + _init() { + super._init({ + name: 'panel', + reactive: true, + }); + + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._sessionStyle = null; + + this.statusArea = {}; + + this.menuManager = new PopupMenu.PopupMenuManager(this); + + this._leftBox = new St.BoxLayout({ name: 'panelLeft' }); + this.add_child(this._leftBox); + this._centerBox = new St.BoxLayout({ name: 'panelCenter' }); + this.add_child(this._centerBox); + this._rightBox = new St.BoxLayout({ name: 'panelRight' }); + this.add_child(this._rightBox); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + this.connect('touch-event', this._onTouchEvent.bind(this)); + + Main.overview.connect('showing', () => { + this.add_style_pseudo_class('overview'); + }); + Main.overview.connect('hiding', () => { + this.remove_style_pseudo_class('overview'); + }); + + Main.layoutManager.panelBox.add(this); + Main.ctrlAltTabManager.addGroup(this, _("Top Bar"), 'focus-top-bar-symbolic', + { sortGroup: CtrlAltTab.SortGroup.TOP }); + + Main.sessionMode.connect('updated', this._updatePanel.bind(this)); + + global.display.connect('workareas-changed', () => this.queue_relayout()); + this._updatePanel(); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. + + // Annotation: Gets called by `this._init()` to populate the top (menu)bar / + // panel initially. + // + // It does the following relevant stuff: + // - Calls `this._hideIndicators()` + // - Calls `this._updateBox()` for `this._leftBox`, `this._centerBox` and + // `this._rightBox` with `panel.left`, `panel.center` and `panel.right` to + // populate the boxes with items defined in `panel.left`, `panel.center` + // and `panel.right`. + // + // `panel.left`, `panel.center` and `panel.right` get set via the line + // `let panel = Main.sessionMode.panel`, which uses the pnale of Mains + // (`js/ui/main.js`) instance of SessionMode (`js/ui/sessionMode.js`). + // + // And in `js/ui/sessionMode.js` (42.5, 2022-10-22) you have different + // modes with different panel configuration. For example the "user" mode + // with: + // ``` + // panel: { + // left: ['activities', 'appMenu'], + // center: ['dateMenu'], + // right: ['screenRecording', 'dwellClick', 'a11y', 'keyboard', 'aggregateMenu'], + // }, + // ``` + // + // This way this function populates the top (menu)bar / panel with the + // default stuff you see on a fresh Gnome. + // + // Compared to panel_master_2021-04-21.js: + // Corner-related code is gone and some function name casing got changed. + _updatePanel() { + let panel = Main.sessionMode.panel; + this._hideIndicators(); + this._updateBox(panel.left, this._leftBox); + this._updateBox(panel.center, this._centerBox); + this._updateBox(panel.right, this._rightBox); + + if (panel.left.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; + else if (panel.right.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; + // Default to center if there is no dateMenu + else + Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; + + if (this._sessionStyle) + this.remove_style_class_name(this._sessionStyle); + + this._sessionStyle = Main.sessionMode.panelStyle; + if (this._sessionStyle) + this.add_style_class_name(this._sessionStyle); + } + + // Annotation: This function hides all items, which are in the top (menu)bar + // panel and in PANEL_ITEM_IMPLEMENTATIONS. + // + // Compared to panel_master_2021-04-21.js: Nothing changed. + _hideIndicators() { + for (let role in PANEL_ITEM_IMPLEMENTATIONS) { + let indicator = this.statusArea[role]; + if (!indicator) + continue; + indicator.container.hide(); + } + } + + // Annotation: This function takes a role (of an item) and returns a + // corresponding indicator, if either of two things are true: + // - The indicator is already in `this.statusArea`. + // Then it just returns the indicator by using `this.statusArea`. + // - The role is in PANEL_ITEM_IMPLEMENTATIONS. + // Then it creates a new indicator, adds it to `this.statusArea` and + // returns it. + // + // Compared to panel_master_2021-04-21.js: Nothing changed. + _ensureIndicator(role) { + let indicator = this.statusArea[role]; + if (!indicator) { + let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; + if (!constructor) { + // This icon is not implemented (this is a bug) + return null; + } + indicator = new constructor(this); + this.statusArea[role] = indicator; + } + return indicator; + } + + // Annotation: This function takes a list of items (or rather their roles) + // and adds the indicators of those items to a box (like `this._leftBox`) + // using `this._ensureIndicator()` to get the indicator corresponding to the + // given role. + // So only items with roles `this._ensureIndicator()` knows, get added. + // + // Compared to panel_master_2021-04-21.js: Nothing changed. + _updateBox(elements, box) { + let nChildren = box.get_n_children(); + + for (let i = 0; i < elements.length; i++) { + let role = elements[i]; + let indicator = this._ensureIndicator(role); + if (indicator == null) + continue; + + this._addToPanelBox(role, indicator, i + nChildren, box); + } + } + + // Annotation: This function adds the given item to the specified top + // (menu)bar / panel box and connects to "destroy" and "menu-set" events. + // + // It takes the following arguments: + // - role: The name of the item to add. + // - indicator: The indicator of the item to add. + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - `this._leftBox` + // - `this._centerBox` + // - `this._rightBox` + // + // Compared to panel_master_2021-04-21.js: Nothing changed. + _addToPanelBox(role, indicator, position, box) { + let container = indicator.container; + container.show(); + + let parent = container.get_parent(); + if (parent) + parent.remove_actor(container); + + + box.insert_child_at_index(container, position); + if (indicator.menu) + this.menuManager.addMenu(indicator.menu); + this.statusArea[role] = indicator; + let destroyId = indicator.connect('destroy', emitter => { + delete this.statusArea[role]; + emitter.disconnect(destroyId); + }); + indicator.connect('menu-set', this._onMenuSet.bind(this)); + this._onMenuSet(indicator); + } + + // Annotation: This function allows you to add an item to the top (menu)bar + // / panel. + // While per default it adds the item to the status area (the right box of + // the top bar), you can specify the box and add the item to any of the + // three boxes of the top bar. + // To add an item to the top bar, you need to give its role and indicator. + // + // This function takes the following arguments: + // - role: A name for the item to add. + // - indicator: The indicator for the item to add (must be an instance of + // PanelMenu.Button). + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - "left": referring to `this._leftBox` + // - "center": referring to `this._centerBox` + // - "right": referring to `this._rightBox` + // These boxes are what you see in the top bar as the left, right and + // center sections. + // + // Finally this function just calls `this._addToPanelBox()` for the actual + // work, so it basically just makes sure the input to + // `this._addToPanelBox()` is correct. + // + // Compared to panel_master_2021-04-21.js: + // Some syntax changes (usage of syntactic sugar). + addToStatusArea(role, indicator, position, box) { + if (this.statusArea[role]) + throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`); + + if (!(indicator instanceof PanelMenu.Button)) + throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); + + position ??= 0; + let boxes = { + left: this._leftBox, + center: this._centerBox, + right: this._rightBox, + }; + let boxContainer = boxes[box] || this._rightBox; + this.statusArea[role] = indicator; + this._addToPanelBox(role, indicator, position, boxContainer); + return indicator; + } + + // Annotation: + // Compared to panel_master_2021-04-21.js: + // `_addStyleClassName(className)` and `_removeStyleClassName(className)` + // are gone, since they apparently only existed to handle stuff related to + // the corners, which are gone. + + _onMenuSet(indicator) { + if (!indicator.menu || indicator.menu._openChangedId) + return; + + indicator.menu._openChangedId = indicator.menu.connect('open-state-changed', + (menu, isOpen) => { + let boxAlignment; + if (this._leftBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.START; + else if (this._centerBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.CENTER; + else if (this._rightBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.END; + + if (boxAlignment == Main.messageTray.bannerAlignment) + Main.messageTray.bannerBlocked = isOpen; + }); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. +}); diff --git a/docs/panel_43.2_2023-01-24.js b/docs/panel_43.2_2023-01-24.js new file mode 100644 index 0000000..85f0a1c --- /dev/null +++ b/docs/panel_43.2_2023-01-24.js @@ -0,0 +1,317 @@ +// My annotated and cut down `js/ui/panel.js` from gnome-shell/43.2. +// All annotations are what I guessed, interpreted and copied while reading the +// code and comparing to other `panel.js` versions and might be wrong. They are +// prefixed with "Annotation:" to indicate that they're my comments, not +// comments that orginally existed. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/43.2/js/ui/panel.js +// On: 2023-01-24 +// License: This code is licensed under GPLv2. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/43.2/js/ui/sessionMode.js +// On: 2023-01-24 +// License: This code is licensed under GPLv2. + +// I'm using the word "item" to refer to the thing, which gets added to the top +// (menu)bar / panel, where an item has a role/name and an indicator. + + +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* exported Panel */ + +const { Atk, Clutter, GLib, GObject, Meta, Shell, St } = imports.gi; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. +const CtrlAltTab = imports.ui.ctrlAltTab; +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. +const PopupMenu = imports.ui.popupMenu; +const PanelMenu = imports.ui.panelMenu; +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. +const Main = imports.ui.main; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +// Compared to panel_42.5_2022-10-22.js: +// "aggregateMenu" is gone with "quickSettings" replacing it. +// "screenSharing" got added. +const PANEL_ITEM_IMPLEMENTATIONS = { + 'activities': ActivitiesButton, + 'appMenu': AppMenuButton, + 'quickSettings': QuickSettings, + 'dateMenu': imports.ui.dateMenu.DateMenuButton, + 'a11y': imports.ui.status.accessibility.ATIndicator, + 'keyboard': imports.ui.status.keyboard.InputSourceIndicator, + 'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator, + 'screenRecording': imports.ui.status.remoteAccess.ScreenRecordingIndicator, + 'screenSharing': imports.ui.status.remoteAccess.ScreenSharingIndicator, +}; + +var Panel = GObject.registerClass( +class Panel extends St.Widget { + // Annotation: Initializes the top (menu)bar / panel. + // Does relevant stuff like: + // - Defining `this._leftBox`, `this._centerBox` and `this._rightBox`. + // - Finally calling `this._updatePanel()`. + // Compared to panel_42.5_2022-10-22.js: Nothing changed. + _init() { + super._init({ + name: 'panel', + reactive: true, + }); + + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._sessionStyle = null; + + this.statusArea = {}; + + this.menuManager = new PopupMenu.PopupMenuManager(this); + + this._leftBox = new St.BoxLayout({ name: 'panelLeft' }); + this.add_child(this._leftBox); + this._centerBox = new St.BoxLayout({ name: 'panelCenter' }); + this.add_child(this._centerBox); + this._rightBox = new St.BoxLayout({ name: 'panelRight' }); + this.add_child(this._rightBox); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + this.connect('touch-event', this._onTouchEvent.bind(this)); + + Main.overview.connect('showing', () => { + this.add_style_pseudo_class('overview'); + }); + Main.overview.connect('hiding', () => { + this.remove_style_pseudo_class('overview'); + }); + + Main.layoutManager.panelBox.add(this); + Main.ctrlAltTabManager.addGroup(this, _("Top Bar"), 'focus-top-bar-symbolic', + { sortGroup: CtrlAltTab.SortGroup.TOP }); + + Main.sessionMode.connect('updated', this._updatePanel.bind(this)); + + global.display.connect('workareas-changed', () => this.queue_relayout()); + this._updatePanel(); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. + + // Annotation: Gets called by `this._init()` to populate the top (menu)bar / + // panel initially. + // + // It does the following relevant stuff: + // - Calls `this._hideIndicators()` + // - Calls `this._updateBox()` for `this._leftBox`, `this._centerBox` and + // `this._rightBox` with `panel.left`, `panel.center` and `panel.right` to + // populate the boxes with items defined in `panel.left`, `panel.center` + // and `panel.right`. + // + // `panel.left`, `panel.center` and `panel.right` get set via the line + // `let panel = Main.sessionMode.panel`, which uses the panel of Mains + // (`js/ui/main.js`) instance of SessionMode (`js/ui/sessionMode.js`). + // + // And in `js/ui/sessionMode.js` (43.2, 2023-01-24) you have different + // modes with different panel configuration. For example the "user" mode + // with: + // ``` + // panel: { + // left: ['activities', 'appMenu'], + // center: ['dateMenu'], + // right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'], + // }, + // ``` + // + // This way this function populates the top (menu)bar / panel with the + // default stuff you see on a fresh Gnome. + // + // Compared to panel_42.5_2022-10-22.js: Nothing changed. + _updatePanel() { + let panel = Main.sessionMode.panel; + this._hideIndicators(); + this._updateBox(panel.left, this._leftBox); + this._updateBox(panel.center, this._centerBox); + this._updateBox(panel.right, this._rightBox); + + if (panel.left.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; + else if (panel.right.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; + // Default to center if there is no dateMenu + else + Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; + + if (this._sessionStyle) + this.remove_style_class_name(this._sessionStyle); + + this._sessionStyle = Main.sessionMode.panelStyle; + if (this._sessionStyle) + this.add_style_class_name(this._sessionStyle); + } + + // Annotation: This function hides all items, which are in the top (menu)bar + // panel and in PANEL_ITEM_IMPLEMENTATIONS. + // + // Compared to panel_42.5_2022-10-22.js: Nothing changed. + _hideIndicators() { + for (let role in PANEL_ITEM_IMPLEMENTATIONS) { + let indicator = this.statusArea[role]; + if (!indicator) + continue; + indicator.container.hide(); + } + } + + // Annotation: This function takes a role (of an item) and returns a + // corresponding indicator, if either of two things are true: + // - The indicator is already in `this.statusArea`. + // Then it just returns the indicator by using `this.statusArea`. + // - The role is in PANEL_ITEM_IMPLEMENTATIONS. + // Then it creates a new indicator, adds it to `this.statusArea` and + // returns it. + // + // Compared to panel_42.5_2022-10-22.js: Nothing changed. + _ensureIndicator(role) { + let indicator = this.statusArea[role]; + if (!indicator) { + let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; + if (!constructor) { + // This icon is not implemented (this is a bug) + return null; + } + indicator = new constructor(this); + this.statusArea[role] = indicator; + } + return indicator; + } + + // Annotation: This function takes a list of items (or rather their roles) + // and adds the indicators of those items to a box (like `this._leftBox`) + // using `this._ensureIndicator()` to get the indicator corresponding to the + // given role. + // So only items with roles `this._ensureIndicator()` knows, get added. + // + // Compared to panel_42.5_2022-10-22.js: Nothing changed. + _updateBox(elements, box) { + let nChildren = box.get_n_children(); + + for (let i = 0; i < elements.length; i++) { + let role = elements[i]; + let indicator = this._ensureIndicator(role); + if (indicator == null) + continue; + + this._addToPanelBox(role, indicator, i + nChildren, box); + } + } + + // Annotation: This function adds the given item to the specified top + // (menu)bar / panel box and connects to "destroy" and "menu-set" events. + // + // It takes the following arguments: + // - role: The name of the item to add. + // - indicator: The indicator of the item to add. + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - `this._leftBox` + // - `this._centerBox` + // - `this._rightBox` + // + // Compared to panel_42.5_2022-10-22.js: + // Code for adding indicator menu to a menu manager got moved to + // `_onMenuSet` method without the if clause. + _addToPanelBox(role, indicator, position, box) { + let container = indicator.container; + container.show(); + + let parent = container.get_parent(); + if (parent) + parent.remove_actor(container); + + + box.insert_child_at_index(container, position); + this.statusArea[role] = indicator; + let destroyId = indicator.connect('destroy', emitter => { + delete this.statusArea[role]; + emitter.disconnect(destroyId); + }); + indicator.connect('menu-set', this._onMenuSet.bind(this)); + this._onMenuSet(indicator); + } + + // Annotation: This function allows you to add an item to the top (menu)bar + // / panel. + // While per default it adds the item to the status area (the right box of + // the top bar), you can specify the box and add the item to any of the + // three boxes of the top bar. + // To add an item to the top bar, you need to give its role and indicator. + // + // This function takes the following arguments: + // - role: A name for the item to add. + // - indicator: The indicator for the item to add (must be an instance of + // PanelMenu.Button). + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - "left": referring to `this._leftBox` + // - "center": referring to `this._centerBox` + // - "right": referring to `this._rightBox` + // These boxes are what you see in the top bar as the left, right and + // center sections. + // + // Finally this function just calls `this._addToPanelBox()` for the actual + // work, so it basically just makes sure the input to + // `this._addToPanelBox()` is correct. + // + // Compared to panel_42.5_2022-10-22.js: Nothing changed. + addToStatusArea(role, indicator, position, box) { + if (this.statusArea[role]) + throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`); + + if (!(indicator instanceof PanelMenu.Button)) + throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); + + position ??= 0; + let boxes = { + left: this._leftBox, + center: this._centerBox, + right: this._rightBox, + }; + let boxContainer = boxes[box] || this._rightBox; + this.statusArea[role] = indicator; + this._addToPanelBox(role, indicator, position, boxContainer); + return indicator; + } + + // Compared to panel_42.5_2022-10-22.js: + // Code for adding indicator menu to a menu manager got moved to here from + // `_addToPanelBox` method without the if clause. + _onMenuSet(indicator) { + if (!indicator.menu || indicator.menu._openChangedId) + return; + + this.menuManager.addMenu(indicator.menu); + + indicator.menu._openChangedId = indicator.menu.connect('open-state-changed', + (menu, isOpen) => { + let boxAlignment; + if (this._leftBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.START; + else if (this._centerBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.CENTER; + else if (this._rightBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.END; + + if (boxAlignment == Main.messageTray.bannerAlignment) + Main.messageTray.bannerBlocked = isOpen; + }); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. +}); diff --git a/docs/panel_45.0_2023-09-26.js b/docs/panel_45.0_2023-09-26.js new file mode 100644 index 0000000..bb44a90 --- /dev/null +++ b/docs/panel_45.0_2023-09-26.js @@ -0,0 +1,344 @@ +// My annotated and cut down `js/ui/panel.js` from gnome-shell/45.0. +// All annotations are what I guessed, interpreted and copied while reading the +// code and comparing to other `panel.js` versions and might be wrong. They are +// prefixed with "Annotation:" to indicate that they're my comments, not +// comments that orginally existed. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/45.0/js/ui/panel.js +// On: 2023-09-26 +// License: This code is licensed under GPLv2. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/45.0/js/ui/sessionMode.js +// On: 2023-09-26 +// License: This code is licensed under GPLv2. + +// I'm using the word "item" to refer to the thing, which gets added to the top +// (menu)bar / panel, where an item has a role/name and an indicator. + + +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import Clutter from 'gi://Clutter'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import GObject from 'gi://GObject'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import St from 'gi://St'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as CtrlAltTab from './ctrlAltTab.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as PopupMenu from './popupMenu.js'; +import * as PanelMenu from './panelMenu.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as Main from './main.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import {DateMenuButton} from './dateMenu.js'; +import {ATIndicator} from './status/accessibility.js'; +import {InputSourceIndicator} from './status/keyboard.js'; +import {DwellClickIndicator} from './status/dwellClick.js'; +import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. +// Of note (for PANEL_ITEM_IMPLEMENTATIONS): +// const AppMenuButton = [...] +// const ActivitiesButton = [...] +// const QuickSettings = [...] + +const PANEL_ITEM_IMPLEMENTATIONS = { + 'activities': ActivitiesButton, + 'appMenu': AppMenuButton, + 'quickSettings': QuickSettings, + 'dateMenu': DateMenuButton, + 'a11y': ATIndicator, + 'keyboard': InputSourceIndicator, + 'dwellClick': DwellClickIndicator, + 'screenRecording': ScreenRecordingIndicator, + 'screenSharing': ScreenSharingIndicator, +}; + +// Annotation: Uses ESM export now. +export const Panel = GObject.registerClass( +class Panel extends St.Widget { + // Annotation: Initializes the top (menu)bar / panel. + // Does relevant stuff like: + // - Defining `this._leftBox`, `this._centerBox` and `this._rightBox`. + // - Finally calling `this._updatePanel()`. + // Compared to panel_43.2_2023-01-24.js: Nothing changed (except for + // formatting). + _init() { + super._init({ + name: 'panel', + reactive: true, + }); + + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._sessionStyle = null; + + this.statusArea = {}; + + this.menuManager = new PopupMenu.PopupMenuManager(this); + + this._leftBox = new St.BoxLayout({name: 'panelLeft'}); + this.add_child(this._leftBox); + this._centerBox = new St.BoxLayout({name: 'panelCenter'}); + this.add_child(this._centerBox); + this._rightBox = new St.BoxLayout({name: 'panelRight'}); + this.add_child(this._rightBox); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + this.connect('touch-event', this._onTouchEvent.bind(this)); + + Main.overview.connect('showing', () => { + this.add_style_pseudo_class('overview'); + }); + Main.overview.connect('hiding', () => { + this.remove_style_pseudo_class('overview'); + }); + + Main.layoutManager.panelBox.add(this); + Main.ctrlAltTabManager.addGroup(this, + _('Top Bar'), 'focus-top-bar-symbolic', + {sortGroup: CtrlAltTab.SortGroup.TOP}); + + Main.sessionMode.connect('updated', this._updatePanel.bind(this)); + + global.display.connect('workareas-changed', () => this.queue_relayout()); + this._updatePanel(); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. + + // Annotation: Gets called by `this._init()` to populate the top (menu)bar / + // panel initially. + // + // It does the following relevant stuff: + // - Calls `this._hideIndicators()` + // - Calls `this._updateBox()` for `this._leftBox`, `this._centerBox` and + // `this._rightBox` with `panel.left`, `panel.center` and `panel.right` to + // populate the boxes with items defined in `panel.left`, `panel.center` + // and `panel.right`. + // + // `panel.left`, `panel.center` and `panel.right` get set via the line + // `let panel = Main.sessionMode.panel`, which uses the panel of Mains + // (`js/ui/main.js`) instance of SessionMode (`js/ui/sessionMode.js`). + // + // And in `js/ui/sessionMode.js` (45.0, 2023-09-26) you have different + // modes with different panel configuration. For example the "user" mode + // with: + // ``` + // panel: { + // left: ['activities'], + // center: ['dateMenu'], + // right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'], + // }, + // ``` + // + // This way this function populates the top (menu)bar / panel with the + // default stuff you see on a fresh Gnome. + // + // Compared to panel_43.2_2023-01-24.js: Nothing changed. + _updatePanel() { + let panel = Main.sessionMode.panel; + this._hideIndicators(); + this._updateBox(panel.left, this._leftBox); + this._updateBox(panel.center, this._centerBox); + this._updateBox(panel.right, this._rightBox); + + if (panel.left.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; + else if (panel.right.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; + // Default to center if there is no dateMenu + else + Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; + + if (this._sessionStyle) + this.remove_style_class_name(this._sessionStyle); + + this._sessionStyle = Main.sessionMode.panelStyle; + if (this._sessionStyle) + this.add_style_class_name(this._sessionStyle); + } + + // Annotation: This function hides all items, which are in the top (menu)bar + // panel and in PANEL_ITEM_IMPLEMENTATIONS. + // + // Compared to panel_43.2_2023-01-24.js: Nothing changed. + _hideIndicators() { + for (let role in PANEL_ITEM_IMPLEMENTATIONS) { + let indicator = this.statusArea[role]; + if (!indicator) + continue; + indicator.container.hide(); + } + } + + // Annotation: This function takes a role (of an item) and returns a + // corresponding indicator, if either of two things are true: + // - The indicator is already in `this.statusArea`. + // Then it just returns the indicator by using `this.statusArea`. + // - The role is in PANEL_ITEM_IMPLEMENTATIONS. + // Then it creates a new indicator, adds it to `this.statusArea` and + // returns it. + // + // Compared to panel_43.2_2023-01-24.js: Nothing changed. + _ensureIndicator(role) { + let indicator = this.statusArea[role]; + if (!indicator) { + let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; + if (!constructor) { + // This icon is not implemented (this is a bug) + return null; + } + indicator = new constructor(this); + this.statusArea[role] = indicator; + } + return indicator; + } + + // Annotation: This function takes a list of items (or rather their roles) + // and adds the indicators of those items to a box (like `this._leftBox`) + // using `this._ensureIndicator()` to get the indicator corresponding to the + // given role. + // So only items with roles `this._ensureIndicator()` knows, get added. + // + // Compared to panel_43.2_2023-01-24.js: Nothing changed. + _updateBox(elements, box) { + let nChildren = box.get_n_children(); + + for (let i = 0; i < elements.length; i++) { + let role = elements[i]; + let indicator = this._ensureIndicator(role); + if (indicator == null) + continue; + + this._addToPanelBox(role, indicator, i + nChildren, box); + } + } + + // Annotation: This function adds the given item to the specified top + // (menu)bar / panel box and connects to "destroy" and "menu-set" events. + // + // It takes the following arguments: + // - role: The name of the item to add. + // - indicator: The indicator of the item to add. + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - `this._leftBox` + // - `this._centerBox` + // - `this._rightBox` + // + // Compared to panel_43.2_2023-01-24.js: Nothing changed. + _addToPanelBox(role, indicator, position, box) { + let container = indicator.container; + container.show(); + + let parent = container.get_parent(); + if (parent) + parent.remove_actor(container); + + + box.insert_child_at_index(container, position); + this.statusArea[role] = indicator; + let destroyId = indicator.connect('destroy', emitter => { + delete this.statusArea[role]; + emitter.disconnect(destroyId); + }); + indicator.connect('menu-set', this._onMenuSet.bind(this)); + this._onMenuSet(indicator); + } + + // Annotation: This function allows you to add an item to the top (menu)bar + // / panel. + // While per default it adds the item to the status area (the right box of + // the top bar), you can specify the box and add the item to any of the + // three boxes of the top bar. + // To add an item to the top bar, you need to give its role and indicator. + // + // This function takes the following arguments: + // - role: A name for the item to add. + // - indicator: The indicator for the item to add (must be an instance of + // PanelMenu.Button). + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - "left": referring to `this._leftBox` + // - "center": referring to `this._centerBox` + // - "right": referring to `this._rightBox` + // These boxes are what you see in the top bar as the left, right and + // center sections. + // + // Finally this function just calls `this._addToPanelBox()` for the actual + // work, so it basically just makes sure the input to + // `this._addToPanelBox()` is correct. + // + // Compared to panel_43.2_2023-01-24.js: Nothing changed. + addToStatusArea(role, indicator, position, box) { + if (this.statusArea[role]) + throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`); + + if (!(indicator instanceof PanelMenu.Button)) + throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); + + position ??= 0; + let boxes = { + left: this._leftBox, + center: this._centerBox, + right: this._rightBox, + }; + let boxContainer = boxes[box] || this._rightBox; + this.statusArea[role] = indicator; + this._addToPanelBox(role, indicator, position, boxContainer); + return indicator; + } + + // Compared to panel_43.2_2023-01-24.js: Nothing changed, except for the + // usage of === instead of ==. + _onMenuSet(indicator) { + if (!indicator.menu || indicator.menu._openChangedId) + return; + + this.menuManager.addMenu(indicator.menu); + + indicator.menu._openChangedId = indicator.menu.connect('open-state-changed', + (menu, isOpen) => { + let boxAlignment; + if (this._leftBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.START; + else if (this._centerBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.CENTER; + else if (this._rightBox.contains(indicator.container)) + boxAlignment = Clutter.ActorAlign.END; + + if (boxAlignment === Main.messageTray.bannerAlignment) + Main.messageTray.bannerBlocked = isOpen; + }); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. +}); diff --git a/docs/panel_46.4_2024-09-11.js b/docs/panel_46.4_2024-09-11.js new file mode 100644 index 0000000..76c2640 --- /dev/null +++ b/docs/panel_46.4_2024-09-11.js @@ -0,0 +1,322 @@ +// My annotated and cut down js/ui/panel.js from gnome-shell/46.4. +// All annotations are what I guessed, interpreted and copied while reading the +// code and comparing to other panel.js versions and might be wrong. They are +// prefixed with "Annotation:" to indicate that they're my comments, not +// comments that orginally existed. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/46.4/js/ui/panel.js +// On: 2024-09-11 +// License: This code is licensed under GPLv2. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/46.4/js/ui/sessionMode.js +// On: 2023-09-11 +// License: This code is licensed under GPLv2. + +// I'm using the word "item" to refer to the thing, which gets added to the top +// (menu)bar / panel, where an item has a role/name and an indicator. + + +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import Clutter from 'gi://Clutter'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import GObject from 'gi://GObject'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import St from 'gi://St'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as CtrlAltTab from './ctrlAltTab.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as PopupMenu from './popupMenu.js'; +import * as PanelMenu from './panelMenu.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as Main from './main.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import {DateMenuButton} from './dateMenu.js'; +import {ATIndicator} from './status/accessibility.js'; +import {InputSourceIndicator} from './status/keyboard.js'; +import {DwellClickIndicator} from './status/dwellClick.js'; +import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +// Of note (for PANEL_ITEM_IMPLEMENTATIONS): +// const AppMenuButton = [...] +// const ActivitiesButton = [...] +// const QuickSettings = [...] + +const PANEL_ITEM_IMPLEMENTATIONS = { + 'activities': ActivitiesButton, + 'appMenu': AppMenuButton, + 'quickSettings': QuickSettings, + 'dateMenu': DateMenuButton, + 'a11y': ATIndicator, + 'keyboard': InputSourceIndicator, + 'dwellClick': DwellClickIndicator, + 'screenRecording': ScreenRecordingIndicator, + 'screenSharing': ScreenSharingIndicator, +}; + +export const Panel = GObject.registerClass( +class Panel extends St.Widget { + // Annotation: Initializes the top (menu)bar / panel. + // Does relevant stuff like: + // - Defining this._leftBox, this._centerBox and this._rightBox. + // - Finally calling this._updatePanel(). + // Compared to panel_45.0_2023-09-26.js: Not much changed except from some + // slightly different function names/calls. + _init() { + super._init({ + name: 'panel', + reactive: true, + }); + + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._sessionStyle = null; + + this.statusArea = {}; + + this.menuManager = new PopupMenu.PopupMenuManager(this); + + this._leftBox = new St.BoxLayout({name: 'panelLeft'}); + this.add_child(this._leftBox); + this._centerBox = new St.BoxLayout({name: 'panelCenter'}); + this.add_child(this._centerBox); + this._rightBox = new St.BoxLayout({name: 'panelRight'}); + this.add_child(this._rightBox); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + this.connect('touch-event', this._onTouchEvent.bind(this)); + + Main.overview.connectObject('showing', + () => this.add_style_pseudo_class('overview'), + this); + Main.overview.connectObject('hiding', + () => this.remove_style_pseudo_class('overview'), + this); + + Main.layoutManager.panelBox.add_child(this); + Main.ctrlAltTabManager.addGroup(this, + _('Top Bar'), 'shell-focus-top-bar-symbolic', + {sortGroup: CtrlAltTab.SortGroup.TOP}); + + Main.sessionMode.connect('updated', this._updatePanel.bind(this)); + + global.display.connect('workareas-changed', () => this.queue_relayout()); + this._updatePanel(); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. + + // Annotation: Gets called by this._init() to populate the top (menu)bar / + // panel initially. + // + // It does the following relevant stuff: + // - Calls this._hideIndicators() + // - Calls this._updateBox() for this._leftBox, this._centerBox and + // this._rightBox with panel.left, panel.center and panel.right to + // populate the boxes with items defined in panel.left, panel.center and + // panel.right. + // + // panel.left, panel.center and panel.right get set via the line let panel + // = Main.sessionMode.panel, which uses the panel of Mains (js/ui/main.js) + // instance of SessionMode (js/ui/sessionMode.js). + // + // And in js/ui/sessionMode.js (46.4, 2024-09-11) you have different modes + // with different panel configuration. For example the "user" mode with: + // panel: { + // left: ['activities'], + // center: ['dateMenu'], + // right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'], + // } + // + // This way this function populates the top (menu)bar / panel with the + // default stuff you see on a fresh Gnome. + // + // Compared to panel_45.0_2023-09-26.js: Nothing changed. + _updatePanel() { + let panel = Main.sessionMode.panel; + this._hideIndicators(); + this._updateBox(panel.left, this._leftBox); + this._updateBox(panel.center, this._centerBox); + this._updateBox(panel.right, this._rightBox); + + if (panel.left.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; + else if (panel.right.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; + // Default to center if there is no dateMenu + else + Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; + + if (this._sessionStyle) + this.remove_style_class_name(this._sessionStyle); + + this._sessionStyle = Main.sessionMode.panelStyle; + if (this._sessionStyle) + this.add_style_class_name(this._sessionStyle); + } + + // Annotation: This function hides all items, which are in the top (menu)bar + // panel and in PANEL_ITEM_IMPLEMENTATIONS. + // + // Compared to panel_45.0_2023-09-26.js: Nothing changed. + _hideIndicators() { + for (let role in PANEL_ITEM_IMPLEMENTATIONS) { + let indicator = this.statusArea[role]; + if (!indicator) + continue; + indicator.container.hide(); + } + } + + // Annotation: This function takes a role (of an item) and returns a + // corresponding indicator, if either of two things are true: + // - The indicator is already in this.statusArea. + // Then it just returns the indicator by using this.statusArea. + // - The role is in PANEL_ITEM_IMPLEMENTATIONS. + // Then it creates a new indicator, adds it to this.statusArea and returns + // it. + // + // Compared to panel_45.0_2023-09-26.js: Nothing changed. + _ensureIndicator(role) { + let indicator = this.statusArea[role]; + if (!indicator) { + let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; + if (!constructor) { + // This icon is not implemented (this is a bug) + return null; + } + indicator = new constructor(this); + this.statusArea[role] = indicator; + } + return indicator; + } + + // Annotation: This function takes a list of items (or rather their roles) + // and adds the indicators of those items to a box (like this._leftBox) + // using this._ensureIndicator() to get the indicator corresponding to the + // given role. + // So only items with roles this._ensureIndicator() knows, get added. + // + // Compared to panel_45.0_2023-09-26.js: Nothing changed. + _updateBox(elements, box) { + let nChildren = box.get_n_children(); + + for (let i = 0; i < elements.length; i++) { + let role = elements[i]; + let indicator = this._ensureIndicator(role); + if (indicator == null) + continue; + + this._addToPanelBox(role, indicator, i + nChildren, box); + } + } + + // Annotation: This function adds the given item to the specified top + // (menu)bar / panel box and connects to "destroy" and "menu-set" events. + // + // It takes the following arguments: + // - role: The name of the item to add. + // - indicator: The indicator of the item to add. + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - this._leftBox + // - this._centerBox + // - this._rightBox + // + // Compared to panel_45.0_2023-09-26.js: The call of + // parent.remove_actor(container) changed to parent.remove_child(container). + _addToPanelBox(role, indicator, position, box) { + let container = indicator.container; + container.show(); + + let parent = container.get_parent(); + if (parent) + parent.remove_child(container); + + + box.insert_child_at_index(container, position); + this.statusArea[role] = indicator; + let destroyId = indicator.connect('destroy', emitter => { + delete this.statusArea[role]; + emitter.disconnect(destroyId); + }); + indicator.connect('menu-set', this._onMenuSet.bind(this)); + this._onMenuSet(indicator); + } + + // Annotation: This function allows you to add an item to the top (menu)bar + // / panel. + // While per default it adds the item to the status area (the right box of + // the top bar), you can specify the box and add the item to any of the + // three boxes of the top bar. + // To add an item to the top bar, you need to give its role and indicator. + // + // This function takes the following arguments: + // - role: A name for the item to add. + // - indicator: The indicator for the item to add (must be an instance of + // PanelMenu.Button). + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - "left": referring to this._leftBox + // - "center": referring to this._centerBox + // - "right": referring to this._rightBox + // These boxes are what you see in the top bar as the left, right and + // center sections. + // + // Finally this function just calls this._addToPanelBox() for the actual + // work, so it basically just makes sure the input to this._addToPanelBox() + // is correct. + // + // Compared to panel_45.0_2023-09-26.js: Nothing changed. + addToStatusArea(role, indicator, position, box) { + if (this.statusArea[role]) + throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`); + + if (!(indicator instanceof PanelMenu.Button)) + throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); + + position ??= 0; + let boxes = { + left: this._leftBox, + center: this._centerBox, + right: this._rightBox, + }; + let boxContainer = boxes[box] || this._rightBox; + this.statusArea[role] = indicator; + this._addToPanelBox(role, indicator, position, boxContainer); + return indicator; + } + + // Of note: + // _onMenuSet(indicator) { [...] } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. +}); diff --git a/docs/panel_47.rc_2024-09-12.js b/docs/panel_47.rc_2024-09-12.js new file mode 100644 index 0000000..504cbb6 --- /dev/null +++ b/docs/panel_47.rc_2024-09-12.js @@ -0,0 +1,317 @@ +// My annotated and cut down js/ui/panel.js from gnome-shell/47.rc. +// All annotations are what I guessed, interpreted and copied while reading the +// code and comparing to other panel.js versions and might be wrong. They are +// prefixed with "Annotation:" to indicate that they're my comments, not +// comments that orginally existed. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/47.rc/js/ui/panel.js +// On: 2024-09-12 +// License: This code is licensed under GPLv2. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/47.rc/js/ui/sessionMode.js +// On: 2023-09-12 +// License: This code is licensed under GPLv2. + +// I'm using the word "item" to refer to the thing, which gets added to the top +// (menu)bar / panel, where an item has a role/name and an indicator. + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import Clutter from 'gi://Clutter'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import GObject from 'gi://GObject'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import St from 'gi://St'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as CtrlAltTab from './ctrlAltTab.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as PopupMenu from './popupMenu.js'; +import * as PanelMenu from './panelMenu.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as Main from './main.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import {DateMenuButton} from './dateMenu.js'; +import {ATIndicator} from './status/accessibility.js'; +import {InputSourceIndicator} from './status/keyboard.js'; +import {DwellClickIndicator} from './status/dwellClick.js'; +import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +// Of note (for PANEL_ITEM_IMPLEMENTATIONS): +// const AppMenuButton = [...] +// const ActivitiesButton = [...] +// const QuickSettings = [...] + +const PANEL_ITEM_IMPLEMENTATIONS = { + 'activities': ActivitiesButton, + 'appMenu': AppMenuButton, + 'quickSettings': QuickSettings, + 'dateMenu': DateMenuButton, + 'a11y': ATIndicator, + 'keyboard': InputSourceIndicator, + 'dwellClick': DwellClickIndicator, + 'screenRecording': ScreenRecordingIndicator, + 'screenSharing': ScreenSharingIndicator, +}; + +export const Panel = GObject.registerClass( +class Panel extends St.Widget { + // Annotation: Initializes the top (menu)bar / panel. + // Does relevant stuff like: + // - Defining this._leftBox, this._centerBox and this._rightBox. + // - Finally calling this._updatePanel(). + // Compared to panel_46.4_2024-09-11.js: Nothing changed. + _init() { + super._init({ + name: 'panel', + reactive: true, + }); + + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._sessionStyle = null; + + this.statusArea = {}; + + this.menuManager = new PopupMenu.PopupMenuManager(this); + + this._leftBox = new St.BoxLayout({name: 'panelLeft'}); + this.add_child(this._leftBox); + this._centerBox = new St.BoxLayout({name: 'panelCenter'}); + this.add_child(this._centerBox); + this._rightBox = new St.BoxLayout({name: 'panelRight'}); + this.add_child(this._rightBox); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + this.connect('touch-event', this._onTouchEvent.bind(this)); + + Main.overview.connectObject('showing', + () => this.add_style_pseudo_class('overview'), + this); + Main.overview.connectObject('hiding', + () => this.remove_style_pseudo_class('overview'), + this); + + Main.layoutManager.panelBox.add_child(this); + Main.ctrlAltTabManager.addGroup(this, + _('Top Bar'), 'shell-focus-top-bar-symbolic', + {sortGroup: CtrlAltTab.SortGroup.TOP}); + + Main.sessionMode.connect('updated', this._updatePanel.bind(this)); + + global.display.connect('workareas-changed', () => this.queue_relayout()); + this._updatePanel(); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. + + // Annotation: Gets called by this._init() to populate the top (menu)bar / + // panel initially. + // + // It does the following relevant stuff: + // - Calls this._hideIndicators() + // - Calls this._updateBox() for this._leftBox, this._centerBox and + // this._rightBox with panel.left, panel.center and panel.right to + // populate the boxes with items defined in panel.left, panel.center and + // panel.right. + // + // panel.left, panel.center and panel.right get set via the line let panel + // = Main.sessionMode.panel, which uses the panel of Mains (js/ui/main.js) + // instance of SessionMode (js/ui/sessionMode.js). + // + // And in js/ui/sessionMode.js (47.rc, 2024-09-12) you have different modes + // with different panel configuration. For example the "user" mode with: + // panel: { + // left: ['activities'], + // center: ['dateMenu'], + // right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'], + // } + // + // This way this function populates the top (menu)bar / panel with the + // default stuff you see on a fresh Gnome. + // + // Compared to panel_46.4_2024-09-11.js: Nothing changed. + _updatePanel() { + let panel = Main.sessionMode.panel; + this._hideIndicators(); + this._updateBox(panel.left, this._leftBox); + this._updateBox(panel.center, this._centerBox); + this._updateBox(panel.right, this._rightBox); + + if (panel.left.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; + else if (panel.right.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; + // Default to center if there is no dateMenu + else + Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; + + if (this._sessionStyle) + this.remove_style_class_name(this._sessionStyle); + + this._sessionStyle = Main.sessionMode.panelStyle; + if (this._sessionStyle) + this.add_style_class_name(this._sessionStyle); + } + + // Annotation: This function hides all items, which are in the top (menu)bar + // panel and in PANEL_ITEM_IMPLEMENTATIONS. + // + // Compared to panel_46.4_2024-09-11.js: Nothing changed. + _hideIndicators() { + for (let role in PANEL_ITEM_IMPLEMENTATIONS) { + let indicator = this.statusArea[role]; + if (!indicator) + continue; + indicator.container.hide(); + } + } + + // Annotation: This function takes a role (of an item) and returns a + // corresponding indicator, if either of two things are true: + // - The indicator is already in this.statusArea. + // Then it just returns the indicator by using this.statusArea. + // - The role is in PANEL_ITEM_IMPLEMENTATIONS. + // Then it creates a new indicator, adds it to this.statusArea and returns + // it. + // + // Compared to panel_46.4_2024-09-11.js: Nothing changed. + _ensureIndicator(role) { + let indicator = this.statusArea[role]; + if (!indicator) { + let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; + if (!constructor) { + // This icon is not implemented (this is a bug) + return null; + } + indicator = new constructor(this); + this.statusArea[role] = indicator; + } + return indicator; + } + + // Annotation: This function takes a list of items (or rather their roles) + // and adds the indicators of those items to a box (like this._leftBox) + // using this._ensureIndicator() to get the indicator corresponding to the + // given role. + // So only items with roles this._ensureIndicator() knows, get added. + // + // Compared to panel_46.4_2024-09-11.js: Nothing changed. + _updateBox(elements, box) { + let nChildren = box.get_n_children(); + + for (let i = 0; i < elements.length; i++) { + let role = elements[i]; + let indicator = this._ensureIndicator(role); + if (indicator == null) + continue; + + this._addToPanelBox(role, indicator, i + nChildren, box); + } + } + + // Annotation: This function adds the given item to the specified top + // (menu)bar / panel box and connects to "destroy" and "menu-set" events. + // + // It takes the following arguments: + // - role: The name of the item to add. + // - indicator: The indicator of the item to add. + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - this._leftBox + // - this._centerBox + // - this._rightBox + // + // Compared to panel_46.4_2024-09-11.js: Nothing changed. + _addToPanelBox(role, indicator, position, box) { + let container = indicator.container; + container.show(); + + let parent = container.get_parent(); + if (parent) + parent.remove_child(container); + + + box.insert_child_at_index(container, position); + this.statusArea[role] = indicator; + let destroyId = indicator.connect('destroy', emitter => { + delete this.statusArea[role]; + emitter.disconnect(destroyId); + }); + indicator.connect('menu-set', this._onMenuSet.bind(this)); + this._onMenuSet(indicator); + } + + // Annotation: This function allows you to add an item to the top (menu)bar + // / panel. + // While per default it adds the item to the status area (the right box of + // the top bar), you can specify the box and add the item to any of the + // three boxes of the top bar. + // To add an item to the top bar, you need to give its role and indicator. + // + // This function takes the following arguments: + // - role: A name for the item to add. + // - indicator: The indicator for the item to add (must be an instance of + // PanelMenu.Button). + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - "left": referring to this._leftBox + // - "center": referring to this._centerBox + // - "right": referring to this._rightBox + // These boxes are what you see in the top bar as the left, right and + // center sections. + // + // Finally this function just calls this._addToPanelBox() for the actual + // work, so it basically just makes sure the input to this._addToPanelBox() + // is correct. + // + // Compared to panel_46.4_2024-09-11.js: Nothing changed. + addToStatusArea(role, indicator, position, box) { + if (this.statusArea[role]) + throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`); + + if (!(indicator instanceof PanelMenu.Button)) + throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); + + position ??= 0; + let boxes = { + left: this._leftBox, + center: this._centerBox, + right: this._rightBox, + }; + let boxContainer = boxes[box] || this._rightBox; + this.statusArea[role] = indicator; + this._addToPanelBox(role, indicator, position, boxContainer); + return indicator; + } + + // Of note: + // _onMenuSet(indicator) { [...] } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. +}); diff --git a/docs/panel_48.2_2025-06-08.js b/docs/panel_48.2_2025-06-08.js new file mode 100644 index 0000000..7becfb8 --- /dev/null +++ b/docs/panel_48.2_2025-06-08.js @@ -0,0 +1,322 @@ +// My annotated and cut down js/ui/panel.js from gnome-shell/48.2. +// All annotations are what I guessed, interpreted and copied while reading the +// code and comparing to other panel.js versions and might be wrong. They are +// prefixed with "Annotation:" to indicate that they're my comments, not +// comments that orginally existed. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/48.2/js/ui/panel.js +// On: 2025-06-08 +// License: This code is licensed under GPLv2. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/48.2/js/ui/sessionMode.js +// On: 2025-06-08 +// License: This code is licensed under GPLv2. + +// I'm using the word "item" to refer to the thing, which gets added to the top +// (menu)bar / panel, where an item has a role/name and an indicator. + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import Clutter from 'gi://Clutter'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import GObject from 'gi://GObject'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import St from 'gi://St'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as CtrlAltTab from './ctrlAltTab.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as PopupMenu from './popupMenu.js'; +import * as PanelMenu from './panelMenu.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as Main from './main.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import {DateMenuButton} from './dateMenu.js'; +import {ATIndicator} from './status/accessibility.js'; +import {InputSourceIndicator} from './status/keyboard.js'; +import {DwellClickIndicator} from './status/dwellClick.js'; +import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +// Of note (for PANEL_ITEM_IMPLEMENTATIONS): +// const AppMenuButton = [...] +// const ActivitiesButton = [...] +// const QuickSettings = [...] + +const PANEL_ITEM_IMPLEMENTATIONS = { + 'activities': ActivitiesButton, + 'appMenu': AppMenuButton, + 'quickSettings': QuickSettings, + 'dateMenu': DateMenuButton, + 'a11y': ATIndicator, + 'keyboard': InputSourceIndicator, + 'dwellClick': DwellClickIndicator, + 'screenRecording': ScreenRecordingIndicator, + 'screenSharing': ScreenSharingIndicator, +}; + +export const Panel = GObject.registerClass( +class Panel extends St.Widget { + // Annotation: Initializes the top (menu)bar / panel. + // Does relevant stuff like: + // - Defining this._leftBox, this._centerBox and this._rightBox. + // - Finally calling this._updatePanel(). + // Compared to panel_47.rc_2024-09-12.js: connectObject instead of connect + // gets used, which shouldn't be relevant for this extension. + _init() { + super._init({ + name: 'panel', + reactive: true, + }); + + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._sessionStyle = null; + + this.statusArea = {}; + + this.menuManager = new PopupMenu.PopupMenuManager(this); + + this._leftBox = new St.BoxLayout({name: 'panelLeft'}); + this.add_child(this._leftBox); + this._centerBox = new St.BoxLayout({name: 'panelCenter'}); + this.add_child(this._centerBox); + this._rightBox = new St.BoxLayout({name: 'panelRight'}); + this.add_child(this._rightBox); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + this.connect('touch-event', this._onTouchEvent.bind(this)); + + Main.overview.connectObject('showing', + () => this.add_style_pseudo_class('overview'), + this); + Main.overview.connectObject('hiding', + () => this.remove_style_pseudo_class('overview'), + this); + + Main.layoutManager.panelBox.add_child(this); + Main.ctrlAltTabManager.addGroup(this, + _('Top Bar'), 'shell-focus-top-bar-symbolic', + {sortGroup: CtrlAltTab.SortGroup.TOP}); + + Main.sessionMode.connectObject('updated', + this._updatePanel.bind(this), + this); + + global.display.connectObject('workareas-changed', + () => this.queue_relayout(), + this); + this._updatePanel(); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. + + // Annotation: Gets called by this._init() to populate the top (menu)bar / + // panel initially. + // + // It does the following relevant stuff: + // - Calls this._hideIndicators() + // - Calls this._updateBox() for this._leftBox, this._centerBox and + // this._rightBox with panel.left, panel.center and panel.right to + // populate the boxes with items defined in panel.left, panel.center and + // panel.right. + // + // panel.left, panel.center and panel.right get set via the line let panel + // = Main.sessionMode.panel, which uses the panel of Mains (js/ui/main.js) + // instance of SessionMode (js/ui/sessionMode.js). + // + // And in js/ui/sessionMode.js (48.2, 2025-06-08) you have different modes + // with different panel configuration. For example the "user" mode with: + // panel: { + // left: ['activities'], + // center: ['dateMenu'], + // right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'], + // } + // + // This way this function populates the top (menu)bar / panel with the + // default stuff you see on a fresh Gnome. + // + // Compared to panel_47.rc_2024-09-12.js: Nothing changed. + _updatePanel() { + let panel = Main.sessionMode.panel; + this._hideIndicators(); + this._updateBox(panel.left, this._leftBox); + this._updateBox(panel.center, this._centerBox); + this._updateBox(panel.right, this._rightBox); + + if (panel.left.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; + else if (panel.right.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; + // Default to center if there is no dateMenu + else + Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; + + if (this._sessionStyle) + this.remove_style_class_name(this._sessionStyle); + + this._sessionStyle = Main.sessionMode.panelStyle; + if (this._sessionStyle) + this.add_style_class_name(this._sessionStyle); + } + + // Annotation: This function hides all items, which are in the top (menu)bar + // panel and in PANEL_ITEM_IMPLEMENTATIONS. + // + // Compared to panel_47.rc_2024-09-12.js: Nothing changed. + _hideIndicators() { + for (let role in PANEL_ITEM_IMPLEMENTATIONS) { + let indicator = this.statusArea[role]; + if (!indicator) + continue; + indicator.container.hide(); + } + } + + // Annotation: This function takes a role (of an item) and returns a + // corresponding indicator, if either of two things are true: + // - The indicator is already in this.statusArea. + // Then it just returns the indicator by using this.statusArea. + // - The role is in PANEL_ITEM_IMPLEMENTATIONS. + // Then it creates a new indicator, adds it to this.statusArea and returns + // it. + // + // Compared to panel_47.rc_2024-09-12.js: Nothing changed. + _ensureIndicator(role) { + let indicator = this.statusArea[role]; + if (!indicator) { + let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; + if (!constructor) { + // This icon is not implemented (this is a bug) + return null; + } + indicator = new constructor(this); + this.statusArea[role] = indicator; + } + return indicator; + } + + // Annotation: This function takes a list of items (or rather their roles) + // and adds the indicators of those items to a box (like this._leftBox) + // using this._ensureIndicator() to get the indicator corresponding to the + // given role. + // So only items with roles this._ensureIndicator() knows, get added. + // + // Compared to panel_47.rc_2024-09-12.js: Nothing changed. + _updateBox(elements, box) { + let nChildren = box.get_n_children(); + + for (let i = 0; i < elements.length; i++) { + let role = elements[i]; + let indicator = this._ensureIndicator(role); + if (indicator == null) + continue; + + this._addToPanelBox(role, indicator, i + nChildren, box); + } + } + + // Annotation: This function adds the given item to the specified top + // (menu)bar / panel box and connects to "destroy" and "menu-set" events. + // + // It takes the following arguments: + // - role: The name of the item to add. + // - indicator: The indicator of the item to add. + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - this._leftBox + // - this._centerBox + // - this._rightBox + // + // Compared to panel_47.rc_2024-09-12.js: Nothing changed. + _addToPanelBox(role, indicator, position, box) { + let container = indicator.container; + container.show(); + + let parent = container.get_parent(); + if (parent) + parent.remove_child(container); + + + box.insert_child_at_index(container, position); + this.statusArea[role] = indicator; + let destroyId = indicator.connect('destroy', emitter => { + delete this.statusArea[role]; + emitter.disconnect(destroyId); + }); + indicator.connect('menu-set', this._onMenuSet.bind(this)); + this._onMenuSet(indicator); + } + + // Annotation: This function allows you to add an item to the top (menu)bar + // / panel. + // While per default it adds the item to the status area (the right box of + // the top bar), you can specify the box and add the item to any of the + // three boxes of the top bar. + // To add an item to the top bar, you need to give its role and indicator. + // + // This function takes the following arguments: + // - role: A name for the item to add. + // - indicator: The indicator for the item to add (must be an instance of + // PanelMenu.Button). + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - "left": referring to this._leftBox + // - "center": referring to this._centerBox + // - "right": referring to this._rightBox + // These boxes are what you see in the top bar as the left, right and + // center sections. + // + // Finally this function just calls this._addToPanelBox() for the actual + // work, so it basically just makes sure the input to this._addToPanelBox() + // is correct. + // + // Compared to panel_47.rc_2024-09-12.js: Nothing changed. + addToStatusArea(role, indicator, position, box) { + if (this.statusArea[role]) + throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`); + + if (!(indicator instanceof PanelMenu.Button)) + throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); + + position ??= 0; + let boxes = { + left: this._leftBox, + center: this._centerBox, + right: this._rightBox, + }; + let boxContainer = boxes[box] || this._rightBox; + this.statusArea[role] = indicator; + this._addToPanelBox(role, indicator, position, boxContainer); + return indicator; + } + + // Of note: + // _onMenuSet(indicator) { [...] } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. +}); diff --git a/docs/panel_49.0_2025-10-03.js b/docs/panel_49.0_2025-10-03.js new file mode 100644 index 0000000..449c038 --- /dev/null +++ b/docs/panel_49.0_2025-10-03.js @@ -0,0 +1,321 @@ +// My annotated and cut down js/ui/panel.js from gnome-shell/49.0. +// All annotations are what I guessed, interpreted and copied while reading the +// code and comparing to other panel.js versions and might be wrong. They are +// prefixed with "Annotation:" to indicate that they're my comments, not +// comments that orginally existed. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/49.0/js/ui/panel.js +// On: 2025-10-03 +// License: This code is licensed under GPLv2. + +// Taken from: https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/49.0/js/ui/sessionMode.js +// On: 2025-10-03 +// License: This code is licensed under GPLv2. + +// I'm using the word "item" to refer to the thing, which gets added to the top +// (menu)bar / panel, where an item has a role/name and an indicator. + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import Clutter from 'gi://Clutter'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import GObject from 'gi://GObject'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import St from 'gi://St'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as CtrlAltTab from './ctrlAltTab.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as PopupMenu from './popupMenu.js'; +import * as PanelMenu from './panelMenu.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import * as Main from './main.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +import {DateMenuButton} from './dateMenu.js'; +import {ATIndicator} from './status/accessibility.js'; +import {InputSourceIndicator} from './status/keyboard.js'; +import {DwellClickIndicator} from './status/dwellClick.js'; +import {ScreenRecordingIndicator, ScreenSharingIndicator} from './status/remoteAccess.js'; + +// Annotation: [...] Cut out bunch of stuff here, which isn't relevant for this +// Extension. + +// Of note (for PANEL_ITEM_IMPLEMENTATIONS): +// const ActivitiesButton = [...] +// const QuickSettings = [...] +// Compared to panel_48.2_2025-06-08.js: AppMenuButton got removed. + +// Compared to panel_48.2_2025-06-08.js: AppMenuButton got removed. +const PANEL_ITEM_IMPLEMENTATIONS = { + 'activities': ActivitiesButton, + 'quickSettings': QuickSettings, + 'dateMenu': DateMenuButton, + 'a11y': ATIndicator, + 'keyboard': InputSourceIndicator, + 'dwellClick': DwellClickIndicator, + 'screenRecording': ScreenRecordingIndicator, + 'screenSharing': ScreenSharingIndicator, +}; + +export const Panel = GObject.registerClass( +class Panel extends St.Widget { + // Annotation: Initializes the top (menu)bar / panel. + // Does relevant stuff like: + // - Defining this._leftBox, this._centerBox and this._rightBox. + // - Finally calling this._updatePanel(). + // Compared to panel_48.2_2025-06-08.js: Nothing changed. + _init() { + super._init({ + name: 'panel', + reactive: true, + }); + + this.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); + + this._sessionStyle = null; + + this.statusArea = {}; + + this.menuManager = new PopupMenu.PopupMenuManager(this); + + this._leftBox = new St.BoxLayout({name: 'panelLeft'}); + this.add_child(this._leftBox); + this._centerBox = new St.BoxLayout({name: 'panelCenter'}); + this.add_child(this._centerBox); + this._rightBox = new St.BoxLayout({name: 'panelRight'}); + this.add_child(this._rightBox); + + this.connect('button-press-event', this._onButtonPress.bind(this)); + this.connect('touch-event', this._onTouchEvent.bind(this)); + + Main.overview.connectObject('showing', + () => this.add_style_pseudo_class('overview'), + this); + Main.overview.connectObject('hiding', + () => this.remove_style_pseudo_class('overview'), + this); + + Main.layoutManager.panelBox.add_child(this); + Main.ctrlAltTabManager.addGroup(this, + _('Top Bar'), 'shell-focus-top-bar-symbolic', + {sortGroup: CtrlAltTab.SortGroup.TOP}); + + Main.sessionMode.connectObject('updated', + this._updatePanel.bind(this), + this); + + global.display.connectObject('workareas-changed', + () => this.queue_relayout(), + this); + this._updatePanel(); + } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. + + // Annotation: Gets called by this._init() to populate the top (menu)bar / + // panel initially. + // + // It does the following relevant stuff: + // - Calls this._hideIndicators() + // - Calls this._updateBox() for this._leftBox, this._centerBox and + // this._rightBox with panel.left, panel.center and panel.right to + // populate the boxes with items defined in panel.left, panel.center and + // panel.right. + // + // panel.left, panel.center and panel.right get set via the line let panel + // = Main.sessionMode.panel, which uses the panel of Mains (js/ui/main.js) + // instance of SessionMode (js/ui/sessionMode.js). + // + // And in js/ui/sessionMode.js (49.0, 2025-10-03) you have different modes + // with different panel configuration. For example the "user" mode with: + // panel: { + // left: ['activities'], + // center: ['dateMenu'], + // right: ['screenRecording', 'screenSharing', 'dwellClick', 'a11y', 'keyboard', 'quickSettings'], + // } + // + // This way this function populates the top (menu)bar / panel with the + // default stuff you see on a fresh Gnome. + // + // Compared to panel_48.2_2025-06-08.js: Nothing changed. + _updatePanel() { + let panel = Main.sessionMode.panel; + this._hideIndicators(); + this._updateBox(panel.left, this._leftBox); + this._updateBox(panel.center, this._centerBox); + this._updateBox(panel.right, this._rightBox); + + if (panel.left.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; + else if (panel.right.includes('dateMenu')) + Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; + // Default to center if there is no dateMenu + else + Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; + + if (this._sessionStyle) + this.remove_style_class_name(this._sessionStyle); + + this._sessionStyle = Main.sessionMode.panelStyle; + if (this._sessionStyle) + this.add_style_class_name(this._sessionStyle); + } + + // Annotation: This function hides all items, which are in the top (menu)bar + // panel and in PANEL_ITEM_IMPLEMENTATIONS. + // + // Compared to panel_48.2_2025-06-08.js: Nothing changed. + _hideIndicators() { + for (let role in PANEL_ITEM_IMPLEMENTATIONS) { + let indicator = this.statusArea[role]; + if (!indicator) + continue; + indicator.container.hide(); + } + } + + // Annotation: This function takes a role (of an item) and returns a + // corresponding indicator, if either of two things are true: + // - The indicator is already in this.statusArea. + // Then it just returns the indicator by using this.statusArea. + // - The role is in PANEL_ITEM_IMPLEMENTATIONS. + // Then it creates a new indicator, adds it to this.statusArea and returns + // it. + // + // Compared to panel_48.2_2025-06-08.js: Nothing changed. + _ensureIndicator(role) { + let indicator = this.statusArea[role]; + if (!indicator) { + let constructor = PANEL_ITEM_IMPLEMENTATIONS[role]; + if (!constructor) { + // This icon is not implemented (this is a bug) + return null; + } + indicator = new constructor(this); + this.statusArea[role] = indicator; + } + return indicator; + } + + // Annotation: This function takes a list of items (or rather their roles) + // and adds the indicators of those items to a box (like this._leftBox) + // using this._ensureIndicator() to get the indicator corresponding to the + // given role. + // So only items with roles this._ensureIndicator() knows, get added. + // + // Compared to panel_48.2_2025-06-08.js: Nothing changed. + _updateBox(elements, box) { + let nChildren = box.get_n_children(); + + for (let i = 0; i < elements.length; i++) { + let role = elements[i]; + let indicator = this._ensureIndicator(role); + if (indicator == null) + continue; + + this._addToPanelBox(role, indicator, i + nChildren, box); + } + } + + // Annotation: This function adds the given item to the specified top + // (menu)bar / panel box and connects to "destroy" and "menu-set" events. + // + // It takes the following arguments: + // - role: The name of the item to add. + // - indicator: The indicator of the item to add. + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - this._leftBox + // - this._centerBox + // - this._rightBox + // + // Compared to panel_48.2_2025-06-08.js: Nothing changed. + _addToPanelBox(role, indicator, position, box) { + let container = indicator.container; + container.show(); + + let parent = container.get_parent(); + if (parent) + parent.remove_child(container); + + + box.insert_child_at_index(container, position); + this.statusArea[role] = indicator; + let destroyId = indicator.connect('destroy', emitter => { + delete this.statusArea[role]; + emitter.disconnect(destroyId); + }); + indicator.connect('menu-set', this._onMenuSet.bind(this)); + this._onMenuSet(indicator); + } + + // Annotation: This function allows you to add an item to the top (menu)bar + // / panel. + // While per default it adds the item to the status area (the right box of + // the top bar), you can specify the box and add the item to any of the + // three boxes of the top bar. + // To add an item to the top bar, you need to give its role and indicator. + // + // This function takes the following arguments: + // - role: A name for the item to add. + // - indicator: The indicator for the item to add (must be an instance of + // PanelMenu.Button). + // - position: Where in the box to add the item. + // - box: The box to add the item to. + // Can be one of the following: + // - "left": referring to this._leftBox + // - "center": referring to this._centerBox + // - "right": referring to this._rightBox + // These boxes are what you see in the top bar as the left, right and + // center sections. + // + // Finally this function just calls this._addToPanelBox() for the actual + // work, so it basically just makes sure the input to this._addToPanelBox() + // is correct. + // + // Compared to panel_48.2_2025-06-08.js: Nothing changed. + addToStatusArea(role, indicator, position, box) { + if (this.statusArea[role]) + throw new Error(`Extension point conflict: there is already a status indicator for role ${role}`); + + if (!(indicator instanceof PanelMenu.Button)) + throw new TypeError('Status indicator must be an instance of PanelMenu.Button'); + + position ??= 0; + let boxes = { + left: this._leftBox, + center: this._centerBox, + right: this._rightBox, + }; + let boxContainer = boxes[box] || this._rightBox; + this.statusArea[role] = indicator; + this._addToPanelBox(role, indicator, position, boxContainer); + return indicator; + } + + // Of note: + // _onMenuSet(indicator) { [...] } + + // Annotation: [...] Cut out bunch of stuff here, which isn't relevant for + // this Extension. +}); diff --git a/docs/panel.js b/docs/panel_master_2021-04-21.js similarity index 100% rename from docs/panel.js rename to docs/panel_master_2021-04-21.js diff --git a/docs/release_notes_template.md b/docs/release_notes_template.md new file mode 100644 index 0000000..66ac266 --- /dev/null +++ b/docs/release_notes_template.md @@ -0,0 +1,26 @@ +Top Bar Organizer vX includes the following changes: + +# Relevant and/or Breaking Changes + +The following relevant and/or breaking changes of this version: + +## Breaking Change + +Description + +## Relevant Change + +Description + +# Other Changes + +- a change +- another change + +# `git shortlog` + +The git shortlog for this version: + +``` +git shortlog vX-1..vX +``` diff --git a/git_annotated_tag_template b/git_annotated_tag_template index 7ad6f78..2c66d8d 100644 --- a/git_annotated_tag_template +++ b/git_annotated_tag_template @@ -1,13 +1,9 @@ -Top Bar Organizer v1 includes the following changes: +Top Bar Organizer vX -# Relevant and/or Breaking Changes +Relevant and/or Breaking Changes: +- breaking change: description +- relevant change: description -The following relevant and/or breaking changes of this version: - - - -# `git shortlog` - -The git shortlog for this version: - - +Other Changes: +- a change +- another change diff --git a/package-lock.json b/package-lock.json index b039aa8..2ca458a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,974 +1,2020 @@ { "name": "top-bar-organizer", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true - }, - "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, + "packages": { + "": { + "name": "top-bar-organizer", + "version": "1.0.0", + "license": "GPL-3.0-or-later", "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gnome-shell": "^48.0.2" + }, + "devDependencies": { + "eslint": "^8.57.1", + "eslint-plugin-jsdoc": "^50.7.1", + "typescript": "^5.8.3" } }, - "@eslint/eslintrc": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", - "integrity": "sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ==", + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "dev": true, - "requires": { + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@es-joy/jsdoccomment": { + "version": "0.50.2", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.50.2.tgz", + "integrity": "sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6", + "@typescript-eslint/types": "^8.11.0", + "comment-parser": "1.4.1", + "esquery": "^1.6.0", + "jsdoc-type-pratt-parser": "~4.1.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.9.1.tgz", + "integrity": "sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, - "dependencies": { - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - } + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true + "node_modules/@girs/accountsservice-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/accountsservice-1.0/-/accountsservice-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-QR7xfIvNbovmhNzzma+NxIMsQR8zgTaYk16Dqa/4YD8XBWrAgWuQhwEKKibG9KbQe5+YSUDUxJlbkSeOb3S5mg==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } }, - "ajv": { + "node_modules/@girs/adw-1": { + "version": "1.8.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/adw-1/-/adw-1-1.8.0-4.0.0-beta.23.tgz", + "integrity": "sha512-yDhOFc7qHTvIXo5naYuljtmpipP+z0JCyw6qw+mGEwyh0PXF9VA7MOUu5lUVQAsJgwUD5hLsWmqhd/JxnaXf7Q==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/gsk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gtk-4.0": "^4.18.3-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/pangocairo-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/atk-1.0": { + "version": "2.56.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/atk-1.0/-/atk-1.0-2.56.0-4.0.0-beta.23.tgz", + "integrity": "sha512-5kFs//8rkORYMJh0FQNeNciEQmbMNfA4Jo8AV9du8WiWGAdFzkPLH9o3eQrqRGIqZBsbqiEy3FS+R9P7+bn/UA==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/cairo-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/cairo-1.0/-/cairo-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-t5fADO+9TmZy8Xzk6Fqk23B3bmakzhNQxa3kFERzy2cL8p1q1pAWm1ARNcadIsl2IbflS4Hbn5sAwRwOm8W67A==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/clutter-16": { + "version": "16.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/clutter-16/-/clutter-16-16.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-dQ0cfxSa4E0AAfsFhs8Yd/eFHv7286c5oo7cku13nlEYBFQpyfUfa+CMSclAbUBUxM35PseXGXEy0jegkKpbXQ==", + "license": "MIT", + "dependencies": { + "@girs/atk-1.0": "^2.56.0-4.0.0-beta.23", + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/cogl-16": "^16.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gl-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/mtk-16": "^16.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/xlib-2.0": "^2.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/cogl-16": { + "version": "16.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/cogl-16/-/cogl-16-16.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-0UxbYg0gk0lcGnAwArY0Gq6ljfpzTUcnlyn1iCVI51nvFTyVCIRk4e89PM30aE/QN0daeFs/4+fjEsYUhHKNjA==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gl-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/mtk-16": "^16.0.0-4.0.0-beta.23", + "@girs/xlib-2.0": "^2.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/cogl-2.0": { + "version": "2.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/cogl-2.0/-/cogl-2.0-2.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-OSFZav9AiarIY5YuTEjOAG1IghHMuz/E+ZroPtuuI4Nhjp5AGVj7OevVw6QOVzhrGY95a3/ZhSP5sSpI8EdWyA==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gl-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/freetype2-2.0": { + "version": "2.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/freetype2-2.0/-/freetype2-2.0-2.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-DpMJET2s2ZXmGjScv+tu8tyEDIT6mIEWgY/Lp5HaXTUkKpw+LdtVV0bEwiXMYn3TSs6QYJZyBxNlwTSD3qb+Ww==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gck-2": { + "version": "4.4.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gck-2/-/gck-2-4.4.0-4.0.0-beta.23.tgz", + "integrity": "sha512-PY1yLymRxNDnUMrLAvu6dAImoUco4RpkFv+u72AUGXcgbmvPNcRwBC/11TcLHlqnzOzy31QjWZBYsbUE89jWiA==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gcr-4": { + "version": "4.4.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gcr-4/-/gcr-4-4.4.0-4.0.0-beta.23.tgz", + "integrity": "sha512-Oo6/wDbbAq+ytfFGhOHFjOjfVdhTkE0EvdvTLSa31qNfCEI+0I0OwL1gG35DoUCKeESqd6WgpqMXmMnop3/yIg==", + "license": "MIT", + "dependencies": { + "@girs/gck-2": "^4.4.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gdesktopenums-3.0": { + "version": "3.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gdesktopenums-3.0/-/gdesktopenums-3.0-3.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-T2LpS68w/HtOH1Cd/RGWOXrqPJEsUNOhUOpjwZhH6lu3Ey1IYEq5yyyIeWi3ts9QS4SjbhZvqMTJKFwA5xywMw==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gdk-4.0": { + "version": "4.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gdk-4.0/-/gdk-4.0-4.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-esdIxWgxyKQKYAsQBnTZPHocf5CXwEovt6Xp2aIVxbATLzzd0x35194PPjq51IbZ7eRi/XnBC9yTlNI5JYVNog==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/pangocairo-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gdkpixbuf-2.0": { + "version": "2.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gdkpixbuf-2.0/-/gdkpixbuf-2.0-2.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-Apku+aCEsosPXdEOnDtI3GPdX2Seo2rWUm10aY3ijGOnvafjmsJkHwWlThHzV850Ygi7dw3hBBys0z8hG6Cf1A==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gdm-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gdm-1.0/-/gdm-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-mKsmZTShn6xh0yzFcgzCddG3X2oXQi0nc5Q3AR3TPk+UURbREBdJwzeoIKI3Uqi03gwaRQVtFopHQ1B5IRSTAg==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gio-2.0": { + "version": "2.84.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gio-2.0/-/gio-2.0-2.84.0-4.0.0-beta.23.tgz", + "integrity": "sha512-FcGgYQ96KhdeFJvUYsIm0XmOYaYnTfpHKzPUOW4IcJm5q38RNDKgoijBMfedv3fMxnPDtpMb08ZFD6P1GjJY8A==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gjs": { + "version": "4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gjs/-/gjs-4.0.0-beta.23.tgz", + "integrity": "sha512-Yz1s23WaGAsVHetWFReVxeYDJbVFtJ6KZ7u+qzWa/B3P2mB+5aXpGc7nV6Bx7GbRc48FuGgtbKxVo0rq26Ug/Q==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gl-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gl-1.0/-/gl-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-OaaqoeoZG2ovpf0SrDXDVNZOGk5Ay+tdw6M+sAsqjI+02epWgItWvdl3HfGd/82ENviIufw3s+vtWKodNUoWFw==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/glib-2.0": { + "version": "2.84.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/glib-2.0/-/glib-2.0-2.84.0-4.0.0-beta.23.tgz", + "integrity": "sha512-epQae3f9rDeIrEgA9hOEiNAppJYQAfVRZ4Uh/0yO9NtAQR6PTfXUpjotw9ndjVxsHpEmRODoM2t/kytCYqKmVg==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gmodule-2.0": { + "version": "2.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gmodule-2.0/-/gmodule-2.0-2.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-Dc+Pq1peNlwQ0o/WFsUzT1qt3oqgMLBhzjEfOTGAD0Jw1Ut3QCoBuryVFFNMIruOKnSSBoBnQO7Qelly5aSd2w==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gnome-shell": { + "version": "48.0.2", + "resolved": "https://registry.npmjs.org/@girs/gnome-shell/-/gnome-shell-48.0.2.tgz", + "integrity": "sha512-hrlnTCc6y9O7GTn7M7YAufKdmIF8Et7ZFTRRUVXyv3hBHAor0bUFDrHdmVD+D7KdMHCJrtaLW6EtUCRQyovU2A==", + "license": "MIT", + "dependencies": { + "@girs/accountsservice-1.0": "1.0.0-4.0.0-beta.23", + "@girs/adw-1": "^1.8.0-4.0.0-beta.23", + "@girs/atk-1.0": "^2.56.0-4.0.0-beta.23", + "@girs/clutter-16": "^16.0.0-4.0.0-beta.23", + "@girs/cogl-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gcr-4": "^4.4.0-4.0.0-beta.23", + "@girs/gdm-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gnomebg-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gnomebluetooth-3.0": "^3.0.0-4.0.0-beta.23", + "@girs/gnomedesktop-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gtk-4.0": "^4.18.3-4.0.0-beta.23", + "@girs/gvc-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/meta-16": "^16.0.0-4.0.0-beta.23", + "@girs/mtk-16": "^16.0.0-4.0.0-beta.23", + "@girs/polkit-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/shell-16": "^16.0.0-4.0.0-beta.23", + "@girs/shew-0": "^0.0.0-4.0.0-beta.23", + "@girs/st-16": "^16.0.0-4.0.0-beta.23", + "@girs/upowerglib-1.0": "^0.99.1-4.0.0-beta.23" + } + }, + "node_modules/@girs/gnomebg-4.0": { + "version": "4.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gnomebg-4.0/-/gnomebg-4.0-4.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-mXipjnVd+lUSMhUeugo49TXo32ihlKiN2ggpGDLIcD6ZfGe644DSgxoHFeYkn44HmNW39DvSqF9AcSvLP0cZJA==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdesktopenums-3.0": "^3.0.0-4.0.0-beta.23", + "@girs/gdk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gnomedesktop-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/pangocairo-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gnomebluetooth-3.0": { + "version": "3.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gnomebluetooth-3.0/-/gnomebluetooth-3.0-3.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-nLpLGgJwqaxtrie//bodjb9+CnELo6AqXM6QDi13C2pDlfMZGeCdLZj3kRvqqCLnPRJybIUVEjRLSEloZUHJnQ==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gnomedesktop-4.0": { + "version": "4.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gnomedesktop-4.0/-/gnomedesktop-4.0-4.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-pkH6574kp371HH0F/M77lz7e8fK4hzpxaOz5w2cNoYdU3sMqUgET0nak0gxOsjuTBzd2gPZm3gQHhOz/J4AEtw==", + "license": "MIT", + "dependencies": { + "@girs/gdesktopenums-3.0": "^3.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gobject-2.0": { + "version": "2.84.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gobject-2.0/-/gobject-2.0-2.84.0-4.0.0-beta.23.tgz", + "integrity": "sha512-GNWQDLo+Nmq2FQNPljKKh4Zplp8vZwwr0hj1sf4lbJ36zbVsovrhfNozqaph0XNjv8vtHeTcXUocGQprM9FHCg==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/graphene-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/graphene-1.0/-/graphene-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-C6HAR88uCAi6xCKip208tPdzYUBFICiCyBSBm+gVzniEfikNM4CVN7+tHHwp4Gd1FKixK5EMVDIYs5sCfToNWQ==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gsk-4.0": { + "version": "4.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gsk-4.0/-/gsk-4.0-4.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-zLjklfxLjef6SV5VEQ2+52G/d+hQqmp6HbBu0/Jn5CLFFYe22LmDQWSEtGOn0NwKDL7XltXaxKoT/JBHEQsgcg==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/pangocairo-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gtk-4.0": { + "version": "4.18.3-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gtk-4.0/-/gtk-4.0-4.18.3-4.0.0-beta.23.tgz", + "integrity": "sha512-CsMPZLHQz+kWP81zR8AfPRpqUh9g1i5QNG08E9shj3Z9hvbR3goXmQXCSjgVxkcD40YsFn2tlc5FF/aztwY45Q==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/gsk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/pangocairo-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/gvc-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/gvc-1.0/-/gvc-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-Pzx/2KP0wBBMLmLcpwyVN6HE6SCPjlOqI2GCM81ztfb/EABAxAgq7liMua9RbIlo+eKj7e+GBb3rq8mEphgvcg==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/harfbuzz-0.0": { + "version": "9.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/harfbuzz-0.0/-/harfbuzz-0.0-9.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-9uWYDEUhkyoClR4GHv69AMHRDtcNSwidtA8MG8c1rMDT5qWNWkwfQK5hwX8/oBCbL0LqJNeMfJByIQ3yLfgQGg==", + "license": "MIT", + "dependencies": { + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/meta-16": { + "version": "16.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/meta-16/-/meta-16-16.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-qKto7mjzIS3D/uFRJxVWUFAV7VD0H2sbW3FDU9pvyVM++mtnrBnSi3SeTBJhIVFOhFCurcyn4SpqSoD7XkZ0uw==", + "license": "MIT", + "dependencies": { + "@girs/atk-1.0": "^2.56.0-4.0.0-beta.23", + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/clutter-16": "^16.0.0-4.0.0-beta.23", + "@girs/cogl-16": "^16.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdesktopenums-3.0": "^3.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gl-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/mtk-16": "^16.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/xfixes-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/xlib-2.0": "^2.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/mtk-16": { + "version": "16.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/mtk-16/-/mtk-16-16.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-nLiqoarVgY4nPl6aBsuxzRdiRMGwyXmzlbLSTtCPUsK73QeKWfsdNtU4xDeC3WkBNtYM8mnosF/sqBodbR+lrg==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/nm-1.0": { + "version": "1.49.4-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/nm-1.0/-/nm-1.0-1.49.4-4.0.0-beta.23.tgz", + "integrity": "sha512-N0TCk6b0KDH4p/h91+aiusSHdJ4986vjkc5NFKEPS+QHBUyqHU4nf/+RAU1bfII/KU2rQejJfx/yDRGc6PwyIw==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/pango-1.0": { + "version": "1.56.4-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/pango-1.0/-/pango-1.0-1.56.4-4.0.0-beta.23.tgz", + "integrity": "sha512-gezMBRQerPUt7xLGzx34W0UFK+AVf2S7GPKm1LK8dGGq4qnjkkEdf1w2wcN/iV3lLCxz1+4U0MmBArrqcSe2hQ==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/pangocairo-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/pangocairo-1.0/-/pangocairo-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-8omT0HAxzj+IDLIYLv08GjXc43yRs5QXxttEtV+/0aDWX0XOyaenuWg9cNS8lwBKPIa5tW9lcdLCudyBJ2xcqA==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23" + } + }, + "node_modules/@girs/polkit-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/polkit-1.0/-/polkit-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-jayhgMeindShO2SMbEZduYcFrU5GaHjgJWK7zGdkg3QDSKhy1pZJwxmDJcq/yvNtMmcGsws7aTrLcnAYrhjg0w==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/polkitagent-1.0": { + "version": "1.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/polkitagent-1.0/-/polkitagent-1.0-1.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-LAwHOtoaWJ1SuAKBKXeTa2pgHZGUyN6X9qujHpnzVDpli1gawlY7irb7fk/sCkc/Nxt4IgN2YfCE+BENi3726Q==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/polkit-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/shell-16": { + "version": "16.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/shell-16/-/shell-16-16.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-Gynf4hxPnbuzW6FkmMIay1ky//tmAIWaOvXgV1So+i2BlyglJXCo2WYSCxjEtp0+JrNvSWBnKf7Hnh9S++30dQ==", + "license": "MIT", + "dependencies": { + "@girs/atk-1.0": "^2.56.0-4.0.0-beta.23", + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/clutter-16": "^16.0.0-4.0.0-beta.23", + "@girs/cogl-16": "^16.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gck-2": "^4.4.0-4.0.0-beta.23", + "@girs/gcr-4": "^4.4.0-4.0.0-beta.23", + "@girs/gdesktopenums-3.0": "^3.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gl-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/gvc-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/meta-16": "^16.0.0-4.0.0-beta.23", + "@girs/mtk-16": "^16.0.0-4.0.0-beta.23", + "@girs/nm-1.0": "^1.49.4-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/polkit-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/polkitagent-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/st-16": "^16.0.0-4.0.0-beta.23", + "@girs/xfixes-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/xlib-2.0": "^2.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/shew-0": { + "version": "0.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/shew-0/-/shew-0-0.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-voxcO7uev7ZnrPfGoz9y0HV3fToXe5Q/Dzn1uKDVmebFKlcl8Oznr/zbPEuym+K7lKc4LVMos+f7HelxVfD27w==", + "license": "MIT", + "dependencies": { + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/gsk-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/gtk-4.0": "^4.18.3-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/pangocairo-1.0": "^1.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/st-16": { + "version": "16.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/st-16/-/st-16-16.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-eVaxgQZzwTyhKSv5OD9phI2c8SzKVnMA6b2SahJ4V/PR6pC65JsGZFmJE6WdtU23XKdFEoMnG/CKegKKyaw0eg==", + "license": "MIT", + "dependencies": { + "@girs/atk-1.0": "^2.56.0-4.0.0-beta.23", + "@girs/cairo-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/clutter-16": "^16.0.0-4.0.0-beta.23", + "@girs/cogl-16": "^16.0.0-4.0.0-beta.23", + "@girs/freetype2-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gdesktopenums-3.0": "^3.0.0-4.0.0-beta.23", + "@girs/gdkpixbuf-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gl-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/graphene-1.0": "^1.0.0-4.0.0-beta.23", + "@girs/harfbuzz-0.0": "^9.0.0-4.0.0-beta.23", + "@girs/meta-16": "^16.0.0-4.0.0-beta.23", + "@girs/mtk-16": "^16.0.0-4.0.0-beta.23", + "@girs/pango-1.0": "^1.56.4-4.0.0-beta.23", + "@girs/xfixes-4.0": "^4.0.0-4.0.0-beta.23", + "@girs/xlib-2.0": "^2.0.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/upowerglib-1.0": { + "version": "0.99.1-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/upowerglib-1.0/-/upowerglib-1.0-0.99.1-4.0.0-beta.23.tgz", + "integrity": "sha512-GpWzIVdXSK2dhk1I7utZIzao7NLSIP4iZ/EOXUUV7HSaDeNW/1orR5jUZMJOQhfH8w0JiFsfaoZTm/7TTZlUPg==", + "license": "MIT", + "dependencies": { + "@girs/gio-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gjs": "^4.0.0-beta.23", + "@girs/glib-2.0": "^2.84.0-4.0.0-beta.23", + "@girs/gmodule-2.0": "^2.0.0-4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/xfixes-4.0": { + "version": "4.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/xfixes-4.0/-/xfixes-4.0-4.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-66pjrl+5kXurr1NRBE6rFBixQp3F15HvYrWkzXzrTJABTt3H1it82jsJkprA1h3Z7Z8ucxx/u3fEWMJGY5xUjQ==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@girs/xlib-2.0": { + "version": "2.0.0-4.0.0-beta.23", + "resolved": "https://registry.npmjs.org/@girs/xlib-2.0/-/xlib-2.0-2.0.0-4.0.0-beta.23.tgz", + "integrity": "sha512-FETH5hCvxs45ZonJ948nosy0tXAIZPTyrtF0r6TwVvjMHSHuINXsU4ylxM7Uc0xeuFh5KL+3ShAPXzwi9FynVg==", + "license": "MIT", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gobject-2.0": "^2.84.0-4.0.0-beta.23" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/types": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "dev": true, + "license": "ISC" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "requires": { - "color-convert": "^1.9.0" + "engines": { + "node": ">=8" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "requires": { - "sprintf-js": "~1.0.2" + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true + "node_modules/are-docs-informative": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", + "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } }, - "balanced-match": { + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", "dev": true, - "requires": { + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "requires": { - "color-name": "1.1.3" + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "concat-map": { + "node_modules/comment-parser": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", + "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, - "cross-spawn": { + "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, - "requires": { - "ms": "2.1.2" + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, - "doctrine": { + "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "requires": { + "dependencies": { "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, - "requires": { - "ansi-colors": "^4.1.1" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.26.0.tgz", - "integrity": "sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg==", + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.1", - "ajv": "^6.10.0", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", - "minimatch": "^3.0.4", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/eslint-plugin-jsdoc": { + "version": "50.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.7.1.tgz", + "integrity": "sha512-XBnVA5g2kUVokTNUiE1McEPse5n9/mNUmuJcx52psT6zBs2eVcXSmQBvjfa7NZdfLVSy3u1pEDDUxoxpwy89WA==", "dev": true, - "requires": { + "license": "BSD-3-Clause", + "dependencies": { + "@es-joy/jsdoccomment": "~0.50.2", + "are-docs-informative": "^0.0.2", + "comment-parser": "1.4.1", + "debug": "^4.4.1", + "escape-string-regexp": "^4.0.0", + "espree": "^10.3.0", + "esquery": "^1.6.0", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.2", + "spdx-expression-parse": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jsdoc/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" + "estraverse": "^5.2.0" }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "funding": { + "url": "https://opencollective.com/eslint" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "requires": { + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { "estraverse": "^5.1.0" }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } + "engines": { + "node": ">=0.10" } }, - "esrecurse": { + "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "requires": { + "dependencies": { "estraverse": "^5.2.0" }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } + "engines": { + "node": ">=4.0" } }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "fast-deep-equal": { + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, - "fast-json-stable-stringify": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, - "fast-levenshtein": { + "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, - "file-entry-cache": { + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "requires": { + "dependencies": { "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "node_modules/flat-cache": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz", + "integrity": "sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==", + "dev": true, + "dependencies": { + "flatted": "^3.2.7", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, - "requires": { - "is-glob": "^4.0.1" + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" } }, - "globals": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.8.0.tgz", - "integrity": "sha512-rHtdA6+PDBIjeEvA91rpqzEvk/k3/i7EeNQiryiWuJH0Hw9cpyJMAt2jtbAwUaRdhD+573X4vWw6IcjKPasi9Q==", + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "type-fest": "^0.20.2" }, - "dependencies": { - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { + "engines": { + "node": ">=8" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "imurmurhash": { + "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "requires": { - "is-extglob": "^2.1.1" + "engines": { + "node": ">=0.10.0" } }, - "isexe": { + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "json-schema-traverse": { + "node_modules/jsdoc-type-pratt-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", + "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, - "json-stable-stringify-without-jsonify": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, - "levn": { + "node_modules/keyv": { + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", + "integrity": "sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "lru-cache": { + "node_modules/locate-path": { "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { - "yallist": "^4.0.0" + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "natural-compare": { + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, - "requires": { + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "parent-module": { + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "path-is-absolute": { + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse-statements": "1.0.11" + } + }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "prelude-ls": { + "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.8.0" + } }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "resolve-from": { + "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "rimraf": { + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "dev": true, - "requires": { + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { - "lru-cache": "^6.0.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" } }, - "shebang-command": { + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "slice-ansi": { + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, + "license": "MIT", "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } + "license": "CC0-1.0" }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { - "ansi-regex": "^5.0.0" + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "license": "MIT", + "engines": { + "node": ">=8" }, - "dependencies": { - "ajv": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.4.0.tgz", - "integrity": "sha512-7QD2l6+KBSLwf+7MuYocbWvRPdOu63/trReTLu2KFwkgctnub1auoF+Y1WYcm09CTM7quuscrzqmASaLHC/K4Q==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "text-table": { + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, - "type-check": { + "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "uri-js": { + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "punycode": "^2.1.0" } }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 7bb10a3..c4d4367 100644 --- a/package.json +++ b/package.json @@ -2,16 +2,23 @@ "name": "top-bar-organizer", "version": "1.0.0", "description": "A Gnome Shell Extension for organizing your Gnome Shell top bar.", + "type": "module", "directories": { "doc": "docs" }, "repository": { "type": "git", - "url": "git@gitlab.gnome.org:julianschacher/top-bar-organizer.git" + "url": "git@gitlab.gnome.org:june/top-bar-organizer.git" }, - "author": "Julian Schacher", + "author": "June", "license": "GPL-3.0-or-later", "devDependencies": { - "eslint": "^7.26.0" + "eslint": "^8.57.1", + "eslint-plugin-jsdoc": "^50.7.1", + "typescript": "^5.8.3" + }, + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gnome-shell": "^48.0.2" } } diff --git a/package.sh b/package.sh index bc05fc0..63497dc 100755 --- a/package.sh +++ b/package.sh @@ -4,8 +4,14 @@ set -e REAL_BASE_DIR=$( dirname $( readlink -f "$0" )) -glib-compile-schemas "$REAL_BASE_DIR/src/schemas" -rm "$REAL_BASE_DIR/top-bar-organizer@julian.gse.jsts.xyz.zip" || true -cd "$REAL_BASE_DIR/src" -zip -r "$REAL_BASE_DIR/top-bar-organizer@julian.gse.jsts.xyz.zip" * -zip -d "$REAL_BASE_DIR/top-bar-organizer@julian.gse.jsts.xyz.zip" "schemas/org.gnome.shell.extensions.top-bar-organizer.gschema.xml" +rm -rf "$REAL_BASE_DIR/dist" +cd "$REAL_BASE_DIR" +npx tsc +cp "$REAL_BASE_DIR/src/metadata.json" "$REAL_BASE_DIR/dist/metadata.json" +gnome-extensions pack "$REAL_BASE_DIR/dist" \ + --force \ + --extra-source extensionModules \ + --extra-source prefsModules \ + --extra-source ../data/ui \ + --extra-source ../data/css \ + --schema ../data/org.gnome.shell.extensions.top-bar-organizer.gschema.xml diff --git a/res/Screenshot of GNOME Shell 43 with Top Bar Organizer v6 and its preferences in light theme 2023-01-30.jpg b/res/Screenshot of GNOME Shell 43 with Top Bar Organizer v6 and its preferences in light theme 2023-01-30.jpg new file mode 100644 index 0000000..bb9594d Binary files /dev/null and b/res/Screenshot of GNOME Shell 43 with Top Bar Organizer v6 and its preferences in light theme 2023-01-30.jpg differ diff --git a/src/extension.js b/src/extension.js deleted file mode 100644 index 44ee11e..0000000 --- a/src/extension.js +++ /dev/null @@ -1,427 +0,0 @@ -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported init */ -"use strict"; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const Main = imports.ui.main; -const Panel = imports.ui.panel; - -const AppIndicatorKStatusNotifierItemManager = Me.imports.extensionModules.AppIndicatorKStatusNotifierItemManager; -const BoxOrderCreator = Me.imports.extensionModules.BoxOrderCreator; - -class Extension { - constructor() { - } - - enable() { - this.settings = ExtensionUtils.getSettings(); - - // Create an instance of AppIndicatorKStatusNotifierItemManager to - // handle AppIndicator/KStatusNotifierItem items. - this._appIndicatorKStatusNotifierItemManager = new AppIndicatorKStatusNotifierItemManager.AppIndicatorKStatusNotifierItemManager(); - - // Create an instance of BoxOrderCreator for the creation of special box - // orders. - this._boxOrderCreator = new BoxOrderCreator.BoxOrderCreator(this._appIndicatorKStatusNotifierItemManager); - - this._addNewItemsToBoxOrders(); - this._orderTopBarItemsOfAllBoxes(); - this._overwritePanelAddToPanelBox(); - - // Handle changes of configured box orders. - this._settingsHandlerIds = [ ]; - - const addConfiguredBoxOrderChangeHandler = (box) => { - let handlerId = this.settings.connect(`changed::${box}-box-order`, () => { - this._orderTopBarItems(box); - - /// For the case, where the currently saved box order is based - /// on a permutation of an outdated box order, get an updated - /// box order and save it, if needed. - let updatedBoxOrder; - switch (box) { - case "left": - updatedBoxOrder = this._createUpdatedBoxOrders().left; - break; - case "center": - updatedBoxOrder = this._createUpdatedBoxOrders().center; - break; - case "right": - updatedBoxOrder = this._createUpdatedBoxOrders().right; - break; - } - // Only save the updated box order to settings, if it is - // different, to avoid looping. - const currentBoxOrder = this.settings.get_strv(`${box}-box-order`); - if (JSON.stringify(currentBoxOrder) !== JSON.stringify(updatedBoxOrder)) { - this.settings.set_strv(`${box}-box-order`, updatedBoxOrder); - } - }); - this._settingsHandlerIds.push(handlerId); - }; - - addConfiguredBoxOrderChangeHandler("left"); - addConfiguredBoxOrderChangeHandler("center"); - addConfiguredBoxOrderChangeHandler("right"); - } - - disable() { - // Revert the overwrite of `Panel._addToPanelBox`. - Panel.Panel.prototype._addToPanelBox = Panel.Panel.prototype._originalAddToPanelBox; - // Set `Panel._originalAddToPanelBox` to `undefined`. - Panel._originalAddToPanelBox = undefined; - - // Disconnect signals. - for (const handlerId of this._settingsHandlerIds) { - this.settings.disconnect(handlerId); - } - } - - //////////////////////////////////////////////////////////////////////////// - /// Methods used on extension enable. /// - //////////////////////////////////////////////////////////////////////////// - - /** - * This method adds all new items currently present in the Gnome Shell top - * bar to the box orders. - */ - _addNewItemsToBoxOrders() { - const boxOrders = this._createUpdatedBoxOrders(); - this.settings.set_strv("left-box-order", boxOrders.left); - this.settings.set_strv("center-box-order", boxOrders.center); - this.settings.set_strv("right-box-order", boxOrders.right); - } - - /** - * This methods orders the top bar items of all boxes according to the - * configured box orders using `this._orderTopBarItems`. - */ - _orderTopBarItemsOfAllBoxes() { - this._orderTopBarItems("left"); - this._orderTopBarItems("center"); - this._orderTopBarItems("right"); - } - - /** - * An object containing a position and box overwrite. - * @typedef PositionAndBoxOverwrite - * @property {Number} position - The position overwrite. - * @property {string} box - The position box overwrite. - */ - - /** - * Overwrite `Panel._addToPanelBox` with a custom method, which handles top - * bar item additions to make sure that they are added in the correct - * position and box. - */ - _overwritePanelAddToPanelBox() { - // Add the original `Panel._addToPanelBox` method as - // `Panel._originalAddToPanelBox`. - Panel.Panel.prototype._originalAddToPanelBox = Panel.Panel.prototype._addToPanelBox; - - // This function gets used by the `Panel._addToPanelBox` overwrite to - // determine the position and box for a new item. - // It also adds the new item to the relevant box order, if it isn't in - // it already. - const getPositionAndBoxOverwrite = (role, box, indicator) => { - const boxOrders = { - left: this.settings.get_strv("left-box-order"), - center: this.settings.get_strv("center-box-order"), - right: this.settings.get_strv("right-box-order"), - }; - let boxOrder; - - // Handle the case where the new item is a - // AppIndicator/KStatusNotifierItem. - if (role.startsWith("appindicator-")) { - switch (box) { - case "left": - boxOrder = this.settings.get_strv("left-box-order"); - this._appIndicatorKStatusNotifierItemManager.handleAppIndicatorKStatusNotifierItemItem(indicator.container, role, boxOrder, boxOrders); - this.settings.set_strv("left-box-order", boxOrder); - break; - case "center": - boxOrder = this.settings.get_strv("center-box-order"); - this._appIndicatorKStatusNotifierItemManager.handleAppIndicatorKStatusNotifierItemItem(indicator.container, role, boxOrder, boxOrders); - this.settings.set_strv("center-box-order", boxOrder); - break; - case "right": - boxOrder = this.settings.get_strv("right-box-order"); - this._appIndicatorKStatusNotifierItemManager.handleAppIndicatorKStatusNotifierItemItem(indicator.container, role, boxOrder, boxOrders, true); - this.settings.set_strv("right-box-order", boxOrder); - break; - } - } - - // Get the resolved box orders for all boxes. - const resolvedBoxOrders = { - left: this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this.settings.get_strv("left-box-order")), - center: this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this.settings.get_strv("center-box-order")), - right: this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this.settings.get_strv("right-box-order")), - }; - // Also get the restricted valid box order of the target box. - const restrictedValidBoxOrderOfTargetBox = this._boxOrderCreator.createRestrictedValidBoxOrder(box); - - // Get the index of the role for each box order. - const indices = { - left: resolvedBoxOrders.left.indexOf(role), - center: resolvedBoxOrders.center.indexOf(role), - right: resolvedBoxOrders.right.indexOf(role), - }; - - // If the role is not already configured in one of the box orders, - // just add it to the target box order at the end/beginning, save - // the updated box order and return the relevant position and box. - if (indices.left === -1 - && indices.center === -1 - && indices.right === -1) { - switch (box) { - // For the left and center box, insert the role at the end, - // since they're LTR. - case "left": - boxOrders["left"].push(role); - this.settings.set_strv("left-box-order", boxOrders["left"]); - return { - position: restrictedValidBoxOrderOfTargetBox.length - 1, - box: box - }; - case "center": - boxOrders["center"].push(role); - this.settings.set_strv("center-box-order", boxOrders["center"]); - return { - position: restrictedValidBoxOrderOfTargetBox.length - 1, - box: box - }; - // For the right box, insert the role at the beginning, - // since it's RTL. - case "right": - boxOrders["right"].unshift(role); - this.settings.set_strv("right-box-order", boxOrders["right"]); - return { - position: 0, - box: box - }; - } - } - - /// Since the role is already configured in one of the box orders, - /// determine the correct insertion index for the position. - const determineInsertionIndex = (index, restrictedValidBoxOrder, boxOrder) => { - // Set the insertion index initially to 0, so that if no closest - // item can be found, the new item just gets inserted at the - // beginning. - let insertionIndex = 0; - - // Find the index of the closest item, which is also in the - // valid box order and before the new item. - // This way, we can insert the new item just after the index of - // this closest item. - for (let i = index - 1; i >= 0; i--) { - let potentialClosestItemIndex = restrictedValidBoxOrder.indexOf(boxOrder[i]); - if (potentialClosestItemIndex !== -1) { - insertionIndex = potentialClosestItemIndex + 1; - break; - } - } - - return insertionIndex; - }; - - if (indices.left !== -1) { - return { - position: determineInsertionIndex(indices.left, this._boxOrderCreator.createRestrictedValidBoxOrder("left"), resolvedBoxOrders.left), - box: "left" - }; - } - - if (indices.center !== -1) { - return { - position: determineInsertionIndex(indices.center, this._boxOrderCreator.createRestrictedValidBoxOrder("center"), resolvedBoxOrders.center), - box: "center" - }; - } - - if (indices.right !== -1) { - return { - position: determineInsertionIndex(indices.right, this._boxOrderCreator.createRestrictedValidBoxOrder("right"), resolvedBoxOrders.right), - box: "right" - }; - } - }; - - // Overwrite `Panel._addToPanelBox`. - Panel.Panel.prototype._addToPanelBox = function (role, indicator, position, box) { - // Get the position and box overwrite. - let positionBoxOverwrite; - switch (box) { - case this._leftBox: - positionBoxOverwrite = getPositionAndBoxOverwrite(role, "left", indicator); - break; - case this._centerBox: - positionBoxOverwrite = getPositionAndBoxOverwrite(role, "center", indicator); - break; - case this._rightBox: - positionBoxOverwrite = getPositionAndBoxOverwrite(role, "right", indicator); - break; - } - - // Call the original `Panel._addToPanelBox` with the position - // overwrite as the position argument and the box determined by the - // box overwrite as the box argument. - switch (positionBoxOverwrite.box) { - case "left": - this._originalAddToPanelBox(role, indicator, positionBoxOverwrite.position, Main.panel._leftBox); - break; - case "center": - this._originalAddToPanelBox(role, indicator, positionBoxOverwrite.position, Main.panel._centerBox); - break; - case "right": - this._originalAddToPanelBox(role, indicator, positionBoxOverwrite.position, Main.panel._rightBox); - break; - } - }; - } - - //////////////////////////////////////////////////////////////////////////// - /// Helper methods holding logic needed by other methods. /// - //////////////////////////////////////////////////////////////////////////// - - /** - * An object containing a box order for the left, center and right top bar - * box. - * @typedef {Object} BoxOrders - * @property {string[]} left - The box order for the left top bar box. - * @property {string[]} center - The box order for the center top bar box. - * @property {string[]} right - The box order for the right top bar box. - */ - - /** - * This method adds all new items currently present in the Gnome Shell top - * bar to the correct box order and returns the new box orders. - * @returns {BoxOrders} - The updated box orders. - */ - _createUpdatedBoxOrders() { - // Load the configured box orders from settings. - const boxOrders = { - left: this.settings.get_strv("left-box-order"), - center: this.settings.get_strv("center-box-order"), - right: this.settings.get_strv("right-box-order"), - }; - - // Get items (or rather their roles) currently present in the Gnome - // Shell top bar and index them using their associated indicator - // container. - let indicatorContainerRoleMap = new Map(); - for (const role in Main.panel.statusArea) { - indicatorContainerRoleMap.set(Main.panel.statusArea[role].container, role); - } - - // Get the indicator containers (of the items) currently present in the - // Gnome Shell top bar boxes. - const boxOrderIndicatorContainers = { - left: Main.panel._leftBox.get_children(), - center: Main.panel._centerBox.get_children(), - // Reverse this array, since the items in the left and center box - // are logically LTR, while the items in the right box are RTL. - right: Main.panel._rightBox.get_children().reverse() - }; - - // This function goes through the items (or rather their indicator - // containers) of the given box and adds new items (or rather their - // roles) to the box order. - const addNewItemsToBoxOrder = (boxIndicatorContainers, boxOrder, box) => { - for (const indicatorContainer of boxIndicatorContainers) { - // First get the role associated with the current indicator - // container. - const associatedRole = indicatorContainerRoleMap.get(indicatorContainer); - if (!associatedRole) continue; - - // Handle an AppIndicator/KStatusNotifierItem item differently. - if (associatedRole.startsWith("appindicator-")) { - this._appIndicatorKStatusNotifierItemManager.handleAppIndicatorKStatusNotifierItemItem(indicatorContainer, associatedRole, boxOrder, boxOrders, box === "right"); - continue; - } - - // Add the role to the box order, if it isn't in in one already. - if (!boxOrders.left.includes(associatedRole) - && !boxOrders.center.includes(associatedRole) - && !boxOrders.right.includes(associatedRole)) { - if (box === "right") { - // Add the items to the beginning for this array, since - // its RTL. - boxOrder.unshift(associatedRole); - } else { - boxOrder.push(associatedRole); - } - } - } - }; - - addNewItemsToBoxOrder(boxOrderIndicatorContainers.left, boxOrders.left, "left"); - addNewItemsToBoxOrder(boxOrderIndicatorContainers.center, boxOrders.center, "center"); - addNewItemsToBoxOrder(boxOrderIndicatorContainers.right, boxOrders.right, "right"); - - return boxOrders; - } - - /** - * This method orders the top bar items of the specified box according to - * the configured box orders. - * @param {string} box - The box to order. - */ - _orderTopBarItems(box) { - // Get the valid box order. - const validBoxOrder = this._boxOrderCreator.createValidBoxOrder(box); - - // Get the relevant box of `Main.panel`. - let panelBox; - switch (box) { - case "left": - panelBox = Main.panel._leftBox; - break; - case "center": - panelBox = Main.panel._centerBox; - break; - case "right": - panelBox = Main.panel._rightBox; - break; - } - - /// Go through the items (or rather their roles) of the validBoxOrder - /// and order the panelBox accordingly. - for (let i = 0; i < validBoxOrder.length; i++) { - const role = validBoxOrder[i]; - // Get the indicator container associated with the current role. - const associatedIndicatorContainer = Main.panel.statusArea[role].container; - - associatedIndicatorContainer.get_parent().remove_child(associatedIndicatorContainer); - panelBox.insert_child_at_index(associatedIndicatorContainer, i); - } - // To handle the case, where the box order got set to a permutation - // of an outdated box order, it would be wise, if the caller updated the - // box order now to include the items present in the top bar. - } -} - -function init() { - return new Extension(); -} diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..026de7e --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,200 @@ +"use strict"; + +import St from "gi://St" +import type Gio from "gi://Gio" + +import * as Main from "resource:///org/gnome/shell/ui/main.js"; +import * as Panel from "resource:///org/gnome/shell/ui/panel.js"; +import { Extension } from "resource:///org/gnome/shell/extensions/extension.js"; + +import BoxOrderManager from "./extensionModules/BoxOrderManager.js"; +import type { Box } from "./extensionModules/BoxOrderManager.js"; + +export interface CustomPanel extends Panel.Panel { + _leftBox: St.BoxLayout; + _centerBox: St.BoxLayout; + _rightBox: St.BoxLayout; +} + +export default class TopBarOrganizerExtension extends Extension { + _settings!: Gio.Settings; + _boxOrderManager!: BoxOrderManager; + _settingsHandlerIds!: number[]; + + enable(): void { + this._settings = this.getSettings(); + + this._boxOrderManager = new BoxOrderManager({}, this._settings); + + /// Stuff to do on startup(extension enable). + // Initially handle new top bar items and order top bar boxes. + this.#handleNewItemsAndOrderTopBar(); + + // Overwrite the `Panel._addToPanelBox` method with one handling new + // items. + this.#overwritePanelAddToPanelBox(); + // Handle AppIndicators getting ready, to handle new AppIndicator items. + this._boxOrderManager.connect("appIndicatorReady", () => { + this.#handleNewItemsAndOrderTopBar(); + }); + + // Handle changes of settings. + this._settingsHandlerIds = []; + const addSettingsChangeHandler = (settingsName: string) => { + const handlerId = this._settings.connect(`changed::${settingsName}`, () => { + this.#handleNewItemsAndOrderTopBar(); + }); + this._settingsHandlerIds.push(handlerId); + }; + addSettingsChangeHandler("left-box-order"); + addSettingsChangeHandler("center-box-order"); + addSettingsChangeHandler("right-box-order"); + addSettingsChangeHandler("hide"); + addSettingsChangeHandler("show"); + } + + disable(): void { + // Revert the overwrite of `Panel._addToPanelBox`. + // @ts-ignore + Panel.Panel.prototype._addToPanelBox = Panel.Panel.prototype._originalAddToPanelBox; + // Set `Panel._originalAddToPanelBox` to `undefined`. + // @ts-ignore + Panel.Panel.prototype._originalAddToPanelBox = undefined; + + // Disconnect signals. + for (const handlerId of this._settingsHandlerIds) { + this._settings.disconnect(handlerId); + } + this._boxOrderManager.disconnectSignals(); + + // @ts-ignore + this._settings = null; + // @ts-ignore + this._boxOrderManager = null; + } + + //////////////////////////////////////////////////////////////////////////// + /// Methods used on extension enable. /// + //////////////////////////////////////////////////////////////////////////// + + /** + * Overwrite `Panel._addToPanelBox` with a custom method, which simply calls + * the original one and handles new items and orders the top bar afterwards. + */ + #overwritePanelAddToPanelBox(): void { + // Add the original `Panel._addToPanelBox` method as + // `Panel._originalAddToPanelBox`. + // @ts-ignore + Panel.Panel.prototype._originalAddToPanelBox = Panel.Panel.prototype._addToPanelBox; + + const handleNewItemsAndOrderTopBar = () => { + this.#handleNewItemsAndOrderTopBar(); + }; + + // Overwrite `Panel._addToPanelBox`. + Panel.Panel.prototype._addToPanelBox = function(role, indicator, position, box) { + // Simply call the original `_addToPanelBox` and order the top bar + // and handle new items afterwards. + // @ts-ignore + this._originalAddToPanelBox(role, indicator, position, box); + handleNewItemsAndOrderTopBar(); + }; + } + + //////////////////////////////////////////////////////////////////////////// + /// Helper methods holding logic needed by other methods. /// + //////////////////////////////////////////////////////////////////////////// + + /** + * This method orders the top bar items of the specified box according to + * the configured box orders. + * @param {Box} box - The box to order. + */ + #orderTopBarItems(box: Box): void { + // Only run, when the session mode is "user" or the parent session mode + // is "user". + if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") { + return; + } + + // Get the valid box order. + const validBoxOrder = this._boxOrderManager.getValidBoxOrder(box); + + // Get the relevant box of `Main.panel`. + let panelBox = (Main.panel as CustomPanel)[`_${box}Box`]; + + /// Go through the items of the validBoxOrder and order the GNOME Shell + /// top bar box accordingly. + for (let i = 0; i < validBoxOrder.length; i++) { + const item = validBoxOrder[i]; + // Get the indicator container associated with the current role. + const associatedIndicatorContainer = (Main.panel.statusArea as any)[item.role]?.container; + if (!(associatedIndicatorContainer instanceof St.Bin)) { + // TODO: maybe add logging + continue; + } + + // Save whether or not the indicator container is visible. + const isVisible = associatedIndicatorContainer.visible; + + const parent = associatedIndicatorContainer.get_parent(); + if (parent !== null) { + parent.remove_child(associatedIndicatorContainer); + } + if (box === "right") { + // If the target panel box is the right panel box, insert the + // indicator container at index `-1`, which just adds it to the + // end (correct order is ensured, since `validBoxOrder` is + // sorted correctly and we're looping over it in order). + // This way unaccounted-for indicator containers will be at the + // left, which is preferred, since the box is logically + // right-to-left. + // The same applies for indicator containers, which are just + // temporarily unaccounted for (like for indicator containers of + // not yet ready app indicators), since them being at the right + // for a probably temporary stay causes all the indicator + // containers to shift. + panelBox.insert_child_at_index(associatedIndicatorContainer, -1); + } else { + panelBox.insert_child_at_index(associatedIndicatorContainer, i); + } + + // Hide the indicator container... + // - ...if it wasn't visible before and the hide property of the + // item is "default". + // - if the hide property of the item is "hide". + // In all other cases have the item show. + // An e.g. screen recording indicator still wouldn't show tho, since + // this here acts on the indicator container, but a screen recording + // indicator is hidden on the indicator level. + if ((!isVisible && item.hide === "default") || + item.hide === "hide") { + associatedIndicatorContainer.hide(); + } + } + // To handle the case, where the box order got set to a permutation + // of an outdated box order, it would be wise, if the caller updated the + // box order now to include the items present in the top bar. + } + + /** + * This method handles all new items currently present in the top bar and + * orders the items of all top bar boxes. + */ + #handleNewItemsAndOrderTopBar(): void { + // Only run, when the session mode is "user" or the parent session mode + // is "user". + if(Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") { + return; + } + + this._boxOrderManager.saveNewTopBarItems(); + this.#orderTopBarItems("left"); + this.#orderTopBarItems("center"); + this.#orderTopBarItems("right"); + // In `this.#orderTopBarItems` it says to update the box orders to + // include potentially new items, since the ordering might have been + // based on an outdated box order. However, since we already handle new + // top bar items at the beginning of this method, this isn't a concern. + } +} diff --git a/src/extensionModules/AppIndicatorKStatusNotifierItemManager.js b/src/extensionModules/AppIndicatorKStatusNotifierItemManager.js deleted file mode 100644 index 6701de4..0000000 --- a/src/extensionModules/AppIndicatorKStatusNotifierItemManager.js +++ /dev/null @@ -1,125 +0,0 @@ -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported AppIndicatorKStatusNotifierItemManager */ -"use strict"; - -var AppIndicatorKStatusNotifierItemManager = class AppIndicatorKStatusNotifierItemManager { - constructor() { - // Create an application-role map for associating roles with - // applications. - // This is needed so that this class can handle/manage - // AppIndicator/KStatusNotifierItem items. - this._applicationRoleMap = new Map(); - } - - /** - * Handle an AppIndicator/KStatusNotifierItem item. - * - * This function basically does the following two things: - * - Associate the role of the given item with the application of the - * AppIndicator/KStatusNotifierItem. - * - Add a placeholder for the roles associated with the application of the - * AppIndiciator/KStatusNotifierItem to the box order, if needed. - * - * Note: The caller is responsible for saving the updated box order to - * settings. - * @param {} indicatorContainer - The container of the indicator of the - * AppIndicator/KStatusNotifierItem item. - * @param {string} role - The role of the AppIndicator/KStatusNotifierItem - * item. - * @param {string[]} - The box order the placeholder should be added to, if - * needed. - * @param {BoxOrders} boxOrders - An object containing the box orders, which - * is currently getting worked on. - * @param {boolean} - Whether to add the placeholder to the beginning of the - * box order. - */ - handleAppIndicatorKStatusNotifierItemItem(indicatorContainer, role, boxOrder, boxOrders, atToBeginning = false) { - // Get the application the AppIndicator/KStatusNotifierItem is - // associated with. - let application = indicatorContainer.get_child()._indicator.id; - - // Since the Dropbox client appends its PID to the id, drop the PID and - // the hyphen before it. - if (application.startsWith("dropbox-client-")) application = "dropbox-client"; - - // Associate the role with the application. - let roles = this._applicationRoleMap.get(application); - if (roles) { - // If the application already has an array of associated roles, just - // add the role to it, if needed. - if (!roles.includes(role)) roles.push(role); - } else { - // Otherwise create a new array. - this._applicationRoleMap.set(application, [ role ]); - } - - // Store a placeholder for the roles associated with the application in - // the box order, if needed. - // (Then later the `this.createResolvedBoxOrder` method can be used to - // get a box order, where the placeholder/s get/s replaced with the - // relevant roles (by using `this._applicationRoleMap`).) - const placeholder = `appindicator-kstatusnotifieritem-${application}`; - if (!boxOrders.left.includes(placeholder) - && !boxOrders.center.includes(placeholder) - && !boxOrders.right.includes(placeholder)) { - if (atToBeginning) { - boxOrder.unshift(placeholder); - } else { - boxOrder.push(placeholder); - } - } - } - - /** - * This function takes a box order and returns a box order, where all - * placeholders got replaced with their relevant roles. - * @param {string[]} boxOrder - The box order of which to replace the - * placeholders. - * @returns {string[]} A resolved box order, where all placeholders got - * replaced with their relevant roles. - */ - createResolvedBoxOrder(boxOrder) { - let resolvedBoxOrder = [ ]; - for (const item of boxOrder) { - // If the item isn't a placeholder, just add it to the new resolved - // box order. - if (!item.startsWith("appindicator-kstatusnotifieritem-")) { - resolvedBoxOrder.push(item); - continue; - } - - /// If the item is a placeholder, replace it. - // First get the application this placeholder is associated with. - const application = item.replace("appindicator-kstatusnotifieritem-", ""); - - // Then get the roles associated with the application. - let roles = this._applicationRoleMap.get(application); - - // Continue, if there are no roles. - if (!roles) continue; - // Otherwise add the roles - for (const role of roles) { - resolvedBoxOrder.push(role); - } - } - - return resolvedBoxOrder; - } -}; diff --git a/src/extensionModules/BoxOrderCreator.js b/src/extensionModules/BoxOrderCreator.js deleted file mode 100644 index 228bea9..0000000 --- a/src/extensionModules/BoxOrderCreator.js +++ /dev/null @@ -1,137 +0,0 @@ -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported BoxOrderCreator */ -"use strict"; - -const ExtensionUtils = imports.misc.extensionUtils; - -const Main = imports.ui.main; - -/** - * A class exposing methods, which create special box orders. - */ -var BoxOrderCreator = class BoxOrderCreator { - /** - * @param {AppIndicatorKStatusNotifierItemManager} - * appIndicatorKStatusNotifierItemManager - An instance of - * AppIndicatorKStatusNotifierItemManager to be used in the methods of - * `this`. - */ - constructor(appIndicatorKStatusNotifierItemManager) { - this._appIndicatorKStatusNotifierItemManager = appIndicatorKStatusNotifierItemManager; - - this._settings = ExtensionUtils.getSettings(); - } - - /** - * This function creates a valid box order for the given box. - * This means it returns a box order for the box, where only roles are - * included, which have their associated indicator container already in some - * box of the Gnome Shell top bar. - * @param {string} box - The box to return the valid box order for. - * Must be one of the following values: - * - "left" - * - "center" - * - "right" - * @returns {string[]} - The valid box order. - */ - createValidBoxOrder(box) { - // Get a resolved box order. - let boxOrder = this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this._settings.get_strv(`${box}-box-order`)); - - // Get the indicator containers (of the items) currently present in the - // Gnome Shell top bar. - const boxIndicatorContainers = [ ]; - - const addIndicatorContainersOfBox = (panelBox) => { - for (const indicatorContainer of panelBox.get_children()) { - boxIndicatorContainers.push(indicatorContainer); - } - }; - - addIndicatorContainersOfBox(Main.panel._leftBox); - addIndicatorContainersOfBox(Main.panel._centerBox); - addIndicatorContainersOfBox(Main.panel._rightBox); - - // Create an indicator containers set from the indicator containers for - // fast easy access. - const boxIndicatorContainersSet = new Set(boxIndicatorContainers); - - // Go through the box order and only add items to the valid box order, - // where their indicator is present in the Gnome Shell top bar - // currently. - let validBoxOrder = [ ]; - for (const role of boxOrder) { - // Get the indicator container associated with the current role. - const associatedIndicatorContainer = Main.panel.statusArea[role]?.container; - - if (boxIndicatorContainersSet.has(associatedIndicatorContainer)) validBoxOrder.push(role); - } - - return validBoxOrder; - } - - /** - * This function creates a restricted valid box order for the given box. - * This means it returns a box order for the box, where only roles are - * included, which have their associated indicator container already in the - * specified box. - * @param {string} box - The box to return the valid box order for. - * Must be one of the following values: - * - "left" - * - "center" - * - "right" - * @returns {string[]} - The restricted valid box order. - */ - createRestrictedValidBoxOrder(box) { - // Get a resolved box order and get the indicator containers (of the - // items) which are currently present in the Gnome Shell top bar in the - // specified box. - let boxOrder = this._appIndicatorKStatusNotifierItemManager.createResolvedBoxOrder(this._settings.get_strv(`${box}-box-order`)); - let boxIndicatorContainers; - switch (box) { - case "left": - boxIndicatorContainers = Main.panel._leftBox.get_children(); - break; - case "center": - boxIndicatorContainers = Main.panel._centerBox.get_children(); - break; - case "right": - boxIndicatorContainers = Main.panel._rightBox.get_children(); - break; - } - - // Create an indicator containers set from the indicator containers for - // fast easy access. - const boxIndicatorContainersSet = new Set(boxIndicatorContainers); - - // Go through the box order and only add items to the restricted valid - // box order, where their indicator is present in the Gnome Shell top - // bar in the specified box currently. - let restrictedValidBoxOrder = [ ]; - for (const role of boxOrder) { - // Get the indicator container associated with the current role. - const associatedIndicatorContainer = Main.panel.statusArea[role]?.container; - - if (boxIndicatorContainersSet.has(associatedIndicatorContainer)) restrictedValidBoxOrder.push(role); - } - - return restrictedValidBoxOrder; - } -}; diff --git a/src/extensionModules/BoxOrderManager.ts b/src/extensionModules/BoxOrderManager.ts new file mode 100644 index 0000000..cfb4dbe --- /dev/null +++ b/src/extensionModules/BoxOrderManager.ts @@ -0,0 +1,388 @@ +"use strict"; + +import GObject from "gi://GObject"; +import St from "gi://St"; +import type Gio from "gi://Gio"; + +import * as Main from "resource:///org/gnome/shell/ui/main.js"; + +import type { CustomPanel } from "../extension.js" + +export type Box = "left" | "center" | "right"; +type Hide = "hide" | "show" | "default"; +/** + * A resolved box order item containing the items role, settings identifier and + * additional information. + */ +interface ResolvedBoxOrderItem { + settingsId: string // The settings identifier of the item. + role: string // The role of the item. + hide: Hide // Whether the item should be (forcefully) hidden, (forcefully) shown or just be left as is. +} + +/** + * This class provides an interfaces to the box orders stored in settings. + * It takes care of handling AppIndicator and Task Up UltraLite items and + * resolving from the internal item settings identifiers to roles. + * In the end this results in convenient functions, which are directly useful in + * other extension code. + */ +export default class BoxOrderManager extends GObject.Object { + static { + GObject.registerClass({ + Signals: { + "appIndicatorReady": {}, + }, + }, this); + } + + // Can't have type guarantees here, since this is working with types from + // the KStatusNotifier/AppIndicator extension. + #appIndicatorReadyHandlerIdMap: Map; + #appIndicatorItemSettingsIdToRolesMap: Map; + #taskUpUltraLiteItemRoles: string[]; + #settings: Gio.Settings; + + constructor(params = {}, settings: Gio.Settings) { + // @ts-ignore Params should be passed, see: https://gjs.guide/guides/gobject/subclassing.html#subclassing-gobject + super(params); + + this.#appIndicatorReadyHandlerIdMap = new Map(); + this.#appIndicatorItemSettingsIdToRolesMap = new Map(); + this.#taskUpUltraLiteItemRoles = []; + + this.#settings = settings; + } + + /** + * Gets a box order for the given top bar box from settings. + * @param {Box} box - The top bar box for which to get the box order. + * @returns {string[]} - The box order consisting of an array of item + * settings identifiers. + */ + #getBoxOrder(box: Box): string[] { + return this.#settings.get_strv(`${box}-box-order`); + } + + /** + * Save the given box order to settings, making sure to only save a changed + * box order, to avoid loops when listening on settings changes. + * @param {Box} box - The top bar box for which to save the box order. + * @param {string[]} boxOrder - The box order to save. Must be an array of + * item settings identifiers. + */ + #saveBoxOrder(box: Box, boxOrder: string[]): void { + const currentBoxOrder = this.#getBoxOrder(box); + + // Only save the given box order to settings, if it is different, to + // avoid loops when listening on settings changes. + if (JSON.stringify(boxOrder) !== JSON.stringify(currentBoxOrder)) { + this.#settings.set_strv(`${box}-box-order`, boxOrder); + } + } + + /** + * Handles an AppIndicator/KStatusNotifierItem item by deriving a settings + * identifier and then associating the role of the given item to the items + * settings identifier. + * It then returns the derived settings identifier. + * In the case, where the settings identifier can't be derived, because the + * application can't be determined, this method throws an error. However it + * then also makes sure that once the app indicators "ready" signal emits, + * this classes "appIndicatorReady" signal emits as well, such that it and + * other methods can be called again to properly handle the item. + * @param {St.Bin} indicatorContainer - The container of the indicator of the + * AppIndicator/KStatusNotifierItem item. + * @param {string} role - The role of the AppIndicator/KStatusNotifierItem + * item. + * @returns {string} The derived items settings identifier. + */ + #handleAppIndicatorItem(indicatorContainer: St.Bin, role: string): string { + // Since this is working with types from the + // AppIndicator/KStatusNotifierItem extension, we loose a bunch of type + // safety here. + // https://github.com/ubuntu/gnome-shell-extension-appindicator + const appIndicator = (indicatorContainer.get_child() as any)._indicator; + let application = appIndicator.id; + + if (!application && this.#appIndicatorReadyHandlerIdMap) { + const handlerId = appIndicator.connect("ready", () => { + this.emit("appIndicatorReady"); + appIndicator.disconnect(handlerId); + this.#appIndicatorReadyHandlerIdMap.delete(handlerId); + }); + this.#appIndicatorReadyHandlerIdMap.set(handlerId, appIndicator); + throw new Error("Application can't be determined."); + } + + // Since the Dropbox client appends its PID to the id, drop the PID and + // the hyphen before it. + if (application.startsWith("dropbox-client-")) { + application = "dropbox-client"; + } + + // Derive the items settings identifier from the application name. + const itemSettingsId = `appindicator-kstatusnotifieritem-${application}`; + + // Associate the role with the items settings identifier. + let roles = this.#appIndicatorItemSettingsIdToRolesMap.get(itemSettingsId); + if (roles) { + // If the settings identifier already has an array of associated + // roles, just add the role to it, if needed. + if (!roles.includes(role)) { + roles.push(role); + } + } else { + // Otherwise create a new array. + this.#appIndicatorItemSettingsIdToRolesMap.set(itemSettingsId, [role]); + } + + // Return the item settings identifier. + return itemSettingsId; + } + + /** + * Handles a Task Up UltraLite item by storing its role and returning the + * Task Up UltraLite settings identifier. + * This is needed since the Task Up UltraLite extension creates a bunch of + * top bar items as part of its functionality, so we want to group them + * under one identifier in the settings. + * https://extensions.gnome.org/extension/7700/task-up-ultralite/ + * @param {string} role - The role of the Task Up UltraLite item. + * @returns {string} The settings identifier to use. + */ + #handleTaskUpUltraLiteItem(role: string): string { + const roles = this.#taskUpUltraLiteItemRoles; + + if (!roles.includes(role)) { + roles.push(role); + } + + return "item-role-group-task-up-ultralite"; + } + + /** + * Gets a resolved box order for the given top bar box, where all + * AppIndicator and Task Up UltraLite items got resolved using their roles, + * meaning they might be present multiple times or not at all depending on + * the roles stored. + * The items of the box order also have additional information stored. + * @param {Box} box - The top bar box for which to get the resolved box order. + * @returns {ResolvedBoxOrderItem[]} - The resolved box order. + */ + #getResolvedBoxOrder(box: Box): ResolvedBoxOrderItem[] { + let boxOrder = this.#getBoxOrder(box); + + const itemsToHide = this.#settings.get_strv("hide"); + const itemsToShow = this.#settings.get_strv("show"); + + let resolvedBoxOrder = []; + for (const itemSettingsId of boxOrder) { + const resolvedBoxOrderItem = { + settingsId: itemSettingsId, + role: "", + hide: "", + }; + + // Set the hide state of the item. + if (itemsToHide.includes(resolvedBoxOrderItem.settingsId)) { + resolvedBoxOrderItem.hide = "hide"; + } else if (itemsToShow.includes(resolvedBoxOrderItem.settingsId)) { + resolvedBoxOrderItem.hide = "show"; + } else { + resolvedBoxOrderItem.hide = "default"; + } + + // If the items settings identifier doesn't indicate that the item + // is an AppIndicator/KStatusNotifierItem item or the Task Up + // UltraLite item role group, then its identifier is the role and it + // can just be added to the resolved box order. + if (!itemSettingsId.startsWith("appindicator-kstatusnotifieritem-") && + itemSettingsId !== "item-role-group-task-up-ultralite") { + resolvedBoxOrderItem.role = resolvedBoxOrderItem.settingsId; + resolvedBoxOrder.push(resolvedBoxOrderItem); + continue; + } + + // If the items settings identifier indicates otherwise, then handle + // the item specially. + + // Get the roles associated with the items settings id. + let roles: string[] = []; + if (itemSettingsId.startsWith("appindicator-kstatusnotifieritem-")) { + roles = this.#appIndicatorItemSettingsIdToRolesMap.get(resolvedBoxOrderItem.settingsId) ?? []; + } else if (itemSettingsId === "item-role-group-task-up-ultralite") { + roles = this.#taskUpUltraLiteItemRoles; + } + + // Create a new resolved box order item for each role and add it to + // the resolved box order. + for (const role of roles) { + const newResolvedBoxOrderItem = JSON.parse(JSON.stringify(resolvedBoxOrderItem)); + newResolvedBoxOrderItem.role = role; + resolvedBoxOrder.push(newResolvedBoxOrderItem); + } + } + + return resolvedBoxOrder; + } + + /** + * Disconnects all signals (and disables future signal connection). + * This is typically used before nulling an instance of this class to make + * sure all signals are disconnected. + */ + disconnectSignals(): void { + for (const [handlerId, appIndicator] of this.#appIndicatorReadyHandlerIdMap) { + if (handlerId && appIndicator?.signalHandlerIsConnected(handlerId)) { + appIndicator.disconnect(handlerId); + } + } + // @ts-ignore + this.#appIndicatorReadyHandlerIdMap = null; + } + + /** + * Gets a valid box order for the given top bar box, where all AppIndicator + * and Task Up UltraLite items got resolved and where only items are + * included, which are in some GNOME Shell top bar box. + * The items of the box order also have additional information stored. + * @param {Box} box - The top bar box to return the valid box order for. + * @returns {ResolvedBoxOrderItem[]} - The valid box order. + */ + getValidBoxOrder(box: Box): ResolvedBoxOrderItem[] { + // Get a resolved box order. + let resolvedBoxOrder = this.#getResolvedBoxOrder(box); + + // Get the indicator containers (of the items) currently present in the + // GNOME Shell top bar. + // They should be St.Bins (see link), so ensure that using a filter. + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/48.2/js/ui/panelMenu.js?ref_type=tags#L21 + const indicatorContainers = new Set([ + (Main.panel as CustomPanel)._leftBox.get_children(), + (Main.panel as CustomPanel)._centerBox.get_children(), + (Main.panel as CustomPanel)._rightBox.get_children(), + ].flat().filter(ic => ic instanceof St.Bin)); + + // Go through the resolved box order and only add items to the valid box + // order, where their indicator is currently present in the GNOME Shell + // top bar. + let validBoxOrder: ResolvedBoxOrderItem[] = []; + for (const item of resolvedBoxOrder) { + const associatedIndicatorContainer = (Main.panel.statusArea as any)[item.role]?.container; + if (!(associatedIndicatorContainer instanceof St.Bin)) { + // TODO: maybe add logging + continue; + } + + if (indicatorContainers.has(associatedIndicatorContainer)) { + validBoxOrder.push(item); + } + } + + return validBoxOrder; + } + + /** + * This method saves all new items currently present in the GNOME Shell top + * bar to the settings. + */ + saveNewTopBarItems(): void { + // Only run, when the session mode is "user" or the parent session mode + // is "user". + if (Main.sessionMode.currentMode !== "user" && Main.sessionMode.parentMode !== "user") { + return; + } + + // Get the box orders. + const boxOrders = { + left: this.#getBoxOrder("left"), + center: this.#getBoxOrder("center"), + right: this.#getBoxOrder("right"), + }; + + // Get roles (of items) currently present in the GNOME Shell top bar and + // index them using their associated indicator container. + let indicatorContainerRoleMap = new Map(); + for (const role in (Main.panel.statusArea as any)) { + const associatedIndicatorContainer = (Main.panel.statusArea as any)[role]?.container; + if (!(associatedIndicatorContainer instanceof St.Bin)) { + // TODO: maybe add logging + continue; + } + indicatorContainerRoleMap.set(associatedIndicatorContainer, role); + } + + // Get the indicator containers (of the items) currently present in the + // GNOME Shell top bar boxes. + // They should be St.Bins (see link), so ensure that using a filter. + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/48.2/js/ui/panelMenu.js?ref_type=tags#L21 + const boxIndicatorContainers = { + left: (Main.panel as CustomPanel)._leftBox.get_children().filter(ic => ic instanceof St.Bin), + center: (Main.panel as CustomPanel)._centerBox.get_children().filter(ic => ic instanceof St.Bin), + // Reverse this array, since the items in the left and center box + // are logically LTR, while the items in the right box are RTL. + right: (Main.panel as CustomPanel)._rightBox.get_children().filter(ic => ic instanceof St.Bin).reverse(), + }; + + // This function goes through the indicator containers of the given box + // and adds new item settings identifiers to the given box order. + const addNewItemSettingsIdsToBoxOrder = (indicatorContainers: St.Bin[], boxOrder: string[], box: Box) => { + for (const indicatorContainer of indicatorContainers) { + // First get the role associated with the current indicator + // container. + let role = indicatorContainerRoleMap.get(indicatorContainer); + if (!role) { + continue; + } + + // Then get a settings identifier for the item. + let itemSettingsId; + if (role.startsWith("appindicator-")) { + // If the role indicates that the item is an + // AppIndicator/KStatusNotifierItem item, then handle it + // differently. + try { + itemSettingsId = this.#handleAppIndicatorItem(indicatorContainer, role); + } catch (e) { + if (!(e instanceof Error)) { + throw(e); + } + if (e.message !== "Application can't be determined.") { + throw(e); + } + continue; + } + } else if (role.startsWith("task-button-")) { + // If the role indicates that the item is a Task Up + // UltraLite item, then handle it differently. + itemSettingsId = this.#handleTaskUpUltraLiteItem(role); + } else { // Otherwise just use the role as the settings identifier. + itemSettingsId = role; + } + + // Add the items settings identifier to the box order, if it + // isn't in in one already. + if (!boxOrders.left.includes(itemSettingsId) + && !boxOrders.center.includes(itemSettingsId) + && !boxOrders.right.includes(itemSettingsId)) { + if (box === "right") { + // Add the items to the beginning for this array, since + // its RTL. + boxOrder.unshift(itemSettingsId); + } else { + boxOrder.push(itemSettingsId); + } + } + } + }; + + addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.left, boxOrders.left, "left"); + addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.center, boxOrders.center, "center"); + addNewItemSettingsIdsToBoxOrder(boxIndicatorContainers.right, boxOrders.right, "right"); + + this.#saveBoxOrder("left", boxOrders.left); + this.#saveBoxOrder("center", boxOrders.center); + this.#saveBoxOrder("right", boxOrders.right); + } +} diff --git a/src/metadata.json b/src/metadata.json index 9a846c6..11b0cce 100644 --- a/src/metadata.json +++ b/src/metadata.json @@ -2,8 +2,8 @@ "uuid": "top-bar-organizer@julian.gse.jsts.xyz", "name": "Top Bar Organizer", "description": "Organize the items of the top (menu)bar.", - "version": 3, - "shell-version": [ "40" ], + "version": 15, + "shell-version": [ "45", "46", "47", "48", "49" ], "settings-schema": "org.gnome.shell.extensions.top-bar-organizer", - "url": "https://gitlab.gnome.org/julianschacher/top-bar-organizer" + "url": "https://gitlab.gnome.org/june/top-bar-organizer" } diff --git a/src/prefs-box-order-item-row.ui b/src/prefs-box-order-item-row.ui deleted file mode 100644 index 2c8e424..0000000 --- a/src/prefs-box-order-item-row.ui +++ /dev/null @@ -1,48 +0,0 @@ - - - - - diff --git a/src/prefs-box-order-list-box.ui b/src/prefs-box-order-list-box.ui deleted file mode 100644 index 7bd6ad6..0000000 --- a/src/prefs-box-order-list-box.ui +++ /dev/null @@ -1,25 +0,0 @@ - - - - - diff --git a/src/prefs-widget.ui b/src/prefs-widget.ui deleted file mode 100644 index d662a65..0000000 --- a/src/prefs-widget.ui +++ /dev/null @@ -1,87 +0,0 @@ - - - - - diff --git a/src/prefs.js b/src/prefs.js deleted file mode 100644 index baf1ab7..0000000 --- a/src/prefs.js +++ /dev/null @@ -1,122 +0,0 @@ -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported buildPrefsWidget, init */ -"use strict"; - -const Gtk = imports.gi.Gtk; -const GObject = imports.gi.GObject; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const PrefsBoxOrderListBox = Me.imports.prefsModules.PrefsBoxOrderListBox; -const PrefsBoxOrderListEmptyPlaceholder = Me.imports.prefsModules.PrefsBoxOrderListEmptyPlaceholder; -const PrefsBoxOrderItemRow = Me.imports.prefsModules.PrefsBoxOrderItemRow; -const ScrollManager = Me.imports.prefsModules.ScrollManager; - -var PrefsWidget = GObject.registerClass({ - GTypeName: "PrefsWidget", - Template: Me.dir.get_child("prefs-widget.ui").get_uri(), - InternalChildren: [ - "left-box", - "center-box", - "right-box" - ] -}, class PrefsWidget extends Gtk.ScrolledWindow { - _init(params = {}) { - super._init(params); - - this._settings = ExtensionUtils.getSettings(); - - // Never show a horizontal scrollbar. - // Achieved by setting the hscrollbar_policy to 2, while setting the - // vscrollbar_policy to 1 (the default value). - this.set_policy(2, 1); - - // Set the default size of the preferences window to a sensible value on - // realize. - this.connect("realize", () => { - // Get the window. - const window = this.get_root(); - - // Use 500 and 750 for the default size. - // Those are the same values the Just Perfection Gnome Shell - // extension uses. - // It seems like those values only get used the first time the - // preferences window gets opened in a session. On all consecutive - // opens, the window is a bit larger than those values. - window.default_width = 500; - window.default_height = 750; - }); - - // Scroll up or down, when a Drag-and-Drop operation is in progress and - // the user has their cursor either in the upper or lower 10% of this - // widget respectively. - this._scrollManager = new ScrollManager.ScrollManager(this); - let controller = new Gtk.DropControllerMotion(); - controller.connect("motion", (_, x, y) => { - // If the pointer is currently in the upper ten percent of this - // widget, then scroll up. - if (y <= this.get_allocated_height() * 0.1) this._scrollManager.startScrollUp(); - // If the pointer is currently in the lower ten percent of this - // widget, then scroll down. - else if (y >= this.get_allocated_height() * 0.9) this._scrollManager.startScrollDown(); - // Otherwise stop scrolling. - else this._scrollManager.stopScrollAll(); - }); - controller.connect("leave", () => { - // Stop scrolling on leave. - this._scrollManager.stopScrollAll(); - }); - this.add_controller(controller); - - // Add custom GTKListBoxes (PrefsBoxOrderListBoxes). - this._left_box_order = new PrefsBoxOrderListBox.PrefsBoxOrderListBox({}, "left-box-order"); - this._left_box.append(this._left_box_order); - this._center_box_order = new PrefsBoxOrderListBox.PrefsBoxOrderListBox({}, "center-box-order"); - this._center_box.append(this._center_box_order); - this._right_box_order = new PrefsBoxOrderListBox.PrefsBoxOrderListBox({}, "right-box-order"); - this._right_box.append(this._right_box_order); - - // Initialize the given `gtkListBox`. - const initializeGtkListBox = (boxOrder, gtkListBox) => { - // Add the items of the given configured box order as - // GtkListBoxRows. - for (const item of boxOrder) { - const listBoxRow = new PrefsBoxOrderItemRow.PrefsBoxOrderItemRow({}, this._scrollManager, item); - gtkListBox.append(listBoxRow); - } - - // Add a placeholder widget for the case, where `gtkListBox` doesn't - // have any GtkListBoxRows. - gtkListBox.set_placeholder(new PrefsBoxOrderListEmptyPlaceholder.PrefsBoxOrderListEmptyPlaceholder()); - }; - - initializeGtkListBox(this._settings.get_strv("left-box-order"), this._left_box_order); - initializeGtkListBox(this._settings.get_strv("center-box-order"), this._center_box_order); - initializeGtkListBox(this._settings.get_strv("right-box-order"), this._right_box_order); - } -}); - -function buildPrefsWidget() { - return new PrefsWidget(); -} - -function init() { -} diff --git a/src/prefs.ts b/src/prefs.ts new file mode 100644 index 0000000..76718ba --- /dev/null +++ b/src/prefs.ts @@ -0,0 +1,32 @@ +"use strict"; + +import Gtk from "gi://Gtk"; +import Gdk from "gi://Gdk"; + +import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js"; + +import PrefsPage from "./prefsModules/PrefsPage.js"; + +export default class TopBarOrganizerPreferences extends ExtensionPreferences { + getPreferencesWidget() { + const provider = new Gtk.CssProvider(); + provider.load_from_path(this.metadata.dir.get_path() + "/css/prefs.css"); + const defaultGdkDisplay = Gdk.Display.get_default(); + Gtk.StyleContext.add_provider_for_display( + (defaultGdkDisplay as Gdk.Display), + provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ); + + const prefsPage = new PrefsPage(); + + prefsPage.connect("destroy", () => { + Gtk.StyleContext.remove_provider_for_display( + (defaultGdkDisplay as Gdk.Display), + provider + ); + }); + + return prefsPage; + } +} diff --git a/src/prefsModules/PrefsBoxOrderItemOptionsDialog.ts b/src/prefsModules/PrefsBoxOrderItemOptionsDialog.ts new file mode 100644 index 0000000..736a773 --- /dev/null +++ b/src/prefsModules/PrefsBoxOrderItemOptionsDialog.ts @@ -0,0 +1,69 @@ +"use strict"; + +import GObject from "gi://GObject"; +import Adw from "gi://Adw"; +import GLib from "gi://GLib"; +import type Gio from "gi://Gio"; +import type Gtk from "gi://Gtk"; + +import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js"; + +export default class PrefsBoxOrderItemOptionsDialog extends Adw.Dialog { + static { + GObject.registerClass({ + GTypeName: "PrefsBoxOrderItemOptionsDialog", + Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-item-options-dialog.ui", GLib.UriFlags.NONE), + InternalChildren: [ + "visibility-row", + ], + }, this); + } + + declare _visibility_row: Adw.ComboRow; + #settings: Gio.Settings; + item: string; + + constructor(params = {}, item: string) { + super(params); + + // Associate `this` with an item. + this.item = item; + // Load the settings. + this.#settings = ExtensionPreferences.lookupByURL(import.meta.url)!.getSettings(); + + // Set the selected visibility row choice to the settings value. + const itemsToHide = new Set(this.#settings.get_strv("hide")); + const itemsToShow = new Set(this.#settings.get_strv("show")); + if (itemsToHide.has(this.item)) { + this._visibility_row.set_selected(1); + } else if (itemsToShow.has(this.item)) { + this._visibility_row.set_selected(2); + } else { + this._visibility_row.set_selected(0); + } + } + + onVisibilityRowSelectionChanged(): void { + const visibility = (this._visibility_row.get_selected_item() as Gtk.StringObject).get_string(); + const itemsToHide = new Set(this.#settings.get_strv("hide")); + const itemsToShow = new Set(this.#settings.get_strv("show")); + + switch (visibility) { + case "Forcefully Hide": + itemsToHide.add(this.item) + itemsToShow.delete(this.item); + break; + case "Forcefully Show": + itemsToHide.delete(this.item) + itemsToShow.add(this.item); + break; + case "Default": + itemsToHide.delete(this.item) + itemsToShow.delete(this.item); + break; + } + + this.#settings.set_strv("hide", Array.from(itemsToHide)); + this.#settings.set_strv("show", Array.from(itemsToShow)); + } +} diff --git a/src/prefsModules/PrefsBoxOrderItemRow.js b/src/prefsModules/PrefsBoxOrderItemRow.js deleted file mode 100644 index b62ab5c..0000000 --- a/src/prefsModules/PrefsBoxOrderItemRow.js +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported PrefsBoxOrderItemRow */ -"use strict"; - -const Gtk = imports.gi.Gtk; -const Gdk = imports.gi.Gdk; -const Gio = imports.gi.Gio; -const GObject = imports.gi.GObject; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -var PrefsBoxOrderItemRow = GObject.registerClass({ - GTypeName: "PrefsBoxOrderItemRow", - Template: Me.dir.get_child("prefs-box-order-item-row.ui").get_uri(), - InternalChildren: [ - "item-name-display-label", - "menu-button" - ] -}, class PrefsBoxOrderItemRow extends Gtk.ListBoxRow { - _init(params = {}, scrollManager, item) { - super._init(params); - - this._associateItem(item); - this._configureMenu(); - - // Make `this` draggable by creating a drag source and adding it to - // `this`. - let dragSource = new Gtk.DragSource(); - dragSource.set_actions(Gdk.DragAction.MOVE); - dragSource.connect("prepare", () => { - return Gdk.ContentProvider.new_for_value(this); - }); - // Stop all scrolling, which is due to this DND operation. - dragSource.connect("drag-end", () => { - scrollManager.stopScrollAll(); - }); - this.add_controller(dragSource); - - /// Make `this` accept drops by creating a drop target and adding it to - /// `this`. - let dropTarget = new Gtk.DropTarget(); - dropTarget.set_gtypes([this.constructor.$gtype]); - dropTarget.set_actions(Gdk.DragAction.MOVE); - // Handle a new drop on `this` properly. - // `value` is the thing getting dropped. - dropTarget.connect("drop", (target, value) => { - // If `this` got dropped onto itself, do nothing. - if (value === this) { - return; - } - - // Get the GtkListBoxes of `this` and the drop value. - const ownListBox = this.get_parent(); - const valueListBox = value.get_parent(); - - // Get the position of `this` and the drop value. - const ownPosition = this.get_index(); - const valuePosition = value.get_index(); - - // Remove the drop value from its list box. - valueListBox.remove(value); - - // Since an element got potentially removed from the list of `this`, - // get the position of `this` again. - const updatedOwnPosition = this.get_index(); - - if (ownListBox !== valueListBox) { - // First handle the case where `this` and the drop value are in - // different list boxes. - if ((ownListBox.boxOrder === "right-box-order" && valueListBox.boxOrder === "left-box-order") - || (ownListBox.boxOrder === "right-box-order" && valueListBox.boxOrder === "center-box-order") - || (ownListBox.boxOrder === "center-box-order" && valueListBox.boxOrder === "left-box-order")) { - // If the list box of the drop value comes before the list - // box of `this`, add the drop value after `this`. - ownListBox.insert(value, updatedOwnPosition + 1); - } else { - // Otherwise, add the drop value where `this` currently is. - ownListBox.insert(value, updatedOwnPosition); - } - } else { - if (valuePosition < ownPosition) { - // If the drop value was before `this`, add the drop value - // after `this`. - ownListBox.insert(value, updatedOwnPosition + 1); - } else { - // Otherwise, add the drop value where `this` currently is. - ownListBox.insert(value, updatedOwnPosition); - } - } - - /// Finally save the box order(/s) to settings. - ownListBox.saveBoxOrderToSettings(); - // If the list boxes of `this` and the drop value were different, - // save an updated box order for the list were the drop value was in - // as well. - if (ownListBox !== valueListBox) valueListBox.saveBoxOrderToSettings(); - }); - this.add_controller(dropTarget); - } - - /** - * Associate `this` with an item. - * @param {String} item - */ - _associateItem(item) { - this.item = item; - - // Set `this._item_name_display_label` to something nicer, if the - // associated item is an AppIndicator/KStatusNotifierItem item. - if (item.startsWith("appindicator-kstatusnotifieritem-")) this._item_name_display_label.set_label(item.replace("appindicator-kstatusnotifieritem-", "")); - // Otherwise just set it to `item`. - else this._item_name_display_label.set_label(item); - } - - /** - * Configure the menu. - */ - _configureMenu() { - let menu = new Gio.Menu(); - menu.append("Forget", `prefsBoxOrderItemRow-${this.item}.forget`); - this._menu_button.set_menu_model(menu); - - const forgetAction = new Gio.SimpleAction({ name: "forget" }); - forgetAction.connect("activate", () => { - const parentListBox = this.get_parent(); - parentListBox.remove(this); - parentListBox.saveBoxOrderToSettings(); - }); - - const actionGroup = new Gio.SimpleActionGroup(); - actionGroup.add_action(forgetAction); - this.insert_action_group(`prefsBoxOrderItemRow-${this.item}`, actionGroup); - } -}); diff --git a/src/prefsModules/PrefsBoxOrderItemRow.ts b/src/prefsModules/PrefsBoxOrderItemRow.ts new file mode 100644 index 0000000..3da4d6b --- /dev/null +++ b/src/prefsModules/PrefsBoxOrderItemRow.ts @@ -0,0 +1,161 @@ +"use strict"; + +import Gtk from "gi://Gtk"; +import Gdk from "gi://Gdk"; +import GObject from "gi://GObject"; +import Adw from "gi://Adw"; +import GLib from "gi://GLib"; + +import PrefsBoxOrderItemOptionsDialog from "./PrefsBoxOrderItemOptionsDialog.js"; +import type PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js"; + +export default class PrefsBoxOrderItemRow extends Adw.ActionRow { + static { + GObject.registerClass({ + GTypeName: "PrefsBoxOrderItemRow", + Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-item-row.ui", GLib.UriFlags.NONE), + Signals: { + "move": { + param_types: [GObject.TYPE_STRING], + }, + }, + }, this); + this.install_action("row.forget", null, (self, _actionName, _param) => { + const parentListBox = self.get_parent() as PrefsBoxOrderListBox; + parentListBox.removeRow(self as PrefsBoxOrderItemRow); + parentListBox.saveBoxOrderToSettings(); + parentListBox.determineRowMoveActionEnable(); + }); + this.install_action("row.options", null, (self, _actionName, _param) => { + const itemOptionsDialog = new PrefsBoxOrderItemOptionsDialog({ + // Get the title from self as the constructor of + // PrefsBoxOrderItemRow already processes the item name into a + // nice title. + title: (self as PrefsBoxOrderItemRow).get_title() + }, (self as PrefsBoxOrderItemRow).item); + itemOptionsDialog.present(self); + }); + this.install_action("row.move-up", null, (self, _actionName, _param) => self.emit("move", "up")); + this.install_action("row.move-down", null, (self, _actionName, _param) => self.emit("move", "down")); + } + + item: string; + #drag_starting_point_x?: number; + #drag_starting_point_y?: number; + + constructor(params = {}, item: string) { + super(params); + + // Associate `this` with an item. + this.item = item; + if (this.item.startsWith("appindicator-kstatusnotifieritem-")) { + // Set the title to something nicer, if the associated item is an + // AppIndicator/KStatusNotifierItem item. + this.set_title(this.item.replace("appindicator-kstatusnotifieritem-", "")); + } else if (this.item === "item-role-group-task-up-ultralite") { + // Set the title to something nicer, if the item in question is the + // Task Up UltraLite item role group. + this.set_title("Task Up UltraLite Items"); + } else { + // Otherwise just set it to `item`. + this.set_title(this.item); + } + } + + onDragPrepare(_source: Gtk.DragSource, x: number, y: number): Gdk.ContentProvider { + const value = new GObject.Value(); + value.init(PrefsBoxOrderItemRow.$gtype); + value.set_object(this); + + this.#drag_starting_point_x = x; + this.#drag_starting_point_y = y; + return Gdk.ContentProvider.new_for_value(value); + } + + onDragBegin(_source: Gtk.DragSource, drag: Gdk.Drag): void { + let dragWidget = new Gtk.ListBox(); + let allocation = this.get_allocation(); + dragWidget.set_size_request(allocation.width, allocation.height); + + let dragPrefsBoxOrderItemRow = new PrefsBoxOrderItemRow({}, this.item); + dragWidget.append(dragPrefsBoxOrderItemRow); + dragWidget.drag_highlight_row(dragPrefsBoxOrderItemRow); + + let currentDragIcon = Gtk.DragIcon.get_for_drag(drag); + currentDragIcon.set_child(dragWidget); + // Even tho this should always be the case, ensure the values for the hotspot aren't undefined. + if (typeof this.#drag_starting_point_x !== "undefined" && + typeof this.#drag_starting_point_y !== "undefined") { + drag.set_hotspot(this.#drag_starting_point_x, this.#drag_starting_point_y); + } + } + + // Handle a new drop on `this` properly. + // `value` is the thing getting dropped. + onDrop(_target: Gtk.DropTarget, value: any, _x: number, _y: number): boolean { + // According to the type annotations of Gtk.DropTarget, value is of type + // GObject.Value, so ensure the one we work with is of type + // PrefsBoxOrderItemRow. + if (!(value instanceof PrefsBoxOrderItemRow)) { + // TODO: maybe add logging + return false; + } + + // If `this` got dropped onto itself, do nothing. + if (value === this) { + return false; + } + + // Get the GtkListBoxes of `this` and the drop value. + const ownListBox = this.get_parent() as PrefsBoxOrderListBox; + const valueListBox = value.get_parent() as PrefsBoxOrderListBox; + + // Get the position of `this` and the drop value. + const ownPosition = this.get_index(); + const valuePosition = value.get_index(); + + // Remove the drop value from its list box. + valueListBox.removeRow(value); + + // Since an element got potentially removed from the list of `this`, + // get the position of `this` again. + const updatedOwnPosition = this.get_index(); + + if (ownListBox !== valueListBox) { + // First handle the case where `this` and the drop value are in + // different list boxes. + if ((ownListBox.boxOrder === "right-box-order" && valueListBox.boxOrder === "left-box-order") + || (ownListBox.boxOrder === "right-box-order" && valueListBox.boxOrder === "center-box-order") + || (ownListBox.boxOrder === "center-box-order" && valueListBox.boxOrder === "left-box-order")) { + // If the list box of the drop value comes before the list + // box of `this`, add the drop value after `this`. + ownListBox.insertRow(value, updatedOwnPosition + 1); + } else { + // Otherwise, add the drop value where `this` currently is. + ownListBox.insertRow(value, updatedOwnPosition); + } + } else { + if (valuePosition < ownPosition) { + // If the drop value was before `this`, add the drop value + // after `this`. + ownListBox.insertRow(value, updatedOwnPosition + 1); + } else { + // Otherwise, add the drop value where `this` currently is. + ownListBox.insertRow(value, updatedOwnPosition); + } + } + + /// Finally save the box order(/s) to settings and make sure move + /// actions are correctly enabled/disabled. + ownListBox.saveBoxOrderToSettings(); + ownListBox.determineRowMoveActionEnable(); + // If the list boxes of `this` and the drop value were different, handle + // the former list box of the drop value as well. + if (ownListBox !== valueListBox) { + valueListBox.saveBoxOrderToSettings(); + valueListBox.determineRowMoveActionEnable(); + } + + return true; + } +} diff --git a/src/prefsModules/PrefsBoxOrderListBox.js b/src/prefsModules/PrefsBoxOrderListBox.js deleted file mode 100644 index 3f6ed91..0000000 --- a/src/prefsModules/PrefsBoxOrderListBox.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported PrefsBoxOrderListBox */ -"use strict"; - -const Gtk = imports.gi.Gtk; -const GObject = imports.gi.GObject; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -var PrefsBoxOrderListBox = GObject.registerClass({ - GTypeName: "PrefsBoxOrderListBox", - Template: Me.dir.get_child("prefs-box-order-list-box.ui").get_uri() -}, class PrefsBoxOrderListBox extends Gtk.ListBox { - /** - * @param {Object} params - * @param {String} boxOrder - The box order this PrefsBoxOrderListBox is - * associated with. - */ - _init(params = {}, boxOrder) { - super._init(params); - - this._settings = ExtensionUtils.getSettings(); - - this.boxOrder = boxOrder; - } - - /** - * Saves the box order represented by `this` (and its - * `PrefsBoxOrderItemRows`) to settings. - */ - saveBoxOrderToSettings() { - let currentBoxOrder = [ ]; - for (let potentialPrefsBoxOrderItemRow of this) { - // Only process PrefsBoxOrderItemRows. - if (potentialPrefsBoxOrderItemRow.constructor.$gtype.name !== "PrefsBoxOrderItemRow") continue; - - const item = potentialPrefsBoxOrderItemRow.item; - currentBoxOrder.push(item); - } - this._settings.set_strv(this.boxOrder, currentBoxOrder); - } -}); diff --git a/src/prefsModules/PrefsBoxOrderListBox.ts b/src/prefsModules/PrefsBoxOrderListBox.ts new file mode 100644 index 0000000..f2b5c98 --- /dev/null +++ b/src/prefsModules/PrefsBoxOrderListBox.ts @@ -0,0 +1,152 @@ +"use strict"; + +import Gtk from "gi://Gtk"; +import GObject from "gi://GObject"; +import GLib from "gi://GLib"; +import type Gio from "gi://Gio"; + +import { ExtensionPreferences } from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js"; + +import PrefsBoxOrderItemRow from "./PrefsBoxOrderItemRow.js"; +import PrefsBoxOrderListEmptyPlaceholder from "./PrefsBoxOrderListEmptyPlaceholder.js"; + +export default class PrefsBoxOrderListBox extends Gtk.ListBox { + static { + GObject.registerClass({ + GTypeName: "PrefsBoxOrderListBox", + Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-list-box.ui", GLib.UriFlags.NONE), + Properties: { + BoxOrder: GObject.ParamSpec.string( + "box-order", + "Box Order", + "The box order this PrefsBoxOrderListBox is associated with.", + GObject.ParamFlags.READWRITE, + "" + ), + }, + Signals: { + "row-move": { + param_types: [PrefsBoxOrderItemRow.$gtype, GObject.TYPE_STRING], + }, + }, + }, this); + } + + _boxOrder!: string; + #settings: Gio.Settings; + #rowSignalHandlerIds = new Map(); + + /** + * @param {Object} params + */ + constructor(params = {}) { + super(params); + + // Load the settings. + this.#settings = ExtensionPreferences.lookupByURL(import.meta.url)!.getSettings(); + + // Add a placeholder widget for the case, where no GtkListBoxRows are + // present. + this.set_placeholder(new PrefsBoxOrderListEmptyPlaceholder()); + } + + get boxOrder(): string { + return this._boxOrder; + } + + set boxOrder(value: string) { + this._boxOrder = value; + + // Get the actual box order for the given box order name from settings. + const boxOrder = this.#settings.get_strv(this._boxOrder); + // Populate this GtkListBox with GtkListBoxRows for the items of the + // given configured box order. + for (const item of boxOrder) { + const row = new PrefsBoxOrderItemRow({}, item); + this.insertRow(row, -1); + } + + this.determineRowMoveActionEnable(); + this.notify("box-order"); + } + + /** + * Inserts the given PrefsBoxOrderItemRow to this list box at the given + * position. + * Also handles stuff like connecting signals. + */ + insertRow(row: PrefsBoxOrderItemRow, position: number): void { + this.insert(row, position); + + const signalHandlerIds: number[] = []; + signalHandlerIds.push(row.connect("move", (row, direction) => { + this.emit("row-move", row, direction); + })); + + this.#rowSignalHandlerIds.set(row, signalHandlerIds); + } + + /** + * Removes the given PrefsBoxOrderItemRow from this list box. + * Also handles stuff like disconnecting signals. + */ + removeRow(row: PrefsBoxOrderItemRow): void { + const signalHandlerIds = this.#rowSignalHandlerIds.get(row) ?? []; + + for (const id of signalHandlerIds) { + row.disconnect(id); + } + + this.remove(row); + } + + /** + * Saves the box order represented by `this` (and its + * `PrefsBoxOrderItemRows`) to settings. + */ + saveBoxOrderToSettings(): void { + let currentBoxOrder: string[] = []; + for (let potentialPrefsBoxOrderItemRow of this) { + // Only process PrefsBoxOrderItemRows. + if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) { + continue; + } + + const item = potentialPrefsBoxOrderItemRow.item; + currentBoxOrder.push(item); + } + this.#settings.set_strv(this.boxOrder, currentBoxOrder); + } + + /** + * Determines whether or not each move action of each PrefsBoxOrderItemRow + * should be enabled or disabled. + */ + determineRowMoveActionEnable(): void { + for (let potentialPrefsBoxOrderItemRow of this) { + // Only process PrefsBoxOrderItemRows. + if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) { + continue; + } + + const row = potentialPrefsBoxOrderItemRow; + + // If the current row is the topmost row in the topmost list box, + // then disable the move-up action. + if (row.get_index() === 0 && this.boxOrder === "left-box-order") { + row.action_set_enabled("row.move-up", false); + } else { // Else enable it. + row.action_set_enabled("row.move-up", true); + } + + // If the current row is the bottommost row in the bottommost list + // box, then disable the move-down action. + const rowNextSibling = row.get_next_sibling(); + if ((rowNextSibling instanceof PrefsBoxOrderListEmptyPlaceholder || rowNextSibling === null) && this.boxOrder === "right-box-order") { + row.action_set_enabled("row.move-down", false); + } else { // Else enable it. + row.action_set_enabled("row.move-down", true); + } + } + } +} diff --git a/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.js b/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.js deleted file mode 100644 index f1cde8f..0000000 --- a/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.js +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported PrefsBoxOrderListEmptyPlaceholder */ -"use strict"; - -const Gtk = imports.gi.Gtk; -const Gdk = imports.gi.Gdk; -const GObject = imports.gi.GObject; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -var PrefsBoxOrderListEmptyPlaceholder = GObject.registerClass({ - GTypeName: "PrefsBoxOrderListEmptyPlaceholder", - Template: Me.dir.get_child("prefs-box-order-list-empty-placeholder.ui").get_uri() -}, class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box { - _init(params = {}) { - super._init(params); - - /// Make `this` accept drops by creating a drop target and adding it to - /// `this`. - let dropTarget = new Gtk.DropTarget(); - dropTarget.set_gtypes([GObject.type_from_name("PrefsBoxOrderItemRow")]); - dropTarget.set_actions(Gdk.DragAction.MOVE); - // Handle a new drop on `this` properly. - // `value` is the thing getting dropped. - dropTarget.connect("drop", (target, value) => { - // Get the GtkListBoxes of `this` and the drop value. - const ownListBox = this.get_parent(); - const valueListBox = value.get_parent(); - - // Remove the drop value from its list box. - valueListBox.remove(value); - - // Insert the drop value into the list box of `this`. - ownListBox.insert(value, 0); - - /// Finally save the box orders to settings. - const settings = ExtensionUtils.getSettings(); - - settings.set_strv(ownListBox.boxOrder, [value.item]); - - let updatedBoxOrder = [ ]; - for (let potentialListBoxRow of valueListBox) { - // Only process PrefsBoxOrderItemRows. - if (potentialListBoxRow.constructor.$gtype.name !== "PrefsBoxOrderItemRow") { - continue; - } - - const item = potentialListBoxRow.item; - updatedBoxOrder.push(item); - } - settings.set_strv(valueListBox.boxOrder, updatedBoxOrder); - }); - this.add_controller(dropTarget); - } -}); diff --git a/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.ts b/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.ts new file mode 100644 index 0000000..dae599b --- /dev/null +++ b/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.ts @@ -0,0 +1,48 @@ +"use strict"; + +import Gtk from "gi://Gtk"; +import GObject from "gi://GObject"; +import GLib from "gi://GLib"; + +import PrefsBoxOrderItemRow from "./PrefsBoxOrderItemRow.js"; +import type PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js"; + +export default class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box { + static { + GObject.registerClass({ + GTypeName: "PrefsBoxOrderListEmptyPlaceholder", + Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-box-order-list-empty-placeholder.ui", GLib.UriFlags.NONE), + }, this); + } + + // Handle a new drop on `this` properly. + // `value` is the thing getting dropped. + onDrop(_target: Gtk.DropTarget, value: any, _x: number, _y: number): boolean { + // According to the type annotations of Gtk.DropTarget, value is of type + // GObject.Value, so ensure the one we work with is of type + // PrefsBoxOrderItemRow. + if (!(value instanceof PrefsBoxOrderItemRow)) { + // TODO: maybe add logging + return false; + } + + // Get the GtkListBoxes of `this` and the drop value. + const ownListBox = this.get_parent() as PrefsBoxOrderListBox; + const valueListBox = value.get_parent() as PrefsBoxOrderListBox; + + // Remove the drop value from its list box. + valueListBox.removeRow(value); + + // Insert the drop value into the list box of `this`. + ownListBox.insertRow(value, 0); + + /// Finally save the box orders to settings and make sure move actions + /// are correctly enabled/disabled. + ownListBox.saveBoxOrderToSettings(); + ownListBox.determineRowMoveActionEnable(); + valueListBox.saveBoxOrderToSettings(); + valueListBox.determineRowMoveActionEnable(); + + return true; + } +} diff --git a/src/prefsModules/PrefsPage.ts b/src/prefsModules/PrefsPage.ts new file mode 100644 index 0000000..6c22d8f --- /dev/null +++ b/src/prefsModules/PrefsPage.ts @@ -0,0 +1,196 @@ +"use strict"; + +import Gdk from "gi://Gdk"; +import Gtk from "gi://Gtk"; +import GObject from "gi://GObject"; +import Adw from "gi://Adw"; +import GLib from "gi://GLib"; + +import ScrollManager from "./ScrollManager.js"; +import PrefsBoxOrderListEmptyPlaceholder from "./PrefsBoxOrderListEmptyPlaceholder.js"; +import type PrefsBoxOrderItemRow from "./PrefsBoxOrderItemRow.js"; + +// Imports to make UI file work. +// eslint-disable-next-line +import PrefsBoxOrderListBox from "./PrefsBoxOrderListBox.js"; + +export default class PrefsPage extends Adw.PreferencesPage { + static { + GObject.registerClass({ + GTypeName: "PrefsPage", + Template: GLib.uri_resolve_relative(import.meta.url, "../ui/prefs-page.ui", GLib.UriFlags.NONE), + InternalChildren: [ + "left-box-order-list-box", + "center-box-order-list-box", + "right-box-order-list-box", + ], + }, this); + } + + _dndEnded?: boolean; + declare _left_box_order_list_box: PrefsBoxOrderListBox; + declare _center_box_order_list_box: PrefsBoxOrderListBox; + declare _right_box_order_list_box: PrefsBoxOrderListBox; + + constructor(params = {}) { + super(params); + + this.#setupDNDScroll(); + } + + /** + * This function sets up Drag-and-Drop scrolling. + * This means that scroll up or down is happening when a Drag-and-Drop + * operation is in progress and the user has their cursor either in the + * upper or lower 10% of this widget respectively. + */ + #setupDNDScroll(): void { + // Pass `this.get_first_child()` to the ScrollManager, since this + // `PrefsPage` extends an `Adw.PreferencesPage` and the first child of + // an `Adw.PreferencesPage` is the built-in `Gtk.ScrolledWindow`. + const scrollManager = new ScrollManager(this.get_first_child() as Gtk.ScrolledWindow); + + /// Setup GtkDropControllerMotion event controller and make use of its + /// events. + let controller = new Gtk.DropControllerMotion(); + + // Make sure scrolling stops, when DND operation ends. + this._dndEnded = true; + + // Scroll, when the pointer is in the right places and a DND operation + // is properly set up (this._dndEnded is false). + controller.connect("motion", (_, _x, y) => { + if ((y <= this.get_allocated_height() * 0.1) && !this._dndEnded) { + // If the pointer is currently in the upper ten percent of this + // widget, then scroll up. + scrollManager.startScrollUp(); + } else if ((y >= this.get_allocated_height() * 0.9) && !this._dndEnded) { + // If the pointer is currently in the lower ten percent of this + // widget, then scroll down. + scrollManager.startScrollDown(); + } else { + // Otherwise stop scrolling. + scrollManager.stopScrollAll(); + } + }); + + const stopScrollAllAtDNDEnd = () => { + this._dndEnded = true; + scrollManager.stopScrollAll(); + }; + controller.connect("leave", () => { + stopScrollAllAtDNDEnd(); + }); + controller.connect("enter", () => { + // Make use of `this._dndEnded` to setup stopScrollAtDNDEnd only + // once per DND operation. + if (this._dndEnded) { + const drag = controller.get_drop()?.get_drag() ?? null; + // Ensure we have a Gdk.Drag. + // If this is not the case for whatever reason, then don't start + // DND scrolling and just return. + if (!(drag instanceof Gdk.Drag)) { + // TODO: maybe add logging + return; + } + drag.connect("drop-performed", () => { + stopScrollAllAtDNDEnd(); + }); + drag.connect("dnd-finished", () => { + stopScrollAllAtDNDEnd(); + }); + drag.connect("cancel", () => { + stopScrollAllAtDNDEnd(); + }); + this._dndEnded = false; + } + }); + + this.add_controller(controller); + } + + onRowMove(listBox: PrefsBoxOrderListBox, row: PrefsBoxOrderItemRow, direction: string): void { + const rowPosition = row.get_index(); + + if (direction === "up") { // If the direction of the move is up. + // Handle the case, where the row is the topmost row in the list box. + if (rowPosition === 0) { + switch (listBox.boxOrder) { + // If the row is also in the topmost list box, then do + // nothing and return. + case "left-box-order": + log("The row is already the topmost row in the topmost box order."); + return; + // If the row is in the center list box, then move it up to + // the left one. + case "center-box-order": + listBox.removeRow(row); + this._left_box_order_list_box.insertRow(row, -1); + // First save the box order of the destination, then do + // "a save for clean up". + this._left_box_order_list_box.saveBoxOrderToSettings(); + this._left_box_order_list_box.determineRowMoveActionEnable(); + listBox.saveBoxOrderToSettings(); + listBox.determineRowMoveActionEnable(); + return; + // If the row is in the right list box, then move it up to + // the center one. + case "right-box-order": + listBox.removeRow(row); + this._center_box_order_list_box.insertRow(row, -1); + this._center_box_order_list_box.saveBoxOrderToSettings(); + this._center_box_order_list_box.determineRowMoveActionEnable(); + listBox.saveBoxOrderToSettings(); + listBox.determineRowMoveActionEnable(); + return; + } + } + + // Else just move the row up in the box. + listBox.removeRow(row); + listBox.insertRow(row, rowPosition - 1); + listBox.saveBoxOrderToSettings(); + listBox.determineRowMoveActionEnable(); + return; + } else { // Else the direction of the move must be down. + // Handle the case, where the row is the bottommost row in the list box. + const rowNextSibling = row.get_next_sibling(); + if (rowNextSibling instanceof PrefsBoxOrderListEmptyPlaceholder || rowNextSibling === null) { + switch (listBox.boxOrder) { + // If the row is also in the bottommost list box, then do + // nothing and return. + case "right-box-order": + log("The row is already the bottommost row in the bottommost box order."); + return; + // If the row is in the center list box, then move it down + // to the right one. + case "center-box-order": + listBox.removeRow(row); + this._right_box_order_list_box.insertRow(row, 0); + this._right_box_order_list_box.saveBoxOrderToSettings(); + this._right_box_order_list_box.determineRowMoveActionEnable(); + listBox.saveBoxOrderToSettings(); + listBox.determineRowMoveActionEnable(); + return; + // If the row is in the left list box, then move it down to + // the center one. + case "left-box-order": + listBox.removeRow(row); + this._center_box_order_list_box.insertRow(row, 0); + this._center_box_order_list_box.saveBoxOrderToSettings(); + this._center_box_order_list_box.determineRowMoveActionEnable(); + listBox.saveBoxOrderToSettings(); + listBox.determineRowMoveActionEnable(); + return; + } + } + + // Else just move the row down in the box. + listBox.removeRow(row); + listBox.insertRow(row, rowPosition + 1); + listBox.saveBoxOrderToSettings(); + listBox.determineRowMoveActionEnable(); + return; + } + } +} diff --git a/src/prefsModules/ScrollManager.js b/src/prefsModules/ScrollManager.js deleted file mode 100644 index 322d4f0..0000000 --- a/src/prefsModules/ScrollManager.js +++ /dev/null @@ -1,99 +0,0 @@ -"use strict"; -/* - * This file is part of Top-Bar-Organizer (a Gnome Shell Extension for - * organizing your Gnome Shell top bar). - * Copyright (C) 2021 Julian Schacher - * - * Top-Bar-Organizer is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -/* exported ScrollManager */ -const GLib = imports.gi.GLib; - -var ScrollManager = class ScrollManager { - /** - * @param {Gtk.ScrolledWindow} gtkScrolledWindow - */ - constructor(gtkScrolledWindow) { - this._gtkScrolledWindow = gtkScrolledWindow; - - this._scrollUp = false; - this._scrollDown = false; - } - - startScrollUp() { - // If the scroll up is already started, don't do anything. - if (this._scrollUp) return; - - // Make sure scroll down is stopped. - this.stopScrollDown(); - - this._scrollUp = true; - - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => { - // Set the new vadjustment value to either the current value minus a - // step increment or to 0. - const newVAdjustementValue = Math.max(this._gtkScrolledWindow.vadjustment.get_value() - this._gtkScrolledWindow.vadjustment.get_step_increment(), 0); - - // If the new value is the old one, return and stop this interval. - if (newVAdjustementValue === this._gtkScrolledWindow.vadjustment.get_value()) { - this._scrollUp = false; - return this._scrollUp; - } - // Otherwise, update the value. - this._gtkScrolledWindow.vadjustment.set_value(newVAdjustementValue); - return this._scrollUp; - }); - } - - startScrollDown() { - // If the scroll down is already started, don't do anything. - if (this._scrollDown) return; - - // Make sure scroll up is stopped. - this.stopScrollUp(); - - this._scrollDown = true; - - GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => { - // Set the new vadjusment value either to the curent value plus a - // step increment or to the upper value minus the page size. - const newVAdjustementValue = Math.min( - this._gtkScrolledWindow.vadjustment.get_value() + this._gtkScrolledWindow.vadjustment.get_step_increment(), - this._gtkScrolledWindow.vadjustment.get_upper() - this._gtkScrolledWindow.vadjustment.get_page_size() - ); - - // If the new value is the old one, return and stop this interval. - if (newVAdjustementValue === this._gtkScrolledWindow.vadjustment.get_value()) { - this._scrollDown = false; - return this._scrollDown; - } - // Otherwise, update the value. - this._gtkScrolledWindow.vadjustment.set_value(newVAdjustementValue); - return this._scrollDown; - }); - } - - stopScrollUp() { - this._scrollUp = false; - } - - stopScrollDown() { - this._scrollDown = false; - } - - stopScrollAll() { - this.stopScrollUp(); - this.stopScrollDown(); - } -}; diff --git a/src/prefsModules/ScrollManager.ts b/src/prefsModules/ScrollManager.ts new file mode 100644 index 0000000..1d9a8f8 --- /dev/null +++ b/src/prefsModules/ScrollManager.ts @@ -0,0 +1,87 @@ +"use strict"; + +import GLib from "gi://GLib"; +import type Gtk from "gi://Gtk"; + +export default class ScrollManager { + #gtkScrolledWindow: Gtk.ScrolledWindow; + #scrollUp: boolean; + #scrollDown: boolean; + + constructor(gtkScrolledWindow: Gtk.ScrolledWindow) { + this.#gtkScrolledWindow = gtkScrolledWindow; + + this.#scrollUp = false; + this.#scrollDown = false; + } + + startScrollUp(): void { + // If the scroll up is already started, don't do anything. + if (this.#scrollUp) { + return; + } + + // Make sure scroll down is stopped. + this.stopScrollDown(); + + this.#scrollUp = true; + + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => { + // Set the new vadjustment value to either the current value minus a + // step increment or to 0. + const newVAdjustementValue = Math.max(this.#gtkScrolledWindow.vadjustment.get_value() - this.#gtkScrolledWindow.vadjustment.get_step_increment(), 0); + + // If the new value is the old one, return and stop this interval. + if (newVAdjustementValue === this.#gtkScrolledWindow.vadjustment.get_value()) { + this.#scrollUp = false; + return this.#scrollUp; + } + // Otherwise, update the value. + this.#gtkScrolledWindow.vadjustment.set_value(newVAdjustementValue); + return this.#scrollUp; + }); + } + + startScrollDown(): void { + // If the scroll down is already started, don't do anything. + if (this.#scrollDown) { + return; + } + + // Make sure scroll up is stopped. + this.stopScrollUp(); + + this.#scrollDown = true; + + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => { + // Set the new vadjusment value either to the curent value plus a + // step increment or to the upper value minus the page size. + const newVAdjustementValue = Math.min( + this.#gtkScrolledWindow.vadjustment.get_value() + this.#gtkScrolledWindow.vadjustment.get_step_increment(), + this.#gtkScrolledWindow.vadjustment.get_upper() - this.#gtkScrolledWindow.vadjustment.get_page_size() + ); + + // If the new value is the old one, return and stop this interval. + if (newVAdjustementValue === this.#gtkScrolledWindow.vadjustment.get_value()) { + this.#scrollDown = false; + return this.#scrollDown; + } + // Otherwise, update the value. + this.#gtkScrolledWindow.vadjustment.set_value(newVAdjustementValue); + return this.#scrollDown; + }); + } + + stopScrollUp(): void { + this.#scrollUp = false; + } + + stopScrollDown(): void { + this.#scrollDown = false; + } + + stopScrollAll(): void { + this.stopScrollUp(); + this.stopScrollDown(); + } +} diff --git a/src/schemas/org.gnome.shell.extensions.top-bar-organizer.gschema.xml b/src/schemas/org.gnome.shell.extensions.top-bar-organizer.gschema.xml deleted file mode 100644 index b4b03a4..0000000 --- a/src/schemas/org.gnome.shell.extensions.top-bar-organizer.gschema.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - Order of items in the left box of the top bar. - [] - - - Order of items in the center box of the top bar. - [] - - - Order of items in the right box of the top bar. - [] - - - diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ea2c80f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "./dist", + "sourceMap": false, + "strict": true, + // To preserve imports for UI files. + "verbatimModuleSyntax": true, + "target": "es2022", + "lib": [ + "ES2022" + ], + }, + "include": [ + "ambient.d.ts", + ], + "files": [ + "src/extension.ts", + "src/prefs.ts" + ] +}