From 0d37eb04f2d5d6a0f9be84b063c37d9f33ec0eb1 Mon Sep 17 00:00:00 2001 From: Bruce Leidl Date: Tue, 12 Nov 2024 17:12:37 -0500 Subject: [PATCH] Initial commit --- .gitignore | 4 + .gitmodules | 3 + ...subgraph.citadel.Realms.data.gresource.xml | 20 + data/com.subgraph.citadel.Realms.desktop | 6 + data/css/style.css | 10 + .../com.subgraph.realms.Manager2.xml | 17 + .../com.subgraph.realms.Realm.xml | 27 + .../com.subgraph.realms.RealmFS.xml | 11 + data/icons/com.subgraph.Realms.png | Bin 0 -> 37507 bytes data/meson.build | 30 + data/ui/ColorSchemeChooser.blp | 35 + data/ui/ColorSchemeListItem.blp | 13 + data/ui/ConfigureDialog.blp | 84 ++ data/ui/ConfigureOption.blp | 23 + data/ui/ConfigureRealm.blp | 75 ++ data/ui/HelpWindow.blp | 39 + data/ui/RealmInfo.blp | 25 + data/ui/RealmInfoEntry.blp | 19 + data/ui/RealmListItem.blp | 7 + data/ui/RealmRow.blp | 15 + data/ui/RealmsView.blp | 39 + data/ui/Window.blp | 60 + gi-types | 1 + js/Application.js | 89 ++ js/ColorSchemeChooser.js | 176 +++ js/ColorSchemeModel.js | 147 +++ js/ConfigureRealm.js | 151 +++ js/RealmInfo.js | 131 ++ js/RealmInfoEntry.js | 20 + js/RealmModel.js | 42 + js/RealmRow.js | 51 + js/RealmsView.js | 91 ++ js/Window.js | 28 + js/colors/Base16Theme.js | 822 ++++++++++++ js/colors/ColorSchemeModel.js | 111 ++ js/main.js | 6 + js/model/Base16Themes.js | 809 ++++++++++++ js/model/LabelColors.js | 69 ++ js/model/ObjectManager.js | 183 +++ js/model/OptionData.js | 73 ++ js/model/Realm.js | 94 ++ js/model/RealmConfig.js | 94 ++ js/model/RealmFS.js | 69 ++ js/model/RealmManager.js | 140 +++ js/model/Utils.js | 14 + meson.build | 16 + package-lock.json | 27 + package.json | 12 + src/Application.ts | 120 ++ src/ColorSchemeChooser.ts | 225 ++++ src/ColorSchemeModel.ts | 176 +++ src/ConfigureRealm.ts | 189 +++ src/RealmInfo.ts | 147 +++ src/RealmInfoEntry.ts | 24 + src/RealmModel.ts | 49 + src/RealmRow.ts | 59 + src/RealmsView.ts | 106 ++ src/Window.ts | 41 + src/colors/Base16Theme.ts | 1097 +++++++++++++++++ src/colors/ColorSchemeModel.ts | 147 +++ src/com.subgraph.citadel.Realms.js | 27 + ....subgraph.citadel.Realms.src.gresource.xml | 26 + src/main.ts | 8 + src/meson.build | 24 + src/model/Base16Themes.ts | 1081 ++++++++++++++++ src/model/LabelColors.ts | 89 ++ src/model/ObjectManager.ts | 239 ++++ src/model/OptionData.ts | 109 ++ src/model/Realm.ts | 129 ++ src/model/RealmConfig.ts | 116 ++ src/model/RealmFS.ts | 93 ++ src/model/RealmManager.ts | 171 +++ src/model/Utils.ts | 17 + subprojects/blueprint-compiler.wrap | 9 + tsconfig.json | 40 + types/ambient.d.ts | 32 + types/gettext.d.ts | 35 + types/gjs.d.ts | 183 +++ types/gjs.js | 6 + 79 files changed, 8842 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 data/com.subgraph.citadel.Realms.data.gresource.xml create mode 100644 data/com.subgraph.citadel.Realms.desktop create mode 100644 data/css/style.css create mode 100644 data/dbus-interfaces/com.subgraph.realms.Manager2.xml create mode 100644 data/dbus-interfaces/com.subgraph.realms.Realm.xml create mode 100644 data/dbus-interfaces/com.subgraph.realms.RealmFS.xml create mode 100644 data/icons/com.subgraph.Realms.png create mode 100644 data/meson.build create mode 100644 data/ui/ColorSchemeChooser.blp create mode 100644 data/ui/ColorSchemeListItem.blp create mode 100644 data/ui/ConfigureDialog.blp create mode 100644 data/ui/ConfigureOption.blp create mode 100644 data/ui/ConfigureRealm.blp create mode 100644 data/ui/HelpWindow.blp create mode 100644 data/ui/RealmInfo.blp create mode 100644 data/ui/RealmInfoEntry.blp create mode 100644 data/ui/RealmListItem.blp create mode 100644 data/ui/RealmRow.blp create mode 100644 data/ui/RealmsView.blp create mode 100644 data/ui/Window.blp create mode 160000 gi-types create mode 100644 js/Application.js create mode 100644 js/ColorSchemeChooser.js create mode 100644 js/ColorSchemeModel.js create mode 100644 js/ConfigureRealm.js create mode 100644 js/RealmInfo.js create mode 100644 js/RealmInfoEntry.js create mode 100644 js/RealmModel.js create mode 100644 js/RealmRow.js create mode 100644 js/RealmsView.js create mode 100644 js/Window.js create mode 100644 js/colors/Base16Theme.js create mode 100644 js/colors/ColorSchemeModel.js create mode 100644 js/main.js create mode 100644 js/model/Base16Themes.js create mode 100644 js/model/LabelColors.js create mode 100644 js/model/ObjectManager.js create mode 100644 js/model/OptionData.js create mode 100644 js/model/Realm.js create mode 100644 js/model/RealmConfig.js create mode 100644 js/model/RealmFS.js create mode 100644 js/model/RealmManager.js create mode 100644 js/model/Utils.js create mode 100644 meson.build create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/Application.ts create mode 100644 src/ColorSchemeChooser.ts create mode 100644 src/ColorSchemeModel.ts create mode 100644 src/ConfigureRealm.ts create mode 100644 src/RealmInfo.ts create mode 100644 src/RealmInfoEntry.ts create mode 100644 src/RealmModel.ts create mode 100644 src/RealmRow.ts create mode 100644 src/RealmsView.ts create mode 100644 src/Window.ts create mode 100644 src/colors/Base16Theme.ts create mode 100644 src/colors/ColorSchemeModel.ts create mode 100644 src/com.subgraph.citadel.Realms.js create mode 100644 src/com.subgraph.citadel.Realms.src.gresource.xml create mode 100644 src/main.ts create mode 100644 src/meson.build create mode 100644 src/model/Base16Themes.ts create mode 100644 src/model/LabelColors.ts create mode 100644 src/model/ObjectManager.ts create mode 100644 src/model/OptionData.ts create mode 100644 src/model/Realm.ts create mode 100644 src/model/RealmConfig.ts create mode 100644 src/model/RealmFS.ts create mode 100644 src/model/RealmManager.ts create mode 100644 src/model/Utils.ts create mode 100644 subprojects/blueprint-compiler.wrap create mode 100644 tsconfig.json create mode 100644 types/ambient.d.ts create mode 100644 types/gettext.d.ts create mode 100644 types/gjs.d.ts create mode 100644 types/gjs.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3490a1e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/subprojects/blueprint-compiler +/node_modules +/build +/install diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..685fbd0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "gi-types"] + path = gi-types + url = https://gitlab.gnome.org/BrainBlasted/gi-typescript-definitions.git diff --git a/data/com.subgraph.citadel.Realms.data.gresource.xml b/data/com.subgraph.citadel.Realms.data.gresource.xml new file mode 100644 index 0000000..1db9946 --- /dev/null +++ b/data/com.subgraph.citadel.Realms.data.gresource.xml @@ -0,0 +1,20 @@ + + + + ui/RealmListItem.ui + ui/RealmRow.ui + ui/RealmsView.ui + ui/RealmInfo.ui + ui/RealmInfoEntry.ui + ui/Window.ui + ui/ConfigureRealm.ui + ui/ConfigureOption.ui + ui/ColorSchemeChooser.ui + ui/ColorSchemeListItem.ui + ui/HelpWindow.ui + css/style.css + dbus-interfaces/com.subgraph.realms.Manager2.xml + dbus-interfaces/com.subgraph.realms.Realm.xml + dbus-interfaces/com.subgraph.realms.RealmFS.xml + + diff --git a/data/com.subgraph.citadel.Realms.desktop b/data/com.subgraph.citadel.Realms.desktop new file mode 100644 index 0000000..fedaa60 --- /dev/null +++ b/data/com.subgraph.citadel.Realms.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Realms +Type=Application +Exec=com.subgraph.citadel.Realms +Terminal=false +Icon=com.subgraph.citadel.Realms diff --git a/data/css/style.css b/data/css/style.css new file mode 100644 index 0000000..3bec583 --- /dev/null +++ b/data/css/style.css @@ -0,0 +1,10 @@ + +welcome { + border-spacing: 18px; + margin: 36px; +} + +welcome.big > label { + font-size: 1.4em; + font-weight: bold; +} diff --git a/data/dbus-interfaces/com.subgraph.realms.Manager2.xml b/data/dbus-interfaces/com.subgraph.realms.Manager2.xml new file mode 100644 index 0000000..bcab568 --- /dev/null +++ b/data/dbus-interfaces/com.subgraph.realms.Manager2.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/data/dbus-interfaces/com.subgraph.realms.Realm.xml b/data/dbus-interfaces/com.subgraph.realms.Realm.xml new file mode 100644 index 0000000..0703b78 --- /dev/null +++ b/data/dbus-interfaces/com.subgraph.realms.Realm.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/dbus-interfaces/com.subgraph.realms.RealmFS.xml b/data/dbus-interfaces/com.subgraph.realms.RealmFS.xml new file mode 100644 index 0000000..0822e5f --- /dev/null +++ b/data/dbus-interfaces/com.subgraph.realms.RealmFS.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/data/icons/com.subgraph.Realms.png b/data/icons/com.subgraph.Realms.png new file mode 100644 index 0000000000000000000000000000000000000000..12c7538b99b001d74cfbf553165f86d8efc3ba25 GIT binary patch literal 37507 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 U_;~RP6mkTnMwYm@SbX$<0e5pLsQ>@~ literal 0 HcmV?d00001 diff --git a/data/meson.build b/data/meson.build new file mode 100644 index 0000000..67b9cd0 --- /dev/null +++ b/data/meson.build @@ -0,0 +1,30 @@ +blueprints = custom_target('blueprints', + input: files( + 'ui/Window.blp', + 'ui/RealmListItem.blp', + 'ui/RealmRow.blp', + 'ui/RealmsView.blp', + 'ui/RealmInfo.blp', + 'ui/RealmInfoEntry.blp', + 'ui/ConfigureOption.blp', + 'ui/ConfigureRealm.blp', + 'ui/ColorSchemeChooser.blp', + 'ui/ColorSchemeListItem.blp', + 'ui/HelpWindow.blp', + ), + output: '.', + command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'], +) + +gnome.compile_resources( + APP_ID + '.data', + APP_ID + '.data.gresource.xml', + gresource_bundle: true, + install: true, + install_dir: get_option('datadir') / APP_ID, + dependencies: blueprints, +) + +install_data('icons/com.subgraph.Realms.png', install_dir: join_paths(get_option('datadir'), 'icons/hicolor/512x512/apps')) /icons W [1 + + diff --git a/data/ui/ColorSchemeChooser.blp b/data/ui/ColorSchemeChooser.blp new file mode 100644 index 0000000..dfed4eb --- /dev/null +++ b/data/ui/ColorSchemeChooser.blp @@ -0,0 +1,35 @@ + +using Gtk 4.0; +using Adw 1; + +template $ColorSchemeChooser: Adw.NavigationPage { + title: "Choose Color Scheme"; + tag: "realm-colorscheme"; + Adw.ToolbarView { + [top] + Adw.HeaderBar {} + content : Paned { + + position: 200; + + ScrolledWindow { + styles ["sidebar"] + + child: ListView colorList { + tab-behavior: item; + + styles ["navigation-sidebar"] + factory: Gtk.BuilderListItemFactory { + resource: "/com/subgraph/citadel/Realms/ui/ColorSchemeListItem.ui"; + }; + }; + } + + Label previewLabel { + use-markup: true; + xalign: 0; + yalign: 0; + } + }; + } +} diff --git a/data/ui/ColorSchemeListItem.blp b/data/ui/ColorSchemeListItem.blp new file mode 100644 index 0000000..c063abf --- /dev/null +++ b/data/ui/ColorSchemeListItem.blp @@ -0,0 +1,13 @@ +using Gtk 4.0; + +template Gtk.ListItem { + focusable: false; + child: Gtk.TreeExpander expander { + list-row: bind (template.item) as ; + child: Label { + hexpand: true; + halign: start; + label: bind expander.item as <$ColorSchemeNode>.text; + }; + }; +} diff --git a/data/ui/ConfigureDialog.blp b/data/ui/ConfigureDialog.blp new file mode 100644 index 0000000..3a3e2e5 --- /dev/null +++ b/data/ui/ConfigureDialog.blp @@ -0,0 +1,84 @@ +using Gtk 4.0; +using Adw 1; + +ColorDialog colorDialog { + title: "Choose a window label color"; + modal: true; + with-alpha: false; +} + + +template $ConfigureDialog: Adw.Dialog { + title: "Configure Realm"; + content-width: 640; + styles ["preferences"] + + child: Adw.NavigationView navigationView { + + Adw.NavigationPage { + title: bind template.title; + Adw.ToolbarView { + [top] + Adw.Banner changedBanner { + title: "Realm configuration has changed."; + button-label: "Apply"; + button-clicked => $_onApplyClicked(); + } + + Adw.PreferencesPage { + Adw.PreferencesGroup optionsGroup { + title: "Options"; + } + Adw.PreferencesGroup { + title: "Other"; + + Adw.ComboRow overlayCombo { + title: "Overlay"; + model: StringList { + strings [ + "Storage", + "TmpFS", + "None", + ] + }; + } + + Adw.ComboRow realmfsCombo { + title: "RealmFS"; + model: StringList { + strings [] + }; + } + + Adw.ActionRow { + title: "Color Scheme"; + activatable-widget: colorSchemeButton; + [suffix] + Button colorSchemeButton { + can-focus: false; + label: "Default Dark"; + } + } + + Adw.ActionRow { + title: "Window Label Color"; + activatable-widget: labelColorButton; + + [suffix] + ColorDialogButton labelColorButton { + can-focus: false; + dialog: colorDialog; + rgba: "#ffff00000000"; + } + } + + } // Adw.PreferencesGroup ("Other") + } // Adw.PreferencesPage + } // Adw.ToolbarView + } // Adw.NavigationPage + + + }; // Adw.NavigationView +} // $ConfigureDialog + + diff --git a/data/ui/ConfigureOption.blp b/data/ui/ConfigureOption.blp new file mode 100644 index 0000000..b2ee517 --- /dev/null +++ b/data/ui/ConfigureOption.blp @@ -0,0 +1,23 @@ +using Gtk 4.0; + +/* +template $ConfigureOption: ListBoxRow { + width-request: 100; + activatable: false; + selectable: false; + + child: Box { + margin-bottom: 5; + spacing: 30; + + Label name { + hexpand: true; + halign: start; + } + + Switch switch { + halign: end; + } + }; +} +*/ diff --git a/data/ui/ConfigureRealm.blp b/data/ui/ConfigureRealm.blp new file mode 100644 index 0000000..2ea118d --- /dev/null +++ b/data/ui/ConfigureRealm.blp @@ -0,0 +1,75 @@ +using Gtk 4.0; +using Adw 1; + + +ColorDialog colorDialog { + title: "Choose a window label color"; + modal: true; + with-alpha: false; +} + +template $ConfigureRealm: Adw.NavigationPage { + title: "Realm Config"; + tag: "realm-config"; + + Adw.ToolbarView { + [top] + Adw.HeaderBar {} + [top] + Adw.Banner changedBanner { + title: "Realm configuration has changed."; + button-label: "Apply"; + button-clicked => $_onApplyClicked(); + } + + content: Adw.PreferencesPage { + Adw.PreferencesGroup optionsGroup { + title: "Options"; + } + + Adw.PreferencesGroup { + title: "Other"; + Adw.ComboRow overlayCombo { + title: "Overlay"; + model: StringList { + strings [ + "Storage", + "TmpFS", + "None", + ] + }; + } + + Adw.ComboRow realmfsCombo { + title: "RealmFS"; + model: StringList { + strings [] + }; + } + + Adw.ActionRow { + title: "Color Scheme"; + activatable-widget: colorSchemeButton; + [suffix] + Button colorSchemeButton { + can-focus: false; + label: "Default Dark"; + } + } + + Adw.ActionRow { + title: "Window Label Color"; + activatable-widget: labelColorButton; + + [suffix] + ColorDialogButton labelColorButton { + can-focus: false; + dialog: colorDialog; + rgba: "#ffff00000000"; + } + } + } // Adw.PreferencesGroup ("Other") + + }; // Adw.PreferencesPage + } // Adw.ToolbarView +} // $ConfigureRealm diff --git a/data/ui/HelpWindow.blp b/data/ui/HelpWindow.blp new file mode 100644 index 0000000..32ca1f9 --- /dev/null +++ b/data/ui/HelpWindow.blp @@ -0,0 +1,39 @@ +using Gtk 4.0; + +ShortcutsWindow helpWindow { + modal: true; + can-focus: false; + + ShortcutsSection { + ShortcutsGroup { + title: "General"; + + ShortcutsShortcut { + accelerator: "Up k"; + title: "Previous / Up"; + } + ShortcutsShortcut { + accelerator: "Down j"; + title: "Next / Down"; + } + + ShortcutsShortcut { + accelerator: "h question"; + title: "Display keyboard help"; + } + ShortcutsShortcut { + accelerator: "q"; + title: "Quit"; + } + } + ShortcutsGroup { + title: "Realm"; + + ShortcutsShortcut { + accelerator: "c"; + title: "Configure selected Realm"; + } + } + } + +} diff --git a/data/ui/RealmInfo.blp b/data/ui/RealmInfo.blp new file mode 100644 index 0000000..6aced83 --- /dev/null +++ b/data/ui/RealmInfo.blp @@ -0,0 +1,25 @@ +using Gtk 4.0; + +template $RealmInfo: ScrolledWindow { + Box { + orientation: horizontal; + margin-bottom: 18; + margin-top: 18; + margin-start: 18; + margin-end: 18; + + Box column { + orientation: vertical; + spacing: 12; + hexpand: true; + + Label columnTitle { + label: "Realm"; + halign: start; + + styles [ "title-2" ] + } + + } + } +} diff --git a/data/ui/RealmInfoEntry.blp b/data/ui/RealmInfoEntry.blp new file mode 100644 index 0000000..e7320b4 --- /dev/null +++ b/data/ui/RealmInfoEntry.blp @@ -0,0 +1,19 @@ +using Gtk 4.0; + +template $RealmInfoEntry: Box { + orientation: vertical; + Label nameLabel { + halign: start; + styles [ + "dim-label", + "caption", + ] + } + + Label valueLabel { + halign: start; + wrap: true; + xalign: 0; + use-markup: true; + } +} diff --git a/data/ui/RealmListItem.blp b/data/ui/RealmListItem.blp new file mode 100644 index 0000000..cdcfe3f --- /dev/null +++ b/data/ui/RealmListItem.blp @@ -0,0 +1,7 @@ +using Gtk 4.0; + +template Gtk.ListItem { + child: $RealmRow { + realm: bind template.item; + }; +} diff --git a/data/ui/RealmRow.blp b/data/ui/RealmRow.blp new file mode 100644 index 0000000..6346286 --- /dev/null +++ b/data/ui/RealmRow.blp @@ -0,0 +1,15 @@ +using Gtk 4.0; + +template $RealmRow { + layout-manager: BoxLayout { + orientation: horizontal; + }; + Label nameLabel { + xalign: 0; + label: bind template.realm as <$Realm>.name; + styles [ + "heading", + "dim-label", + ] + } +} diff --git a/data/ui/RealmsView.blp b/data/ui/RealmsView.blp new file mode 100644 index 0000000..8ca608b --- /dev/null +++ b/data/ui/RealmsView.blp @@ -0,0 +1,39 @@ +using Gtk 4.0; + +template $RealmsView { + selected-realm: bind realmsSelection.selected-item; + layout-manager: BinLayout {}; + Frame { + width-request: 200; + [label] + Label { + margin-start: 10; + margin-top: 20; + styles ["title-4"] + label: "Realms"; + } + + ScrolledWindow { + child: ListView { + styles ["navigation-sidebar"] + margin-start: 20; + margin-end: 20; + margin-top: 10; + show-separators: true; + focusable: false; + can-focus: false; + model: SingleSelection realmsSelection { + model: FilterListModel hiddenRealmsFilterModel { + model: SortListModel { + model: $RealmModel{}; + sorter: CustomSorter realmSorter {}; + }; + }; + }; + factory: BuilderListItemFactory { + resource: "/com/subgraph/citadel/Realms/ui/RealmListItem.ui"; + }; + }; + } + } +} diff --git a/data/ui/Window.blp b/data/ui/Window.blp new file mode 100644 index 0000000..8700de5 --- /dev/null +++ b/data/ui/Window.blp @@ -0,0 +1,60 @@ +using Gtk 4.0; +using Adw 1; + +menu menu { + section { + item { + action: "app.about"; + label: "About Realms"; + } + } +} +template $RealmsWindow : Adw.ApplicationWindow { + default-width: 1000; + default-height: 640; + + /* 'width-request and 'height-request' declare minimum window size */ + width-request: 400; + height-request: 300; + + title: "Realms"; + + Adw.NavigationView navView { + + Adw.NavigationPage { + title: "Realms"; + tag: "main"; + Adw.ToolbarView { + [top] + Adw.HeaderBar header { + title-widget: Adw.WindowTitle { + title: "Realms"; + }; + [end] + MenuButton { + can-focus: false; + primary: true; + menu-model: menu; + icon-name: "open-menu-symbolic"; + } + } + content: Paned { + resize-end-child: true; + resize-start-child: false; + shrink-start-child: false; + $RealmsView realmsView {} + $RealmInfo { + realm: bind realmsView.selected-realm; + } + }; + } + } + + $ConfigureRealm configureRealm { + navigation-view: navView; + colorscheme-chooser: colorChooser; + } + + $ColorSchemeChooser colorChooser {} + } +} diff --git a/gi-types b/gi-types new file mode 160000 index 0000000..dbbaa05 --- /dev/null +++ b/gi-types @@ -0,0 +1 @@ +Subproject commit dbbaa0527556cd3ce5434c4a5072cd99348eff7a diff --git a/js/Application.js b/js/Application.js new file mode 100644 index 0000000..4be7253 --- /dev/null +++ b/js/Application.js @@ -0,0 +1,89 @@ +var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { + if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); + if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); + return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); +}; +var _Application_instances, _a, _Application_setupAccelerators, _Application_setupActions, _Application_onQuit, _Application_onRealmConfig, _Application_onShowHelp, _Application_onAbout; +import Adw from 'gi://Adw'; +import Gtk from 'gi://Gtk?version=4.0'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import './model/RealmManager.js'; +import './model/Realm.js'; +import './RealmsView.js'; +import './RealmRow.js'; +import './RealmInfo.js'; +import './RealmModel.js'; +import './ConfigureRealm.js'; +import { Window } from './Window.js'; +export class Application extends Adw.Application { + constructor() { + super({ + application_id: 'com.subgraph.citadel.Realms', + flags: Gio.ApplicationFlags.DEFAULT_FLAGS, + }); + _Application_instances.add(this); + } + vfunc_activate() { + let { activeWindow } = this; + if (!activeWindow) { + activeWindow = new Window(this); + activeWindow.set_hide_on_close(true); + } + activeWindow.present(); + } + vfunc_startup() { + super.vfunc_startup(); + __classPrivateFieldGet(this, _Application_instances, "m", _Application_setupActions).call(this); + __classPrivateFieldGet(this, _Application_instances, "m", _Application_setupAccelerators).call(this); + // this.#loadStyleSheet(); + const styleManager = Adw.StyleManager.get_default(); + styleManager.colorScheme = Adw.ColorScheme.FORCE_DARK; + } +} +_a = Application, _Application_instances = new WeakSet(), _Application_setupAccelerators = function _Application_setupAccelerators() { + this.set_accels_for_action('app.quit', ['q']); + this.set_accels_for_action('app.realmConfig', ['c']); + this.set_accels_for_action('app.showHelp', ['h', 'question']); +}, _Application_setupActions = function _Application_setupActions() { + this.add_action_entries([ + // @ts-ignore + { name: 'quit', activate: __classPrivateFieldGet(this, _Application_instances, "m", _Application_onQuit).bind(this) }, + // @ts-ignore + { name: 'realmConfig', activate: __classPrivateFieldGet(this, _Application_instances, "m", _Application_onRealmConfig).bind(this) }, + // @ts-ignore + { name: 'showHelp', activate: __classPrivateFieldGet(this, _Application_instances, "m", _Application_onShowHelp).bind(this) }, + // @ts-ignore + { name: 'about', activate: __classPrivateFieldGet(this, _Application_instances, "m", _Application_onAbout).bind(this) }, + ]); +}, _Application_onQuit = function _Application_onQuit() { + let { activeWindow } = this; + if (activeWindow) { + activeWindow.close(); + } +}, _Application_onRealmConfig = function _Application_onRealmConfig() { + let { activeWindow } = this; + if (activeWindow) { + let window = activeWindow; + const realm = window.realms_view.selectedRealm; + if (realm) { + window.configureRealm(realm); + } + } +}, _Application_onShowHelp = function _Application_onShowHelp() { + const help = Gtk.Builder.new_from_resource('/com/subgraph/citadel/Realms/ui/HelpWindow.ui').get_object('helpWindow'); + help.set_transient_for(this._window); + help.present(); +}, _Application_onAbout = function _Application_onAbout() { + const dialog = new Adw.AboutDialog({ + application_icon: 'face-smile', + application_name: 'Realms', + developer_name: "Subgraph", + }); + dialog.present(this._window); +}; +(() => { + GObject.registerClass({ + GTypeName: 'RealmsApplication' + }, _a); +})(); diff --git a/js/ColorSchemeChooser.js b/js/ColorSchemeChooser.js new file mode 100644 index 0000000..4a86e94 --- /dev/null +++ b/js/ColorSchemeChooser.js @@ -0,0 +1,176 @@ +var _a; +import Adw from 'gi://Adw'; +import GObject from 'gi://GObject'; +import GLib from 'gi://GLib'; +import Gdk from 'gi://Gdk?version=4.0'; +import Gtk from 'gi://Gtk?version=4.0'; +import { ColorSchemeModel } from './ColorSchemeModel.js'; +class PreviewRenderer { + constructor(theme) { + this.theme = theme; + this.buffer = ''; + } + colorAttrib(name, color) { + this.buffer += ` ${name}='${color}'`; + } + colorSpan(fg, bg = null) { + this.buffer += '').nl() + .func('#include ').string('').nl() + .nl() + .vtype('static char').text(' theme[] = ').string(`"${name}"`).text(';').nl() + .nl() + .vtype('int').text(' main(').vtype('int').text(' argc, ').vtype('char').text(' **argv) {').nl() + .text(' printf(').string('"Hello, ').keyword('%s').text('!').keyword('\\n').string('"').text(', theme);').nl() + .text(' exit(').konst('0').text(');').nl() + .text('}') + .nl(); + return this.buffer; + } +} +export class ColorSchemeChooser extends Adw.NavigationPage { + addExpandToggleShortcut(keyval) { + let trigger = Gtk.KeyvalTrigger.new(keyval, Gdk.ModifierType.NO_MODIFIER_MASK); + let action = Gtk.CallbackAction.new((expander) => expander.activate_action('listitem.toggle-expand', null)); + Gtk.TreeExpander.add_shortcut(Gtk.Shortcut.new(trigger, action)); + } + constructor() { + super(); + this.model = new ColorSchemeModel(); + this._previewLabel.add_css_class("preview"); + this.css_provider = new Gtk.CssProvider(); + const display = Gdk.Display.get_default(); + if (display) { + Gtk.StyleContext.add_provider_for_display(display, this.css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } + this.addExpandToggleShortcut(Gdk.KEY_space); + this.addExpandToggleShortcut(Gdk.KEY_l); + this._colorList.connect('activate', () => { + this.previewSelectedTheme(); + }); + this._colorList.single_click_activate = true; + this._colorList.model = this.model.selection; + const keyController = new Gtk.EventControllerKey(); + keyController.connect('key-pressed', (_, keyval) => { + switch (keyval) { + case Gdk.KEY_j: + case Gdk.KEY_Down: + this.nextScheme(); + break; + case Gdk.KEY_k: + case Gdk.KEY_Up: + this.prevScheme(); + break; + } + }); + this._colorList.add_controller(keyController); + } + nextScheme() { + let pos = this.model.changeSelected(1); + if (pos) { + this._colorList.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, null); + } + } + prevScheme() { + let pos = this.model.changeSelected(-1); + if (pos) { + this._colorList.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, null); + } + } + selectTheme(id) { + const DEFAULT_THEME = '3024'; + let row = this.model.searchId(id !== null && id !== void 0 ? id : DEFAULT_THEME); + if (row) { + let pos = row.get_position(); + this.model.selectPosition(pos); + this._colorList.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, null); + this.previewSelectedTheme(); + } + } + getSelectedTheme() { + return this.model.selectedTheme(); + } + previewSelectedTheme() { + let theme = this.model.selectedTheme(); + if (theme) { + this.setBackgroundColor(theme); + let renderer = new PreviewRenderer(theme); + // @ts-ignore + this._previewLabel.label = renderer.renderPreview(); + } + } + setBackgroundColor(theme) { + let css = ` +label.preview { + background-color: ${theme.terminal_background()}; + font-family: monospace; + font-size: 14pt; +}`; + this.css_provider.load_from_string(css); + } +} +_a = ColorSchemeChooser; +(() => { + GObject.registerClass({ + GTypeName: 'ColorSchemeChooser', + Template: 'resource:///com/subgraph/citadel/Realms/ui/ColorSchemeChooser.ui', + InternalChildren: ['colorList', 'previewLabel'], + }, _a); +})(); diff --git a/js/ColorSchemeModel.js b/js/ColorSchemeModel.js new file mode 100644 index 0000000..7ce3f64 --- /dev/null +++ b/js/ColorSchemeModel.js @@ -0,0 +1,147 @@ +var _a; +import GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; +import Gtk from 'gi://Gtk?version=4.0'; +import { Base16Theme } from './model/Base16Themes.js'; +class ModelBuilder { + constructor() { + this.CATEGORIES = [ + ['atelier', 'Atelier'], + ['black-metal', 'Black Metal'], + ['brushtrees', 'Brush Trees'], + ['classic', 'Classic'], + ['default', 'Default'], + ['google', 'Google'], + ['grayscale', 'Grayscale'], + ['gruvbox', 'Gruvbox'], + ['harmonic', 'Harmonic'], + ['ia', 'iA'], + ['material', 'Material'], + ['papercolor', 'PaperColor'], + ['solarized', 'Solarized'], + ['summerfruit', 'Summerfruit'], + ['tomorrow', 'Tomorrow'], + ['unikitty', 'Unikitty'], + ]; + this.currentCategory = null; + this.storeModel = new Gio.ListStore(ColorSchemeNode); + } + matchesCategory(theme) { + return (this.CATEGORIES.length > 0 && theme.id.startsWith(this.CATEGORIES[0][0])); + } + appendCurrentCategory() { + if (this.currentCategory) { + this.storeModel.append(this.currentCategory); + this.currentCategory = null; + this.CATEGORIES.shift(); + } + } + addToCategory(theme) { + if (!this.currentCategory) { + this.currentCategory = new ColorSchemeNode(this.CATEGORIES[0][1]); + } + this.currentCategory.addChild(new ColorSchemeNode(theme.name, theme)); + } + addTheme(theme) { + if (this.matchesCategory(theme)) { + this.addToCategory(theme); + } + else { + if (this.currentCategory) { + this.appendCurrentCategory(); + } + this.storeModel.append(new ColorSchemeNode(theme.name, theme)); + } + } + static buildTreeModel() { + let builder = new ModelBuilder(); + Base16Theme.THEME_LIST.forEach(theme => builder.addTheme(theme)); + return Gtk.TreeListModel.new(builder.storeModel, false, false, item => item.child_model); + } +} +export class ColorSchemeNode extends GObject.Object { + constructor(text, theme = null) { + super(); + this._text = text; + this.theme = theme; + this.child_model = null; + } + get text() { + return this._text; + } + addChild(child) { + if (this.child_model === null) { + this.child_model = new Gio.ListStore(_a); + } + this.child_model.append(child); + } + matchesId(id) { + return this.theme !== null && this.theme.id === id; + } + searchChildren(id) { + if (this.child_model) { + for (let i = 0; i < this.child_model.n_items; i++) { + let node = this.child_model.get_item(i); + if (node.matchesId(id)) { + return true; + } + } + } + return false; + } +} +_a = ColorSchemeNode; +(() => { + GObject.registerClass({ + GTypeName: 'ColorSchemeNode', + Properties: { + 'text': GObject.ParamSpec.string('text', '', '', GObject.ParamFlags.READWRITE, ''), + } + }, _a); +})(); +export class ColorSchemeModel { + constructor() { + this.treeModel = ModelBuilder.buildTreeModel(); + this.selection = Gtk.SingleSelection.new(this.treeModel); + } + selectedTheme() { + // @ts-ignore + let item = this.selection.selected_item.item; + return item === null || item === void 0 ? void 0 : item.theme; + } + changeSelected(offset) { + const n_items = this.selection.n_items; + if (n_items <= 1) { + return; + } + const pos = this.selection.selected; + if (pos == Gtk.INVALID_LIST_POSITION) { + return; + } + const newPos = pos + offset; + if (newPos < 0 || newPos >= n_items) { + return; + } + this.selection.selected = newPos; + return newPos; + } + selectPosition(pos) { + this.selection.selected = pos; + } + searchId(id, from = 0) { + for (let i = from; i < this.treeModel.n_items; i++) { + let row = this.treeModel.get_row(i); + let item = row === null || row === void 0 ? void 0 : row.item; + if (item && item.matchesId(id)) { + return row; + } + if (item.searchChildren(id)) { + row === null || row === void 0 ? void 0 : row.set_expanded(true); + if (from === 0) { + return this.searchId(id, i); + } + } + } + return null; + } +} diff --git a/js/ConfigureRealm.js b/js/ConfigureRealm.js new file mode 100644 index 0000000..f68ba87 --- /dev/null +++ b/js/ConfigureRealm.js @@ -0,0 +1,151 @@ +var _a; +import GObject from 'gi://GObject'; +import Gdk from 'gi://Gdk?version=4.0'; +import Gtk from 'gi://Gtk?version=4.0'; +import Adw from 'gi://Adw'; +import { BoolOptionData } from './model/RealmConfig.js'; +import { Base16Theme } from './model/Base16Themes.js'; +import { RealmManager } from './model/RealmManager.js'; +import { ColorSchemeChooser } from './ColorSchemeChooser.js'; +class OptionState { + constructor(row) { + this.value = null; + this.row = row; + } + initValue(value) { + this.value = value; + this.originalValue = value; + this.row.active = value; + } + /** @param {any} value */ + setValue(value) { + this.value = value; + } + hasChanged() { + return this.value !== this.originalValue; + } +} +export class ConfigureRealm extends Adw.NavigationPage { + constructor() { + var _b, _c; + super(); + this.realm = null; + this.optionState = new Map(); + this.theme = Base16Theme.lookup('default-dark'); + this._addOptions(); + const keyController = new Gtk.EventControllerKey(); + keyController.connect('key-pressed', (_, keyval) => { + if (keyval === Gdk.KEY_j) { + this.vfunc_move_focus(Gtk.DirectionType.TAB_FORWARD); + } + else if (keyval === Gdk.KEY_k) { + this.vfunc_move_focus(Gtk.DirectionType.TAB_BACKWARD); + } + }); + this.add_controller(keyController); + (_b = this._labelColorButton) === null || _b === void 0 ? void 0 : _b.connect('notify::rgba', () => this._scanChanges()); + (_c = this._colorSchemeButton) === null || _c === void 0 ? void 0 : _c.connect('clicked', () => { + var _b; + (_b = this.navigationView) === null || _b === void 0 ? void 0 : _b.push_by_tag('realm-colorscheme'); + }); + } + get colorschemeChooser() { + return this._colorschemeChooser; + } + set colorschemeChooser(val) { + this._colorschemeChooser = val; + } + set navigationView(val) { + var _b; + this._navigationView = val; + (_b = this._navigationView) === null || _b === void 0 ? void 0 : _b.connect('popped', (_view, page) => { + if (page === this.colorschemeChooser) { + let theme = this.colorschemeChooser.getSelectedTheme(); + if (theme && this._colorSchemeButton) { + this.theme = theme; + this._colorSchemeButton.label = this.theme.name; + } + } + }); + } + get navigationView() { + return this._navigationView; + } + configure(realm) { + this.realm = realm; + this.set_title(`Configure realm-${realm.name}`); + this._setOptions(realm); + } + _setOptions(realm) { + var _b; + let config = realm.config; + this.optionState.forEach((op, name) => { + let v = config.get_bool(name); + op.initValue(v); + }); + let scheme = realm.config.get_colorscheme(); + this.theme = Base16Theme.lookup(scheme); + if (this.theme && this._colorSchemeButton) { + this._colorSchemeButton.label = this.theme.name; + (_b = this.colorschemeChooser) === null || _b === void 0 ? void 0 : _b.selectTheme(this.theme.id); + } + let realmfs_list = RealmManager.instance().realmfsList(); + // @ts-ignore + let model = this._realmfsCombo.model; + if (model.n_items > 0) { + model.splice(0, model.n_items, []); + } + realmfs_list.forEach(realmfs => model.append(realmfs.name)); + let labelColor = realm.getLabelColor(); + this._labelColorButton.rgba = labelColor; + this._scanChanges(); + } + _addOptions() { + BoolOptionData.allOptions().forEach(option => { + let row = new Adw.SwitchRow({ + title: option.description, + }); + if (option.tooltip.length > 0) { + row.tooltipMarkup = option.tooltip; + } + let state = new OptionState(row); + this.optionState.set(option.name, state); + row.connect("notify::active", () => { + state.setValue(row.active); + this._scanChanges(); + }); + this._optionsGroup.add(row); + }); + } + _scanChanges() { + var _b; + let changed = false; + this.optionState.forEach(op => { + if (op.hasChanged()) { + changed = true; + } + }); + let labelColor = (_b = this.realm) === null || _b === void 0 ? void 0 : _b.getLabelColor(); + if (labelColor) { + if (!this._labelColorButton.rgba.equal(labelColor)) { + changed = true; + } + } + this._changedBanner.set_revealed(changed); + } + _onApplyClicked() { + print("clikkk"); + } +} +_a = ConfigureRealm; +(() => { + GObject.registerClass({ + GTypeName: "ConfigureRealm", + Template: 'resource:///com/subgraph/citadel/Realms/ui/ConfigureRealm.ui', + InternalChildren: ['optionsGroup', 'changedBanner', 'overlayCombo', 'realmfsCombo', 'colorSchemeButton', 'labelColorButton'], + Properties: { + 'navigation-view': GObject.ParamSpec.object('navigation-view', '', '', GObject.ParamFlags.READWRITE, Adw.NavigationView), + 'colorscheme-chooser': GObject.ParamSpec.object('colorscheme-chooser', '', '', GObject.ParamFlags.READWRITE, ColorSchemeChooser), + } + }, _a); +})(); diff --git a/js/RealmInfo.js b/js/RealmInfo.js new file mode 100644 index 0000000..54b3e4d --- /dev/null +++ b/js/RealmInfo.js @@ -0,0 +1,131 @@ +var _a; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk?version=4.0'; +import { Realm } from "./model/Realm.js"; +import { RealmInfoEntry } from './RealmInfoEntry.js'; +const Sections = { + STATUS: 'Status', + OPTIONS: 'Options', + REALMFS: 'RealmFS', + MOUNTPOINT: 'RealmFS Mountpoint', +}; +export class RealmInfo extends Gtk.ScrolledWindow { + constructor() { + super(); + this._buffer = ""; + this._changeId = 0; + this._realm = null; + this._entries = new Map(); + this._addEntries({ + left: [ + Sections.STATUS, + Sections.REALMFS, + Sections.MOUNTPOINT, + Sections.OPTIONS, + ], + right: [] + }); + } + _addEntries(labels) { + let addSectionEntries = (section, entries) => { + entries.forEach(name => { + let entry = new RealmInfoEntry(name); + this._entries.set(name, entry); + section.append(entry); + }); + }; + addSectionEntries(this._column, labels['left']); + } + get realm() { + return this._realm; + } + set realm(value) { + if (this.realm === value) { + return; + } + if (this._realm) { + this._realm.disconnect(this._changeId); + } + this._realm = value; + if (this._realm) { + this._changeId = this._realm.connect('changed', () => { + this.displayRealm(); + }); + this.displayRealm(); + } + } + setEntry(name, value) { + let entry = this._entries.get(name); + if (entry) { + entry.setValue(value); + } + } + setEntryVisible(name, isVisible = true) { + let entry = this._entries.get(name); + if (entry) { + entry.set_visible(isVisible); + } + } + displayConfig() { + var _b; + let config = (_b = this._realm) === null || _b === void 0 ? void 0 : _b.config; + if (config) { + let enabled_default = config.enabled_bool_option_labels(true); + let enabled = config.enabled_bool_option_labels(false); + let buffer = enabled_default.join(' '); + buffer += '\n'; + buffer += enabled.map(s => `${s}`) + .join(' '); + this.setEntry(Sections.OPTIONS, buffer); + } + else { + this.setEntry(Sections.OPTIONS, ''); + } + } + displayRealmFS() { + var _b; + let realmfs = (_b = this._realm) === null || _b === void 0 ? void 0 : _b.realmfs; + if (realmfs) { + this.setEntryVisible(Sections.REALMFS); + this.setEntry(Sections.REALMFS, `${realmfs.name}-realmfs.img`); + if (realmfs.activated) { + this.setEntryVisible(Sections.MOUNTPOINT); + this.setEntry(Sections.MOUNTPOINT, realmfs.mountpoint); + } + else { + this.setEntryVisible(Sections.MOUNTPOINT, false); + } + } + else { + this.setEntryVisible(Sections.REALMFS, false); + this.setEntryVisible(Sections.MOUNTPOINT, false); + } + } + displayRealm() { + if (this._realm) { + this._columnTitle.label = `Realm ${this._realm.name}`; + if (this._realm.is_running()) { + this.setEntry("Status", "Running"); + } + else { + this.setEntry("Status", "Stopped"); + } + this.displayConfig(); + this.displayRealmFS(); + } + } +} +_a = RealmInfo; +(() => { + GObject.registerClass({ + GTypeName: "RealmInfo", + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmInfo.ui', + Properties: { + 'realm': GObject.ParamSpec.object('realm', '', '', GObject.ParamFlags.READWRITE, Realm), + }, + InternalChildren: [ + 'column', + 'columnTitle', + ] + }, _a); +})(); diff --git a/js/RealmInfoEntry.js b/js/RealmInfoEntry.js new file mode 100644 index 0000000..73cc434 --- /dev/null +++ b/js/RealmInfoEntry.js @@ -0,0 +1,20 @@ +var _a; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk?version=4.0'; +export class RealmInfoEntry extends Gtk.Box { + constructor(name) { + super(); + this._nameLabel.label = name; + } + setValue(value) { + this._valueLabel.label = value; + } +} +_a = RealmInfoEntry; +(() => { + GObject.registerClass({ + GTypeName: "RealmInfoEntry", + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmInfoEntry.ui', + InternalChildren: ['nameLabel', 'valueLabel'] + }, _a); +})(); diff --git a/js/RealmModel.js b/js/RealmModel.js new file mode 100644 index 0000000..66bc469 --- /dev/null +++ b/js/RealmModel.js @@ -0,0 +1,42 @@ +var _a; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import { Realm } from './model/Realm.js'; +import { RealmManager } from "./model/RealmManager.js"; +export class RealmModel extends GObject.Object { + constructor() { + super(); + this._realms = []; + this._realmManager = RealmManager.instance(); + this._realmManager.connect('realm-added', (_manager, realm) => { + let pos = this._realms.length; + this._realms.push(realm); + // @ts-ignore + this.items_changed(pos, 0, 1); + }); + this._realmManager.connect('realm-removed', (_manager, realm) => { + let pos = this._realms.findIndex(r => r === realm); + if (pos >= 0) { + this._realms.splice(pos, 1); + // @ts-ignore + this.items_changed(pos, 1, 0); + } + }); + } + vfunc_get_item_type() { + return Realm; + } + vfunc_get_item(position) { + return this._realms[position] || null; + } + vfunc_get_n_items() { + return this._realms.length; + } +} +_a = RealmModel; +(() => { + GObject.registerClass({ + GTypeName: 'RealmModel', + Implements: [Gio.ListModel], + }, _a); +})(); diff --git a/js/RealmRow.js b/js/RealmRow.js new file mode 100644 index 0000000..cf47cef --- /dev/null +++ b/js/RealmRow.js @@ -0,0 +1,51 @@ +var _a; +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk?version=4.0'; +import { Realm } from './model/Realm.js'; +export class RealmRow extends Gtk.Widget { + constructor() { + super(...arguments); + this._notifyId = 0; + } + get realm() { + if (this._realm === undefined) { + this._realm = null; + } + return this._realm; + } + set realm(value) { + if (this.realm === value) { + return; + } + if (this.realm && this._notifyId) { + this.realm.disconnect(this._notifyId); + this._notifyId = 0; + } + this._realm = value; + if (this.realm) { + this._notifyId = this.realm.connect('notify', this.syncRealm.bind(this)); + this.syncRealm(); + } + this.notify('realm'); + } + syncRealm() { + if (this.realm && this.realm.is_running()) { + this._nameLabel.remove_css_class('dim-label'); + } + else { + // @ts-ignore + this._nameLabel.add_css_class('dim-label'); + } + } +} +_a = RealmRow; +(() => { + GObject.registerClass({ + GTypeName: 'RealmRow', + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmRow.ui', + Properties: { + 'realm': GObject.ParamSpec.object('realm', '', '', GObject.ParamFlags.READWRITE, Realm), + }, + InternalChildren: ['nameLabel'], + }, _a); +})(); diff --git a/js/RealmsView.js b/js/RealmsView.js new file mode 100644 index 0000000..a6fbabd --- /dev/null +++ b/js/RealmsView.js @@ -0,0 +1,91 @@ +var _a; +import GObject from 'gi://GObject'; +import Gdk from 'gi://Gdk?version=4.0'; +import Gtk from 'gi://Gtk?version=4.0'; +import { Realm } from "./model/Realm.js"; +export class RealmsView extends Gtk.Widget { + constructor() { + super(); + this._selectedRealm = null; + this._setupHiddenRealmsFilter(); + this._setupRealmSorter(); + const keyController = new Gtk.EventControllerKey(); + keyController.connect('key-pressed', (_, keyval) => { + switch (keyval) { + case Gdk.KEY_j: + case Gdk.KEY_Down: + this.nextRealm(); + break; + case Gdk.KEY_k: + case Gdk.KEY_Up: + this.prevRealm(); + break; + case Gdk.KEY_Escape: + this.activate_action('app.quit', null); + print("escaped"); + break; + } + }); + this.add_controller(keyController); + } + _changeSelected(offset) { + const n_items = this._realmsSelection.n_items; + if (n_items <= 1) { + return; + } + const pos = this._realmsSelection.selected; + if (pos == Gtk.INVALID_LIST_POSITION) { + return; + } + const newPos = pos + offset; + if (newPos < 0 || newPos >= n_items) { + return; + } + this._realmsSelection.selected = newPos; + } + nextRealm() { + this._changeSelected(1); + } + prevRealm() { + this._changeSelected(-1); + } + get selectedRealm() { + return this._selectedRealm; + } + set selectedRealm(value) { + if (this.selectedRealm === value) { + return; + } + this._selectedRealm = value; + } + _setupHiddenRealmsFilter() { + this._hiddenRealmsFilterModel.filter = Gtk.CustomFilter.new(item => !item.is_system_realm); + } + _setupRealmSorter() { + // 1) Sort 'Current' the lowest (top of list). + // 2) Then sort 'Running' lower than not 'Running' + // 3) Realms in the same run state sorted by lowest timestamp + this._realmSorter.set_sort_func((a, b) => { + if (a.is_current || (a.is_running && !b.is_running)) { + return Gtk.Ordering.SMALLER; + } + else if (b.is_current || (b.is_running && !a.is_running)) { + return Gtk.Ordering.LARGER; + } + else { + return b.timestamp - a.timestamp; + } + }); + } +} +_a = RealmsView; +(() => { + GObject.registerClass({ + GTypeName: 'RealmsView', + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmsView.ui', + Properties: { + 'selected-realm': GObject.ParamSpec.object('selected-realm', '', '', GObject.ParamFlags.READWRITE, Realm), + }, + InternalChildren: ['realmsSelection', 'hiddenRealmsFilterModel', 'realmSorter'], + }, _a); +})(); diff --git a/js/Window.js b/js/Window.js new file mode 100644 index 0000000..1d91634 --- /dev/null +++ b/js/Window.js @@ -0,0 +1,28 @@ +var _a; +import GObject from 'gi://GObject'; +import Adw from 'gi://Adw'; +export class Window extends Adw.ApplicationWindow { + constructor(application) { + super({ application: application }); + } + configureRealm(realm) { + this._configureRealm.configure(realm); + this._navView.push(this._configureRealm); + } + get realms_view() { + return this._realmsView; + } + vfunc_close_request() { + super.vfunc_close_request(); + this.run_dispose(); + return true; + } +} +_a = Window; +(() => { + GObject.registerClass({ + GTypeName: 'RealmsWindow', + Template: 'resource:///com/subgraph/citadel/Realms/ui/Window.ui', + InternalChildren: ['realmsView', 'configureRealm', 'navView'], + }, _a); +})(); diff --git a/js/colors/Base16Theme.js b/js/colors/Base16Theme.js new file mode 100644 index 0000000..a16dcbc --- /dev/null +++ b/js/colors/Base16Theme.js @@ -0,0 +1,822 @@ +export class Base16Theme { + static add(id, name, colors) { + const theme = new Base16Theme(id, name, colors); + Base16Theme.THEMES.push(theme); + } + constructor(id, name, colors) { + this.id = id; + this.name = name; + this.colors = colors; + } + color(idx) { + let hex = this.colors[idx].toString(16).padStart(6, '0'); + return `#${hex}`; + } + terminal_background() { + return this.color(0); + } + terminal_foreground() { + return this.color(5); + } + terminal_palette_color(idx) { + return this.color(Base16Theme.TERM_MAP[idx]); + } +} +Base16Theme.CATEGORIES = [ + ['atelier', 'Atelier'], + ['black-metal', 'Black Metal'], + ['brushtrees', 'Brush Trees'], + ['classic', 'Classic'], + ['default', 'Default'], + ['google', 'Google'], + ['grayscale', 'Grayscale'], + ['gruvbox', 'Gruvbox'], + ['harmonic', 'Harmonic'], + ['ia', 'iA'], + ['material', 'Material'], + ['papercolor', 'PaperColor'], + ['solarized', 'Solarized'], + ['summerfruit', 'Summerfruit'], + ['tomorrow', 'Tomorrow'], + ['unikitty', 'Unikitty'], +]; +Base16Theme.TERM_MAP = [ + 0x00, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x05, + 0x03, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x07, + 0x09, 0x0F, 0x01, 0x02, 0x04, 0x06, +]; +Base16Theme.THEMES = []; +Base16Theme.add("3024", "3024", [ + 0x090300, 0x3a3432, 0x4a4543, 0x5c5855, + 0x807d7c, 0xa5a2a2, 0xd6d5d4, 0xf7f7f7, + 0xdb2d20, 0xe8bbd0, 0xfded02, 0x01a252, + 0xb5e4f4, 0x01a0e4, 0xa16a94, 0xcdab53, +]); +Base16Theme.add("apathy", 'Apathy', [ + 0x031A16, 0x0B342D, 0x184E45, 0x2B685E, + 0x5F9C92, 0x81B5AC, 0xA7CEC8, 0xD2E7E4, + 0x3E9688, 0x3E7996, 0x3E4C96, 0x883E96, + 0x963E4C, 0x96883E, 0x4C963E, 0x3E965B, +]); +Base16Theme.add("ashes", "Ashes", [ + 0x1C2023, 0x393F45, 0x565E65, 0x747C84, + 0xADB3BA, 0xC7CCD1, 0xDFE2E5, 0xF3F4F5, + 0xC7AE95, 0xC7C795, 0xAEC795, 0x95C7AE, + 0x95AEC7, 0xAE95C7, 0xC795AE, 0xC79595, +]); +Base16Theme.add("atelier-cave-light", "Atelier Cave Light", [ + 0xefecf4, 0xe2dfe7, 0x8b8792, 0x7e7887, + 0x655f6d, 0x585260, 0x26232a, 0x19171c, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, +]); +Base16Theme.add("atelier-cave", "Atelier Cave", [ + 0x19171c, 0x26232a, 0x585260, 0x655f6d, + 0x7e7887, 0x8b8792, 0xe2dfe7, 0xefecf4, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, +]); +Base16Theme.add("atelier-dune-light", "Atelier Dune Light", [ + 0xfefbec, 0xe8e4cf, 0xa6a28c, 0x999580, + 0x7d7a68, 0x6e6b5e, 0x292824, 0x20201d, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, +]); +Base16Theme.add("atelier-dune", "Atelier Dune", [ + 0x20201d, 0x292824, 0x6e6b5e, 0x7d7a68, + 0x999580, 0xa6a28c, 0xe8e4cf, 0xfefbec, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, +]); +Base16Theme.add("atelier-estuary-light", "Atelier Estuary Light", [ + 0xf4f3ec, 0xe7e6df, 0x929181, 0x878573, + 0x6c6b5a, 0x5f5e4e, 0x302f27, 0x22221b, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, +]); +Base16Theme.add("atelier-estuary", "Atelier Estuary", [ + 0x22221b, 0x302f27, 0x5f5e4e, 0x6c6b5a, + 0x878573, 0x929181, 0xe7e6df, 0xf4f3ec, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, +]); +Base16Theme.add("atelier-forest-light", "Atelier Forest Light", [ + 0xf1efee, 0xe6e2e0, 0xa8a19f, 0x9c9491, + 0x766e6b, 0x68615e, 0x2c2421, 0x1b1918, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, +]); +Base16Theme.add("atelier-forest", "Atelier Forest", [ + 0x1b1918, 0x2c2421, 0x68615e, 0x766e6b, + 0x9c9491, 0xa8a19f, 0xe6e2e0, 0xf1efee, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, +]); +Base16Theme.add("atelier-heath-light", "Atelier Heath Light", [ + 0xf7f3f7, 0xd8cad8, 0xab9bab, 0x9e8f9e, + 0x776977, 0x695d69, 0x292329, 0x1b181b, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, +]); +Base16Theme.add("atelier-heath", "Atelier Heath", [ + 0x1b181b, 0x292329, 0x695d69, 0x776977, + 0x9e8f9e, 0xab9bab, 0xd8cad8, 0xf7f3f7, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, +]); +Base16Theme.add("atelier-lakeside-light", "Atelier Lakeside Light", [ + 0xebf8ff, 0xc1e4f6, 0x7ea2b4, 0x7195a8, + 0x5a7b8c, 0x516d7b, 0x1f292e, 0x161b1d, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, +]); +Base16Theme.add("atelier-lakeside", "Atelier Lakeside", [ + 0x161b1d, 0x1f292e, 0x516d7b, 0x5a7b8c, + 0x7195a8, 0x7ea2b4, 0xc1e4f6, 0xebf8ff, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, +]); +Base16Theme.add("atelier-plateau-light", "Atelier Plateau Light", [ + 0xf4ecec, 0xe7dfdf, 0x8a8585, 0x7e7777, + 0x655d5d, 0x585050, 0x292424, 0x1b1818, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, +]); +Base16Theme.add("atelier-plateau", "Atelier Plateau", [ + 0x1b1818, 0x292424, 0x585050, 0x655d5d, + 0x7e7777, 0x8a8585, 0xe7dfdf, 0xf4ecec, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, +]); +Base16Theme.add("atelier-savanna-light", "Atelier Savanna Light", [ + 0xecf4ee, 0xdfe7e2, 0x87928a, 0x78877d, + 0x5f6d64, 0x526057, 0x232a25, 0x171c19, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, +]); +Base16Theme.add("atelier-savanna", "Atelier Savanna", [ + 0x171c19, 0x232a25, 0x526057, 0x5f6d64, + 0x78877d, 0x87928a, 0xdfe7e2, 0xecf4ee, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, +]); +Base16Theme.add("atelier-seaside-light", "Atelier Seaside Light", [ + 0xf4fbf4, 0xcfe8cf, 0x8ca68c, 0x809980, + 0x687d68, 0x5e6e5e, 0x242924, 0x131513, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, +]); +Base16Theme.add("atelier-seaside", "Atelier Seaside", [ + 0x131513, 0x242924, 0x5e6e5e, 0x687d68, + 0x809980, 0x8ca68c, 0xcfe8cf, 0xf4fbf4, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, +]); +Base16Theme.add("atelier-sulphurpool-light", "Atelier Sulphurpool Light", [ + 0xf5f7ff, 0xdfe2f1, 0x979db4, 0x898ea4, + 0x6b7394, 0x5e6687, 0x293256, 0x202746, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, +]); +Base16Theme.add("atelier-sulphurpool", "Atelier Sulphurpool", [ + 0x202746, 0x293256, 0x5e6687, 0x6b7394, + 0x898ea4, 0x979db4, 0xdfe2f1, 0xf5f7ff, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, +]); +Base16Theme.add("atlas", "Atlas", [ + 0x002635, 0x00384d, 0x517F8D, 0x6C8B91, + 0x869696, 0xa1a19a, 0xe6e6dc, 0xfafaf8, + 0xff5a67, 0xf08e48, 0xffcc1b, 0x7fc06e, + 0x14747e, 0x5dd7b9, 0x9a70a4, 0xc43060, +]); +Base16Theme.add("bespin", "Bespin", [ + 0x28211c, 0x36312e, 0x5e5d5c, 0x666666, + 0x797977, 0x8a8986, 0x9d9b97, 0xbaae9e, + 0xcf6a4c, 0xcf7d34, 0xf9ee98, 0x54be0d, + 0xafc4db, 0x5ea6ea, 0x9b859d, 0x937121, +]); +Base16Theme.add("black-metal-bathory", "Black Metal (Bathory)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xe78a53, 0xfbcb97, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-burzum", "Black Metal (Burzum)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x99bbaa, 0xddeecc, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-dark-funeral", "Black Metal (Dark Funeral)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x5f81a5, 0xd0dfee, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-gorgoroth", "Black Metal (Gorgoroth)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x8c7f70, 0x9b8d7f, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-immortal", "Black Metal (Immortal)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x556677, 0x7799bb, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-khold", "Black Metal (Khold)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x974b46, 0xeceee3, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-marduk", "Black Metal (Marduk)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x626b67, 0xa5aaa7, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-mayhem", "Black Metal (Mayhem)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xeecc6c, 0xf3ecd4, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-nile", "Black Metal (Nile)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x777755, 0xaa9988, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-venom", "Black Metal (Venom)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x79241f, 0xf8f7f2, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal", "Black Metal", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xa06666, 0xdd9999, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("brewer", "Brewer", [ + 0x0c0d0e, 0x2e2f30, 0x515253, 0x737475, + 0x959697, 0xb7b8b9, 0xdadbdc, 0xfcfdfe, + 0xe31a1c, 0xe6550d, 0xdca060, 0x31a354, + 0x80b1d3, 0x3182bd, 0x756bb1, 0xb15928, +]); +Base16Theme.add("bright", "Bright", [ + 0x000000, 0x303030, 0x505050, 0xb0b0b0, + 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, 0xffffff, + 0xfb0120, 0xfc6d24, 0xfda331, 0xa1c659, + 0x76c7b7, 0x6fb3d2, 0xd381c3, 0xbe643c, +]); +Base16Theme.add("brogrammer", "Brogrammer", [ + 0x1f1f1f, 0xf81118, 0x2dc55e, 0xecba0f, + 0x2a84d2, 0x4e5ab7, 0x1081d6, 0xd6dbe5, + 0xd6dbe5, 0xde352e, 0x1dd361, 0xf3bd09, + 0x1081d6, 0x5350b9, 0x0f7ddb, 0xffffff, +]); +Base16Theme.add("brushtrees-dark", "Brush Trees Dark", [ + 0x485867, 0x5A6D7A, 0x6D828E, 0x8299A1, + 0x98AFB5, 0xB0C5C8, 0xC9DBDC, 0xE3EFEF, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, +]); +Base16Theme.add("brushtrees", "Brush Trees", [ + 0xE3EFEF, 0xC9DBDC, 0xB0C5C8, 0x98AFB5, + 0x8299A1, 0x6D828E, 0x5A6D7A, 0x485867, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, +]); +Base16Theme.add("chalk", "Chalk", [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, + 0xfb9fb1, 0xeda987, 0xddb26f, 0xacc267, + 0x12cfc0, 0x6fc2ef, 0xe1a3ee, 0xdeaf8f, +]); +Base16Theme.add("circus", "Circus", [ + 0x191919, 0x202020, 0x303030, 0x5f5a60, + 0x505050, 0xa7a7a7, 0x808080, 0xffffff, + 0xdc657d, 0x4bb1a7, 0xc3ba63, 0x84b97c, + 0x4bb1a7, 0x639ee4, 0xb888e2, 0xb888e2, +]); +Base16Theme.add("classic-dark", "Classic Dark", [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xF5F5F5, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, +]); +Base16Theme.add("classic-light", "Classic Light", [ + 0xF5F5F5, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x505050, 0x303030, 0x202020, 0x151515, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, +]); +Base16Theme.add("codeschool", "Codeschool", [ + 0x232c31, 0x1c3657, 0x2a343a, 0x3f4944, + 0x84898c, 0x9ea7a6, 0xa7cfa3, 0xb5d8f6, + 0x2a5491, 0x43820d, 0xa03b1e, 0x237986, + 0xb02f30, 0x484d79, 0xc59820, 0xc98344, +]); +Base16Theme.add("cupcake", "Cupcake", [ + 0xfbf1f2, 0xf2f1f4, 0xd8d5dd, 0xbfb9c6, + 0xa59daf, 0x8b8198, 0x72677E, 0x585062, + 0xD57E85, 0xEBB790, 0xDCB16C, 0xA3B367, + 0x69A9A7, 0x7297B9, 0xBB99B4, 0xBAA58C, +]); +Base16Theme.add("cupertino", "Cupertino", [ + 0xffffff, 0xc0c0c0, 0xc0c0c0, 0x808080, + 0x808080, 0x404040, 0x404040, 0x5e5e5e, + 0xc41a15, 0xeb8500, 0x826b28, 0x007400, + 0x318495, 0x0000ff, 0xa90d91, 0x826b28, +]); +Base16Theme.add("darktooth", "Darktooth", [ + 0x1D2021, 0x32302F, 0x504945, 0x665C54, + 0x928374, 0xA89984, 0xD5C4A1, 0xFDF4C1, + 0xFB543F, 0xFE8625, 0xFAC03B, 0x95C085, + 0x8BA59B, 0x0D6678, 0x8F4673, 0xA87322, +]); +Base16Theme.add("default-dark", "Default Dark", [ + 0x181818, 0x282828, 0x383838, 0x585858, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, +]); +Base16Theme.add("default-light", "Default Light", [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, +]); +Base16Theme.add("dracula", "Dracula", [ + 0x282936, 0x3a3c4e, 0x4d4f68, 0x626483, + 0x62d6e8, 0xe9e9f4, 0xf1f2f8, 0xf7f7fb, + 0xea51b2, 0xb45bcf, 0x00f769, 0xebff87, + 0xa1efe4, 0x62d6e8, 0xb45bcf, 0x00f769, +]); +Base16Theme.add("eighties", "Eighties", [ + 0x2d2d2d, 0x393939, 0x515151, 0x747369, + 0xa09f93, 0xd3d0c8, 0xe8e6df, 0xf2f0ec, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xd27b53, +]); +Base16Theme.add("embers", "Embers", [ + 0x16130F, 0x2C2620, 0x433B32, 0x5A5047, + 0x8A8075, 0xA39A90, 0xBEB6AE, 0xDBD6D1, + 0x826D57, 0x828257, 0x6D8257, 0x57826D, + 0x576D82, 0x6D5782, 0x82576D, 0x825757, +]); +Base16Theme.add("flat", "Flat", [ + 0x2C3E50, 0x34495E, 0x7F8C8D, 0x95A5A6, + 0xBDC3C7, 0xe0e0e0, 0xf5f5f5, 0xECF0F1, + 0xE74C3C, 0xE67E22, 0xF1C40F, 0x2ECC71, + 0x1ABC9C, 0x3498DB, 0x9B59B6, 0xbe643c, +]); +Base16Theme.add("fruit-soda", "Fruit Soda", [ + 0xf1ecf1, 0xe0dee0, 0xd8d5d5, 0xb5b4b6, + 0x979598, 0x515151, 0x474545, 0x2d2c2c, + 0xfe3e31, 0xfe6d08, 0xf7e203, 0x47f74c, + 0x0f9cfd, 0x2931df, 0x611fce, 0xb16f40, +]); +Base16Theme.add("github", "Github", [ + 0xffffff, 0xf5f5f5, 0xc8c8fa, 0x969896, + 0xe8e8e8, 0x333333, 0xffffff, 0xffffff, + 0xed6a43, 0x0086b3, 0x795da3, 0x183691, + 0x183691, 0x795da3, 0xa71d5d, 0x333333, +]); +Base16Theme.add("google-dark", "Google Dark", [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, +]); +Base16Theme.add("google-light", "Google Light", [ + 0xffffff, 0xe0e0e0, 0xc5c8c6, 0xb4b7b4, + 0x969896, 0x373b41, 0x282a2e, 0x1d1f21, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, +]); +Base16Theme.add("grayscale-dark", "Grayscale Dark", [ + 0x101010, 0x252525, 0x464646, 0x525252, + 0xababab, 0xb9b9b9, 0xe3e3e3, 0xf7f7f7, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, +]); +Base16Theme.add("grayscale-light", "Grayscale Light", [ + 0xf7f7f7, 0xe3e3e3, 0xb9b9b9, 0xababab, + 0x525252, 0x464646, 0x252525, 0x101010, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, +]); +Base16Theme.add("greenscreen", "Green Screen", [ + 0x001100, 0x003300, 0x005500, 0x007700, + 0x009900, 0x00bb00, 0x00dd00, 0x00ff00, + 0x007700, 0x009900, 0x007700, 0x00bb00, + 0x005500, 0x009900, 0x00bb00, 0x005500, +]); +Base16Theme.add("gruvbox-dark-hard", "Gruvbox dark, hard", [ + 0x1d2021, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, +]); +Base16Theme.add("gruvbox-dark-medium", "Gruvbox dark, medium", [ + 0x282828, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, +]); +Base16Theme.add("gruvbox-dark-pale", "Gruvbox dark, pale", [ + 0x262626, 0x3a3a3a, 0x4e4e4e, 0x8a8a8a, + 0x949494, 0xdab997, 0xd5c4a1, 0xebdbb2, + 0xd75f5f, 0xff8700, 0xffaf00, 0xafaf00, + 0x85ad85, 0x83adad, 0xd485ad, 0xd65d0e, +]); +Base16Theme.add("gruvbox-dark-soft", "Gruvbox dark, soft", [ + 0x32302f, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, +]); +Base16Theme.add("gruvbox-light-hard", "Gruvbox light, hard", [ + 0xf9f5d7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, +]); +Base16Theme.add("gruvbox-light-medium", "Gruvbox light, medium", [ + 0xfbf1c7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, +]); +Base16Theme.add("gruvbox-light-soft", "Gruvbox light, soft", [ + 0xf2e5bc, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, +]); +Base16Theme.add("harmonic-dark", "Harmonic16 Dark", [ + 0x0b1c2c, 0x223b54, 0x405c79, 0x627e99, + 0xaabcce, 0xcbd6e2, 0xe5ebf1, 0xf7f9fb, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, +]); +Base16Theme.add("harmonic-light", "Harmonic16 Light", [ + 0xf7f9fb, 0xe5ebf1, 0xcbd6e2, 0xaabcce, + 0x627e99, 0x405c79, 0x223b54, 0x0b1c2c, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, +]); +Base16Theme.add("heetch-light", "Heetch Light", [ + 0xfeffff, 0x392551, 0x7b6d8b, 0x9c92a8, + 0xddd6e5, 0x5a496e, 0x470546, 0x190134, + 0x27d9d5, 0xbdb6c5, 0x5ba2b6, 0xf80059, + 0xc33678, 0x47f9f5, 0xbd0152, 0xdedae2, +]); +Base16Theme.add("heetch", "Heetch Dark", [ + 0x190134, 0x392551, 0x5A496E, 0x7B6D8B, + 0x9C92A8, 0xBDB6C5, 0xDEDAE2, 0xFEFFFF, + 0x27D9D5, 0x5BA2B6, 0x8F6C97, 0xC33678, + 0xF80059, 0xBD0152, 0x82034C, 0x470546, +]); +Base16Theme.add("helios", "Helios", [ + 0x1d2021, 0x383c3e, 0x53585b, 0x6f7579, + 0xcdcdcd, 0xd5d5d5, 0xdddddd, 0xe5e5e5, + 0xd72638, 0xeb8413, 0xf19d1a, 0x88b92d, + 0x1ba595, 0x1e8bac, 0xbe4264, 0xc85e0d, +]); +Base16Theme.add("hopscotch", "Hopscotch", [ + 0x322931, 0x433b42, 0x5c545b, 0x797379, + 0x989498, 0xb9b5b8, 0xd5d3d5, 0xffffff, + 0xdd464c, 0xfd8b19, 0xfdcc59, 0x8fc13e, + 0x149b93, 0x1290bf, 0xc85e7c, 0xb33508, +]); +Base16Theme.add("horizon-dark", "Horizon Dark", [ + 0x1c1e26, 0x232530, 0x2e303e, 0x676a8d, + 0xced1d0, 0xcbced0, 0xdcdfe4, 0xe3e6ee, + 0xe93c58, 0xe58d7d, 0xefb993, 0xefaf8e, + 0x24a8b4, 0xdf5273, 0xb072d1, 0xe4a382, +]); +Base16Theme.add("ia-dark", "iA Dark", [ + 0x1a1a1a, 0x222222, 0x1d414d, 0x767676, + 0xb8b8b8, 0xcccccc, 0xe8e8e8, 0xf8f8f8, + 0xd88568, 0xd86868, 0xb99353, 0x83a471, + 0x7c9cae, 0x8eccdd, 0xb98eb2, 0x8b6c37, +]); +Base16Theme.add("ia-light", "iA Light", [ + 0xf6f6f6, 0xdedede, 0xbde5f2, 0x898989, + 0x767676, 0x181818, 0xe8e8e8, 0xf8f8f8, + 0x9c5a02, 0xc43e18, 0xc48218, 0x38781c, + 0x2d6bb1, 0x48bac2, 0xa94598, 0x8b6c37, +]); +Base16Theme.add("icy", "Icy Dark", [ + 0x021012, 0x031619, 0x041f23, 0x052e34, + 0x064048, 0x095b67, 0x0c7c8c, 0x109cb0, + 0x16c1d9, 0xb3ebf2, 0x80deea, 0x4dd0e1, + 0x26c6da, 0x00bcd4, 0x00acc1, 0x0097a7, +]); +Base16Theme.add("irblack", "IR Black", [ + 0x000000, 0x242422, 0x484844, 0x6c6c66, + 0x918f88, 0xb5b3aa, 0xd9d7cc, 0xfdfbee, + 0xff6c60, 0xe9c062, 0xffffb6, 0xa8ff60, + 0xc6c5fe, 0x96cbfe, 0xff73fd, 0xb18a3d, +]); +Base16Theme.add("isotope", "Isotope", [ + 0x000000, 0x404040, 0x606060, 0x808080, + 0xc0c0c0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xff0000, 0xff9900, 0xff0099, 0x33ff00, + 0x00ffff, 0x0066ff, 0xcc00ff, 0x3300ff, +]); +Base16Theme.add("macintosh", "Macintosh", [ + 0x000000, 0x404040, 0x404040, 0x808080, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0xffffff, + 0xdd0907, 0xff6403, 0xfbf305, 0x1fb714, + 0x02abea, 0x0000d3, 0x4700a5, 0x90713a, +]); +Base16Theme.add("marrakesh", "Marrakesh", [ + 0x201602, 0x302e00, 0x5f5b17, 0x6c6823, + 0x86813b, 0x948e48, 0xccc37a, 0xfaf0a5, + 0xc35359, 0xb36144, 0xa88339, 0x18974e, + 0x75a738, 0x477ca1, 0x8868b3, 0xb3588e, +]); +Base16Theme.add("materia", "Materia", [ + 0x263238, 0x2C393F, 0x37474F, 0x707880, + 0xC9CCD3, 0xCDD3DE, 0xD5DBE5, 0xFFFFFF, + 0xEC5F67, 0xEA9560, 0xFFCC00, 0x8BD649, + 0x80CBC4, 0x89DDFF, 0x82AAFF, 0xEC5F67, +]); +Base16Theme.add("material-darker", "Material Darker", [ + 0x212121, 0x303030, 0x353535, 0x4A4A4A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, +]); +Base16Theme.add("material-lighter", "Material Lighter", [ + 0xFAFAFA, 0xE7EAEC, 0xCCEAE7, 0xCCD7DA, + 0x8796B0, 0x80CBC4, 0x80CBC4, 0xFFFFFF, + 0xFF5370, 0xF76D47, 0xFFB62C, 0x91B859, + 0x39ADB5, 0x6182B8, 0x7C4DFF, 0xE53935, +]); +Base16Theme.add("material-palenight", "Material Palenight", [ + 0x292D3E, 0x444267, 0x32374D, 0x676E95, + 0x8796B0, 0x959DCB, 0x959DCB, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, +]); +Base16Theme.add("material-vivid", "Material Vivid", [ + 0x202124, 0x27292c, 0x323639, 0x44464d, + 0x676c71, 0x80868b, 0x9e9e9e, 0xffffff, + 0xf44336, 0xff9800, 0xffeb3b, 0x00e676, + 0x00bcd4, 0x2196f3, 0x673ab7, 0x8d6e63, +]); +Base16Theme.add("material", "Material", [ + 0x263238, 0x2E3C43, 0x314549, 0x546E7A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, +]); +Base16Theme.add("mellow-purple", "Mellow Purple", [ + 0x1e0528, 0x1A092D, 0x331354, 0x320f55, + 0x873582, 0xffeeff, 0xffeeff, 0xf8c0ff, + 0x00d9e9, 0xaa00a3, 0x955ae7, 0x05cb0d, + 0xb900b1, 0x550068, 0x8991bb, 0x4d6fff, +]); +Base16Theme.add("mexico-light", "Mexico Light", [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf79a0e, 0x538947, + 0x4b8093, 0x7cafc2, 0x96609e, 0xa16946, +]); +Base16Theme.add("mocha", "Mocha", [ + 0x3B3228, 0x534636, 0x645240, 0x7e705a, + 0xb8afad, 0xd0c8c6, 0xe9e1dd, 0xf5eeeb, + 0xcb6077, 0xd28b71, 0xf4bc87, 0xbeb55b, + 0x7bbda4, 0x8ab3b5, 0xa89bb9, 0xbb9584, +]); +Base16Theme.add("monokai", "Monokai", [ + 0x272822, 0x383830, 0x49483e, 0x75715e, + 0xa59f85, 0xf8f8f2, 0xf5f4f1, 0xf9f8f5, + 0xf92672, 0xfd971f, 0xf4bf75, 0xa6e22e, + 0xa1efe4, 0x66d9ef, 0xae81ff, 0xcc6633, +]); +Base16Theme.add("nord", "Nord", [ + 0x2E3440, 0x3B4252, 0x434C5E, 0x4C566A, + 0xD8DEE9, 0xE5E9F0, 0xECEFF4, 0x8FBCBB, + 0x88C0D0, 0x81A1C1, 0x5E81AC, 0xBF616A, + 0xD08770, 0xEBCB8B, 0xA3BE8C, 0xB48EAD, +]); +Base16Theme.add("ocean", "Ocean", [ + 0x2b303b, 0x343d46, 0x4f5b66, 0x65737e, + 0xa7adba, 0xc0c5ce, 0xdfe1e8, 0xeff1f5, + 0xbf616a, 0xd08770, 0xebcb8b, 0xa3be8c, + 0x96b5b4, 0x8fa1b3, 0xb48ead, 0xab7967, +]); +Base16Theme.add("oceanicnext", "OceanicNext", [ + 0x1B2B34, 0x343D46, 0x4F5B66, 0x65737E, + 0xA7ADBA, 0xC0C5CE, 0xCDD3DE, 0xD8DEE9, + 0xEC5F67, 0xF99157, 0xFAC863, 0x99C794, + 0x5FB3B3, 0x6699CC, 0xC594C5, 0xAB7967, +]); +Base16Theme.add("one-light", "One Light", [ + 0xfafafa, 0xf0f0f1, 0xe5e5e6, 0xa0a1a7, + 0x696c77, 0x383a42, 0x202227, 0x090a0b, + 0xca1243, 0xd75f00, 0xc18401, 0x50a14f, + 0x0184bc, 0x4078f2, 0xa626a4, 0x986801, +]); +Base16Theme.add("onedark", "OneDark", [ + 0x282c34, 0x353b45, 0x3e4451, 0x545862, + 0x565c64, 0xabb2bf, 0xb6bdca, 0xc8ccd4, + 0xe06c75, 0xd19a66, 0xe5c07b, 0x98c379, + 0x56b6c2, 0x61afef, 0xc678dd, 0xbe5046, +]); +Base16Theme.add("outrun-dark", "Outrun Dark", [ + 0x00002A, 0x20204A, 0x30305A, 0x50507A, + 0xB0B0DA, 0xD0D0FA, 0xE0E0FF, 0xF5F5FF, + 0xFF4242, 0xFC8D28, 0xF3E877, 0x59F176, + 0x0EF0F0, 0x66B0FF, 0xF10596, 0xF003EF, +]); +Base16Theme.add("papercolor-dark", "PaperColor Dark", [ + 0x1c1c1c, 0xaf005f, 0x5faf00, 0xd7af5f, + 0x5fafd7, 0x808080, 0xd7875f, 0xd0d0d0, + 0x585858, 0x5faf5f, 0xafd700, 0xaf87d7, + 0xffaf00, 0xff5faf, 0x00afaf, 0x5f8787, +]); +Base16Theme.add("papercolor-light", "PaperColor Light", [ + 0xeeeeee, 0xaf0000, 0x008700, 0x5f8700, + 0x0087af, 0x878787, 0x005f87, 0x444444, + 0xbcbcbc, 0xd70000, 0xd70087, 0x8700af, + 0xd75f00, 0xd75f00, 0x005faf, 0x005f87, +]); +Base16Theme.add("paraiso", "Paraiso", [ + 0x2f1e2e, 0x41323f, 0x4f424c, 0x776e71, + 0x8d8687, 0xa39e9b, 0xb9b6b0, 0xe7e9db, + 0xef6155, 0xf99b15, 0xfec418, 0x48b685, + 0x5bc4bf, 0x06b6ef, 0x815ba4, 0xe96ba8, +]); +Base16Theme.add("phd", "PhD", [ + 0x061229, 0x2a3448, 0x4d5666, 0x717885, + 0x9a99a3, 0xb8bbc2, 0xdbdde0, 0xffffff, + 0xd07346, 0xf0a000, 0xfbd461, 0x99bf52, + 0x72b9bf, 0x5299bf, 0x9989cc, 0xb08060, +]); +Base16Theme.add("pico", "Pico", [ + 0x000000, 0x1d2b53, 0x7e2553, 0x008751, + 0xab5236, 0x5f574f, 0xc2c3c7, 0xfff1e8, + 0xff004d, 0xffa300, 0xfff024, 0x00e756, + 0x29adff, 0x83769c, 0xff77a8, 0xffccaa, +]); +Base16Theme.add("pop", "Pop", [ + 0x000000, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xeb008a, 0xf29333, 0xf8ca12, 0x37b349, + 0x00aabb, 0x0e5a94, 0xb31e8d, 0x7a2d00, +]); +Base16Theme.add("porple", "Porple", [ + 0x292c36, 0x333344, 0x474160, 0x65568a, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xf84547, 0xd28e5d, 0xefa16b, 0x95c76f, + 0x64878f, 0x8485ce, 0xb74989, 0x986841, +]); +Base16Theme.add("qualia", "Qualia", [ + 0x101010, 0x454545, 0x454545, 0x454545, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0x454545, + 0xefa6a2, 0xa3b8ef, 0xe6a3dc, 0x80c990, + 0xc8c874, 0x50cacd, 0xe0af85, 0x808080, +]); +Base16Theme.add("railscasts", "Railscasts", [ + 0x2b2b2b, 0x272935, 0x3a4055, 0x5a647e, + 0xd4cfc9, 0xe6e1dc, 0xf4f1ed, 0xf9f7f3, + 0xda4939, 0xcc7833, 0xffc66d, 0xa5c261, + 0x519f50, 0x6d9cbe, 0xb6b3eb, 0xbc9458, +]); +Base16Theme.add("rebecca", "Rebecca", [ + 0x292a44, 0x663399, 0x383a62, 0x666699, + 0xa0a0c5, 0xf1eff8, 0xccccff, 0x53495d, + 0xa0a0c5, 0xefe4a1, 0xae81ff, 0x6dfedf, + 0x8eaee0, 0x2de0a7, 0x7aa5ff, 0xff79c6, +]); +Base16Theme.add("seti", "Seti UI", [ + 0x151718, 0x282a2b, 0x3B758C, 0x41535B, + 0x43a5d5, 0xd6d6d6, 0xeeeeee, 0xffffff, + 0xcd3f45, 0xdb7b55, 0xe6cd69, 0x9fca56, + 0x55dbbe, 0x55b5db, 0xa074c4, 0x8a553f, +]); +Base16Theme.add("shapeshifter", "Shapeshifter", [ + 0xf9f9f9, 0xe0e0e0, 0xababab, 0x555555, + 0x343434, 0x102015, 0x040404, 0x000000, + 0xe92f2f, 0xe09448, 0xdddd13, 0x0ed839, + 0x23edda, 0x3b48e3, 0xf996e2, 0x69542d, +]); +Base16Theme.add("snazzy", "Snazzy", [ + 0x282a36, 0x34353e, 0x43454f, 0x78787e, + 0xa5a5a9, 0xe2e4e5, 0xeff0eb, 0xf1f1f0, + 0xff5c57, 0xff9f43, 0xf3f99d, 0x5af78e, + 0x9aedfe, 0x57c7ff, 0xff6ac1, 0xb2643c, +]); +Base16Theme.add("solarflare", "Solar Flare", [ + 0x18262F, 0x222E38, 0x586875, 0x667581, + 0x85939E, 0xA6AFB8, 0xE8E9ED, 0xF5F7FA, + 0xEF5253, 0xE66B2B, 0xE4B51C, 0x7CC844, + 0x52CBB0, 0x33B5E1, 0xA363D5, 0xD73C9A, +]); +Base16Theme.add("solarized-dark", "Solarized Dark", [ + 0x002b36, 0x073642, 0x586e75, 0x657b83, + 0x839496, 0x93a1a1, 0xeee8d5, 0xfdf6e3, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, +]); +Base16Theme.add("solarized-light", "Solarized Light", [ + 0xfdf6e3, 0xeee8d5, 0x93a1a1, 0x839496, + 0x657b83, 0x586e75, 0x073642, 0x002b36, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, +]); +Base16Theme.add("spacemacs", "Spacemacs", [ + 0x1f2022, 0x282828, 0x444155, 0x585858, + 0xb8b8b8, 0xa3a3a3, 0xe8e8e8, 0xf8f8f8, + 0xf2241f, 0xffa500, 0xb1951d, 0x67b11d, + 0x2d9574, 0x4f97d7, 0xa31db1, 0xb03060, +]); +Base16Theme.add("summerfruit-dark", "Summerfruit Dark", [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xFFFFFF, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, +]); +Base16Theme.add("summerfruit-light", "Summerfruit Light", [ + 0xFFFFFF, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x000000, 0x101010, 0x151515, 0x202020, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, +]); +Base16Theme.add("synth-midnight-dark", "Synth Midnight", [ + 0x040404, 0x141414, 0x242424, 0x61507A, + 0xBFBBBF, 0xDFDBDF, 0xEFEBEF, 0xFFFBFF, + 0xB53B50, 0xE4600E, 0xDAE84D, 0x06EA61, + 0x7CEDE9, 0x03AEFF, 0xEA5CE2, 0x9D4D0E, +]); +Base16Theme.add("tomorrow-night-eighties", "Tomorrow Night Eighties", [ + 0x2d2d2d, 0x393939, 0x515151, 0x999999, + 0xb4b7b4, 0xcccccc, 0xe0e0e0, 0xffffff, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xa3685a, +]); +Base16Theme.add("tomorrow-night", "Tomorrow Night", [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xcc6666, 0xde935f, 0xf0c674, 0xb5bd68, + 0x8abeb7, 0x81a2be, 0xb294bb, 0xa3685a, +]); +Base16Theme.add("tomorrow", "Tomorrow", [ + 0xffffff, 0xe0e0e0, 0xd6d6d6, 0x8e908c, + 0x969896, 0x4d4d4c, 0x282a2e, 0x1d1f21, + 0xc82829, 0xf5871f, 0xeab700, 0x718c00, + 0x3e999f, 0x4271ae, 0x8959a8, 0xa3685a, +]); +Base16Theme.add("tube", "London Tube", [ + 0x231f20, 0x1c3f95, 0x5a5758, 0x737171, + 0x959ca1, 0xd9d8d8, 0xe7e7e8, 0xffffff, + 0xee2e24, 0xf386a1, 0xffd204, 0x00853e, + 0x85cebc, 0x009ddc, 0x98005d, 0xb06110, +]); +Base16Theme.add("twilight", "Twilight", [ + 0x1e1e1e, 0x323537, 0x464b50, 0x5f5a60, + 0x838184, 0xa7a7a7, 0xc3c3c3, 0xffffff, + 0xcf6a4c, 0xcda869, 0xf9ee98, 0x8f9d6a, + 0xafc4db, 0x7587a6, 0x9b859d, 0x9b703f, +]); +Base16Theme.add("unikitty-dark", "Unikitty Dark", [ + 0x2e2a31, 0x4a464d, 0x666369, 0x838085, + 0x9f9da2, 0xbcbabe, 0xd8d7da, 0xf5f4f7, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x796af5, 0xbb60ea, 0xc720ca, +]); +Base16Theme.add("unikitty-light", "Unikitty Light", [ + 0xffffff, 0xe1e1e2, 0xc4c3c5, 0xa7a5a8, + 0x89878b, 0x6c696e, 0x4f4b51, 0x322d34, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x775dff, 0xaa17e6, 0xe013d0, +]); +Base16Theme.add("woodland", "Woodland", [ + 0x231e18, 0x302b25, 0x48413a, 0x9d8b70, + 0xb4a490, 0xcabcb1, 0xd7c8bc, 0xe4d4c8, + 0xd35c5c, 0xca7f32, 0xe0ac16, 0xb7ba53, + 0x6eb958, 0x88a4d3, 0xbb90e2, 0xb49368, +]); +Base16Theme.add("xcode-dusk", "XCode Dusk", [ + 0x282B35, 0x3D4048, 0x53555D, 0x686A71, + 0x7E8086, 0x939599, 0xA9AAAE, 0xBEBFC2, + 0xB21889, 0x786DC5, 0x438288, 0xDF0002, + 0x00A0BE, 0x790EAD, 0xB21889, 0xC77C48, +]); +Base16Theme.add("zenburn", "Zenburn", [ + 0x383838, 0x404040, 0x606060, 0x6f6f6f, + 0x808080, 0xdcdccc, 0xc0c0c0, 0xffffff, + 0xdca3a3, 0xdfaf8f, 0xe0cf9f, 0x5f7f5f, + 0x93e0e3, 0x7cb8bb, 0xdc8cc3, 0x000000, +]); diff --git a/js/colors/ColorSchemeModel.js b/js/colors/ColorSchemeModel.js new file mode 100644 index 0000000..3aba664 --- /dev/null +++ b/js/colors/ColorSchemeModel.js @@ -0,0 +1,111 @@ +var _a; +import GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; +import Gtk from 'gi://Gtk?version=4.0'; +import { Base16Theme } from './Base16Theme.js'; +export class ColorSchemeModel { + constructor() { + let builder = new TreeModelBuilder(); + this.treeModel = builder.buildTreeModel(); + this.selection = Gtk.SingleSelection.new(this.treeModel); + } +} +class ColorModelNode extends GObject.Object { + constructor(data) { + super(); + this.data = data; + } + get text() { + return this.data.name; + } + child_model() { + if (this.data instanceof ColorThemeCategory) { + return this.data.children; + } + else { + return null; + } + } + isThemeWithId(id) { + return (this.data instanceof Base16Theme) && + (this.data.id === id); + } + hasChildThemeWithId(id) { + const child_model = this.child_model(); + if (child_model) { + for (let i = 0; i < child_model.n_items; i++) { + let node = child_model.get_item(i); + if (node.isThemeWithId(id)) { + return true; + } + } + } + return false; + } + static newScheme(color) { + return new _a(color); + } + static newCategory(category) { + return new _a(category); + } +} +_a = ColorModelNode; +(() => { + GObject.registerClass({ + GTypeName: 'ColorModelNode', + Properties: { + 'text': GObject.ParamSpec.string('text', '', '', GObject.ParamFlags.READWRITE, ''), + } + }, _a); +})(); +class ColorThemeCategory { + constructor(name) { + this.name = name; + this.children = new Gio.ListStore(ColorModelNode); + } + append(color) { + this.children.append(ColorModelNode.newScheme(color)); + } +} +class TreeModelBuilder { + constructor() { + this.categories = Base16Theme.CATEGORIES; + this.currentCategory = null; + this.storeModel = new Gio.ListStore(ColorModelNode); + } + buildTreeModel() { + Base16Theme.THEMES.forEach(theme => this.addTheme(theme)); + return Gtk.TreeListModel.new(this.storeModel, false, false, item => item.child_model()); + } + appendCurrentCategory() { + if (this.currentCategory) { + this.storeModel.append(ColorModelNode.newCategory(this.currentCategory)); + this.currentCategory = null; + this.categories.shift(); + } + } + matchesNextCategory(theme) { + if (this.categories.length === 0) { + return false; + } + let [id, _name] = this.categories[0]; + return theme.id === id; + } + addTheme(theme) { + if (this.matchesNextCategory(theme)) { + this.addToCategory(theme); + return; + } + if (this.currentCategory) { + this.appendCurrentCategory(); + } + this.storeModel.append(ColorModelNode.newScheme(theme)); + } + addToCategory(theme) { + if (!this.currentCategory) { + let [_id, name] = this.categories[0]; + this.currentCategory = new ColorThemeCategory(name); + } + this.currentCategory.append(theme); + } +} diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..f284c98 --- /dev/null +++ b/js/main.js @@ -0,0 +1,6 @@ +import 'gi://Gdk?version=4.0'; +import 'gi://Gtk?version=4.0'; +import { Application } from './Application.js'; +export function main(argv) { + return new Application().run(argv); +} diff --git a/js/model/Base16Themes.js b/js/model/Base16Themes.js new file mode 100644 index 0000000..3b5c469 --- /dev/null +++ b/js/model/Base16Themes.js @@ -0,0 +1,809 @@ +const TERM_MAP = [ + 0x00, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x05, + 0x03, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x07, + 0x09, 0x0F, 0x01, 0x02, 0x04, 0x06, +]; +export class Base16Theme { + constructor(id, name, colors) { + this.id = id; + this.name = name; + this.colors = colors; + } + static add(id, name, colors) { + const theme = new Base16Theme(id, name, colors); + Base16Theme.THEME_LIST.push(theme); + Base16Theme.THEMES.set(id, theme); + } + static lookup(id) { + return Base16Theme.THEMES.get(id); + } + color(idx) { + let hex = this.colors[idx].toString(16).padStart(6, '0'); + return `#${hex}`; + } + terminal_background() { + return this.color(0); + } + terminal_foreground() { + return this.color(5); + } + terminal_palette_color(idx) { + return this.color(TERM_MAP[idx]); + } +} +Base16Theme.THEMES = new Map(); +Base16Theme.THEME_LIST = []; +Base16Theme.add("3024", "3024", [ + 0x090300, 0x3a3432, 0x4a4543, 0x5c5855, + 0x807d7c, 0xa5a2a2, 0xd6d5d4, 0xf7f7f7, + 0xdb2d20, 0xe8bbd0, 0xfded02, 0x01a252, + 0xb5e4f4, 0x01a0e4, 0xa16a94, 0xcdab53, +]); +Base16Theme.add("apathy", 'Apathy', [ + 0x031A16, 0x0B342D, 0x184E45, 0x2B685E, + 0x5F9C92, 0x81B5AC, 0xA7CEC8, 0xD2E7E4, + 0x3E9688, 0x3E7996, 0x3E4C96, 0x883E96, + 0x963E4C, 0x96883E, 0x4C963E, 0x3E965B, +]); +Base16Theme.add("ashes", "Ashes", [ + 0x1C2023, 0x393F45, 0x565E65, 0x747C84, + 0xADB3BA, 0xC7CCD1, 0xDFE2E5, 0xF3F4F5, + 0xC7AE95, 0xC7C795, 0xAEC795, 0x95C7AE, + 0x95AEC7, 0xAE95C7, 0xC795AE, 0xC79595, +]); +Base16Theme.add("atelier-cave-light", "Atelier Cave Light", [ + 0xefecf4, 0xe2dfe7, 0x8b8792, 0x7e7887, + 0x655f6d, 0x585260, 0x26232a, 0x19171c, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, +]); +Base16Theme.add("atelier-cave", "Atelier Cave", [ + 0x19171c, 0x26232a, 0x585260, 0x655f6d, + 0x7e7887, 0x8b8792, 0xe2dfe7, 0xefecf4, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, +]); +Base16Theme.add("atelier-dune-light", "Atelier Dune Light", [ + 0xfefbec, 0xe8e4cf, 0xa6a28c, 0x999580, + 0x7d7a68, 0x6e6b5e, 0x292824, 0x20201d, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, +]); +Base16Theme.add("atelier-dune", "Atelier Dune", [ + 0x20201d, 0x292824, 0x6e6b5e, 0x7d7a68, + 0x999580, 0xa6a28c, 0xe8e4cf, 0xfefbec, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, +]); +Base16Theme.add("atelier-estuary-light", "Atelier Estuary Light", [ + 0xf4f3ec, 0xe7e6df, 0x929181, 0x878573, + 0x6c6b5a, 0x5f5e4e, 0x302f27, 0x22221b, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, +]); +Base16Theme.add("atelier-estuary", "Atelier Estuary", [ + 0x22221b, 0x302f27, 0x5f5e4e, 0x6c6b5a, + 0x878573, 0x929181, 0xe7e6df, 0xf4f3ec, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, +]); +Base16Theme.add("atelier-forest-light", "Atelier Forest Light", [ + 0xf1efee, 0xe6e2e0, 0xa8a19f, 0x9c9491, + 0x766e6b, 0x68615e, 0x2c2421, 0x1b1918, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, +]); +Base16Theme.add("atelier-forest", "Atelier Forest", [ + 0x1b1918, 0x2c2421, 0x68615e, 0x766e6b, + 0x9c9491, 0xa8a19f, 0xe6e2e0, 0xf1efee, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, +]); +Base16Theme.add("atelier-heath-light", "Atelier Heath Light", [ + 0xf7f3f7, 0xd8cad8, 0xab9bab, 0x9e8f9e, + 0x776977, 0x695d69, 0x292329, 0x1b181b, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, +]); +Base16Theme.add("atelier-heath", "Atelier Heath", [ + 0x1b181b, 0x292329, 0x695d69, 0x776977, + 0x9e8f9e, 0xab9bab, 0xd8cad8, 0xf7f3f7, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, +]); +Base16Theme.add("atelier-lakeside-light", "Atelier Lakeside Light", [ + 0xebf8ff, 0xc1e4f6, 0x7ea2b4, 0x7195a8, + 0x5a7b8c, 0x516d7b, 0x1f292e, 0x161b1d, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, +]); +Base16Theme.add("atelier-lakeside", "Atelier Lakeside", [ + 0x161b1d, 0x1f292e, 0x516d7b, 0x5a7b8c, + 0x7195a8, 0x7ea2b4, 0xc1e4f6, 0xebf8ff, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, +]); +Base16Theme.add("atelier-plateau-light", "Atelier Plateau Light", [ + 0xf4ecec, 0xe7dfdf, 0x8a8585, 0x7e7777, + 0x655d5d, 0x585050, 0x292424, 0x1b1818, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, +]); +Base16Theme.add("atelier-plateau", "Atelier Plateau", [ + 0x1b1818, 0x292424, 0x585050, 0x655d5d, + 0x7e7777, 0x8a8585, 0xe7dfdf, 0xf4ecec, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, +]); +Base16Theme.add("atelier-savanna-light", "Atelier Savanna Light", [ + 0xecf4ee, 0xdfe7e2, 0x87928a, 0x78877d, + 0x5f6d64, 0x526057, 0x232a25, 0x171c19, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, +]); +Base16Theme.add("atelier-savanna", "Atelier Savanna", [ + 0x171c19, 0x232a25, 0x526057, 0x5f6d64, + 0x78877d, 0x87928a, 0xdfe7e2, 0xecf4ee, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, +]); +Base16Theme.add("atelier-seaside-light", "Atelier Seaside Light", [ + 0xf4fbf4, 0xcfe8cf, 0x8ca68c, 0x809980, + 0x687d68, 0x5e6e5e, 0x242924, 0x131513, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, +]); +Base16Theme.add("atelier-seaside", "Atelier Seaside", [ + 0x131513, 0x242924, 0x5e6e5e, 0x687d68, + 0x809980, 0x8ca68c, 0xcfe8cf, 0xf4fbf4, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, +]); +Base16Theme.add("atelier-sulphurpool-light", "Atelier Sulphurpool Light", [ + 0xf5f7ff, 0xdfe2f1, 0x979db4, 0x898ea4, + 0x6b7394, 0x5e6687, 0x293256, 0x202746, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, +]); +Base16Theme.add("atelier-sulphurpool", "Atelier Sulphurpool", [ + 0x202746, 0x293256, 0x5e6687, 0x6b7394, + 0x898ea4, 0x979db4, 0xdfe2f1, 0xf5f7ff, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, +]); +Base16Theme.add("atlas", "Atlas", [ + 0x002635, 0x00384d, 0x517F8D, 0x6C8B91, + 0x869696, 0xa1a19a, 0xe6e6dc, 0xfafaf8, + 0xff5a67, 0xf08e48, 0xffcc1b, 0x7fc06e, + 0x14747e, 0x5dd7b9, 0x9a70a4, 0xc43060, +]); +Base16Theme.add("bespin", "Bespin", [ + 0x28211c, 0x36312e, 0x5e5d5c, 0x666666, + 0x797977, 0x8a8986, 0x9d9b97, 0xbaae9e, + 0xcf6a4c, 0xcf7d34, 0xf9ee98, 0x54be0d, + 0xafc4db, 0x5ea6ea, 0x9b859d, 0x937121, +]); +Base16Theme.add("black-metal-bathory", "Black Metal (Bathory)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xe78a53, 0xfbcb97, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-burzum", "Black Metal (Burzum)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x99bbaa, 0xddeecc, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-dark-funeral", "Black Metal (Dark Funeral)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x5f81a5, 0xd0dfee, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-gorgoroth", "Black Metal (Gorgoroth)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x8c7f70, 0x9b8d7f, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-immortal", "Black Metal (Immortal)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x556677, 0x7799bb, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-khold", "Black Metal (Khold)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x974b46, 0xeceee3, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-marduk", "Black Metal (Marduk)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x626b67, 0xa5aaa7, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-mayhem", "Black Metal (Mayhem)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xeecc6c, 0xf3ecd4, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-nile", "Black Metal (Nile)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x777755, 0xaa9988, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal-venom", "Black Metal (Venom)", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x79241f, 0xf8f7f2, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("black-metal", "Black Metal", [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xa06666, 0xdd9999, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, +]); +Base16Theme.add("brewer", "Brewer", [ + 0x0c0d0e, 0x2e2f30, 0x515253, 0x737475, + 0x959697, 0xb7b8b9, 0xdadbdc, 0xfcfdfe, + 0xe31a1c, 0xe6550d, 0xdca060, 0x31a354, + 0x80b1d3, 0x3182bd, 0x756bb1, 0xb15928, +]); +Base16Theme.add("bright", "Bright", [ + 0x000000, 0x303030, 0x505050, 0xb0b0b0, + 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, 0xffffff, + 0xfb0120, 0xfc6d24, 0xfda331, 0xa1c659, + 0x76c7b7, 0x6fb3d2, 0xd381c3, 0xbe643c, +]); +Base16Theme.add("brogrammer", "Brogrammer", [ + 0x1f1f1f, 0xf81118, 0x2dc55e, 0xecba0f, + 0x2a84d2, 0x4e5ab7, 0x1081d6, 0xd6dbe5, + 0xd6dbe5, 0xde352e, 0x1dd361, 0xf3bd09, + 0x1081d6, 0x5350b9, 0x0f7ddb, 0xffffff, +]); +Base16Theme.add("brushtrees-dark", "Brush Trees Dark", [ + 0x485867, 0x5A6D7A, 0x6D828E, 0x8299A1, + 0x98AFB5, 0xB0C5C8, 0xC9DBDC, 0xE3EFEF, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, +]); +Base16Theme.add("brushtrees", "Brush Trees", [ + 0xE3EFEF, 0xC9DBDC, 0xB0C5C8, 0x98AFB5, + 0x8299A1, 0x6D828E, 0x5A6D7A, 0x485867, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, +]); +Base16Theme.add("chalk", "Chalk", [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, + 0xfb9fb1, 0xeda987, 0xddb26f, 0xacc267, + 0x12cfc0, 0x6fc2ef, 0xe1a3ee, 0xdeaf8f, +]); +Base16Theme.add("circus", "Circus", [ + 0x191919, 0x202020, 0x303030, 0x5f5a60, + 0x505050, 0xa7a7a7, 0x808080, 0xffffff, + 0xdc657d, 0x4bb1a7, 0xc3ba63, 0x84b97c, + 0x4bb1a7, 0x639ee4, 0xb888e2, 0xb888e2, +]); +Base16Theme.add("classic-dark", "Classic Dark", [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xF5F5F5, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, +]); +Base16Theme.add("classic-light", "Classic Light", [ + 0xF5F5F5, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x505050, 0x303030, 0x202020, 0x151515, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, +]); +Base16Theme.add("codeschool", "Codeschool", [ + 0x232c31, 0x1c3657, 0x2a343a, 0x3f4944, + 0x84898c, 0x9ea7a6, 0xa7cfa3, 0xb5d8f6, + 0x2a5491, 0x43820d, 0xa03b1e, 0x237986, + 0xb02f30, 0x484d79, 0xc59820, 0xc98344, +]); +Base16Theme.add("cupcake", "Cupcake", [ + 0xfbf1f2, 0xf2f1f4, 0xd8d5dd, 0xbfb9c6, + 0xa59daf, 0x8b8198, 0x72677E, 0x585062, + 0xD57E85, 0xEBB790, 0xDCB16C, 0xA3B367, + 0x69A9A7, 0x7297B9, 0xBB99B4, 0xBAA58C, +]); +Base16Theme.add("cupertino", "Cupertino", [ + 0xffffff, 0xc0c0c0, 0xc0c0c0, 0x808080, + 0x808080, 0x404040, 0x404040, 0x5e5e5e, + 0xc41a15, 0xeb8500, 0x826b28, 0x007400, + 0x318495, 0x0000ff, 0xa90d91, 0x826b28, +]); +Base16Theme.add("darktooth", "Darktooth", [ + 0x1D2021, 0x32302F, 0x504945, 0x665C54, + 0x928374, 0xA89984, 0xD5C4A1, 0xFDF4C1, + 0xFB543F, 0xFE8625, 0xFAC03B, 0x95C085, + 0x8BA59B, 0x0D6678, 0x8F4673, 0xA87322, +]); +Base16Theme.add("default-dark", "Default Dark", [ + 0x181818, 0x282828, 0x383838, 0x585858, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, +]); +Base16Theme.add("default-light", "Default Light", [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, +]); +Base16Theme.add("dracula", "Dracula", [ + 0x282936, 0x3a3c4e, 0x4d4f68, 0x626483, + 0x62d6e8, 0xe9e9f4, 0xf1f2f8, 0xf7f7fb, + 0xea51b2, 0xb45bcf, 0x00f769, 0xebff87, + 0xa1efe4, 0x62d6e8, 0xb45bcf, 0x00f769, +]); +Base16Theme.add("eighties", "Eighties", [ + 0x2d2d2d, 0x393939, 0x515151, 0x747369, + 0xa09f93, 0xd3d0c8, 0xe8e6df, 0xf2f0ec, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xd27b53, +]); +Base16Theme.add("embers", "Embers", [ + 0x16130F, 0x2C2620, 0x433B32, 0x5A5047, + 0x8A8075, 0xA39A90, 0xBEB6AE, 0xDBD6D1, + 0x826D57, 0x828257, 0x6D8257, 0x57826D, + 0x576D82, 0x6D5782, 0x82576D, 0x825757, +]); +Base16Theme.add("flat", "Flat", [ + 0x2C3E50, 0x34495E, 0x7F8C8D, 0x95A5A6, + 0xBDC3C7, 0xe0e0e0, 0xf5f5f5, 0xECF0F1, + 0xE74C3C, 0xE67E22, 0xF1C40F, 0x2ECC71, + 0x1ABC9C, 0x3498DB, 0x9B59B6, 0xbe643c, +]); +Base16Theme.add("fruit-soda", "Fruit Soda", [ + 0xf1ecf1, 0xe0dee0, 0xd8d5d5, 0xb5b4b6, + 0x979598, 0x515151, 0x474545, 0x2d2c2c, + 0xfe3e31, 0xfe6d08, 0xf7e203, 0x47f74c, + 0x0f9cfd, 0x2931df, 0x611fce, 0xb16f40, +]); +Base16Theme.add("github", "Github", [ + 0xffffff, 0xf5f5f5, 0xc8c8fa, 0x969896, + 0xe8e8e8, 0x333333, 0xffffff, 0xffffff, + 0xed6a43, 0x0086b3, 0x795da3, 0x183691, + 0x183691, 0x795da3, 0xa71d5d, 0x333333, +]); +Base16Theme.add("google-dark", "Google Dark", [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, +]); +Base16Theme.add("google-light", "Google Light", [ + 0xffffff, 0xe0e0e0, 0xc5c8c6, 0xb4b7b4, + 0x969896, 0x373b41, 0x282a2e, 0x1d1f21, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, +]); +Base16Theme.add("grayscale-dark", "Grayscale Dark", [ + 0x101010, 0x252525, 0x464646, 0x525252, + 0xababab, 0xb9b9b9, 0xe3e3e3, 0xf7f7f7, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, +]); +Base16Theme.add("grayscale-light", "Grayscale Light", [ + 0xf7f7f7, 0xe3e3e3, 0xb9b9b9, 0xababab, + 0x525252, 0x464646, 0x252525, 0x101010, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, +]); +Base16Theme.add("greenscreen", "Green Screen", [ + 0x001100, 0x003300, 0x005500, 0x007700, + 0x009900, 0x00bb00, 0x00dd00, 0x00ff00, + 0x007700, 0x009900, 0x007700, 0x00bb00, + 0x005500, 0x009900, 0x00bb00, 0x005500, +]); +Base16Theme.add("gruvbox-dark-hard", "Gruvbox dark, hard", [ + 0x1d2021, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, +]); +Base16Theme.add("gruvbox-dark-medium", "Gruvbox dark, medium", [ + 0x282828, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, +]); +Base16Theme.add("gruvbox-dark-pale", "Gruvbox dark, pale", [ + 0x262626, 0x3a3a3a, 0x4e4e4e, 0x8a8a8a, + 0x949494, 0xdab997, 0xd5c4a1, 0xebdbb2, + 0xd75f5f, 0xff8700, 0xffaf00, 0xafaf00, + 0x85ad85, 0x83adad, 0xd485ad, 0xd65d0e, +]); +Base16Theme.add("gruvbox-dark-soft", "Gruvbox dark, soft", [ + 0x32302f, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, +]); +Base16Theme.add("gruvbox-light-hard", "Gruvbox light, hard", [ + 0xf9f5d7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, +]); +Base16Theme.add("gruvbox-light-medium", "Gruvbox light, medium", [ + 0xfbf1c7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, +]); +Base16Theme.add("gruvbox-light-soft", "Gruvbox light, soft", [ + 0xf2e5bc, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, +]); +Base16Theme.add("harmonic-dark", "Harmonic16 Dark", [ + 0x0b1c2c, 0x223b54, 0x405c79, 0x627e99, + 0xaabcce, 0xcbd6e2, 0xe5ebf1, 0xf7f9fb, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, +]); +Base16Theme.add("harmonic-light", "Harmonic16 Light", [ + 0xf7f9fb, 0xe5ebf1, 0xcbd6e2, 0xaabcce, + 0x627e99, 0x405c79, 0x223b54, 0x0b1c2c, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, +]); +Base16Theme.add("heetch-light", "Heetch Light", [ + 0xfeffff, 0x392551, 0x7b6d8b, 0x9c92a8, + 0xddd6e5, 0x5a496e, 0x470546, 0x190134, + 0x27d9d5, 0xbdb6c5, 0x5ba2b6, 0xf80059, + 0xc33678, 0x47f9f5, 0xbd0152, 0xdedae2, +]); +Base16Theme.add("heetch", "Heetch Dark", [ + 0x190134, 0x392551, 0x5A496E, 0x7B6D8B, + 0x9C92A8, 0xBDB6C5, 0xDEDAE2, 0xFEFFFF, + 0x27D9D5, 0x5BA2B6, 0x8F6C97, 0xC33678, + 0xF80059, 0xBD0152, 0x82034C, 0x470546, +]); +Base16Theme.add("helios", "Helios", [ + 0x1d2021, 0x383c3e, 0x53585b, 0x6f7579, + 0xcdcdcd, 0xd5d5d5, 0xdddddd, 0xe5e5e5, + 0xd72638, 0xeb8413, 0xf19d1a, 0x88b92d, + 0x1ba595, 0x1e8bac, 0xbe4264, 0xc85e0d, +]); +Base16Theme.add("hopscotch", "Hopscotch", [ + 0x322931, 0x433b42, 0x5c545b, 0x797379, + 0x989498, 0xb9b5b8, 0xd5d3d5, 0xffffff, + 0xdd464c, 0xfd8b19, 0xfdcc59, 0x8fc13e, + 0x149b93, 0x1290bf, 0xc85e7c, 0xb33508, +]); +Base16Theme.add("horizon-dark", "Horizon Dark", [ + 0x1c1e26, 0x232530, 0x2e303e, 0x676a8d, + 0xced1d0, 0xcbced0, 0xdcdfe4, 0xe3e6ee, + 0xe93c58, 0xe58d7d, 0xefb993, 0xefaf8e, + 0x24a8b4, 0xdf5273, 0xb072d1, 0xe4a382, +]); +Base16Theme.add("ia-dark", "iA Dark", [ + 0x1a1a1a, 0x222222, 0x1d414d, 0x767676, + 0xb8b8b8, 0xcccccc, 0xe8e8e8, 0xf8f8f8, + 0xd88568, 0xd86868, 0xb99353, 0x83a471, + 0x7c9cae, 0x8eccdd, 0xb98eb2, 0x8b6c37, +]); +Base16Theme.add("ia-light", "iA Light", [ + 0xf6f6f6, 0xdedede, 0xbde5f2, 0x898989, + 0x767676, 0x181818, 0xe8e8e8, 0xf8f8f8, + 0x9c5a02, 0xc43e18, 0xc48218, 0x38781c, + 0x2d6bb1, 0x48bac2, 0xa94598, 0x8b6c37, +]); +Base16Theme.add("icy", "Icy Dark", [ + 0x021012, 0x031619, 0x041f23, 0x052e34, + 0x064048, 0x095b67, 0x0c7c8c, 0x109cb0, + 0x16c1d9, 0xb3ebf2, 0x80deea, 0x4dd0e1, + 0x26c6da, 0x00bcd4, 0x00acc1, 0x0097a7, +]); +Base16Theme.add("irblack", "IR Black", [ + 0x000000, 0x242422, 0x484844, 0x6c6c66, + 0x918f88, 0xb5b3aa, 0xd9d7cc, 0xfdfbee, + 0xff6c60, 0xe9c062, 0xffffb6, 0xa8ff60, + 0xc6c5fe, 0x96cbfe, 0xff73fd, 0xb18a3d, +]); +Base16Theme.add("isotope", "Isotope", [ + 0x000000, 0x404040, 0x606060, 0x808080, + 0xc0c0c0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xff0000, 0xff9900, 0xff0099, 0x33ff00, + 0x00ffff, 0x0066ff, 0xcc00ff, 0x3300ff, +]); +Base16Theme.add("macintosh", "Macintosh", [ + 0x000000, 0x404040, 0x404040, 0x808080, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0xffffff, + 0xdd0907, 0xff6403, 0xfbf305, 0x1fb714, + 0x02abea, 0x0000d3, 0x4700a5, 0x90713a, +]); +Base16Theme.add("marrakesh", "Marrakesh", [ + 0x201602, 0x302e00, 0x5f5b17, 0x6c6823, + 0x86813b, 0x948e48, 0xccc37a, 0xfaf0a5, + 0xc35359, 0xb36144, 0xa88339, 0x18974e, + 0x75a738, 0x477ca1, 0x8868b3, 0xb3588e, +]); +Base16Theme.add("materia", "Materia", [ + 0x263238, 0x2C393F, 0x37474F, 0x707880, + 0xC9CCD3, 0xCDD3DE, 0xD5DBE5, 0xFFFFFF, + 0xEC5F67, 0xEA9560, 0xFFCC00, 0x8BD649, + 0x80CBC4, 0x89DDFF, 0x82AAFF, 0xEC5F67, +]); +Base16Theme.add("material-darker", "Material Darker", [ + 0x212121, 0x303030, 0x353535, 0x4A4A4A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, +]); +Base16Theme.add("material-lighter", "Material Lighter", [ + 0xFAFAFA, 0xE7EAEC, 0xCCEAE7, 0xCCD7DA, + 0x8796B0, 0x80CBC4, 0x80CBC4, 0xFFFFFF, + 0xFF5370, 0xF76D47, 0xFFB62C, 0x91B859, + 0x39ADB5, 0x6182B8, 0x7C4DFF, 0xE53935, +]); +Base16Theme.add("material-palenight", "Material Palenight", [ + 0x292D3E, 0x444267, 0x32374D, 0x676E95, + 0x8796B0, 0x959DCB, 0x959DCB, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, +]); +Base16Theme.add("material-vivid", "Material Vivid", [ + 0x202124, 0x27292c, 0x323639, 0x44464d, + 0x676c71, 0x80868b, 0x9e9e9e, 0xffffff, + 0xf44336, 0xff9800, 0xffeb3b, 0x00e676, + 0x00bcd4, 0x2196f3, 0x673ab7, 0x8d6e63, +]); +Base16Theme.add("material", "Material", [ + 0x263238, 0x2E3C43, 0x314549, 0x546E7A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, +]); +Base16Theme.add("mellow-purple", "Mellow Purple", [ + 0x1e0528, 0x1A092D, 0x331354, 0x320f55, + 0x873582, 0xffeeff, 0xffeeff, 0xf8c0ff, + 0x00d9e9, 0xaa00a3, 0x955ae7, 0x05cb0d, + 0xb900b1, 0x550068, 0x8991bb, 0x4d6fff, +]); +Base16Theme.add("mexico-light", "Mexico Light", [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf79a0e, 0x538947, + 0x4b8093, 0x7cafc2, 0x96609e, 0xa16946, +]); +Base16Theme.add("mocha", "Mocha", [ + 0x3B3228, 0x534636, 0x645240, 0x7e705a, + 0xb8afad, 0xd0c8c6, 0xe9e1dd, 0xf5eeeb, + 0xcb6077, 0xd28b71, 0xf4bc87, 0xbeb55b, + 0x7bbda4, 0x8ab3b5, 0xa89bb9, 0xbb9584, +]); +Base16Theme.add("monokai", "Monokai", [ + 0x272822, 0x383830, 0x49483e, 0x75715e, + 0xa59f85, 0xf8f8f2, 0xf5f4f1, 0xf9f8f5, + 0xf92672, 0xfd971f, 0xf4bf75, 0xa6e22e, + 0xa1efe4, 0x66d9ef, 0xae81ff, 0xcc6633, +]); +Base16Theme.add("nord", "Nord", [ + 0x2E3440, 0x3B4252, 0x434C5E, 0x4C566A, + 0xD8DEE9, 0xE5E9F0, 0xECEFF4, 0x8FBCBB, + 0x88C0D0, 0x81A1C1, 0x5E81AC, 0xBF616A, + 0xD08770, 0xEBCB8B, 0xA3BE8C, 0xB48EAD, +]); +Base16Theme.add("ocean", "Ocean", [ + 0x2b303b, 0x343d46, 0x4f5b66, 0x65737e, + 0xa7adba, 0xc0c5ce, 0xdfe1e8, 0xeff1f5, + 0xbf616a, 0xd08770, 0xebcb8b, 0xa3be8c, + 0x96b5b4, 0x8fa1b3, 0xb48ead, 0xab7967, +]); +Base16Theme.add("oceanicnext", "OceanicNext", [ + 0x1B2B34, 0x343D46, 0x4F5B66, 0x65737E, + 0xA7ADBA, 0xC0C5CE, 0xCDD3DE, 0xD8DEE9, + 0xEC5F67, 0xF99157, 0xFAC863, 0x99C794, + 0x5FB3B3, 0x6699CC, 0xC594C5, 0xAB7967, +]); +Base16Theme.add("one-light", "One Light", [ + 0xfafafa, 0xf0f0f1, 0xe5e5e6, 0xa0a1a7, + 0x696c77, 0x383a42, 0x202227, 0x090a0b, + 0xca1243, 0xd75f00, 0xc18401, 0x50a14f, + 0x0184bc, 0x4078f2, 0xa626a4, 0x986801, +]); +Base16Theme.add("onedark", "OneDark", [ + 0x282c34, 0x353b45, 0x3e4451, 0x545862, + 0x565c64, 0xabb2bf, 0xb6bdca, 0xc8ccd4, + 0xe06c75, 0xd19a66, 0xe5c07b, 0x98c379, + 0x56b6c2, 0x61afef, 0xc678dd, 0xbe5046, +]); +Base16Theme.add("outrun-dark", "Outrun Dark", [ + 0x00002A, 0x20204A, 0x30305A, 0x50507A, + 0xB0B0DA, 0xD0D0FA, 0xE0E0FF, 0xF5F5FF, + 0xFF4242, 0xFC8D28, 0xF3E877, 0x59F176, + 0x0EF0F0, 0x66B0FF, 0xF10596, 0xF003EF, +]); +Base16Theme.add("papercolor-dark", "PaperColor Dark", [ + 0x1c1c1c, 0xaf005f, 0x5faf00, 0xd7af5f, + 0x5fafd7, 0x808080, 0xd7875f, 0xd0d0d0, + 0x585858, 0x5faf5f, 0xafd700, 0xaf87d7, + 0xffaf00, 0xff5faf, 0x00afaf, 0x5f8787, +]); +Base16Theme.add("papercolor-light", "PaperColor Light", [ + 0xeeeeee, 0xaf0000, 0x008700, 0x5f8700, + 0x0087af, 0x878787, 0x005f87, 0x444444, + 0xbcbcbc, 0xd70000, 0xd70087, 0x8700af, + 0xd75f00, 0xd75f00, 0x005faf, 0x005f87, +]); +Base16Theme.add("paraiso", "Paraiso", [ + 0x2f1e2e, 0x41323f, 0x4f424c, 0x776e71, + 0x8d8687, 0xa39e9b, 0xb9b6b0, 0xe7e9db, + 0xef6155, 0xf99b15, 0xfec418, 0x48b685, + 0x5bc4bf, 0x06b6ef, 0x815ba4, 0xe96ba8, +]); +Base16Theme.add("phd", "PhD", [ + 0x061229, 0x2a3448, 0x4d5666, 0x717885, + 0x9a99a3, 0xb8bbc2, 0xdbdde0, 0xffffff, + 0xd07346, 0xf0a000, 0xfbd461, 0x99bf52, + 0x72b9bf, 0x5299bf, 0x9989cc, 0xb08060, +]); +Base16Theme.add("pico", "Pico", [ + 0x000000, 0x1d2b53, 0x7e2553, 0x008751, + 0xab5236, 0x5f574f, 0xc2c3c7, 0xfff1e8, + 0xff004d, 0xffa300, 0xfff024, 0x00e756, + 0x29adff, 0x83769c, 0xff77a8, 0xffccaa, +]); +Base16Theme.add("pop", "Pop", [ + 0x000000, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xeb008a, 0xf29333, 0xf8ca12, 0x37b349, + 0x00aabb, 0x0e5a94, 0xb31e8d, 0x7a2d00, +]); +Base16Theme.add("porple", "Porple", [ + 0x292c36, 0x333344, 0x474160, 0x65568a, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xf84547, 0xd28e5d, 0xefa16b, 0x95c76f, + 0x64878f, 0x8485ce, 0xb74989, 0x986841, +]); +Base16Theme.add("qualia", "Qualia", [ + 0x101010, 0x454545, 0x454545, 0x454545, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0x454545, + 0xefa6a2, 0xa3b8ef, 0xe6a3dc, 0x80c990, + 0xc8c874, 0x50cacd, 0xe0af85, 0x808080, +]); +Base16Theme.add("railscasts", "Railscasts", [ + 0x2b2b2b, 0x272935, 0x3a4055, 0x5a647e, + 0xd4cfc9, 0xe6e1dc, 0xf4f1ed, 0xf9f7f3, + 0xda4939, 0xcc7833, 0xffc66d, 0xa5c261, + 0x519f50, 0x6d9cbe, 0xb6b3eb, 0xbc9458, +]); +Base16Theme.add("rebecca", "Rebecca", [ + 0x292a44, 0x663399, 0x383a62, 0x666699, + 0xa0a0c5, 0xf1eff8, 0xccccff, 0x53495d, + 0xa0a0c5, 0xefe4a1, 0xae81ff, 0x6dfedf, + 0x8eaee0, 0x2de0a7, 0x7aa5ff, 0xff79c6, +]); +Base16Theme.add("seti", "Seti UI", [ + 0x151718, 0x282a2b, 0x3B758C, 0x41535B, + 0x43a5d5, 0xd6d6d6, 0xeeeeee, 0xffffff, + 0xcd3f45, 0xdb7b55, 0xe6cd69, 0x9fca56, + 0x55dbbe, 0x55b5db, 0xa074c4, 0x8a553f, +]); +Base16Theme.add("shapeshifter", "Shapeshifter", [ + 0xf9f9f9, 0xe0e0e0, 0xababab, 0x555555, + 0x343434, 0x102015, 0x040404, 0x000000, + 0xe92f2f, 0xe09448, 0xdddd13, 0x0ed839, + 0x23edda, 0x3b48e3, 0xf996e2, 0x69542d, +]); +Base16Theme.add("snazzy", "Snazzy", [ + 0x282a36, 0x34353e, 0x43454f, 0x78787e, + 0xa5a5a9, 0xe2e4e5, 0xeff0eb, 0xf1f1f0, + 0xff5c57, 0xff9f43, 0xf3f99d, 0x5af78e, + 0x9aedfe, 0x57c7ff, 0xff6ac1, 0xb2643c, +]); +Base16Theme.add("solarflare", "Solar Flare", [ + 0x18262F, 0x222E38, 0x586875, 0x667581, + 0x85939E, 0xA6AFB8, 0xE8E9ED, 0xF5F7FA, + 0xEF5253, 0xE66B2B, 0xE4B51C, 0x7CC844, + 0x52CBB0, 0x33B5E1, 0xA363D5, 0xD73C9A, +]); +Base16Theme.add("solarized-dark", "Solarized Dark", [ + 0x002b36, 0x073642, 0x586e75, 0x657b83, + 0x839496, 0x93a1a1, 0xeee8d5, 0xfdf6e3, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, +]); +Base16Theme.add("solarized-light", "Solarized Light", [ + 0xfdf6e3, 0xeee8d5, 0x93a1a1, 0x839496, + 0x657b83, 0x586e75, 0x073642, 0x002b36, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, +]); +Base16Theme.add("spacemacs", "Spacemacs", [ + 0x1f2022, 0x282828, 0x444155, 0x585858, + 0xb8b8b8, 0xa3a3a3, 0xe8e8e8, 0xf8f8f8, + 0xf2241f, 0xffa500, 0xb1951d, 0x67b11d, + 0x2d9574, 0x4f97d7, 0xa31db1, 0xb03060, +]); +Base16Theme.add("summerfruit-dark", "Summerfruit Dark", [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xFFFFFF, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, +]); +Base16Theme.add("summerfruit-light", "Summerfruit Light", [ + 0xFFFFFF, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x000000, 0x101010, 0x151515, 0x202020, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, +]); +Base16Theme.add("synth-midnight-dark", "Synth Midnight", [ + 0x040404, 0x141414, 0x242424, 0x61507A, + 0xBFBBBF, 0xDFDBDF, 0xEFEBEF, 0xFFFBFF, + 0xB53B50, 0xE4600E, 0xDAE84D, 0x06EA61, + 0x7CEDE9, 0x03AEFF, 0xEA5CE2, 0x9D4D0E, +]); +Base16Theme.add("tomorrow-night-eighties", "Tomorrow Night Eighties", [ + 0x2d2d2d, 0x393939, 0x515151, 0x999999, + 0xb4b7b4, 0xcccccc, 0xe0e0e0, 0xffffff, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xa3685a, +]); +Base16Theme.add("tomorrow-night", "Tomorrow Night", [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xcc6666, 0xde935f, 0xf0c674, 0xb5bd68, + 0x8abeb7, 0x81a2be, 0xb294bb, 0xa3685a, +]); +Base16Theme.add("tomorrow", "Tomorrow", [ + 0xffffff, 0xe0e0e0, 0xd6d6d6, 0x8e908c, + 0x969896, 0x4d4d4c, 0x282a2e, 0x1d1f21, + 0xc82829, 0xf5871f, 0xeab700, 0x718c00, + 0x3e999f, 0x4271ae, 0x8959a8, 0xa3685a, +]); +Base16Theme.add("tube", "London Tube", [ + 0x231f20, 0x1c3f95, 0x5a5758, 0x737171, + 0x959ca1, 0xd9d8d8, 0xe7e7e8, 0xffffff, + 0xee2e24, 0xf386a1, 0xffd204, 0x00853e, + 0x85cebc, 0x009ddc, 0x98005d, 0xb06110, +]); +Base16Theme.add("twilight", "Twilight", [ + 0x1e1e1e, 0x323537, 0x464b50, 0x5f5a60, + 0x838184, 0xa7a7a7, 0xc3c3c3, 0xffffff, + 0xcf6a4c, 0xcda869, 0xf9ee98, 0x8f9d6a, + 0xafc4db, 0x7587a6, 0x9b859d, 0x9b703f, +]); +Base16Theme.add("unikitty-dark", "Unikitty Dark", [ + 0x2e2a31, 0x4a464d, 0x666369, 0x838085, + 0x9f9da2, 0xbcbabe, 0xd8d7da, 0xf5f4f7, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x796af5, 0xbb60ea, 0xc720ca, +]); +Base16Theme.add("unikitty-light", "Unikitty Light", [ + 0xffffff, 0xe1e1e2, 0xc4c3c5, 0xa7a5a8, + 0x89878b, 0x6c696e, 0x4f4b51, 0x322d34, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x775dff, 0xaa17e6, 0xe013d0, +]); +Base16Theme.add("woodland", "Woodland", [ + 0x231e18, 0x302b25, 0x48413a, 0x9d8b70, + 0xb4a490, 0xcabcb1, 0xd7c8bc, 0xe4d4c8, + 0xd35c5c, 0xca7f32, 0xe0ac16, 0xb7ba53, + 0x6eb958, 0x88a4d3, 0xbb90e2, 0xb49368, +]); +Base16Theme.add("xcode-dusk", "XCode Dusk", [ + 0x282B35, 0x3D4048, 0x53555D, 0x686A71, + 0x7E8086, 0x939599, 0xA9AAAE, 0xBEBFC2, + 0xB21889, 0x786DC5, 0x438288, 0xDF0002, + 0x00A0BE, 0x790EAD, 0xB21889, 0xC77C48, +]); +Base16Theme.add("zenburn", "Zenburn", [ + 0x383838, 0x404040, 0x606060, 0x6f6f6f, + 0x808080, 0xdcdccc, 0xc0c0c0, 0xffffff, + 0xdca3a3, 0xdfaf8f, 0xe0cf9f, 0x5f7f5f, + 0x93e0e3, 0x7cb8bb, 0xdc8cc3, 0x000000, +]); diff --git a/js/model/LabelColors.js b/js/model/LabelColors.js new file mode 100644 index 0000000..c51b8a3 --- /dev/null +++ b/js/model/LabelColors.js @@ -0,0 +1,69 @@ +import Gdk from 'gi://Gdk?version=4.0'; +import Gio from 'gi://Gio'; +const CITADEL_SETTINGS_SCHEMA = 'com.subgraph.citadel'; +const LABEL_COLOR_LIST_KEY = 'label-color-list'; +const REALM_LABEL_COLORS_KEY = 'realm-label-colors'; +const DEFAULT_LABEL_COLOR = new Gdk.RGBA({ red: 153, green: 193, blue: 241, }); +export class LabelColorManager { + constructor() { + this._citadelSettings = new Gio.Settings({ schema_id: CITADEL_SETTINGS_SCHEMA }); + this._defaultColors = []; + this._realmLabelColors = new Map(); + this._loadColors(); + } + _loadColors() { + let entries = this._citadelSettings.get_strv(LABEL_COLOR_LIST_KEY); + entries.forEach(entry => { + let c = new Gdk.RGBA(); + if (c.parse(entry)) { + this._defaultColors.push(c); + } + }); + entries = this._citadelSettings.get_strv(REALM_LABEL_COLORS_KEY); + entries.forEach(entry => { + let parts = entry.split(":"); + if (parts.length === 2) { + let c = new Gdk.RGBA(); + if (c.parse(parts[1])) { + this._realmLabelColors.set(parts[0], c); + } + } + }); + } + updateRealmColor(realm, color) { + this._realmLabelColors.set(realm.name, color); + this._storeRealmColors(); + } + lookupRealmColor(realm) { + let c = this._realmLabelColors.get(realm.name); + if (c) { + return c; + } + let newColor = this._allocateColor(); + this.updateRealmColor(realm, newColor); + return newColor; + } + _storeRealmColors() { + let entries = []; + this._realmLabelColors.forEach((v, k) => { + entries.push(`${k}:${v.to_string()}`); + }); + entries.sort(); + this._citadelSettings.set_strv(REALM_LABEL_COLORS_KEY, entries); + } + _allocateColor() { + // 1) No default colors? return a built in color + if (this._defaultColors.length === 0) { + return DEFAULT_LABEL_COLOR; + } + // 2) Find first color on default color list that isn't used already + let usedColors = Array.from(this._realmLabelColors.values()); + let defaultColor = this._defaultColors.find(color => !usedColors.some(c => c.equal(color))); + if (defaultColor) { + return defaultColor; + } + // 3) Choose a random element of the default list + let index = Math.floor(Math.random() * this._defaultColors.length); + return this._defaultColors[index]; + } +} diff --git a/js/model/ObjectManager.js b/js/model/ObjectManager.js new file mode 100644 index 0000000..ab3feb4 --- /dev/null +++ b/js/model/ObjectManager.js @@ -0,0 +1,183 @@ +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +const Signals = imports.signals; +Gio._promisify(Gio.DBusProxy.prototype, 'init_async', 'init_finish'); +// Specified in the D-Bus specification here: +// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager +const ObjectManagerIface = ` + + + + + + + + + + + + + + +`; +const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface); +; +export class ObjectManager { + constructor(params) { + var _a; + this._connection = params.connection; + this._serviceName = params.name; + this._managerPath = params.objectPath; + this._cancellable = (_a = params.cancellable) !== null && _a !== void 0 ? _a : null; + this._managerProxy = new Gio.DBusProxy({ + g_connection: this._connection, + g_interface_name: ObjectManagerInfo.name, + g_interface_info: ObjectManagerInfo, + g_name: this._serviceName, + g_object_path: this._managerPath, + g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START, + }); + this._interfaceInfos = {}; + this._objects = {}; + this._interfaces = {}; + this._onLoaded = params.onLoaded; + if (params.knownInterfaces) + this._registerInterfaces(params.knownInterfaces); + this._initManagerProxy(); + } + _completeLoad() { + if (this._onLoaded) + this._onLoaded(); + } + async _addInterface(objectPath, interfaceName) { + let info = this._interfaceInfos[interfaceName]; + if (!info) + return; + const proxy = new Gio.DBusProxy({ + g_connection: this._connection, + g_name: this._serviceName, + g_object_path: objectPath, + g_interface_name: interfaceName, + g_interface_info: info, + g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START, + }); + try { + await proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable); + } + catch (e) { + logError(e, `could not initialize proxy for interface ${interfaceName}`); + return; + } + let isNewObject; + if (!this._objects[objectPath]) { + this._objects[objectPath] = {}; + isNewObject = true; + } + else { + isNewObject = false; + } + this._objects[objectPath][interfaceName] = proxy; + if (!this._interfaces[interfaceName]) + this._interfaces[interfaceName] = []; + this._interfaces[interfaceName].push(proxy); + if (isNewObject) + this.emit('object-added', objectPath); + this.emit('interface-added', interfaceName, proxy); + } + _removeInterface(objectPath, interfaceName) { + if (!this._objects[objectPath]) + return; + let proxy = this._objects[objectPath][interfaceName]; + if (this._interfaces[interfaceName]) { + let index = this._interfaces[interfaceName].indexOf(proxy); + if (index >= 0) + this._interfaces[interfaceName].splice(index, 1); + if (this._interfaces[interfaceName].length === 0) + delete this._interfaces[interfaceName]; + } + this.emit('interface-removed', interfaceName, proxy); + delete this._objects[objectPath][interfaceName]; + if (Object.keys(this._objects[objectPath]).length === 0) { + delete this._objects[objectPath]; + this.emit('object-removed', objectPath); + } + } + async _initManagerProxy() { + try { + await this._managerProxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable); + } + catch (e) { + logError(e, `could not initialize object manager for object ${this._serviceName}`); + this._completeLoad(); + return; + } + this._managerProxy.connectSignal('InterfacesAdded', (_objectManager, _sender, [objectPath, interfaces]) => { + let interfaceNames = Object.keys(interfaces); + for (let i = 0; i < interfaceNames.length; i++) + this._addInterface(objectPath, interfaceNames[i]); + }); + this._managerProxy.connectSignal('InterfacesRemoved', (_objectManager, _sender, [objectPath, interfaceNames]) => { + for (let i = 0; i < interfaceNames.length; i++) + this._removeInterface(objectPath, interfaceNames[i]); + }); + if (Object.keys(this._interfaceInfos).length === 0) { + this._completeLoad(); + return; + } + this._managerProxy.connect('notify::g-name-owner', () => { + if (this._managerProxy.g_name_owner) + this._onNameAppeared(); + else + this._onNameVanished(); + }); + if (this._managerProxy.g_name_owner) + this._onNameAppeared().catch(logError); + } + async _onNameAppeared() { + try { + const [objects] = await this._managerProxy.GetManagedObjectsAsync(); + if (!objects) { + this._completeLoad(); + return; + } + const objectPaths = Object.keys(objects); + await Promise.allSettled(objectPaths.flatMap(objectPath => { + const object = objects[objectPath]; + const interfaceNames = Object.getOwnPropertyNames(object); + return interfaceNames.map(ifaceName => this._addInterface(objectPath, ifaceName)); + })); + } + catch (error) { + logError(error, `could not get remote objects for service ${this._serviceName} path ${this._managerPath}`); + } + finally { + this._completeLoad(); + } + } + _onNameVanished() { + let objectPaths = Object.keys(this._objects); + for (let i = 0; i < objectPaths.length; i++) { + let objectPath = objectPaths[i]; + let object = this._objects[objectPath]; + let interfaceNames = Object.keys(object); + for (let j = 0; j < interfaceNames.length; j++) { + let interfaceName = interfaceNames[j]; + if (object[interfaceName]) + this._removeInterface(objectPath, interfaceName); + } + } + } + _registerInterfaces(interfaces) { + for (let i = 0; i < interfaces.length; i++) { + let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]); + this._interfaceInfos[info.name] = info; + } + } + getProxiesForInterface(interfaceName) { + let proxyList = this._interfaces[interfaceName]; + if (!proxyList) + return []; + return proxyList; + } +} +Signals.addSignalMethods(ObjectManager.prototype); diff --git a/js/model/OptionData.js b/js/model/OptionData.js new file mode 100644 index 0000000..99737b6 --- /dev/null +++ b/js/model/OptionData.js @@ -0,0 +1,73 @@ +export class OptionData { + constructor(description, tooltip) { + this.tooltip = tooltip; + this.description = description; + } + static add(name, description, tooltip) { + OptionData.map.set(name, new OptionData(description, tooltip)); + } + static description(name) { + var _a; + return (_a = OptionData.map.get(name)) === null || _a === void 0 ? void 0 : _a.description; + } + static tooltip(name) { + var _a; + return (_a = OptionData.map.get(name)) === null || _a === void 0 ? void 0 : _a.tooltip; + } +} +OptionData.map = new Map(); +OptionData.add('use-gpu', 'Use GPU in Realm', `If enabled the render node device /dev/dri/renderD128 will be mounted into the realm container. + +If privileged device /dev/dri/card0 is also needed set +additional variable in realm configuration file: + + use-gpu-card0 = true +`); +OptionData.add('use-wayland', 'Use Wayland in Realm', `If enabled access to Wayland display will be permitted in realm by adding wayland socket to realm. + + /run/user/1000/wayland-0 + +`); +OptionData.add('use-x11', 'Use X11 in Realm', `If enabled access to X11 server will be added by mounting directory X11 directory into realm. + + /tmp/.X11-unix + +`); +OptionData.add('use-sound', 'Use sound in Realm', `If enabled allows use of sound inside of realm. The following items will be added: + + /dev/snd + /dev/shm + /run/user/1000/pulse + +`); +OptionData.add('use-shared-dir', 'Mount /Shared directory in Realm', `If enabled the shared directory will be mounted as /Shared in home directory of realm. + +This directory is shared between all realms with this option enabled and is an easy way to move files between realms. + +`); +OptionData.add('use-network', 'Realm has network access', `If enabled the realm will have access to the network.`); +OptionData.add('use-kvm', 'Use KVM (/dev/kvm) in Realm', `If enabled device /dev/kvm will be added to realm`); +OptionData.add('use-ephemeral-home', 'Use ephemeral tmpfs mount for home directory', `If enabled the home directory of realm will be set up in ephemeral mode. + +The ephemeral home directory is set up with the following steps: + + 1. Home directory is mounted as tmpfs filesystem + 2. Any files in /realms/skel are copied into home directory + 3. Any files in /realms/realm-$name/skel are copied into home directory. + 4. Any directories listed in config file variable ephemeral_persistent_dirs + are bind mounted from /realms/realm-$name/home into ephemeral + home directory. + +`); +OptionData.add('overlay', 'Type of rootfs overlay Realm is configured to use.', `Overlay +Type of rootfs overlay realm is configured to use. + None Don't use a rootfs overlay + TmpFS Use a rootfs overlay stored on tmpfs + Storage Use a rootfs overlay stored on disk in storage partition +`); +OptionData.add('realmfs', 'Root filesystem image to use for Realm.', `RealmFS +Root filesystem image to use for realm.`); +OptionData.add('colorscheme', 'Terminal Color Scheme', `Terminal Color Scheme +Choose a color scheme to use in the terminals in this realm.`); +OptionData.add('window-label-color', 'Window Label Color', `Window Label Color +Set a color to be used when a label is drawn on the window titlebar indicating which realm the application is running in.`); diff --git a/js/model/Realm.js b/js/model/Realm.js new file mode 100644 index 0000000..b2b8c6f --- /dev/null +++ b/js/model/Realm.js @@ -0,0 +1,94 @@ +var _a; +import GObject from 'gi://GObject'; +import { RealmFS } from './RealmFS.js'; +import { RealmConfig } from './RealmConfig.js'; +export const RunStatus = { + STOPPED: 0, + STARTING: 1, + RUNNING: 2, + CURRENT: 3, + STOPPING: 4, +}; +export class Realm extends GObject.Object { + constructor(proxy, labelColors) { + super(); + this.config = new RealmConfig(); + this._proxy = proxy; + this._labelColors = labelColors; + this._proxy.connect('g-properties-changed', (_proxy, _changed, _invalidated) => { + this._sync(); + }); + this._sync(); + } + get realmfs() { + return this._realmfs; + } + set realmfs(value) { + if (this.realmfs === value) { + return; + } + this._realmfs = value; + this.emit('changed'); + } + _sync() { + this._name = this._proxy.Name; + this._description = this._proxy.Description; + this._pidns = this._proxy.PidNS; + this._run_status = this._proxy.RunStatus; + this._is_system_realm = this._proxy.IsSystemRealm; + this.emit('changed'); + } + get name() { + return this._name; + } + get description() { + return this._description; + } + get pidns() { + return this._pidns; + } + get run_status() { + return this._run_status; + } + get is_system_realm() { + return this._is_system_realm; + } + is_running() { + return this.run_status === RunStatus.RUNNING || this.run_status === RunStatus.CURRENT; + } + is_current() { + return this.run_status === RunStatus.CURRENT; + } + getLabelColor() { + return this._labelColors.lookupRealmColor(this); + } + setLabelColor(color) { + this._labelColors.updateRealmColor(this, color); + } + realmfs_index() { + return this._proxy.RealmFS; + } + set_global_config(config) { + this.config.set_global_config(config); + } + async load_config() { + const result = await this._proxy.GetConfigAsync(); + this.config.set_config(result[0]); + this.emit('changed'); + } +} +_a = Realm; +(() => { + GObject.registerClass({ + GTypeName: 'Realm', + Properties: { + 'name': GObject.ParamSpec.string('name', '', '', GObject.ParamFlags.READWRITE, ''), + 'description': GObject.ParamSpec.string('description', '', '', GObject.ParamFlags.READWRITE, ''), + 'pidns': GObject.ParamSpec.uint64('pidns', '', '', GObject.ParamFlags.READWRITE, 0, Number.MAX_SAFE_INTEGER, 0), + 'run-status': GObject.ParamSpec.uint('run-status', '', '', GObject.ParamFlags.READWRITE, 0, 4, 0), + 'is-system-realm': GObject.ParamSpec.boolean('is-system-realm', '', '', GObject.ParamFlags.READWRITE, false), + 'realmfs': GObject.ParamSpec.object('realmfs', '', '', GObject.ParamFlags.READWRITE, RealmFS), + }, + Signals: { 'changed': {} }, + }, _a); +})(); diff --git a/js/model/RealmConfig.js b/js/model/RealmConfig.js new file mode 100644 index 0000000..5c04802 --- /dev/null +++ b/js/model/RealmConfig.js @@ -0,0 +1,94 @@ +import { OptionData } from './OptionData.js'; +export class BoolOptionData { + constructor(name) { + var _a, _b; + this.name = name; + this.description = (_a = OptionData.description(name)) !== null && _a !== void 0 ? _a : name; + this.tooltip = (_b = OptionData.tooltip(name)) !== null && _b !== void 0 ? _b : ""; + } + static allOptions() { + let options = []; + BoolOptionData.ALL_OPTIONS.forEach(name => { + options.push(new BoolOptionData(name)); + }); + return options; + } +} +BoolOptionData.ALL_OPTIONS = [ + 'use-gpu', + 'use-wayland', + 'use-x11', + 'use-sound', + 'use-network', + 'use-kvm', + 'use-shared-dir', + 'use-ephemeral-home', +]; +export class ConfigOption { + constructor(name, value, defaultValue, description, tooltip) { + this.name = name; + this.value = value; + this.defaultValue = defaultValue; + this.description = description; + this.tooltip = tooltip; + } +} +export class RealmConfig { + constructor() { + this._configVars = {}; + this._globalConfig = {}; + } + set_config(config) { + this._configVars = config; + } + set_global_config(globalConfig) { + this._globalConfig = globalConfig; + } + get_var(name) { + let value = this._configVars[name]; + if (value && value.length > 0) { + return value; + } + value = this._globalConfig[name]; + if (value && value.length > 0) { + return value; + } + return null; + } + get_bool(name) { + return this.get_var(name) === 'true'; + } + get_colorscheme() { + var _a; + return (_a = this.get_var('terminal-scheme')) !== null && _a !== void 0 ? _a : 'default-dark'; + } + get_realmfs() { + var _a; + return (_a = this.get_var('realmfs')) !== null && _a !== void 0 ? _a : 'base'; + } + static capitalize(term) { + const acronyms = ["gpu", "kvm"]; + if (acronyms.find(s => term === s)) { + return term.toUpperCase(); + } + else { + return term.charAt(0).toUpperCase() + term.slice(1); + } + } + static option_label(key) { + return key.substring(4) + .split('-') + .map(RealmConfig.capitalize) + .join(''); + } + is_enabled_bool_option(name, value, isDefault) { + const default_val = this._globalConfig[name]; + return name.startsWith("use-") && value === "true" && (value === default_val) === isDefault; + } + enabled_bool_option_labels(isDefault) { + return Object.entries(this._configVars) + .filter(([k, v]) => this.is_enabled_bool_option(k, v, isDefault)) + .map(([k,]) => RealmConfig.option_label(k)) + .sort(); + } +} diff --git a/js/model/RealmFS.js b/js/model/RealmFS.js new file mode 100644 index 0000000..e0f60a7 --- /dev/null +++ b/js/model/RealmFS.js @@ -0,0 +1,69 @@ +var _a; +import GObject from 'gi://GObject'; +const OBJECT_PREFIX = "/com/subgraph/Realms2/RealmFS"; +export class RealmFS extends GObject.Object { + constructor(proxy) { + super(); + this._proxy = proxy; + this._proxy.connect('g-properties-changed', (_proxy, _changed, _invalidated) => { + this._sync(); + }); + this._sync(); + } + index() { + let path = this._proxy.get_object_path(); + if (path.startsWith(OBJECT_PREFIX)) { + const tail = path.substring(OBJECT_PREFIX.length); + let index = Number.parseInt(tail); + if (!Number.isNaN(index)) { + return index; + } + } + return -1; + } + get name() { + return this._name; + } + get in_use() { + return this._in_use; + } + get activated() { + return this._activated; + } + get mountpoint() { + return this._mountpoint; + } + get path() { + return this._path; + } + get free_space() { + return this._free_space; + } + get allocated_space() { + return this._allocated_space; + } + _sync() { + this._name = this._proxy.Name; + this._in_use = this._proxy.InUse; + this._activated = this._proxy.Activated; + this._mountpoint = this._proxy.Mountpoint; + this._path = this._proxy.Path; + this._free_space = this._proxy.FreeSpace; + this._allocated_space = this._proxy.AllocatedSpace; + } +} +_a = RealmFS; +(() => { + GObject.registerClass({ + GTypeName: 'RealmFS', + Properties: { + 'name': GObject.ParamSpec.string('name', '', '', GObject.ParamFlags.READWRITE, ''), + 'in-use': GObject.ParamSpec.boolean('in-use', '', '', GObject.ParamFlags.READWRITE, false), + 'activated': GObject.ParamSpec.boolean('activated', '', '', GObject.ParamFlags.READWRITE, false), + 'mountpoint': GObject.ParamSpec.string('mountpoint', '', '', GObject.ParamFlags.READWRITE, ''), + 'path': GObject.ParamSpec.string('path', '', '', GObject.ParamFlags.READWRITE, ''), + 'free-space': GObject.ParamSpec.uint64('free-space', '', '', GObject.ParamFlags.READWRITE, 0, Number.MAX_SAFE_INTEGER, 0), + 'allocated-space': GObject.ParamSpec.uint64('allocated-space', '', '', GObject.ParamFlags.READWRITE, 0, Number.MAX_SAFE_INTEGER, 0), + }, + }, _a); +})(); diff --git a/js/model/RealmManager.js b/js/model/RealmManager.js new file mode 100644 index 0000000..51969d8 --- /dev/null +++ b/js/model/RealmManager.js @@ -0,0 +1,140 @@ +import Gio from 'gi://Gio'; +import { ObjectManager } from './ObjectManager.js'; +import { loadInterfaceXML } from './Utils.js'; +import { Realm } from './Realm.js'; +import { RealmFS } from './RealmFS.js'; +import { LabelColorManager } from './LabelColors.js'; +const Signals = imports.signals; +const RealmIface = loadInterfaceXML("com.subgraph.realms.Realm"); +const RealmFSIface = loadInterfaceXML("com.subgraph.realms.RealmFS"); +const BUS_NAME = 'com.subgraph.Realms2'; +const OBJECT_PATH = '/com/subgraph/Realms2'; +const ManagerInterface = loadInterfaceXML("com.subgraph.realms.Manager2"); +const ManagerProxy = Gio.DBusProxy.makeProxyWrapper(ManagerInterface); +; +export class RealmManager { + static instance() { + return RealmManager.INSTANCE; + } + constructor() { + this._globalConfig = {}; + this._realms = {}; + this._realmfs = {}; + this._realmfs = {}; + this._objectManager = new ObjectManager({ + connection: Gio.DBus.system, + name: 'com.subgraph.Realms2', + objectPath: '/com/subgraph/Realms2', + knownInterfaces: [RealmIface, RealmFSIface], + onLoaded: this._onLoaded.bind(this), + }); + this._proxy = new ManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH, (_proxy, error) => { + if (error) { + logError(error); + } + else { + this._setGlobalConfig().catch(logError); + } + }); + this._labelColors = new LabelColorManager(); + } + async _setGlobalConfig() { + let result = await this._proxy.GetGlobalConfigAsync(); + this._globalConfig = result[0]; + this._assignGlobalConfigToRealms(); + } + async _onLoaded() { + let realms = this._objectManager.getProxiesForInterface('com.subgraph.realms.Realm'); + for (let i = 0; i < realms.length; i++) { + this._addRealm(realms[i]); + } + let realmfs = this._objectManager.getProxiesForInterface('com.subgraph.realms.RealmFS'); + for (let i = 0; i < realmfs.length; i++) { + this._addRealmFS(realmfs[i]); + } + this._objectManager.connect('interface-added', (_objectManager, interfaceName, proxy) => { + if (interfaceName === 'com.subgraph.Realm') { + this._addRealm(proxy); + } + else if (interfaceName === 'com.subgraph.RealmFS') { + this._addRealmFS(proxy); + } + }); + this._objectManager.connect('interface-removed', (_objectManager, interfaceName, proxy) => { + if (interfaceName === 'com.subgraph.Realm') { + this._removeRealm(proxy); + } + else if (interfaceName === 'com.subgraph.RealmFS') { + this._removeRealmFS(proxy); + } + }); + } + async _addRealm(proxy) { + let objectPath = proxy.get_object_path(); + let realm = new Realm(proxy, this._labelColors); + this._realms[objectPath] = realm; + this._findRealmFSForRealm(realm); + if (this._globalConfig) { + realm.set_global_config(this._globalConfig); + } + await realm.load_config(); + this.emit('realm-added', realm); + } + _removeRealm(proxy) { + var _a; + let objectPath = proxy.get_object_path(); + if (((_a = this._realms[objectPath]) === null || _a === void 0 ? void 0 : _a._proxy) === proxy) { + let realm = this._realms[objectPath]; + delete this._realms[objectPath]; + this.emit('realm-removed', realm); + } + proxy.disconnectAll(); + } + _addRealmFS(proxy) { + let objectPath = proxy.get_object_path(); + let realmfs = new RealmFS(proxy); + this._realmfs[objectPath] = realmfs; + this._assignRealmFSToRealms(realmfs); + this.emit('realmfs-added', realmfs); + } + _findRealmFSForRealm(realm) { + var _a; + if (realm.realmfs_index() > 0) { + realm.realmfs = (_a = Object.values(this._realmfs) + .find(realmfs => realmfs.index() === realm.realmfs_index())) !== null && _a !== void 0 ? _a : null; + } + } + realmfsList() { + return Object.values(this._realmfs); + } + _assignRealmFSToRealms(realmfs) { + Object.values(this._realms).forEach(r => { + if (r.realmfs_index() > 0 && r.realmfs_index() === realmfs.index() && r.realmfs != realmfs) { + r.realmfs = realmfs; + } + }); + } + _assignGlobalConfigToRealms() { + Object.values(this._realms).forEach(r => r.set_global_config(this._globalConfig)); + } + _removeRealmFSFromRealms(realmfs) { + Object.values(this._realms).forEach(r => { + if (r.realmfs === realmfs) { + r.realmfs = null; + } + }); + } + _removeRealmFS(proxy) { + var _a; + let objectPath = proxy.get_object_path(); + if (((_a = this._realmfs[objectPath]) === null || _a === void 0 ? void 0 : _a._proxy) === proxy) { + let realmfs = this._realmfs[objectPath]; + delete this._realmfs[objectPath]; + this._removeRealmFSFromRealms(realmfs); + this.emit('realmfs-removed', realmfs); + } + proxy.disconnectAll(); + } +} +RealmManager.INSTANCE = new RealmManager(); +Signals.addSignalMethods(RealmManager.prototype); diff --git a/js/model/Utils.js b/js/model/Utils.js new file mode 100644 index 0000000..d256968 --- /dev/null +++ b/js/model/Utils.js @@ -0,0 +1,14 @@ +import Gio from 'gi://Gio'; +export function loadInterfaceXML(iface) { + let uri = `resource:///com/subgraph/citadel/Realms/dbus-interfaces/${iface}.xml`; + let f = Gio.File.new_for_uri(uri); + try { + let [_ok, bytes] = f.load_contents(null); + // @ts-ignore + return new TextDecoder().decode(bytes); + } + catch (e) { + log(`Failed to load D-Bus interface ${iface}`); + } + return null; +} diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..3abee6f --- /dev/null +++ b/meson.build @@ -0,0 +1,16 @@ +project( + 'Realms', + version: '0.0.1', + license: ['GLP-Stallmans-Razor'], + meson_version: '>= 0.59.0' +) + +APP_ID = 'com.subgraph.citadel.Realms' + +gnome = import('gnome') + +#tsc = find_program('tsc', required: true) + +subdir('data') +subdir('src') + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..1681cca --- /dev/null +++ b/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "Realms", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "Realms", + "version": "1.0.0", + "dependencies": { + "typescript": "^5.3.3" + } + }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..5446e5b --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "Realms", + "version": "1.0.0", + "main": "src/main.ts", + "dependencies": { + "typescript": "^5.3.3" + }, + "scripts": { + "build": "tsc --strict", + "typecheck": "tsc --strict --noEmit" + } +} diff --git a/src/Application.ts b/src/Application.ts new file mode 100644 index 0000000..8bbc585 --- /dev/null +++ b/src/Application.ts @@ -0,0 +1,120 @@ +import Adw from 'gi://Adw'; +import Gtk from 'gi://Gtk?version=4.0'; +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; + +import './model/RealmManager.js'; +import './model/Realm.js'; +import './RealmsView.js'; +import './RealmRow.js'; +import './RealmInfo.js'; +import './RealmModel.js'; +import './ConfigureRealm.js'; + + +import { Window } from './Window.js'; + + +export class Application extends Adw.Application { + static { + GObject.registerClass({ + GTypeName: 'RealmsApplication' + }, this); + } + + _window?: Window; + + constructor() { + super({ + application_id: 'com.subgraph.citadel.Realms', + flags: Gio.ApplicationFlags.DEFAULT_FLAGS, + }) + } + + vfunc_activate() { + let {activeWindow} = this; + if (!activeWindow) { + activeWindow = new Window(this); + activeWindow.set_hide_on_close(true); + } + activeWindow.present(); + } + + vfunc_startup() { + super.vfunc_startup(); + this.#setupActions(); + this.#setupAccelerators(); +// this.#loadStyleSheet(); + const styleManager = Adw.StyleManager.get_default(); + styleManager.colorScheme = Adw.ColorScheme.FORCE_DARK; + } + + /* + #loadStyleSheet() { + const provider = new Gtk.CssProvider(); + provider.load_from_resource('/com/subgraph/citadel/Realms/css/style.css'); + + const display = Gdk.Display.get_default(); + if (display) { + Gtk.StyleContext.add_provider_for_display( + display, + provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ); + } + } + */ + + #setupAccelerators() { + this.set_accels_for_action('app.quit', ['q']); + this.set_accels_for_action('app.realmConfig', ['c']); + this.set_accels_for_action('app.showHelp', ['h','question']); + } + + #setupActions() { + this.add_action_entries([ + // @ts-ignore + { name: 'quit', activate: this.#onQuit.bind(this) }, + // @ts-ignore + { name: 'realmConfig', activate: this.#onRealmConfig.bind(this) }, + // @ts-ignore + { name: 'showHelp', activate: this.#onShowHelp.bind(this) }, + // @ts-ignore + { name: 'about', activate: this.#onAbout.bind(this) }, + ]); + } + + #onQuit() { + let {activeWindow} = this; + if (activeWindow) { + activeWindow.close(); + } + } + + #onRealmConfig() { + let {activeWindow} = this; + if (activeWindow) { + let window = activeWindow as Window; + + const realm = window.realms_view.selectedRealm; + if (realm) { + window.configureRealm(realm); + } + } + } + + #onShowHelp() { + const help: Gtk.ShortcutsWindow = Gtk.Builder.new_from_resource('/com/subgraph/citadel/Realms/ui/HelpWindow.ui').get_object('helpWindow'); + help.set_transient_for(this._window); + help.present(); + } + + #onAbout() { + const dialog = new Adw.AboutDialog({ + application_icon: 'face-smile', + application_name: 'Realms', + developer_name: "Subgraph", + }) + dialog.present(this._window); + } +} diff --git a/src/ColorSchemeChooser.ts b/src/ColorSchemeChooser.ts new file mode 100644 index 0000000..84c0ab8 --- /dev/null +++ b/src/ColorSchemeChooser.ts @@ -0,0 +1,225 @@ +import Adw from 'gi://Adw'; +import GObject from 'gi://GObject'; +import GLib from 'gi://GLib'; +import Gdk from 'gi://Gdk?version=4.0'; +import Gtk from 'gi://Gtk?version=4.0'; + +import {Base16Theme} from './model/Base16Themes.js'; +import {ColorSchemeModel} from './ColorSchemeModel.js'; + + +class PreviewRenderer { + + private theme: Base16Theme; + private buffer: string; + + constructor(theme: Base16Theme) { + this.theme = theme; + this.buffer = ''; + + } + + colorAttrib(name: string, color: string) { + this.buffer += ` ${name}='${color}'`; + } + + colorSpan(fg: string, bg = null) { + this.buffer += '').nl() + .func('#include ').string('').nl() + .nl() + .vtype('static char').text(' theme[] = ').string(`"${name}"`).text(';').nl() + .nl() + .vtype('int').text(' main(').vtype('int').text(' argc, ').vtype('char').text(' **argv) {').nl() + .text(' printf(').string('"Hello, ').keyword('%s').text('!').keyword('\\n').string('"').text(', theme);').nl() + .text(' exit(').konst('0').text(');').nl() + .text('}') + .nl(); + + return this.buffer; + } + +} + +export class ColorSchemeChooser extends Adw.NavigationPage{ + + private _colorList!: Gtk.ListView; + private _previewLabel!: Gtk.Label; + css_provider: Gtk.CssProvider; + model: ColorSchemeModel; + + static { + GObject.registerClass({ + GTypeName: 'ColorSchemeChooser', + Template: 'resource:///com/subgraph/citadel/Realms/ui/ColorSchemeChooser.ui', + InternalChildren: ['colorList', 'previewLabel'], + }, this); + } + + addExpandToggleShortcut(keyval: number) { + let trigger = Gtk.KeyvalTrigger.new(keyval, Gdk.ModifierType.NO_MODIFIER_MASK); + let action = Gtk.CallbackAction.new((expander) => expander.activate_action('listitem.toggle-expand', null)); + Gtk.TreeExpander.add_shortcut(Gtk.Shortcut.new(trigger, action)); + } + + constructor() { + super(); + + this.model = new ColorSchemeModel(); + + this._previewLabel.add_css_class("preview"); + this.css_provider = new Gtk.CssProvider(); + + const display = Gdk.Display.get_default(); + if (display) { + Gtk.StyleContext.add_provider_for_display( + display, + this.css_provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ); + } + + this.addExpandToggleShortcut(Gdk.KEY_space); + this.addExpandToggleShortcut(Gdk.KEY_l); + + + this._colorList.connect('activate', () => { + this.previewSelectedTheme(); + }) + this._colorList.single_click_activate = true; + this._colorList.model = this.model.selection; + + const keyController = new Gtk.EventControllerKey(); + keyController.connect('key-pressed', (_, keyval) => { + switch (keyval) { + case Gdk.KEY_j: + case Gdk.KEY_Down: + this.nextScheme(); + break; + case Gdk.KEY_k: + case Gdk.KEY_Up: + this.prevScheme(); + break; + } + }); + this._colorList.add_controller(keyController); + + } + + nextScheme() { + let pos = this.model.changeSelected(1); + if (pos) { + this._colorList.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, null); + } + } + prevScheme() { + let pos = this.model.changeSelected(-1); + if(pos) { + this._colorList.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, null); + } + } + + selectTheme(id?: string) { + const DEFAULT_THEME = '3024'; + let row = this.model.searchId(id ?? DEFAULT_THEME); + if (row) { + let pos = row.get_position(); + this.model.selectPosition(pos); + this._colorList.scroll_to(pos, Gtk.ListScrollFlags.SELECT | Gtk.ListScrollFlags.FOCUS, null); + this.previewSelectedTheme(); + } + } + + getSelectedTheme() { + return this.model.selectedTheme(); + } + + previewSelectedTheme() { + let theme = this.model.selectedTheme(); + if (theme) { + this.setBackgroundColor(theme); + let renderer = new PreviewRenderer(theme); + // @ts-ignore + this._previewLabel.label = renderer.renderPreview(); + } + } + + setBackgroundColor(theme: Base16Theme) { + let css = ` +label.preview { + background-color: ${theme.terminal_background()}; + font-family: monospace; + font-size: 14pt; +}`; + this.css_provider.load_from_string(css); + } + +} diff --git a/src/ColorSchemeModel.ts b/src/ColorSchemeModel.ts new file mode 100644 index 0000000..40b8cba --- /dev/null +++ b/src/ColorSchemeModel.ts @@ -0,0 +1,176 @@ +import GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; +import Gtk from 'gi://Gtk?version=4.0'; + +import {Base16Theme} from './model/Base16Themes.js'; + +class ModelBuilder { + CATEGORIES = [ + ['atelier', 'Atelier'], + ['black-metal', 'Black Metal'], + ['brushtrees', 'Brush Trees'], + ['classic', 'Classic'], + ['default', 'Default'], + ['google', 'Google'], + ['grayscale', 'Grayscale'], + ['gruvbox', 'Gruvbox'], + ['harmonic', 'Harmonic'], + ['ia', 'iA'], + ['material', 'Material'], + ['papercolor', 'PaperColor'], + ['solarized', 'Solarized'], + ['summerfruit', 'Summerfruit'], + ['tomorrow', 'Tomorrow'], + ['unikitty', 'Unikitty'], + ]; + + currentCategory: null | ColorSchemeNode = null; + storeModel = new Gio.ListStore(ColorSchemeNode); + + + matchesCategory(theme: Base16Theme): boolean { + return (this.CATEGORIES.length > 0 && theme.id.startsWith(this.CATEGORIES[0][0])) + } + + appendCurrentCategory() { + if (this.currentCategory) { + this.storeModel.append(this.currentCategory); + this.currentCategory = null; + this.CATEGORIES.shift(); + } + } + + addToCategory(theme: Base16Theme) { + if (!this.currentCategory) { + this.currentCategory = new ColorSchemeNode(this.CATEGORIES[0][1]); + } + this.currentCategory.addChild(new ColorSchemeNode(theme.name, theme)); + } + + addTheme(theme: Base16Theme) { + if (this.matchesCategory(theme)) { + this.addToCategory(theme); + } else { + if (this.currentCategory) { + this.appendCurrentCategory(); + } + this.storeModel.append(new ColorSchemeNode(theme.name, theme)); + } + } + + static buildTreeModel(): Gtk.TreeListModel { + let builder = new ModelBuilder(); + Base16Theme.THEME_LIST.forEach(theme => builder.addTheme(theme)); + return Gtk.TreeListModel.new( + builder.storeModel, + false, + false, + item => (item as ColorSchemeNode).child_model, + ); + } + +} + +export class ColorSchemeNode extends GObject.Object { + private _text: string; + + theme: Base16Theme | null; + child_model: Gio.ListStore | null; + static { + GObject.registerClass({ + GTypeName: 'ColorSchemeNode', + Properties: { + 'text': GObject.ParamSpec.string('text', '', '', GObject.ParamFlags.READWRITE, ''), + } + + }, this); + } + + constructor(text: string, theme: Base16Theme | null = null) { + super(); + this._text = text; + this.theme = theme; + this.child_model = null; + } + + get text(): string { + return this._text; + } + + addChild(child: ColorSchemeNode) { + if(this.child_model === null) { + this.child_model = new Gio.ListStore(ColorSchemeNode); + } + this.child_model.append(child); + } + + matchesId(id: string): boolean { + return this.theme !== null && this.theme.id === id; + } + + searchChildren(id: string): boolean { + if (this.child_model) { + for(let i = 0; i < this.child_model.n_items; i++) { + let node = this.child_model.get_item(i) as ColorSchemeNode; + if (node.matchesId(id)) { + return true; + } + } + } + return false; + + } +} + +export class ColorSchemeModel { + selection: Gtk.SingleSelection; + treeModel: Gtk.TreeListModel; + constructor() { + this.treeModel = ModelBuilder.buildTreeModel(); + this.selection = Gtk.SingleSelection.new(this.treeModel); + } + + selectedTheme() { + // @ts-ignore + let item = this.selection.selected_item.item; + return item?.theme; + } + + changeSelected(offset: number) { + const n_items = this.selection.n_items; + if (n_items <= 1) { + return; + } + const pos = this.selection.selected; + if (pos == Gtk.INVALID_LIST_POSITION) { + return; + } + const newPos = pos + offset; + if (newPos < 0 || newPos >= n_items) { + return; + } + this.selection.selected = newPos; + return newPos; + } + + selectPosition(pos: number) { + this.selection.selected = pos; + } + + searchId(id: string, from = 0): Gtk.TreeListRow | null { + for(let i = from; i < this.treeModel.n_items; i++) { + let row = this.treeModel.get_row(i); + let item = row?.item as ColorSchemeNode; + if (item && item.matchesId(id)) { + return row; + } + if (item.searchChildren(id)) { + row?.set_expanded(true); + if(from === 0) { + return this.searchId(id, i); + } + } + } + return null; + } +} diff --git a/src/ConfigureRealm.ts b/src/ConfigureRealm.ts new file mode 100644 index 0000000..75e2412 --- /dev/null +++ b/src/ConfigureRealm.ts @@ -0,0 +1,189 @@ +import GObject from 'gi://GObject' +import Gdk from 'gi://Gdk?version=4.0' +import Gtk from 'gi://Gtk?version=4.0' +import Adw from 'gi://Adw'; + +import {Realm} from './model/Realm.js'; +import {BoolOptionData} from './model/RealmConfig.js'; +import {Base16Theme} from './model/Base16Themes.js'; +import { RealmManager } from './model/RealmManager.js'; +import {ColorSchemeChooser} from './ColorSchemeChooser.js'; + +class OptionState { + value: any = null; + originalValue: any; + + row: Adw.SwitchRow; + + constructor(row: Adw.SwitchRow) { + this.row = row; + } + + initValue(value: any) { + this.value = value; + this.originalValue = value; + this.row.active = value; + } + + /** @param {any} value */ + setValue(value: any) { + this.value = value; + } + + hasChanged() { + return this.value !== this.originalValue; + } + +} + +export class ConfigureRealm extends Adw.NavigationPage { + + static { + GObject.registerClass({ + GTypeName: "ConfigureRealm", + Template: 'resource:///com/subgraph/citadel/Realms/ui/ConfigureRealm.ui', + InternalChildren: ['optionsGroup', 'changedBanner', 'overlayCombo', 'realmfsCombo', 'colorSchemeButton', 'labelColorButton'], + Properties: { + 'navigation-view': GObject.ParamSpec.object('navigation-view', '', '', GObject.ParamFlags.READWRITE, Adw.NavigationView), + 'colorscheme-chooser': GObject.ParamSpec.object('colorscheme-chooser', '', '', GObject.ParamFlags.READWRITE, ColorSchemeChooser), + } + }, this); + } + + realm: Realm | null = null; + + private _colorSchemeButton!: Gtk.Button ; + + private _labelColorButton!: Gtk.ColorDialogButton; + + private _optionsGroup!: Adw.PreferencesGroup; + private _changedBanner!: Adw.Banner; + + private _navigationView!: Adw.NavigationView; + + private _colorschemeChooser!: ColorSchemeChooser; + + optionState: Map = new Map(); + + theme = Base16Theme.lookup('default-dark'); + + constructor() { + super(); + this._addOptions(); + const keyController = new Gtk.EventControllerKey(); + keyController.connect('key-pressed', (_, keyval) => { + if (keyval === Gdk.KEY_j) { + this.vfunc_move_focus(Gtk.DirectionType.TAB_FORWARD); + } else if (keyval === Gdk.KEY_k) { + this.vfunc_move_focus(Gtk.DirectionType.TAB_BACKWARD); + } + }); + + this.add_controller(keyController); + this._labelColorButton?.connect('notify::rgba', () => this._scanChanges()); + this._colorSchemeButton?.connect('clicked', () => { + this.navigationView?.push_by_tag('realm-colorscheme'); + }); + + } + + get colorschemeChooser() { + return this._colorschemeChooser; + } + + set colorschemeChooser(val) { + this._colorschemeChooser = val; + } + + set navigationView(val) { + this._navigationView = val; + this._navigationView?.connect('popped', (_view, page) => { + if (page === this.colorschemeChooser) { + let theme = this.colorschemeChooser.getSelectedTheme(); + if (theme && this._colorSchemeButton) { + this.theme = theme; + this._colorSchemeButton.label = this.theme.name; + } + } + }); + } + + get navigationView() { + return this._navigationView; + + } + + configure(realm: Realm) { + this.realm = realm; + this.set_title(`Configure realm-${realm.name}`); + this._setOptions(realm); + } + + _setOptions(realm: Realm) { + let config = realm.config; + this.optionState.forEach((op,name) => { + let v = config.get_bool(name); + op.initValue(v); + }) + + let scheme = realm.config.get_colorscheme(); + this.theme = Base16Theme.lookup(scheme); + if (this.theme && this._colorSchemeButton) { + this._colorSchemeButton.label = this.theme.name; + this.colorschemeChooser?.selectTheme(this.theme.id); + } + + let realmfs_list = RealmManager.instance().realmfsList(); + // @ts-ignore + let model = this._realmfsCombo.model; + if(model.n_items > 0) { + model.splice(0, model.n_items, []); + } + realmfs_list.forEach(realmfs => model.append(realmfs.name)); + + let labelColor = realm.getLabelColor(); + this._labelColorButton.rgba = labelColor; + + this._scanChanges(); + } + + _addOptions() { + BoolOptionData.allOptions().forEach(option => { + let row = new Adw.SwitchRow({ + title: option.description, + }); + if (option.tooltip.length > 0) { + row.tooltipMarkup = option.tooltip; + } + let state = new OptionState(row); + this.optionState.set(option.name, state); + + row.connect("notify::active", () => { + state.setValue(row.active); + this._scanChanges(); + }); + + this._optionsGroup.add(row); + }) + } + + _scanChanges() { + let changed = false; + this.optionState.forEach(op => { + if (op.hasChanged()) { + changed = true; + } + }); + let labelColor = this.realm?.getLabelColor(); + if (labelColor) { + if (!this._labelColorButton.rgba.equal(labelColor)) { + changed = true; + } + } + this._changedBanner.set_revealed(changed); + } + + _onApplyClicked() { + print("clikkk"); + } +} diff --git a/src/RealmInfo.ts b/src/RealmInfo.ts new file mode 100644 index 0000000..78109c5 --- /dev/null +++ b/src/RealmInfo.ts @@ -0,0 +1,147 @@ +import GObject from 'gi://GObject' +import Gtk from 'gi://Gtk?version=4.0'; +import {Realm} from "./model/Realm.js"; + +import {RealmInfoEntry} from './RealmInfoEntry.js'; + +const Sections = { + STATUS: 'Status', + OPTIONS: 'Options', + REALMFS: 'RealmFS', + MOUNTPOINT: 'RealmFS Mountpoint', +}; + +export class RealmInfo extends Gtk.ScrolledWindow { + private _column!: Gtk.Box; + private _columnTitle!: Gtk.Label; + + static { + GObject.registerClass({ + GTypeName: "RealmInfo", + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmInfo.ui', + Properties: { + 'realm': GObject.ParamSpec.object('realm', '', '', GObject.ParamFlags.READWRITE, Realm), + }, + InternalChildren: [ + 'column', + 'columnTitle', + ] + }, this); + } + + + _buffer = ""; + _changeId: number = 0; + _realm: Realm | null = null; + + _entries: Map = new Map(); + + constructor() { + super(); + this._addEntries({ + left: [ + Sections.STATUS, + Sections.REALMFS, + Sections.MOUNTPOINT, + Sections.OPTIONS, + ], + right: [] + }); + } + + + _addEntries(labels: { left: string[]; right: string[]; }) { + let addSectionEntries = (section: Gtk.Box, entries: string[]) => { + entries.forEach(name => { + let entry = new RealmInfoEntry(name); + this._entries.set(name, entry); + section.append(entry); + }) + }; + + addSectionEntries(this._column, labels['left']); + } + + get realm(): Realm | null { + return this._realm; + } + + set realm(value) { + if (this.realm === value) { + return; + } + if (this._realm) { + this._realm.disconnect(this._changeId); + } + this._realm = value; + if (this._realm) { + this._changeId = this._realm.connect('changed', () => { + this.displayRealm(); + }); + this.displayRealm(); + } + } + + + setEntry(name: string, value: string) { + let entry = this._entries.get(name); + if (entry) { + entry.setValue(value); + } + } + + setEntryVisible(name: string, isVisible: boolean = true) { + let entry = this._entries.get(name); + if (entry) { + entry.set_visible(isVisible); + } + + } + + displayConfig() { + let config = this._realm?.config; + if (config) { + let enabled_default = config.enabled_bool_option_labels(true); + let enabled = config.enabled_bool_option_labels(false); + let buffer = enabled_default.join(' '); + buffer += '\n'; + buffer += enabled.map(s => `${s}`) + .join(' '); + this.setEntry(Sections.OPTIONS, buffer); + } else { + this.setEntry(Sections.OPTIONS, ''); + } + } + + displayRealmFS() { + let realmfs = this._realm?.realmfs; + if (realmfs) { + this.setEntryVisible(Sections.REALMFS); + this.setEntry(Sections.REALMFS, `${realmfs.name}-realmfs.img`); + if(realmfs.activated) { + this.setEntryVisible(Sections.MOUNTPOINT); + this.setEntry(Sections.MOUNTPOINT, realmfs.mountpoint); + + } else { + this.setEntryVisible(Sections.MOUNTPOINT, false); + } + + } else { + this.setEntryVisible(Sections.REALMFS, false); + this.setEntryVisible(Sections.MOUNTPOINT, false); + } + } + + displayRealm() { + if (this._realm) { + this._columnTitle.label = `Realm ${this._realm.name}`; + if (this._realm.is_running()) { + this.setEntry("Status", "Running"); + } else { + this.setEntry("Status", "Stopped"); + } + this.displayConfig(); + this.displayRealmFS(); + } + } +} diff --git a/src/RealmInfoEntry.ts b/src/RealmInfoEntry.ts new file mode 100644 index 0000000..8620fdf --- /dev/null +++ b/src/RealmInfoEntry.ts @@ -0,0 +1,24 @@ +import GObject from 'gi://GObject' +import Gtk from 'gi://Gtk?version=4.0'; + +export class RealmInfoEntry extends Gtk.Box { + private _nameLabel!: Gtk.Label; + private _valueLabel!: Gtk.Label; + static { + GObject.registerClass({ + GTypeName: "RealmInfoEntry", + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmInfoEntry.ui', + InternalChildren: ['nameLabel', 'valueLabel'] + }, this); + } + + constructor(name: string) { + super(); + this._nameLabel.label = name; + } + + setValue(value: string) { + this._valueLabel.label = value; + } +} + diff --git a/src/RealmModel.ts b/src/RealmModel.ts new file mode 100644 index 0000000..d2c2085 --- /dev/null +++ b/src/RealmModel.ts @@ -0,0 +1,49 @@ +import Gio from 'gi://Gio'; +import GObject from 'gi://GObject'; +import { Realm } from './model/Realm.js'; +import {RealmManager} from "./model/RealmManager.js"; + +export class RealmModel extends GObject.Object { + static { + GObject.registerClass({ + GTypeName: 'RealmModel', + Implements: [Gio.ListModel], + }, this); + } + + _realms: Realm[] = []; + _realmManager: RealmManager; + + constructor() { + super(); + this._realmManager = RealmManager.instance(); + + this._realmManager.connect('realm-added', (_manager, realm: Realm) => { + let pos = this._realms.length; + this._realms.push(realm); + // @ts-ignore + this.items_changed(pos, 0, 1); + }); + + this._realmManager.connect('realm-removed', (_manager, realm) => { + let pos = this._realms.findIndex(r => r === realm); + if (pos >= 0) { + this._realms.splice(pos, 1); + // @ts-ignore + this.items_changed(pos, 1, 0); + } + }); + } + + vfunc_get_item_type() { + return Realm; + } + + vfunc_get_item(position: number) { + return this._realms[position] || null; + } + + vfunc_get_n_items() { + return this._realms.length; + } +} diff --git a/src/RealmRow.ts b/src/RealmRow.ts new file mode 100644 index 0000000..4a99e85 --- /dev/null +++ b/src/RealmRow.ts @@ -0,0 +1,59 @@ +import GObject from 'gi://GObject'; +import Gtk from 'gi://Gtk?version=4.0'; + +import { Realm } from './model/Realm.js'; + +export class RealmRow extends Gtk.Widget { + private _nameLabel!: Gtk.Label; + + static { + GObject.registerClass({ + GTypeName: 'RealmRow', + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmRow.ui', + Properties: { + 'realm': GObject.ParamSpec.object('realm', '','',GObject.ParamFlags.READWRITE, Realm), + }, + InternalChildren: ['nameLabel'], + }, this); + } + + _realm!: Realm | null; + + _notifyId = 0; + + + get realm() { + if (this._realm === undefined) { + this._realm = null; + } + return this._realm; + } + set realm(value) { + if(this.realm === value) { + return; + } + + if(this.realm && this._notifyId) { + this.realm.disconnect(this._notifyId); + this._notifyId = 0; + } + this._realm = value; + + if(this.realm) { + this._notifyId = this.realm.connect('notify', this.syncRealm.bind(this)); + this.syncRealm(); + } + + this.notify('realm'); + } + + syncRealm() { + if (this.realm && this.realm.is_running()) { + this._nameLabel.remove_css_class('dim-label'); + } else { + // @ts-ignore + this._nameLabel.add_css_class('dim-label'); + } + } +} + diff --git a/src/RealmsView.ts b/src/RealmsView.ts new file mode 100644 index 0000000..ae72b38 --- /dev/null +++ b/src/RealmsView.ts @@ -0,0 +1,106 @@ +import GObject from 'gi://GObject'; +import Gdk from 'gi://Gdk?version=4.0'; +import Gtk from 'gi://Gtk?version=4.0'; + +import {Realm} from "./model/Realm.js"; + +export class RealmsView extends Gtk.Widget { + private _realmsSelection!: Gtk.SingleSelection; + private _hiddenRealmsFilterModel!: Gtk.FilterListModel; + private _realmSorter!: Gtk.CustomSorter; + + static { + GObject.registerClass({ + GTypeName: 'RealmsView', + Template: 'resource:///com/subgraph/citadel/Realms/ui/RealmsView.ui', + Properties: { + 'selected-realm': GObject.ParamSpec.object('selected-realm', '', '', GObject.ParamFlags.READWRITE, Realm), + }, + InternalChildren: ['realmsSelection', 'hiddenRealmsFilterModel', 'realmSorter'], + }, this); + } + + _selectedRealm = null; + + constructor() { + super(); + this._setupHiddenRealmsFilter(); + this._setupRealmSorter(); + const keyController = new Gtk.EventControllerKey(); + keyController.connect('key-pressed', (_, keyval) => { + switch (keyval) { + case Gdk.KEY_j: + case Gdk.KEY_Down: + this.nextRealm(); + break; + case Gdk.KEY_k: + case Gdk.KEY_Up: + this.prevRealm(); + break; + case Gdk.KEY_Escape: + this.activate_action('app.quit', null); + print ("escaped"); + break; + } + }); + this.add_controller(keyController); + } + + _changeSelected(offset: number) { + const n_items = this._realmsSelection.n_items; + + if (n_items <= 1) { + return; + } + const pos = this._realmsSelection.selected; + if (pos == Gtk.INVALID_LIST_POSITION) { + return; + } + + const newPos = pos + offset; + + if (newPos < 0 || newPos >= n_items) { + return; + } + + this._realmsSelection.selected = newPos; + } + + nextRealm() { + this._changeSelected(1); + } + + prevRealm() { + this._changeSelected(-1); + } + + get selectedRealm() { + return this._selectedRealm; + } + + set selectedRealm(value) { + if (this.selectedRealm === value) { + return; + } + this._selectedRealm = value; + } + + _setupHiddenRealmsFilter() { + this._hiddenRealmsFilterModel.filter = Gtk.CustomFilter.new(item => !(item as Realm).is_system_realm); + } + + _setupRealmSorter() { + // 1) Sort 'Current' the lowest (top of list). + // 2) Then sort 'Running' lower than not 'Running' + // 3) Realms in the same run state sorted by lowest timestamp + this._realmSorter.set_sort_func((a,b) => { + if (a.is_current || (a.is_running && !b.is_running)) { + return Gtk.Ordering.SMALLER; + } else if (b.is_current || (b.is_running && !a.is_running)) { + return Gtk.Ordering.LARGER; + } else { + return b.timestamp - a.timestamp; + } + }); + } +} diff --git a/src/Window.ts b/src/Window.ts new file mode 100644 index 0000000..78c1565 --- /dev/null +++ b/src/Window.ts @@ -0,0 +1,41 @@ +import GObject from 'gi://GObject'; +import Adw from 'gi://Adw'; +import { Realm } from './model/Realm.js'; +import {ConfigureRealm} from './ConfigureRealm.js' +import { RealmsView } from './RealmsView.js'; + +export class Window extends Adw.ApplicationWindow { + + private _realmsView!: RealmsView; + private _configureRealm!: ConfigureRealm; + private _navView!: Adw.NavigationView; + + static { + GObject.registerClass({ + GTypeName: 'RealmsWindow', + Template: 'resource:///com/subgraph/citadel/Realms/ui/Window.ui', + InternalChildren: ['realmsView', 'configureRealm', 'navView'], + }, this); + } + + constructor(application: Adw.Application) { + super({ application: application }); + } + + + configureRealm(realm: Realm) { + this._configureRealm.configure(realm); + this._navView.push(this._configureRealm); + } + + get realms_view() { + return this._realmsView; + } + + + vfunc_close_request() { + super.vfunc_close_request(); + this.run_dispose(); + return true; + } +} diff --git a/src/colors/Base16Theme.ts b/src/colors/Base16Theme.ts new file mode 100644 index 0000000..e9c4a4f --- /dev/null +++ b/src/colors/Base16Theme.ts @@ -0,0 +1,1097 @@ + + + + +export class Base16Theme { + + id: string; + name: string; + colors: number[]; + + static CATEGORIES: [string,string][] = [ + ['atelier', 'Atelier'], + ['black-metal', 'Black Metal'], + ['brushtrees', 'Brush Trees'], + ['classic', 'Classic'], + ['default', 'Default'], + ['google', 'Google'], + ['grayscale', 'Grayscale'], + ['gruvbox', 'Gruvbox'], + ['harmonic', 'Harmonic'], + ['ia', 'iA'], + ['material', 'Material'], + ['papercolor', 'PaperColor'], + ['solarized', 'Solarized'], + ['summerfruit', 'Summerfruit'], + ['tomorrow', 'Tomorrow'], + ['unikitty', 'Unikitty'], + ]; + + static TERM_MAP = [ + 0x00, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x05, + 0x03, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x07, + 0x09, 0x0F, 0x01, 0x02, 0x04, 0x06, + ]; + static THEMES: Base16Theme[] = []; + + static add(id: string, name: string, colors: number[]) { + const theme = new Base16Theme(id, name, colors); + Base16Theme.THEMES.push(theme); + } + + constructor(id: string, name: string, colors: number[]) { + this.id = id; + this.name = name; + this.colors = colors; + } + + color(idx: number) { + let hex = this.colors[idx].toString(16).padStart(6, '0'); + return `#${hex}`; + } + + terminal_background() { + return this.color(0); + } + + terminal_foreground() { + return this.color(5); + } + + terminal_palette_color(idx: number) { + return this.color(Base16Theme.TERM_MAP[idx]); + } +} + +Base16Theme.add("3024", "3024", + [ + 0x090300, 0x3a3432, 0x4a4543, 0x5c5855, + 0x807d7c, 0xa5a2a2, 0xd6d5d4, 0xf7f7f7, + 0xdb2d20, 0xe8bbd0, 0xfded02, 0x01a252, + 0xb5e4f4, 0x01a0e4, 0xa16a94, 0xcdab53, + ]); + +Base16Theme.add("apathy", 'Apathy', + [ + 0x031A16, 0x0B342D, 0x184E45, 0x2B685E, + 0x5F9C92, 0x81B5AC, 0xA7CEC8, 0xD2E7E4, + 0x3E9688, 0x3E7996, 0x3E4C96, 0x883E96, + 0x963E4C, 0x96883E, 0x4C963E, 0x3E965B, + ]); + +Base16Theme.add("ashes", "Ashes", + [ + 0x1C2023, 0x393F45, 0x565E65, 0x747C84, + 0xADB3BA, 0xC7CCD1, 0xDFE2E5, 0xF3F4F5, + 0xC7AE95, 0xC7C795, 0xAEC795, 0x95C7AE, + 0x95AEC7, 0xAE95C7, 0xC795AE, 0xC79595, + ]); + + +Base16Theme.add("atelier-cave-light", "Atelier Cave Light", + [ + 0xefecf4, 0xe2dfe7, 0x8b8792, 0x7e7887, + 0x655f6d, 0x585260, 0x26232a, 0x19171c, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, + ]); + +Base16Theme.add("atelier-cave", "Atelier Cave", + [ + 0x19171c, 0x26232a, 0x585260, 0x655f6d, + 0x7e7887, 0x8b8792, 0xe2dfe7, 0xefecf4, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, + ]); + +Base16Theme.add("atelier-dune-light", "Atelier Dune Light", + [ + 0xfefbec, 0xe8e4cf, 0xa6a28c, 0x999580, + 0x7d7a68, 0x6e6b5e, 0x292824, 0x20201d, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, + ]); + +Base16Theme.add("atelier-dune", "Atelier Dune", + [ + 0x20201d, 0x292824, 0x6e6b5e, 0x7d7a68, + 0x999580, 0xa6a28c, 0xe8e4cf, 0xfefbec, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, + ]); + +Base16Theme.add("atelier-estuary-light", "Atelier Estuary Light", + [ + 0xf4f3ec, 0xe7e6df, 0x929181, 0x878573, + 0x6c6b5a, 0x5f5e4e, 0x302f27, 0x22221b, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, + ]); + +Base16Theme.add("atelier-estuary", "Atelier Estuary", + [ + 0x22221b, 0x302f27, 0x5f5e4e, 0x6c6b5a, + 0x878573, 0x929181, 0xe7e6df, 0xf4f3ec, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, + ]); + +Base16Theme.add("atelier-forest-light", "Atelier Forest Light", + [ + 0xf1efee, 0xe6e2e0, 0xa8a19f, 0x9c9491, + 0x766e6b, 0x68615e, 0x2c2421, 0x1b1918, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, + ]); + +Base16Theme.add("atelier-forest", "Atelier Forest", + [ + 0x1b1918, 0x2c2421, 0x68615e, 0x766e6b, + 0x9c9491, 0xa8a19f, 0xe6e2e0, 0xf1efee, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, + ]); + +Base16Theme.add("atelier-heath-light", "Atelier Heath Light", + [ + 0xf7f3f7, 0xd8cad8, 0xab9bab, 0x9e8f9e, + 0x776977, 0x695d69, 0x292329, 0x1b181b, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, + ]); + +Base16Theme.add("atelier-heath", "Atelier Heath", + [ + 0x1b181b, 0x292329, 0x695d69, 0x776977, + 0x9e8f9e, 0xab9bab, 0xd8cad8, 0xf7f3f7, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, + ]); + +Base16Theme.add("atelier-lakeside-light", "Atelier Lakeside Light", + [ + 0xebf8ff, 0xc1e4f6, 0x7ea2b4, 0x7195a8, + 0x5a7b8c, 0x516d7b, 0x1f292e, 0x161b1d, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, + ]); + +Base16Theme.add("atelier-lakeside", "Atelier Lakeside", + [ + 0x161b1d, 0x1f292e, 0x516d7b, 0x5a7b8c, + 0x7195a8, 0x7ea2b4, 0xc1e4f6, 0xebf8ff, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, + ]); + +Base16Theme.add("atelier-plateau-light", "Atelier Plateau Light", + [ + 0xf4ecec, 0xe7dfdf, 0x8a8585, 0x7e7777, + 0x655d5d, 0x585050, 0x292424, 0x1b1818, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, + ]); + +Base16Theme.add("atelier-plateau", "Atelier Plateau", + [ + 0x1b1818, 0x292424, 0x585050, 0x655d5d, + 0x7e7777, 0x8a8585, 0xe7dfdf, 0xf4ecec, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, + ]); + +Base16Theme.add("atelier-savanna-light", "Atelier Savanna Light", + [ + 0xecf4ee, 0xdfe7e2, 0x87928a, 0x78877d, + 0x5f6d64, 0x526057, 0x232a25, 0x171c19, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, + ]); + +Base16Theme.add("atelier-savanna", "Atelier Savanna", + [ + 0x171c19, 0x232a25, 0x526057, 0x5f6d64, + 0x78877d, 0x87928a, 0xdfe7e2, 0xecf4ee, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, + ]); + +Base16Theme.add("atelier-seaside-light", "Atelier Seaside Light", + [ + 0xf4fbf4, 0xcfe8cf, 0x8ca68c, 0x809980, + 0x687d68, 0x5e6e5e, 0x242924, 0x131513, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, + ]); + +Base16Theme.add("atelier-seaside", "Atelier Seaside", + [ + 0x131513, 0x242924, 0x5e6e5e, 0x687d68, + 0x809980, 0x8ca68c, 0xcfe8cf, 0xf4fbf4, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, + ]); + +Base16Theme.add("atelier-sulphurpool-light", "Atelier Sulphurpool Light", + [ + 0xf5f7ff, 0xdfe2f1, 0x979db4, 0x898ea4, + 0x6b7394, 0x5e6687, 0x293256, 0x202746, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, + ]); + +Base16Theme.add("atelier-sulphurpool", "Atelier Sulphurpool", + [ + 0x202746, 0x293256, 0x5e6687, 0x6b7394, + 0x898ea4, 0x979db4, 0xdfe2f1, 0xf5f7ff, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, + ]); + +Base16Theme.add("atlas", "Atlas", + [ + 0x002635, 0x00384d, 0x517F8D, 0x6C8B91, + 0x869696, 0xa1a19a, 0xe6e6dc, 0xfafaf8, + 0xff5a67, 0xf08e48, 0xffcc1b, 0x7fc06e, + 0x14747e, 0x5dd7b9, 0x9a70a4, 0xc43060, + ]); + +Base16Theme.add("bespin", "Bespin", + [ + 0x28211c, 0x36312e, 0x5e5d5c, 0x666666, + 0x797977, 0x8a8986, 0x9d9b97, 0xbaae9e, + 0xcf6a4c, 0xcf7d34, 0xf9ee98, 0x54be0d, + 0xafc4db, 0x5ea6ea, 0x9b859d, 0x937121, + ]); + +Base16Theme.add("black-metal-bathory", "Black Metal (Bathory)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xe78a53, 0xfbcb97, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-burzum", "Black Metal (Burzum)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x99bbaa, 0xddeecc, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-dark-funeral", "Black Metal (Dark Funeral)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x5f81a5, 0xd0dfee, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-gorgoroth", "Black Metal (Gorgoroth)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x8c7f70, 0x9b8d7f, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-immortal", "Black Metal (Immortal)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x556677, 0x7799bb, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-khold", "Black Metal (Khold)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x974b46, 0xeceee3, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-marduk", "Black Metal (Marduk)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x626b67, 0xa5aaa7, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-mayhem", "Black Metal (Mayhem)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xeecc6c, 0xf3ecd4, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-nile", "Black Metal (Nile)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x777755, 0xaa9988, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-venom", "Black Metal (Venom)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x79241f, 0xf8f7f2, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal", "Black Metal", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xa06666, 0xdd9999, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("brewer", "Brewer", + [ + 0x0c0d0e, 0x2e2f30, 0x515253, 0x737475, + 0x959697, 0xb7b8b9, 0xdadbdc, 0xfcfdfe, + 0xe31a1c, 0xe6550d, 0xdca060, 0x31a354, + 0x80b1d3, 0x3182bd, 0x756bb1, 0xb15928, + ]); + +Base16Theme.add("bright", "Bright", + [ + 0x000000, 0x303030, 0x505050, 0xb0b0b0, + 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, 0xffffff, + 0xfb0120, 0xfc6d24, 0xfda331, 0xa1c659, + 0x76c7b7, 0x6fb3d2, 0xd381c3, 0xbe643c, + ]); + +Base16Theme.add("brogrammer", "Brogrammer", + [ + 0x1f1f1f, 0xf81118, 0x2dc55e, 0xecba0f, + 0x2a84d2, 0x4e5ab7, 0x1081d6, 0xd6dbe5, + 0xd6dbe5, 0xde352e, 0x1dd361, 0xf3bd09, + 0x1081d6, 0x5350b9, 0x0f7ddb, 0xffffff, + ]); + +Base16Theme.add("brushtrees-dark", "Brush Trees Dark", + [ + 0x485867, 0x5A6D7A, 0x6D828E, 0x8299A1, + 0x98AFB5, 0xB0C5C8, 0xC9DBDC, 0xE3EFEF, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, + ]); + +Base16Theme.add("brushtrees", "Brush Trees", + [ + 0xE3EFEF, 0xC9DBDC, 0xB0C5C8, 0x98AFB5, + 0x8299A1, 0x6D828E, 0x5A6D7A, 0x485867, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, + ]); + +Base16Theme.add("chalk", "Chalk", + [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, + 0xfb9fb1, 0xeda987, 0xddb26f, 0xacc267, + 0x12cfc0, 0x6fc2ef, 0xe1a3ee, 0xdeaf8f, + ]); + +Base16Theme.add("circus", "Circus", + [ + 0x191919, 0x202020, 0x303030, 0x5f5a60, + 0x505050, 0xa7a7a7, 0x808080, 0xffffff, + 0xdc657d, 0x4bb1a7, 0xc3ba63, 0x84b97c, + 0x4bb1a7, 0x639ee4, 0xb888e2, 0xb888e2, + ]); + +Base16Theme.add("classic-dark", "Classic Dark", + [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xF5F5F5, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, + ]); + +Base16Theme.add("classic-light", "Classic Light", + [ + 0xF5F5F5, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x505050, 0x303030, 0x202020, 0x151515, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, + ]); + +Base16Theme.add("codeschool", "Codeschool", + [ + 0x232c31, 0x1c3657, 0x2a343a, 0x3f4944, + 0x84898c, 0x9ea7a6, 0xa7cfa3, 0xb5d8f6, + 0x2a5491, 0x43820d, 0xa03b1e, 0x237986, + 0xb02f30, 0x484d79, 0xc59820, 0xc98344, + ]); + +Base16Theme.add("cupcake", "Cupcake", + [ + 0xfbf1f2, 0xf2f1f4, 0xd8d5dd, 0xbfb9c6, + 0xa59daf, 0x8b8198, 0x72677E, 0x585062, + 0xD57E85, 0xEBB790, 0xDCB16C, 0xA3B367, + 0x69A9A7, 0x7297B9, 0xBB99B4, 0xBAA58C, + ]); + +Base16Theme.add("cupertino", "Cupertino", + [ + 0xffffff, 0xc0c0c0, 0xc0c0c0, 0x808080, + 0x808080, 0x404040, 0x404040, 0x5e5e5e, + 0xc41a15, 0xeb8500, 0x826b28, 0x007400, + 0x318495, 0x0000ff, 0xa90d91, 0x826b28, + ]); + +Base16Theme.add("darktooth", "Darktooth", + [ + 0x1D2021, 0x32302F, 0x504945, 0x665C54, + 0x928374, 0xA89984, 0xD5C4A1, 0xFDF4C1, + 0xFB543F, 0xFE8625, 0xFAC03B, 0x95C085, + 0x8BA59B, 0x0D6678, 0x8F4673, 0xA87322, + ]); + +Base16Theme.add("default-dark", "Default Dark", + [ + 0x181818, 0x282828, 0x383838, 0x585858, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, + ]); + +Base16Theme.add("default-light", "Default Light", + [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, + ]); + +Base16Theme.add("dracula", "Dracula", + [ + 0x282936, 0x3a3c4e, 0x4d4f68, 0x626483, + 0x62d6e8, 0xe9e9f4, 0xf1f2f8, 0xf7f7fb, + 0xea51b2, 0xb45bcf, 0x00f769, 0xebff87, + 0xa1efe4, 0x62d6e8, 0xb45bcf, 0x00f769, + ]); + +Base16Theme.add("eighties", "Eighties", + [ + 0x2d2d2d, 0x393939, 0x515151, 0x747369, + 0xa09f93, 0xd3d0c8, 0xe8e6df, 0xf2f0ec, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xd27b53, + ]); + +Base16Theme.add("embers", "Embers", + [ + 0x16130F, 0x2C2620, 0x433B32, 0x5A5047, + 0x8A8075, 0xA39A90, 0xBEB6AE, 0xDBD6D1, + 0x826D57, 0x828257, 0x6D8257, 0x57826D, + 0x576D82, 0x6D5782, 0x82576D, 0x825757, + ]); + +Base16Theme.add("flat", "Flat", + [ + 0x2C3E50, 0x34495E, 0x7F8C8D, 0x95A5A6, + 0xBDC3C7, 0xe0e0e0, 0xf5f5f5, 0xECF0F1, + 0xE74C3C, 0xE67E22, 0xF1C40F, 0x2ECC71, + 0x1ABC9C, 0x3498DB, 0x9B59B6, 0xbe643c, + ]); + +Base16Theme.add("fruit-soda", "Fruit Soda", + [ + 0xf1ecf1, 0xe0dee0, 0xd8d5d5, 0xb5b4b6, + 0x979598, 0x515151, 0x474545, 0x2d2c2c, + 0xfe3e31, 0xfe6d08, 0xf7e203, 0x47f74c, + 0x0f9cfd, 0x2931df, 0x611fce, 0xb16f40, + ]); + +Base16Theme.add("github", "Github", + [ + 0xffffff, 0xf5f5f5, 0xc8c8fa, 0x969896, + 0xe8e8e8, 0x333333, 0xffffff, 0xffffff, + 0xed6a43, 0x0086b3, 0x795da3, 0x183691, + 0x183691, 0x795da3, 0xa71d5d, 0x333333, + ]); + +Base16Theme.add("google-dark", "Google Dark", + [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, + ]); + +Base16Theme.add("google-light", "Google Light", + [ + 0xffffff, 0xe0e0e0, 0xc5c8c6, 0xb4b7b4, + 0x969896, 0x373b41, 0x282a2e, 0x1d1f21, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, + ]); + +Base16Theme.add("grayscale-dark", "Grayscale Dark", + [ + 0x101010, 0x252525, 0x464646, 0x525252, + 0xababab, 0xb9b9b9, 0xe3e3e3, 0xf7f7f7, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, + ]); + +Base16Theme.add("grayscale-light", "Grayscale Light", + [ + 0xf7f7f7, 0xe3e3e3, 0xb9b9b9, 0xababab, + 0x525252, 0x464646, 0x252525, 0x101010, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, + ]); + +Base16Theme.add("greenscreen", "Green Screen", + [ + 0x001100, 0x003300, 0x005500, 0x007700, + 0x009900, 0x00bb00, 0x00dd00, 0x00ff00, + 0x007700, 0x009900, 0x007700, 0x00bb00, + 0x005500, 0x009900, 0x00bb00, 0x005500, + ]); + +Base16Theme.add("gruvbox-dark-hard", "Gruvbox dark, hard", + [ + 0x1d2021, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-dark-medium", "Gruvbox dark, medium", + [ + 0x282828, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-dark-pale", "Gruvbox dark, pale", + [ + 0x262626, 0x3a3a3a, 0x4e4e4e, 0x8a8a8a, + 0x949494, 0xdab997, 0xd5c4a1, 0xebdbb2, + 0xd75f5f, 0xff8700, 0xffaf00, 0xafaf00, + 0x85ad85, 0x83adad, 0xd485ad, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-dark-soft", "Gruvbox dark, soft", + [ + 0x32302f, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-light-hard", "Gruvbox light, hard", + [ + 0xf9f5d7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-light-medium", "Gruvbox light, medium", + [ + 0xfbf1c7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-light-soft", "Gruvbox light, soft", + [ + 0xf2e5bc, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, + ]); + +Base16Theme.add("harmonic-dark", "Harmonic16 Dark", + [ + 0x0b1c2c, 0x223b54, 0x405c79, 0x627e99, + 0xaabcce, 0xcbd6e2, 0xe5ebf1, 0xf7f9fb, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, + ]); + +Base16Theme.add("harmonic-light", "Harmonic16 Light", + [ + 0xf7f9fb, 0xe5ebf1, 0xcbd6e2, 0xaabcce, + 0x627e99, 0x405c79, 0x223b54, 0x0b1c2c, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, + ]); + +Base16Theme.add("heetch-light", "Heetch Light", + [ + 0xfeffff, 0x392551, 0x7b6d8b, 0x9c92a8, + 0xddd6e5, 0x5a496e, 0x470546, 0x190134, + 0x27d9d5, 0xbdb6c5, 0x5ba2b6, 0xf80059, + 0xc33678, 0x47f9f5, 0xbd0152, 0xdedae2, + ]); + +Base16Theme.add("heetch", "Heetch Dark", + [ + 0x190134, 0x392551, 0x5A496E, 0x7B6D8B, + 0x9C92A8, 0xBDB6C5, 0xDEDAE2, 0xFEFFFF, + 0x27D9D5, 0x5BA2B6, 0x8F6C97, 0xC33678, + 0xF80059, 0xBD0152, 0x82034C, 0x470546, + ]); + +Base16Theme.add("helios", "Helios", + [ + 0x1d2021, 0x383c3e, 0x53585b, 0x6f7579, + 0xcdcdcd, 0xd5d5d5, 0xdddddd, 0xe5e5e5, + 0xd72638, 0xeb8413, 0xf19d1a, 0x88b92d, + 0x1ba595, 0x1e8bac, 0xbe4264, 0xc85e0d, + ]); + +Base16Theme.add("hopscotch", "Hopscotch", + [ + 0x322931, 0x433b42, 0x5c545b, 0x797379, + 0x989498, 0xb9b5b8, 0xd5d3d5, 0xffffff, + 0xdd464c, 0xfd8b19, 0xfdcc59, 0x8fc13e, + 0x149b93, 0x1290bf, 0xc85e7c, 0xb33508, + ]); + +Base16Theme.add("horizon-dark", "Horizon Dark", + [ + 0x1c1e26, 0x232530, 0x2e303e, 0x676a8d, + 0xced1d0, 0xcbced0, 0xdcdfe4, 0xe3e6ee, + 0xe93c58, 0xe58d7d, 0xefb993, 0xefaf8e, + 0x24a8b4, 0xdf5273, 0xb072d1, 0xe4a382, + ]); + +Base16Theme.add("ia-dark", "iA Dark", + [ + 0x1a1a1a, 0x222222, 0x1d414d, 0x767676, + 0xb8b8b8, 0xcccccc, 0xe8e8e8, 0xf8f8f8, + 0xd88568, 0xd86868, 0xb99353, 0x83a471, + 0x7c9cae, 0x8eccdd, 0xb98eb2, 0x8b6c37, + ]); + +Base16Theme.add("ia-light", "iA Light", + [ + 0xf6f6f6, 0xdedede, 0xbde5f2, 0x898989, + 0x767676, 0x181818, 0xe8e8e8, 0xf8f8f8, + 0x9c5a02, 0xc43e18, 0xc48218, 0x38781c, + 0x2d6bb1, 0x48bac2, 0xa94598, 0x8b6c37, + ]); + +Base16Theme.add("icy", "Icy Dark", + [ + 0x021012, 0x031619, 0x041f23, 0x052e34, + 0x064048, 0x095b67, 0x0c7c8c, 0x109cb0, + 0x16c1d9, 0xb3ebf2, 0x80deea, 0x4dd0e1, + 0x26c6da, 0x00bcd4, 0x00acc1, 0x0097a7, + ]); + +Base16Theme.add("irblack", "IR Black", + [ + 0x000000, 0x242422, 0x484844, 0x6c6c66, + 0x918f88, 0xb5b3aa, 0xd9d7cc, 0xfdfbee, + 0xff6c60, 0xe9c062, 0xffffb6, 0xa8ff60, + 0xc6c5fe, 0x96cbfe, 0xff73fd, 0xb18a3d, + ]); + +Base16Theme.add("isotope", "Isotope", + [ + 0x000000, 0x404040, 0x606060, 0x808080, + 0xc0c0c0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xff0000, 0xff9900, 0xff0099, 0x33ff00, + 0x00ffff, 0x0066ff, 0xcc00ff, 0x3300ff, + ]); + +Base16Theme.add("macintosh", "Macintosh", + [ + 0x000000, 0x404040, 0x404040, 0x808080, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0xffffff, + 0xdd0907, 0xff6403, 0xfbf305, 0x1fb714, + 0x02abea, 0x0000d3, 0x4700a5, 0x90713a, + ]); + +Base16Theme.add("marrakesh", "Marrakesh", + [ + 0x201602, 0x302e00, 0x5f5b17, 0x6c6823, + 0x86813b, 0x948e48, 0xccc37a, 0xfaf0a5, + 0xc35359, 0xb36144, 0xa88339, 0x18974e, + 0x75a738, 0x477ca1, 0x8868b3, 0xb3588e, + ]); + +Base16Theme.add("materia", "Materia", + [ + 0x263238, 0x2C393F, 0x37474F, 0x707880, + 0xC9CCD3, 0xCDD3DE, 0xD5DBE5, 0xFFFFFF, + 0xEC5F67, 0xEA9560, 0xFFCC00, 0x8BD649, + 0x80CBC4, 0x89DDFF, 0x82AAFF, 0xEC5F67, + ]); + +Base16Theme.add("material-darker", "Material Darker", + [ + 0x212121, 0x303030, 0x353535, 0x4A4A4A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, + ]); + +Base16Theme.add("material-lighter", "Material Lighter", + [ + 0xFAFAFA, 0xE7EAEC, 0xCCEAE7, 0xCCD7DA, + 0x8796B0, 0x80CBC4, 0x80CBC4, 0xFFFFFF, + 0xFF5370, 0xF76D47, 0xFFB62C, 0x91B859, + 0x39ADB5, 0x6182B8, 0x7C4DFF, 0xE53935, + ]); + +Base16Theme.add("material-palenight", "Material Palenight", + [ + 0x292D3E, 0x444267, 0x32374D, 0x676E95, + 0x8796B0, 0x959DCB, 0x959DCB, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, + ]); + +Base16Theme.add("material-vivid", "Material Vivid", + [ + 0x202124, 0x27292c, 0x323639, 0x44464d, + 0x676c71, 0x80868b, 0x9e9e9e, 0xffffff, + 0xf44336, 0xff9800, 0xffeb3b, 0x00e676, + 0x00bcd4, 0x2196f3, 0x673ab7, 0x8d6e63, + ]); + +Base16Theme.add("material", "Material", + [ + 0x263238, 0x2E3C43, 0x314549, 0x546E7A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, + ]); + +Base16Theme.add("mellow-purple", "Mellow Purple", + [ + 0x1e0528, 0x1A092D, 0x331354, 0x320f55, + 0x873582, 0xffeeff, 0xffeeff, 0xf8c0ff, + 0x00d9e9, 0xaa00a3, 0x955ae7, 0x05cb0d, + 0xb900b1, 0x550068, 0x8991bb, 0x4d6fff, + ]); + +Base16Theme.add("mexico-light", "Mexico Light", + [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf79a0e, 0x538947, + 0x4b8093, 0x7cafc2, 0x96609e, 0xa16946, + ]); + +Base16Theme.add("mocha", "Mocha", + [ + 0x3B3228, 0x534636, 0x645240, 0x7e705a, + 0xb8afad, 0xd0c8c6, 0xe9e1dd, 0xf5eeeb, + 0xcb6077, 0xd28b71, 0xf4bc87, 0xbeb55b, + 0x7bbda4, 0x8ab3b5, 0xa89bb9, 0xbb9584, + ]); + +Base16Theme.add("monokai", "Monokai", + [ + 0x272822, 0x383830, 0x49483e, 0x75715e, + 0xa59f85, 0xf8f8f2, 0xf5f4f1, 0xf9f8f5, + 0xf92672, 0xfd971f, 0xf4bf75, 0xa6e22e, + 0xa1efe4, 0x66d9ef, 0xae81ff, 0xcc6633, + ]); + +Base16Theme.add("nord", "Nord", + [ + 0x2E3440, 0x3B4252, 0x434C5E, 0x4C566A, + 0xD8DEE9, 0xE5E9F0, 0xECEFF4, 0x8FBCBB, + 0x88C0D0, 0x81A1C1, 0x5E81AC, 0xBF616A, + 0xD08770, 0xEBCB8B, 0xA3BE8C, 0xB48EAD, + ]); + +Base16Theme.add("ocean", "Ocean", + [ + 0x2b303b, 0x343d46, 0x4f5b66, 0x65737e, + 0xa7adba, 0xc0c5ce, 0xdfe1e8, 0xeff1f5, + 0xbf616a, 0xd08770, 0xebcb8b, 0xa3be8c, + 0x96b5b4, 0x8fa1b3, 0xb48ead, 0xab7967, + ]); + +Base16Theme.add("oceanicnext", "OceanicNext", + [ + 0x1B2B34, 0x343D46, 0x4F5B66, 0x65737E, + 0xA7ADBA, 0xC0C5CE, 0xCDD3DE, 0xD8DEE9, + 0xEC5F67, 0xF99157, 0xFAC863, 0x99C794, + 0x5FB3B3, 0x6699CC, 0xC594C5, 0xAB7967, + ]); + +Base16Theme.add("one-light", "One Light", + [ + 0xfafafa, 0xf0f0f1, 0xe5e5e6, 0xa0a1a7, + 0x696c77, 0x383a42, 0x202227, 0x090a0b, + 0xca1243, 0xd75f00, 0xc18401, 0x50a14f, + 0x0184bc, 0x4078f2, 0xa626a4, 0x986801, + ]); + +Base16Theme.add("onedark", "OneDark", + [ + 0x282c34, 0x353b45, 0x3e4451, 0x545862, + 0x565c64, 0xabb2bf, 0xb6bdca, 0xc8ccd4, + 0xe06c75, 0xd19a66, 0xe5c07b, 0x98c379, + 0x56b6c2, 0x61afef, 0xc678dd, 0xbe5046, + ]); + +Base16Theme.add("outrun-dark", "Outrun Dark", + [ + 0x00002A, 0x20204A, 0x30305A, 0x50507A, + 0xB0B0DA, 0xD0D0FA, 0xE0E0FF, 0xF5F5FF, + 0xFF4242, 0xFC8D28, 0xF3E877, 0x59F176, + 0x0EF0F0, 0x66B0FF, 0xF10596, 0xF003EF, + ]); + +Base16Theme.add("papercolor-dark", "PaperColor Dark", + [ + 0x1c1c1c, 0xaf005f, 0x5faf00, 0xd7af5f, + 0x5fafd7, 0x808080, 0xd7875f, 0xd0d0d0, + 0x585858, 0x5faf5f, 0xafd700, 0xaf87d7, + 0xffaf00, 0xff5faf, 0x00afaf, 0x5f8787, + ]); + +Base16Theme.add("papercolor-light", "PaperColor Light", + [ + 0xeeeeee, 0xaf0000, 0x008700, 0x5f8700, + 0x0087af, 0x878787, 0x005f87, 0x444444, + 0xbcbcbc, 0xd70000, 0xd70087, 0x8700af, + 0xd75f00, 0xd75f00, 0x005faf, 0x005f87, + ]); + +Base16Theme.add("paraiso", "Paraiso", + [ + 0x2f1e2e, 0x41323f, 0x4f424c, 0x776e71, + 0x8d8687, 0xa39e9b, 0xb9b6b0, 0xe7e9db, + 0xef6155, 0xf99b15, 0xfec418, 0x48b685, + 0x5bc4bf, 0x06b6ef, 0x815ba4, 0xe96ba8, + ]); + +Base16Theme.add("phd", "PhD", + [ + 0x061229, 0x2a3448, 0x4d5666, 0x717885, + 0x9a99a3, 0xb8bbc2, 0xdbdde0, 0xffffff, + 0xd07346, 0xf0a000, 0xfbd461, 0x99bf52, + 0x72b9bf, 0x5299bf, 0x9989cc, 0xb08060, + ]); + +Base16Theme.add("pico", "Pico", + [ + 0x000000, 0x1d2b53, 0x7e2553, 0x008751, + 0xab5236, 0x5f574f, 0xc2c3c7, 0xfff1e8, + 0xff004d, 0xffa300, 0xfff024, 0x00e756, + 0x29adff, 0x83769c, 0xff77a8, 0xffccaa, + ]); + +Base16Theme.add("pop", "Pop", + [ + 0x000000, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xeb008a, 0xf29333, 0xf8ca12, 0x37b349, + 0x00aabb, 0x0e5a94, 0xb31e8d, 0x7a2d00, + ]); + +Base16Theme.add("porple", "Porple", + [ + 0x292c36, 0x333344, 0x474160, 0x65568a, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xf84547, 0xd28e5d, 0xefa16b, 0x95c76f, + 0x64878f, 0x8485ce, 0xb74989, 0x986841, + ]); + +Base16Theme.add("qualia", "Qualia", + [ + 0x101010, 0x454545, 0x454545, 0x454545, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0x454545, + 0xefa6a2, 0xa3b8ef, 0xe6a3dc, 0x80c990, + 0xc8c874, 0x50cacd, 0xe0af85, 0x808080, + ]); + +Base16Theme.add("railscasts", "Railscasts", + [ + 0x2b2b2b, 0x272935, 0x3a4055, 0x5a647e, + 0xd4cfc9, 0xe6e1dc, 0xf4f1ed, 0xf9f7f3, + 0xda4939, 0xcc7833, 0xffc66d, 0xa5c261, + 0x519f50, 0x6d9cbe, 0xb6b3eb, 0xbc9458, + ]); + +Base16Theme.add("rebecca", "Rebecca", + [ + 0x292a44, 0x663399, 0x383a62, 0x666699, + 0xa0a0c5, 0xf1eff8, 0xccccff, 0x53495d, + 0xa0a0c5, 0xefe4a1, 0xae81ff, 0x6dfedf, + 0x8eaee0, 0x2de0a7, 0x7aa5ff, 0xff79c6, + ]); + +Base16Theme.add("seti", "Seti UI", + [ + 0x151718, 0x282a2b, 0x3B758C, 0x41535B, + 0x43a5d5, 0xd6d6d6, 0xeeeeee, 0xffffff, + 0xcd3f45, 0xdb7b55, 0xe6cd69, 0x9fca56, + 0x55dbbe, 0x55b5db, 0xa074c4, 0x8a553f, + ]); + +Base16Theme.add("shapeshifter", "Shapeshifter", + [ + 0xf9f9f9, 0xe0e0e0, 0xababab, 0x555555, + 0x343434, 0x102015, 0x040404, 0x000000, + 0xe92f2f, 0xe09448, 0xdddd13, 0x0ed839, + 0x23edda, 0x3b48e3, 0xf996e2, 0x69542d, + ]); + +Base16Theme.add("snazzy", "Snazzy", + [ + 0x282a36, 0x34353e, 0x43454f, 0x78787e, + 0xa5a5a9, 0xe2e4e5, 0xeff0eb, 0xf1f1f0, + 0xff5c57, 0xff9f43, 0xf3f99d, 0x5af78e, + 0x9aedfe, 0x57c7ff, 0xff6ac1, 0xb2643c, + ]); + +Base16Theme.add("solarflare", "Solar Flare", + [ + 0x18262F, 0x222E38, 0x586875, 0x667581, + 0x85939E, 0xA6AFB8, 0xE8E9ED, 0xF5F7FA, + 0xEF5253, 0xE66B2B, 0xE4B51C, 0x7CC844, + 0x52CBB0, 0x33B5E1, 0xA363D5, 0xD73C9A, + ]); + +Base16Theme.add("solarized-dark", "Solarized Dark", + [ + 0x002b36, 0x073642, 0x586e75, 0x657b83, + 0x839496, 0x93a1a1, 0xeee8d5, 0xfdf6e3, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, + ]); + +Base16Theme.add("solarized-light", "Solarized Light", + [ + 0xfdf6e3, 0xeee8d5, 0x93a1a1, 0x839496, + 0x657b83, 0x586e75, 0x073642, 0x002b36, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, + ]); + +Base16Theme.add("spacemacs", "Spacemacs", + [ + 0x1f2022, 0x282828, 0x444155, 0x585858, + 0xb8b8b8, 0xa3a3a3, 0xe8e8e8, 0xf8f8f8, + 0xf2241f, 0xffa500, 0xb1951d, 0x67b11d, + 0x2d9574, 0x4f97d7, 0xa31db1, 0xb03060, + ]); + +Base16Theme.add("summerfruit-dark", "Summerfruit Dark", + [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xFFFFFF, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, + ]); + +Base16Theme.add("summerfruit-light", "Summerfruit Light", + [ + 0xFFFFFF, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x000000, 0x101010, 0x151515, 0x202020, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, + ]); + +Base16Theme.add("synth-midnight-dark", "Synth Midnight", + [ + 0x040404, 0x141414, 0x242424, 0x61507A, + 0xBFBBBF, 0xDFDBDF, 0xEFEBEF, 0xFFFBFF, + 0xB53B50, 0xE4600E, 0xDAE84D, 0x06EA61, + 0x7CEDE9, 0x03AEFF, 0xEA5CE2, 0x9D4D0E, + ]); + +Base16Theme.add("tomorrow-night-eighties", "Tomorrow Night Eighties", + [ + 0x2d2d2d, 0x393939, 0x515151, 0x999999, + 0xb4b7b4, 0xcccccc, 0xe0e0e0, 0xffffff, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xa3685a, + ]); + +Base16Theme.add("tomorrow-night", "Tomorrow Night", + [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xcc6666, 0xde935f, 0xf0c674, 0xb5bd68, + 0x8abeb7, 0x81a2be, 0xb294bb, 0xa3685a, + ]); + +Base16Theme.add("tomorrow", "Tomorrow", + [ + 0xffffff, 0xe0e0e0, 0xd6d6d6, 0x8e908c, + 0x969896, 0x4d4d4c, 0x282a2e, 0x1d1f21, + 0xc82829, 0xf5871f, 0xeab700, 0x718c00, + 0x3e999f, 0x4271ae, 0x8959a8, 0xa3685a, + ]); + +Base16Theme.add("tube", "London Tube", + [ + 0x231f20, 0x1c3f95, 0x5a5758, 0x737171, + 0x959ca1, 0xd9d8d8, 0xe7e7e8, 0xffffff, + 0xee2e24, 0xf386a1, 0xffd204, 0x00853e, + 0x85cebc, 0x009ddc, 0x98005d, 0xb06110, + ]); + +Base16Theme.add("twilight", "Twilight", + [ + 0x1e1e1e, 0x323537, 0x464b50, 0x5f5a60, + 0x838184, 0xa7a7a7, 0xc3c3c3, 0xffffff, + 0xcf6a4c, 0xcda869, 0xf9ee98, 0x8f9d6a, + 0xafc4db, 0x7587a6, 0x9b859d, 0x9b703f, + ]); + +Base16Theme.add("unikitty-dark", "Unikitty Dark", + [ + 0x2e2a31, 0x4a464d, 0x666369, 0x838085, + 0x9f9da2, 0xbcbabe, 0xd8d7da, 0xf5f4f7, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x796af5, 0xbb60ea, 0xc720ca, + ]); + +Base16Theme.add("unikitty-light", "Unikitty Light", + [ + 0xffffff, 0xe1e1e2, 0xc4c3c5, 0xa7a5a8, + 0x89878b, 0x6c696e, 0x4f4b51, 0x322d34, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x775dff, 0xaa17e6, 0xe013d0, + ]); + +Base16Theme.add("woodland", "Woodland", + [ + 0x231e18, 0x302b25, 0x48413a, 0x9d8b70, + 0xb4a490, 0xcabcb1, 0xd7c8bc, 0xe4d4c8, + 0xd35c5c, 0xca7f32, 0xe0ac16, 0xb7ba53, + 0x6eb958, 0x88a4d3, 0xbb90e2, 0xb49368, + ]); + +Base16Theme.add("xcode-dusk", "XCode Dusk", + [ + 0x282B35, 0x3D4048, 0x53555D, 0x686A71, + 0x7E8086, 0x939599, 0xA9AAAE, 0xBEBFC2, + 0xB21889, 0x786DC5, 0x438288, 0xDF0002, + 0x00A0BE, 0x790EAD, 0xB21889, 0xC77C48, + ]); + +Base16Theme.add("zenburn", "Zenburn", + [ + 0x383838, 0x404040, 0x606060, 0x6f6f6f, + 0x808080, 0xdcdccc, 0xc0c0c0, 0xffffff, + 0xdca3a3, 0xdfaf8f, 0xe0cf9f, 0x5f7f5f, + 0x93e0e3, 0x7cb8bb, 0xdc8cc3, 0x000000, + ]); diff --git a/src/colors/ColorSchemeModel.ts b/src/colors/ColorSchemeModel.ts new file mode 100644 index 0000000..d2fcc11 --- /dev/null +++ b/src/colors/ColorSchemeModel.ts @@ -0,0 +1,147 @@ +import GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; +import Gtk from 'gi://Gtk?version=4.0'; + +import { Base16Theme } from './Base16Theme.js'; + + +export class ColorSchemeModel { + selection: Gtk.SingleSelection; + treeModel: Gtk.TreeListModel; + + constructor() { + let builder = new TreeModelBuilder(); + this.treeModel = builder.buildTreeModel(); + this.selection = Gtk.SingleSelection.new(this.treeModel); + } +} + +class ColorModelNode extends GObject.Object { + static { + GObject.registerClass({ + GTypeName: 'ColorModelNode', + Properties: { + 'text': GObject.ParamSpec.string('text', '', '', GObject.ParamFlags.READWRITE, ''), + + } + }, this); + } + + data: Base16Theme | ColorThemeCategory; + + constructor(data: Base16Theme | ColorThemeCategory) { + super(); + this.data = data; + } + + get text(): string { + return this.data.name; + } + + child_model() { + if (this.data instanceof ColorThemeCategory) { + return this.data.children; + } else { + return null; + } + } + + isThemeWithId(id: string): boolean { + return (this.data instanceof Base16Theme) && + (this.data.id === id); + } + + hasChildThemeWithId(id: string): boolean { + const child_model = this.child_model(); + + if (child_model) { + for (let i = 0; i < child_model.n_items; i++) { + let node = child_model.get_item(i) as ColorModelNode; + if (node.isThemeWithId(id)) { + return true; + } + } + } + return false; + } + + static newScheme(color: Base16Theme) { + return new ColorModelNode(color); + } + + static newCategory(category: ColorThemeCategory) { + return new ColorModelNode(category); + } +} + +class ColorThemeCategory { + name: string; + children: Gio.ListStore; + + constructor(name: string) { + this.name = name; + this.children = new Gio.ListStore(ColorModelNode); + } + + append(color: Base16Theme) { + this.children.append(ColorModelNode.newScheme(color)); + } +} + +class TreeModelBuilder { + categories: [string,string][]; + currentCategory: null | ColorThemeCategory; + storeModel: Gio.ListStore; + + constructor() { + this.categories = Base16Theme.CATEGORIES; + this.currentCategory = null; + this.storeModel = new Gio.ListStore(ColorModelNode); + } + + buildTreeModel() { + Base16Theme.THEMES.forEach(theme => this.addTheme(theme)); + return Gtk.TreeListModel.new( + this.storeModel, + false, + false, + item => (item as ColorModelNode).child_model(), + ); + } + + appendCurrentCategory() { + if (this.currentCategory) { + this.storeModel.append(ColorModelNode.newCategory(this.currentCategory)); + this.currentCategory = null; + this.categories.shift(); + } + } + + matchesNextCategory(theme: Base16Theme): boolean { + if (this.categories.length === 0) { + return false; + } + let [id, _name] = this.categories[0]; + return theme.id === id; + } + + addTheme(theme: Base16Theme) { + if (this.matchesNextCategory(theme)) { + this.addToCategory(theme); + return; + } + if (this.currentCategory) { + this.appendCurrentCategory(); + } + this.storeModel.append(ColorModelNode.newScheme(theme)); + } + + addToCategory(theme: Base16Theme) { + if (!this.currentCategory) { + let [_id, name] = this.categories[0]; + this.currentCategory = new ColorThemeCategory(name); + } + this.currentCategory.append(theme); + } + +} diff --git a/src/com.subgraph.citadel.Realms.js b/src/com.subgraph.citadel.Realms.js new file mode 100644 index 0000000..943101d --- /dev/null +++ b/src/com.subgraph.citadel.Realms.js @@ -0,0 +1,27 @@ +#!@GJS@ -m + +import GLib from 'gi://GLib' + +// Initialize the package +imports.package.init({ + name: '@PACKAGE_NAME@', + version: '@PACKAGE_VERSION@', + prefix: '@PREFIX@', + libdir: '@LIBDIR@', +}); + +// Import the main module and run the main function +const loop = new GLib.MainLoop(null, false); + +// @ts-ignore +import('resource:///com/subgraph/citadel/Realms/js/main.js') + .then((main) => { + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + loop.quit(); + imports.package.run(main); + return GLib.SOURCE_REMOVE; + }); + }) + .catch(logError); + +loop.run(); diff --git a/src/com.subgraph.citadel.Realms.src.gresource.xml b/src/com.subgraph.citadel.Realms.src.gresource.xml new file mode 100644 index 0000000..8ae1cac --- /dev/null +++ b/src/com.subgraph.citadel.Realms.src.gresource.xml @@ -0,0 +1,26 @@ + + + + main.js + model/Base16Themes.js + model/LabelColors.js + model/ObjectManager.js + model/RealmManager.js + model/Realm.js + model/RealmConfig.js + model/OptionData.js + model/RealmFS.js + model/Utils.js + Application.js + ConfigureRealm.js + ColorSchemeChooser.js + ColorSchemeModel.js + + RealmRow.js + RealmsView.js + RealmInfo.js + RealmInfoEntry.js + RealmModel.js + Window.js + + diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..59001b1 --- /dev/null +++ b/src/main.ts @@ -0,0 +1,8 @@ +import 'gi://Gdk?version=4.0' +import 'gi://Gtk?version=4.0' + +import { Application } from './Application.js' + +export function main(argv: string[]) { + return new Application().run(argv); +} diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..b377c79 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,24 @@ +configure_file( + input: APP_ID + '.js', + output: APP_ID, + configuration: { + 'GJS': find_program('gjs').full_path(), + 'PACKAGE_NAME': APP_ID, + 'PACKAGE_VERSION': meson.project_version(), + 'PREFIX': get_option('prefix'), + 'LIBDIR': get_option('prefix') / get_option('libdir') + }, + install_dir: get_option('bindir'), + install_mode: 'rwxr-xr-x' +) + + + +app_resource = gnome.compile_resources( + APP_ID + '.src', + APP_ID + '.src.gresource.xml', + gresource_bundle: true, + source_dir: '../js', + install: true, + install_dir: get_option('datadir') / APP_ID, +) diff --git a/src/model/Base16Themes.ts b/src/model/Base16Themes.ts new file mode 100644 index 0000000..a805916 --- /dev/null +++ b/src/model/Base16Themes.ts @@ -0,0 +1,1081 @@ + + +const TERM_MAP = [ + 0x00, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x05, + 0x03, 0x08, 0x0B, 0x0A, 0x0D, 0x0E, 0x0C, 0x07, + 0x09, 0x0F, 0x01, 0x02, 0x04, 0x06, +]; + +export class Base16Theme { + id: string; + name: string; + colors: number[]; + + constructor(id: string, name: string, colors: number[]) { + this.id = id; + this.name = name; + this.colors = colors; + } + + static THEMES = new Map(); + static THEME_LIST: Base16Theme[] = []; + + static add(id: string, name: string, colors: number[]) { + const theme = new Base16Theme(id, name, colors); + Base16Theme.THEME_LIST.push(theme); + Base16Theme.THEMES.set(id, theme); + } + + static lookup(id: string) { + return Base16Theme.THEMES.get(id); + } + + color(idx: number) { + let hex = this.colors[idx].toString(16).padStart(6, '0'); + return `#${hex}`; + } + + terminal_background() { + return this.color(0); + } + + terminal_foreground() { + return this.color(5); + } + + terminal_palette_color(idx: number) { + return this.color(TERM_MAP[idx]); + } +} + +Base16Theme.add("3024", "3024", + [ + 0x090300, 0x3a3432, 0x4a4543, 0x5c5855, + 0x807d7c, 0xa5a2a2, 0xd6d5d4, 0xf7f7f7, + 0xdb2d20, 0xe8bbd0, 0xfded02, 0x01a252, + 0xb5e4f4, 0x01a0e4, 0xa16a94, 0xcdab53, + ]); + +Base16Theme.add("apathy", 'Apathy', + [ + 0x031A16, 0x0B342D, 0x184E45, 0x2B685E, + 0x5F9C92, 0x81B5AC, 0xA7CEC8, 0xD2E7E4, + 0x3E9688, 0x3E7996, 0x3E4C96, 0x883E96, + 0x963E4C, 0x96883E, 0x4C963E, 0x3E965B, + ]); + +Base16Theme.add("ashes", "Ashes", + [ + 0x1C2023, 0x393F45, 0x565E65, 0x747C84, + 0xADB3BA, 0xC7CCD1, 0xDFE2E5, 0xF3F4F5, + 0xC7AE95, 0xC7C795, 0xAEC795, 0x95C7AE, + 0x95AEC7, 0xAE95C7, 0xC795AE, 0xC79595, + ]); + + +Base16Theme.add("atelier-cave-light", "Atelier Cave Light", + [ + 0xefecf4, 0xe2dfe7, 0x8b8792, 0x7e7887, + 0x655f6d, 0x585260, 0x26232a, 0x19171c, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, + ]); + +Base16Theme.add("atelier-cave", "Atelier Cave", + [ + 0x19171c, 0x26232a, 0x585260, 0x655f6d, + 0x7e7887, 0x8b8792, 0xe2dfe7, 0xefecf4, + 0xbe4678, 0xaa573c, 0xa06e3b, 0x2a9292, + 0x398bc6, 0x576ddb, 0x955ae7, 0xbf40bf, + ]); + +Base16Theme.add("atelier-dune-light", "Atelier Dune Light", + [ + 0xfefbec, 0xe8e4cf, 0xa6a28c, 0x999580, + 0x7d7a68, 0x6e6b5e, 0x292824, 0x20201d, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, + ]); + +Base16Theme.add("atelier-dune", "Atelier Dune", + [ + 0x20201d, 0x292824, 0x6e6b5e, 0x7d7a68, + 0x999580, 0xa6a28c, 0xe8e4cf, 0xfefbec, + 0xd73737, 0xb65611, 0xae9513, 0x60ac39, + 0x1fad83, 0x6684e1, 0xb854d4, 0xd43552, + ]); + +Base16Theme.add("atelier-estuary-light", "Atelier Estuary Light", + [ + 0xf4f3ec, 0xe7e6df, 0x929181, 0x878573, + 0x6c6b5a, 0x5f5e4e, 0x302f27, 0x22221b, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, + ]); + +Base16Theme.add("atelier-estuary", "Atelier Estuary", + [ + 0x22221b, 0x302f27, 0x5f5e4e, 0x6c6b5a, + 0x878573, 0x929181, 0xe7e6df, 0xf4f3ec, + 0xba6236, 0xae7313, 0xa5980d, 0x7d9726, + 0x5b9d48, 0x36a166, 0x5f9182, 0x9d6c7c, + ]); + +Base16Theme.add("atelier-forest-light", "Atelier Forest Light", + [ + 0xf1efee, 0xe6e2e0, 0xa8a19f, 0x9c9491, + 0x766e6b, 0x68615e, 0x2c2421, 0x1b1918, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, + ]); + +Base16Theme.add("atelier-forest", "Atelier Forest", + [ + 0x1b1918, 0x2c2421, 0x68615e, 0x766e6b, + 0x9c9491, 0xa8a19f, 0xe6e2e0, 0xf1efee, + 0xf22c40, 0xdf5320, 0xc38418, 0x7b9726, + 0x3d97b8, 0x407ee7, 0x6666ea, 0xc33ff3, + ]); + +Base16Theme.add("atelier-heath-light", "Atelier Heath Light", + [ + 0xf7f3f7, 0xd8cad8, 0xab9bab, 0x9e8f9e, + 0x776977, 0x695d69, 0x292329, 0x1b181b, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, + ]); + +Base16Theme.add("atelier-heath", "Atelier Heath", + [ + 0x1b181b, 0x292329, 0x695d69, 0x776977, + 0x9e8f9e, 0xab9bab, 0xd8cad8, 0xf7f3f7, + 0xca402b, 0xa65926, 0xbb8a35, 0x918b3b, + 0x159393, 0x516aec, 0x7b59c0, 0xcc33cc, + ]); + +Base16Theme.add("atelier-lakeside-light", "Atelier Lakeside Light", + [ + 0xebf8ff, 0xc1e4f6, 0x7ea2b4, 0x7195a8, + 0x5a7b8c, 0x516d7b, 0x1f292e, 0x161b1d, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, + ]); + +Base16Theme.add("atelier-lakeside", "Atelier Lakeside", + [ + 0x161b1d, 0x1f292e, 0x516d7b, 0x5a7b8c, + 0x7195a8, 0x7ea2b4, 0xc1e4f6, 0xebf8ff, + 0xd22d72, 0x935c25, 0x8a8a0f, 0x568c3b, + 0x2d8f6f, 0x257fad, 0x6b6bb8, 0xb72dd2, + ]); + +Base16Theme.add("atelier-plateau-light", "Atelier Plateau Light", + [ + 0xf4ecec, 0xe7dfdf, 0x8a8585, 0x7e7777, + 0x655d5d, 0x585050, 0x292424, 0x1b1818, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, + ]); + +Base16Theme.add("atelier-plateau", "Atelier Plateau", + [ + 0x1b1818, 0x292424, 0x585050, 0x655d5d, + 0x7e7777, 0x8a8585, 0xe7dfdf, 0xf4ecec, + 0xca4949, 0xb45a3c, 0xa06e3b, 0x4b8b8b, + 0x5485b6, 0x7272ca, 0x8464c4, 0xbd5187, + ]); + +Base16Theme.add("atelier-savanna-light", "Atelier Savanna Light", + [ + 0xecf4ee, 0xdfe7e2, 0x87928a, 0x78877d, + 0x5f6d64, 0x526057, 0x232a25, 0x171c19, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, + ]); + +Base16Theme.add("atelier-savanna", "Atelier Savanna", + [ + 0x171c19, 0x232a25, 0x526057, 0x5f6d64, + 0x78877d, 0x87928a, 0xdfe7e2, 0xecf4ee, + 0xb16139, 0x9f713c, 0xa07e3b, 0x489963, + 0x1c9aa0, 0x478c90, 0x55859b, 0x867469, + ]); + +Base16Theme.add("atelier-seaside-light", "Atelier Seaside Light", + [ + 0xf4fbf4, 0xcfe8cf, 0x8ca68c, 0x809980, + 0x687d68, 0x5e6e5e, 0x242924, 0x131513, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, + ]); + +Base16Theme.add("atelier-seaside", "Atelier Seaside", + [ + 0x131513, 0x242924, 0x5e6e5e, 0x687d68, + 0x809980, 0x8ca68c, 0xcfe8cf, 0xf4fbf4, + 0xe6193c, 0x87711d, 0x98981b, 0x29a329, + 0x1999b3, 0x3d62f5, 0xad2bee, 0xe619c3, + ]); + +Base16Theme.add("atelier-sulphurpool-light", "Atelier Sulphurpool Light", + [ + 0xf5f7ff, 0xdfe2f1, 0x979db4, 0x898ea4, + 0x6b7394, 0x5e6687, 0x293256, 0x202746, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, + ]); + +Base16Theme.add("atelier-sulphurpool", "Atelier Sulphurpool", + [ + 0x202746, 0x293256, 0x5e6687, 0x6b7394, + 0x898ea4, 0x979db4, 0xdfe2f1, 0xf5f7ff, + 0xc94922, 0xc76b29, 0xc08b30, 0xac9739, + 0x22a2c9, 0x3d8fd1, 0x6679cc, 0x9c637a, + ]); + +Base16Theme.add("atlas", "Atlas", + [ + 0x002635, 0x00384d, 0x517F8D, 0x6C8B91, + 0x869696, 0xa1a19a, 0xe6e6dc, 0xfafaf8, + 0xff5a67, 0xf08e48, 0xffcc1b, 0x7fc06e, + 0x14747e, 0x5dd7b9, 0x9a70a4, 0xc43060, + ]); + +Base16Theme.add("bespin", "Bespin", + [ + 0x28211c, 0x36312e, 0x5e5d5c, 0x666666, + 0x797977, 0x8a8986, 0x9d9b97, 0xbaae9e, + 0xcf6a4c, 0xcf7d34, 0xf9ee98, 0x54be0d, + 0xafc4db, 0x5ea6ea, 0x9b859d, 0x937121, + ]); + +Base16Theme.add("black-metal-bathory", "Black Metal (Bathory)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xe78a53, 0xfbcb97, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-burzum", "Black Metal (Burzum)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x99bbaa, 0xddeecc, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-dark-funeral", "Black Metal (Dark Funeral)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x5f81a5, 0xd0dfee, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-gorgoroth", "Black Metal (Gorgoroth)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x8c7f70, 0x9b8d7f, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-immortal", "Black Metal (Immortal)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x556677, 0x7799bb, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-khold", "Black Metal (Khold)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x974b46, 0xeceee3, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-marduk", "Black Metal (Marduk)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x626b67, 0xa5aaa7, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-mayhem", "Black Metal (Mayhem)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xeecc6c, 0xf3ecd4, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-nile", "Black Metal (Nile)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x777755, 0xaa9988, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal-venom", "Black Metal (Venom)", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0x79241f, 0xf8f7f2, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("black-metal", "Black Metal", + [ + 0x000000, 0x121212, 0x222222, 0x333333, + 0x999999, 0xc1c1c1, 0x999999, 0xc1c1c1, + 0x5f8787, 0xaaaaaa, 0xa06666, 0xdd9999, + 0xaaaaaa, 0x888888, 0x999999, 0x444444, + ]); + +Base16Theme.add("brewer", "Brewer", + [ + 0x0c0d0e, 0x2e2f30, 0x515253, 0x737475, + 0x959697, 0xb7b8b9, 0xdadbdc, 0xfcfdfe, + 0xe31a1c, 0xe6550d, 0xdca060, 0x31a354, + 0x80b1d3, 0x3182bd, 0x756bb1, 0xb15928, + ]); + +Base16Theme.add("bright", "Bright", + [ + 0x000000, 0x303030, 0x505050, 0xb0b0b0, + 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, 0xffffff, + 0xfb0120, 0xfc6d24, 0xfda331, 0xa1c659, + 0x76c7b7, 0x6fb3d2, 0xd381c3, 0xbe643c, + ]); + +Base16Theme.add("brogrammer", "Brogrammer", + [ + 0x1f1f1f, 0xf81118, 0x2dc55e, 0xecba0f, + 0x2a84d2, 0x4e5ab7, 0x1081d6, 0xd6dbe5, + 0xd6dbe5, 0xde352e, 0x1dd361, 0xf3bd09, + 0x1081d6, 0x5350b9, 0x0f7ddb, 0xffffff, + ]); + +Base16Theme.add("brushtrees-dark", "Brush Trees Dark", + [ + 0x485867, 0x5A6D7A, 0x6D828E, 0x8299A1, + 0x98AFB5, 0xB0C5C8, 0xC9DBDC, 0xE3EFEF, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, + ]); + +Base16Theme.add("brushtrees", "Brush Trees", + [ + 0xE3EFEF, 0xC9DBDC, 0xB0C5C8, 0x98AFB5, + 0x8299A1, 0x6D828E, 0x5A6D7A, 0x485867, + 0xb38686, 0xd8bba2, 0xaab386, 0x87b386, + 0x86b3b3, 0x868cb3, 0xb386b2, 0xb39f9f, + ]); + +Base16Theme.add("chalk", "Chalk", + [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xf5f5f5, + 0xfb9fb1, 0xeda987, 0xddb26f, 0xacc267, + 0x12cfc0, 0x6fc2ef, 0xe1a3ee, 0xdeaf8f, + ]); + +Base16Theme.add("circus", "Circus", + [ + 0x191919, 0x202020, 0x303030, 0x5f5a60, + 0x505050, 0xa7a7a7, 0x808080, 0xffffff, + 0xdc657d, 0x4bb1a7, 0xc3ba63, 0x84b97c, + 0x4bb1a7, 0x639ee4, 0xb888e2, 0xb888e2, + ]); + +Base16Theme.add("classic-dark", "Classic Dark", + [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xF5F5F5, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, + ]); + +Base16Theme.add("classic-light", "Classic Light", + [ + 0xF5F5F5, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x505050, 0x303030, 0x202020, 0x151515, + 0xAC4142, 0xD28445, 0xF4BF75, 0x90A959, + 0x75B5AA, 0x6A9FB5, 0xAA759F, 0x8F5536, + ]); + +Base16Theme.add("codeschool", "Codeschool", + [ + 0x232c31, 0x1c3657, 0x2a343a, 0x3f4944, + 0x84898c, 0x9ea7a6, 0xa7cfa3, 0xb5d8f6, + 0x2a5491, 0x43820d, 0xa03b1e, 0x237986, + 0xb02f30, 0x484d79, 0xc59820, 0xc98344, + ]); + +Base16Theme.add("cupcake", "Cupcake", + [ + 0xfbf1f2, 0xf2f1f4, 0xd8d5dd, 0xbfb9c6, + 0xa59daf, 0x8b8198, 0x72677E, 0x585062, + 0xD57E85, 0xEBB790, 0xDCB16C, 0xA3B367, + 0x69A9A7, 0x7297B9, 0xBB99B4, 0xBAA58C, + ]); + +Base16Theme.add("cupertino", "Cupertino", + [ + 0xffffff, 0xc0c0c0, 0xc0c0c0, 0x808080, + 0x808080, 0x404040, 0x404040, 0x5e5e5e, + 0xc41a15, 0xeb8500, 0x826b28, 0x007400, + 0x318495, 0x0000ff, 0xa90d91, 0x826b28, + ]); + +Base16Theme.add("darktooth", "Darktooth", + [ + 0x1D2021, 0x32302F, 0x504945, 0x665C54, + 0x928374, 0xA89984, 0xD5C4A1, 0xFDF4C1, + 0xFB543F, 0xFE8625, 0xFAC03B, 0x95C085, + 0x8BA59B, 0x0D6678, 0x8F4673, 0xA87322, + ]); + +Base16Theme.add("default-dark", "Default Dark", + [ + 0x181818, 0x282828, 0x383838, 0x585858, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, + ]); + +Base16Theme.add("default-light", "Default Light", + [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf7ca88, 0xa1b56c, + 0x86c1b9, 0x7cafc2, 0xba8baf, 0xa16946, + ]); + +Base16Theme.add("dracula", "Dracula", + [ + 0x282936, 0x3a3c4e, 0x4d4f68, 0x626483, + 0x62d6e8, 0xe9e9f4, 0xf1f2f8, 0xf7f7fb, + 0xea51b2, 0xb45bcf, 0x00f769, 0xebff87, + 0xa1efe4, 0x62d6e8, 0xb45bcf, 0x00f769, + ]); + +Base16Theme.add("eighties", "Eighties", + [ + 0x2d2d2d, 0x393939, 0x515151, 0x747369, + 0xa09f93, 0xd3d0c8, 0xe8e6df, 0xf2f0ec, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xd27b53, + ]); + +Base16Theme.add("embers", "Embers", + [ + 0x16130F, 0x2C2620, 0x433B32, 0x5A5047, + 0x8A8075, 0xA39A90, 0xBEB6AE, 0xDBD6D1, + 0x826D57, 0x828257, 0x6D8257, 0x57826D, + 0x576D82, 0x6D5782, 0x82576D, 0x825757, + ]); + +Base16Theme.add("flat", "Flat", + [ + 0x2C3E50, 0x34495E, 0x7F8C8D, 0x95A5A6, + 0xBDC3C7, 0xe0e0e0, 0xf5f5f5, 0xECF0F1, + 0xE74C3C, 0xE67E22, 0xF1C40F, 0x2ECC71, + 0x1ABC9C, 0x3498DB, 0x9B59B6, 0xbe643c, + ]); + +Base16Theme.add("fruit-soda", "Fruit Soda", + [ + 0xf1ecf1, 0xe0dee0, 0xd8d5d5, 0xb5b4b6, + 0x979598, 0x515151, 0x474545, 0x2d2c2c, + 0xfe3e31, 0xfe6d08, 0xf7e203, 0x47f74c, + 0x0f9cfd, 0x2931df, 0x611fce, 0xb16f40, + ]); + +Base16Theme.add("github", "Github", + [ + 0xffffff, 0xf5f5f5, 0xc8c8fa, 0x969896, + 0xe8e8e8, 0x333333, 0xffffff, 0xffffff, + 0xed6a43, 0x0086b3, 0x795da3, 0x183691, + 0x183691, 0x795da3, 0xa71d5d, 0x333333, + ]); + +Base16Theme.add("google-dark", "Google Dark", + [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, + ]); + +Base16Theme.add("google-light", "Google Light", + [ + 0xffffff, 0xe0e0e0, 0xc5c8c6, 0xb4b7b4, + 0x969896, 0x373b41, 0x282a2e, 0x1d1f21, + 0xCC342B, 0xF96A38, 0xFBA922, 0x198844, + 0x3971ED, 0x3971ED, 0xA36AC7, 0x3971ED, + ]); + +Base16Theme.add("grayscale-dark", "Grayscale Dark", + [ + 0x101010, 0x252525, 0x464646, 0x525252, + 0xababab, 0xb9b9b9, 0xe3e3e3, 0xf7f7f7, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, + ]); + +Base16Theme.add("grayscale-light", "Grayscale Light", + [ + 0xf7f7f7, 0xe3e3e3, 0xb9b9b9, 0xababab, + 0x525252, 0x464646, 0x252525, 0x101010, + 0x7c7c7c, 0x999999, 0xa0a0a0, 0x8e8e8e, + 0x868686, 0x686868, 0x747474, 0x5e5e5e, + ]); + +Base16Theme.add("greenscreen", "Green Screen", + [ + 0x001100, 0x003300, 0x005500, 0x007700, + 0x009900, 0x00bb00, 0x00dd00, 0x00ff00, + 0x007700, 0x009900, 0x007700, 0x00bb00, + 0x005500, 0x009900, 0x00bb00, 0x005500, + ]); + +Base16Theme.add("gruvbox-dark-hard", "Gruvbox dark, hard", + [ + 0x1d2021, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-dark-medium", "Gruvbox dark, medium", + [ + 0x282828, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-dark-pale", "Gruvbox dark, pale", + [ + 0x262626, 0x3a3a3a, 0x4e4e4e, 0x8a8a8a, + 0x949494, 0xdab997, 0xd5c4a1, 0xebdbb2, + 0xd75f5f, 0xff8700, 0xffaf00, 0xafaf00, + 0x85ad85, 0x83adad, 0xd485ad, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-dark-soft", "Gruvbox dark, soft", + [ + 0x32302f, 0x3c3836, 0x504945, 0x665c54, + 0xbdae93, 0xd5c4a1, 0xebdbb2, 0xfbf1c7, + 0xfb4934, 0xfe8019, 0xfabd2f, 0xb8bb26, + 0x8ec07c, 0x83a598, 0xd3869b, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-light-hard", "Gruvbox light, hard", + [ + 0xf9f5d7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-light-medium", "Gruvbox light, medium", + [ + 0xfbf1c7, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, + ]); + +Base16Theme.add("gruvbox-light-soft", "Gruvbox light, soft", + [ + 0xf2e5bc, 0xebdbb2, 0xd5c4a1, 0xbdae93, + 0x665c54, 0x504945, 0x3c3836, 0x282828, + 0x9d0006, 0xaf3a03, 0xb57614, 0x79740e, + 0x427b58, 0x076678, 0x8f3f71, 0xd65d0e, + ]); + +Base16Theme.add("harmonic-dark", "Harmonic16 Dark", + [ + 0x0b1c2c, 0x223b54, 0x405c79, 0x627e99, + 0xaabcce, 0xcbd6e2, 0xe5ebf1, 0xf7f9fb, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, + ]); + +Base16Theme.add("harmonic-light", "Harmonic16 Light", + [ + 0xf7f9fb, 0xe5ebf1, 0xcbd6e2, 0xaabcce, + 0x627e99, 0x405c79, 0x223b54, 0x0b1c2c, + 0xbf8b56, 0xbfbf56, 0x8bbf56, 0x56bf8b, + 0x568bbf, 0x8b56bf, 0xbf568b, 0xbf5656, + ]); + +Base16Theme.add("heetch-light", "Heetch Light", + [ + 0xfeffff, 0x392551, 0x7b6d8b, 0x9c92a8, + 0xddd6e5, 0x5a496e, 0x470546, 0x190134, + 0x27d9d5, 0xbdb6c5, 0x5ba2b6, 0xf80059, + 0xc33678, 0x47f9f5, 0xbd0152, 0xdedae2, + ]); + +Base16Theme.add("heetch", "Heetch Dark", + [ + 0x190134, 0x392551, 0x5A496E, 0x7B6D8B, + 0x9C92A8, 0xBDB6C5, 0xDEDAE2, 0xFEFFFF, + 0x27D9D5, 0x5BA2B6, 0x8F6C97, 0xC33678, + 0xF80059, 0xBD0152, 0x82034C, 0x470546, + ]); + +Base16Theme.add("helios", "Helios", + [ + 0x1d2021, 0x383c3e, 0x53585b, 0x6f7579, + 0xcdcdcd, 0xd5d5d5, 0xdddddd, 0xe5e5e5, + 0xd72638, 0xeb8413, 0xf19d1a, 0x88b92d, + 0x1ba595, 0x1e8bac, 0xbe4264, 0xc85e0d, + ]); + +Base16Theme.add("hopscotch", "Hopscotch", + [ + 0x322931, 0x433b42, 0x5c545b, 0x797379, + 0x989498, 0xb9b5b8, 0xd5d3d5, 0xffffff, + 0xdd464c, 0xfd8b19, 0xfdcc59, 0x8fc13e, + 0x149b93, 0x1290bf, 0xc85e7c, 0xb33508, + ]); + +Base16Theme.add("horizon-dark", "Horizon Dark", + [ + 0x1c1e26, 0x232530, 0x2e303e, 0x676a8d, + 0xced1d0, 0xcbced0, 0xdcdfe4, 0xe3e6ee, + 0xe93c58, 0xe58d7d, 0xefb993, 0xefaf8e, + 0x24a8b4, 0xdf5273, 0xb072d1, 0xe4a382, + ]); + +Base16Theme.add("ia-dark", "iA Dark", + [ + 0x1a1a1a, 0x222222, 0x1d414d, 0x767676, + 0xb8b8b8, 0xcccccc, 0xe8e8e8, 0xf8f8f8, + 0xd88568, 0xd86868, 0xb99353, 0x83a471, + 0x7c9cae, 0x8eccdd, 0xb98eb2, 0x8b6c37, + ]); + +Base16Theme.add("ia-light", "iA Light", + [ + 0xf6f6f6, 0xdedede, 0xbde5f2, 0x898989, + 0x767676, 0x181818, 0xe8e8e8, 0xf8f8f8, + 0x9c5a02, 0xc43e18, 0xc48218, 0x38781c, + 0x2d6bb1, 0x48bac2, 0xa94598, 0x8b6c37, + ]); + +Base16Theme.add("icy", "Icy Dark", + [ + 0x021012, 0x031619, 0x041f23, 0x052e34, + 0x064048, 0x095b67, 0x0c7c8c, 0x109cb0, + 0x16c1d9, 0xb3ebf2, 0x80deea, 0x4dd0e1, + 0x26c6da, 0x00bcd4, 0x00acc1, 0x0097a7, + ]); + +Base16Theme.add("irblack", "IR Black", + [ + 0x000000, 0x242422, 0x484844, 0x6c6c66, + 0x918f88, 0xb5b3aa, 0xd9d7cc, 0xfdfbee, + 0xff6c60, 0xe9c062, 0xffffb6, 0xa8ff60, + 0xc6c5fe, 0x96cbfe, 0xff73fd, 0xb18a3d, + ]); + +Base16Theme.add("isotope", "Isotope", + [ + 0x000000, 0x404040, 0x606060, 0x808080, + 0xc0c0c0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xff0000, 0xff9900, 0xff0099, 0x33ff00, + 0x00ffff, 0x0066ff, 0xcc00ff, 0x3300ff, + ]); + +Base16Theme.add("macintosh", "Macintosh", + [ + 0x000000, 0x404040, 0x404040, 0x808080, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0xffffff, + 0xdd0907, 0xff6403, 0xfbf305, 0x1fb714, + 0x02abea, 0x0000d3, 0x4700a5, 0x90713a, + ]); + +Base16Theme.add("marrakesh", "Marrakesh", + [ + 0x201602, 0x302e00, 0x5f5b17, 0x6c6823, + 0x86813b, 0x948e48, 0xccc37a, 0xfaf0a5, + 0xc35359, 0xb36144, 0xa88339, 0x18974e, + 0x75a738, 0x477ca1, 0x8868b3, 0xb3588e, + ]); + +Base16Theme.add("materia", "Materia", + [ + 0x263238, 0x2C393F, 0x37474F, 0x707880, + 0xC9CCD3, 0xCDD3DE, 0xD5DBE5, 0xFFFFFF, + 0xEC5F67, 0xEA9560, 0xFFCC00, 0x8BD649, + 0x80CBC4, 0x89DDFF, 0x82AAFF, 0xEC5F67, + ]); + +Base16Theme.add("material-darker", "Material Darker", + [ + 0x212121, 0x303030, 0x353535, 0x4A4A4A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, + ]); + +Base16Theme.add("material-lighter", "Material Lighter", + [ + 0xFAFAFA, 0xE7EAEC, 0xCCEAE7, 0xCCD7DA, + 0x8796B0, 0x80CBC4, 0x80CBC4, 0xFFFFFF, + 0xFF5370, 0xF76D47, 0xFFB62C, 0x91B859, + 0x39ADB5, 0x6182B8, 0x7C4DFF, 0xE53935, + ]); + +Base16Theme.add("material-palenight", "Material Palenight", + [ + 0x292D3E, 0x444267, 0x32374D, 0x676E95, + 0x8796B0, 0x959DCB, 0x959DCB, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, + ]); + +Base16Theme.add("material-vivid", "Material Vivid", + [ + 0x202124, 0x27292c, 0x323639, 0x44464d, + 0x676c71, 0x80868b, 0x9e9e9e, 0xffffff, + 0xf44336, 0xff9800, 0xffeb3b, 0x00e676, + 0x00bcd4, 0x2196f3, 0x673ab7, 0x8d6e63, + ]); + +Base16Theme.add("material", "Material", + [ + 0x263238, 0x2E3C43, 0x314549, 0x546E7A, + 0xB2CCD6, 0xEEFFFF, 0xEEFFFF, 0xFFFFFF, + 0xF07178, 0xF78C6C, 0xFFCB6B, 0xC3E88D, + 0x89DDFF, 0x82AAFF, 0xC792EA, 0xFF5370, + ]); + +Base16Theme.add("mellow-purple", "Mellow Purple", + [ + 0x1e0528, 0x1A092D, 0x331354, 0x320f55, + 0x873582, 0xffeeff, 0xffeeff, 0xf8c0ff, + 0x00d9e9, 0xaa00a3, 0x955ae7, 0x05cb0d, + 0xb900b1, 0x550068, 0x8991bb, 0x4d6fff, + ]); + +Base16Theme.add("mexico-light", "Mexico Light", + [ + 0xf8f8f8, 0xe8e8e8, 0xd8d8d8, 0xb8b8b8, + 0x585858, 0x383838, 0x282828, 0x181818, + 0xab4642, 0xdc9656, 0xf79a0e, 0x538947, + 0x4b8093, 0x7cafc2, 0x96609e, 0xa16946, + ]); + +Base16Theme.add("mocha", "Mocha", + [ + 0x3B3228, 0x534636, 0x645240, 0x7e705a, + 0xb8afad, 0xd0c8c6, 0xe9e1dd, 0xf5eeeb, + 0xcb6077, 0xd28b71, 0xf4bc87, 0xbeb55b, + 0x7bbda4, 0x8ab3b5, 0xa89bb9, 0xbb9584, + ]); + +Base16Theme.add("monokai", "Monokai", + [ + 0x272822, 0x383830, 0x49483e, 0x75715e, + 0xa59f85, 0xf8f8f2, 0xf5f4f1, 0xf9f8f5, + 0xf92672, 0xfd971f, 0xf4bf75, 0xa6e22e, + 0xa1efe4, 0x66d9ef, 0xae81ff, 0xcc6633, + ]); + +Base16Theme.add("nord", "Nord", + [ + 0x2E3440, 0x3B4252, 0x434C5E, 0x4C566A, + 0xD8DEE9, 0xE5E9F0, 0xECEFF4, 0x8FBCBB, + 0x88C0D0, 0x81A1C1, 0x5E81AC, 0xBF616A, + 0xD08770, 0xEBCB8B, 0xA3BE8C, 0xB48EAD, + ]); + +Base16Theme.add("ocean", "Ocean", + [ + 0x2b303b, 0x343d46, 0x4f5b66, 0x65737e, + 0xa7adba, 0xc0c5ce, 0xdfe1e8, 0xeff1f5, + 0xbf616a, 0xd08770, 0xebcb8b, 0xa3be8c, + 0x96b5b4, 0x8fa1b3, 0xb48ead, 0xab7967, + ]); + +Base16Theme.add("oceanicnext", "OceanicNext", + [ + 0x1B2B34, 0x343D46, 0x4F5B66, 0x65737E, + 0xA7ADBA, 0xC0C5CE, 0xCDD3DE, 0xD8DEE9, + 0xEC5F67, 0xF99157, 0xFAC863, 0x99C794, + 0x5FB3B3, 0x6699CC, 0xC594C5, 0xAB7967, + ]); + +Base16Theme.add("one-light", "One Light", + [ + 0xfafafa, 0xf0f0f1, 0xe5e5e6, 0xa0a1a7, + 0x696c77, 0x383a42, 0x202227, 0x090a0b, + 0xca1243, 0xd75f00, 0xc18401, 0x50a14f, + 0x0184bc, 0x4078f2, 0xa626a4, 0x986801, + ]); + +Base16Theme.add("onedark", "OneDark", + [ + 0x282c34, 0x353b45, 0x3e4451, 0x545862, + 0x565c64, 0xabb2bf, 0xb6bdca, 0xc8ccd4, + 0xe06c75, 0xd19a66, 0xe5c07b, 0x98c379, + 0x56b6c2, 0x61afef, 0xc678dd, 0xbe5046, + ]); + +Base16Theme.add("outrun-dark", "Outrun Dark", + [ + 0x00002A, 0x20204A, 0x30305A, 0x50507A, + 0xB0B0DA, 0xD0D0FA, 0xE0E0FF, 0xF5F5FF, + 0xFF4242, 0xFC8D28, 0xF3E877, 0x59F176, + 0x0EF0F0, 0x66B0FF, 0xF10596, 0xF003EF, + ]); + +Base16Theme.add("papercolor-dark", "PaperColor Dark", + [ + 0x1c1c1c, 0xaf005f, 0x5faf00, 0xd7af5f, + 0x5fafd7, 0x808080, 0xd7875f, 0xd0d0d0, + 0x585858, 0x5faf5f, 0xafd700, 0xaf87d7, + 0xffaf00, 0xff5faf, 0x00afaf, 0x5f8787, + ]); + +Base16Theme.add("papercolor-light", "PaperColor Light", + [ + 0xeeeeee, 0xaf0000, 0x008700, 0x5f8700, + 0x0087af, 0x878787, 0x005f87, 0x444444, + 0xbcbcbc, 0xd70000, 0xd70087, 0x8700af, + 0xd75f00, 0xd75f00, 0x005faf, 0x005f87, + ]); + +Base16Theme.add("paraiso", "Paraiso", + [ + 0x2f1e2e, 0x41323f, 0x4f424c, 0x776e71, + 0x8d8687, 0xa39e9b, 0xb9b6b0, 0xe7e9db, + 0xef6155, 0xf99b15, 0xfec418, 0x48b685, + 0x5bc4bf, 0x06b6ef, 0x815ba4, 0xe96ba8, + ]); + +Base16Theme.add("phd", "PhD", + [ + 0x061229, 0x2a3448, 0x4d5666, 0x717885, + 0x9a99a3, 0xb8bbc2, 0xdbdde0, 0xffffff, + 0xd07346, 0xf0a000, 0xfbd461, 0x99bf52, + 0x72b9bf, 0x5299bf, 0x9989cc, 0xb08060, + ]); + +Base16Theme.add("pico", "Pico", + [ + 0x000000, 0x1d2b53, 0x7e2553, 0x008751, + 0xab5236, 0x5f574f, 0xc2c3c7, 0xfff1e8, + 0xff004d, 0xffa300, 0xfff024, 0x00e756, + 0x29adff, 0x83769c, 0xff77a8, 0xffccaa, + ]); + +Base16Theme.add("pop", "Pop", + [ + 0x000000, 0x202020, 0x303030, 0x505050, + 0xb0b0b0, 0xd0d0d0, 0xe0e0e0, 0xffffff, + 0xeb008a, 0xf29333, 0xf8ca12, 0x37b349, + 0x00aabb, 0x0e5a94, 0xb31e8d, 0x7a2d00, + ]); + +Base16Theme.add("porple", "Porple", + [ + 0x292c36, 0x333344, 0x474160, 0x65568a, + 0xb8b8b8, 0xd8d8d8, 0xe8e8e8, 0xf8f8f8, + 0xf84547, 0xd28e5d, 0xefa16b, 0x95c76f, + 0x64878f, 0x8485ce, 0xb74989, 0x986841, + ]); +Base16Theme.add("qualia", "Qualia", + [ + 0x101010, 0x454545, 0x454545, 0x454545, + 0x808080, 0xc0c0c0, 0xc0c0c0, 0x454545, + 0xefa6a2, 0xa3b8ef, 0xe6a3dc, 0x80c990, + 0xc8c874, 0x50cacd, 0xe0af85, 0x808080, + ]); + +Base16Theme.add("railscasts", "Railscasts", + [ + 0x2b2b2b, 0x272935, 0x3a4055, 0x5a647e, + 0xd4cfc9, 0xe6e1dc, 0xf4f1ed, 0xf9f7f3, + 0xda4939, 0xcc7833, 0xffc66d, 0xa5c261, + 0x519f50, 0x6d9cbe, 0xb6b3eb, 0xbc9458, + ]); + +Base16Theme.add("rebecca", "Rebecca", + [ + 0x292a44, 0x663399, 0x383a62, 0x666699, + 0xa0a0c5, 0xf1eff8, 0xccccff, 0x53495d, + 0xa0a0c5, 0xefe4a1, 0xae81ff, 0x6dfedf, + 0x8eaee0, 0x2de0a7, 0x7aa5ff, 0xff79c6, + ]); + +Base16Theme.add("seti", "Seti UI", + [ + 0x151718, 0x282a2b, 0x3B758C, 0x41535B, + 0x43a5d5, 0xd6d6d6, 0xeeeeee, 0xffffff, + 0xcd3f45, 0xdb7b55, 0xe6cd69, 0x9fca56, + 0x55dbbe, 0x55b5db, 0xa074c4, 0x8a553f, + ]); + +Base16Theme.add("shapeshifter", "Shapeshifter", + [ + 0xf9f9f9, 0xe0e0e0, 0xababab, 0x555555, + 0x343434, 0x102015, 0x040404, 0x000000, + 0xe92f2f, 0xe09448, 0xdddd13, 0x0ed839, + 0x23edda, 0x3b48e3, 0xf996e2, 0x69542d, + ]); + +Base16Theme.add("snazzy", "Snazzy", + [ + 0x282a36, 0x34353e, 0x43454f, 0x78787e, + 0xa5a5a9, 0xe2e4e5, 0xeff0eb, 0xf1f1f0, + 0xff5c57, 0xff9f43, 0xf3f99d, 0x5af78e, + 0x9aedfe, 0x57c7ff, 0xff6ac1, 0xb2643c, + ]); + +Base16Theme.add("solarflare", "Solar Flare", + [ + 0x18262F, 0x222E38, 0x586875, 0x667581, + 0x85939E, 0xA6AFB8, 0xE8E9ED, 0xF5F7FA, + 0xEF5253, 0xE66B2B, 0xE4B51C, 0x7CC844, + 0x52CBB0, 0x33B5E1, 0xA363D5, 0xD73C9A, + ]); + +Base16Theme.add("solarized-dark", "Solarized Dark", + [ + 0x002b36, 0x073642, 0x586e75, 0x657b83, + 0x839496, 0x93a1a1, 0xeee8d5, 0xfdf6e3, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, + ]); + +Base16Theme.add("solarized-light", "Solarized Light", + [ + 0xfdf6e3, 0xeee8d5, 0x93a1a1, 0x839496, + 0x657b83, 0x586e75, 0x073642, 0x002b36, + 0xdc322f, 0xcb4b16, 0xb58900, 0x859900, + 0x2aa198, 0x268bd2, 0x6c71c4, 0xd33682, + ]); + +Base16Theme.add("spacemacs", "Spacemacs", + [ + 0x1f2022, 0x282828, 0x444155, 0x585858, + 0xb8b8b8, 0xa3a3a3, 0xe8e8e8, 0xf8f8f8, + 0xf2241f, 0xffa500, 0xb1951d, 0x67b11d, + 0x2d9574, 0x4f97d7, 0xa31db1, 0xb03060, + ]); + +Base16Theme.add("summerfruit-dark", "Summerfruit Dark", + [ + 0x151515, 0x202020, 0x303030, 0x505050, + 0xB0B0B0, 0xD0D0D0, 0xE0E0E0, 0xFFFFFF, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, + ]); + +Base16Theme.add("summerfruit-light", "Summerfruit Light", + [ + 0xFFFFFF, 0xE0E0E0, 0xD0D0D0, 0xB0B0B0, + 0x000000, 0x101010, 0x151515, 0x202020, + 0xFF0086, 0xFD8900, 0xABA800, 0x00C918, + 0x1FAAAA, 0x3777E6, 0xAD00A1, 0xCC6633, + ]); + +Base16Theme.add("synth-midnight-dark", "Synth Midnight", + [ + 0x040404, 0x141414, 0x242424, 0x61507A, + 0xBFBBBF, 0xDFDBDF, 0xEFEBEF, 0xFFFBFF, + 0xB53B50, 0xE4600E, 0xDAE84D, 0x06EA61, + 0x7CEDE9, 0x03AEFF, 0xEA5CE2, 0x9D4D0E, + ]); + +Base16Theme.add("tomorrow-night-eighties", "Tomorrow Night Eighties", + [ + 0x2d2d2d, 0x393939, 0x515151, 0x999999, + 0xb4b7b4, 0xcccccc, 0xe0e0e0, 0xffffff, + 0xf2777a, 0xf99157, 0xffcc66, 0x99cc99, + 0x66cccc, 0x6699cc, 0xcc99cc, 0xa3685a, + ]); + +Base16Theme.add("tomorrow-night", "Tomorrow Night", + [ + 0x1d1f21, 0x282a2e, 0x373b41, 0x969896, + 0xb4b7b4, 0xc5c8c6, 0xe0e0e0, 0xffffff, + 0xcc6666, 0xde935f, 0xf0c674, 0xb5bd68, + 0x8abeb7, 0x81a2be, 0xb294bb, 0xa3685a, + ]); + +Base16Theme.add("tomorrow", "Tomorrow", + [ + 0xffffff, 0xe0e0e0, 0xd6d6d6, 0x8e908c, + 0x969896, 0x4d4d4c, 0x282a2e, 0x1d1f21, + 0xc82829, 0xf5871f, 0xeab700, 0x718c00, + 0x3e999f, 0x4271ae, 0x8959a8, 0xa3685a, + ]); + +Base16Theme.add("tube", "London Tube", + [ + 0x231f20, 0x1c3f95, 0x5a5758, 0x737171, + 0x959ca1, 0xd9d8d8, 0xe7e7e8, 0xffffff, + 0xee2e24, 0xf386a1, 0xffd204, 0x00853e, + 0x85cebc, 0x009ddc, 0x98005d, 0xb06110, + ]); + +Base16Theme.add("twilight", "Twilight", + [ + 0x1e1e1e, 0x323537, 0x464b50, 0x5f5a60, + 0x838184, 0xa7a7a7, 0xc3c3c3, 0xffffff, + 0xcf6a4c, 0xcda869, 0xf9ee98, 0x8f9d6a, + 0xafc4db, 0x7587a6, 0x9b859d, 0x9b703f, + ]); + +Base16Theme.add("unikitty-dark", "Unikitty Dark", + [ + 0x2e2a31, 0x4a464d, 0x666369, 0x838085, + 0x9f9da2, 0xbcbabe, 0xd8d7da, 0xf5f4f7, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x796af5, 0xbb60ea, 0xc720ca, + ]); + +Base16Theme.add("unikitty-light", "Unikitty Light", + [ + 0xffffff, 0xe1e1e2, 0xc4c3c5, 0xa7a5a8, + 0x89878b, 0x6c696e, 0x4f4b51, 0x322d34, + 0xd8137f, 0xd65407, 0xdc8a0e, 0x17ad98, + 0x149bda, 0x775dff, 0xaa17e6, 0xe013d0, + ]); + +Base16Theme.add("woodland", "Woodland", + [ + 0x231e18, 0x302b25, 0x48413a, 0x9d8b70, + 0xb4a490, 0xcabcb1, 0xd7c8bc, 0xe4d4c8, + 0xd35c5c, 0xca7f32, 0xe0ac16, 0xb7ba53, + 0x6eb958, 0x88a4d3, 0xbb90e2, 0xb49368, + ]); + +Base16Theme.add("xcode-dusk", "XCode Dusk", + [ + 0x282B35, 0x3D4048, 0x53555D, 0x686A71, + 0x7E8086, 0x939599, 0xA9AAAE, 0xBEBFC2, + 0xB21889, 0x786DC5, 0x438288, 0xDF0002, + 0x00A0BE, 0x790EAD, 0xB21889, 0xC77C48, + ]); + +Base16Theme.add("zenburn", "Zenburn", + [ + 0x383838, 0x404040, 0x606060, 0x6f6f6f, + 0x808080, 0xdcdccc, 0xc0c0c0, 0xffffff, + 0xdca3a3, 0xdfaf8f, 0xe0cf9f, 0x5f7f5f, + 0x93e0e3, 0x7cb8bb, 0xdc8cc3, 0x000000, + ]); diff --git a/src/model/LabelColors.ts b/src/model/LabelColors.ts new file mode 100644 index 0000000..2854272 --- /dev/null +++ b/src/model/LabelColors.ts @@ -0,0 +1,89 @@ +import Gdk from 'gi://Gdk?version=4.0'; +import Gio from 'gi://Gio'; +import {Realm} from './Realm.js' + +const CITADEL_SETTINGS_SCHEMA = 'com.subgraph.citadel'; +const LABEL_COLOR_LIST_KEY = 'label-color-list'; +const REALM_LABEL_COLORS_KEY = 'realm-label-colors'; + +const DEFAULT_LABEL_COLOR = new Gdk.RGBA({ red: 153, green: 193, blue: 241, }); + +export class LabelColorManager { + private _citadelSettings: Gio.Settings; + private _defaultColors: Gdk.RGBA[]; + private _realmLabelColors: Map; + + constructor() { + this._citadelSettings = new Gio.Settings({ schema_id: CITADEL_SETTINGS_SCHEMA }); + this._defaultColors = []; + this._realmLabelColors = new Map(); + this._loadColors(); + } + + _loadColors() { + let entries = this._citadelSettings.get_strv(LABEL_COLOR_LIST_KEY); + entries.forEach(entry => { + let c = new Gdk.RGBA(); + if (c.parse(entry)) { + this._defaultColors.push(c); + + } + }); + + entries = this._citadelSettings.get_strv(REALM_LABEL_COLORS_KEY); + entries.forEach(entry => { + let parts = entry.split(":"); + if (parts.length === 2) { + let c = new Gdk.RGBA(); + if (c.parse(parts[1])) { + this._realmLabelColors.set(parts[0], c); + } + } + }) + } + + + updateRealmColor(realm: Realm, color: Gdk.RGBA) { + this._realmLabelColors.set(realm.name, color); + this._storeRealmColors(); + } + + lookupRealmColor(realm: Realm): Gdk.RGBA { + let c = this._realmLabelColors.get(realm.name); + if (c) { + return c; + } + + let newColor = this._allocateColor(); + this.updateRealmColor(realm, newColor); + return newColor; + } + + _storeRealmColors() { + let entries: string[] = []; + this._realmLabelColors.forEach((v,k) => { + entries.push(`${k}:${v.to_string()}`); + }); + entries.sort(); + + this._citadelSettings.set_strv(REALM_LABEL_COLORS_KEY, entries); + } + + _allocateColor() { + // 1) No default colors? return a built in color + if (this._defaultColors.length === 0) { + return DEFAULT_LABEL_COLOR; + } + + // 2) Find first color on default color list that isn't used already + let usedColors = Array.from(this._realmLabelColors.values()); + let defaultColor = this._defaultColors.find(color => !usedColors.some(c => c.equal(color))); + if (defaultColor) { + return defaultColor; + } + + // 3) Choose a random element of the default list + let index = Math.floor(Math.random() * this._defaultColors.length); + return this._defaultColors[index]; + } +} diff --git a/src/model/ObjectManager.ts b/src/model/ObjectManager.ts new file mode 100644 index 0000000..51ee855 --- /dev/null +++ b/src/model/ObjectManager.ts @@ -0,0 +1,239 @@ +import Gio from 'gi://Gio'; +import GLib from 'gi://GLib'; +const Signals = imports.signals; + +import type {SignalMethods} from 'gjs'; + +Gio._promisify(Gio.DBusProxy.prototype, 'init_async', 'init_finish'); + +// Specified in the D-Bus specification here: +// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager +const ObjectManagerIface = ` + + + + + + + + + + + + + + +`; + +const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface); + + +export interface ObjectManager extends SignalMethods {}; + +export class ObjectManager { + private _connection: Gio.DBusConnection; + private _serviceName: string; + private _managerPath: string; + private _cancellable: any; + private _interfaceInfos: { [s: string]: Gio.DBusInterfaceInfo }; + private _objects: { [s: string]: { [s: string]: Gio.DBusProxy }}; + private _onLoaded: any; + private _managerProxy: Gio.DBusProxy; + private _interfaces: { [s: string]: Gio.DBusProxy[] }; + + constructor(params: { connection: Gio.DBusConnection; name: string; objectPath: string; knownInterfaces: any; onLoaded: any; cancellable?: any; }) { + this._connection = params.connection; + this._serviceName = params.name; + this._managerPath = params.objectPath; + this._cancellable = params.cancellable ?? null; + + this._managerProxy = new Gio.DBusProxy({ + g_connection: this._connection, + g_interface_name: ObjectManagerInfo.name, + g_interface_info: ObjectManagerInfo, + g_name: this._serviceName, + g_object_path: this._managerPath, + g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START, + }); + + this._interfaceInfos = {}; + this._objects = {}; + this._interfaces = {}; + this._onLoaded = params.onLoaded; + + if (params.knownInterfaces) + this._registerInterfaces(params.knownInterfaces); + + this._initManagerProxy(); + } + + _completeLoad() { + if (this._onLoaded) + this._onLoaded(); + } + + async _addInterface(objectPath: string, interfaceName: string) { + let info = this._interfaceInfos[interfaceName]; + + if (!info) + return; + + const proxy = new Gio.DBusProxy({ + g_connection: this._connection, + g_name: this._serviceName, + g_object_path: objectPath, + g_interface_name: interfaceName, + g_interface_info: info, + g_flags: Gio.DBusProxyFlags.DO_NOT_AUTO_START, + }); + + try { + await proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable); + } catch (e: any) { + logError(e, `could not initialize proxy for interface ${interfaceName}`); + return; + } + + let isNewObject; + if (!this._objects[objectPath]) { + this._objects[objectPath] = {}; + isNewObject = true; + } else { + isNewObject = false; + } + + this._objects[objectPath][interfaceName] = proxy; + + if (!this._interfaces[interfaceName]) + this._interfaces[interfaceName] = []; + + this._interfaces[interfaceName].push(proxy); + + if (isNewObject) + this.emit('object-added', objectPath); + + this.emit('interface-added', interfaceName, proxy); + } + + _removeInterface(objectPath: string, interfaceName: string) { + if (!this._objects[objectPath]) + return; + + let proxy = this._objects[objectPath][interfaceName]; + + if (this._interfaces[interfaceName]) { + let index = this._interfaces[interfaceName].indexOf(proxy); + + if (index >= 0) + this._interfaces[interfaceName].splice(index, 1); + + if (this._interfaces[interfaceName].length === 0) + delete this._interfaces[interfaceName]; + } + + this.emit('interface-removed', interfaceName, proxy); + + delete this._objects[objectPath][interfaceName]; + + if (Object.keys(this._objects[objectPath]).length === 0) { + delete this._objects[objectPath]; + this.emit('object-removed', objectPath); + } + } + + async _initManagerProxy() { + try { + await this._managerProxy.init_async( + GLib.PRIORITY_DEFAULT, this._cancellable); + } catch (e: any) { + logError(e, `could not initialize object manager for object ${this._serviceName}`); + + this._completeLoad(); + return; + } + + this._managerProxy.connectSignal('InterfacesAdded', + (_objectManager: any, _sender: any, [objectPath, interfaces]: any) => { + let interfaceNames = Object.keys(interfaces); + for (let i = 0; i < interfaceNames.length; i++) + this._addInterface(objectPath, interfaceNames[i]); + }); + this._managerProxy.connectSignal('InterfacesRemoved', + (_objectManager: any, _sender: any, [objectPath, interfaceNames]: any) => { + for (let i = 0; i < interfaceNames.length; i++) + this._removeInterface(objectPath, interfaceNames[i]); + }); + + if (Object.keys(this._interfaceInfos).length === 0) { + this._completeLoad(); + return; + } + + this._managerProxy.connect('notify::g-name-owner', () => { + if (this._managerProxy.g_name_owner) + this._onNameAppeared(); + else + this._onNameVanished(); + }); + + if (this._managerProxy.g_name_owner) + this._onNameAppeared().catch(logError); + } + + async _onNameAppeared() { + try { + const [objects] = await this._managerProxy.GetManagedObjectsAsync(); + + if (!objects) { + this._completeLoad(); + return; + } + + const objectPaths = Object.keys(objects); + await Promise.allSettled(objectPaths.flatMap(objectPath => { + const object = objects[objectPath]; + const interfaceNames = Object.getOwnPropertyNames(object); + return interfaceNames.map( + ifaceName => this._addInterface(objectPath, ifaceName)); + })); + } catch (error: any) { + logError(error, `could not get remote objects for service ${this._serviceName} path ${this._managerPath}`); + } finally { + this._completeLoad(); + } + } + + _onNameVanished() { + let objectPaths = Object.keys(this._objects); + for (let i = 0; i < objectPaths.length; i++) { + let objectPath = objectPaths[i]; + let object = this._objects[objectPath]; + + let interfaceNames = Object.keys(object); + for (let j = 0; j < interfaceNames.length; j++) { + let interfaceName = interfaceNames[j]; + + if (object[interfaceName]) + this._removeInterface(objectPath, interfaceName); + } + } + } + + _registerInterfaces(interfaces: string | any[]) { + for (let i = 0; i < interfaces.length; i++) { + let info = Gio.DBusInterfaceInfo.new_for_xml(interfaces[i]); + this._interfaceInfos[info.name] = info; + } + } + + + getProxiesForInterface(interfaceName: string) { + let proxyList = this._interfaces[interfaceName]; + + if (!proxyList) + return []; + + return proxyList; + } +} +Signals.addSignalMethods(ObjectManager.prototype); diff --git a/src/model/OptionData.ts b/src/model/OptionData.ts new file mode 100644 index 0000000..6bc6dc2 --- /dev/null +++ b/src/model/OptionData.ts @@ -0,0 +1,109 @@ + +export class OptionData { + tooltip; + description; + static map: Map = new Map(); + + constructor(description: string, tooltip: string) { + this.tooltip = tooltip; + this.description = description; + } + + static add(name: string, description: string, tooltip: string) { + OptionData.map.set(name, new OptionData(description, tooltip)); + } + + static description(name: string) { + return OptionData.map.get(name)?.description; + } + + static tooltip(name: string) { + return OptionData.map.get(name)?.tooltip; + } +} + +OptionData.add('use-gpu','Use GPU in Realm', + +`If enabled the render node device /dev/dri/renderD128 will be mounted into the realm container. + +If privileged device /dev/dri/card0 is also needed set +additional variable in realm configuration file: + + use-gpu-card0 = true +`); + +OptionData.add('use-wayland', 'Use Wayland in Realm', + +`If enabled access to Wayland display will be permitted in realm by adding wayland socket to realm. + + /run/user/1000/wayland-0 + +`); + +OptionData.add('use-x11', 'Use X11 in Realm', + +`If enabled access to X11 server will be added by mounting directory X11 directory into realm. + + /tmp/.X11-unix + +`); + +OptionData.add('use-sound', 'Use sound in Realm', + +`If enabled allows use of sound inside of realm. The following items will be added: + + /dev/snd + /dev/shm + /run/user/1000/pulse + +`); + +OptionData.add('use-shared-dir', 'Mount /Shared directory in Realm', + +`If enabled the shared directory will be mounted as /Shared in home directory of realm. + +This directory is shared between all realms with this option enabled and is an easy way to move files between realms. + +`); + +OptionData.add('use-network', 'Realm has network access', + `If enabled the realm will have access to the network.`); + +OptionData.add('use-kvm', 'Use KVM (/dev/kvm) in Realm', + `If enabled device /dev/kvm will be added to realm`); + + +OptionData.add('use-ephemeral-home', 'Use ephemeral tmpfs mount for home directory', + +`If enabled the home directory of realm will be set up in ephemeral mode. + +The ephemeral home directory is set up with the following steps: + + 1. Home directory is mounted as tmpfs filesystem + 2. Any files in /realms/skel are copied into home directory + 3. Any files in /realms/realm-$name/skel are copied into home directory. + 4. Any directories listed in config file variable ephemeral_persistent_dirs + are bind mounted from /realms/realm-$name/home into ephemeral + home directory. + +`); + +OptionData.add('overlay', 'Type of rootfs overlay Realm is configured to use.', +`Overlay +Type of rootfs overlay realm is configured to use. + None Don't use a rootfs overlay + TmpFS Use a rootfs overlay stored on tmpfs + Storage Use a rootfs overlay stored on disk in storage partition +`); + +OptionData.add('realmfs', 'Root filesystem image to use for Realm.', +`RealmFS +Root filesystem image to use for realm.`); + +OptionData.add('colorscheme', 'Terminal Color Scheme', +`Terminal Color Scheme +Choose a color scheme to use in the terminals in this realm.`); + +OptionData.add('window-label-color', 'Window Label Color', +`Window Label Color +Set a color to be used when a label is drawn on the window titlebar indicating which realm the application is running in.`); diff --git a/src/model/Realm.ts b/src/model/Realm.ts new file mode 100644 index 0000000..69bb813 --- /dev/null +++ b/src/model/Realm.ts @@ -0,0 +1,129 @@ +import GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; + +import {RealmFS} from './RealmFS.js'; +import {RealmConfig} from './RealmConfig.js'; +import {LabelColorManager} from './LabelColors.js'; +import { RGBA } from 'gi-types/gdk4.js'; + +export const RunStatus = { + STOPPED: 0, + STARTING: 1, + RUNNING: 2, + CURRENT: 3, + STOPPING: 4, +}; + +export class Realm extends GObject.Object { + private _name!: string; + private _description!: string; + private _pidns!: number; + private _run_status!: number; + private _is_system_realm!: boolean; + private _realmfs!: RealmFS | null; + private _labelColors: LabelColorManager; + + static { + GObject.registerClass({ + GTypeName: 'Realm', + Properties: { + 'name': GObject.ParamSpec.string('name', '', '', GObject.ParamFlags.READWRITE, ''), + 'description': GObject.ParamSpec.string('description', '', '', GObject.ParamFlags.READWRITE, ''), + 'pidns': GObject.ParamSpec.uint64('pidns', '', '', GObject.ParamFlags.READWRITE, + 0, Number.MAX_SAFE_INTEGER, 0), + 'run-status': GObject.ParamSpec.uint('run-status', '', '', GObject.ParamFlags.READWRITE, + 0, 4, 0), + 'is-system-realm': GObject.ParamSpec.boolean('is-system-realm', '', '', GObject.ParamFlags.READWRITE, false), + 'realmfs': GObject.ParamSpec.object('realmfs', '', '', GObject.ParamFlags.READWRITE, RealmFS), + }, + Signals: { 'changed': {}}, + }, this); + } + + _proxy: Gio.DBusProxy; + + config = new RealmConfig(); + + + constructor(proxy: Gio.DBusProxy, labelColors: LabelColorManager) { + super(); + this._proxy = proxy; + this._labelColors = labelColors; + this._proxy.connect('g-properties-changed', (_proxy, _changed, _invalidated) => { + this._sync(); + }); + this._sync(); + } + + get realmfs(): RealmFS | null { + return this._realmfs; + } + + set realmfs(value: RealmFS | null) { + if (this.realmfs === value) { + return; + } + this._realmfs = value; + this.emit('changed'); + } + + _sync() { + this._name = this._proxy.Name; + this._description = this._proxy.Description; + this._pidns = this._proxy.PidNS; + this._run_status = this._proxy.RunStatus; + this._is_system_realm = this._proxy.IsSystemRealm; + this.emit('changed'); + } + + get name() { + return this._name; + } + get description() { + return this._description; + } + + get pidns() { + return this._pidns; + } + + get run_status() { + return this._run_status; + } + + get is_system_realm() { + return this._is_system_realm; + } + + is_running() { + return this.run_status === RunStatus.RUNNING || this.run_status === RunStatus.CURRENT; + } + + is_current() { + return this.run_status === RunStatus.CURRENT + } + + getLabelColor() { + return this._labelColors.lookupRealmColor(this); + } + + setLabelColor(color: RGBA) { + this._labelColors.updateRealmColor(this, color); + } + + + realmfs_index() { + return this._proxy.RealmFS + } + + set_global_config(config: { [s: string]: string; }) { + this.config.set_global_config(config); + } + + async load_config() { + const result = await this._proxy.GetConfigAsync(); + this.config.set_config(result[0]); + this.emit('changed'); + } +} + diff --git a/src/model/RealmConfig.ts b/src/model/RealmConfig.ts new file mode 100644 index 0000000..382e777 --- /dev/null +++ b/src/model/RealmConfig.ts @@ -0,0 +1,116 @@ + +import {OptionData} from './OptionData.js'; + +export class BoolOptionData { + static ALL_OPTIONS = [ + 'use-gpu', + 'use-wayland', + 'use-x11', + 'use-sound', + 'use-network', + 'use-kvm', + 'use-shared-dir', + 'use-ephemeral-home', + ] + + name; + description; + tooltip; + + constructor(name: string) { + this.name = name; + this.description = OptionData.description(name) ?? name; + this.tooltip = OptionData.tooltip(name) ?? ""; + } + + static allOptions(): BoolOptionData[] { + let options: BoolOptionData[] = []; + BoolOptionData.ALL_OPTIONS.forEach(name => { + options.push(new BoolOptionData(name)); + + }) + return options; + } +} + +export class ConfigOption { + name; + value; + defaultValue; + description; + tooltip; + + constructor(name: string, value: string, defaultValue: string, description: string, tooltip: string) { + this.name = name; + this.value = value; + this.defaultValue = defaultValue; + this.description = description; + this.tooltip = tooltip; + } +} + +export class RealmConfig { + _configVars: { [s: string]: string; } = {}; + _globalConfig: { [s: string]: string; } = {}; + + set_config(config: { [s: string]: string; }) { + this._configVars = config; + } + + set_global_config(globalConfig: { [s: string]: string; }) { + this._globalConfig = globalConfig; + } + + get_var(name: string) { + let value = this._configVars[name]; + if (value && value.length > 0) { + return value; + } + value = this._globalConfig[name]; + if (value && value.length > 0) { + return value; + } + return null; + } + + get_bool(name: string): boolean { + return this.get_var(name) === 'true'; + } + + get_colorscheme() { + return this.get_var('terminal-scheme') ?? 'default-dark'; + } + + get_realmfs() { + return this.get_var('realmfs') ?? 'base'; + } + + static capitalize(term: string) { + const acronyms = ["gpu", "kvm"]; + if (acronyms.find(s => term === s)) { + return term.toUpperCase(); + } else { + return term.charAt(0).toUpperCase() + term.slice(1); + } + } + + static option_label(key: string) { + return key.substring(4) + .split('-') + .map(RealmConfig.capitalize) + .join(''); + } + + is_enabled_bool_option(name: string, value: string, isDefault: boolean) { + const default_val = this._globalConfig[name]; + return name.startsWith("use-") && value === "true" && (value === default_val) === isDefault; + } + + enabled_bool_option_labels(isDefault: boolean) { + return Object.entries(this._configVars) + .filter(([k,v]) => this.is_enabled_bool_option(k, v, isDefault)) + .map(([k,]) => RealmConfig.option_label(k)) + .sort(); + } +} + diff --git a/src/model/RealmFS.ts b/src/model/RealmFS.ts new file mode 100644 index 0000000..f0e5532 --- /dev/null +++ b/src/model/RealmFS.ts @@ -0,0 +1,93 @@ +import GObject from 'gi://GObject'; +import Gio from 'gi://Gio'; + +const OBJECT_PREFIX = "/com/subgraph/Realms2/RealmFS"; + +export class RealmFS extends GObject.Object { + private _name!: string; + private _in_use!: boolean; + private _activated!: boolean; + private _mountpoint!: string; + private _path!: string; + private _free_space!: number; + private _allocated_space!: number; + + static { + GObject.registerClass({ + GTypeName: 'RealmFS', + Properties: { + 'name': GObject.ParamSpec.string('name', '', '', GObject.ParamFlags.READWRITE, ''), + 'in-use': GObject.ParamSpec.boolean('in-use', '', '', GObject.ParamFlags.READWRITE, false), + 'activated': GObject.ParamSpec.boolean('activated', '', '', GObject.ParamFlags.READWRITE, false), + 'mountpoint': GObject.ParamSpec.string('mountpoint', '', '', GObject.ParamFlags.READWRITE, ''), + 'path': GObject.ParamSpec.string('path', '', '', GObject.ParamFlags.READWRITE, ''), + 'free-space': GObject.ParamSpec.uint64('free-space', '', '', GObject.ParamFlags.READWRITE, + 0, Number.MAX_SAFE_INTEGER, 0), + 'allocated-space': GObject.ParamSpec.uint64('allocated-space', '', '', GObject.ParamFlags.READWRITE, + 0, Number.MAX_SAFE_INTEGER, 0), + }, + }, this); + } + + _proxy: Gio.DBusProxy; + + constructor(proxy: Gio.DBusProxy) { + super(); + this._proxy = proxy; + this._proxy.connect('g-properties-changed', (_proxy, _changed, _invalidated) => { + this._sync(); + }); + this._sync(); + } + + index() { + let path = this._proxy.get_object_path(); + if (path.startsWith(OBJECT_PREFIX)) { + const tail = path.substring(OBJECT_PREFIX.length); + let index = Number.parseInt(tail); + if (!Number.isNaN(index)) { + return index; + } + } + return -1; + } + + get name() { + return this._name; + } + + get in_use() { + return this._in_use; + } + + get activated() { + return this._activated; + } + + get mountpoint() { + return this._mountpoint; + } + + get path() { + return this._path; + } + + get free_space() { + return this._free_space; + } + + get allocated_space() { + return this._allocated_space; + } + + _sync() { + this._name = this._proxy.Name; + this._in_use = this._proxy.InUse; + this._activated = this._proxy.Activated; + this._mountpoint = this._proxy.Mountpoint; + this._path = this._proxy.Path; + this._free_space = this._proxy.FreeSpace; + this._allocated_space = this._proxy.AllocatedSpace; + } +} + diff --git a/src/model/RealmManager.ts b/src/model/RealmManager.ts new file mode 100644 index 0000000..5a33e51 --- /dev/null +++ b/src/model/RealmManager.ts @@ -0,0 +1,171 @@ +import Gio from 'gi://Gio'; + +import {ObjectManager} from './ObjectManager.js'; +import {loadInterfaceXML} from './Utils.js' +import {Realm} from './Realm.js'; +import {RealmFS} from './RealmFS.js'; +import {LabelColorManager} from './LabelColors.js'; + +import type {SignalMethods} from 'gjs'; + +const Signals = imports.signals; + +const RealmIface = loadInterfaceXML("com.subgraph.realms.Realm"); +const RealmFSIface = loadInterfaceXML("com.subgraph.realms.RealmFS"); + +const BUS_NAME = 'com.subgraph.Realms2'; +const OBJECT_PATH = '/com/subgraph/Realms2'; +const ManagerInterface = loadInterfaceXML("com.subgraph.realms.Manager2"); +const ManagerProxy = Gio.DBusProxy.makeProxyWrapper(ManagerInterface); + + + +export interface RealmManager extends SignalMethods {}; + +export class RealmManager { + + _globalConfig: { [s: string]: string; } = {}; + _realms: { [s: string]: Realm; } = {}; + _realmfs: { [s: string]: RealmFS; } = {}; + + static INSTANCE = new RealmManager(); + private _objectManager: ObjectManager; + private _labelColors: LabelColorManager; + private _proxy: Gio.DBusProxy; + + + static instance(): RealmManager { + return RealmManager.INSTANCE; + } + + constructor() { + this._realmfs = {}; + + this._objectManager = new ObjectManager({ + connection: Gio.DBus.system, + name: 'com.subgraph.Realms2', + objectPath: '/com/subgraph/Realms2', + knownInterfaces: [RealmIface, RealmFSIface], + onLoaded: this._onLoaded.bind(this), + }); + this._proxy = new ManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH, + (_proxy: any, error: any) => { + if(error) { + logError(error); + } else { + this._setGlobalConfig().catch(logError); + } + }); + + this._labelColors = new LabelColorManager(); + } + + + async _setGlobalConfig() { + let result = await this._proxy.GetGlobalConfigAsync(); + this._globalConfig = result[0]; + this._assignGlobalConfigToRealms(); + } + + async _onLoaded() { + let realms = this._objectManager.getProxiesForInterface('com.subgraph.realms.Realm'); + for (let i = 0; i < realms.length; i++) { + this._addRealm(realms[i]); + } + + let realmfs = this._objectManager.getProxiesForInterface('com.subgraph.realms.RealmFS'); + for (let i = 0; i < realmfs.length; i++) { + this._addRealmFS(realmfs[i]); + } + + + this._objectManager.connect('interface-added', (_objectManager, interfaceName, proxy) => { + if (interfaceName === 'com.subgraph.Realm') { + this._addRealm(proxy); + } else if (interfaceName === 'com.subgraph.RealmFS') { + this._addRealmFS(proxy); + } + }); + + this._objectManager.connect('interface-removed', (_objectManager, interfaceName, proxy) => { + if (interfaceName === 'com.subgraph.Realm') { + this._removeRealm(proxy); + } else if (interfaceName === 'com.subgraph.RealmFS') { + this._removeRealmFS(proxy); + } + }); + } + + async _addRealm(proxy: Gio.DBusProxy) { + let objectPath = proxy.get_object_path(); + let realm = new Realm(proxy, this._labelColors); + this._realms[objectPath] = realm; + this._findRealmFSForRealm(realm); + if (this._globalConfig) { + realm.set_global_config(this._globalConfig); + } + await realm.load_config(); + this.emit('realm-added', realm); + } + + _removeRealm(proxy: Gio.DBusProxy) { + let objectPath = proxy.get_object_path(); + if (this._realms[objectPath]?._proxy === proxy) { + let realm = this._realms[objectPath]; + delete this._realms[objectPath]; + this.emit('realm-removed', realm); + } + proxy.disconnectAll(); + } + + _addRealmFS(proxy: Gio.DBusProxy) { + let objectPath = proxy.get_object_path(); + let realmfs = new RealmFS(proxy); + this._realmfs[objectPath] = realmfs; + this._assignRealmFSToRealms(realmfs); + this.emit('realmfs-added', realmfs); + } + + _findRealmFSForRealm(realm: Realm) { + if (realm.realmfs_index() > 0) { + realm.realmfs = Object.values(this._realmfs) + .find(realmfs => realmfs.index() === realm.realmfs_index()) ?? null; + } + } + + realmfsList() { + return Object.values(this._realmfs); + } + + _assignRealmFSToRealms(realmfs: RealmFS) { + Object.values(this._realms).forEach(r => { + if (r.realmfs_index() > 0 && r.realmfs_index() === realmfs.index() && r.realmfs != realmfs) { + r.realmfs = realmfs; + } + }) ; + } + + _assignGlobalConfigToRealms() { + Object.values(this._realms).forEach(r => r.set_global_config(this._globalConfig)); + } + + _removeRealmFSFromRealms(realmfs: RealmFS) { + Object.values(this._realms).forEach(r => { + if (r.realmfs === realmfs) { + r.realmfs = null; + } + }); + } + + _removeRealmFS(proxy: Gio.DBusProxy) { + let objectPath = proxy.get_object_path(); + if (this._realmfs[objectPath]?._proxy === proxy) { + let realmfs = this._realmfs[objectPath]; + delete this._realmfs[objectPath]; + this._removeRealmFSFromRealms(realmfs); + this.emit('realmfs-removed', realmfs); + } + proxy.disconnectAll(); + } +} +Signals.addSignalMethods(RealmManager.prototype); diff --git a/src/model/Utils.ts b/src/model/Utils.ts new file mode 100644 index 0000000..186b81c --- /dev/null +++ b/src/model/Utils.ts @@ -0,0 +1,17 @@ +import Gio from 'gi://Gio'; + +export function loadInterfaceXML(iface: string): string | null { + + let uri = `resource:///com/subgraph/citadel/Realms/dbus-interfaces/${iface}.xml`; + let f = Gio.File.new_for_uri(uri); + + try { + let [_ok, bytes] = f.load_contents(null); + // @ts-ignore + return new TextDecoder().decode(bytes); + } catch (e) { + log(`Failed to load D-Bus interface ${iface}`); + } + + return null; +} diff --git a/subprojects/blueprint-compiler.wrap b/subprojects/blueprint-compiler.wrap new file mode 100644 index 0000000..decb0d0 --- /dev/null +++ b/subprojects/blueprint-compiler.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory = blueprint-compiler +url = https://gitlab.gnome.org/jwestman/blueprint-compiler.git +revision = main +depth = 1 + +[provide] +program_names = blueprint-compiler + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..d1a1cb9 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,40 @@ +{ + "compilerOptions": { + "strictNullChecks": true, + "noImplicitReturns": true, + "noImplicitAny": true, + "noUnusedParameters": true, + "experimentalDecorators": true, + "strict": true, + + "paths": { + "*": [ + "*", + "types/*", + "gi-types/*" + ] + }, + "target": "ES2018", + "baseUrl": ".", + "moduleResolution": "node", + "module": "ES2020", + "outDir": "js", + "lib": [ + "es2020" + ], + "skipLibCheck": true, + "typeRoots": [ + "types", + "gi-types", + ], + }, + "include": [ + "gi-types/gi.d.ts", + "types/ambient.d.ts", + "types/gjs.d.ts", + "src/**/*", + ], + "exclude": [ + "node_modules/@types/**", + ], +} diff --git a/types/ambient.d.ts b/types/ambient.d.ts new file mode 100644 index 0000000..141d177 --- /dev/null +++ b/types/ambient.d.ts @@ -0,0 +1,32 @@ +declare function _(id: string): string; +declare function print(args: string): void; +declare function log(obj: object, others?: object[]): void; +declare function log(msg: string, subsitutions?: any[]): void; + +declare const pkg: { + version: string; + name: string; +}; + +declare module console { + export function error(obj: object, others?: object[]): void; + export function error(msg: string, subsitutions?: any[]): void; +} + +declare class TextDecoder { + constructor(format: string); + decode(buffer: ArrayBuffer): string; +} +declare class TextEncoder { + constructor(); + encode(str: string): Uint8Array; +} + +declare interface String { + format(...replacements: string[]): string; + format(...replacements: number[]): string; +} +declare interface Number { + toFixed(digits: number): number; +} + diff --git a/types/gettext.d.ts b/types/gettext.d.ts new file mode 100644 index 0000000..ffde984 --- /dev/null +++ b/types/gettext.d.ts @@ -0,0 +1,35 @@ +export namespace Gettext { + export enum LocaleCategory { + CTYPE = 0, + NUMERIC = 1, + TIME = 2, + COLLATE = 3, + MONETARY = 4, + MESSAGES = 5, + ALL = 6, + } + + export function setlocale(category: LocaleCategory, locale: string | null): string | null; + export function textdomain(domainName: string): void; + export function bindtextdomain(domainName: string, dirName: string): void; + + export function gettext(msgid: string): string; + export function dgettext(domainName: string | null, msgid: string): string; + export function dcgettext(domainName: string | null, msgid: string, category: LocaleCategory): string; + + export function ngettext(msgid1: string, msgid2: string, n: number): string; + export function dngettext(domainName: string | null, msgid1: string, msgid2: string, n: number): string; + + export function pgettext(context: string | null, msgid: string): string; + export function dpgettext(domainName: string | null, context: string | null, msgid: string): string; + + export class GettextObject { + gettext(msgid: string): string; + ngettext(msgid1: string, msgid2: string, n: number): string; + pgettext(context: string | null, msgid: string): string; + } + + export function domain(domainName: string | null): GettextObject; +} + +export default Gettext; \ No newline at end of file diff --git a/types/gjs.d.ts b/types/gjs.d.ts new file mode 100644 index 0000000..1ff9405 --- /dev/null +++ b/types/gjs.d.ts @@ -0,0 +1,183 @@ +/** + * Type Definitions for Gjs (https://gjs.guide/) + * + * These type definitions are automatically generated, do not edit them by hand. + * If you found a bug fix it in ts-for-gir itself or create a bug report on https://github.com/gjsify/ts-for-gir + */ + +/* +import type GObject from '@girs/gobject-2.0'; +import type GLib from '@girs/glib-2.0'; + +import gettext from './gettext.js'; +import system from './system.js'; +import cairo from './cairo.js'; +*/ + + + + +/** + * You can use the `Signals.addSignalMethods` method to apply the `Signals` convenience methods to an `Object`. + * Generally, this is called on an object prototype, but may also be called on an object instance. + * You can use this Interface for this object or prototype to make the methods in typescript known + * @example + * ```ts + * const Signals = imports.signals; + * + * // Define an interface with the same name of your class to make the methods known + * interface Events extends Signals.Methods {} + * + * class Events {} + * Signals.addSignalMethods(Events.prototype); + * + * const events = new Events(); + * + * // Typescript will not complain here + * events.emit("test-signal", "test argument"); + * ``` + */ +export interface SignalMethods { + /** + * Connects a callback to a signal for an object. Pass the returned ID to + * `disconnect()` to remove the handler. + * + * If `callback` returns `true`, emission will stop and no other handlers will be + * invoked. + * + * > Warning: Unlike GObject signals, `this` within a signal callback will always + * > refer to the global object (ie. `globalThis`). + * + * @param sigName A signal name + * @param callback A callback function + * @returns A handler ID + */ + connect(sigName: string, callback: (self: any, ...args: any[]) => void): number; + /** + * Emits a signal for an object. Emission stops if a signal handler returns `true`. + * + * Unlike GObject signals, it is not necessary to declare signals or define their + * signature. Simply call `emit()` with whatever signal name you wish, with + * whatever arguments you wish. + * @param sigName A signal name + * @param args Any number of arguments, of any type + */ + emit(sigName: string, ...args: any[]): void; + /** + * Disconnects a handler for a signal. + * @param id The ID of the handler to be disconnected + */ + disconnect(id: number): void; + /** + * Disconnects all signal handlers for an object. + */ + disconnectAll(): void; + /** + * Checks if a handler ID is connected. + * @param id The ID of the handler to be disconnected + * @returns `true` if connected, or `false` if not + */ + signalHandlerIsConnected(id: number): boolean; +} + +declare namespace signals { + interface Methods { + /** + * Connects a callback to a signal for an object. Pass the returned ID to + * `disconnect()` to remove the handler. + * + * If `callback` returns `true`, emission will stop and no other handlers will be + * invoked. + * + * > Warning: Unlike GObject signals, `this` within a signal callback will always + * > refer to the global object (ie. `globalThis`). + * + * @param sigName A signal name + * @param callback A callback function + * @returns A handler ID + */ + connect(sigName: string, callback: (self: any, ...args: any[]) => void): number; + /** + * Emits a signal for an object. Emission stops if a signal handler returns `true`. + * + * Unlike GObject signals, it is not necessary to declare signals or define their + * signature. Simply call `emit()` with whatever signal name you wish, with + * whatever arguments you wish. + * @param sigName A signal name + * @param args Any number of arguments, of any type + */ + emit(sigName: string, ...args: any[]): void; + /** + * Disconnects a handler for a signal. + * @param id The ID of the handler to be disconnected + */ + disconnect(id: number): void; + /** + * Disconnects all signal handlers for an object. + */ + disconnectAll(): void; + /** + * Checks if a handler ID is connected. + * @param id The ID of the handler to be disconnected + * @returns `true` if connected, or `false` if not + */ + signalHandlerIsConnected(id: number): boolean; + } + export function addSignalMethods(proto: T): proto is T & Methods; +} + +declare global { + interface GjsGiImports { + // Will be extended by the import of more gir types + versions: { + [namespace: string]: string; + }; + } + + interface GjsImports { + gi: GjsGiImports; + signals: typeof signals; + searchPath: string[]; + } + + /** + * Run `pkg.initGettext()` before using this. + * Currently not implemented. + */ + const N_: undefined | ((x: string) => string); + + function print(...args: any[]): void; + function printerr(...args: any[]): void; + function log(obj: object, others?: object[]): void; + function log(msg: string, substitutions?: any[]): void; + function logError(exception: object, message?: any): void; + function logError(message?: any): void; + + interface BooleanConstructor { + $gtype: GObject.GType; + } + + interface NumberConstructor { + $gtype: GObject.GType; + } + + interface StringConstructor { + $gtype: GObject.GType; + } + + interface StringConstructor { + $gtype: GObject.GType; + } + + interface ObjectConstructor { + $gtype: GObject.GType; + } + + const imports: GjsImports; + + const ARGV: string[]; +} + +declare const _imports: GjsImports; +export default _imports; +export { _imports as imports }; diff --git a/types/gjs.js b/types/gjs.js new file mode 100644 index 0000000..1a74967 --- /dev/null +++ b/types/gjs.js @@ -0,0 +1,6 @@ +const imports = globalThis.imports || {}; + +export { imports } +export default imports; + +