feat(connections): copy to clipboard on right click

This commit is contained in:
kunish 2023-09-03 17:32:08 +08:00
parent 3f90a81615
commit 58afb5fce2
No known key found for this signature in database
GPG Key ID: 647A12B4F782C430
5 changed files with 89 additions and 67 deletions

View File

@ -21,6 +21,7 @@
"@solid-primitives/keyed": "^1.2.0", "@solid-primitives/keyed": "^1.2.0",
"@solid-primitives/media": "^2.2.5", "@solid-primitives/media": "^2.2.5",
"@solid-primitives/storage": "^2.1.1", "@solid-primitives/storage": "^2.1.1",
"@solid-primitives/timer": "^1.3.7",
"@solid-primitives/websocket": "^1.1.0", "@solid-primitives/websocket": "^1.1.0",
"@solidjs/router": "^0.8.3", "@solidjs/router": "^0.8.3",
"@tabler/icons-solidjs": "^2.32.0", "@tabler/icons-solidjs": "^2.32.0",
@ -28,6 +29,7 @@
"@tanstack/solid-virtual": "3.0.0-beta.6", "@tanstack/solid-virtual": "3.0.0-beta.6",
"@thisbeyond/solid-dnd": "^0.7.4", "@thisbeyond/solid-dnd": "^0.7.4",
"@types/byte-size": "^8.1.0", "@types/byte-size": "^8.1.0",
"@types/lodash": "^4.14.197",
"@types/node": "^20.5.8", "@types/node": "^20.5.8",
"@types/uuid": "^9.0.3", "@types/uuid": "^9.0.3",
"@typescript-eslint/eslint-plugin": "^6.5.0", "@typescript-eslint/eslint-plugin": "^6.5.0",
@ -45,6 +47,7 @@
"is-ip": "^5.0.1", "is-ip": "^5.0.1",
"ky": "^1.0.0", "ky": "^1.0.0",
"lint-staged": "^14.0.1", "lint-staged": "^14.0.1",
"lodash": "^4.17.21",
"prettier": "^3.0.3", "prettier": "^3.0.3",
"prettier-plugin-organize-imports": "^3.2.3", "prettier-plugin-organize-imports": "^3.2.3",
"prettier-plugin-tailwindcss": "^0.5.4", "prettier-plugin-tailwindcss": "^0.5.4",

View File

