diff --git a/src/main/resources/org/arl/fjage/web/shell/index.html b/src/main/resources/org/arl/fjage/web/shell/index.html index 96373d69..faf25626 100644 --- a/src/main/resources/org/arl/fjage/web/shell/index.html +++ b/src/main/resources/org/arl/fjage/web/shell/index.html @@ -1,3 +1,12 @@ +<!-- fjåge Web Shell + +# API + +- shellready : will be true when the shell is ready to accept input +- hideModeToggle : if true, the mode toggle button will be hidden +- window.postMessage({theme: 'dark'}) : to set the theme to 'dark' mode + +--> <html> <head> <title>fjåge shell</title> @@ -36,66 +45,13 @@ right: 20px; z-index: 100; } - .tgl { - display: none; - } - .tgl, .tgl:after, .tgl:before, .tgl *, .tgl *:after, .tgl *:before, .tgl + .tgl-btn { - box-sizing: border-box; - } - .tgl::-moz-selection, .tgl:after::-moz-selection, .tgl:before::-moz-selection, .tgl *::-moz-selection, .tgl *:after::-moz-selection, .tgl *:before::-moz-selection, .tgl + .tgl-btn::-moz-selection { - background: none; - } - .tgl::selection, .tgl:after::selection, .tgl:before::selection, .tgl *::selection, .tgl *:after::selection, .tgl *:before::selection, .tgl + .tgl-btn::selection { - background: none; - } - .tgl + .tgl-btn { - outline: 0; - display: block; - width: 3.2em; - height: 1.5em; - position: relative; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - .tgl + .tgl-btn:after, .tgl + .tgl-btn:before { - position: relative; - display: block; - content: ""; - width: 40%; - height: 100%; - } - .tgl + .tgl-btn:after { - left: 0; - } - .tgl + .tgl-btn:before { - display: none; - } - .tgl:checked + .tgl-btn:after { - left: 60%; - } - .tgl-flat + .tgl-btn { - padding: 2px; - transition: all 0.2s ease; - background: #fff; - border: 4px solid #dfdfdf; - border-radius: 2em; - } - .tgl-flat + .tgl-btn:after { - transition: all 0.2s ease; - background: #dfdfdf; - content: ""; - border-radius: 1em; - } - .tgl-flat:checked + .tgl-btn { - border: 4px solid #8d8d8d; - } - .tgl-flat:checked + .tgl-btn:after { - left: 60%; - background: #8d8d8d; + button.modeToggle { + background-color: transparent; + border: none; + cursor: pointer; + padding: 0; + margin: 0; } </style> </head> @@ -103,21 +59,24 @@ <div class="container"> <div id='terminal'></div> <div class="btn-container" title="Toggle Light/Dark ColorMode"> - <input class="tgl tgl-flat" id="darkmode-tgl" type="checkbox" checked/> - <label class="tgl-btn" for="darkmode-tgl"></label> + <button class="modeToggle" id="darkmode-tgl"> + <svg id="darkmode-dark" style="display: none;" data-v-bd832875="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="icon" width="24px" height="24px" viewBox="0 0 24 24"><path fill="white" d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2h.1A6.98 6.98 0 0 0 10 7m-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938A7.999 7.999 0 0 0 4 12"></path></svg> + <svg id="darkmode-light" style="display: none;" data-v-bd832875="" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="icon" width="24px" height="24px" viewBox="0 0 24 24"><path fill="currentColor" d="M12 18a6 6 0 1 1 0-12a6 6 0 0 1 0 12m0-2a4 4 0 1 0 0-8a4 4 0 0 0 0 8M11 1h2v3h-2zm0 19h2v3h-2zM3.515 4.929l1.414-1.414L7.05 5.636L5.636 7.05zM16.95 18.364l1.414-1.414l2.121 2.121l-1.414 1.414zm2.121-14.85l1.414 1.415l-2.121 2.121l-1.414-1.414zM5.636 16.95l1.414 1.414l-2.121 2.121l-1.414-1.414zM23 11v2h-3v-2zM4 11v2H1v-2z"></path></svg> + </button> </div> </div> <script> const ignoreKeys = ['Meta-R', 'Meta-Shift-R']; - var defaultCursorWidth; - var rtime = 0; - var resizing = false; - var delta = 200; - var fitAddon; + let defaultCursorWidth; + let rtime = 0; + let resizing = false; + let delta = 200; + let fitAddon; + let currentTheme = null; window.shellready = false; function connectSocket(term, url, path){ const ws = new WebSocket('ws://' + url + path); - var attachAddon; + let attachAddon; window.ws = ws; ws.onerror = () => reconnectSocket(term, attachAddon, url, path, ws) ws.onclose = () => reconnectSocket(term, attachAddon, url, path, ws) @@ -160,6 +119,9 @@ cursor: '#586e75', selection: '#d3c494' }); + document.getElementById('darkmode-dark').style.display = 'none'; + document.getElementById('darkmode-light').style.display = 'block'; + currentTheme = theme; }else if (theme == 'dark'){ term.setOption('theme', { background: '#000000', @@ -171,6 +133,9 @@ red: '#cc0000', cursor: '#ff6e75' }); + document.getElementById('darkmode-dark').style.display = 'block'; + document.getElementById('darkmode-light').style.display = 'none'; + currentTheme = theme; } }; function resizeend() { @@ -182,9 +147,17 @@ } } window.addEventListener('load', () => { + let hideModeToggle = false; + if (window.parent.document.documentElement.classList.contains('dark')){ + currentTheme = 'dark'; + hideModeToggle = true; + }else if (window.parent.document.documentElement.classList.contains('light')){ + currentTheme = 'light'; + hideModeToggle = true; + } + hideModeToggle = hideModeToggle || window.parent.hideModeToggle; const darkmodeBtn = document.getElementById('darkmode-tgl'); - var lightmode = false; - var userTheme = false; + let userTheme = false; const term = new Terminal(); fitAddon = new FitAddon.FitAddon(); @@ -195,21 +168,29 @@ fitAddon.fit(); defaultCursorWidth = term.getOption("cursorWidth"); - if (window.matchMedia){ - lightmode = !window.matchMedia('(prefers-color-scheme: dark)').matches; - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { - if (userTheme) return; - const newColorScheme = e.matches ? "dark" : "light"; - darkmodeBtn.checked = !!e.matches; - setTheme(term, newColorScheme); + if (currentTheme == null && window.matchMedia) currentTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + + if (hideModeToggle){ + darkmodeBtn.parentElement.style.display = 'none'; + window.addEventListener('message', evt => { + if (evt.data && evt.data.theme){ + setTheme(term, evt.data.theme); + } }); + }else { + if (window.matchMedia){ + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => { + if (userTheme) return; + const newColorScheme = e.matches ? "dark" : "light"; + setTheme(term, newColorScheme); + }); + } + darkmodeBtn.addEventListener('click', evt => { + setTheme(term, currentTheme == 'dark' ? 'light' : 'dark'); + userTheme = true; + }) } - darkmodeBtn.checked = !lightmode; - setTheme(term, lightmode ? 'light' : 'dark'); - darkmodeBtn.addEventListener('change', evt => { - setTheme(term, evt.target.checked ? 'dark' : 'light'); - userTheme = true; - }) + setTheme(term, currentTheme); const urlParams = new URLSearchParams(window.location.search); const url = urlParams.get('url') || window.location.hostname + ':' + window.location.port;;