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 2e93655..524273b 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -3,3 +3,5 @@
/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/.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/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/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 @@
+
+
+
+
+ 640
+
+
+
+
+
diff --git a/data/ui/prefs-box-order-item-row.ui b/data/ui/prefs-box-order-item-row.ui
index 239ac55..dd06b32 100644
--- a/data/ui/prefs-box-order-item-row.ui
+++ b/data/ui/prefs-box-order-item-row.ui
@@ -1,12 +1,6 @@
-
-
- start
- True
-
- list-drag-handle-symbolic
@@ -52,6 +46,12 @@
row.move-down
+
+
+ Options
+ row.options
+
+ Forget
diff --git a/docs/Creating_a_New_Release.md b/docs/Creating_a_New_Release.md
index 38d7980..049afd2 100644
--- a/docs/Creating_a_New_Release.md
+++ b/docs/Creating_a_New_Release.md
@@ -43,7 +43,7 @@ To create a new tag, do the following:
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. Upload the release-ZIP created in the previous step as a release asset.
+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
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/package-lock.json b/package-lock.json
index 87fbe20..2ca458a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,8 +8,14 @@
"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.50.0"
+ "eslint": "^8.57.1",
+ "eslint-plugin-jsdoc": "^50.7.1",
+ "typescript": "^5.8.3"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -21,6 +27,23 @@
"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",
@@ -46,10 +69,11 @@
}
},
"node_modules/@eslint/eslintrc": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz",
- "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==",
+ "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",
@@ -69,22 +93,680 @@
}
},
"node_modules/@eslint/js": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.50.0.tgz",
- "integrity": "sha512-NCC3zz2+nvYd+Ckfh87rA47zfu2QsQpvc6k1yzTk+b9KzRj0wkGa8LSoGOXN6Zv4lRf/EIoZ80biDh9HOI+RNQ==",
+ "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/@humanwhocodes/config-array": {
- "version": "0.11.11",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz",
- "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==",
- "dev": true,
+ "node_modules/@girs/accountsservice-1.0": {
+ "version": "1.0.0-4.0.0-beta.23",
+ "resolved": "https://registry.npmjs.org/@girs/accountsservice-1.0/-/accountsservice-1.0-1.0.0-4.0.0-beta.23.tgz",
+ "integrity": "sha512-QR7xfIvNbovmhNzzma+NxIMsQR8zgTaYk16Dqa/4YD8XBWrAgWuQhwEKKibG9KbQe5+YSUDUxJlbkSeOb3S5mg==",
+ "license": "MIT",
"dependencies": {
- "@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": {
@@ -105,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",
@@ -145,11 +829,40 @@
"node": ">= 8"
}
},
- "node_modules/acorn": {
- "version": "8.10.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
- "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "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"
},
@@ -162,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"
}
@@ -171,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",
@@ -206,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",
@@ -233,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"
}
@@ -271,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",
@@ -292,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"
@@ -339,18 +1077,21 @@
}
},
"node_modules/eslint": {
- "version": "8.50.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.50.0.tgz",
- "integrity": "sha512-FOnOGSuFuFLv/Sa+FDVRZl4GGVAAFFi8LecRsI5a1tMO5HIE8nCm4ivAlzt4dT3ol/PaaGC0rJEEXQmHJBGoOg==",
+ "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-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.2",
- "@eslint/js": "8.50.0",
- "@humanwhocodes/config-array": "^0.11.11",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@@ -392,6 +1133,62 @@
"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.2.2",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
@@ -425,6 +1222,7 @@
"resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
"integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
"dev": true,
+ "license": "BSD-2-Clause",
"dependencies": {
"acorn": "^8.9.0",
"acorn-jsx": "^5.3.2",
@@ -438,10 +1236,11 @@
}
},
"node_modules/esquery": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
- "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "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"
},
@@ -483,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",
@@ -593,10 +1394,11 @@
}
},
"node_modules/globals": {
- "version": "13.22.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.22.0.tgz",
- "integrity": "sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==",
+ "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"
},
@@ -623,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"
@@ -713,6 +1517,7 @@
"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"
},
@@ -720,6 +1525,16 @@
"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",
@@ -730,7 +1545,8 @@
"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",
@@ -794,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",
@@ -866,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"
},
@@ -873,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",
@@ -910,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"
}
@@ -943,6 +1779,7 @@
"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"
}
@@ -995,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",
@@ -1016,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",
@@ -1033,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"
},
@@ -1075,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"
},
@@ -1082,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"
}
diff --git a/package.json b/package.json
index efcc3a5..c4d4367 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
"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"
},
@@ -12,6 +13,12 @@
"author": "June",
"license": "GPL-3.0-or-later",
"devDependencies": {
- "eslint": "^8.50.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 99392e9..63497dc 100755
--- a/package.sh
+++ b/package.sh
@@ -4,7 +4,11 @@ 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 \
diff --git a/src/extension.js b/src/extension.ts
similarity index 68%
rename from src/extension.js
rename to src/extension.ts
index 7caa5b3..026de7e 100644
--- a/src/extension.js
+++ b/src/extension.ts
@@ -1,13 +1,27 @@
"use strict";
+import St from "gi://St"
+import type Gio from "gi://Gio"
+
import * as Main from "resource:///org/gnome/shell/ui/main.js";
import * as Panel from "resource:///org/gnome/shell/ui/panel.js";
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
import BoxOrderManager from "./extensionModules/BoxOrderManager.js";
+import type { Box } from "./extensionModules/BoxOrderManager.js";
+
+export interface CustomPanel extends Panel.Panel {
+ _leftBox: St.BoxLayout;
+ _centerBox: St.BoxLayout;
+ _rightBox: St.BoxLayout;
+}
export default class TopBarOrganizerExtension extends Extension {
- enable() {
+ _settings!: Gio.Settings;
+ _boxOrderManager!: BoxOrderManager;
+ _settingsHandlerIds!: number[];
+
+ enable(): void {
this._settings = this.getSettings();
this._boxOrderManager = new BoxOrderManager({}, this._settings);
@@ -16,30 +30,35 @@ export default class TopBarOrganizerExtension extends Extension {
// 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`.
+ // @ts-ignore
Panel.Panel.prototype._originalAddToPanelBox = undefined;
// Disconnect signals.
@@ -48,7 +67,9 @@ export default class TopBarOrganizerExtension extends Extension {
}
this._boxOrderManager.disconnectSignals();
+ // @ts-ignore
this._settings = null;
+ // @ts-ignore
this._boxOrderManager = null;
}
@@ -60,9 +81,10 @@ export default class TopBarOrganizerExtension extends 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 = () => {
@@ -73,6 +95,7 @@ export default class TopBarOrganizerExtension extends 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();
};
@@ -85,9 +108,9 @@ export default class TopBarOrganizerExtension extends 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") {
@@ -95,33 +118,29 @@ export default class TopBarOrganizerExtension extends 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
@@ -140,8 +159,16 @@ export default class TopBarOrganizerExtension extends 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();
}
}
@@ -154,7 +181,7 @@ export default class TopBarOrganizerExtension extends 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") {
diff --git a/src/extensionModules/BoxOrderManager.js b/src/extensionModules/BoxOrderManager.js
deleted file mode 100644
index c6f371d..0000000
--- a/src/extensionModules/BoxOrderManager.js
+++ /dev/null
@@ -1,274 +0,0 @@
-"use strict";
-
-import GObject from "gi://GObject";
-
-import * as Main from "resource:///org/gnome/shell/ui/main.js";
-
-/**
- * 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.
- */
-export default class BoxOrderManager extends GObject.Object {
- static {
- GObject.registerClass({
- Signals: {
- "appIndicatorReady": {},
- },
- }, this);
- }
-
- #appIndicatorReadyHandlerIdMap;
- #appIndicatorItemApplicationRoleMap;
- #settings;
-
- constructor(params = {}, settings) {
- super(params);
-
- this.#appIndicatorReadyHandlerIdMap = new Map();
- this.#appIndicatorItemApplicationRoleMap = new Map();
-
- this.#settings = settings;
- }
-
- /**
- * 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 4248aa5..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": 11,
- "shell-version": [ "45", "46" ],
+ "version": 15,
+ "shell-version": [ "45", "46", "47", "48", "49" ],
"settings-schema": "org.gnome.shell.extensions.top-bar-organizer",
"url": "https://gitlab.gnome.org/june/top-bar-organizer"
}
diff --git a/src/prefs.js b/src/prefs.ts
similarity index 89%
rename from src/prefs.js
rename to src/prefs.ts
index 967277d..76718ba 100644
--- a/src/prefs.js
+++ b/src/prefs.ts
@@ -13,7 +13,7 @@ export default class TopBarOrganizerPreferences extends ExtensionPreferences {
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,
+ (defaultGdkDisplay as Gdk.Display),
provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
);
@@ -22,7 +22,7 @@ export default class TopBarOrganizerPreferences extends ExtensionPreferences {
prefsPage.connect("destroy", () => {
Gtk.StyleContext.remove_provider_for_display(
- defaultGdkDisplay,
+ (defaultGdkDisplay as Gdk.Display),
provider
);
});
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.ts
similarity index 61%
rename from src/prefsModules/PrefsBoxOrderItemRow.js
rename to src/prefsModules/PrefsBoxOrderItemRow.ts
index c44f51f..3da4d6b 100644
--- a/src/prefsModules/PrefsBoxOrderItemRow.js
+++ b/src/prefsModules/PrefsBoxOrderItemRow.ts
@@ -6,14 +6,14 @@ 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),
- InternalChildren: [
- "item-name-display-label",
- ],
Signals: {
"move": {
param_types: [GObject.TYPE_STRING],
@@ -21,44 +21,50 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
},
}, this);
this.install_action("row.forget", null, (self, _actionName, _param) => {
- const parentListBox = self.get_parent();
- parentListBox.removeRow(self);
+ 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"));
}
- #drag_starting_point_x;
- #drag_starting_point_y;
+ item: string;
+ #drag_starting_point_x?: number;
+ #drag_starting_point_y?: number;
- constructor(params = {}, item) {
+ constructor(params = {}, item: string) {
super(params);
- this.#associateItem(item);
- }
-
- /**
- * Associate `this` with an item.
- * @param {String} item
- */
- #associateItem(item) {
+ // Associate `this` with an 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-", ""));
+ 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._item_name_display_label.set_label(item);
+ this.set_title(this.item);
}
}
- onDragPrepare(_source, x, y) {
+ onDragPrepare(_source: Gtk.DragSource, x: number, y: number): Gdk.ContentProvider {
const value = new GObject.Value();
- value.init(PrefsBoxOrderItemRow);
+ value.init(PrefsBoxOrderItemRow.$gtype);
value.set_object(this);
this.#drag_starting_point_x = x;
@@ -66,7 +72,7 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
return Gdk.ContentProvider.new_for_value(value);
}
- onDragBegin(_source, drag) {
+ 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);
@@ -77,20 +83,32 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
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);
+ // 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, value, _x, _y) {
+ 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;
+ return false;
}
// Get the GtkListBoxes of `this` and the drop value.
- const ownListBox = this.get_parent();
- const valueListBox = value.get_parent();
+ 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();
@@ -137,5 +155,7 @@ export default class PrefsBoxOrderItemRow extends Adw.ActionRow {
valueListBox.saveBoxOrderToSettings();
valueListBox.determineRowMoveActionEnable();
}
+
+ return true;
}
}
diff --git a/src/prefsModules/PrefsBoxOrderListBox.js b/src/prefsModules/PrefsBoxOrderListBox.ts
similarity index 84%
rename from src/prefsModules/PrefsBoxOrderListBox.js
rename to src/prefsModules/PrefsBoxOrderListBox.ts
index 59d4417..f2b5c98 100644
--- a/src/prefsModules/PrefsBoxOrderListBox.js
+++ b/src/prefsModules/PrefsBoxOrderListBox.ts
@@ -3,6 +3,7 @@
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";
@@ -25,14 +26,15 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
},
Signals: {
"row-move": {
- param_types: [PrefsBoxOrderItemRow, GObject.TYPE_STRING],
+ param_types: [PrefsBoxOrderItemRow.$gtype, GObject.TYPE_STRING],
},
},
}, this);
}
- #settings;
- #rowSignalHandlerIds = new Map();
+ _boxOrder!: string;
+ #settings: Gio.Settings;
+ #rowSignalHandlerIds = new Map();
/**
* @param {Object} params
@@ -41,18 +43,18 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
super(params);
// Load the settings.
- this.#settings = ExtensionPreferences.lookupByURL(import.meta.url).getSettings();
+ 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() {
+ get boxOrder(): string {
return this._boxOrder;
}
- set boxOrder(value) {
+ set boxOrder(value: string) {
this._boxOrder = value;
// Get the actual box order for the given box order name from settings.
@@ -73,10 +75,10 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
* position.
* Also handles stuff like connecting signals.
*/
- insertRow(row, position) {
+ insertRow(row: PrefsBoxOrderItemRow, position: number): void {
this.insert(row, position);
- const signalHandlerIds = [];
+ const signalHandlerIds: number[] = [];
signalHandlerIds.push(row.connect("move", (row, direction) => {
this.emit("row-move", row, direction);
}));
@@ -88,8 +90,8 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
* Removes the given PrefsBoxOrderItemRow from this list box.
* Also handles stuff like disconnecting signals.
*/
- removeRow(row) {
- const signalHandlerIds = this.#rowSignalHandlerIds.get(row);
+ removeRow(row: PrefsBoxOrderItemRow): void {
+ const signalHandlerIds = this.#rowSignalHandlerIds.get(row) ?? [];
for (const id of signalHandlerIds) {
row.disconnect(id);
@@ -102,11 +104,11 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
* Saves the box order represented by `this` (and its
* `PrefsBoxOrderItemRows`) to settings.
*/
- saveBoxOrderToSettings() {
- let currentBoxOrder = [];
+ saveBoxOrderToSettings(): void {
+ let currentBoxOrder: string[] = [];
for (let potentialPrefsBoxOrderItemRow of this) {
// Only process PrefsBoxOrderItemRows.
- if (potentialPrefsBoxOrderItemRow.constructor.$gtype.name !== "PrefsBoxOrderItemRow") {
+ if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) {
continue;
}
@@ -120,10 +122,10 @@ export default class PrefsBoxOrderListBox extends Gtk.ListBox {
* Determines whether or not each move action of each PrefsBoxOrderItemRow
* should be enabled or disabled.
*/
- determineRowMoveActionEnable() {
+ determineRowMoveActionEnable(): void {
for (let potentialPrefsBoxOrderItemRow of this) {
// Only process PrefsBoxOrderItemRows.
- if (potentialPrefsBoxOrderItemRow.constructor.$gtype.name !== "PrefsBoxOrderItemRow") {
+ if (!(potentialPrefsBoxOrderItemRow instanceof PrefsBoxOrderItemRow)) {
continue;
}
diff --git a/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.js b/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.ts
similarity index 61%
rename from src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.js
rename to src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.ts
index 0fa0db3..dae599b 100644
--- a/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.js
+++ b/src/prefsModules/PrefsBoxOrderListEmptyPlaceholder.ts
@@ -4,6 +4,9 @@ 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({
@@ -14,10 +17,18 @@ export default class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box {
// Handle a new drop on `this` properly.
// `value` is the thing getting dropped.
- onDrop(_target, value, _x, _y) {
+ 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();
- const valueListBox = value.get_parent();
+ 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);
@@ -31,5 +42,7 @@ export default class PrefsBoxOrderListEmptyPlaceholder extends Gtk.Box {
ownListBox.determineRowMoveActionEnable();
valueListBox.saveBoxOrderToSettings();
valueListBox.determineRowMoveActionEnable();
+
+ return true;
}
}
diff --git a/src/prefsModules/PrefsPage.js b/src/prefsModules/PrefsPage.ts
similarity index 86%
rename from src/prefsModules/PrefsPage.js
rename to src/prefsModules/PrefsPage.ts
index 1816cce..6c22d8f 100644
--- a/src/prefsModules/PrefsPage.js
+++ b/src/prefsModules/PrefsPage.ts
@@ -1,5 +1,6 @@
"use strict";
+import Gdk from "gi://Gdk";
import Gtk from "gi://Gtk";
import GObject from "gi://GObject";
import Adw from "gi://Adw";
@@ -7,6 +8,7 @@ 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
@@ -25,6 +27,11 @@ export default class PrefsPage extends Adw.PreferencesPage {
}, 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);
@@ -37,23 +44,27 @@ export default class PrefsPage extends Adw.PreferencesPage {
* operation is in progress and the user has their cursor either in the
* upper or lower 10% of this widget respectively.
*/
- #setupDNDScroll() {
+ #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());
+ 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();
- // Scroll, when the pointer is in the right places.
+ // 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) {
+ 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) {
+ } 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();
@@ -63,11 +74,9 @@ export default class PrefsPage extends Adw.PreferencesPage {
}
});
- // Make sure scrolling stops, when DND operation ends.
- this._dndEnded = true;
const stopScrollAllAtDNDEnd = () => {
- scrollManager.stopScrollAll();
this._dndEnded = true;
+ scrollManager.stopScrollAll();
};
controller.connect("leave", () => {
stopScrollAllAtDNDEnd();
@@ -76,7 +85,14 @@ export default class PrefsPage extends Adw.PreferencesPage {
// Make use of `this._dndEnded` to setup stopScrollAtDNDEnd only
// once per DND operation.
if (this._dndEnded) {
- let drag = controller.get_drop().get_drag();
+ 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();
});
@@ -93,7 +109,7 @@ export default class PrefsPage extends Adw.PreferencesPage {
this.add_controller(controller);
}
- onRowMove(listBox, row, direction) {
+ onRowMove(listBox: PrefsBoxOrderListBox, row: PrefsBoxOrderItemRow, direction: string): void {
const rowPosition = row.get_index();
if (direction === "up") { // If the direction of the move is up.
diff --git a/src/prefsModules/ScrollManager.js b/src/prefsModules/ScrollManager.ts
similarity index 88%
rename from src/prefsModules/ScrollManager.js
rename to src/prefsModules/ScrollManager.ts
index 07f221e..1d9a8f8 100644
--- a/src/prefsModules/ScrollManager.js
+++ b/src/prefsModules/ScrollManager.ts
@@ -1,23 +1,21 @@
"use strict";
import GLib from "gi://GLib";
+import type Gtk from "gi://Gtk";
export default class ScrollManager {
- #gtkScrolledWindow;
- #scrollUp;
- #scrollDown;
+ #gtkScrolledWindow: Gtk.ScrolledWindow;
+ #scrollUp: boolean;
+ #scrollDown: boolean;
- /**
- * @param {Gtk.ScrolledWindow} gtkScrolledWindow
- */
- constructor(gtkScrolledWindow) {
+ 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 @@ export default class ScrollManager {
});
}
- startScrollDown() {
+ startScrollDown(): void {
// If the scroll down is already started, don't do anything.
if (this.#scrollDown) {
return;
@@ -74,15 +72,15 @@ export default 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"
+ ]
+}