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 092c1f1..524273b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,7 @@ /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 a0fbb75..caf7992 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -3,6 +3,7 @@ env: extends: 'eslint:recommended' parserOptions: ecmaVersion: 2022 + sourceType: module rules: indent: - error @@ -48,6 +49,12 @@ rules: 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 5e78671..9bb16b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ +/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 index 20527b4..91ea15c 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,6 @@ Top Bar Organizer allows you to organize the items of the GNOME Shell top (menu) ## 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/julianschacher/top-bar-organizer/-/releases). +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 index 24e17fe..8b143e7 100644 --- a/data/org.gnome.shell.extensions.top-bar-organizer.gschema.xml +++ b/data/org.gnome.shell.extensions.top-bar-organizer.gschema.xml @@ -13,5 +13,13 @@ 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 index 86bf461..dd06b32 100644 --- a/data/ui/prefs-box-order-item-row.ui +++ b/data/ui/prefs-box-order-item-row.ui @@ -1,12 +1,6 @@ +
+ + Move Up + row.move-up + + + Move Down + row.move-down + +
+
+ + Options + row.options + +
Forget - options.forget + row.forget
diff --git a/data/ui/prefs-page.ui b/data/ui/prefs-page.ui index 288fa75..7eca8d0 100644 --- a/data/ui/prefs-page.ui +++ b/data/ui/prefs-page.ui @@ -13,8 +13,9 @@ - + left-box-order + @@ -26,8 +27,9 @@ - + center-box-order + @@ -39,8 +41,9 @@ - + right-box-order + 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/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/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 5fc35e4..2ca458a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,26 +1,83 @@ { "name": "top-bar-organizer", "version": "1.0.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "top-bar-organizer", "version": "1.0.0", "license": "GPL-3.0-or-later", + "dependencies": { + "@girs/gjs": "^4.0.0-beta.23", + "@girs/gnome-shell": "^48.0.2" + }, "devDependencies": { - "eslint": "^8.32.0" + "eslint": "^8.57.1", + "eslint-plugin-jsdoc": "^50.7.1", + "typescript": "^5.8.3" + } + }, + "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, + "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": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "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.3.2", - "espree": "^9.4.0", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -35,14 +92,681 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "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" + } + }, + "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": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@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/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": { @@ -63,10 +787,12 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "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", @@ -103,11 +829,40 @@ "node": ">= 8" } }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "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" }, @@ -120,6 +875,7 @@ "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" } @@ -129,6 +885,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -164,11 +921,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "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" + } + }, "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 + "dev": true, + "license": "Python-2.0" }, "node_modules/balanced-match": { "version": "1.0.2", @@ -191,6 +959,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -229,6 +998,16 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "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", @@ -250,12 +1029,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -297,49 +1077,50 @@ } }, "node_modules/eslint": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", - "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", + "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, + "license": "MIT", "dependencies": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", + "@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", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.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", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -352,10 +1133,66 @@ "url": "https://opencollective.com/eslint" } }, + "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, + "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.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "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", @@ -363,53 +1200,33 @@ }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-utils/node_modules/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, - "engines": { - "node": ">=10" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "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, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "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.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -419,10 +1236,11 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "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" }, @@ -464,13 +1282,15 @@ "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" }, "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" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -516,22 +1336,23 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "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.1.0", + "flatted": "^3.2.7", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12.0.0" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "node_modules/fs.realpath": { @@ -573,10 +1394,11 @@ } }, "node_modules/globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -587,10 +1409,10 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, "node_modules/has-flag": { @@ -603,19 +1425,21 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "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.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "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" @@ -688,21 +1512,12 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "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, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -710,11 +1525,28 @@ "js-yaml": "bin/js-yaml.js" } }, + "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" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -722,6 +1554,15 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "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", @@ -769,10 +1610,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "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", @@ -790,17 +1632,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "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" @@ -841,6 +1683,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -848,6 +1691,23 @@ "node": ">=6" } }, + "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", @@ -885,10 +1745,11 @@ } }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "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" } @@ -913,23 +1774,12 @@ } ] }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "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, + "license": "MIT", "engines": { "node": ">=4" } @@ -982,6 +1832,19 @@ "queue-microtask": "^1.2.2" } }, + "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", @@ -1003,6 +1866,31 @@ "node": ">=8" } }, + "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/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz", + "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "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, + "license": "CC0-1.0" + }, "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -1020,6 +1908,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -1062,6 +1951,7 @@ "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" }, @@ -1069,11 +1959,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -1093,15 +1998,6 @@ "node": ">= 8" } }, - "node_modules/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, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -1120,802 +2016,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@eslint/eslintrc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", - "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.4.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@humanwhocodes/config-array": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", - "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@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 - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@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, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@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 - }, - "@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, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", - "dev": true - }, - "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, - "requires": {} - }, - "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": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "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 - }, - "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" - } - }, - "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 - }, - "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": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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 - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "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": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "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": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "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 - }, - "eslint": { - "version": "8.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz", - "integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.4.1", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - } - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "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 - } - } - }, - "eslint-visitor-keys": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", - "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", - "dev": true - }, - "espree": { - "version": "9.4.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", - "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", - "dev": true, - "requires": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.3.0" - } - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - } - }, - "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": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "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 - }, - "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 - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "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, - "requires": { - "reusify": "^1.0.4" - } - }, - "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": { - "flat-cache": "^3.0.4" - } - }, - "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": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "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.3" - } - }, - "globals": { - "version": "13.19.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz", - "integrity": "sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "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 - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "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==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "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": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "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, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "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 - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "js-sdsl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", - "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", - "dev": true - }, - "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": "^2.0.1" - } - }, - "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 - }, - "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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "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": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "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 - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "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==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "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" - } - }, - "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, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "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, - "requires": { - "p-limit": "^3.0.2" - } - }, - "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": { - "callsites": "^3.0.0" - } - }, - "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 - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "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 - }, - "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 - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "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 - }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "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 - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "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": { - "glob": "^7.1.3" - } - }, - "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": { - "queue-microtask": "^1.2.2" - } - }, - "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": { - "shebang-regex": "^3.0.0" - } - }, - "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 - }, - "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.1" - } - }, - "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": "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" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "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": { - "prelude-ls": "^1.2.1" - } - }, - "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 - }, - "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": { - "punycode": "^2.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "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": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "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 - } } } diff --git a/package.json b/package.json index 3b57509..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": "^8.32.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 7c9739d..63497dc 100755 --- a/package.sh +++ b/package.sh @@ -4,9 +4,14 @@ set -e REAL_BASE_DIR=$( dirname $( readlink -f "$0" )) -gnome-extensions pack "$REAL_BASE_DIR/src" \ +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/src/extension.js b/src/extension.ts similarity index 62% rename from src/extension.js rename to src/extension.ts index 99cfaec..026de7e 100644 --- a/src/extension.js +++ b/src/extension.ts @@ -1,52 +1,65 @@ "use strict"; -/* exported init */ -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); +import St from "gi://St" +import type Gio from "gi://Gio" -const Main = imports.ui.main; -const Panel = imports.ui.panel; +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"; -const BoxOrderManager = Me.imports.extensionModules.BoxOrderManager; +import BoxOrderManager from "./extensionModules/BoxOrderManager.js"; +import type { Box } from "./extensionModules/BoxOrderManager.js"; -class Extension { - constructor() { - } +export interface CustomPanel extends Panel.Panel { + _leftBox: St.BoxLayout; + _centerBox: St.BoxLayout; + _rightBox: St.BoxLayout; +} - enable() { - this._settings = ExtensionUtils.getSettings(); +export default class TopBarOrganizerExtension extends Extension { + _settings!: Gio.Settings; + _boxOrderManager!: BoxOrderManager; + _settingsHandlerIds!: number[]; - this._boxOrderManager = new BoxOrderManager.BoxOrderManager(); + 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 `Panel._addToPanelBox` method with one handling new items - // and also handle AppIndicators getting ready, to handle new items. + // 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 configured box orders. + // Handle changes of settings. this._settingsHandlerIds = []; - const addConfiguredBoxOrderChangeHandler = (box) => { - let handlerId = this._settings.connect(`changed::${box}-box-order`, () => { + const addSettingsChangeHandler = (settingsName: string) => { + const handlerId = this._settings.connect(`changed::${settingsName}`, () => { this.#handleNewItemsAndOrderTopBar(); }); this._settingsHandlerIds.push(handlerId); }; - addConfiguredBoxOrderChangeHandler("left"); - addConfiguredBoxOrderChangeHandler("center"); - addConfiguredBoxOrderChangeHandler("right"); + addSettingsChangeHandler("left-box-order"); + addSettingsChangeHandler("center-box-order"); + addSettingsChangeHandler("right-box-order"); + addSettingsChangeHandler("hide"); + addSettingsChangeHandler("show"); } - disable() { + disable(): void { // Revert the overwrite of `Panel._addToPanelBox`. + // @ts-ignore Panel.Panel.prototype._addToPanelBox = Panel.Panel.prototype._originalAddToPanelBox; // Set `Panel._originalAddToPanelBox` to `undefined`. - Panel._originalAddToPanelBox = undefined; + // @ts-ignore + Panel.Panel.prototype._originalAddToPanelBox = undefined; // Disconnect signals. for (const handlerId of this._settingsHandlerIds) { @@ -54,7 +67,9 @@ class Extension { } this._boxOrderManager.disconnectSignals(); + // @ts-ignore this._settings = null; + // @ts-ignore this._boxOrderManager = null; } @@ -66,9 +81,10 @@ class Extension { * Overwrite `Panel._addToPanelBox` with a custom method, which simply calls * the original one and handles new items and orders the top bar afterwards. */ - #overwritePanelAddToPanelBox() { + #overwritePanelAddToPanelBox(): void { // Add the original `Panel._addToPanelBox` method as // `Panel._originalAddToPanelBox`. + // @ts-ignore Panel.Panel.prototype._originalAddToPanelBox = Panel.Panel.prototype._addToPanelBox; const handleNewItemsAndOrderTopBar = () => { @@ -79,6 +95,7 @@ class Extension { 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(); }; @@ -91,9 +108,9 @@ class Extension { /** * This method orders the top bar items of the specified box according to * the configured box orders. - * @param {string} box - The box to order. + * @param {Box} box - The box to order. */ - #orderTopBarItems(box) { + #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") { @@ -101,33 +118,29 @@ class Extension { } // Get the valid box order. - const validBoxOrder = this._boxOrderManager.createValidBoxOrder(box); + const validBoxOrder = this._boxOrderManager.getValidBoxOrder(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; - } + let panelBox = (Main.panel as CustomPanel)[`_${box}Box`]; - /// Go through the items (or rather their roles) of the validBoxOrder - /// and order the panelBox accordingly. + /// 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 role = validBoxOrder[i]; + const item = validBoxOrder[i]; // Get the indicator container associated with the current role. - const associatedIndicatorContainer = Main.panel.statusArea[role].container; + 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; - associatedIndicatorContainer.get_parent().remove_child(associatedIndicatorContainer); + 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 @@ -146,8 +159,16 @@ class Extension { panelBox.insert_child_at_index(associatedIndicatorContainer, i); } - // Hide the indicator container again, if it wasn't visible. - if (!isVisible) { + // 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(); } } @@ -160,7 +181,7 @@ class Extension { * This method handles all new items currently present in the top bar and * orders the items of all top bar boxes. */ - #handleNewItemsAndOrderTopBar() { + #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") { @@ -177,7 +198,3 @@ class Extension { // top bar items at the beginning of this method, this isn't a concern. } } - -function init() { - return new Extension(); -} diff --git a/src/extensionModules/BoxOrderManager.js b/src/extensionModules/BoxOrderManager.js deleted file mode 100644 index a206739..0000000 --- a/src/extensionModules/BoxOrderManager.js +++ /dev/null @@ -1,273 +0,0 @@ -"use strict"; -/* exported BoxOrderManager */ - -const GObject = imports.gi.GObject; - -const ExtensionUtils = imports.misc.extensionUtils; - -const Main = imports.ui.main; - -/** - * This class provides methods get, set and interact with box orders, while - * taking over the work of translating between what is stored in settings and - * what is really useable by the other extension code. - * It's basically a heavy wrapper around the box orders stored in the settings. - */ -var BoxOrderManager = GObject.registerClass({ - Signals: { - "appIndicatorReady": {} - } -}, class BoxOrderManager extends GObject.Object { - #appIndicatorReadyHandlerIdMap; - #appIndicatorItemApplicationRoleMap; - #settings; - - constructor(params = {}) { - super(params); - - this.#appIndicatorReadyHandlerIdMap = new Map(); - this.#appIndicatorItemApplicationRoleMap = new Map(); - - this.#settings = ExtensionUtils.getSettings(); - } - - /** - * Handles an AppIndicator/KStatusNotifierItem item by associating the role - * of the given item with the application of the - * AppIndicator/KStatusNotifier item and returning a placeholder role. - * In the case, where the application can't be determined, this method - * throws an error. However it also makes sure that once the app indicators - * "ready" signal emits, this classes "appIndicatorReady" signal emits as - * well. - * @param {string} indicatorContainer - The container of the indicator of the - * AppIndicator/KStatusNotifierItem item. - * @param {string} role - The role of the AppIndicator/KStatusNotifierItem - * item. - * @returns {string} The placeholder role. - */ - #handleAppIndicatorItem(indicatorContainer, role) { - const appIndicator = indicatorContainer.get_child()._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"; - } - - // Associate the role with the application. - let roles = this.#appIndicatorItemApplicationRoleMap.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.#appIndicatorItemApplicationRoleMap.set(application, [role]); - } - - // Return the placeholder. - // A box order containing this placeholder can later be resolved to - // relevant roles using `#resolveAppIndicatorPlaceholders`. - return `appindicator-kstatusnotifieritem-${application}`; - } - - /** - * Takes a box order and replaces AppIndicator placeholder roles with - * actual roles. - * @param {string[]} - The box order of which to replace placeholder roles. - * @returns {string[]} - A box order with all placeholder roles - * resolved/replaced to/with actual roles. - */ - #resolveAppIndicatorPlaceholders(boxOrder) { - let resolvedBoxOrder = []; - for (const role of boxOrder) { - // If the role isn't a placeholder, just add it to the resolved box - // order. - if (!role.startsWith("appindicator-kstatusnotifieritem-")) { - resolvedBoxOrder.push(role); - continue; - } - - /// If the role is a placeholder, replace it. - // First get the application this placeholder is associated with. - const application = role.replace("appindicator-kstatusnotifieritem-", ""); - - // Then get the actual roles associated with this application. - let actualRoles = this.#appIndicatorItemApplicationRoleMap.get(application); - - // If there are no actual roles, continue. - if (!actualRoles) { - continue; - } - - // Otherwise add the actual roles to the resolved box order. - resolvedBoxOrder.push(...actualRoles); - } - - 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() { - for (const [handlerId, appIndicator] of this.#appIndicatorReadyHandlerIdMap) { - if (handlerId && appIndicator?.signalHandlerIsConnected(handlerId)) { - appIndicator.disconnect(handlerId); - } - } - this.#appIndicatorReadyHandlerIdMap = null; - } - - /** - * This method returns a valid box order for the given top bar box. - * This means it returns a box order, 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 top bar 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.#resolveAppIndicatorPlaceholders(this.#settings.get_strv(`${box}-box-order`)); - - // ToDo: simplify. - // Get the indicator containers (of the items) currently present in the - // Gnome Shell top bar. - const indicatorContainers = [ - Main.panel._leftBox.get_children(), - Main.panel._centerBox.get_children(), - Main.panel._rightBox.get_children() - ].flat(); - - // Create an indicator containers set from the indicator containers for - // fast easy access. - const indicatorContainerSet = new Set(indicatorContainers); - - // 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 (indicatorContainerSet.has(associatedIndicatorContainer)) { - validBoxOrder.push(role); - } - } - - return validBoxOrder; - } - - /** - * This method saves all new items currently present in the Gnome Shell top - * bar to the correct box orders. - */ - saveNewTopBarItems() { - // 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; - } - - // 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 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) { - 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 boxIndicatorContainers = { - 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 indicator containers of the given box - // and adds roles of new items to the box order. - const addNewItemsToBoxOrder = (indicatorContainers, boxOrder, box) => { - for (const indicatorContainer of indicatorContainers) { - // First get the role associated with the current indicator - // container. - let role = indicatorContainerRoleMap.get(indicatorContainer); - if (!role) { - continue; - } - - // Handle an AppIndicator/KStatusNotifierItem item differently. - if (role.startsWith("appindicator-")) { - try { - role = this.#handleAppIndicatorItem(indicatorContainer, role); - } catch (e) { - if (e.message !== "Application can't be determined.") { - throw(e); - } - continue; - } - } - - // Add the role to the box order, if it isn't in in one already. - if (!boxOrders.left.includes(role) - && !boxOrders.center.includes(role) - && !boxOrders.right.includes(role)) { - if (box === "right") { - // Add the items to the beginning for this array, since - // its RTL. - boxOrder.unshift(role); - } else { - boxOrder.push(role); - } - } - } - }; - - addNewItemsToBoxOrder(boxIndicatorContainers.left, boxOrders.left, "left"); - addNewItemsToBoxOrder(boxIndicatorContainers.center, boxOrders.center, "center"); - addNewItemsToBoxOrder(boxIndicatorContainers.right, boxOrders.right, "right"); - - // This function saves the given box order to settings. - const saveBoxOrderToSettings = (boxOrder, box) => { - const currentBoxOrder = this.#settings.get_strv(`${box}-box-order`); - // Only save the updated box order to settings, if it is different, - // to avoid loops, when listening on settings changes. - if (JSON.stringify(currentBoxOrder) !== JSON.stringify(boxOrder)) { - this.#settings.set_strv(`${box}-box-order`, boxOrder); - } - }; - - saveBoxOrderToSettings(boxOrders.left, "left"); - saveBoxOrderToSettings(boxOrders.center, "center"); - saveBoxOrderToSettings(boxOrders.right, "right"); - } -}); 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 eb9407a..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": 7, - "shell-version": [ "42", "43" ], + "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.js b/src/prefs.js deleted file mode 100644 index 309bbc6..0000000 --- a/src/prefs.js +++ /dev/null @@ -1,14 +0,0 @@ -"use strict"; -/* exported buildPrefsWidget, init */ - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const PrefsPage = Me.imports.prefsModules.PrefsPage; - -function buildPrefsWidget() { - return new PrefsPage.PrefsPage(); -} - -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 d98286f..0000000 --- a/src/prefsModules/PrefsBoxOrderItemRow.js +++ /dev/null @@ -1,146 +0,0 @@ -"use strict"; -/* exported PrefsBoxOrderItemRow */ - -const Gtk = imports.gi.Gtk; -const Gdk = imports.gi.Gdk; -const Gio = imports.gi.Gio; -const GObject = imports.gi.GObject; -const Adw = imports.gi.Adw; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -var PrefsBoxOrderItemRow = GObject.registerClass({ - GTypeName: "PrefsBoxOrderItemRow", - Template: Me.dir.get_child("ui").get_child("prefs-box-order-item-row.ui").get_uri(), - InternalChildren: [ - "item-name-display-label" - ] -}, class PrefsBoxOrderItemRow extends Adw.ActionRow { - #drag_starting_point_x; - #drag_starting_point_y; - - constructor(params = {}, item) { - super(params); - - this.#associateItem(item); - this.#setupActions(); - } - - /** - * Associate `this` with an item. - * @param {String} item - */ - #associateItem(item) { - this.item = item; - - if (item.startsWith("appindicator-kstatusnotifieritem-")) { - // Set `this._item_name_display_label` to something nicer, if the - // associated item is an AppIndicator/KStatusNotifierItem item. - this._item_name_display_label.set_label(item.replace("appindicator-kstatusnotifieritem-", "")); - } else { - // Otherwise just set it to `item`. - this._item_name_display_label.set_label(item); - } - } - - /** - * Setup actions. - */ - #setupActions() { - const actionGroup = new Gio.SimpleActionGroup(); - - const forgetAction = new Gio.SimpleAction({ - name: "forget" - }); - forgetAction.connect("activate", (_action, _params) => { - const parentListBox = this.get_parent(); - parentListBox.remove(this); - parentListBox.saveBoxOrderToSettings(); - }); - actionGroup.add_action(forgetAction); - - this.insert_action_group("options", actionGroup); - } - - onDragPrepare(_source, x, y) { - const value = new GObject.Value(); - value.init(PrefsBoxOrderItemRow); - 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, drag) { - 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); - 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, value, _x, _y) { - // 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(); - } - } -}); 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 11e2202..0000000 --- a/src/prefsModules/PrefsBoxOrderListBox.js +++ /dev/null @@ -1,81 +0,0 @@ -"use strict"; -/* exported PrefsBoxOrderListBox */ - -const Gtk = imports.gi.Gtk; -const GObject = imports.gi.GObject; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const PrefsBoxOrderItemRow = Me.imports.prefsModules.PrefsBoxOrderItemRow; -const PrefsBoxOrderListEmptyPlaceholder = Me.imports.prefsModules.PrefsBoxOrderListEmptyPlaceholder; - -var PrefsBoxOrderListBox = GObject.registerClass({ - GTypeName: "PrefsBoxOrderListBox", - Template: Me.dir.get_child("ui").get_child("prefs-box-order-list-box.ui").get_uri(), - Properties: { - BoxOrder: GObject.ParamSpec.string( - "box-order", - "Box Order", - "The box order this PrefsBoxOrderListBox is associated with.", - GObject.ParamFlags.READWRITE | GObject.ParamFlags.CONSTRUCT_ONLY, - "" - ) - } -}, class PrefsBoxOrderListBox extends Gtk.ListBox { - #settings; - - /** - * @param {Object} params - */ - constructor(params = {}) { - super(params); - - // Load the settings. - this.#settings = ExtensionUtils.getSettings(); - - // Add a placeholder widget for the case, where no GtkListBoxRows are - // present. - this.set_placeholder(new PrefsBoxOrderListEmptyPlaceholder.PrefsBoxOrderListEmptyPlaceholder()); - } - - get boxOrder() { - return this._boxOrder; - } - - set boxOrder(value) { - this._boxOrder = value; - - // Load the settings here as well, since a `CONSTRUCT_ONLY` property - // apparently can't access `this.#settings`. - const settings = ExtensionUtils.getSettings(); - // Get the actual box order for the given box order name from settings. - const boxOrder = 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 listBoxRow = new PrefsBoxOrderItemRow.PrefsBoxOrderItemRow({}, item); - this.append(listBoxRow); - } - - this.notify("box-order"); - } - - /** - * 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 cfeed56..0000000 --- a/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.js +++ /dev/null @@ -1,31 +0,0 @@ -"use strict"; -/* exported PrefsBoxOrderListEmptyPlaceholder */ - -const Gtk = imports.gi.Gtk; -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("ui").get_child("prefs-box-order-list-empty-placeholder.ui").get_uri() -}, class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box { - // Handle a new drop on `this` properly. - // `value` is the thing getting dropped. - onDrop(_target, value, _x, _y) { - // 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. - ownListBox.saveBoxOrderToSettings(); - valueListBox.saveBoxOrderToSettings(); - } -}); 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.js b/src/prefsModules/PrefsPage.js deleted file mode 100644 index f9059b0..0000000 --- a/src/prefsModules/PrefsPage.js +++ /dev/null @@ -1,88 +0,0 @@ -"use strict"; -/* exported PrefsPage */ - -const Gtk = imports.gi.Gtk; -const GObject = imports.gi.GObject; -const Adw = imports.gi.Adw; - -const ExtensionUtils = imports.misc.extensionUtils; -const Me = ExtensionUtils.getCurrentExtension(); - -const ScrollManager = Me.imports.prefsModules.ScrollManager; - -// Imports to make UI file work. -/* exported PrefsBoxOrderListBox */ -const PrefsBoxOrderListBox = Me.imports.prefsModules.PrefsBoxOrderListBox; - -var PrefsPage = GObject.registerClass({ - GTypeName: "PrefsPage", - Template: Me.dir.get_child("ui").get_child("prefs-page.ui").get_uri() -}, class PrefsPage extends Adw.PreferencesPage { - 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() { - // 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.ScrollManager(this.get_first_child()); - - /// Setup GtkDropControllerMotion event controller and make use of its - /// events. - let controller = new Gtk.DropControllerMotion(); - - // Scroll, when the pointer is in the right places. - controller.connect("motion", (_, _x, y) => { - if (y <= this.get_allocated_height() * 0.1) { - // 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) { - // If the pointer is currently in the lower ten percent of this - // widget, then scroll down. - scrollManager.startScrollDown(); - } else { - // Otherwise stop scrolling. - scrollManager.stopScrollAll(); - } - }); - - // Make sure scrolling stops, when DND operation ends. - this._dndEnded = true; - const stopScrollAllAtDNDEnd = () => { - scrollManager.stopScrollAll(); - this._dndEnded = true; - }; - controller.connect("leave", () => { - stopScrollAllAtDNDEnd(); - }); - controller.connect("enter", () => { - // Make use of `this._dndEnded` to setup stopScrollAtDNDEnd only - // once per DND operation. - if (this._dndEnded) { - let drag = controller.get_drop().get_drag(); - drag.connect("drop-performed", () => { - stopScrollAllAtDNDEnd(); - }); - drag.connect("dnd-finished", () => { - stopScrollAllAtDNDEnd(); - }); - drag.connect("cancel", () => { - stopScrollAllAtDNDEnd(); - }); - this._dndEnded = false; - } - }); - - this.add_controller(controller); - } -}); 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.ts similarity index 86% rename from src/prefsModules/ScrollManager.js rename to src/prefsModules/ScrollManager.ts index cd3bdce..1d9a8f8 100644 --- a/src/prefsModules/ScrollManager.js +++ b/src/prefsModules/ScrollManager.ts @@ -1,23 +1,21 @@ "use strict"; -/* exported ScrollManager */ -const GLib = imports.gi.GLib; -var ScrollManager = class ScrollManager { - #gtkScrolledWindow; - #scrollUp; - #scrollDown; +import GLib from "gi://GLib"; +import type Gtk from "gi://Gtk"; - /** - * @param {Gtk.ScrolledWindow} gtkScrolledWindow - */ - constructor(gtkScrolledWindow) { +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() { + startScrollUp(): void { // If the scroll up is already started, don't do anything. if (this.#scrollUp) { return; @@ -44,7 +42,7 @@ var ScrollManager = class ScrollManager { }); } - startScrollDown() { + startScrollDown(): void { // If the scroll down is already started, don't do anything. if (this.#scrollDown) { return; @@ -74,16 +72,16 @@ var ScrollManager = class ScrollManager { }); } - stopScrollUp() { + stopScrollUp(): void { this.#scrollUp = false; } - stopScrollDown() { + stopScrollDown(): void { this.#scrollDown = false; } - stopScrollAll() { + stopScrollAll(): void { this.stopScrollUp(); this.stopScrollDown(); } -}; +} 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" + ] +}