mirror of
https://github.com/Av0idD4rk/packmate.git
synced 2026-10-23 16:31:01 +00:00
Add dark mode
This commit is contained in:
parent
2a60cd29cc
commit
0b406890f9
3703
package-lock.json
generated
3703
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"bootstrap": "^4.6.2",
|
"bootstrap": "^4.6.2",
|
||||||
|
"bootstrap-darkmode": "^0.9.1",
|
||||||
"bootstrap-vue": "^2.23.1",
|
"bootstrap-vue": "^2.23.1",
|
||||||
"core-js": "^3.26.1",
|
"core-js": "^3.26.1",
|
||||||
"idb": "^5.0.8",
|
"idb": "^5.0.8",
|
||||||
@ -30,6 +31,8 @@
|
|||||||
"babel-eslint": "^10.1.0",
|
"babel-eslint": "^10.1.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"eslint-plugin-vue": "^6.2.2",
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
|
"node-sass": "^8.0.0",
|
||||||
|
"sass-loader": "^10.4.1",
|
||||||
"vue-template-compiler": "^2.7.14"
|
"vue-template-compiler": "^2.7.14"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|||||||
@ -307,3 +307,7 @@
|
|||||||
padding-top: 55px; /* Space for fixed navbar */
|
padding-top: 55px; /* Space for fixed navbar */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
@import "~bootstrap-darkmode/scss/darktheme";
|
||||||
|
</style>
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="packet" :class="{'packet-incoming': packet.incoming}">
|
<div :class="{'packet-incoming': packet.incoming, 'packet-outgoing': !packet.incoming}">
|
||||||
<div>#{{ packet.id }} at {{ dateToText(packet.timestamp) }}, {{ printPacketFlags(packet) }}
|
<div>#{{ packet.id }} at {{ dateToText(packet.timestamp) }}, {{ printPacketFlags(packet) }}
|
||||||
<button @click.prevent="copyRaw" class="btn btn-link">Copy HEX</button>
|
<button @click.prevent="copyRaw" class="btn btn-link">Copy HEX</button>
|
||||||
<button @click.prevent="copyText" class="btn btn-link">Copy text</button>
|
<button @click.prevent="copyText" class="btn btn-link">Copy text</button>
|
||||||
<button @click.prevent="copyPythonBytes" class="btn btn-link">Copy as Python bytes</button>
|
<button @click.prevent="copyPythonBytes" class="btn btn-link">Copy as Python bytes</button>
|
||||||
</div>
|
</div>
|
||||||
<p v-if="!this.$store.state.hexdumpMode"
|
<p v-if="!this.$store.state.hexdumpMode"
|
||||||
class="pt-2 pb-2 mb-3 border-bottom"
|
class="pt-2 pb-2 mb-3"
|
||||||
v-html="stringdata"/>
|
v-html="stringdata"/>
|
||||||
<p v-else
|
<p v-else
|
||||||
class="pt-2 pb-2 mb-3 border-bottom"
|
class="pt-2 pb-2 mb-3"
|
||||||
v-html="hexdata"/>
|
v-html="hexdata"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!--suppress JSUnresolvedVariable -->
|
<!--suppress JSUnresolvedVariable, JSDeprecatedSymbols -->
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'Packet',
|
name: 'Packet',
|
||||||
@ -176,21 +176,19 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.packet {
|
.packet-outgoing {
|
||||||
background: rgba(224, 246, 255, 0.4);
|
background: var(--response-packet-color);
|
||||||
box-shadow: 0 0 5px 5px rgba(224, 246, 255, 0.4);
|
box-shadow: 0 0 5px 5px var(--response-packet-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*noinspection CssUnusedSymbol*/
|
|
||||||
.packet-incoming {
|
.packet-incoming {
|
||||||
background: rgba(251, 233, 231, 0.4);
|
background: var(--request-packet-color);
|
||||||
box-shadow: 0 0 5px 5px rgba(251, 233, 231, 0.4);
|
box-shadow: 0 0 5px 5px var(--request-packet-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-family: "Ubuntu Mono", "Lucida Console", monospace;
|
font-family: "Ubuntu Mono", "Lucida Console", monospace;
|
||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
color: black;
|
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
padding-bottom: 5px;
|
padding-bottom: 5px;
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<nav class="col-sm-3 d-none d-sm-block bg-light sidebar">
|
<nav class="col-sm-3 d-none d-sm-block sidebar"
|
||||||
|
:class="this.$store.state.theme === 'light' ? 'bg-light' : 'bg-dark'">
|
||||||
<div class="m-2">
|
<div class="m-2">
|
||||||
<button type="button" class="btn btn-sm"
|
<button type="button" class="btn btn-sm"
|
||||||
:title="this.$store.state.pause ? 'Continue' : 'Pause new streams'"
|
:title="this.$store.state.pause ? 'Continue' : 'Pause new streams'"
|
||||||
|
|||||||
@ -127,12 +127,15 @@
|
|||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[data-theme=dark] .nav-link {
|
||||||
|
color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
/*noinspection CssUnusedSymbol*/
|
/*noinspection CssUnusedSymbol*/
|
||||||
.nav-link.active {
|
.nav-link.active {
|
||||||
color: #007BFF;
|
color: #007BFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*noinspection CssUnusedSymbol*/
|
|
||||||
.highlight {
|
.highlight {
|
||||||
background: rgba(220, 53, 69, 0.3);
|
background: rgba(220, 53, 69, 0.3);
|
||||||
box-shadow: 0 0 1px 1px rgba(255, 119, 182, 0.3);
|
box-shadow: 0 0 1px 1px rgba(255, 119, 182, 0.3);
|
||||||
@ -140,7 +143,10 @@
|
|||||||
|
|
||||||
.stream-item {
|
.stream-item {
|
||||||
transition: all .5s;
|
transition: all .5s;
|
||||||
border: 0;
|
box-shadow: 0 24px 3px -25px #4b4e51;
|
||||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
}
|
||||||
|
|
||||||
|
[data-theme=dark] .stream-item {
|
||||||
|
box-shadow: 0 24px 3px -25px #84898E;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
127
src/components/ThemeButton.vue
Normal file
127
src/components/ThemeButton.vue
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
@change="toggleTheme"
|
||||||
|
id="checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
class="switch-checkbox"
|
||||||
|
/>
|
||||||
|
<label for="checkbox" class="switch-label">
|
||||||
|
<span class="checkbox-emoji">🌙</span>
|
||||||
|
<span class="checkbox-emoji">☀️</span>
|
||||||
|
<div
|
||||||
|
class="switch-toggle"
|
||||||
|
:class="{ 'switch-toggle-checked': chosenTheme === 'dark' }"
|
||||||
|
></div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mounted() {
|
||||||
|
this.initTheme();
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
chosenTheme: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.theme || this.detectTheme();
|
||||||
|
},
|
||||||
|
|
||||||
|
set(theme) {
|
||||||
|
this.$store.commit('setTheme', theme);
|
||||||
|
this.displayTheme(theme);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
toggleTheme() {
|
||||||
|
const activeTheme = this.chosenTheme;
|
||||||
|
|
||||||
|
if (activeTheme === "light") {
|
||||||
|
this.chosenTheme = 'dark';
|
||||||
|
} else {
|
||||||
|
this.chosenTheme = 'light';
|
||||||
|
}
|
||||||
|
|
||||||
|
console.debug('Toggling theme from ', activeTheme, ' to ', this.chosenTheme);
|
||||||
|
},
|
||||||
|
|
||||||
|
initTheme() {
|
||||||
|
this.displayTheme(this.chosenTheme);
|
||||||
|
},
|
||||||
|
|
||||||
|
detectTheme() {
|
||||||
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
|
},
|
||||||
|
|
||||||
|
displayTheme(theme) {
|
||||||
|
document.body.setAttribute('data-theme', theme);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.switch-checkbox {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-label {
|
||||||
|
align-items: center;
|
||||||
|
background: var(--button-text-primary-color);
|
||||||
|
border: calc(var(--button-element-size) * 0.025) solid var(--button-accent-color);
|
||||||
|
border-radius: var(--button-element-size);
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
font-size: calc(var(--button-element-size) * 0.3);
|
||||||
|
height: calc(var(--button-element-size) * 0.35);
|
||||||
|
position: relative;
|
||||||
|
padding: calc(var(--button-element-size) * 0.1);
|
||||||
|
transition: background 0.5s ease;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: var(--button-element-size);
|
||||||
|
z-index: 1;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-toggle {
|
||||||
|
position: absolute;
|
||||||
|
background-color: var(--button-background-color-primary);
|
||||||
|
border-radius: 50%;
|
||||||
|
top: calc(var(--button-element-size) * 0.07);
|
||||||
|
left: calc(var(--button-element-size) * 0.07);
|
||||||
|
height: calc(var(--button-element-size) * 0.4);
|
||||||
|
width: calc(var(--button-element-size) * 0.4);
|
||||||
|
transform: translateX(0);
|
||||||
|
transition: transform 0.3s ease, background-color 0.5s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.switch-toggle-checked {
|
||||||
|
transform: translateX(calc(var(--button-element-size) * 0.6)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-emoji {
|
||||||
|
color: transparent;
|
||||||
|
text-shadow: 0 0 0 var(--button-background-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
>>> {
|
||||||
|
--button-element-size: 3rem;
|
||||||
|
|
||||||
|
--button-background-color-primary: #ebebeb;
|
||||||
|
--button-background-color-secondary: #fafafa;
|
||||||
|
--button-accent-color: #cacaca;
|
||||||
|
--button-text-primary-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme=dark] * {
|
||||||
|
--button-background-color-primary: #1e1e1e;
|
||||||
|
--button-background-color-secondary: #2d2d30;
|
||||||
|
--button-accent-color: #3f3f3f;
|
||||||
|
--button-text-primary-color: #ddd;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -8,6 +8,8 @@ Vue.use(Vuex);
|
|||||||
// noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols
|
// noinspection JSUnusedLocalSymbols,JSUnusedGlobalSymbols
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
|
theme: null,
|
||||||
|
|
||||||
hexdumpBlockSize: 16,
|
hexdumpBlockSize: 16,
|
||||||
hexdumpLineNumberBase: 10,
|
hexdumpLineNumberBase: 10,
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
@ -29,6 +31,7 @@ export default new Vuex.Store({
|
|||||||
currentServicesStreamsCount: {},
|
currentServicesStreamsCount: {},
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
|
setTheme: (s, p) => s.theme = p,
|
||||||
setHexdumpBlockSize: (s, p) => s.hexdumpBlockSize = p,
|
setHexdumpBlockSize: (s, p) => s.hexdumpBlockSize = p,
|
||||||
setHexdumpLineNumberBase: (s, p) => s.hexdumpLineNumberBase = p,
|
setHexdumpLineNumberBase: (s, p) => s.hexdumpLineNumberBase = p,
|
||||||
setPageSize: (s, p) => s.pageSize = p,
|
setPageSize: (s, p) => s.pageSize = p,
|
||||||
@ -64,6 +67,7 @@ export default new Vuex.Store({
|
|||||||
console.debug('Got mutation:', mutation);
|
console.debug('Got mutation:', mutation);
|
||||||
const mName = mutation?.type;
|
const mName = mutation?.type;
|
||||||
const process = mName !== 'toggleDisplayFavoritesOnly'
|
const process = mName !== 'toggleDisplayFavoritesOnly'
|
||||||
|
&& mName !== 'setTheme'
|
||||||
&& mName !== 'setServiceModalName'
|
&& mName !== 'setServiceModalName'
|
||||||
&& mName !== 'setServiceModalId'
|
&& mName !== 'setServiceModalId'
|
||||||
&& mName !== 'togglePause'
|
&& mName !== 'togglePause'
|
||||||
|
|||||||
@ -1,18 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<p>
|
<p>
|
||||||
<span style="background: rgba(224, 246, 255, 0.4); box-shadow: 0 0 5px 5px rgba(224, 246, 255, 0.4);">Response</span>,
|
<span style="background: var(--request-packet-color); box-shadow: 0 0 3px 3px var(--request-packet-color);">Request</span>,
|
||||||
<span style="background: rgba(251, 233, 231, 0.4); box-shadow: 0 0 5px 5px rgba(251, 233, 231, 0.4);">request</span>.
|
<span style="background: var(--response-packet-color); box-shadow: 0 0 3px 3px var(--response-packet-color);">response</span>.
|
||||||
|
<ThemeButton class="d-inline float-right" style="margin-top: -0.25rem" />
|
||||||
</p>
|
</p>
|
||||||
<Packet v-for="packet in packets"
|
<Packet v-for="packet in packets"
|
||||||
:key="packet.id"
|
:key="packet.id"
|
||||||
:packet="packet"/>
|
:packet="packet"/>
|
||||||
<infinite-loading @infinite="infiniteLoadingHandler" ref="infiniteLoader"/>
|
<infinite-loading @infinite="infiniteLoadingHandler" ref="infiniteLoader">
|
||||||
|
<span slot="no-results"></span>
|
||||||
|
</infinite-loading>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Packet from '../components/Packet';
|
import Packet from '../components/Packet';
|
||||||
|
import ThemeButton from '@/components/ThemeButton.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ContentStream',
|
name: 'ContentStream',
|
||||||
@ -77,7 +81,21 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
ThemeButton,
|
||||||
Packet,
|
Packet,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--response-packet-color: rgb(231, 248, 253);
|
||||||
|
--request-packet-color: rgb(254, 234, 231);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root [data-theme=dark] {
|
||||||
|
color-scheme: dark;
|
||||||
|
--response-packet-color: rgb(43, 83, 120);
|
||||||
|
--request-packet-color: rgb(52, 58, 64);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user