Compare commits

...

5 Commits

Author SHA1 Message Date
tiny-craft c6f1daed44 fix: cursor offset incorrect when prompt prefix contains multibyte char in cli 2023-10-31 11:55:34 +08:00
tiny-craft 929c4d2794 fix: window may flash when startup 2023-10-31 11:45:55 +08:00
tiny-craft 694214bedf feat: add ZSTD decode/encode support #59 2023-10-31 11:43:46 +08:00
tiny-craft ff8da4ca60 fix: long string overflow in value page
fix: border radius show and hide logic incorrect on Windows
2023-10-30 21:54:20 +08:00
tiny-craft 0143e8f52a perf: cropped icon size 2023-10-30 11:52:46 +08:00
23 changed files with 321 additions and 281 deletions

View File

@ -9,4 +9,5 @@ const DECODE_NONE = "None"
const DECODE_BASE64 = "Base64" const DECODE_BASE64 = "Base64"
const DECODE_GZIP = "GZip" const DECODE_GZIP = "GZip"
const DECODE_DEFLATE = "Deflate" const DECODE_DEFLATE = "Deflate"
const DECODE_ZSTD = "ZStd"
const DECODE_BROTLI = "Brotli" const DECODE_BROTLI = "Brotli"

View File

@ -1,9 +1,10 @@
package strutil package strutil
import "unicode/utf8" import (
"unicode"
)
func containsBinary(str string) bool { func containsBinary(str string) bool {
//buf := []byte(str)
//size := 0 //size := 0
//for start := 0; start < len(buf); start += size { //for start := 0; start < len(buf); start += size {
// var r rune // var r rune
@ -11,9 +12,11 @@ func containsBinary(str string) bool {
// return true // return true
// } // }
//} //}
rs := []rune(str)
if !utf8.ValidString(str) { for _, r := range rs {
return true if !unicode.IsPrint(r) {
return true
}
} }
return false return false
} }

View File

@ -2,14 +2,15 @@ package strutil
import ( import (
"bytes" "bytes"
"compress/flate"
"compress/gzip"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/andybalholm/brotli" "github.com/andybalholm/brotli"
"github.com/klauspost/compress/flate"
"github.com/klauspost/compress/gzip"
"github.com/klauspost/compress/zstd"
"io" "io"
"strconv" "strconv"
"strings" "strings"
@ -70,8 +71,17 @@ func decodeWith(str, decodeType string) (value, resultDecode string) {
return return
case types.DECODE_DEFLATE: case types.DECODE_DEFLATE:
if gzipStr, ok := decodeDeflate(str); ok { if falteStr, ok := decodeDeflate(str); ok {
value = gzipStr value = falteStr
} else {
value = str
}
resultDecode = decodeType
return
case types.DECODE_ZSTD:
if zstdStr, ok := decodeZStd(str); ok {
value = zstdStr
} else { } else {
value = str value = str
} }
@ -79,8 +89,8 @@ func decodeWith(str, decodeType string) (value, resultDecode string) {
return return
case types.DECODE_BROTLI: case types.DECODE_BROTLI:
if gzipStr, ok := decodeBrotli(str); ok { if brotliStr, ok := decodeBrotli(str); ok {
value = gzipStr value = brotliStr
} else { } else {
value = str value = str
} }
@ -112,6 +122,11 @@ func autoDecode(str string) (value, resultDecode string) {
return return
} }
if value, ok = decodeZStd(str); ok {
resultDecode = types.DECODE_ZSTD
return
}
if value, ok = decodeBrotli(str); ok { if value, ok = decodeBrotli(str); ok {
resultDecode = types.DECODE_BROTLI resultDecode = types.DECODE_BROTLI
return return
@ -244,6 +259,16 @@ func decodeDeflate(str string) (string, bool) {
return str, false return str, false
} }
func decodeZStd(str string) (string, bool) {
if reader, err := zstd.NewReader(strings.NewReader(str)); err == nil {
defer reader.Close()
if decompressed, err := io.ReadAll(reader); err == nil {
return string(decompressed), true
}
}
return str, false
}
func decodeBrotli(str string) (string, bool) { func decodeBrotli(str string) (string, bool) {
reader := brotli.NewReader(strings.NewReader(str)) reader := brotli.NewReader(strings.NewReader(str))
if decompressed, err := io.ReadAll(reader); err == nil { if decompressed, err := io.ReadAll(reader); err == nil {
@ -304,6 +329,14 @@ func SaveAs(str, viewType, decodeType string) (value string, err error) {
} }
return return
case types.DECODE_ZSTD:
if zstdStr, ok := encodeZStd(str); ok {
value = zstdStr
} else {
err = errors.New("fail to build zstd")
}
return
case types.DECODE_BROTLI: case types.DECODE_BROTLI:
if brotliStr, ok := encodeBrotli(str); ok { if brotliStr, ok := encodeBrotli(str); ok {
value = brotliStr value = brotliStr
@ -391,6 +424,26 @@ func encodeDeflate(str string) (string, bool) {
return str, false return str, false
} }
func encodeZStd(str string) (string, bool) {
var compress = func(b []byte) (string, error) {
var buf bytes.Buffer
writer, err := zstd.NewWriter(&buf)
if err != nil {
return "", err
}
if _, err = writer.Write([]byte(str)); err != nil {
writer.Close()
return "", err
}
writer.Close()
return string(buf.Bytes()), nil
}
if zstdStr, err := compress([]byte(str)); err == nil {
return zstdStr, true
}
return str, false
}
func encodeBrotli(str string) (string, bool) { func encodeBrotli(str string) (string, bool) {
var compress = func(b []byte) (string, error) { var compress = func(b []byte) (string, error) {
var buf bytes.Buffer var buf bytes.Buffer

View File

@ -69,6 +69,16 @@ const wrapperStyle = computed(() => {
borderRadius: '10px', borderRadius: '10px',
} }
}) })
const spinStyle = computed(() => {
return hideRadius.value
? {
backgroundColor: themeVars.value.bodyColor,
}
: {
backgroundColor: themeVars.value.bodyColor,
borderRadius: '10px',
}
})
const onToggleFullscreen = (fullscreen) => { const onToggleFullscreen = (fullscreen) => {
hideRadius.value = fullscreen hideRadius.value = fullscreen
@ -82,10 +92,14 @@ const onToggleFullscreen = (fullscreen) => {
const onToggleMaximize = (isMaximised) => { const onToggleMaximize = (isMaximised) => {
if (isMaximised) { if (isMaximised) {
maximised.value = true maximised.value = true
hideRadius.value = isMacOS() if (!isMacOS()) {
hideRadius.value = true
}
} else { } else {
maximised.value = false maximised.value = false
hideRadius.value = !isMacOS() if (!isMacOS()) {
hideRadius.value = false
}
} }
} }
@ -105,10 +119,7 @@ onMounted(async () => {
<template> <template>
<!-- app content--> <!-- app content-->
<n-spin <n-spin :show="props.loading" :style="spinStyle" :theme-overrides="{ opacitySpinning: 0 }">
:show="props.loading"
:style="{ backgroundColor: themeVars.bodyColor }"
:theme-overrides="{ opacitySpinning: 0 }">
<div id="app-content-wrapper" :style="wrapperStyle" class="flex-box-v"> <div id="app-content-wrapper" :style="wrapperStyle" class="flex-box-v">
<!-- title bar --> <!-- title bar -->
<div <div
@ -156,7 +167,7 @@ onMounted(async () => {
style="--wails-draggable: none"> style="--wails-draggable: none">
<nav-menu v-model:value="tabStore.nav" :width="data.navMenuWidth" /> <nav-menu v-model:value="tabStore.nav" :width="data.navMenuWidth" />
<!-- browser page --> <!-- browser page -->
<div v-show="tabStore.nav === 'browser'" class="flex-box-h flex-item-expand"> <div v-show="tabStore.nav === 'browser'" class="content-area flex-box-h flex-item-expand">
<resizeable-wrapper <resizeable-wrapper
v-model:size="prefStore.behavior.asideWidth" v-model:size="prefStore.behavior.asideWidth"
:min-size="300" :min-size="300"
@ -178,7 +189,7 @@ onMounted(async () => {
</div> </div>
<!-- server list page --> <!-- server list page -->
<div v-show="tabStore.nav === 'server'" class="flex-box-h flex-item-expand"> <div v-show="tabStore.nav === 'server'" class="content-area flex-box-h flex-item-expand">
<resizeable-wrapper <resizeable-wrapper
v-model:size="prefStore.behavior.asideWidth" v-model:size="prefStore.behavior.asideWidth"
:min-size="300" :min-size="300"
@ -191,7 +202,7 @@ onMounted(async () => {
</div> </div>
<!-- log page --> <!-- log page -->
<div v-show="tabStore.nav === 'log'" class="flex-box-h flex-item-expand"> <div v-show="tabStore.nav === 'log'" class="content-area flex-box-h flex-item-expand">
<content-log-pane ref="logPaneRef" class="flex-item-expand" /> <content-log-pane ref="logPaneRef" class="flex-item-expand" />
</div> </div>
</div> </div>
@ -229,6 +240,10 @@ onMounted(async () => {
#app-content { #app-content {
height: calc(100% - 60px); height: calc(100% - 60px);
.content-area {
overflow: hidden;
}
} }
.app-side { .app-side {

View File

@ -11,6 +11,8 @@ import Log from '@/components/icons/Log.vue'
import Detail from '@/components/icons/Detail.vue' import Detail from '@/components/icons/Detail.vue'
import ContentValueWrapper from '@/components/content_value/ContentValueWrapper.vue' import ContentValueWrapper from '@/components/content_value/ContentValueWrapper.vue'
import ContentCli from '@/components/content_value/ContentCli.vue' import ContentCli from '@/components/content_value/ContentCli.vue'
import Monitor from '@/components/icons/Monitor.vue'
import Pub from '@/components/icons/Pub.vue'
const themeVars = useThemeVars() const themeVars = useThemeVars()
@ -106,7 +108,10 @@ watch(
<template #tab> <template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center"> <n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16"> <n-icon size="16">
<status :inverse="selectedSubTab === BrowserTabType.Status.toString()" stroke-width="4" /> <status
:inverse="selectedSubTab === BrowserTabType.Status.toString()"
:stroke-color="themeVars.tabColor"
stroke-width="4" />
</n-icon> </n-icon>
<span>{{ $t('interface.sub_tab.status') }}</span> <span>{{ $t('interface.sub_tab.status') }}</span>
</n-space> </n-space>
@ -121,7 +126,8 @@ watch(
<n-icon size="16"> <n-icon size="16">
<detail <detail
:inverse="selectedSubTab === BrowserTabType.KeyDetail.toString()" :inverse="selectedSubTab === BrowserTabType.KeyDetail.toString()"
fill-color="none" /> :stroke-color="themeVars.tabColor"
stroke-width="4" />
</n-icon> </n-icon>
<span>{{ $t('interface.sub_tab.key_detail') }}</span> <span>{{ $t('interface.sub_tab.key_detail') }}</span>
</n-space> </n-space>
@ -146,7 +152,10 @@ watch(
<template #tab> <template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center"> <n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16"> <n-icon size="16">
<terminal :inverse="selectedSubTab === BrowserTabType.Cli.toString()" /> <terminal
:inverse="selectedSubTab === BrowserTabType.Cli.toString()"
:stroke-color="themeVars.tabColor"
stroke-width="4" />
</n-icon> </n-icon>
<span>{{ $t('interface.sub_tab.cli') }}</span> <span>{{ $t('interface.sub_tab.cli') }}</span>
</n-space> </n-space>
@ -155,16 +164,49 @@ watch(
</n-tab-pane> </n-tab-pane>
<!-- slow log pane --> <!-- slow log pane -->
<n-tab-pane :name="BrowserTabType.SlowLog.toString()"> <n-tab-pane :disabled="true" :name="BrowserTabType.SlowLog.toString()">
<template #tab> <template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center"> <n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16"> <n-icon size="16">
<log :inverse="selectedSubTab === BrowserTabType.SlowLog.toString()" /> <log
:inverse="selectedSubTab === BrowserTabType.SlowLog.toString()"
:stroke-color="themeVars.tabColor"
stroke-width="4" />
</n-icon> </n-icon>
<span>{{ $t('interface.sub_tab.slow_log') }}</span> <span>{{ $t('interface.sub_tab.slow_log') }}</span>
</n-space> </n-space>
</template> </template>
</n-tab-pane> </n-tab-pane>
<!-- command monitor pane -->
<n-tab-pane :disabled="true" :name="BrowserTabType.CmdMonitor.toString()">
<template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16">
<monitor
:inverse="selectedSubTab === BrowserTabType.CmdMonitor.toString()"
:stroke-color="themeVars.tabColor"
stroke-width="4" />
</n-icon>
<span>{{ $t('interface.sub_tab.cmd_monitor') }}</span>
</n-space>
</template>
</n-tab-pane>
<!-- pub/sub message pane -->
<n-tab-pane :disabled="true" :name="BrowserTabType.PubMessage.toString()">
<template #tab>
<n-space :size="5" :wrap-item="false" align="center" inline justify="center">
<n-icon size="16">
<pub
:inverse="selectedSubTab === BrowserTabType.PubMessage.toString()"
:stroke-color="themeVars.tabColor"
stroke-width="4" />
</n-icon>
<span>{{ $t('interface.sub_tab.pub_message') }}</span>
</n-space>
</template>
</n-tab-pane>
</n-tabs> </n-tabs>
</div> </div>
</template> </template>

View File

@ -4,7 +4,7 @@ import { FitAddon } from 'xterm-addon-fit'
import { computed, defineExpose, onMounted, onUnmounted, ref, watch } from 'vue' import { computed, defineExpose, onMounted, onUnmounted, ref, watch } from 'vue'
import 'xterm/css/xterm.css' import 'xterm/css/xterm.css'
import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js' import { EventsEmit, EventsOff, EventsOn } from 'wailsjs/runtime/runtime.js'
import { get, isEmpty, set, size } from 'lodash' import { get, isEmpty, set } from 'lodash'
import { CloseCli, StartCli } from 'wailsjs/go/services/cliService.js' import { CloseCli, StartCli } from 'wailsjs/go/services/cliService.js'
import usePreferencesStore from 'stores/preferences.js' import usePreferencesStore from 'stores/preferences.js'
import { i18nGlobal } from '@/utils/i18n.js' import { i18nGlobal } from '@/utils/i18n.js'
@ -102,6 +102,21 @@ const prefixContent = computed(() => {
return '\x1b[33m' + promptPrefix.value + '\x1b[0m' return '\x1b[33m' + promptPrefix.value + '\x1b[0m'
}) })
const prefixLen = computed(() => {
let len = 0
for (let i = 0; i < promptPrefix.value.length; i++) {
const char = promptPrefix.value.charCodeAt(i)
if (char >= 0x0000 && char <= 0x00ff) {
// single byte ASCII char
len += 1
} else {
// multibyte Unicode char
len += 2
}
}
return len
})
let promptPrefix = ref('') let promptPrefix = ref('')
let inputCursor = 0 let inputCursor = 0
const inputHistory = [] const inputHistory = []
@ -193,7 +208,7 @@ const moveInputCursor = (step) => {
} }
if (updateCursor) { if (updateCursor) {
termInst.write(`\x1B[${size(promptPrefix.value) + inputCursor + 1}G`) termInst.write(`\x1B[${prefixLen.value + inputCursor + 1}G`)
} }
} }

View File

@ -183,7 +183,7 @@ const onSaveValue = async () => {
</div> </div>
<div class="value-wrapper value-item-part flex-item-expand flex-box-v"> <div class="value-wrapper value-item-part flex-item-expand flex-box-v">
<n-scrollbar v-if="!inEdit" class="flex-item-expand"> <n-scrollbar v-if="!inEdit" class="flex-item-expand">
<n-code :code="props.value" :language="viewLanguage" show-line-numbers style="cursor: text" word-wrap /> <n-code :code="props.value" :language="viewLanguage" style="cursor: text" word-wrap />
</n-scrollbar> </n-scrollbar>
<n-input <n-input
v-else v-else

View File

@ -14,10 +14,10 @@ const props = defineProps({
</script> </script>
<template> <template>
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect <rect
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
fill="currentColor"
height="8" height="8"
stroke="currentColor" stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
@ -26,8 +26,8 @@ const props = defineProps({
x="4" x="4"
y="34" /> y="34" />
<rect <rect
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
fill="currentColor"
height="12" height="12"
stroke="currentColor" stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
@ -48,8 +48,8 @@ const props = defineProps({
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<rect <rect
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
fill="currentColor"
height="8" height="8"
stroke="currentColor" stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
@ -58,8 +58,8 @@ const props = defineProps({
x="36" x="36"
y="34" /> y="34" />
<rect <rect
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
fill="currentColor"
height="8" height="8"
stroke="currentColor" stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
@ -68,69 +68,9 @@ const props = defineProps({
x="20" x="20"
y="34" /> y="34" />
<path <path
:stroke="props.inverse ? '#FFF' : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M14 12H16" d="M14 12H16"
stroke="#FFF"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="8"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
width="8"
x="4"
y="34" />
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="12"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
width="32"
x="8"
y="6" />
<path
:stroke-width="props.strokeWidth"
d="M24 34V18"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M8 34V26H40V34"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="8"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
width="8"
x="36"
y="34" />
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="8"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
width="8"
x="20"
y="34" />
<path
:stroke-width="props.strokeWidth"
d="M14 12H16"
stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
</svg> </svg>

View File

@ -14,7 +14,7 @@ const props = defineProps({
</script> </script>
<template> <template>
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path <path
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M44.0001 11C44.0001 11 44 36.0623 44 38C44 41.3137 35.0457 44 24 44C12.9543 44 4.00003 41.3137 4.00003 38C4.00003 36.1423 4 11 4 11" d="M44.0001 11C44.0001 11 44 36.0623 44 38C44 41.3137 35.0457 44 24 44C12.9543 44 4.00003 41.3137 4.00003 38C4.00003 36.1423 4 11 4 11"
@ -34,40 +34,10 @@ const props = defineProps({
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<ellipse <ellipse
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
cx="24" cx="24"
cy="10" cy="10"
fill="currentColor"
rx="20"
ry="6"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="props.strokeWidth"
d="M44.0001 11C44.0001 11 44 36.0623 44 38C44 41.3137 35.0457 44 24 44C12.9543 44 4.00003 41.3137 4.00003 38C4.00003 36.1423 4 11 4 11"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M44 29C44 32.3137 35.0457 35 24 35C12.9543 35 4 32.3137 4 29"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M44 20C44 23.3137 35.0457 26 24 26C12.9543 26 4 23.3137 4 20"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<ellipse
:stroke-width="props.strokeWidth"
cx="24"
cy="10"
fill="none"
rx="20" rx="20"
ry="6" ry="6"
stroke="currentColor" stroke="currentColor"

View File

@ -10,14 +10,18 @@ const props = defineProps({
type: [Number, String], type: [Number, String],
default: 3, default: 3,
}, },
strokeColor: {
type: String,
default: '#FFF',
},
}) })
</script> </script>
<template> <template>
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect <rect
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
fill="currentColor"
height="36" height="36"
rx="3" rx="3"
stroke="currentColor" stroke="currentColor"
@ -26,81 +30,36 @@ const props = defineProps({
x="6" x="6"
y="6" /> y="6" />
<rect <rect
:fill="props.inverse ? props.strokeColor : 'none'"
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
fill="#FFF"
height="8" height="8"
stroke="#FFF"
stroke-linejoin="round" stroke-linejoin="round"
width="8" width="8"
x="13" x="13"
y="13" /> y="13" />
<path <path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M27 13L35 13" d="M27 13L35 13"
stroke="#FFF"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M27 20L35 20" d="M27 20L35 20"
stroke="#FFF"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M13 28L35 28" d="M13 28L35 28"
stroke="#FFF"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M13 35H35" d="M13 35H35"
stroke="#FFF"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="36"
rx="3"
stroke="currentColor"
stroke-linejoin="round"
width="36"
x="6"
y="6" />
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="8"
stroke="currentColor"
stroke-linejoin="round"
width="8"
x="13"
y="13" />
<path
:stroke-width="props.strokeWidth"
d="M27 13L35 13"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M27 20L35 20"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M13 28L35 28"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M13 35H35"
stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
</svg> </svg>

View File

@ -8,17 +8,21 @@ const props = defineProps({
type: [Number, String], type: [Number, String],
default: 3, default: 3,
}, },
strokeColor: {
type: String,
default: '#FFF',
},
}) })
</script> </script>
<template> <template>
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect <rect
fill="currentColor" :fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth"
height="34" height="34"
stroke="currentColor" stroke="currentColor"
stroke-linejoin="round" stroke-linejoin="round"
stroke-width="3"
width="28" width="28"
x="13" x="13"
y="10" /> y="10" />
@ -28,35 +32,16 @@ const props = defineProps({
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" stroke-linejoin="round"
stroke-width="3" /> stroke-width="3" />
<path d="M21 22H33" stroke="#FFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" />
<path d="M21 30H33" stroke="#FFF" stroke-linecap="round" stroke-linejoin="round" stroke-width="3" />
</svg>
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect
:stroke-width="strokeWidth"
fill="none"
height="34"
stroke="currentColor"
stroke-linejoin="round"
width="28"
x="13"
y="10" />
<path <path
:stroke-width="strokeWidth" :stroke="props.inverse ? props.strokeColor : 'currentColor'"
d="M35 10V4H8C7.44772 4 7 4.44772 7 5V38H13" :stroke-width="props.strokeWidth"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="strokeWidth"
d="M21 22H33" d="M21 22H33"
stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path
:stroke-width="strokeWidth" :stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth"
d="M21 30H33" d="M21 30H33"
stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
</svg> </svg>

View File

@ -0,0 +1,40 @@
<script setup>
const props = defineProps({
inverse: {
type: Boolean,
default: false,
},
strokeWidth: {
type: [Number, String],
default: 3,
},
strokeColor: {
type: String,
default: '#FFF',
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth"
height="36"
rx="3"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
width="36"
x="6"
y="6" />
<path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth"
d="M12 25H15L19 14L22 36L27 23L31 29L34 25H37"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,46 @@
<script setup>
const props = defineProps({
inverse: {
type: Boolean,
default: false,
},
strokeWidth: {
type: [Number, String],
default: 3,
},
strokeColor: {
type: String,
default: '#FFF',
},
})
</script>
<template>
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="props.strokeWidth"
d="M33 38H22V30H36V22H44V38H39L36 41L33 38Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth"
d="M4 6H36V30H17L13 34L9 30H4V6Z"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth"
d="M12 22H18"
stroke-linecap="round" />
<path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth"
d="M12 14H24"
stroke-linecap="round" />
</svg>
</template>
<style lang="scss" scoped></style>

View File

@ -14,59 +14,29 @@ const props = defineProps({
</script> </script>
<template> <template>
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path <path
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M41 4H7C5.34315 4 4 5.34315 4 7V41C4 42.6569 5.34315 44 7 44H41C42.6569 44 44 42.6569 44 41V7C44 5.34315 42.6569 4 41 4Z" d="M41 4H7C5.34315 4 4 5.34315 4 7V41C4 42.6569 5.34315 44 7 44H41C42.6569 44 44 42.6569 44 41V7C44 5.34315 42.6569 4 41 4Z"
fill="currentColor"
stroke="currentColor" stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path :stroke-width="props.strokeWidth" d="M4 32H44" stroke="#FFF" stroke-linecap="round" />
<path <path
:stroke="props.inverse ? '#FFF' : 'currentColor'"
:stroke-width="props.strokeWidth"
d="M4 32H44"
stroke-linecap="round" />
<path
:stroke="props.inverse ? '#FFF' : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M10 38H11" d="M10 38H11"
stroke="#FFF"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path
:stroke="props.inverse ? '#FFF' : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M26 38H38" d="M26 38H38"
stroke="#FFF"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M44 37V27"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M4 37V27"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path
:stroke-width="props.strokeWidth"
d="M41 4H7C5.34315 4 4 5.34315 4 7V41C4 42.6569 5.34315 44 7 44H41C42.6569 44 44 42.6569 44 41V7C44 5.34315 42.6569 4 41 4Z"
fill="none"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path :stroke-width="props.strokeWidth" d="M4 32H44" stroke="currentColor" stroke-linecap="round" />
<path
:stroke-width="props.strokeWidth"
d="M10 38H11"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M26 38H38"
stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path

View File

@ -8,6 +8,10 @@ const props = defineProps({
type: [Number, String], type: [Number, String],
default: 3, default: 3,
}, },
strokeColor: {
type: String,
default: '#FFF',
},
}) })
</script> </script>
@ -18,9 +22,9 @@ const props = defineProps({
fill="currentColor" fill="currentColor"
stroke="currentColor" stroke="currentColor"
stroke-width="3" /> stroke-width="3" />
<path :stroke-width="props.strokeWidth" d="M24 17V31" stroke="#FFF" stroke-linecap="round" /> <path :stroke="props.strokeColor" :stroke-width="props.strokeWidth" d="M24 17V31" stroke-linecap="round" />
<path :stroke-width="props.strokeWidth" d="M32 24V31" stroke="#FFF" stroke-linecap="round" /> <path :stroke="props.strokeColor" :stroke-width="props.strokeWidth" d="M32 24V31" stroke-linecap="round" />
<path :stroke-width="props.strokeWidth" d="M16 22V31" stroke="#FFF" stroke-linecap="round" /> <path :stroke="props.strokeColor" :stroke-width="props.strokeWidth" d="M16 22V31" stroke-linecap="round" />
</svg> </svg>
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect <rect

View File

@ -10,14 +10,18 @@ const props = defineProps({
type: [Number, String], type: [Number, String],
default: 3, default: 3,
}, },
strokeColor: {
type: String,
default: '#FFF',
},
}) })
</script> </script>
<template> <template>
<svg v-if="props.inverse" fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg"> <svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect <rect
:fill="props.inverse ? 'currentColor' : 'none'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
fill="currentColor"
height="32" height="32"
rx="2" rx="2"
stroke="currentColor" stroke="currentColor"
@ -26,39 +30,15 @@ const props = defineProps({
x="4" x="4"
y="8" /> y="8" />
<path <path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M12 18L19 24L12 30" d="M12 18L19 24L12 30"
stroke="#FFF"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
<path <path
:stroke="props.inverse ? props.strokeColor : 'currentColor'"
:stroke-width="props.strokeWidth" :stroke-width="props.strokeWidth"
d="M23 32H36" d="M23 32H36"
stroke="#FFF"
stroke-linecap="round"
stroke-linejoin="round" />
</svg>
<svg v-else fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<rect
:stroke-width="props.strokeWidth"
fill="none"
height="32"
rx="2"
stroke="currentColor"
stroke-linejoin="round"
width="40"
x="4"
y="8" />
<path
:stroke-width="props.strokeWidth"
d="M12 18L19 24L12 30"
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round" />
<path
:stroke-width="props.strokeWidth"
d="M23 32H36"
stroke="currentColor"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="round" /> stroke-linejoin="round" />
</svg> </svg>

View File

@ -7,4 +7,6 @@ export const BrowserTabType = {
KeyDetail: 'key_detail', KeyDetail: 'key_detail',
Cli: 'cli', Cli: 'cli',
SlowLog: 'slow_log', SlowLog: 'slow_log',
CmdMonitor: 'cmd_monitor',
PubMessage: 'pub_message',
} }

View File

@ -5,6 +5,8 @@
export const formatTypes = { export const formatTypes = {
PLAIN_TEXT: 'Plain Text', PLAIN_TEXT: 'Plain Text',
JSON: 'JSON', JSON: 'JSON',
// XML: 'XML',
// YML: 'YML',
HEX: 'Hex', HEX: 'Hex',
BINARY: 'Binary', BINARY: 'Binary',
} }
@ -18,6 +20,7 @@ export const decodeTypes = {
BASE64: 'Base64', BASE64: 'Base64',
GZIP: 'GZip', GZIP: 'GZip',
DEFLATE: 'Deflate', DEFLATE: 'Deflate',
ZSTD: 'ZStd',
BROTLI: 'Brotli', BROTLI: 'Brotli',
// PHP: 'PHP', // PHP: 'PHP',
// Java: 'Java', // Java: 'Java',

View File

@ -101,8 +101,10 @@
"sub_tab": { "sub_tab": {
"status": "Status", "status": "Status",
"key_detail": "Key Detail", "key_detail": "Key Detail",
"cli": "Command Line", "cli": "Console",
"slow_log": "Slow Log" "slow_log": "Slow Log",
"cmd_monitor": "Monitor Commands",
"pub_message": "Pub/Sub"
} }
}, },
"ribbon": { "ribbon": {

View File

@ -102,7 +102,9 @@
"status": "状态", "status": "状态",
"key_detail": "键详情", "key_detail": "键详情",
"cli": "命令行", "cli": "命令行",
"slow_log": "慢日志" "slow_log": "慢日志",
"cmd_monitor": "监控命令",
"pub_message": "推送/订阅"
} }
}, },
"ribbon": { "ribbon": {

3
go.mod
View File

@ -6,6 +6,7 @@ require (
github.com/adrg/sysfont v0.1.2 github.com/adrg/sysfont v0.1.2
github.com/andybalholm/brotli v1.0.6 github.com/andybalholm/brotli v1.0.6
github.com/google/uuid v1.3.1 github.com/google/uuid v1.3.1
github.com/klauspost/compress v1.17.2
github.com/redis/go-redis/v9 v9.2.1 github.com/redis/go-redis/v9 v9.2.1
github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68 github.com/vrischmann/userdir v0.0.0-20151206171402-20f291cebd68
github.com/wailsapp/wails/v2 v2.6.0 github.com/wailsapp/wails/v2 v2.6.0
@ -33,7 +34,7 @@ require (
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/rivo/uniseg v0.4.4 // indirect github.com/rivo/uniseg v0.4.4 // indirect
github.com/samber/lo v1.38.1 // indirect github.com/samber/lo v1.38.1 // indirect
github.com/stretchr/testify v1.8.3 // indirect github.com/stretchr/testify v1.8.4 // indirect
github.com/tkrajina/go-reflector v0.5.6 // indirect github.com/tkrajina/go-reflector v0.5.6 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect

6
go.sum
View File

@ -27,6 +27,8 @@ github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e h1:Q3+PugElBCf4PFpxhErSzU3/PY5sFL5Z6rfv4AbGAck=
github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs= github.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4=
github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
@ -71,8 +73,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE= github.com/tkrajina/go-reflector v0.5.6 h1:hKQ0gyocG7vgMD2M3dRlYN6WBBOmdoOzJ6njQSepKdE=
github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4= github.com/tkrajina/go-reflector v0.5.6/go.mod h1:ECbqLgccecY5kPmPmXg1MrHW585yMcDkVl6IvJe64T4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=

View File

@ -10,6 +10,7 @@ import (
"github.com/wailsapp/wails/v2/pkg/options/linux" "github.com/wailsapp/wails/v2/pkg/options/linux"
"github.com/wailsapp/wails/v2/pkg/options/mac" "github.com/wailsapp/wails/v2/pkg/options/mac"
"github.com/wailsapp/wails/v2/pkg/options/windows" "github.com/wailsapp/wails/v2/pkg/options/windows"
runtime2 "github.com/wailsapp/wails/v2/pkg/runtime"
"runtime" "runtime"
"tinyrdm/backend/consts" "tinyrdm/backend/consts"
"tinyrdm/backend/services" "tinyrdm/backend/services"
@ -54,6 +55,7 @@ func main() {
Assets: assets, Assets: assets,
}, },
BackgroundColour: options.NewRGBA(27, 38, 54, 0), BackgroundColour: options.NewRGBA(27, 38, 54, 0),
StartHidden: true,
OnStartup: func(ctx context.Context) { OnStartup: func(ctx context.Context) {
sysSvc.Start(ctx) sysSvc.Start(ctx)
connSvc.Start(ctx) connSvc.Start(ctx)
@ -62,6 +64,9 @@ func main() {
services.GA().SetSecretKey(gaMeasurementID, gaSecretKey) services.GA().SetSecretKey(gaMeasurementID, gaSecretKey)
services.GA().Startup(version) services.GA().Startup(version)
}, },
OnDomReady: func(ctx context.Context) {
runtime2.WindowShow(ctx)
},
OnShutdown: func(ctx context.Context) { OnShutdown: func(ctx context.Context) {
connSvc.Stop() connSvc.Stop()
cliSvc.CloseAll() cliSvc.CloseAll()