Vivaldi

  • Browser
  • Mail
  • News
  • Community
  • About

Navigation

    • Browser
    • Mail
    • News
    • Community
    • About
    • Register
    • Login
    • Search
    CommunityForumHelpThemesWebmail
    • Home
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    1. Home
    2. General Discussion
    3. Customizations & Extensions
    4. Modifications
    5. Import and Export Themes

    Import and Export Themes

    Modifications
    javascript modding archived
    15
    122
    7416
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • luetage
      luetage last edited by luetage

      I had a little fun and wrote an import/export function for Vivaldi themes – a feature still missing from the browser. The new version adds some more useful functionality…

      Features

      • Import user theme
      • Export user theme
      • Backup all user themes
      • Import a backup and add it to themes
      • Sort user themes alphabetically
      • Move user theme left/right with buttons

      Paste following code into a custom.js file to implement the mod. Explanation for the uninitiated here. Caution: Works only when you check “Open Settings in a Tab” in vivaldi://settings/appearance/.

      // Theme Import and Export
      // version 2021.9.0
      // https://forum.vivaldi.net/topic/33154/import-and-export-themes
      // Adds functionality to import, export, backup, sort and move themes to
      // Vivaldi's settings page.
      
      (function () {
        function _checkImport() {
          // written by tam710562
          if (
            typeof _test.colors !== "object" ||
            typeof _test.colors.accentBg !== "string" ||
            !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.accentBg) ||
            typeof _test.colors.baseBg !== "string" ||
            !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.baseBg) ||
            typeof _test.colors.baseFg !== "string" ||
            !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.baseFg) ||
            typeof _test.colors.highlightBg !== "string" ||
            !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.highlightBg) ||
            typeof _test.name !== "string" ||
            typeof _test.settings !== "object" ||
            typeof _test.settings.accentFromPage !== "boolean" ||
            typeof _test.settings.accentOnWindow !== "boolean" ||
            (typeof _test.settings.borderRadius !== "number" &&
              typeof _test.settings.borderRadius !== "string") ||
            typeof _test.settings.tabsTransparent !== "boolean" ||
            typeof _test.version !== "number"
          ) {
            return false;
          } else {
            return true;
          }
        }
      
        function _message(pnt) {
          clearTimeout(_timeout);
          if (pnt === "export") {
            _msg.innerText = "Theme code copied to clipboard.";
          } else if (pnt === "backup") {
            _msg.innerText = "Backup copied to clipboard.";
          } else if (pnt === "import") {
            _msg.innerText = "Theme imported.";
          } else if (pnt === "restore") {
            _msg.innerText = "Backup imported.";
          } else if (pnt === "notice") {
            _msg.innerText = "Nothing to import. Check console.log";
          } else if (pnt === "sort") {
            _msg.innerText = "User themes sorted alphabetically.";
          } else {
            _msg.innerText = "Theme code error.";
          }
          _timeout = setTimeout(function () {
            _msg.innerText = "";
          }, 5000);
        }
      
        function _importBackup() {
          chrome.storage.local.get({ THEMES_USER: "" }, function (res) {
            var userThemes = res.THEMES_USER;
            console.log("Importing themes...");
            for (i = 0; i < _set.length; i++) {
              _test = _set[i];
              var test = _checkImport;
              if (test()) {
                var compare = userThemes.findIndex((x) => x.name == _set[i].name);
                if (compare === -1) {
                  var ok = true;
                  userThemes.push(_set[i]);
                  console.log(_set[i].name + " imported");
                } else {
                  console.log(_set[i].name + " is a duplicate");
                }
              } else {
                console.log(_set[i].name + " failed");
              }
            }
            if (ok === true) {
              chrome.storage.local.set({ THEMES_USER: userThemes }, function () {
                _message("restore");
              });
            } else {
              _message("notice");
            }
          });
        }
      
        function _importTheme() {
          event.stopPropagation();
          event.preventDefault();
          if (_eventType === "paste") {
            var clipboardData = event.clipboardData || window.clipboardData;
            var themeCode = clipboardData.getData("text");
          } else {
            var themeCode = event.dataTransfer.getData("text");
          }
          try {
            _set = JSON.parse(themeCode);
          } catch (err) {
            _message("error");
            return;
          }
          if (Object.keys(_set)[0] === "colors") {
            _test = _set;
            var test = _checkImport;
            if (test()) {
              const nameField = document.querySelector(".theme-name");
              nameField.select();
              document.execCommand("insertText", false, _set.name);
              chrome.storage.local.get({ THEMES_USER: "" }, function (imp) {
                var userThemes = imp.THEMES_USER;
                for (i = 0; i < userThemes.length; i++) {
                  if (userThemes[i].name === nameField.value) {
                    _set.name = nameField.value;
                    userThemes[i] = _set;
                    chrome.storage.local.set({
                      THEMES_USER: userThemes,
                      BROWSER_COLOR_ACCENT_BG: _set.colors.accentBg,
                      BROWSER_COLOR_BG: _set.colors.baseBg,
                      BROWSER_COLOR_FG: _set.colors.baseFg,
                      BROWSER_COLOR_HIGHLIGHT_BG: _set.colors.highlightBg,
                      TABCOLOR_BEHIND_TABS: _set.settings.accentOnWindow,
                      USE_TABCOLOR: _set.settings.accentFromPage,
                      BORDER_RADIUS: _set.settings.borderRadius,
                      USE_TAB_TRANSPARENT_TABS: _set.settings.tabsTransparent,
                      THEME_CURRENT: _set.name,
                    });
                    _message("import");
                    break;
                  }
                }
              });
            } else {
              _message("error");
            }
          } else if (Object.keys(_set)[0] === "0") {
            _importBackup();
          } else {
            _message("error");
          }
        }
      
        function _exportTheme(event) {
          if (event.altKey) {
            var backup = true;
          }
          if (event.shiftKey) {
            var order = true;
          }
          chrome.storage.local.get(
            {
              THEME_CURRENT: "",
              THEMES_USER: "",
            },
            function (exp) {
              const themeName = exp.THEME_CURRENT;
              const userThemes = exp.THEMES_USER;
              if (backup === true) {
                const themeCode = JSON.stringify(userThemes);
                navigator.clipboard.writeText(themeCode);
                _message("backup");
              } else if (order === true) {
                userThemes.sort(function (a, b) {
                  return a.name.localeCompare(b.name);
                });
                chrome.storage.local.set({ THEMES_USER: userThemes }, function () {
                  _message("sort");
                });
              } else {
                for (i = 0; i < userThemes.length; i++) {
                  if (userThemes[i].name === themeName) {
                    const themeCode = JSON.stringify(userThemes[i]);
                    navigator.clipboard.writeText(themeCode);
                    _message("export");
                    break;
                  }
                }
              }
            }
          );
        }
      
        function _moveTheme() {
          chrome.storage.local.get(
            {
              THEME_CURRENT: "",
              THEMES_USER: "",
            },
            function (mv) {
              const themeName = mv.THEME_CURRENT;
              const userThemes = mv.THEMES_USER;
              var index = userThemes.findIndex((x) => x.name == themeName);
              if (index !== -1) {
                if (_toMove === "left") {
                  if (index !== 0) {
                    var fromI = userThemes[index];
                    var toI = userThemes[index - 1];
                    userThemes[index - 1] = fromI;
                    userThemes[index] = toI;
                  } else {
                    return;
                  }
                } else {
                  var last = userThemes.length - 1;
                  if (index < last) {
                    var fromI = userThemes[index];
                    var toI = userThemes[index + 1];
                    userThemes[index + 1] = fromI;
                    userThemes[index] = toI;
                  } else {
                    return;
                  }
                }
                chrome.storage.local.set({ THEMES_USER: userThemes });
              }
            }
          );
        }
      
        function createPort() {
          if (
            document.querySelector(_themeBtn).classList.contains("button-pressed")
          ) {
            const cont = document.querySelector(".theme-metadata");
            const importBtn = document.createElement("input");
            importBtn.setAttribute("type", "text");
            importBtn.setAttribute("placeholder", "Import");
            importBtn.id = "importTheme";
            cont.appendChild(importBtn);
            const exportBtn = document.createElement("input");
            exportBtn.setAttribute("type", "submit");
            exportBtn.classList.add("primary");
            exportBtn.setAttribute("value", "Export");
            exportBtn.setAttribute(
              "title",
              "Click to export theme\nAlt-click to backup all themes\nShift-click to sort themes"
            );
            exportBtn.id = "exportTheme";
            cont.appendChild(exportBtn);
            _msg = document.createElement("span");
            _msg.id = "modInfo";
            cont.appendChild(_msg);
            document
              .getElementById("exportTheme")
              .addEventListener("click", _exportTheme);
            const importInput = document.getElementById("importTheme");
            importInput.addEventListener("paste", function () {
              _eventType = "paste";
              _importTheme(event);
            });
            importInput.addEventListener("drop", function () {
              _eventType = "drop";
              _importTheme(event);
            });
            _timeout = {};
          }
        }
      
        function portThemes() {
          const styleCheck = document.getElementById("portThemes");
          if (!styleCheck) {
            const style = document.createElement("style");
            style.type = "text/css";
            style.id = "portThemes";
            style.innerHTML =
              ".move-left button:focus, .move-right button:focus {border-color: var(--colorBorder) !important;box-shadow: none !important;}#importTheme, #exportTheme {width: 80px;margin-left: 6px;}#importTheme::-webkit-input-placeholder {opacity: 1;color: var(--colorHighlightBg);text-align: center;}#modInfo {margin-top: 6px;margin-left: 12px;}";
            document.getElementsByTagName("head")[0].appendChild(style);
          }
          const modCheck = document.querySelector(".move-left");
          if (!modCheck) {
            const group = document.createElement("div");
            group.classList.add("toolbar", "toolbar-group");
            group.innerHTML =
              '<div class="button-toolbar move-left"><button draggable="false" tabindex="auto" title="Move Theme Left" class=""><svg width="16" height="16" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1216 448v896q0 26-19 45t-45 19-45-19l-448-448q-19-19-19-45t19-45l448-448q19-19 45-19t45 19 19 45z"/></svg></button></div><hr><div class="button-toolbar move-right"><button draggable="false" tabindex="auto" title="Move Theme Right" class=""><svg width="16" height="16" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"/></svg></button></div>';
            document
              .querySelector(_themeBtn)
              .parentNode.parentNode.appendChild(group);
            document
              .querySelector(".move-left")
              .addEventListener("click", function () {
                _toMove = "left";
                _moveTheme();
              });
            document
              .querySelector(".move-right")
              .addEventListener("click", function () {
                _toMove = "right";
                _moveTheme();
              });
            document.querySelector(_themeBtn).addEventListener("click", function () {
              setTimeout(createPort, 50);
            });
          }
        }
      
        const settingsUrl =
          "chrome-extension://mpognobbkildjkofajifpdfhcoklimli/components/settings/settings.html?path=";
        const _themeBtn =
          ".setting-group.unlimited > .toolbar.toolbar-default > .button-toolbar > button";
        chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
          if (changeInfo.url === `${settingsUrl}themes`) {
            setTimeout(portThemes, 100);
          }
        });
      })();
      

      0_1552400270282_Screenshot 2019-03-12 15.17.24.png


      How the mod works

      The mod adds buttons to move themes to the right of the Edit Theme button in vivaldi://settings/themes. When you click the Edit Theme button, an import input field and an export button are available

      • Move selected user theme left or right by clicking the arrow buttons beside the "edit theme" button.
      • Export a theme by clicking Export. The theme code will be copied to the clipboard. It's easiest to save the theme code as note in Vivaldi's notes panel (native Sync!!).
      • Alt-click on Export button to make a backup of all user themes – save copy to notes.
      • Import: Paste or Drag&Drop a copy of an individual user theme or a whole backup into the import field.
      • Shift-click on Export button to sort all user themes alphabetically.

      Anyway, have fun with this – some might even find it useful beer

      github ◊ vfm

      iAN CooG burbuja 2 Replies Last reply Reply Quote 31
      • iAN CooG
        iAN CooG @luetage last edited by iAN CooG

        @luetage Well done, apart the needed space trick to force the refresh of the values, which is no biggie, all ok: I could save my 2 custom themes, delete them and reimport them.
        If you could save/import also the theme preferences like Accent color from active page, rounding, etc it would be 100% perfect.

        http://iancoog.altervista.org/
        --=[]=-----------------------------------------------------------------------=[]=--
        Windows10 64bits - 8core i9-9900K @ 3.60GHz - 16Gb RAM - nVidia GT1030

        luetage 1 Reply Last reply Reply Quote 1
        • luetage
          luetage @iAN CooG last edited by luetage

          @iAN-CooG I haven't tried it yet. We can likely export and import the additional values without problem, but Vivaldi won't commit them to storage. I have tried to trick Vivaldi with simulated keyboard events, but so far I haven't found a satisfying solution.

          github ◊ vfm

          1 Reply Last reply Reply Quote 0
          • luetage
            luetage last edited by

            The code and description in the first post have been updated. I got rid of a nasty bug and added import and export of theme preferences (accent color from active page, apply accent color to window, transparent tabs and corner rounding). All is working now, except that you still have to manually trigger every input to commit to Vivaldi storage proper after theme import.

            github ◊ vfm

            1 Reply Last reply Reply Quote 1
            • burbuja
              burbuja @luetage last edited by

              @luetage said in Import and Export Themes:

              You can only import themes that have been exported by this mod previously.

              I think, you can create such a file by hand, if you know the structure and values ... cool

              Vivaldi -- Browser für unsere Freunde
              Vivaldi -- Navegador web para nuestros amigos
              Vivaldi -- Browser for our friends
              Vivaldi -- Browser pro nase pratele

              Win 7 Enterprise, 32 bit

              luetage 1 Reply Last reply Reply Quote 1
              • luetage
                luetage @burbuja last edited by

                @burbuja Yeah, you can of course create and modify the json file yourself. You can also import other user's themes, if they share the file.

                I automated setting the checkboxes now, which was simple (OP updated). But the really annoying thing is that I can't get keyboard events to fire, which would be required to automate everything. I remember I had this problem before… on another mod about a year ago. It seems like chromium is just bugged and simulating keypresses is very complicated. If anyone has a working solution, I'd be interested.

                github ◊ vfm

                1 Reply Last reply Reply Quote 2
                • iAN CooG
                  iAN CooG last edited by

                  flags are set on import, well done!

                  http://iancoog.altervista.org/
                  --=[]=-----------------------------------------------------------------------=[]=--
                  Windows10 64bits - 8core i9-9900K @ 3.60GHz - 16Gb RAM - nVidia GT1030

                  1 Reply Last reply Reply Quote 0
                  • luetage
                    luetage last edited by luetage

                    wait a minute, I just said nonsense

                    github ◊ vfm

                    1 Reply Last reply Reply Quote 0
                    • luetage
                      luetage last edited by

                      I had a breakthrough. Everything but corner rounding is automated now. Original post code and description have been updated, I also included explanations how to share themes and an example theme to download and import. Feel free to share your own themes jester

                      github ◊ vfm

                      hlehyaric Hadden89 2 Replies Last reply Reply Quote 4
                      • iAN CooG
                        iAN CooG last edited by

                        OMG perfect, @luetage #1

                        http://iancoog.altervista.org/
                        --=[]=-----------------------------------------------------------------------=[]=--
                        Windows10 64bits - 8core i9-9900K @ 3.60GHz - 16Gb RAM - nVidia GT1030

                        1 Reply Last reply Reply Quote 1
                        • hlehyaric
                          hlehyaric @luetage last edited by

                          @luetage It works like a charm. Amazing job. Is this your Christmas gift to V users? Anyway, thanks a lot (I missed the ability to import/export themes, just in case I should refresh my profile).

                          luetage 1 Reply Last reply Reply Quote 2
                          • luetage
                            luetage @hlehyaric last edited by

                            @hlehyaric No, the christmas gift to the community is in progress and will be released over the next days. This is just something I wanted for myself for the longest time and I thought why not give it a try? Doesn't seem like Vivaldi will be doing something about it any time soon.

                            I could make a version that lets you save and share the themes as a string instead of a file. This way the themes could be saved to Vivaldi's notes feature and shared by just pasting the string in the forum. It's also something I'm considering for the forum mod, because I noticed no one is sharing themes. Uploading a file seems to bother users.

                            Question to everyone: Does uploading a file bother you? Which do you prefer, string or file?

                            github ◊ vfm

                            iAN CooG ? 2 Replies Last reply Reply Quote 2
                            • iAN CooG
                              iAN CooG @luetage last edited by

                              @luetage I don't see the difference, a json IS a string 🙂

                              http://iancoog.altervista.org/
                              --=[]=-----------------------------------------------------------------------=[]=--
                              Windows10 64bits - 8core i9-9900K @ 3.60GHz - 16Gb RAM - nVidia GT1030

                              luetage 1 Reply Last reply Reply Quote 0
                              • luetage
                                luetage @iAN CooG last edited by

                                @iAN-CooG True, but currently you can only export the string to a file and import it from a file, therefore sharing a theme requires an upload in order for others to access and try it out comfortably.

                                github ◊ vfm

                                1 Reply Last reply Reply Quote 1
                                • ?
                                  A Former User @luetage last edited by

                                  @luetage said:

                                  Question to everyone: Does uploading a file bother you? Which do you prefer, string or file?

                                  I like strings because it's much easier and I can e. g. save multiple themes into one file.
                                  Another thing that bothers me is exporting/importing all themes with files. I have to click the button, then go through the save dialog, sometimes rename the file and confirm overwrite (when overwriting).

                                  I'll be also very happy with a button 'Export all' (if as string or file doesn't matter).

                                  luetage 1 Reply Last reply Reply Quote 1
                                  • luetage
                                    luetage @Guest last edited by

                                    @potmeklecbohdan Exporting all themes is likely very hard to do from an outside script. I'm sorry, but I don't see this happening.

                                    github ◊ vfm

                                    ? 1 Reply Last reply Reply Quote 0
                                    • ?
                                      A Former User @luetage last edited by

                                      @luetage I didn't expect you'll do that, it was only idea what to do if you (or somebody in V Team) are really bored and need something to do.

                                      luetage 1 Reply Last reply Reply Quote 0
                                      • Hadden89
                                        Hadden89 @luetage last edited by Hadden89

                                        @luetage said in Import and Export Themes:

                                        Feel free to share your own themes jester

                                        Done.

                                        Previews can be found here and here. wizard

                                        w11 20H2; Android 12; Vivaldi [RC] Snapshot User | Blog | Broken-Ext | DarkChromeCSS | Github | ScrollsMod |

                                        1 Reply Last reply Reply Quote 1
                                        • luetage
                                          luetage @Guest last edited by

                                          @potmeklecbohdan It's not about boredom, it's just far easier to do this from the inside. Vivaldi has access to all the theme variables, while I have to read them out individually. This would probably mean that I have to simulate click events to load every theme and then take each value and collect it – it's just not feasible. So yeah, I'm not gonna do it – it would be really ugly lol.

                                          I have no doubt that Vivaldi will come out with a solution one day. Themes in sync is bound to happen, it's a given. The question is if they will introduce a way to share themes.

                                          github ◊ vfm

                                          1 Reply Last reply Reply Quote 1
                                          • luetage
                                            luetage last edited by

                                            I implement the copy/pasting of theme code in the forum extension now. Should I do the same thing for importing/exporting themes?

                                            github ◊ vfm

                                            1 Reply Last reply Reply Quote 2
                                            Loading More Posts
                                            • Oldest to Newest
                                            • Newest to Oldest
                                            • Most Votes
                                            • Reply as topic
                                            Log in to reply
                                            • 1
                                            • 2
                                            • 3
                                            • 4
                                            • 5
                                            • 6
                                            • 7
                                            • 1 / 7
                                            • First post
                                              Last post

                                            Looks like your connection to Vivaldi Forum was lost, please wait while we try to reconnect.

                                            Copyright © Vivaldi Technologies™ — All rights reserved. Privacy Policy | Code of conduct | Terms of use | Vivaldi Status