@ -35,6 +35,9 @@ dependencies:
'@solid-primitives/storage': '@solid-primitives/storage':
specifier: ^2.1.1 specifier: ^2.1.1
version: 2.1.1(solid-js@1.7.11) version: 2.1.1(solid-js@1.7.11)
'@solid-primitives/timer':
specifier: ^1.3.7
version: 1.3.7(solid-js@1.7.11)
'@solid-primitives/websocket': '@solid-primitives/websocket':
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0(solid-js@1.7.11) version: 1.1.0(solid-js@1.7.11)
@ -56,6 +59,9 @@ dependencies:
'@types/byte-size': '@types/byte-size':
specifier: ^8.1.0 specifier: ^8.1.0
version: 8.1.0 version: 8.1.0
'@types/lodash':
specifier: ^4.14.197
version: 4.14.197
'@types/node': '@types/node':
specifier: ^20.5.8 specifier: ^20.5.8
version: 20.5.8 version: 20.5.8
@ -107,6 +113,9 @@ dependencies:
lint-staged: lint-staged:
specifier: ^14.0.1 specifier: ^14.0.1
version: 14.0.1 version: 14.0.1
lodash:
specifier: ^4.17.21
version: 4.17.21
prettier: prettier:
specifier: ^3.0.3 specifier: ^3.0.3
version: 3.0.3 version: 3.0.3
@ -2655,6 +2664,17 @@ packages:
solid-js: 1.7.11 solid-js: 1.7.11
dev: false dev: false
/@solid-primitives/timer@1.3.7(solid-js@1.7.11):
resolution:
{
integrity: sha512-zS3qA7WVZYsW7+iTdk2M4W1wpMvRhdcMnO23Tcd+nX3YD7eMvjOnO15Oz2mymyfl/OC2ZgM1L5ec66GayEvPwQ==,
}
peerDependencies:
solid-js: ^1.6.12
dependencies:
solid-js: 1.7.11
dev: false
/@solid-primitives/utils@6.2.1(solid-js@1.7.11): /@solid-primitives/utils@6.2.1(solid-js@1.7.11):
resolution: resolution:
{ {
@ -2851,6 +2871,13 @@ packages:
} }
dev: false dev: false
/@types/lodash@4.14.197:
resolution:
{
integrity: sha512-BMVOiWs0uNxHVlHBgzTIqJYmj+PgCo4euloGF+5m4okL3rEYzM2EEv78mw8zWSMM57dM7kVIgJ2QDvwHSoCI5g==,
}
dev: false
/@types/minimist@1.2.2: /@types/minimist@1.2.2:
resolution: resolution:
{ {

View File

@ -1,3 +1,6 @@
import { ApexOptions } from 'apexcharts'
import byteSize from 'byte-size'
export const themes = [ export const themes = [
'light', 'light',
'dark', 'dark',
@ -40,6 +43,38 @@ export enum ROUTES {
Config = '/config', Config = '/config',
} }
export const CHART_MAX_XAXIS = 10
export const DEFAULT_CHART_OPTIONS: ApexOptions = {
title: { align: 'center', style: { color: 'gray' } },
chart: {
toolbar: { show: false },
zoom: { enabled: false },
animations: { easing: 'linear' },
},
noData: { text: 'Loading...' },
legend: {
fontSize: '14px',
labels: { colors: 'gray' },
itemMargin: { horizontal: 64 },
},
dataLabels: { enabled: false },
grid: { yaxis: { lines: { show: false } } },
stroke: { curve: 'smooth' },
tooltip: { enabled: false },
xaxis: {
range: CHART_MAX_XAXIS,
labels: { show: false },
axisTicks: { show: false },
},
yaxis: {
labels: {
style: { colors: 'gray' },
formatter: (val) => byteSize(val).toString(),
},
},
}
export enum LATENCY_QUALITY_MAP_HTTP { export enum LATENCY_QUALITY_MAP_HTTP {
NOT_CONNECTED = -1, NOT_CONNECTED = -1,
MEDIUM = 200, MEDIUM = 200,

View File

@ -1,3 +1,4 @@
import { writeClipboard } from '@solid-primitives/clipboard'
import { createEventSignal } from '@solid-primitives/event-listener' import { createEventSignal } from '@solid-primitives/event-listener'
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { makePersisted } from '@solid-primitives/storage' import { makePersisted } from '@solid-primitives/storage'
@ -304,7 +305,12 @@ export default () => {
<tr class="hover"> <tr class="hover">
<For each={row.getVisibleCells()}> <For each={row.getVisibleCells()}>
{(cell) => ( {(cell) => (
<td> <td
onContextMenu={(e) => {
e.preventDefault()
writeClipboard(cell.renderValue() as string)
}}
>
{flexRender( {flexRender(
cell.column.columnDef.cell, cell.column.columnDef.cell,
cell.getContext(), cell.getContext(),

View File

@ -1,8 +1,10 @@
import { createEventSignal } from '@solid-primitives/event-listener' import { createEventSignal } from '@solid-primitives/event-listener'
import { useI18n } from '@solid-primitives/i18n' import { useI18n } from '@solid-primitives/i18n'
import { makeTimer } from '@solid-primitives/timer'
import { createReconnectingWS } from '@solid-primitives/websocket' import { createReconnectingWS } from '@solid-primitives/websocket'
import type { ApexOptions } from 'apexcharts' import type { ApexOptions } from 'apexcharts'
import byteSize from 'byte-size' import byteSize from 'byte-size'
import { merge } from 'lodash'
import { SolidApexCharts } from 'solid-apexcharts' import { SolidApexCharts } from 'solid-apexcharts'
import { import {
JSX, JSX,
@ -11,13 +13,11 @@ import {
createEffect, createEffect,
createMemo, createMemo,
createSignal, createSignal,
onCleanup,
} from 'solid-js' } from 'solid-js'
import { CHART_MAX_XAXIS, DEFAULT_CHART_OPTIONS } from '~/constants'
import { secret, wsEndpointURL } from '~/signals' import { secret, wsEndpointURL } from '~/signals'
import type { Connection } from '~/types' import type { Connection } from '~/types'
const CHART_MAX_XAXIS = 10
const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => ( const TrafficWidget: ParentComponent<{ label: JSX.Element }> = (props) => (
<div class="stat flex-1"> <div class="stat flex-1">
<div class="stat-title text-primary-content">{props.label}</div> <div class="stat-title text-primary-content">{props.label}</div>
@ -34,26 +34,24 @@ export default () => {
[], [],
) )
const [memories, setMemories] = createSignal<number[]>([]) const [memories, setMemories] = createSignal<number[]>([])
// https://github.com/apexcharts/apexcharts.js/blob/main/samples/source/line/realtime.xml // https://github.com/apexcharts/apexcharts.js/blob/main/samples/source/line/realtime.xml
// TODO: need a better way // TODO: need a better way
const preventLeakTimer = setInterval( makeTimer(
() => { () => {
setTraffics((traffics) => traffics.slice(-CHART_MAX_XAXIS)) setTraffics((traffics) => traffics.slice(-CHART_MAX_XAXIS))
setMemories((memo) => memo.slice(-CHART_MAX_XAXIS)) setMemories((memo) => memo.slice(-CHART_MAX_XAXIS))
}, },
// we shrink the chart data array size down every 10 minutes // we shrink the chart data array size down every 10 minutes to prevent memory leaks
10 * 60 * 1000, 10 * 60 * 1000,
setInterval,
) )
onCleanup(() => clearInterval(preventLeakTimer))
const trafficWS = createReconnectingWS( const trafficWS = createReconnectingWS(
`${wsEndpointURL()}/traffic?token=${secret()}`, `${wsEndpointURL()}/traffic?token=${secret()}`,
) )
const trafficWSMessageEvent = createEventSignal<{ const trafficWSMessageEvent = createEventSignal(trafficWS, 'message')
message: WebSocketEventMap['message']
}>(trafficWS, 'message')
const traffic = () => { const traffic = () => {
const data = trafficWSMessageEvent()?.data const data = trafficWSMessageEvent()?.data
@ -64,50 +62,12 @@ export default () => {
createEffect(() => { createEffect(() => {
const t = traffic() const t = traffic()
if (t) { if (t) setTraffics((traffics) => [...traffics, t])
setTraffics((traffics) => [...traffics, t])
}
}) })
const defaultChartOptions: ApexOptions = { const trafficChartOptions = createMemo<ApexOptions>(() =>
chart: { merge({ title: { text: t('traffic') } }, DEFAULT_CHART_OPTIONS),
toolbar: { show: false }, )
zoom: { enabled: false },
animations: { easing: 'linear' },
},
noData: { text: 'Loading...' },
legend: {
fontSize: '14px',
labels: { colors: 'gray' },
itemMargin: { horizontal: 64 },
},
dataLabels: { enabled: false },
grid: { yaxis: { lines: { show: false } } },
stroke: { curve: 'smooth' },
tooltip: { enabled: false },
xaxis: {
range: CHART_MAX_XAXIS,
labels: { show: false },
axisTicks: { show: false },
},
yaxis: {
labels: {
style: { colors: 'gray' },
formatter(val) {
return byteSize(val).toString()
},
},
},
}
const trafficChartOptions = createMemo<ApexOptions>(() => ({
title: {
text: t('traffic'),
align: 'center',
style: { color: 'gray' },
},
...defaultChartOptions,
}))
const trafficChartSeries = createMemo(() => [ const trafficChartSeries = createMemo(() => [
{ {
@ -124,9 +84,7 @@ export default () => {
`${wsEndpointURL()}/memory?token=${secret()}`, `${wsEndpointURL()}/memory?token=${secret()}`,
) )
const memoryWSMessageEvent = createEventSignal<{ const memoryWSMessageEvent = createEventSignal(memoryWS, 'message')
message: WebSocketEventMap['message']
}>(memoryWS, 'message')
const memory = () => { const memory = () => {
const data = memoryWSMessageEvent()?.data const data = memoryWSMessageEvent()?.data
@ -137,19 +95,12 @@ export default () => {
createEffect(() => { createEffect(() => {
const m = memory() const m = memory()
if (m) { if (m) setMemories((memories) => [...memories, m])
setMemories((memories) => [...memories, m])
}
}) })
const memoryChartOptions = createMemo<ApexOptions>(() => ({ const memoryChartOptions = createMemo<ApexOptions>(() =>
title: { merge({ title: { text: t('memory') } }, DEFAULT_CHART_OPTIONS),
text: t('memory'), )
align: 'center',
style: { color: 'gray' },
},
...defaultChartOptions,
}))
const memoryChartSeries = createMemo(() => [{ data: memories() }]) const memoryChartSeries = createMemo(() => [{ data: memories() }])