diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..18851e9a
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,177 @@
+include:
+ - remote: 'https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/bbe5232986c9b98eb1efe62484e07216f7d1a4df/templates/fedora.yml'
+ - remote: "https://gitlab.freedesktop.org/freedesktop/ci-templates/-/raw/6f86b8bcb0cd5168c32779c4fea9a893c4a0c046/templates/ci-fairy.yml"
+
+image: registry.gitlab.gnome.org/gnome/gnome-shell/fedora/34:2021-08-12.0
+
+stages:
+ - pre_review
+ - prepare
+ - review
+ - build
+ - deploy
+
+default:
+ # Cancel jobs if newer commits are pushed to the branch
+ interruptible: true
+ # Auto-retry jobs in case of infra failures
+ retry:
+ max: 1
+ when:
+ - 'runner_system_failure'
+ - 'stuck_or_timeout_failure'
+ - 'scheduler_failure'
+ - 'api_failure'
+
+variables:
+ FDO_UPSTREAM_REPO: GNOME/gnome-shell-extensions
+ LINT_LOG: "eslint-report.xml"
+ JS_LOG: "js-report.txt"
+
+workflow:
+ rules:
+ - if: '$CI_MERGE_REQUEST_IID'
+ - if: '$CI_COMMIT_TAG'
+ - if: '$CI_COMMIT_BRANCH'
+
+.pipeline_guard: &pipeline_guard
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+ - if: '$CI_COMMIT_TAG'
+ - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+ - if: '$CI_COMMIT_BRANCH =~ /^gnome-[0-9-]+$/'
+ - when: 'manual'
+
+.gnome-shell-extensions.fedora:34:
+ variables:
+ FDO_DISTRIBUTION_VERSION: 34
+ FDO_DISTRIBUTION_TAG: '2021-08-31.0'
+ FDO_DISTRIBUTION_PACKAGES: >
+ meson git gettext sassc
+
+.prereview_req: &prereview_req
+ needs:
+ - check_commit_log
+ - check-merge-request
+
+check_commit_log:
+ extends:
+ - .fdo.ci-fairy
+ stage: pre_review
+ script:
+ - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
+ then
+ ci-fairy check-commits --junit-xml=commit-message-junit-report.xml ;
+ else
+ echo "Not a merge request" ;
+ fi
+ <<: *pipeline_guard
+ artifacts:
+ expire_in: 1 week
+ paths:
+ - commit-message-junit-report.xml
+ reports:
+ junit: commit-message-junit-report.xml
+
+check-merge-request:
+ extends:
+ - .fdo.ci-fairy
+ stage: pre_review
+ script:
+ - if [[ x"$CI_MERGE_REQUEST_TARGET_BRANCH_NAME" != "x" ]] ;
+ then
+ ci-fairy check-merge-request --require-allow-collaboration --junit-xml=check-merge-request-report.xml ;
+ else
+ echo "Not a merge request" ;
+ fi
+ <<: *pipeline_guard
+ artifacts:
+ expire_in: 1 week
+ paths:
+ - check-merge-request-report.xml
+ reports:
+ junit: check-merge-request-report.xml
+
+build-fedora-container:
+ extends:
+ - .fdo.container-build@fedora@x86_64
+ - .gnome-shell-extensions.fedora:34
+ stage: prepare
+ <<: *prereview_req
+
+js_check:
+ stage: review
+ <<: *prereview_req
+ script:
+ - find extensions -name '*.js' -exec js78 -c '{}' ';' 2>&1 | tee $JS_LOG
+ - (! grep -q . $JS_LOG)
+ artifacts:
+ paths:
+ - ${JS_LOG}
+ when: on_failure
+
+eslint:
+ stage: review
+ <<: *prereview_req
+ script:
+ - eslint -o $LINT_LOG -f junit --resolve-plugins-relative-to $(npm root -g) extensions
+ artifacts:
+ paths:
+ - ${LINT_LOG}
+ reports:
+ junit: ${LINT_LOG}
+
+build-bundles:
+ stage: build
+ <<: *prereview_req
+ script:
+ - ./export-zips.sh
+ artifacts:
+ name: 'Extension bundles'
+ expose_as: 'Get Extension bundles here'
+ paths:
+ - zip-files/
+
+fedora-build:
+ extends:
+ - .fdo.distribution-image@fedora
+ - .gnome-shell-extensions.fedora:34
+ stage: build
+ needs:
+ - build-fedora-container
+ before_script:
+ - git submodule update --init
+ script:
+ - meson setup build --werror -Dextension_set=all -Dclassic_mode=true
+ - meson compile -C build
+ - meson test -C build
+ - meson install -C build
+ artifacts:
+ paths:
+ - build
+
+fedora-dist:
+ extends:
+ - .fdo.distribution-image@fedora
+ - .gnome-shell-extensions.fedora:34
+ stage: deploy
+ needs:
+ - fedora-build
+ before_script:
+ - git submodule update --init
+ script:
+ - meson dist -C build
+ rules:
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
+ changes:
+ - "**/meson.build"
+ - meson/*
+
+fedora-dist-tarball:
+ extends: fedora-dist
+ artifacts:
+ expose_as: 'Get tarball here'
+ paths:
+ - build/meson-dist/$CI_PROJECT_NAME-$CI_COMMIT_TAG.tar.xz
+ rules:
+ - if: '$CI_COMMIT_TAG'
diff --git a/.gitlab-ci/commit-rules.yml b/.gitlab-ci/commit-rules.yml
new file mode 100644
index 00000000..a3913a98
--- /dev/null
+++ b/.gitlab-ci/commit-rules.yml
@@ -0,0 +1,13 @@
+patterns:
+ deny:
+ - regex: '^$CI_MERGE_REQUEST_PROJECT_URL/(-/)?merge_requests/$CI_MERGE_REQUEST_IID$'
+ message: Commit message must not contain a link to its own merge request
+ - regex: '^extensions/'
+ message: Commit message subject should not be prefixed with 'extensions/', use the extension name instead
+ where: subject
+ - regex: '^[^:]+: [a-z]'
+ message: "Commit message subject should be properly Capitalized. E.g. 'window: Marginalize extradicity'"
+ where: subject
+ - regex: '^\S*\.js:'
+ message: Commit message subject prefix should not include .js
+ where: subject
diff --git a/HACKING.md b/HACKING.md
index f8786287..6d87c52c 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -28,4 +28,4 @@ imports (like imports.lang or imports.dbus) and introspection,
the other for Shell API. Within the same group, put everything
in alphabetic order.
-[coding-style]: https://gitlab.gnome.org/GNOME/gjs/blob/master/doc/Style_Guide.md
+[coding-style]: https://gitlab.gnome.org/GNOME/gjs/blob/HEAD/doc/Style_Guide.md
diff --git a/NEWS b/NEWS
index 85f18abe..79c07254 100644
--- a/NEWS
+++ b/NEWS
@@ -1,30 +1,36 @@
-40.4
+41.0
====
-* drive-menu: Fix indicator visibility [Florian; !176]
-* Use distinct gettext domain for e.g.o uploads [Florian; #335]
+* Bump version
+
+41.rc.1
+=======
+* Fix pre-generating stylesheets in tarball [Florian; !190]
Contributors:
Florian Müllner
-
-40.3
-====
-* drive-menu: Improve detection of network mounts [Florian; !27]
-* Misc. bug fixes [Florian; #340]
+41.rc
+=====
+* window-list: Adapt to overview-on-startup [Florian; !185]
+* apps-menu: Use a custom 'toggle-menu' shortcut [Florian; !173]
+* Misc. bug fixes and cleanups [Florian; !186]
Contributors:
Florian Müllner
-40.2
-====
+41.beta
+=======
* window-list: Extend reactive area of minimap to screen edges [Adam; !171]
-* Misc. bug fixes [Florian; !172]
+* drive-menu: Improve detection of network mounts [Florian; !27, !176]
+* Use distinct gettext domain for e.g.o uploads [Florian; #335]
+* Misc. bug fixes and cleanups [Florian; !172, !174, !177, !167, !178, !180,
+ !181, !182, !183]
Contributors:
- Adam Goode, Florian Müllner
+ Marco Trevisan (Treviño), Adam Goode, Florian Müllner
Translators:
- Hugo Carvalho [pt], Juliano de Souza Camargo [pt]
+ Hugo Carvalho [pt], Juliano de Souza Camargo [pt], Alexander Shopov [bg]
40.1
====
diff --git a/README.md b/README.md
index 3b84cb18..4759ce60 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,19 @@ GSettings key.
Adds a simple workspace switcher to the top bar.
+## Default branch
+
+The default development branch is `main`. If you still have a local
+checkout under the old name, use:
+```sh
+git checkout master
+git branch -m master main
+git fetch
+git branch --unset-upstream
+git branch -u origin/master
+git symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main
+```
+
## License
GNOME Shell Extensions are distributed under the terms of the GNU General
diff --git a/data/gnome-classic.scss b/data/gnome-classic.scss
index 750a9f51..51eb2fa8 100644
--- a/data/gnome-classic.scss
+++ b/data/gnome-classic.scss
@@ -84,8 +84,8 @@ $variant: 'light';
}
#appMenu {
- padding: 0 8px 0 8px;
spinner-image: url("classic-process-working.svg");
+ .panel-status-menu-box { padding: 0; }
}
.tile-preview-left.on-primary,
.tile-preview-right.on-primary,
diff --git a/data/gnome-shell-sass/README.md b/data/gnome-shell-sass/README.md
index a5f219ae..443b4dc5 100644
--- a/data/gnome-shell-sass/README.md
+++ b/data/gnome-shell-sass/README.md
@@ -11,6 +11,6 @@ will then be synchronized periodically before releases.
GNOME Shell Sass is distributed under the terms of the GNU General Public
License, version 2 or later. See the [COPYING][license] file for details.
-[shell-subtree]: https://gitlab.gnome.org/GNOME/gnome-shell/tree/master/data/theme/gnome-shell-sass
+[shell-subtree]: https://gitlab.gnome.org/GNOME/gnome-shell/tree/HEAD/data/theme/gnome-shell-sass
[sass-repo]: https://gitlab.gnome.org/GNOME/gnome-shell-sass
[license]: COPYING
diff --git a/data/gnome-shell-sass/_drawing.scss b/data/gnome-shell-sass/_drawing.scss
index d5d959f0..f09eb123 100644
--- a/data/gnome-shell-sass/_drawing.scss
+++ b/data/gnome-shell-sass/_drawing.scss
@@ -46,7 +46,7 @@
border-color: if($fc==$selected_bg_color,
$selected_borders_color,
darken($fc,35%));
- box-shadow: inset 0 0 0 1px $fc;
+ box-shadow: inset 0 0 0 2px $fc;
}
@if $t==hover { }
@if $t==insensitive {
diff --git a/data/gnome-shell-sass/widgets/_app-grid.scss b/data/gnome-shell-sass/widgets/_app-grid.scss
index 626925ab..2df69116 100644
--- a/data/gnome-shell-sass/widgets/_app-grid.scss
+++ b/data/gnome-shell-sass/widgets/_app-grid.scss
@@ -108,8 +108,7 @@ $app_grid_fg_color: #fff;
}
// right-click app menu
-.app-menu,
-.app-well-menu {
+.app-menu {
max-width: 27.25em;
}
diff --git a/data/gnome-shell-sass/widgets/_entries.scss b/data/gnome-shell-sass/widgets/_entries.scss
index 0a43e86f..5a119455 100644
--- a/data/gnome-shell-sass/widgets/_entries.scss
+++ b/data/gnome-shell-sass/widgets/_entries.scss
@@ -3,7 +3,7 @@
StEntry {
border-radius: $base_border_radius;
padding: 8px;
- border-width: 1px;
+ border-width: 0;
color: $fg_color;
@include entry(normal);
//&:hover { @include entry(hover);}
diff --git a/data/gnome-shell-sass/widgets/_message-list.scss b/data/gnome-shell-sass/widgets/_message-list.scss
index 02d122b9..bb9239f4 100644
--- a/data/gnome-shell-sass/widgets/_message-list.scss
+++ b/data/gnome-shell-sass/widgets/_message-list.scss
@@ -25,6 +25,20 @@
// NOTE: remove the padding if notification_bubble could remove margin for drop shadow
padding: $base_margin;
spacing: $base_spacing * 2;
+
+ .dnd-button {
+ // We need this because the focus outline isn't inset like for the buttons
+ // so the dnd button would grow when it gets focus if we didn't change only
+ // its color when focusing.
+ border-width: 2px;
+ border-color: transparent;
+ border-radius: 99px;
+ border-style: solid;
+
+ &:focus {
+ border-color: transparentize($selected_bg_color, 0.4);
+ }
+ }
}
// message bubbles
diff --git a/data/gnome-shell-sass/widgets/_search-entry.scss b/data/gnome-shell-sass/widgets/_search-entry.scss
index b184e674..d3584fb9 100644
--- a/data/gnome-shell-sass/widgets/_search-entry.scss
+++ b/data/gnome-shell-sass/widgets/_search-entry.scss
@@ -10,19 +10,16 @@ $search_entry_height: 36px;
border-radius: $search_entry_height * 0.5; // half the height
color: transparentize($fg_color,0.3);
background-color: $bg_color;
- border-color: $borders_color;
margin-top: $base_spacing * 2;
margin-bottom: $base_spacing;
-
+ border-width: 2px;
+ border-color: transparent;
&:hover {
background-color: $hover_bg_color;
- border-color: $hover_borders_color;
color: $hover_fg_color;
}
&:focus {
- padding: $base_padding $base_padding+2; // 1px less to account for wider border
- border-width: 2px;
border-style: solid;
border-color: $selected_bg_color;
color: $fg_color;
diff --git a/data/gnome-shell-sass/widgets/_window-picker.scss b/data/gnome-shell-sass/widgets/_window-picker.scss
index b3b93f29..fa3f1919 100644
--- a/data/gnome-shell-sass/widgets/_window-picker.scss
+++ b/data/gnome-shell-sass/widgets/_window-picker.scss
@@ -48,6 +48,5 @@ $window_close_button_padding: 3px;
.workspace-background {
// keep in sync with BACKGROUND_CORNER_RADIUS_PIXELS in workspace.js
border-radius: 30px;
- background-color: $osd_bg_color;
box-shadow: 0 4px 16px 4px transparentize(darken($osd_bg_color, 30%), 0.7);
}
diff --git a/export-zips.sh b/export-zips.sh
index 30991449..1c3cc0d7 100755
--- a/export-zips.sh
+++ b/export-zips.sh
@@ -7,7 +7,7 @@ builddir=`mktemp -p $srcdir -d _build.XXXXXX` || exit 1
installdir=`mktemp -p $srcdir -d _install.XXXXXX` || exit 1
meson setup --prefix=$installdir -Dextension_set=all $srcdir $builddir
-ninja -C$builddir install
+meson install -C $builddir
rm -rf $srcdir/zip-files
mkdir $srcdir/zip-files
diff --git a/extensions/apps-menu/extension.js b/extensions/apps-menu/extension.js
index e36b0fe6..2cbb7548 100644
--- a/extensions/apps-menu/extension.js
+++ b/extensions/apps-menu/extension.js
@@ -12,9 +12,7 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
-const Me = ExtensionUtils.getCurrentExtension();
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
const appSys = Shell.AppSystem.get_default();
@@ -380,9 +378,12 @@ class ApplicationsButton extends PanelMenu.Button {
this._hidingId = Main.overview.connect('hiding', () => {
this.remove_accessible_state(Atk.StateType.CHECKED);
});
- Main.layoutManager.connect('startup-complete',
- this._setKeybinding.bind(this));
- this._setKeybinding();
+ Main.wm.addKeybinding(
+ 'apps-menu-toggle-menu',
+ ExtensionUtils.getSettings(),
+ Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
+ Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
+ () => this.menu.toggle());
this._desktopTarget = new DesktopTarget();
this._desktopTarget.connect('app-dropped', () => {
@@ -433,11 +434,7 @@ class ApplicationsButton extends PanelMenu.Button {
this._tree.disconnect(this._treeChangedId);
this._tree = null;
- Main.wm.setCustomKeybindingHandler('panel-main-menu',
- Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
- Main.sessionMode.hasOverview
- ? Main.overview.toggle.bind(Main.overview)
- : null);
+ Main.wm.removeKeybinding('apps-menu-toggle-menu');
this._desktopTarget.destroy();
}
@@ -479,12 +476,6 @@ class ApplicationsButton extends PanelMenu.Button {
super._onOpenStateChanged(menu, open);
}
- _setKeybinding() {
- Main.wm.setCustomKeybindingHandler('panel-main-menu',
- Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
- () => this.menu.toggle());
- }
-
_redisplay() {
this.applicationsBox.destroy_all_children();
this.categoriesBox.destroy_all_children();
@@ -674,17 +665,20 @@ class ApplicationsButton extends PanelMenu.Button {
let appsMenuButton;
+/** */
function enable() {
appsMenuButton = new ApplicationsButton();
let index = Main.sessionMode.panel.left.indexOf('activities') + 1;
Main.panel.addToStatusArea('apps-menu', appsMenuButton, index, 'left');
}
+/** */
function disable() {
Main.panel.menuManager.removeMenu(appsMenuButton.menu);
appsMenuButton.destroy();
}
+/** */
function init() {
ExtensionUtils.initTranslations();
}
diff --git a/extensions/apps-menu/meson.build b/extensions/apps-menu/meson.build
index 48504f63..164b95ea 100644
--- a/extensions/apps-menu/meson.build
+++ b/extensions/apps-menu/meson.build
@@ -3,3 +3,4 @@ extension_data += configure_file(
output: metadata_name,
configuration: metadata_conf
)
+extension_schemas += files(metadata_conf.get('gschemaname') + '.gschema.xml')
diff --git a/extensions/apps-menu/metadata.json.in b/extensions/apps-menu/metadata.json.in
index f57a47f3..b00cd3b3 100644
--- a/extensions/apps-menu/metadata.json.in
+++ b/extensions/apps-menu/metadata.json.in
@@ -1,6 +1,7 @@
{
"extension-id": "@extension_id@",
"uuid": "@uuid@",
+"settings-schema": "@gschemaname@",
"gettext-domain": "@gettext_domain@",
"name": "Applications Menu",
"description": "Add a category-based menu for applications.\nThis extension is part of Classic Mode and is officially supported by GNOME. Please do not report bugs using the form below, use GNOME's GitLab instance instead.",
diff --git a/extensions/apps-menu/org.gnome.shell.extensions.apps-menu.gschema.xml b/extensions/apps-menu/org.gnome.shell.extensions.apps-menu.gschema.xml
new file mode 100644
index 00000000..b0cdb862
--- /dev/null
+++ b/extensions/apps-menu/org.gnome.shell.extensions.apps-menu.gschema.xml
@@ -0,0 +1,12 @@
+
+
+
diff --git a/extensions/auto-move-windows/extension.js b/extensions/auto-move-windows/extension.js
index 3859809b..72fed2ec 100644
--- a/extensions/auto-move-windows/extension.js
+++ b/extensions/auto-move-windows/extension.js
@@ -108,10 +108,14 @@ class WindowMover {
let prevCheckWorkspaces;
let winMover;
+/** */
function init() {
ExtensionUtils.initTranslations();
}
+/**
+ * @returns {bool} - false (used as MetaLater handler)
+ */
function myCheckWorkspaces() {
let keepAliveWorkspaces = [];
let foundNonEmpty = false;
@@ -132,6 +136,7 @@ function myCheckWorkspaces() {
return false;
}
+/** */
function enable() {
prevCheckWorkspaces = Main.wm._workspaceTracker._checkWorkspaces;
Main.wm._workspaceTracker._checkWorkspaces = myCheckWorkspaces;
@@ -139,6 +144,7 @@ function enable() {
winMover = new WindowMover();
}
+/** */
function disable() {
Main.wm._workspaceTracker._checkWorkspaces = prevCheckWorkspaces;
winMover.destroy();
diff --git a/extensions/auto-move-windows/prefs.js b/extensions/auto-move-windows/prefs.js
index 2c529067..9e303dad 100644
--- a/extensions/auto-move-windows/prefs.js
+++ b/extensions/auto-move-windows/prefs.js
@@ -5,10 +5,8 @@
const { Gio, GLib, GObject, Gtk, Pango } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
-const Me = ExtensionUtils.getCurrentExtension();
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
const SETTINGS_KEY = 'application-list';
@@ -267,10 +265,14 @@ class NewRuleDialog extends Gtk.AppChooserDialog {
}
});
+/** */
function init() {
ExtensionUtils.initTranslations();
}
+/**
+ * @returns {Gtk.Widget} - the prefs widget
+ */
function buildPrefsWidget() {
return new AutoMoveSettingsWidget();
}
diff --git a/extensions/drive-menu/extension.js b/extensions/drive-menu/extension.js
index fd3a9214..cc31e777 100644
--- a/extensions/drive-menu/extension.js
+++ b/extensions/drive-menu/extension.js
@@ -8,9 +8,7 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ShellMountOperation = imports.ui.shellMountOperation;
-const Me = ExtensionUtils.getCurrentExtension();
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
var MountMenuItem = GObject.registerClass(
class MountMenuItem extends PopupMenu.PopupBaseMenuItem {
@@ -218,17 +216,20 @@ class DriveMenu extends PanelMenu.Button {
}
});
+/** */
function init() {
ExtensionUtils.initTranslations();
}
let _indicator;
+/** */
function enable() {
_indicator = new DriveMenu();
Main.panel.addToStatusArea('drive-menu', _indicator);
}
+/** */
function disable() {
_indicator.destroy();
}
diff --git a/extensions/launch-new-instance/extension.js b/extensions/launch-new-instance/extension.js
index 12a8df28..a249cd48 100644
--- a/extensions/launch-new-instance/extension.js
+++ b/extensions/launch-new-instance/extension.js
@@ -3,6 +3,7 @@ const AppDisplay = imports.ui.appDisplay;
let _activateOriginal = null;
+/** */
function enable() {
_activateOriginal = AppDisplay.AppIcon.prototype.activate;
AppDisplay.AppIcon.prototype.activate = function () {
@@ -10,6 +11,7 @@ function enable() {
};
}
+/** */
function disable() {
AppDisplay.AppIcon.prototype.activate = _activateOriginal;
}
diff --git a/extensions/native-window-placement/extension.js b/extensions/native-window-placement/extension.js
index 018a7421..35c31a41 100644
--- a/extensions/native-window-placement/extension.js
+++ b/extensions/native-window-placement/extension.js
@@ -238,11 +238,13 @@ class NaturalLayoutStrategy extends Workspace.LayoutStrategy {
let winInjections, workspaceInjections;
+/** */
function resetState() {
winInjections = { };
workspaceInjections = { };
}
+/** */
function enable() {
resetState();
@@ -282,6 +284,11 @@ function enable() {
};
}
+/**
+ * @param {Object} object - object that was modified
+ * @param {Object} injection - the map of previous injections
+ * @param {string} name - the @injection key that should be removed
+ */
function removeInjection(object, injection, name) {
if (injection[name] === undefined)
delete object[name];
@@ -289,6 +296,7 @@ function removeInjection(object, injection, name) {
object[name] = injection[name];
}
+/** */
function disable() {
var i;
diff --git a/extensions/places-menu/extension.js b/extensions/places-menu/extension.js
index e3b04d6d..3fe9d9fe 100644
--- a/extensions/places-menu/extension.js
+++ b/extensions/places-menu/extension.js
@@ -9,13 +9,11 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Me = ExtensionUtils.getCurrentExtension();
-
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
-const N_ = x => x;
-
const PlaceDisplay = Me.imports.placeDisplay;
+const _ = ExtensionUtils.gettext;
+const N_ = x => x;
+
const PLACE_ICON_SIZE = 16;
var PlaceMenuItem = GObject.registerClass(
@@ -134,12 +132,14 @@ class PlacesMenu extends PanelMenu.Button {
}
});
+/** */
function init() {
ExtensionUtils.initTranslations();
}
let _indicator;
+/** */
function enable() {
_indicator = new PlacesMenu();
@@ -149,6 +149,7 @@ function enable() {
Main.panel.addToStatusArea('places-menu', _indicator, pos, 'left');
}
+/** */
function disable() {
_indicator.destroy();
}
diff --git a/extensions/places-menu/placeDisplay.js b/extensions/places-menu/placeDisplay.js
index d2afc1ea..05b22804 100644
--- a/extensions/places-menu/placeDisplay.js
+++ b/extensions/places-menu/placeDisplay.js
@@ -7,10 +7,7 @@ const ExtensionUtils = imports.misc.extensionUtils;
const Main = imports.ui.main;
const ShellMountOperation = imports.ui.shellMountOperation;
-const Me = ExtensionUtils.getCurrentExtension();
-
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
const N_ = x => x;
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
diff --git a/extensions/screenshot-window-sizer/extension.js b/extensions/screenshot-window-sizer/extension.js
index 1a7634cd..997bb700 100644
--- a/extensions/screenshot-window-sizer/extension.js
+++ b/extensions/screenshot-window-sizer/extension.js
@@ -28,11 +28,15 @@ const MESSAGE_FADE_TIME = 2000;
let text;
+/** */
function hideMessage() {
text.destroy();
text = null;
}
+/**
+ * @param {string} message - the message to flash
+ */
function flashMessage(message) {
if (!text) {
text = new St.Label({ style_class: 'screenshot-sizer-message' });
@@ -67,6 +71,11 @@ let SIZES = [
[720, 360], // Phone landscape fullscreen
];
+/**
+ * @param {Meta.Display} display - the display
+ * @param {Meta.Window=} window - for per-window bindings, the window
+ * @param {Meta.KeyBinding} binding - the key binding
+ */
function cycleScreenshotSizes(display, window, binding) {
// Probably this isn't useful with 5 sizes, but you can decrease instead
// of increase by holding down shift.
@@ -133,6 +142,7 @@ function cycleScreenshotSizes(display, window, binding) {
flashMessage(message);
}
+/** */
function enable() {
Main.wm.addKeybinding(
'cycle-screenshot-sizes',
@@ -148,6 +158,7 @@ function enable() {
cycleScreenshotSizes);
}
+/** */
function disable() {
Main.wm.removeKeybinding('cycle-screenshot-sizes');
Main.wm.removeKeybinding('cycle-screenshot-sizes-backward');
diff --git a/extensions/user-theme/extension.js b/extensions/user-theme/extension.js
index 99866268..f54f7571 100644
--- a/extensions/user-theme/extension.js
+++ b/extensions/user-theme/extension.js
@@ -58,6 +58,9 @@ class ThemeManager {
}
}
+/**
+ * @returns {ThemeManager} - the extension state object
+ */
function init() {
return new ThemeManager();
}
diff --git a/extensions/user-theme/prefs.js b/extensions/user-theme/prefs.js
index b9c12fa0..214da4a6 100644
--- a/extensions/user-theme/prefs.js
+++ b/extensions/user-theme/prefs.js
@@ -174,9 +174,13 @@ class ThemeRow extends Gtk.ListBoxRow {
}
});
+/** */
function init() {
}
+/**
+ * @returns {Gtk.Widget} - the prefs widget
+ */
function buildPrefsWidget() {
return new UserThemePrefsWidget();
}
diff --git a/extensions/user-theme/util.js b/extensions/user-theme/util.js
index e57a99f0..cb000a76 100644
--- a/extensions/user-theme/util.js
+++ b/extensions/user-theme/util.js
@@ -3,6 +3,9 @@ const { GLib } = imports.gi;
const fn = (...args) => GLib.build_filenamev(args);
+/**
+ * @returns {string[]} - an ordered list of theme directories
+ */
function getThemeDirs() {
return [
fn(GLib.get_home_dir(), '.themes'),
@@ -11,6 +14,9 @@ function getThemeDirs() {
];
}
+/**
+ * @returns {string[]} - an ordered list of mode theme directories
+ */
function getModeThemeDirs() {
return GLib.get_system_data_dirs()
.map(dir => fn(dir, 'gnome-shell', 'theme'));
diff --git a/extensions/window-list/extension.js b/extensions/window-list/extension.js
index f7ecd190..b97a2820 100644
--- a/extensions/window-list/extension.js
+++ b/extensions/window-list/extension.js
@@ -11,8 +11,7 @@ const Me = ExtensionUtils.getCurrentExtension();
const { WindowPicker, WindowPickerToggle } = Me.imports.windowPicker;
const { WorkspaceIndicator } = Me.imports.workspaceIndicator;
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
const ICON_TEXTURE_SIZE = 24;
const DND_ACTIVATE_TIMEOUT = 500;
@@ -23,34 +22,10 @@ const GroupingMode = {
ALWAYS: 2,
};
-
-function _minimizeOrActivateWindow(window) {
- let focusWindow = global.display.focus_window;
- if (focusWindow === window ||
- focusWindow && focusWindow.get_transient_for() === window)
- window.minimize();
- else
- window.activate(global.get_current_time());
-}
-
-function _openMenu(menu) {
- menu.open();
-
- let event = Clutter.get_current_event();
- if (event && event.type() === Clutter.EventType.KEY_RELEASE)
- menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
-}
-
-function _onMenuStateChanged(menu, isOpen) {
- if (isOpen)
- return;
-
- let [x, y] = global.get_pointer();
- let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
- if (Me.stateObj.someWindowListContains(actor))
- actor.sync_hover();
-}
-
+/**
+ * @param {Shell.App} app - an app
+ * @returns {number} - the smallest stable sequence of the app's windows
+ */
function _getAppStableSequence(app) {
let windows = app.get_windows().filter(w => !w.skip_taskbar);
return windows.reduce((prev, cur) => {
@@ -58,7 +33,6 @@ function _getAppStableSequence(app) {
}, Infinity);
}
-
class WindowContextMenu extends PopupMenu.PopupMenu {
constructor(source, metaWindow) {
super(source, 0.5, St.Side.BOTTOM);
@@ -282,10 +256,37 @@ const BaseButton = GObject.registerClass({
return true;
}
+ _openMenu(menu) {
+ menu.open();
+
+ let event = Clutter.get_current_event();
+ if (event && event.type() === Clutter.EventType.KEY_RELEASE)
+ menu.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
+ }
+
+ _minimizeOrActivateWindow(window) {
+ let focusWindow = global.display.focus_window;
+ if (focusWindow === window ||
+ focusWindow && focusWindow.get_transient_for() === window)
+ window.minimize();
+ else
+ window.activate(global.get_current_time());
+ }
+
+ _onMenuStateChanged(menu, isOpen) {
+ if (isOpen)
+ return;
+
+ let [x, y] = global.get_pointer();
+ let actor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
+ if (Me.stateObj.someWindowListContains(actor))
+ actor.sync_hover();
+ }
+
_onPopupMenu(_actor) {
if (!this._canOpenPopupMenu() || this._contextMenu.isOpen)
return;
- _openMenu(this._contextMenu);
+ this._openMenu(this._contextMenu);
}
_isFocused() {
@@ -362,7 +363,8 @@ class WindowButton extends BaseButton {
this.label_actor = this._windowTitle.label_actor;
this._contextMenu = new WindowContextMenu(this, this.metaWindow);
- this._contextMenu.connect('open-state-changed', _onMenuStateChanged);
+ this._contextMenu.connect('open-state-changed',
+ this._onMenuStateChanged.bind(this));
this._contextMenu.actor.hide();
this._contextMenuManager.addMenu(this._contextMenu);
Main.uiGroup.add_actor(this._contextMenu.actor);
@@ -382,9 +384,9 @@ class WindowButton extends BaseButton {
}
if (button === 1)
- _minimizeOrActivateWindow(this.metaWindow);
+ this._minimizeOrActivateWindow(this.metaWindow);
else
- _openMenu(this._contextMenu);
+ this._openMenu(this._contextMenu);
}
_isFocused() {
@@ -518,14 +520,16 @@ class AppButton extends BaseButton {
this._menuManager = new PopupMenu.PopupMenuManager(this);
this._menu = new PopupMenu.PopupMenu(this, 0.5, St.Side.BOTTOM);
- this._menu.connect('open-state-changed', _onMenuStateChanged);
+ this._menu.connect('open-state-changed',
+ this._onMenuStateChanged.bind(this));
this._menu.actor.hide();
this._menu.connect('activate', this._onMenuActivate.bind(this));
this._menuManager.addMenu(this._menu);
Main.uiGroup.add_actor(this._menu.actor);
this._appContextMenu = new AppContextMenu(this);
- this._appContextMenu.connect('open-state-changed', _onMenuStateChanged);
+ this._appContextMenu.connect('open-state-changed',
+ this._onMenuStateChanged.bind(this));
this._appContextMenu.actor.hide();
Main.uiGroup.add_actor(this._appContextMenu.actor);
@@ -592,7 +596,7 @@ class AppButton extends BaseButton {
this._singleWindowTitle.child = this._windowTitle;
this._windowContextMenu = new WindowContextMenu(this, this.metaWindow);
this._windowContextMenu.connect(
- 'open-state-changed', _onMenuStateChanged);
+ 'open-state-changed', this._onMenuStateChanged.bind(this));
Main.uiGroup.add_actor(this._windowContextMenu.actor);
this._windowContextMenu.actor.hide();
this._contextMenuManager.addMenu(this._windowContextMenu);
@@ -631,7 +635,7 @@ class AppButton extends BaseButton {
if (windows.length === 1) {
if (contextMenuWasOpen)
return;
- _minimizeOrActivateWindow(windows[0]);
+ this._minimizeOrActivateWindow(windows[0]);
} else {
this._menu.removeAll();
@@ -642,12 +646,12 @@ class AppButton extends BaseButton {
item._window = windows[i];
this._menu.addMenuItem(item);
}
- _openMenu(this._menu);
+ this._openMenu(this._menu);
}
} else {
if (contextMenuWasOpen)
return;
- _openMenu(this._contextMenu);
+ this._openMenu(this._contextMenu);
}
}
@@ -774,13 +778,16 @@ class WindowList extends St.Widget {
this._updateKeyboardAnchor();
});
- this._overviewHidingId = Main.overview.connect('hiding', () => {
+ this._overviewHidingId = Main.overview.connect('hidden', () => {
this.visible = !Main.layoutManager.primaryMonitor.inFullscreen;
this._updateKeyboardAnchor();
});
this._fullscreenChangedId =
global.display.connect('in-fullscreen-changed', () => {
+ // Work-around for initial change from unknown to !fullscreen
+ if (Main.overview.visible)
+ this.hide();
this._updateKeyboardAnchor();
});
@@ -1170,6 +1177,9 @@ class Extension {
}
}
+/**
+ * @returns {Extension} - the extension's state object
+ */
function init() {
return new Extension();
}
diff --git a/extensions/window-list/prefs.js b/extensions/window-list/prefs.js
index aec8cc9d..0dbf7a6c 100644
--- a/extensions/window-list/prefs.js
+++ b/extensions/window-list/prefs.js
@@ -1,15 +1,13 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
/* exported init buildPrefsWidget */
-const { Gio, GObject, Gtk } = imports.gi;
+const { Gio, GLib, GObject, Gtk } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
-const Me = ExtensionUtils.getCurrentExtension();
-
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
+/** */
function init() {
ExtensionUtils.initTranslations();
}
@@ -27,6 +25,17 @@ class WindowListPrefsWidget extends Gtk.Box {
halign: Gtk.Align.CENTER,
});
+ this._actionGroup = new Gio.SimpleActionGroup();
+ this.insert_action_group('window-list', this._actionGroup);
+
+ this._settings = ExtensionUtils.getSettings();
+ this._actionGroup.add_action(
+ this._settings.create_action('grouping-mode'));
+ this._actionGroup.add_action(
+ this._settings.create_action('show-on-all-monitors'));
+ this._actionGroup.add_action(
+ this._settings.create_action('display-all-workspaces'));
+
let groupingLabel = '%s'.format(_('Window Grouping'));
this.append(new Gtk.Label({
label: groupingLabel, use_markup: true,
@@ -50,61 +59,39 @@ class WindowListPrefsWidget extends Gtk.Box {
context.add_class('frame');
context.add_class('view');
- this._settings = ExtensionUtils.getSettings();
- let currentMode = this._settings.get_string('grouping-mode');
- let range = this._settings.get_range('grouping-mode');
- let modes = range.deep_unpack()[1].deep_unpack();
-
- let modeLabels = {
- 'never': _('Never group windows'),
- 'auto': _('Group windows when space is limited'),
- 'always': _('Always group windows'),
- };
-
- let radio = null;
- let currentRadio = null;
- for (let i = 0; i < modes.length; i++) {
- let mode = modes[i];
- let label = modeLabels[mode];
- if (!label) {
- log('Unhandled option "%s" for grouping-mode'.format(mode));
- continue;
- }
-
- radio = new Gtk.CheckButton({
- active: !i,
+ const modes = [
+ { mode: 'never', label: _('Never group windows') },
+ { mode: 'auto', label: _('Group windows when space is limited') },
+ { mode: 'always', label: _('Always group windows') },
+ ];
+ let group = null;
+ for (const { mode, label } of modes) {
+ const check = new Gtk.CheckButton({
+ action_name: 'window-list.grouping-mode',
+ action_target: new GLib.Variant('s', mode),
label,
- group: radio,
+ group,
margin_end: 12,
});
- box.append(radio);
-
- if (currentMode === mode)
- currentRadio = radio;
-
- radio.connect('toggled', button => {
- if (button.active)
- this._settings.set_string('grouping-mode', mode);
- });
+ group = check;
+ box.append(check);
}
- if (currentRadio)
- currentRadio.active = true;
-
- let check = new Gtk.CheckButton({
+ this.append(new Gtk.CheckButton({
label: _('Show on all monitors'),
- });
- this._settings.bind('show-on-all-monitors', check, 'active', Gio.SettingsBindFlags.DEFAULT);
- this.append(check);
+ action_name: 'window-list.show-on-all-monitors',
+ }));
- check = new Gtk.CheckButton({
+ this.append(new Gtk.CheckButton({
label: _('Show windows from all workspaces'),
- });
- this._settings.bind('display-all-workspaces', check, 'active', Gio.SettingsBindFlags.DEFAULT);
- this.append(check);
+ action_name: 'window-list.display-all-workspaces',
+ }));
}
});
+/**
+ * @returns {Gtk.Widget} - the prefs widget
+ */
function buildPrefsWidget() {
return new WindowListPrefsWidget();
}
diff --git a/extensions/window-list/workspaceIndicator.js b/extensions/window-list/workspaceIndicator.js
index cdfe5b61..06646cc9 100644
--- a/extensions/window-list/workspaceIndicator.js
+++ b/extensions/window-list/workspaceIndicator.js
@@ -7,10 +7,7 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
-const Me = ExtensionUtils.getCurrentExtension();
-
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
const TOOLTIP_OFFSET = 6;
const TOOLTIP_ANIMATION_TIME = 150;
diff --git a/extensions/windowsNavigator/extension.js b/extensions/windowsNavigator/extension.js
index bb72d7bb..a2cab423 100644
--- a/extensions/windowsNavigator/extension.js
+++ b/extensions/windowsNavigator/extension.js
@@ -262,6 +262,9 @@ class Extension {
}
}
+/**
+ * @returns {Extension} - the extension's state object
+ */
function init() {
return new Extension();
}
diff --git a/extensions/workspace-indicator/extension.js b/extensions/workspace-indicator/extension.js
index 6974062b..a2c95b6a 100644
--- a/extensions/workspace-indicator/extension.js
+++ b/extensions/workspace-indicator/extension.js
@@ -9,10 +9,7 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
-const Me = ExtensionUtils.getCurrentExtension();
-
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
const WORKSPACE_KEY = 'workspace-names';
@@ -445,17 +442,20 @@ class WorkspaceIndicator extends PanelMenu.Button {
}
});
+/** */
function init() {
ExtensionUtils.initTranslations();
}
let _indicator;
+/** */
function enable() {
_indicator = new WorkspaceIndicator();
Main.panel.addToStatusArea('workspace-indicator', _indicator);
}
+/** */
function disable() {
_indicator.destroy();
}
diff --git a/extensions/workspace-indicator/prefs.js b/extensions/workspace-indicator/prefs.js
index 567f3e99..807ab7bb 100644
--- a/extensions/workspace-indicator/prefs.js
+++ b/extensions/workspace-indicator/prefs.js
@@ -4,10 +4,8 @@
const { Gio, GLib, GObject, Gtk, Pango } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
-const Me = ExtensionUtils.getCurrentExtension();
-const Gettext = imports.gettext.domain(Me.metadata['gettext-domain']);
-const _ = Gettext.gettext;
+const _ = ExtensionUtils.gettext;
const N_ = e => e;
const WORKSPACE_SCHEMA = 'org.gnome.desktop.wm.preferences';
@@ -210,10 +208,14 @@ class NewWorkspaceRow extends Gtk.ListBoxRow {
}
});
+/** */
function init() {
ExtensionUtils.initTranslations();
}
+/**
+ * @returns {Gtk.Widget} - the prefs widget
+ */
function buildPrefsWidget() {
return new WorkspaceSettingsWidget();
}
diff --git a/lint/eslintrc-gjs.yml b/lint/eslintrc-gjs.yml
index 13114fa7..dadf40bd 100644
--- a/lint/eslintrc-gjs.yml
+++ b/lint/eslintrc-gjs.yml
@@ -1,8 +1,12 @@
---
# SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
+# SPDX-FileCopyrightText: 2018 Claudio André
env:
es6: true
+ es2020: true
extends: 'eslint:recommended'
+plugins:
+ - jsdoc
rules:
array-bracket-newline:
- error
@@ -60,6 +64,17 @@ rules:
- 'CallExpression[callee.object.name=GObject][callee.property.name=registerClass] > ClassExpression:first-child'
# Allow dedenting chained member expressions
MemberExpression: 'off'
+ jsdoc/check-alignment: error
+ jsdoc/check-param-names: error
+ jsdoc/check-tag-names: error
+ jsdoc/check-types: error
+ jsdoc/implements-on-classes: error
+ jsdoc/newline-after-description: error
+ jsdoc/require-jsdoc: error
+ jsdoc/require-param: error
+ jsdoc/require-param-description: error
+ jsdoc/require-param-name: error
+ jsdoc/require-param-type: error
key-spacing:
- error
- beforeColon: false
@@ -107,8 +122,15 @@ rules:
no-octal-escape: error
no-proto: error
no-prototype-builtins: 'off'
+ no-restricted-globals: [error, window]
no-restricted-properties:
- error
+ - object: imports
+ property: format
+ message: Use template strings
+ - object: pkg
+ property: initFormat
+ message: Use template strings
- object: Lang
property: copyProperties
message: Use Object.assign()
@@ -167,6 +189,7 @@ rules:
object-curly-newline:
- error
- consistent: true
+ multiline: true
object-curly-spacing: error
object-shorthand: error
operator-assignment: error
@@ -214,14 +237,14 @@ rules:
template-curly-spacing: error
template-tag-spacing: error
unicode-bom: error
- valid-jsdoc:
- - error
- - requireReturn: false
wrap-iife:
- error
- inside
yield-star-spacing: error
yoda: error
+settings:
+ jsdoc:
+ mode: typescript
globals:
ARGV: readonly
Debugger: readonly
@@ -233,5 +256,8 @@ globals:
logError: readonly
print: readonly
printerr: readonly
+ window: readonly
+ TextEncoder: readonly
+ TextDecoder: readonly
parserOptions:
ecmaVersion: 2020
diff --git a/meson.build b/meson.build
index 1ecdc71e..8f2afda3 100644
--- a/meson.build
+++ b/meson.build
@@ -1,6 +1,6 @@
project('gnome-shell-extensions',
- version: '40.4',
- meson_version: '>= 0.44.0',
+ version: '41.0',
+ meson_version: '>= 0.53.0',
license: 'GPL2+'
)
@@ -89,3 +89,6 @@ subdir('extensions')
subdir('po')
meson.add_dist_script('meson/generate-stylesheets.py')
+meson.add_dist_script('meson/check-version.py',
+ meson.project_version(),
+ 'NEWS')
diff --git a/meson/check-version.py b/meson/check-version.py
new file mode 100755
index 00000000..81750c99
--- /dev/null
+++ b/meson/check-version.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python3
+
+import os, sys
+from pathlib import Path
+import argparse, subprocess
+
+def check_version(version, file, type='news'):
+ if type == 'news':
+ line = file.open().readline()
+ ok = line.startswith(version)
+ print("{}: {}".format(file, "OK" if ok else "FAILED"))
+ if not ok:
+ raise Exception("{} does not start with {}".format(file, version))
+ elif type == 'metainfo':
+ subprocess.run(['appstream-util', 'validate-version', file, version],
+ check=True)
+ else:
+ raise Exception('Not implemented')
+
+parser = argparse.ArgumentParser(description='Check release version information.')
+parser.add_argument('--type', choices=['metainfo','news'], default='news')
+parser.add_argument('version', help='the version to check for')
+parser.add_argument('files', nargs='+', help='files to check')
+args = parser.parse_args()
+
+distroot = os.environ.get('MESON_DIST_ROOT', './')
+
+try:
+ for file in args.files:
+ check_version(args.version, Path(distroot, file), args.type)
+except:
+ sys.exit(1)
diff --git a/meson/generate-stylesheets.py b/meson/generate-stylesheets.py
index 6e402b61..599d098f 100644
--- a/meson/generate-stylesheets.py
+++ b/meson/generate-stylesheets.py
@@ -10,4 +10,4 @@ distroot = os.environ.get('MESON_DIST_ROOT')
stylesheet_path = PurePath('data/gnome-classic.css')
src = PurePath(sourceroot, stylesheet_path.with_suffix('.scss'))
dst = PurePath(distroot, stylesheet_path)
-subprocess.call(['sassc', '-a', src, dst])
+subprocess.run(['sassc', '-a', src, dst], check=True)
diff --git a/po/bg.po b/po/bg.po
index 7dd9f5ca..3f15896a 100644
--- a/po/bg.po
+++ b/po/bg.po
@@ -1,359 +1,263 @@
-# Bulgarian translation for gnome-shell-extensions po-file.
-# Copyright (C) 2014, 2015, 2017 Free Software Foundation, Inc.
-# This file is distributed under the same license as the gnome-shell-extensions package.
-# Ivaylo Valkov , 2014.
-# Alexander Shopov , 2014, 2015.
-# Lyubomir Vasilev , 2017.
-msgid ""
-msgstr ""
-"Project-Id-Version: gnome-shell-extensions master\n"
-"Report-Msgid-Bugs-To: https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-"
-"shell&keywords=I18N+L10N&component=extensions\n"
-"POT-Creation-Date: 2017-09-09 15:20+0000\n"
-"PO-Revision-Date: 2017-09-08 08:47+0300\n"
-"Last-Translator: Lyubomir Vasilev \n"
-"Language-Team: Bulgarian \n"
-"Language: bg\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: data/gnome-classic.desktop.in:3 data/gnome-classic.session.desktop.in:3
-msgid "GNOME Classic"
-msgstr "Класически GNOME"
-
-#: data/gnome-classic.desktop.in:4
-msgid "This session logs you into GNOME Classic"
-msgstr "Работната среда изглежда като класическия GNOME (2.x)"
-
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:7
-msgid "Attach modal dialog to the parent window"
-msgstr "Прикрепяне на модалните прозорци към родителските им прозорци"
-
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:8
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:25
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:33
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:41
-msgid ""
-"This key overrides the key in org.gnome.mutter when running GNOME Shell."
-msgstr ""
-"Този ключ при е с по-голям приоритет от „org.gnome.mutter“ при изпълнението "
-"на обвивката на GNOME."
-
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:16
-msgid "Arrangement of buttons on the titlebar"
-msgstr "Подредба на бутоните на заглавната лента"
-
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:17
-msgid ""
-"This key overrides the key in org.gnome.desktop.wm.preferences when running "
-"GNOME Shell."
-msgstr ""
-"Този ключ при е с по-голям приоритет от „org.gnome.desktop.wm.preferences“ "
-"при изпълнението на обвивката на GNOME."
-
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:24
-msgid "Enable edge tiling when dropping windows on screen edges"
-msgstr ""
-"Включване на специална подредба при приближаване на прозорец до ръбовете на "
-"екрана"
-
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:32
-msgid "Workspaces only on primary monitor"
-msgstr "Работни плотове само на основния екран"
-
-#: data/org.gnome.shell.extensions.classic-overrides.gschema.xml:40
-msgid "Delay focus changes in mouse mode until the pointer stops moving"
-msgstr "Забавяне на смяната на фокуса до спирането на движението на показалеца"
-
-#: extensions/alternate-tab/prefs.js:20
-msgid "Thumbnail only"
-msgstr "Само миниатюри"
-
-#: extensions/alternate-tab/prefs.js:21
-msgid "Application icon only"
-msgstr "Само икони на приложенията"
-
-#: extensions/alternate-tab/prefs.js:22
-msgid "Thumbnail and application icon"
-msgstr "Миниатюри и икони на приложенията"
-
-#: extensions/alternate-tab/prefs.js:38
-msgid "Present windows as"
-msgstr "Показване на прозорците като"
-
-#: extensions/alternate-tab/prefs.js:69
-msgid "Show only windows in the current workspace"
-msgstr "Да се показват само прозорците на текущия работен плот"
-
-#: extensions/apps-menu/extension.js:41
-msgid "Activities Overview"
-msgstr "Показване на програмите"
-
-#: extensions/apps-menu/extension.js:141
-msgid "Favorites"
-msgstr "Любими"
-
-#: extensions/apps-menu/extension.js:436
-msgid "Applications"
-msgstr "Програми"
-
-#: extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml:6
-msgid "Application and workspace list"
-msgstr "Списък с програмите и работните плотове"
-
-#: extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml:7
-msgid ""
-"A list of strings, each containing an application id (desktop file name), "
-"followed by a colon and the workspace number"
-msgstr ""
-"Списък от низове. Всеки съдържа идентификатор на програма (име на файл „."
-"desktop“ file name), следван от знака „:“ и номер на работен плот"
-
-#: extensions/auto-move-windows/prefs.js:60
-msgid "Application"
-msgstr "Програма"
-
-#: extensions/auto-move-windows/prefs.js:69
-#: extensions/auto-move-windows/prefs.js:127
-msgid "Workspace"
-msgstr "Работен плот"
-
-#: extensions/auto-move-windows/prefs.js:85
-msgid "Add Rule"
-msgstr "Добавяне на правило"
-
-#: extensions/auto-move-windows/prefs.js:106
-msgid "Create new matching rule"
-msgstr "Създаване на правило за съвпадение"
-
-#: extensions/auto-move-windows/prefs.js:111
-msgid "Add"
-msgstr "Добавяне"
-
-#. TRANSLATORS: %s is the filesystem name
-#: extensions/drive-menu/extension.js:107
-#, javascript-format
-msgid "Ejecting drive “%s” failed:"
-msgstr "Неуспешно изваждане на устройство „%s“:"
-
-#: extensions/drive-menu/extension.js:125
-msgid "Removable devices"
-msgstr "Преносими медии"
-
-#: extensions/drive-menu/extension.js:150
-msgid "Open Files"
-msgstr "Отваряне на файлове"
-
-#: extensions/example/extension.js:17
-msgid "Hello, world!"
-msgstr "Здравей, свят!"
-
-#: extensions/example/org.gnome.shell.extensions.example.gschema.xml:5
-msgid "Alternative greeting text."
-msgstr "Друго приветстващо съобщение."
-
-#: extensions/example/org.gnome.shell.extensions.example.gschema.xml:6
-msgid ""
-"If not empty, it contains the text that will be shown when clicking on the "
-"panel."
-msgstr ""
-"Ако ключът не е празен, съдържанието му се извежда при натискането на панела."
-
-#: extensions/example/prefs.js:30
-msgid "Message"
-msgstr "Съобщение"
-
-#. TRANSLATORS: Example is the name of the extension, should not be
-#. translated
-#: extensions/example/prefs.js:43
-msgid ""
-"Example aims to show how to build well behaved extensions for the Shell and "
-"as such it has little functionality on its own.\n"
-"Nevertheless it’s possible to customize the greeting message."
-msgstr ""
-"Това е пример за добре работещо разширение на обвивката на GNOME и има "
-"минимална функционалност.\n"
-"С него можете да промените приветстващото съобщение на панела."
-
-#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:5
-msgid "Use more screen for windows"
-msgstr "Повече пространство за прозорците"
-
-#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:6
-msgid ""
-"Try to use more screen for placing window thumbnails by adapting to screen "
-"aspect ratio, and consolidating them further to reduce the bounding box. "
-"This setting applies only with the natural placement strategy."
-msgstr ""
-"Използване на по-голяма част от екрана за поставянето на мини изображения "
-"чрез промяна на съотношението на страните и допълнително обединяване за "
-"смаляване на обхващащия ги правоъгълник. Тази настройка се прилага само при "
-"естествената стратегия за поставяне на прозорците."
-
-#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:11
-msgid "Place window captions on top"
-msgstr "Заглавия на прозорците отгоре"
-
-#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:12
-msgid ""
-"If true, place window captions on top the respective thumbnail, overriding "
-"shell default of placing it at the bottom. Changing this setting requires "
-"restarting the shell to have any effect."
-msgstr ""
-"Ако е истина, заглавията на прозорците се поставят над мини изображенията "
-"им, а не както е стандартно — отдолу. За прилагане на промяната на "
-"настройката трябва да рестартирате обвивката на GNOME."
-
-#: extensions/places-menu/extension.js:78
-#: extensions/places-menu/extension.js:81
-msgid "Places"
-msgstr "Места"
-
-#: extensions/places-menu/placeDisplay.js:65
-#, javascript-format
-msgid "Failed to mount volume for “%s”"
-msgstr "Неуспешно монтиране на тома „%s“"
-
-#: extensions/places-menu/placeDisplay.js:78
-#, javascript-format
-msgid "Failed to launch “%s”"
-msgstr "Неуспешно стартиране на „%s“"
-
-#: extensions/places-menu/placeDisplay.js:137
-#: extensions/places-menu/placeDisplay.js:160
-msgid "Computer"
-msgstr "Компютър"
-
-#: extensions/places-menu/placeDisplay.js:303
-msgid "Home"
-msgstr "Домашна папка"
-
-#: extensions/places-menu/placeDisplay.js:347
-msgid "Browse Network"
-msgstr "Мрежа"
-
-#: extensions/screenshot-window-sizer/org.gnome.shell.extensions.screenshot-window-sizer.gschema.xml:7
-msgid "Cycle Screenshot Sizes"
-msgstr "Смяна на размерите на снимката на екрана"
-
-#: extensions/screenshot-window-sizer/org.gnome.shell.extensions.screenshot-window-sizer.gschema.xml:11
-msgid "Cycle Screenshot Sizes Backward"
-msgstr "Смяна на размерите на снимката на екрана наобратно"
-
-#: extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml:5
-msgid "Theme name"
-msgstr "Име на темата"
-
-#: extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml:6
-msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell"
-msgstr ""
-"Името на темата, която да бъде заредена от „~/.themes/name/gnome-shell“"
-
-#: extensions/window-list/extension.js:110
-msgid "Close"
-msgstr "Затваряне"
-
-#: extensions/window-list/extension.js:129
-msgid "Unminimize"
-msgstr "Деминимизиране"
-
-#: extensions/window-list/extension.js:130
-msgid "Minimize"
-msgstr "Минимизиране"
-
-#: extensions/window-list/extension.js:136
-msgid "Unmaximize"
-msgstr "Демаксимизиране"
-
-#: extensions/window-list/extension.js:137
-msgid "Maximize"
-msgstr "Максимизиране"
-
-#: extensions/window-list/extension.js:420
-msgid "Minimize all"
-msgstr "Минимизиране на всички"
-
-#: extensions/window-list/extension.js:428
-msgid "Unminimize all"
-msgstr "Деминимизиране на всички"
-
-#: extensions/window-list/extension.js:436
-msgid "Maximize all"
-msgstr "Максимизиране на всички"
-
-#: extensions/window-list/extension.js:445
-msgid "Unmaximize all"
-msgstr "Демаксимизиране на всички"
-
-#: extensions/window-list/extension.js:454
-msgid "Close all"
-msgstr "Затваряне на всички"
-
-#: extensions/window-list/extension.js:678
-#: extensions/workspace-indicator/extension.js:30
-msgid "Workspace Indicator"
-msgstr "Индикатор на работните плотове"
-
-#: extensions/window-list/extension.js:842
-msgid "Window List"
-msgstr "Списък на прозорците"
-
-#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:12
-msgid "When to group windows"
-msgstr "Кога да се групират прозорците"
-
-#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:13
-msgid ""
-"Decides when to group windows from the same application on the window list. "
-"Possible values are “never”, “auto” and “always”."
-msgstr ""
-"Кога да се групират прозорците на една програма в списъка с прозорците. "
-"Възможните стойности са „never“ (никога), „auto“ (автоматично) и "
-"„always“ (винаги)."
-
-#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:20
-msgid "Show the window list on all monitors"
-msgstr "Извеждане на списъка с прозорци на всички монитори"
-
-#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:21
-msgid ""
-"Whether to show the window list on all connected monitors or only on the "
-"primary one."
-msgstr ""
-"Дали списъкът с прозорци да се извежда на всички монитори или само на "
-"основния"
-
-#: extensions/window-list/prefs.js:32
-msgid "Window Grouping"
-msgstr "Групиране на прозорци"
-
-#: extensions/window-list/prefs.js:50
-msgid "Never group windows"
-msgstr "Никога да не се групират"
-
-#: extensions/window-list/prefs.js:51
-msgid "Group windows when space is limited"
-msgstr "Групиране при ограничено място"
-
-#: extensions/window-list/prefs.js:52
-msgid "Always group windows"
-msgstr "Винаги да се групират"
-
-#: extensions/window-list/prefs.js:75
-msgid "Show on all monitors"
-msgstr "На всички монитори"
-
-#: extensions/workspace-indicator/prefs.js:141
-msgid "Workspace Names"
-msgstr "Имена на работните плотове"
-
-#: extensions/workspace-indicator/prefs.js:157
-msgid "Name"
-msgstr "Име"
-
-#: extensions/workspace-indicator/prefs.js:198
-#, javascript-format
-msgid "Workspace %d"
-msgstr "Работен плот %d"
+# Bulgarian translation for gnome-shell-extensions po-file.
+# Copyright (C) 2014, 2015, 2017 Free Software Foundation, Inc.
+# Copyright (C) 2021 Alexander Shopov .
+# This file is distributed under the same license as the gnome-shell-extensions package.
+# Ivaylo Valkov , 2014.
+# Alexander Shopov , 2014, 2015, 2021.
+# Lyubomir Vasilev , 2017.
+msgid ""
+msgstr ""
+"Project-Id-Version: gnome-shell-extensions master\n"
+"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/gnome-shell-extensions/"
+"issues\n"
+"POT-Creation-Date: 2021-07-05 17:13+0000\n"
+"PO-Revision-Date: 2021-07-11 10:19+0200\n"
+"Last-Translator: Alexander Shopov \n"
+"Language-Team: Bulgarian \n"
+"Language: bg\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: data/gnome-classic.desktop.in:3
+msgid "GNOME Classic"
+msgstr "Класически GNOME"
+
+#: data/gnome-classic.desktop.in:4
+msgid "This session logs you into GNOME Classic"
+msgstr "Работната среда изглежда като класическия GNOME (2.x)"
+
+#: extensions/apps-menu/extension.js:113
+msgid "Favorites"
+msgstr "Любими"
+
+#: extensions/apps-menu/extension.js:367
+msgid "Applications"
+msgstr "Програми"
+
+#: extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml:6
+msgid "Application and workspace list"
+msgstr "Списък с програмите и работните плотове"
+
+#: extensions/auto-move-windows/org.gnome.shell.extensions.auto-move-windows.gschema.xml:7
+msgid ""
+"A list of strings, each containing an application id (desktop file name), "
+"followed by a colon and the workspace number"
+msgstr ""
+"Списък от низове. Всеки съдържа идентификатор на програма (име на файл „."
+"desktop“), следван от знака „:“ и номер на работен плот"
+
+#: extensions/auto-move-windows/prefs.js:35
+msgid "Workspace Rules"
+msgstr "Правила за работните плотове"
+
+#: extensions/auto-move-windows/prefs.js:237
+msgid "Add Rule"
+msgstr "Добавяне на правило"
+
+#. TRANSLATORS: %s is the filesystem name
+#: extensions/drive-menu/extension.js:132
+#: extensions/places-menu/placeDisplay.js:233
+#, javascript-format
+msgid "Ejecting drive “%s” failed:"
+msgstr "Неуспешно изваждане на устройство „%s“:"
+
+#: extensions/drive-menu/extension.js:148
+msgid "Removable devices"
+msgstr "Преносими медии"
+
+#: extensions/drive-menu/extension.js:172
+msgid "Open Files"
+msgstr "Отваряне на файлове"
+
+#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:5
+msgid "Use more screen for windows"
+msgstr "Повече пространство за прозорците"
+
+#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:6
+msgid ""
+"Try to use more screen for placing window thumbnails by adapting to screen "
+"aspect ratio, and consolidating them further to reduce the bounding box. "
+"This setting applies only with the natural placement strategy."
+msgstr ""
+"Използване на по-голяма част от екрана за поставянето на мини изображения "
+"чрез промяна на съотношението на страните и допълнително обединяване за "
+"смаляване на обхващащия ги правоъгълник. Тази настройка се прилага само при "
+"естествената стратегия за поставяне на прозорците."
+
+#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:11
+msgid "Place window captions on top"
+msgstr "Заглавия на прозорците отгоре"
+
+#: extensions/native-window-placement/org.gnome.shell.extensions.native-window-placement.gschema.xml:12
+msgid ""
+"If true, place window captions on top the respective thumbnail, overriding "
+"shell default of placing it at the bottom. Changing this setting requires "
+"restarting the shell to have any effect."
+msgstr ""
+"Ако е истина, заглавията на прозорците се поставят над мини изображенията "
+"им, а не както е стандартно — отдолу. За прилагане на промяната на "
+"настройката трябва да рестартирате обвивката на GNOME."
+
+#: extensions/places-menu/extension.js:89
+#: extensions/places-menu/extension.js:92
+msgid "Places"
+msgstr "Места"
+
+#: extensions/places-menu/placeDisplay.js:46
+#, javascript-format
+msgid "Failed to launch “%s”"
+msgstr "Неуспешно стартиране на „%s“"
+
+#: extensions/places-menu/placeDisplay.js:61
+#, javascript-format
+msgid "Failed to mount volume for “%s”"
+msgstr "Неуспешно монтиране на тома „%s“"
+
+#: extensions/places-menu/placeDisplay.js:148
+#: extensions/places-menu/placeDisplay.js:171
+msgid "Computer"
+msgstr "Компютър"
+
+#: extensions/places-menu/placeDisplay.js:359
+msgid "Home"
+msgstr "Домашна папка"
+
+#: extensions/places-menu/placeDisplay.js:404
+msgid "Browse Network"
+msgstr "Мрежа"
+
+#: extensions/screenshot-window-sizer/org.gnome.shell.extensions.screenshot-window-sizer.gschema.xml:7
+msgid "Cycle Screenshot Sizes"
+msgstr "Смяна на размерите на снимката на екрана"
+
+#: extensions/screenshot-window-sizer/org.gnome.shell.extensions.screenshot-window-sizer.gschema.xml:11
+msgid "Cycle Screenshot Sizes Backward"
+msgstr "Смяна на размерите на снимката на екрана наобратно"
+
+#: extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml:5
+msgid "Theme name"
+msgstr "Име на темата"
+
+#: extensions/user-theme/org.gnome.shell.extensions.user-theme.gschema.xml:6
+msgid "The name of the theme, to be loaded from ~/.themes/name/gnome-shell"
+msgstr ""
+"Името на темата, която да бъде заредена от „~/.themes/name/gnome-shell“"
+
+#: extensions/window-list/extension.js:98
+msgid "Close"
+msgstr "Затваряне"
+
+#: extensions/window-list/extension.js:118
+msgid "Unminimize"
+msgstr "Деминимизиране"
+
+#: extensions/window-list/extension.js:118
+msgid "Minimize"
+msgstr "Минимизиране"
+
+#: extensions/window-list/extension.js:125
+msgid "Unmaximize"
+msgstr "Демаксимизиране"
+
+#: extensions/window-list/extension.js:125
+msgid "Maximize"
+msgstr "Максимизиране"
+
+#: extensions/window-list/extension.js:432
+msgid "Minimize all"
+msgstr "Минимизиране на всички"
+
+#: extensions/window-list/extension.js:438
+msgid "Unminimize all"
+msgstr "Деминимизиране на всички"
+
+#: extensions/window-list/extension.js:444
+msgid "Maximize all"
+msgstr "Максимизиране на всички"
+
+#: extensions/window-list/extension.js:452
+msgid "Unmaximize all"
+msgstr "Демаксимизиране на всички"
+
+#: extensions/window-list/extension.js:460
+msgid "Close all"
+msgstr "Затваряне на всички"
+
+#: extensions/window-list/extension.js:737
+msgid "Window List"
+msgstr "Списък на прозорците"
+
+#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:12
+msgid "When to group windows"
+msgstr "Кога прозорците да се групират"
+
+#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:13
+msgid ""
+"Decides when to group windows from the same application on the window list. "
+"Possible values are “never”, “auto” and “always”."
+msgstr ""
+"Кога прозорците на една програма да се групират в списъка с прозорците. "
+"Възможните стойности са „never“ (никога), „auto“ (автоматично) и "
+"„always“ (винаги)."
+
+#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:20
+#: extensions/window-list/prefs.js:100
+msgid "Show windows from all workspaces"
+msgstr "Да се показват прозорците от всички работни плотове"
+
+#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:21
+msgid "Whether to show windows from all workspaces or only the current one."
+msgstr ""
+"Дали да се показват прозорците от всички работни плотове или само от текущия."
+
+#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:27
+msgid "Show the window list on all monitors"
+msgstr "Извеждане на списъка с прозорци на всички монитори"
+
+#: extensions/window-list/org.gnome.shell.extensions.window-list.gschema.xml:28
+msgid ""
+"Whether to show the window list on all connected monitors or only on the "
+"primary one."
+msgstr ""
+"Дали списъкът с прозорци да се извежда на всички монитори или само на "
+"основния"
+
+#: extensions/window-list/prefs.js:29
+msgid "Window Grouping"
+msgstr "Групиране на прозорци"
+
+#: extensions/window-list/prefs.js:58
+msgid "Never group windows"
+msgstr "Никога да не се групират"
+
+#: extensions/window-list/prefs.js:59
+msgid "Group windows when space is limited"
+msgstr "Групиране при ограничено място"
+
+#: extensions/window-list/prefs.js:60
+msgid "Always group windows"
+msgstr "Винаги да се групират"
+
+#: extensions/window-list/prefs.js:94
+msgid "Show on all monitors"
+msgstr "На всички монитори"
+
+#: extensions/window-list/workspaceIndicator.js:249
+#: extensions/workspace-indicator/extension.js:255
+msgid "Workspace Indicator"
+msgstr "Индикатор на работните плотове"
+
+#: extensions/workspace-indicator/prefs.js:34
+msgid "Workspace Names"
+msgstr "Имена на работните плотове"
+
+#: extensions/workspace-indicator/prefs.js:67
+#, javascript-format
+msgid "Workspace %d"
+msgstr "Работен плот %d"
+
+#: extensions/workspace-indicator/prefs.js:208
+msgid "Add Workspace"
+msgstr "Добавяне на работен плот"