• Community
    • Community
    • Vivaldi Social
    • Blogs
  • Forum
    • Vivaldi Forum
    • Categories
    • Recent
    • Popular
  • Themes
    • Vivaldi Themes
    • My Themes
    • FAQ
  • Contribute
    • Contribute
    • Volunteer
    • Donate
  • Browser
    • Vivaldi Browser
    • Latest News
    • Snapshots
    • Help
Register Login

Vivaldi

  • Community
  • Themes
  • Contribute
  • Browser

Navigation

    • Home
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    1. Home
    2. Desktop
    3. Customizations & Extensions
    4. Modifications
    5. Import and Export Themes

    Import and Export Themes

    Scheduled Pinned Locked Moved Modifications
    javascriptmoddingarchived
    135 Posts 19 Posters 30.4k Views 18 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • luetage
      L
      luetage Supporters Soprano
      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
      I
      burbuja
      B
      2 Replies Last reply
      Reply Quote 33
    • iAN CooG
      I
      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
      L
      1 Reply Last reply
      Reply Quote 1
    • luetage
      L
      luetage Supporters Soprano @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
      L
      luetage Supporters Soprano
      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
      B
      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
      L
      1 Reply Last reply
      Reply Quote 1
    • luetage
      L
      luetage Supporters Soprano @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
      I
      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
      L
      luetage Supporters Soprano
      last edited by luetage

      wait a minute, I just said nonsense

      github ◊ vfm

      1 Reply Last reply Reply Quote 0
    • luetage
      L
      luetage Supporters Soprano
      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
      H
      Hadden89
      H
      2 Replies Last reply
      Reply Quote 4
    • iAN CooG
      I
      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
      H
      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
      L
      1 Reply Last reply
      Reply Quote 2
    • luetage
      L
      luetage Supporters Soprano @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
      I
      ?
      2 Replies Last reply
      Reply Quote 2
    • iAN CooG
      I
      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
      L
      1 Reply Last reply
      Reply Quote 0
    • luetage
      L
      luetage Supporters Soprano @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
      L
      1 Reply Last reply
      Reply Quote 1
    • luetage
      L
      luetage Supporters Soprano @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
      L
      1 Reply Last reply
      Reply Quote 0
    • Hadden89
      H
      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

      Vivaldi Stable+Snap | Patience Is The Key To Get The Vivaldi Spree | Unsupported Extensions | Github | windows 11 | Manjaro KDE | Q4OS Trinity | Android 13

      1 Reply Last reply Reply Quote 1
    • luetage
      L
      luetage Supporters Soprano @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
      L
      luetage Supporters Soprano
      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
    • Reply as topic
    Log in to reply
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1 / 7
    • First post
      Last post

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