1
0
mirror of https://github.com/amir20/dozzle.git synced 2025-12-21 21:33:18 +01:00
* Settings in WIP

* Updates some styles

* Removes unused import

* Adds version and switcher

* Adds ionicons instead of fontawesome

* Fixes ionicon for vuejs

* Updates modules

* Adds buefy

* Adds search filter as settings

* Adds localstorage

* Fixes tests

* Adds settings for menu width

* Changes copy
This commit is contained in:
Amir Raminfar
2019-12-29 11:12:46 -08:00
committed by GitHub
parent c7ce201050
commit f4987ff9c3
18 changed files with 367 additions and 64 deletions

View File

@@ -18,7 +18,8 @@ describe("<App />", () => {
containers: [ containers: [
{ id: "abc", name: "Test 1" }, { id: "abc", name: "Test 1" },
{ id: "xyz", name: "Test 2" } { id: "xyz", name: "Test 2" }
] ],
settings: { menuWidth: 15 }
}; };
const actions = { const actions = {

View File

@@ -1,13 +1,14 @@
<template lang="html"> <template lang="html">
<main> <main>
<mobile-menu v-if="isMobile"></mobile-menu> <mobile-menu v-if="isMobile"></mobile-menu>
<splitpanes> <splitpanes @resized="updateSetting({ menuWidth: $event[0].size })">
<pane min-size="10" size="15" v-if="!isMobile"> <pane min-size="10" :size="settings.menuWidth" v-if="!isMobile">
<side-menu></side-menu> <side-menu></side-menu>
</pane> </pane>
<pane :size="isMobile ? 100 : 85"> <pane :size="isMobile ? 100 : 100 - settings.menuWidth" min-size="10">
<splitpanes> <splitpanes>
<pane> <pane>
<search></search>
<router-view></router-view> <router-view></router-view>
</pane> </pane>
<pane v-for="other in activeContainers" :key="other.id"> <pane v-for="other in activeContainers" :key="other.id">
@@ -37,6 +38,7 @@ import LogViewerWithSource from "./components/LogViewerWithSource";
import ScrollableView from "./components/ScrollableView"; import ScrollableView from "./components/ScrollableView";
import SideMenu from "./components/SideMenu"; import SideMenu from "./components/SideMenu";
import MobileMenu from "./components/MobileMenu"; import MobileMenu from "./components/MobileMenu";
import Search from "./components/Search";
export default { export default {
name: "App", name: "App",
@@ -46,7 +48,8 @@ export default {
MobileMenu, MobileMenu,
ScrollableView, ScrollableView,
Splitpanes, Splitpanes,
Pane Pane,
Search
}, },
data() { data() {
return { return {
@@ -65,12 +68,13 @@ export default {
this.title = `${this.containers.length} containers`; this.title = `${this.containers.length} containers`;
}, },
computed: { computed: {
...mapState(["containers", "activeContainers", "isMobile"]) ...mapState(["containers", "activeContainers", "isMobile", "settings"])
}, },
methods: { methods: {
...mapActions({ ...mapActions({
fetchContainerList: "FETCH_CONTAINERS", fetchContainerList: "FETCH_CONTAINERS",
removeActiveContainer: "REMOVE_ACTIVE_CONTAINER" removeActiveContainer: "REMOVE_ACTIVE_CONTAINER",
updateSetting: "UPDATE_SETTING"
}) })
} }
}; };
@@ -91,4 +95,8 @@ export default {
background: rgb(255, 221, 87); background: rgb(255, 221, 87);
} }
} }
.button.has-no-border {
border-color: transparent !important;
}
</style> </style>

View File

@@ -18,7 +18,7 @@ exports[`<App /> renders correctly 1`] = `
<pane-stub <pane-stub
maxsize="100" maxsize="100"
minsize="0" minsize="10"
size="85" size="85"
> >
<splitpanes-stub <splitpanes-stub
@@ -29,6 +29,8 @@ exports[`<App /> renders correctly 1`] = `
maxsize="100" maxsize="100"
minsize="0" minsize="0"
> >
<search-stub />
<router-view-stub /> <router-view-stub />
</pane-stub> </pane-stub>

View File

@@ -34,7 +34,7 @@ describe("<LogEventSource />", () => {
localVue.component("log-event-source", LogEventSource); localVue.component("log-event-source", LogEventSource);
localVue.component("log-viewer", LogViewer); localVue.component("log-viewer", LogViewer);
const state = { searchFilter }; const state = { searchFilter, settings: { size: "medium" } };
const store = new Vuex.Store({ const store = new Vuex.Store({
state state
@@ -66,7 +66,7 @@ describe("<LogEventSource />", () => {
/> />
<ul <ul
class="events" class="events medium"
/> />
</div> </div>
`); `);
@@ -126,7 +126,7 @@ describe("<LogEventSource />", () => {
sources["/api/logs/stream?id=abc"].emitMessage({ data: `2019-06-12T10:55:42.459034602Z "This is a message."` }); sources["/api/logs/stream?id=abc"].emitMessage({ data: `2019-06-12T10:55:42.459034602Z "This is a message."` });
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(` expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
<ul class="events"> <ul class="events medium">
<li><span class="date">today at 10:55 AM</span> <span class="text">"This is a message."</span></li> <li><span class="date">today at 10:55 AM</span> <span class="text">"This is a message."</span></li>
</ul> </ul>
`); `);
@@ -140,7 +140,7 @@ describe("<LogEventSource />", () => {
}); });
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(` expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
<ul class="events"> <ul class="events medium">
<li><span class="date">today at 10:55 AM</span> <span class="text"><span style="color:#000">black<span style="color:#AAA">white</span></span></span></li> <li><span class="date">today at 10:55 AM</span> <span class="text"><span style="color:#000">black<span style="color:#AAA">white</span></span></span></li>
</ul> </ul>
`); `);
@@ -154,7 +154,7 @@ describe("<LogEventSource />", () => {
}); });
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(` expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
<ul class="events"> <ul class="events medium">
<li><span class="date">today at 10:55 AM</span> <span class="text">&lt;test&gt;foo bar&lt;/test&gt;</span></li> <li><span class="date">today at 10:55 AM</span> <span class="text">&lt;test&gt;foo bar&lt;/test&gt;</span></li>
</ul> </ul>
`); `);
@@ -171,7 +171,7 @@ describe("<LogEventSource />", () => {
}); });
expect(wrapper.find("ul.events")).toMatchInlineSnapshot(` expect(wrapper.find("ul.events")).toMatchInlineSnapshot(`
<ul class="events"> <ul class="events medium">
<li><span class="date">today at 10:55 AM</span> <span class="text">This is a <mark>test</mark> &lt;hi&gt;&lt;/hi&gt;</span></li> <li><span class="date">today at 10:55 AM</span> <span class="text">This is a <mark>test</mark> &lt;hi&gt;&lt;/hi&gt;</span></li>
</ul> </ul>
`); `);

View File

@@ -1,5 +1,5 @@
<template lang="html"> <template lang="html">
<ul class="events"> <ul class="events" :class="settings.size">
<li v-for="item in filtered" :key="item.key"> <li v-for="item in filtered" :key="item.key">
<span class="date">{{ item.date | relativeTime }}</span> <span class="date">{{ item.date | relativeTime }}</span>
<span class="text" v-html="colorize(item.message)"></span> <span class="text" v-html="colorize(item.message)"></span>
@@ -32,7 +32,7 @@ export default {
} }
}, },
computed: { computed: {
...mapState(["searchFilter"]), ...mapState(["searchFilter", "settings"]),
filtered() { filtered() {
const { searchFilter, messages } = this; const { searchFilter, messages } = this;
if (searchFilter) { if (searchFilter) {
@@ -69,9 +69,20 @@ export default {
font-family: "Roboto Mono", monaco, monospace; font-family: "Roboto Mono", monaco, monospace;
& > li { & > li {
font-size: 13px;
line-height: 16px;
word-wrap: break-word; word-wrap: break-word;
line-height: 130%;
}
&.small {
font-size: 60%;
}
&.medium {
font-size: 80%;
}
&.large {
font-size: 120%;
} }
} }
@@ -84,7 +95,7 @@ export default {
white-space: pre-wrap; white-space: pre-wrap;
} }
>>> mark { ::v-deep mark {
border-radius: 2px; border-radius: 2px;
background-color: #ffdd57; background-color: #ffdd57;
animation: pops 0.2s ease-out; animation: pops 0.2s ease-out;

View File

@@ -14,7 +14,7 @@
@click="scrollToBottom('smooth')" @click="scrollToBottom('smooth')"
v-show="paused" v-show="paused"
> >
<span class="icon large"> <i class="fas fa-chevron-down"></i> </span> <ion-icon name="download"></ion-icon>
</button> </button>
</transition> </transition>
</div> </div>

View File

@@ -1,9 +1,16 @@
<template lang="html"> <template lang="html">
<div class="search columns is-gapless is-vcentered" v-show="showSearch"> <div class="search columns is-gapless is-vcentered" v-show="showSearch" v-if="settings.search">
<div class="column"> <div class="column">
<p class="control has-icons-left"> <p class="control has-icons-left">
<input class="input" type="text" placeholder="Filter" ref="filter" v-model="filter" /> <input
<span class="icon is-small is-left"><i class="fas fa-search"></i></span> class="input"
type="text"
placeholder="Filter"
ref="filter"
v-model="filter"
@keyup.esc="resetSearch()"
/>
<span class="icon is-small is-left"><ion-icon name="search"></ion-icon></span>
</p> </p>
</div> </div>
<div class="column is-1 has-text-centered"> <div class="column is-1 has-text-centered">
@@ -14,6 +21,8 @@
<script> <script>
import { mapActions, mapState } from "vuex"; import { mapActions, mapState } from "vuex";
import hotkeys from "hotkeys-js";
export default { export default {
props: [], props: [],
name: "Search", name: "Search",
@@ -23,31 +32,26 @@ export default {
}; };
}, },
mounted() { mounted() {
window.addEventListener("keydown", this.onKeyDown); hotkeys("command+f, ctrl+f", (event, handler) => {
}, this.showSearch = true;
destroyed() { this.$nextTick(() => this.$refs.filter.focus() || this.$refs.filter.select());
window.removeEventListener("keydown", this.onKeyDown); event.preventDefault();
});
hotkeys("esc", (event, handler) => {
this.resetSearch();
});
}, },
methods: { methods: {
...mapActions({ ...mapActions({
updateSearchFilter: "SET_SEARCH" updateSearchFilter: "SET_SEARCH"
}), }),
onKeyDown(e) {
if ((e.metaKey || e.ctrlKey) && e.key === "f") {
this.showSearch = true;
this.$nextTick(() => this.$refs.filter.focus());
e.preventDefault();
} else if (e.key === "Escape") {
this.resetSearch();
}
},
resetSearch() { resetSearch() {
this.showSearch = false; this.showSearch = false;
this.filter = ""; this.filter = "";
} }
}, },
computed: { computed: {
...mapState(["searchFilter"]), ...mapState(["searchFilter", "settings"]),
filter: { filter: {
get() { get() {
return this.searchFilter; return this.searchFilter;

View File

@@ -1,6 +1,19 @@
<template lang="html"> <template lang="html">
<aside> <aside>
<h1 class="title has-text-warning is-marginless">Dozzle</h1> <div class="columns is-marginless">
<div class="column">
<h1 class="title has-text-warning is-marginless">Dozzle</h1>
</div>
<div class="column is-narrow has-text-right is-hidden-mobile">
<router-link
:to="{ name: 'settings' }"
active-class="is-active"
class="button is-small is-primary is-rounded is-inverted is-outlined "
>
<span class="icon"><ion-icon name="settings" size="large"></ion-icon></span>
</router-link>
</div>
</div>
<p class="menu-label is-hidden-mobile">Containers</p> <p class="menu-label is-hidden-mobile">Containers</p>
<ul class="menu-list is-hidden-mobile"> <ul class="menu-list is-hidden-mobile">
<li v-for="item in containers"> <li v-for="item in containers">
@@ -15,7 +28,7 @@
class="icon is-small will-append-container" class="icon is-small will-append-container"
:class="{ 'is-active': activeContainersById[item.id] }" :class="{ 'is-active': activeContainersById[item.id] }"
> >
<i class="fas fa-thumbtack"></i> <ion-icon name="ios-add-circle"></ion-icon>
</span> </span>
{{ item.name }} {{ item.name }}
</div> </div>
@@ -74,7 +87,6 @@ aside {
.will-append-container.icon { .will-append-container.icon {
transition: transform 0.2s ease-out; transition: transform 0.2s ease-out;
&.is-active { &.is-active {
transform: rotate(25deg);
pointer-events: none; pointer-events: none;
color: #00d1b2; color: #00d1b2;
} }

View File

@@ -7,11 +7,13 @@
<link href="https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono|Gafata" rel="stylesheet" /> <link href="https://fonts.googleapis.com/css?family=Roboto|Roboto+Mono|Gafata" rel="stylesheet" />
<link rel="manifest" href="manifest.webmanifest" /> <link rel="manifest" href="manifest.webmanifest" />
<link href="styles.scss" rel="stylesheet" /> <link href="styles.scss" rel="stylesheet" />
<link rel="icon" href="favicon.ico"> <link rel="icon" href="favicon.ico" />
<script> <script>
window["BASE_PATH"] = "{{ .Base }}"; window["BASE_PATH"] = "{{ .Base }}";
window["VERSION"] = "{{ .Version }}";
</script> </script>
<script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script> <script type="module" src="https://unpkg.com/ionicons@4.5.10-0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule="" src="https://unpkg.com/ionicons@4.5.10-0/dist/ionicons/ionicons.js"></script>
</head> </head>
<body class="is-dark"> <body class="is-dark">

View File

@@ -1,14 +1,19 @@
import Vue from "vue"; import Vue from "vue";
import VueRouter from "vue-router"; import VueRouter from "vue-router";
import Meta from "vue-meta"; import Meta from "vue-meta";
import Vuex from "vuex"; import { Dropdown, Switch } from "buefy";
import store from "./store"; import store from "./store";
import App from "./App.vue"; import App from "./App.vue";
import Container from "./pages/Container.vue"; import Container from "./pages/Container.vue";
import Settings from "./pages/Settings.vue";
import Index from "./pages/Index.vue"; import Index from "./pages/Index.vue";
Vue.use(VueRouter); Vue.use(VueRouter);
Vue.use(Meta); Vue.use(Meta);
Vue.use(Dropdown);
Vue.use(Switch);
Vue.config.ignoredElements = [/^ion-/];
const routes = [ const routes = [
{ {
@@ -21,6 +26,11 @@ const routes = [
component: Container, component: Container,
name: "container", name: "container",
props: true props: true
},
{
path: "/settings",
component: Settings,
name: "settings"
} }
]; ];

View File

@@ -1,20 +1,17 @@
<template lang="html"> <template lang="html">
<div> <div>
<search></search>
<scrollable-logs-with-source :id="id"></scrollable-logs-with-source> <scrollable-logs-with-source :id="id"></scrollable-logs-with-source>
</div> </div>
</template> </template>
<script> <script>
import ScrollableLogsWithSource from "../components/ScrollableLogsWithSource"; import ScrollableLogsWithSource from "../components/ScrollableLogsWithSource";
import Search from "../components/Search";
export default { export default {
props: ["id", "name"], props: ["id", "name"],
name: "Container", name: "Container",
components: { components: {
ScrollableLogsWithSource, ScrollableLogsWithSource
Search
}, },
metaInfo() { metaInfo() {
return { return {

143
assets/pages/Settings.vue Normal file
View File

@@ -0,0 +1,143 @@
<template lang="html">
<div class="is-fullheight">
<section class="section">
<div class="has-underline">
<h2 class="title is-4">About</h2>
</div>
<h2 class="title is-6 is-marginless">Version</h2>
<div>
You are using Dozzle <i>{{ currentVersion }}</i
>.
<span v-if="hasUpdate">
New version is available! Update to
<a :href="nextRelease.html_url" class="next-release">{{ nextRelease.name }}</a
>.
</span>
</div>
</section>
<section class="section">
<div class="has-underline">
<h2 class="title is-4">Display</h2>
</div>
<div class="item">
<b-switch v-model="search">
Enable searching with Dozzle using <code>command+f</code> or <code>ctrl+f</code>
</b-switch>
</div>
<div class="item">
<h2 class="title is-6 is-marginless">Font size</h2>
Modify the font size when viewing logs.
<br /><br />
<b-dropdown v-model="size" aria-role="list">
<button class="button is-primary" type="button" slot="trigger">
<span class="is-capitalized">{{ size }}</span>
<span class="icon"><ion-icon name="ios-arrow-down"></ion-icon></span>
</button>
<b-dropdown-item :value="value" aria-role="listitem" v-for="value in ['small', 'medium', 'large']">
<div class="media">
<span class="icon">
<ion-icon name="checkmark" v-if="value == size"></ion-icon>
</span>
<div class="media-content">
<h3 class="is-capitalized">{{ value }}</h3>
</div>
</div>
</b-dropdown-item>
</b-dropdown>
</div>
</section>
</div>
</template>
<script>
import gt from "semver/functions/gt";
import valid from "semver/functions/valid";
import { mapActions, mapState } from "vuex";
function computedSettings(names) {
return names.reduce((map, name) => {
map[name] = {
get() {
return this.settings[name];
},
set(value) {
this.updateSetting({ [name]: value });
}
};
return map;
}, {});
}
export default {
props: [],
name: "Settings",
components: {},
data() {
return {
currentVersion: VERSION,
nextRelease: null,
hasUpdate: false
};
},
async created() {
const releases = await (await fetch("https://api.github.com/repos/amir20/dozzle/releases")).json();
this.hasUpdate = gt(releases[0].tag_name, this.currentVersion);
this.nextRelease = releases[0];
},
metaInfo() {
return {
title: "Settings",
titleTemplate: "%s - Dozzle"
};
},
methods: {
...mapActions({
updateSetting: "UPDATE_SETTING"
})
},
computed: {
...mapState(["settings"]),
...computedSettings.bind(this)(["search", "size"])
}
};
</script>
<style lang="scss">
.is-fullheight {
min-height: 100vh;
}
.title {
color: #eee;
}
a.next-release {
text-decoration: underline;
color: #00d1b2;
&:hover {
text-decoration: none;
}
}
.section {
padding: 1rem 1.5rem;
}
.has-underline {
border-bottom: 1px solid #fff;
padding: 1em 0px;
margin-bottom: 1em;
}
.item {
padding: 1em 0;
}
code {
border-radius: 4px;
background-color: #444;
}
</style>

View File

@@ -1,15 +1,20 @@
import Vue from "vue"; import Vue from "vue";
import Vuex from "vuex"; import Vuex from "vuex";
import storage from "store/dist/store.modern";
import { DEFAULT_SETTINGS, DOZZLE_SETTINGS_KEY } from "./settings";
Vue.use(Vuex);
const mql = window.matchMedia("(max-width: 770px)"); const mql = window.matchMedia("(max-width: 770px)");
Vue.use(Vuex); storage.set(DOZZLE_SETTINGS_KEY, { ...DEFAULT_SETTINGS, ...storage.get(DOZZLE_SETTINGS_KEY) });
const state = { const state = {
containers: [], containers: [],
activeContainers: [], activeContainers: [],
searchFilter: null, searchFilter: null,
isMobile: mql.matches isMobile: mql.matches,
settings: storage.get(DOZZLE_SETTINGS_KEY)
}; };
const mutations = { const mutations = {
@@ -27,6 +32,10 @@ const mutations = {
}, },
SET_MOBILE_WIDTH(state, value) { SET_MOBILE_WIDTH(state, value) {
state.isMobile = value; state.isMobile = value;
},
UPDATE_SETTINGS(state, newValues) {
state.settings = { ...state.settings, ...newValues };
storage.set(DOZZLE_SETTINGS_KEY, state.settings);
} }
}; };
@@ -43,13 +52,15 @@ const actions = {
async FETCH_CONTAINERS({ commit }) { async FETCH_CONTAINERS({ commit }) {
const containers = await (await fetch(`${BASE_PATH}/api/containers.json`)).json(); const containers = await (await fetch(`${BASE_PATH}/api/containers.json`)).json();
commit("SET_CONTAINERS", containers); commit("SET_CONTAINERS", containers);
},
UPDATE_SETTING({ commit }, setting) {
commit("UPDATE_SETTINGS", setting);
} }
}; };
const getters = {}; const getters = {};
const es = new EventSource(`${BASE_PATH}/api/events/stream`); const es = new EventSource(`${BASE_PATH}/api/events/stream`);
es.addEventListener("containers-changed", e => setTimeout(() => store.dispatch("FETCH_CONTAINERS"), 1000), false); es.addEventListener("containers-changed", e => setTimeout(() => store.dispatch("FETCH_CONTAINERS"), 1000), false);
mql.addListener(e => store.commit("SET_MOBILE_WIDTH", e.matches)); mql.addListener(e => store.commit("SET_MOBILE_WIDTH", e.matches));
const store = new Vuex.Store({ const store = new Vuex.Store({

6
assets/store/settings.js Normal file
View File

@@ -0,0 +1,6 @@
export const DOZZLE_SETTINGS_KEY = "DOZZLE_SETTINGS";
export const DEFAULT_SETTINGS = {
search: true,
size: "medium",
menuWidth: 15
};

View File

@@ -3,8 +3,11 @@
$menu-item-active-background-color: hsl(171, 100%, 41%); $menu-item-active-background-color: hsl(171, 100%, 41%);
$menu-item-color: hsl(0, 6%, 87%); $menu-item-color: hsl(0, 6%, 87%);
@import "../node_modules/bulma/bulma.sass"; @import "~bulma";
@import "../node_modules/splitpanes/dist/splitpanes.css"; @import "../node_modules/splitpanes/dist/splitpanes.css";
@import "~buefy/src/scss/utils/_all";
@import "~buefy/src/scss/components/_dropdown";
@import "~buefy/src/scss/components/_switch";
.is-dark { .is-dark {
color: #ddd; color: #ddd;

View File

@@ -150,7 +150,10 @@ func (h *handler) index(w http.ResponseWriter, req *http.Request) {
path = base path = base
} }
data := struct{ Base string }{path} data := struct {
Base string
Version string
}{path, version}
err = tmpl.Execute(w, data) err = tmpl.Execute(w, data)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

107
package-lock.json generated
View File

@@ -153,6 +153,12 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true "dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
} }
} }
}, },
@@ -1202,6 +1208,12 @@
"lodash": "^4.17.13", "lodash": "^4.17.13",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
} }
} }
}, },
@@ -1331,6 +1343,12 @@
"lodash": "^4.17.13", "lodash": "^4.17.13",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
} }
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
} }
} }
}, },
@@ -2766,6 +2784,21 @@
"node-int64": "^0.4.0" "node-int64": "^0.4.0"
} }
}, },
"buefy": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/buefy/-/buefy-0.8.8.tgz",
"integrity": "sha512-kTUnroPBLm998KFZbeJuUgJV+nJbDUJxw1c8gzeJoe+Mve73Nb3hi6AZpgrIH8FtXmh5r8nMBYBqwN54EtPWXg==",
"requires": {
"bulma": "0.7.5"
},
"dependencies": {
"bulma": {
"version": "0.7.5",
"resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.5.tgz",
"integrity": "sha512-cX98TIn0I6sKba/DhW0FBjtaDpxTelU166pf7ICXpCCuplHWyu6C9LYZmL5PEsnePIeJaiorsTEzzNk3Tsm1hw=="
}
}
},
"buffer": { "buffer": {
"version": "4.9.1", "version": "4.9.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
@@ -3417,6 +3450,14 @@
"semver": "^5.5.0", "semver": "^5.5.0",
"shebang-command": "^1.2.0", "shebang-command": "^1.2.0",
"which": "^1.2.9" "which": "^1.2.9"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
} }
}, },
"crypto-browserify": { "crypto-browserify": {
@@ -4156,6 +4197,12 @@
"pseudomap": "^1.0.2", "pseudomap": "^1.0.2",
"yallist": "^2.1.2" "yallist": "^2.1.2"
} }
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
} }
} }
}, },
@@ -5585,6 +5632,11 @@
"integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
"dev": true "dev": true
}, },
"hotkeys-js": {
"version": "3.7.3",
"resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.7.3.tgz",
"integrity": "sha512-CSaeVPAKEEYNexYR35znMJnCqoofk7oqG/AOOqWow1qDT0Yxy+g+Y8Hs/LhGlsZaSJ7973YN6/N41LAr3t30QQ=="
},
"hsl-regex": { "hsl-regex": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz", "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
@@ -5774,6 +5826,14 @@
"resolve": "^1.10.0", "resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5", "semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1" "validate-npm-package-license": "^3.0.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
} }
}, },
"parse-json": { "parse-json": {
@@ -7783,6 +7843,12 @@
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"dev": true "dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
} }
} }
}, },
@@ -8133,6 +8199,14 @@
"semver": "^5.5.0", "semver": "^5.5.0",
"shellwords": "^0.1.1", "shellwords": "^0.1.1",
"which": "^1.3.0" "which": "^1.3.0"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
} }
}, },
"node-releases": { "node-releases": {
@@ -8178,6 +8252,14 @@
"is-builtin-module": "^1.0.0", "is-builtin-module": "^1.0.0",
"semver": "2 || 3 || 4 || 5", "semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1" "validate-npm-package-license": "^3.0.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
} }
}, },
"normalize-path": { "normalize-path": {
@@ -8754,6 +8836,12 @@
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true "dev": true
}, },
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"source-map": { "source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -10130,9 +10218,9 @@
} }
}, },
"sass": { "sass": {
"version": "1.23.7", "version": "1.24.0",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.23.7.tgz", "resolved": "https://registry.npmjs.org/sass/-/sass-1.24.0.tgz",
"integrity": "sha512-cYgc0fanwIpi0rXisGxl+/wadVQ/HX3RhpdRcjLdj2o2ye/sxUTpAxIhbmJy3PLQgRFbf6Pn8Jsrta2vdXcoOQ==", "integrity": "sha512-1TsPyMhLTx+9DLlmwg02iBW2p4poGA7LlkWJLpUY/XticFKNhPcx+l4FsIJLKl6oSUfXmAKpVljHEez1hwjqiw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chokidar": ">=2.0.0 <4.0.0" "chokidar": ">=2.0.0 <4.0.0"
@@ -10154,10 +10242,9 @@
} }
}, },
"semver": { "semver": {
"version": "5.6.0", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.1.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==", "integrity": "sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A=="
"dev": true
}, },
"semver-compare": { "semver-compare": {
"version": "1.0.0", "version": "1.0.0",
@@ -10535,9 +10622,9 @@
} }
}, },
"splitpanes": { "splitpanes": {
"version": "2.1.2", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-2.1.2.tgz", "resolved": "https://registry.npmjs.org/splitpanes/-/splitpanes-2.2.0.tgz",
"integrity": "sha512-IiVeC1wk7yVq4QZ3VTj6FbOTUJQmy/gFxpR1pDk67P+Pj8V0daUsPeoBNcKmcvJp2v3272GHSeLTNEyDCKRXSQ==" "integrity": "sha512-n87JXR3xNHC6Zrcvg1JGU4wgqOxV95bFShhHhGM/cvQVFxN2HRJuaVWvvwgQzd9cF+4UyXtjcreqkOxr5bPnoA=="
}, },
"sprintf-js": { "sprintf-js": {
"version": "1.0.3", "version": "1.0.3",

View File

@@ -25,10 +25,13 @@
"homepage": "https://github.com/amir20/dozzle#readme", "homepage": "https://github.com/amir20/dozzle#readme",
"dependencies": { "dependencies": {
"ansi-to-html": "^0.6.13", "ansi-to-html": "^0.6.13",
"buefy": "^0.8.8",
"bulma": "^0.8.0", "bulma": "^0.8.0",
"date-fns": "^2.8.1", "date-fns": "^2.8.1",
"hotkeys-js": "^3.7.3",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"splitpanes": "^2.1.2", "semver": "^7.1.1",
"splitpanes": "^2.2.0",
"store": "^2.0.12", "store": "^2.0.12",
"vue": "^2.6.11", "vue": "^2.6.11",
"vue-meta": "^2.3.1", "vue-meta": "^2.3.1",
@@ -52,7 +55,7 @@
"node-fetch": "^2.6.0", "node-fetch": "^2.6.0",
"parcel-bundler": "^1.12.4", "parcel-bundler": "^1.12.4",
"prettier": "^1.19.1", "prettier": "^1.19.1",
"sass": "^1.23.7", "sass": "^1.24.0",
"vue-hot-reload-api": "^2.3.4", "vue-hot-reload-api": "^2.3.4",
"vue-jest": "^3.0.5", "vue-jest": "^3.0.5",
"vue-template-compiler": "^2.6.11" "vue-template-compiler": "^2.6.11"