Browse Source

Implement theming support and add MetroMumbleDark theme

Jonas Herzig 5 years ago
parent
commit
ca83780109

+ 10 - 0
README.md

@@ -86,8 +86,18 @@ map $http_upgrade $connection_upgrade {
 }
 ```
 
+### Themes
+The default theme of mumble-web tries to mimic the excellent [MetroMumble]Light theme.
+mumble-web also includes a dark version, named MetroMumbleDark, which is heavily inspired by [MetroMumble]'s dark version.
+
+To select a theme other than the default one, append a `theme=dark` query parameter (where `dark` is the name of the theme) when accessing the mumble-web page.
+E.g. [this](https://voice.johni0702.de/?address=voice.johni0702.de&port=443/demo&theme=dark)is the live demo linked above but using the dark theme (`dark` is an alias for `MetroMumbleDark`).
+
+Custom themes can be created by deriving them from the MetroMumbleLight/Dark themes just like the MetroMumbleDark theme is derived from the MetroMumbleLight theme.
+
 ### License
 ISC
 
 [Mumble]: https://wiki.mumble.info/wiki/Main_Page
 [websockify GitHub page]: https://github.com/novnc/websockify
+[MetroMumble]: https://github.com/xPoke/MetroMumble

+ 1 - 2
app/index.html

@@ -13,7 +13,7 @@
     <meta name="msapplication-config" content="${require('./favicon/browserconfig.xml')}">
     <meta name="theme-color" content="#ffffff">
 
-    <link rel="stylesheet" type="text/css" href="/loading.css">
+    <script src="theme.js"></script>
 
     <script src="matrix.js"></script>
   </head>
@@ -340,6 +340,5 @@
       </div>
     </div>
   </body>
-  <link rel="stylesheet" type="text/css" href="/main.css">
   <script src="index.js"></script>
 </html>

+ 33 - 0
app/theme.js

@@ -0,0 +1,33 @@
+import url from 'url'
+
+var queryParams = url.parse(document.location.href, true).query
+var theme = queryParams.theme || window.localStorage.getItem('mumble.theme')
+var themes = {
+  'MetroMumbleLight': 'MetroMumbleLight',
+  'MetroMumbleDark': 'MetroMumbleDark',
+  'light': 'MetroMumbleLight',
+  'dark': 'MetroMumbleDark'
+}
+theme = themes[theme] || 'MetroMumbleLight'
+window.theme = theme
+
+var [loadingTheme, mainTheme] = {
+  'MetroMumbleLight': [
+    require('../themes/MetroMumbleLight/loading.scss'),
+    require('../themes/MetroMumbleLight/main.scss')
+  ],
+  'MetroMumbleDark': [
+    require('../themes/MetroMumbleDark/loading.scss'),
+    require('../themes/MetroMumbleDark/main.scss')
+  ]
+}[theme]
+
+function useStyle (url) {
+  var style = document.createElement('link')
+  style.rel = 'stylesheet'
+  style.type = 'text/css'
+  style.href = url
+  document.getElementsByTagName('head')[0].appendChild(style)
+}
+useStyle(loadingTheme)
+useStyle(mainTheme)

+ 2 - 0
package.json

@@ -36,8 +36,10 @@
     "knockout": "^3.4.0",
     "lodash.assign": "^4.2.0",
     "microphone-stream": "^3.0.5",
+    "node-sass": "^4.9.3",
     "raw-loader": "^0.5.1",
     "regexp-replace-loader": "0.0.1",
+    "sass-loader": "^4.1.1",
     "stream-chunker": "^1.2.8",
     "transform-loader": "^0.2.3",
     "voice-activity-detection": "johni0702/voice-activity-detection#9f8bd90",

+ 5 - 0
themes/MetroMumbleDark/loading.scss

@@ -0,0 +1,5 @@
+$bg-color: #2c2c2c !default
+$spinner-color: #888 !default
+$spinner-bg-color: #222 !default
+
+@import '../MetroMumbleLight/loading';

+ 22 - 0
themes/MetroMumbleDark/main.scss

@@ -0,0 +1,22 @@
+$black: #bbb !default
+$darkgray: #777 !default
+$gray: #555 !default
+$lightgray: #555 !default
+$bg-color: #2c2c2c !default
+$white: #1c1c1c !default
+
+$lightblue: #557 !default
+
+$toolbar-hover-bg-color: $lightblue !default
+$toolbar-hover-border-color: $lightblue !default
+$toolbar-active-bg-color: $gray !default
+$toolbar-active-border-color: $gray !default
+$dialog-color: $black !default
+$channel-selected-bg-color: $lightblue !default
+
+@import '../MetroMumbleLight/main';
+
+.dialog-header {
+  color: #000;
+  font-weight: bold;
+}

+ 7 - 3
themes/MetroMumbleLight/loading.css → themes/MetroMumbleLight/loading.scss

@@ -1,9 +1,13 @@
+$bg-color: #eee !default
+$spinner-color: #999 !default
+$spinner-bg-color: #ddd !default
+
 .loading-container {
   position: absolute;
   top: 0;
   width: 100%;
   height: 100%;
-  background-color: #eee;
+  background-color: $bg-color;
   z-index: 1000;
 }
 
@@ -15,8 +19,8 @@
   top: calc(50% - 40px);
   left: calc(50% - 40px);
   border-radius: 100%;
-  border: 10px solid #ddd;
-  border-top-color: #999;
+  border: 10px solid $spinner-bg-color;
+  border-top-color: $spinner-color;
   animation: spin 1s infinite linear;
 }
 

+ 87 - 41
themes/MetroMumbleLight/main.css → themes/MetroMumbleLight/main.scss

@@ -1,5 +1,50 @@
+$black: #000 !default
+$darkgray: #888 !default
+$gray: #a9a9a9 !default
+$lightgray: #d3d3d3 !default
+$bg-color: #eee !default
+$white: #fff !default
+
+$font-color: $black !default
+$panel-bg-color: $white !default
+$panel-border-color: $lightgray !default
+$channel-tree-color: $lightgray !default
+$channel-hover-bg-color: $lightgray !default
+$channel-selected-bg-color: lightblue !default
+$channel-selected-border-color: $darkgray !default
+$tooltip-border-color: $darkgray !default
+$chat-channel-color: orange !default
+$chat-user-color: green !default
+$chat-input-color: $font-color !default
+$mic-volume-border-color: $black !default
+
+$toolbar-hover-bg-color: $lightgray !default
+$toolbar-hover-border-color: $gray !default
+$toolbar-active-bg-color: $white !default
+$toolbar-active-border-color: $toolbar-hover-bg-color !default
+$toolbar-divider-color: $lightgray !default
+$dialog-header-color: $white !default
+$dialog-header-bg-color: $darkgray !default
+$dialog-header-border-bottom-color: $gray !default
+$dialog-bg-color: $bg-color !default
+$dialog-border-color: $darkgray !default
+$dialog-color: $font-color !default
+$dialog-button-border-color: $darkgray !default
+$dialog-button-bg-color: $white !default
+$dialog-button-color: $dialog-color !default
+$dialog-input-border-color: $darkgray !default
+$dialog-input-bg-color: $white !default
+$dialog-input-color: $dialog-color !default
+
+$tooltip-bg-color: $panel-bg-color !default
+$channels-bg-color: $panel-bg-color !default
+$channels-border-color: $panel-border-color !default
+$chat-bg-color: $panel-bg-color !default
+$chat-border-color: $panel-border-color !default
+
 html, body {
-  background-color: #eee;
+  background-color: $bg-color;
+  color: $font-color;
   margin: 0;
   overflow: hidden;
   height: 100%
@@ -10,8 +55,8 @@ html, body {
 .channel-root-container {
   text-size: 16px;
   margin-left: 2px;
-  background-color: white;
-  border: 1px solid lightgray;
+  background-color: $channels-bg-color;
+  border: 1px solid $channels-border-color;
   float: left;
   border-radius: 3px;
   overflow-x: hidden;
@@ -44,10 +89,10 @@ html, body {
   height: calc(98% - 4px);
 }
 .log {
-  background-color: white;
+  background-color: $chat-bg-color;
   height: calc(100% - 42px);
   padding: 5px;
-  border: 1px lightgray solid;
+  border: 1px $chat-border-color solid;
   border-radius: 3px;
   overflow-x: hidden;
   overflow-y: scroll;
@@ -59,8 +104,7 @@ html, body {
   float: left;
   padding-top: 3px;
   padding-bottom: 3px;
-  background-color: white;
-  margin-right:
+  background-color: $channels-bg-color;
 }
 .channel-sub {
   margin-left: 9px;
@@ -68,7 +112,7 @@ html, body {
   padding-left: 9px;
 }
 .channel-wrapper:nth-last-child(n + 2) > .branch:not(:empty) + .channel-sub {
-  border-left: 1px lightgray solid;
+  border-left: 1px $channel-tree-color solid;
 }
 .channel-tree,
 .user-wrapper {
@@ -84,8 +128,8 @@ html, body {
   display: block;
   position: relative;
   width: 9px;
-  border-left: 1px lightgray solid;
-  border-bottom: 1px lightgray solid;
+  border-left: 1px $channel-tree-color solid;
+  border-bottom: 1px $channel-tree-color solid;
   height: 14px;
 }
 .channel-wrapper:nth-last-child(n + 2) > .channel-tree:after,
@@ -94,7 +138,7 @@ html, body {
   display: block;
   position: relative;
   width: 0px;
-  border-left: 1px lightgray solid;
+  border-left: 1px $channel-tree-color solid;
   height: 14px;
 }
 .user {
@@ -109,12 +153,12 @@ html, body {
   border: 1px solid transparent;
 }
 .selected {
-  background-color: lightblue !important;
-  border: 1px solid gray;
+  background-color: $channel-selected-bg-color !important;
+  border: 1px solid $channel-selected-border-color;
   border-radius: 3px;
 }
 .user:hover,.channel:hover {
-  background-color: lightgray;
+  background-color: $channel-hover-bg-color;
 }
 .thisClient {
   font-weight: bold
@@ -142,8 +186,8 @@ html, body {
 .tooltip {
   visibility: hidden;
   height: 0px;
-  background: white;
-  border: 1px solid gray;
+  background: $tooltip-bg-color;
+  border: 1px solid $tooltip-border-color;
   margin-top: 16px;
   margin-left: 30px;
   padding: 10px;
@@ -162,12 +206,12 @@ html, body {
   border-radius: 3px;
 }
 .toolbar img:hover {
-  border: 1px solid #bbb;
-  background-color: #ddd;
+  border: 1px solid $toolbar-hover-bg-color;
+  background-color: $toolbar-hover-border-color;
 }
 .toolbar .tb-active {
-  border: 1px solid #bbb;
-  background-color: white;
+  border: 1px solid $toolbar-active-bg-color;
+  background-color: $toolbar-active-border-color;
 }
 .toolbar-horizontal {
   flex-direction: row;
@@ -195,16 +239,16 @@ html, body {
 }
 .toolbar-horizontal .divider {
   height: 32px;
-  border-left: 1px lightgray solid;
+  border-left: 1px $toolbar-divider-color solid;
 }
 .toolbar-vertical .divider {
   width: 32px;
-  border-top: 1px lightgray solid;
+  border-top: 1px $toolbar-divider-color solid;
 }
 .toolbar-horizontal .handle-horizontal {
   width: auto !important;
   border: none !important;
-  background-color: #eee !important;
+  background-color: $bg-color !important;
 }
 .toolbar-horizontal .handle-vertical {
   display: none;
@@ -212,7 +256,7 @@ html, body {
 .toolbar-vertical .handle-vertical {
   height: auto !important;
   border: none !important;
-  background-color: #eee !important;
+  background-color: $bg-color !important;
 }
 .toolbar-vertical .handle-horizontal {
   display: none;
@@ -222,16 +266,17 @@ html, body {
 }
 .channel-tag {
   font-weight: bold;
-  color: orange;
+  color: $chat-channel-color;
 }
 .user-tag {
   font-weight: bold;
-  color: green;
+  color: $chat-user-color;
 }
 #message-box {
   width: 100%;
   border: none;
   background: none;
+  color: $chat-input-color;
   margin: 5px 0 5px 0;
   padding: 0;
   height: 20px;
@@ -251,9 +296,9 @@ form {
   width: calc(100% - 10px);
   padding: 5px;
   text-align: center;
-  color: white;
-  background-color: gray;
-  border-bottom: 1px solid darkgray;
+  color: $dialog-header-color;
+  background-color: $dialog-header-bg-color;
+  border-bottom: 1px solid $dialog-header-border-bottom-color;
 }
 .dialog-footer {
   position: absolute;
@@ -270,10 +315,10 @@ form {
 .dialog-close, .dialog-submit {
   width: 45%;
   font-size: 15px;
-  border: 1px gray solid;
+  border: 1px $dialog-button-border-color solid;
   border-radius: 3px;
-  background-color: white;
-  color: black;
+  background-color: $dialog-button-bg-color;
+  color: $dialog-button-color;
   padding: 1px;
 }
 .connect-dialog table {
@@ -282,8 +327,9 @@ form {
 }
 .dialog {
   position: absolute;
-  background-color: #eee;
-  border: 1px gray solid;
+  background-color: $dialog-bg-color;
+  color: $dialog-color;
+  border: 1px $dialog-border-color solid;
   box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.25);
   z-index: 20;
 }
@@ -309,7 +355,7 @@ form {
 }
 .settings-dialog .mic-volume-container {
   height: 10px;
-  border: 3px solid black;
+  border: 3px solid $mic-volume-border-color;
 }
 .settings-dialog .mic-volume {
   height: 100%;
@@ -335,19 +381,19 @@ form {
 }
 .connect-dialog input[type=text] {
   font-size: 15px;
-  border: 1px gray solid;
+  border: 1px $dialog-input-border-color solid;
   border-radius: 3px;
-  background-color: white;
-  color: black;
+  background-color: $dialog-input-bg-color;
+  color: $dialog-input-color;
   padding: 2px;
   width: calc(100% - 8px);
 }
 .connect-dialog input[type=password] {
   font-size: 15px;
-  border: 1px gray solid;
+  border: 1px $dialog-input-border-color solid;
   border-radius: 3px;
-  background-color: white;
-  color: black;
+  background-color: $dialog-input-bg-color;
+  color: $dialog-input-color;
   padding: 2px;
   width: calc(100% - 8px);
 }

+ 10 - 0
webpack.config.js

@@ -8,6 +8,7 @@ module.exports = {
       './app/index.js',
       './app/index.html'
     ],
+    theme: './app/theme.js',
     matrix: './app/matrix.js'
   },
   output: {
@@ -52,6 +53,15 @@ module.exports = {
         ]
       },
       {
+        test: /\.scss$/,
+        loaders: [
+          'file-loader?name=[hash].css',
+          'extract-loader',
+          'css-loader',
+          'sass-loader'
+        ]
+      },
+      {
         test: /manifest\.json$|\.xml$/,
         loaders: [
           'file-loader',