Compare commits
60 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3814455cb | |||
| 67dbbfce97 | |||
| a9afe7a299 | |||
| 00cac97114 | |||
| 58edb4f602 | |||
| c3b5991dc7 | |||
| 9751ce3cf5 | |||
| 40d02d993b | |||
| 889ebf3acb | |||
| a051245805 | |||
| 66738313c8 | |||
| e8aa6adbda | |||
| 45ec71a6a1 | |||
| 072a6c09bb | |||
| c233549b4b | |||
| 4a30a2ba5e | |||
| 538a1e5dcd | |||
| 46ba01e3a8 | |||
| 9074895653 | |||
| 9e30045e6f | |||
| ad461c9dbc | |||
| b150a6b772 | |||
| c123c62bf7 | |||
| ccc42443c8 | |||
| 907360a334 | |||
| 3c4442a8dc | |||
| 9e98640731 | |||
| fa47512771 | |||
| e31e3138fb | |||
| e2bb23b2ed | |||
| 60beb85bb1 | |||
| b437850063 | |||
| 9f3a256334 | |||
| 2d30c8c529 | |||
| 8acd5794e8 | |||
| 2e01f3acb4 | |||
| 87951b0027 | |||
| 552a9cf78f | |||
| 5b0f18dd28 | |||
| 0ac2ba4873 | |||
| cfba470c0e | |||
| 9e1de4f226 | |||
| 0812bfaca4 | |||
| 45d8991c5f | |||
| 0f46d7ffd7 | |||
| fcf60daafa | |||
| 617bf71cad | |||
| 06c6a605fd | |||
| d41508d4e7 | |||
| a77db667ab | |||
| 34ad45c64e | |||
| 512b181b48 | |||
| be5e496380 | |||
| 887670ebfe | |||
| 9cdc8ad8f5 | |||
| 0dd68b22ca | |||
| 60ac81ff94 | |||
| a79495cdfc | |||
| 409363b26b | |||
| 2aaaf2ba1f |
@@ -6,6 +6,7 @@ daysUntilClose: 3
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- keep
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: wontfix
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
|
||||
@@ -8,7 +8,7 @@ jobs:
|
||||
name: JavaScript Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
name: Install Node
|
||||
with:
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Go Tests with Coverage
|
||||
run: make test SKIP_ASSET=1
|
||||
int-test:
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
name: Install Node
|
||||
with:
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Build
|
||||
uses: docker/bake-action@v3
|
||||
uses: docker/bake-action@v4
|
||||
with:
|
||||
load: true
|
||||
set: |
|
||||
@@ -83,14 +83,14 @@ jobs:
|
||||
with:
|
||||
images: amir20/dozzle
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.9.1
|
||||
uses: docker/setup-buildx-action@885d1462b80bc1c1c7f0b00334ad271f09369c55 # v2.10.0
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2.2.0
|
||||
uses: docker/login-action@v3.0.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4.1.1
|
||||
uses: docker/build-push-action@v5.0.0
|
||||
with:
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Install Node
|
||||
|
||||
@@ -18,14 +18,14 @@ jobs:
|
||||
with:
|
||||
images: amir20/dozzle
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.9.1
|
||||
uses: docker/setup-buildx-action@885d1462b80bc1c1c7f0b00334ad271f09369c55 # v2.10.0
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2.2.0
|
||||
uses: docker/login-action@v3.0.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4.1.1
|
||||
uses: docker/build-push-action@v5.0.0
|
||||
with:
|
||||
push: true
|
||||
platforms: linux/amd64,linux/arm/v7,linux/arm64/v8
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
name: Typecheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
name: Install Node
|
||||
with:
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
name: JavaScript Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
name: Install Node
|
||||
with:
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
with:
|
||||
go-version: 1.20.x
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Run Go Tests with Coverage
|
||||
run: make test SKIP_ASSET=1
|
||||
int-test:
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v3
|
||||
name: Install Node
|
||||
with:
|
||||
@@ -81,7 +81,7 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Build
|
||||
uses: docker/bake-action@v3
|
||||
uses: docker/bake-action@v4
|
||||
with:
|
||||
load: true
|
||||
set: |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
module.exports = {
|
||||
printWidth: 120,
|
||||
plugins: ["prettier-plugin-tailwindcss"],
|
||||
};
|
||||
|
||||
@@ -1 +1 @@
|
||||
-r '\.(go)$' -R 'node_modules' -G '\*\_test.go' -s -- go run main.go --level debug --remote-host tcp://64.225.88.189:2376|clashleaders.com
|
||||
-r '\.(go)$' -R 'node_modules' -G '\*\_test.go' -s -- go run main.go --level debug
|
||||
|
||||
@@ -14,7 +14,7 @@ COPY package.json ./
|
||||
RUN pnpm install --offline --ignore-scripts --no-optional
|
||||
|
||||
# Copy assets and translations to build
|
||||
COPY .* vite.config.ts ./
|
||||
COPY .* *.config.ts *.config.js ./
|
||||
COPY assets ./assets
|
||||
COPY locales ./locales
|
||||
COPY public ./public
|
||||
@@ -22,7 +22,7 @@ COPY public ./public
|
||||
# Build assets
|
||||
RUN pnpm build
|
||||
|
||||
FROM --platform=$BUILDPLATFORM golang:1.21.0-alpine AS builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.21.1-alpine AS builder
|
||||
|
||||
RUN apk add --no-cache ca-certificates && mkdir /dozzle
|
||||
|
||||
|
||||
@@ -22,5 +22,34 @@ watchEffect(() => {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="postcss">
|
||||
html.has-custom-scrollbars {
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
display: content;
|
||||
}
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(128, 128, 128, 0.33);
|
||||
outline: 1px solid slategrey;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background-color: rgba(64, 64, 64, 0.33);
|
||||
}
|
||||
|
||||
section main {
|
||||
scrollbar-color: #353535 transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,6 +18,7 @@ declare global {
|
||||
const arrayEquals: typeof import('./utils/index')['arrayEquals']
|
||||
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||
const automaticRedirect: typeof import('./composables/settings')['automaticRedirect']
|
||||
const collapseNav: typeof import('./composables/settings')['collapseNav']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const computedAsync: typeof import('@vueuse/core')['computedAsync']
|
||||
@@ -25,13 +26,16 @@ declare global {
|
||||
const computedInject: typeof import('@vueuse/core')['computedInject']
|
||||
const computedWithControl: typeof import('@vueuse/core')['computedWithControl']
|
||||
const config: typeof import('./stores/config')['default']
|
||||
const containerContext: typeof import('./composables/containerContext')['containerContext']
|
||||
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
|
||||
const controlledRef: typeof import('@vueuse/core')['controlledRef']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const createEventHook: typeof import('@vueuse/core')['createEventHook']
|
||||
const createGenericProjection: typeof import('@vueuse/math')['createGenericProjection']
|
||||
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
|
||||
const createInjectionState: typeof import('@vueuse/core')['createInjectionState']
|
||||
const createPinia: typeof import('pinia')['createPinia']
|
||||
const createProjection: typeof import('@vueuse/math')['createProjection']
|
||||
const createReactiveFn: typeof import('@vueuse/core')['createReactiveFn']
|
||||
const createReusableTemplate: typeof import('@vueuse/core')['createReusableTemplate']
|
||||
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
|
||||
@@ -60,12 +64,14 @@ declare global {
|
||||
const isDefined: typeof import('@vueuse/core')['isDefined']
|
||||
const isMobile: typeof import('./composables/media')['isMobile']
|
||||
const isObject: typeof import('./utils/index')['isObject']
|
||||
const isPinnedContainer: typeof import('./composables/storage')['isPinnedContainer']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const lightTheme: typeof import('./composables/settings')['lightTheme']
|
||||
const logicAnd: typeof import('@vueuse/math')['logicAnd']
|
||||
const logicNot: typeof import('@vueuse/math')['logicNot']
|
||||
const logicOr: typeof import('@vueuse/math')['logicOr']
|
||||
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
@@ -98,6 +104,7 @@ declare global {
|
||||
const persistentVisibleKeys: typeof import('./composables/storage')['persistentVisibleKeys']
|
||||
const pinnedContainers: typeof import('./composables/storage')['pinnedContainers']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const provideContainerContext: typeof import('./composables/containerContext')['provideContainerContext']
|
||||
const reactify: typeof import('@vueuse/core')['reactify']
|
||||
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
@@ -141,7 +148,6 @@ declare global {
|
||||
const toRef: typeof import('vue')['toRef']
|
||||
const toRefs: typeof import('vue')['toRefs']
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const togglePinnedContainer: typeof import('./composables/storage')['togglePinnedContainer']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const tryOnBeforeMount: typeof import('@vueuse/core')['tryOnBeforeMount']
|
||||
const tryOnBeforeUnmount: typeof import('@vueuse/core')['tryOnBeforeUnmount']
|
||||
@@ -151,6 +157,7 @@ declare global {
|
||||
const unref: typeof import('vue')['unref']
|
||||
const unrefElement: typeof import('@vueuse/core')['unrefElement']
|
||||
const until: typeof import('@vueuse/core')['until']
|
||||
const useAbs: typeof import('@vueuse/math')['useAbs']
|
||||
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||
const useAnimate: typeof import('@vueuse/core')['useAnimate']
|
||||
const useArrayDifference: typeof import('@vueuse/core')['useArrayDifference']
|
||||
@@ -168,6 +175,7 @@ declare global {
|
||||
const useAsyncQueue: typeof import('@vueuse/core')['useAsyncQueue']
|
||||
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useAverage: typeof import('@vueuse/math')['useAverage']
|
||||
const useBase64: typeof import('@vueuse/core')['useBase64']
|
||||
const useBattery: typeof import('@vueuse/core')['useBattery']
|
||||
const useBluetooth: typeof import('@vueuse/core')['useBluetooth']
|
||||
@@ -175,10 +183,13 @@ declare global {
|
||||
const useBroadcastChannel: typeof import('@vueuse/core')['useBroadcastChannel']
|
||||
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
|
||||
const useCached: typeof import('@vueuse/core')['useCached']
|
||||
const useCeil: typeof import('@vueuse/math')['useCeil']
|
||||
const useClamp: typeof import('@vueuse/math')['useClamp']
|
||||
const useClipboard: typeof import('@vueuse/core')['useClipboard']
|
||||
const useCloned: typeof import('@vueuse/core')['useCloned']
|
||||
const useColorMode: typeof import('@vueuse/core')['useColorMode']
|
||||
const useConfirmDialog: typeof import('@vueuse/core')['useConfirmDialog']
|
||||
const useContainerContext: typeof import('./composables/containerContext')['useContainerContext']
|
||||
const useContainerStore: typeof import('./stores/container')['useContainerStore']
|
||||
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
@@ -213,6 +224,7 @@ declare global {
|
||||
const useFetch: typeof import('@vueuse/core')['useFetch']
|
||||
const useFileDialog: typeof import('@vueuse/core')['useFileDialog']
|
||||
const useFileSystemAccess: typeof import('@vueuse/core')['useFileSystemAccess']
|
||||
const useFloor: typeof import('@vueuse/math')['useFloor']
|
||||
const useFocus: typeof import('@vueuse/core')['useFocus']
|
||||
const useFocusWithin: typeof import('@vueuse/core')['useFocusWithin']
|
||||
const useFps: typeof import('@vueuse/core')['useFps']
|
||||
@@ -234,10 +246,13 @@ declare global {
|
||||
const useLogStream: typeof import('./composables/eventsource')['useLogStream']
|
||||
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
|
||||
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
|
||||
const useMath: typeof import('@vueuse/math')['useMath']
|
||||
const useMax: typeof import('@vueuse/math')['useMax']
|
||||
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
|
||||
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
||||
const useMemoize: typeof import('@vueuse/core')['useMemoize']
|
||||
const useMemory: typeof import('@vueuse/core')['useMemory']
|
||||
const useMin: typeof import('@vueuse/math')['useMin']
|
||||
const useMounted: typeof import('@vueuse/core')['useMounted']
|
||||
const useMouse: typeof import('@vueuse/core')['useMouse']
|
||||
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
|
||||
@@ -257,15 +272,18 @@ declare global {
|
||||
const usePointer: typeof import('@vueuse/core')['usePointer']
|
||||
const usePointerLock: typeof import('@vueuse/core')['usePointerLock']
|
||||
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
|
||||
const usePrecision: typeof import('@vueuse/math')['usePrecision']
|
||||
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
|
||||
const usePreferredContrast: typeof import('@vueuse/core')['usePreferredContrast']
|
||||
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
|
||||
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
|
||||
const usePreferredReducedMotion: typeof import('@vueuse/core')['usePreferredReducedMotion']
|
||||
const usePrevious: typeof import('@vueuse/core')['usePrevious']
|
||||
const useProjection: typeof import('@vueuse/math')['useProjection']
|
||||
const useRafFn: typeof import('@vueuse/core')['useRafFn']
|
||||
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
|
||||
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
|
||||
const useRound: typeof import('@vueuse/math')['useRound']
|
||||
const useRoute: typeof import('vue-router')['useRoute']
|
||||
const useRouter: typeof import('vue-router')['useRouter']
|
||||
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']
|
||||
@@ -285,6 +303,7 @@ declare global {
|
||||
const useStorage: typeof import('@vueuse/core')['useStorage']
|
||||
const useStorageAsync: typeof import('@vueuse/core')['useStorageAsync']
|
||||
const useStyleTag: typeof import('@vueuse/core')['useStyleTag']
|
||||
const useSum: typeof import('@vueuse/math')['useSum']
|
||||
const useSupported: typeof import('@vueuse/core')['useSupported']
|
||||
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||
@@ -302,8 +321,10 @@ declare global {
|
||||
const useTitle: typeof import('@vueuse/core')['useTitle']
|
||||
const useToNumber: typeof import('@vueuse/core')['useToNumber']
|
||||
const useToString: typeof import('@vueuse/core')['useToString']
|
||||
const useToast: typeof import('./composables/toast')['useToast']
|
||||
const useToggle: typeof import('@vueuse/core')['useToggle']
|
||||
const useTransition: typeof import('@vueuse/core')['useTransition']
|
||||
const useTrunc: typeof import('@vueuse/math')['useTrunc']
|
||||
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
|
||||
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
|
||||
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||
@@ -358,6 +379,7 @@ declare module 'vue' {
|
||||
readonly arrayEquals: UnwrapRef<typeof import('./utils/index')['arrayEquals']>
|
||||
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
|
||||
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
|
||||
readonly automaticRedirect: UnwrapRef<typeof import('./composables/settings')['automaticRedirect']>
|
||||
readonly collapseNav: UnwrapRef<typeof import('./composables/settings')['collapseNav']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
|
||||
@@ -365,6 +387,7 @@ declare module 'vue' {
|
||||
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
|
||||
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
|
||||
readonly config: UnwrapRef<typeof import('./stores/config')['default']>
|
||||
readonly containerContext: UnwrapRef<typeof import('./composables/containerContext')['containerContext']>
|
||||
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
|
||||
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
@@ -437,6 +460,7 @@ declare module 'vue' {
|
||||
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composables/storage')['persistentVisibleKeys']>
|
||||
readonly pinnedContainers: UnwrapRef<typeof import('./composables/storage')['pinnedContainers']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly provideContainerContext: UnwrapRef<typeof import('./composables/containerContext')['provideContainerContext']>
|
||||
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
|
||||
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
@@ -480,7 +504,6 @@ declare module 'vue' {
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly togglePinnedContainer: UnwrapRef<typeof import('./composables/storage')['togglePinnedContainer']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']>
|
||||
readonly tryOnBeforeUnmount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeUnmount']>
|
||||
@@ -518,6 +541,7 @@ declare module 'vue' {
|
||||
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
|
||||
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
|
||||
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
|
||||
readonly useContainerContext: UnwrapRef<typeof import('./composables/containerContext')['useContainerContext']>
|
||||
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
|
||||
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
@@ -641,6 +665,7 @@ declare module 'vue' {
|
||||
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
|
||||
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
|
||||
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
|
||||
readonly useToast: UnwrapRef<typeof import('./composables/toast')['useToast']>
|
||||
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
|
||||
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
|
||||
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
|
||||
@@ -691,6 +716,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly arrayEquals: UnwrapRef<typeof import('./utils/index')['arrayEquals']>
|
||||
readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
|
||||
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
|
||||
readonly automaticRedirect: UnwrapRef<typeof import('./composables/settings')['automaticRedirect']>
|
||||
readonly collapseNav: UnwrapRef<typeof import('./composables/settings')['collapseNav']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
|
||||
@@ -698,6 +724,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly computedInject: UnwrapRef<typeof import('@vueuse/core')['computedInject']>
|
||||
readonly computedWithControl: UnwrapRef<typeof import('@vueuse/core')['computedWithControl']>
|
||||
readonly config: UnwrapRef<typeof import('./stores/config')['default']>
|
||||
readonly containerContext: UnwrapRef<typeof import('./composables/containerContext')['containerContext']>
|
||||
readonly controlledComputed: UnwrapRef<typeof import('@vueuse/core')['controlledComputed']>
|
||||
readonly controlledRef: UnwrapRef<typeof import('@vueuse/core')['controlledRef']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
@@ -770,6 +797,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly persistentVisibleKeys: UnwrapRef<typeof import('./composables/storage')['persistentVisibleKeys']>
|
||||
readonly pinnedContainers: UnwrapRef<typeof import('./composables/storage')['pinnedContainers']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly provideContainerContext: UnwrapRef<typeof import('./composables/containerContext')['provideContainerContext']>
|
||||
readonly reactify: UnwrapRef<typeof import('@vueuse/core')['reactify']>
|
||||
readonly reactifyObject: UnwrapRef<typeof import('@vueuse/core')['reactifyObject']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
@@ -813,7 +841,6 @@ declare module '@vue/runtime-core' {
|
||||
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
|
||||
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly togglePinnedContainer: UnwrapRef<typeof import('./composables/storage')['togglePinnedContainer']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly tryOnBeforeMount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeMount']>
|
||||
readonly tryOnBeforeUnmount: UnwrapRef<typeof import('@vueuse/core')['tryOnBeforeUnmount']>
|
||||
@@ -851,6 +878,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly useCloned: UnwrapRef<typeof import('@vueuse/core')['useCloned']>
|
||||
readonly useColorMode: UnwrapRef<typeof import('@vueuse/core')['useColorMode']>
|
||||
readonly useConfirmDialog: UnwrapRef<typeof import('@vueuse/core')['useConfirmDialog']>
|
||||
readonly useContainerContext: UnwrapRef<typeof import('./composables/containerContext')['useContainerContext']>
|
||||
readonly useContainerStore: UnwrapRef<typeof import('./stores/container')['useContainerStore']>
|
||||
readonly useCounter: UnwrapRef<typeof import('@vueuse/core')['useCounter']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
@@ -974,6 +1002,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly useTitle: UnwrapRef<typeof import('@vueuse/core')['useTitle']>
|
||||
readonly useToNumber: UnwrapRef<typeof import('@vueuse/core')['useToNumber']>
|
||||
readonly useToString: UnwrapRef<typeof import('@vueuse/core')['useToString']>
|
||||
readonly useToast: UnwrapRef<typeof import('./composables/toast')['useToast']>
|
||||
readonly useToggle: UnwrapRef<typeof import('@vueuse/core')['useToggle']>
|
||||
readonly useTransition: UnwrapRef<typeof import('@vueuse/core')['useTransition']>
|
||||
readonly useUrlSearchParams: UnwrapRef<typeof import('@vueuse/core')['useUrlSearchParams']>
|
||||
|
||||
@@ -10,13 +10,14 @@ declare module 'vue' {
|
||||
BarChart: typeof import('./components/BarChart.vue')['default']
|
||||
'Carbon:caretDown': typeof import('~icons/carbon/caret-down')['default']
|
||||
'Carbon:circleSolid': typeof import('~icons/carbon/circle-solid')['default']
|
||||
'Carbon:information': typeof import('~icons/carbon/information')['default']
|
||||
'Carbon:macShift': typeof import('~icons/carbon/mac-shift')['default']
|
||||
'Carbon:star': typeof import('~icons/carbon/star')['default']
|
||||
'Carbon:starFilled': typeof import('~icons/carbon/star-filled')['default']
|
||||
'Carbon:warning': typeof import('~icons/carbon/warning')['default']
|
||||
'Cil:checkCircle': typeof import('~icons/cil/check-circle')['default']
|
||||
'Cil:circle': typeof import('~icons/cil/circle')['default']
|
||||
'Cil:columns': typeof import('~icons/cil/columns')['default']
|
||||
'Cil:findInPage': typeof import('~icons/cil/find-in-page')['default']
|
||||
'Cil:xCircle': typeof import('~icons/cil/x-circle')['default']
|
||||
ComplexLogItem: typeof import('./components/LogViewer/ComplexLogItem.vue')['default']
|
||||
ContainerHealth: typeof import('./components/LogViewer/ContainerHealth.vue')['default']
|
||||
@@ -27,11 +28,13 @@ declare module 'vue' {
|
||||
DateTime: typeof import('./components/common/DateTime.vue')['default']
|
||||
DistanceTime: typeof import('./components/common/DistanceTime.vue')['default']
|
||||
DockerEventLogItem: typeof import('./components/LogViewer/DockerEventLogItem.vue')['default']
|
||||
DropdownMenu: typeof import('./components/DropdownMenu.vue')['default']
|
||||
Dropdown: typeof import('./components/common/Dropdown.vue')['default']
|
||||
FieldList: typeof import('./components/LogViewer/FieldList.vue')['default']
|
||||
FuzzySearchModal: typeof import('./components/FuzzySearchModal.vue')['default']
|
||||
'Ic:sharpFindInPage': typeof import('~icons/ic/sharp-find-in-page')['default']
|
||||
'Ic:sharpKeyboardReturn': typeof import('~icons/ic/sharp-keyboard-return')['default']
|
||||
InfiniteLoader: typeof import('./components/InfiniteLoader.vue')['default']
|
||||
KeyShortcut: typeof import('./components/KeyShortcut.vue')['default']
|
||||
KeyShortcut: typeof import('./components/common/KeyShortcut.vue')['default']
|
||||
LogActionsToolbar: typeof import('./components/LogViewer/LogActionsToolbar.vue')['default']
|
||||
LogContainer: typeof import('./components/LogViewer/LogContainer.vue')['default']
|
||||
LogDate: typeof import('./components/LogViewer/LogDate.vue')['default']
|
||||
@@ -41,18 +44,22 @@ declare module 'vue' {
|
||||
LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default']
|
||||
LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default']
|
||||
'Mdi:arrowUp': typeof import('~icons/mdi/arrow-up')['default']
|
||||
'Mdi:dotsVertical': typeof import('~icons/mdi/dots-vertical')['default']
|
||||
'Mdi:close': typeof import('~icons/mdi/close')['default']
|
||||
'Mdi:hamburgerMenu': typeof import('~icons/mdi/hamburger-menu')['default']
|
||||
'Mdi:keyboardEsc': typeof import('~icons/mdi/keyboard-esc')['default']
|
||||
'Mdi:lightChevronDoubleDown': typeof import('~icons/mdi-light/chevron-double-down')['default']
|
||||
'Mdi:lightChevronLeft': typeof import('~icons/mdi-light/chevron-left')['default']
|
||||
'Mdi:lightChevronRight': typeof import('~icons/mdi-light/chevron-right')['default']
|
||||
'Mdi:lightCog': typeof import('~icons/mdi-light/cog')['default']
|
||||
'Mdi:lightLogout': typeof import('~icons/mdi-light/logout')['default']
|
||||
'Mdi:lightMagnify': typeof import('~icons/mdi-light/magnify')['default']
|
||||
'MdiLight:check': typeof import('~icons/mdi-light/check')['default']
|
||||
MobileMenu: typeof import('./components/common/MobileMenu.vue')['default']
|
||||
'Octicon:container24': typeof import('~icons/octicon/container24')['default']
|
||||
'Octicon:download24': typeof import('~icons/octicon/download24')['default']
|
||||
'Octicon:trash24': typeof import('~icons/octicon/trash24')['default']
|
||||
'Ph:command': typeof import('~icons/ph/command')['default']
|
||||
'Ph:computerTower': typeof import('~icons/ph/computer-tower')['default']
|
||||
'Ph:controlBold': typeof import('~icons/ph/control-bold')['default']
|
||||
Popup: typeof import('./components/Popup.vue')['default']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
@@ -67,6 +74,8 @@ declare module 'vue' {
|
||||
StatMonitor: typeof import('./components/LogViewer/StatMonitor.vue')['default']
|
||||
StatSparkline: typeof import('./components/LogViewer/StatSparkline.vue')['default']
|
||||
Tag: typeof import('./components/common/Tag.vue')['default']
|
||||
TimedButton: typeof import('./components/common/TimedButton.vue')['default']
|
||||
Toggle: typeof import('./components/common/Toggle.vue')['default']
|
||||
ZigZag: typeof import('./components/LogViewer/ZigZag.vue')['default']
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="is-relative">
|
||||
<div class="bar"></div>
|
||||
<div class="is-overlay">
|
||||
<div class="relative">
|
||||
<div class="bar h-7 origin-left rounded-br rounded-tr bg-primary transition-transform"></div>
|
||||
<div class="absolute inset-0 flex flex-col justify-center px-2 text-sm">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
@@ -15,17 +15,6 @@ const minValue = computed(() => Math.min(value, 1));
|
||||
|
||||
<style scoped>
|
||||
.bar {
|
||||
height: 1.5em;
|
||||
background-color: var(--primary-color);
|
||||
transform-origin: left;
|
||||
transform: scaleX(v-bind(minValue));
|
||||
transition: transform 0.2s ease-out;
|
||||
border-top-right-radius: 0.2em;
|
||||
border-bottom-right-radius: 0.2em;
|
||||
}
|
||||
|
||||
.is-overlay {
|
||||
font-size: 0.9em;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
!
|
||||
<template>
|
||||
<table class="table is-fullwidth">
|
||||
<table class="table table-lg bg-base">
|
||||
<thead>
|
||||
<tr :data-direction="direction > 0 ? 'asc' : 'desc'">
|
||||
<th
|
||||
@@ -10,12 +10,10 @@
|
||||
:class="{ 'selected-sort': key === sortField }"
|
||||
v-show="isVisible(key)"
|
||||
>
|
||||
<a>
|
||||
<span class="icon-text">
|
||||
<span>{{ $t(value.label) }}</span>
|
||||
<span class="icon">
|
||||
<mdi:arrow-up />
|
||||
</span>
|
||||
<a class="inline-flex cursor-pointer gap-2 text-sm uppercase">
|
||||
<span>{{ $t(value.label) }}</span>
|
||||
<span class="h-4" data-icon>
|
||||
<mdi:arrow-up />
|
||||
</span>
|
||||
</a>
|
||||
</th>
|
||||
@@ -28,6 +26,7 @@
|
||||
{{ container.name }}
|
||||
</router-link>
|
||||
</td>
|
||||
<td v-if="isVisible('host')">{{ container.hostLabel }}</td>
|
||||
<td v-if="isVisible('state')">{{ container.state }}</td>
|
||||
<td v-if="isVisible('created')">
|
||||
<distance-time :date="container.created" strict :suffix="false"></distance-time>
|
||||
@@ -45,15 +44,18 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<nav class="pagination is-right" role="navigation" aria-label="pagination" v-if="isPaginated">
|
||||
<ul class="pagination-list">
|
||||
<li v-for="i in totalPages">
|
||||
<a class="pagination-link" :class="{ 'is-current': i === currentPage }" @click.prevent="currentPage = i">{{
|
||||
i
|
||||
}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<div class="p-4 text-center">
|
||||
<nav class="join" v-if="isPaginated">
|
||||
<button
|
||||
v-for="i in totalPages"
|
||||
class="btn join-item"
|
||||
:class="{ 'btn-primary': i === currentPage }"
|
||||
@click="currentPage = i"
|
||||
>
|
||||
{{ i }}
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -66,6 +68,11 @@ const fields = {
|
||||
sortFunc: (a: Container, b: Container) => a.name.localeCompare(b.name) * direction.value,
|
||||
mobileVisible: true,
|
||||
},
|
||||
host: {
|
||||
label: "label.host",
|
||||
sortFunc: (a: Container, b: Container) => a.hostLabel.localeCompare(b.hostLabel) * direction.value,
|
||||
mobileVisible: false,
|
||||
},
|
||||
state: {
|
||||
label: "label.status",
|
||||
sortFunc: (a: Container, b: Container) => a.state.localeCompare(b.state) * direction.value,
|
||||
@@ -131,19 +138,23 @@ function isVisible(field: keys) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
<style lang="postcss" scoped>
|
||||
[data-icon] {
|
||||
display: none;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
[data-direction="desc"] & {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
.selected-sort {
|
||||
font-weight: bold;
|
||||
border-color: var(--primary-color);
|
||||
.icon {
|
||||
display: inline-block;
|
||||
|
||||
th {
|
||||
@apply border-b-2 border-base-lighter;
|
||||
&.selected-sort {
|
||||
font-weight: bold;
|
||||
@apply border-primary;
|
||||
[data-icon] {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,4 +164,8 @@ tbody td {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply hover:text-primary;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
<template>
|
||||
<div class="dropdown is-hoverable">
|
||||
<div class="dropdown-trigger">
|
||||
<slot name="trigger">
|
||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||
<span class="icon">
|
||||
<mdi:dots-vertical />
|
||||
</span>
|
||||
</button>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="dropdown-menu" id="dropdown-menu" role="menu">
|
||||
<div class="dropdown-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.minimal .button {
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border: none;
|
||||
padding: 0.1em;
|
||||
height: 100%;
|
||||
|
||||
& > .icon {
|
||||
height: 100%;
|
||||
|
||||
& > svg {
|
||||
align-self: flex-start;
|
||||
height: 0.85em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.is-top {
|
||||
& .dropdown-menu {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.is-last .dropdown-menu {
|
||||
top: -30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,56 +1,64 @@
|
||||
<template>
|
||||
<div class="panel">
|
||||
<o-autocomplete
|
||||
ref="autocomplete"
|
||||
v-model="query"
|
||||
:placeholder="$t('placeholder.search-containers')"
|
||||
open-on-focus
|
||||
keep-first
|
||||
expanded
|
||||
:data="data"
|
||||
@select="selected"
|
||||
>
|
||||
<template #default="{ option: item }">
|
||||
<div class="media">
|
||||
<div class="media-left">
|
||||
<span class="icon is-small" :class="item.state">
|
||||
<octicon:container-24 />
|
||||
</span>
|
||||
<div class="dropdown dropdown-open w-full">
|
||||
<div class="input input-primary flex h-auto items-center">
|
||||
<mdi:light-magnify class="flex h-8 w-8" />
|
||||
<input
|
||||
tabindex="0"
|
||||
class="input input-ghost input-lg flex-1 px-1"
|
||||
ref="input"
|
||||
@keyup.down="selectedIndex = Math.min(selectedIndex + 1, data.length - 1)"
|
||||
@keyup.up="selectedIndex = Math.max(selectedIndex - 1, 0)"
|
||||
@keyup.enter.exact="selected(data[selectedIndex])"
|
||||
@keyup.alt.enter="addColumn(data[selectedIndex])"
|
||||
v-model="query"
|
||||
:placeholder="$t('placeholder.search-containers')"
|
||||
/>
|
||||
<mdi:keyboard-esc class="flex" />
|
||||
</div>
|
||||
<ul tabindex="0" class="menu dropdown-content rounded-box !relative mt-2 w-full bg-base-lighter p-2">
|
||||
<li v-for="(item, index) in data">
|
||||
<a
|
||||
class="grid auto-cols-max grid-cols-[min-content,auto] gap-2 py-4"
|
||||
@click.prevent="selected(item)"
|
||||
@mouseenter="selectedIndex = index"
|
||||
:class="index === selectedIndex ? 'focus' : ''"
|
||||
>
|
||||
<div :class="{ 'text-primary': item.state === 'running' }">
|
||||
<octicon:container-24 />
|
||||
</div>
|
||||
<div class="media-content">{{ item.host }} / {{ item.name }}</div>
|
||||
<div class="media-right">
|
||||
<span
|
||||
class="icon is-small column-icon"
|
||||
@click.stop.prevent="addColumn(item)"
|
||||
:title="$t('tooltip.pin-column')"
|
||||
>
|
||||
<cil:columns />
|
||||
</span>
|
||||
<div class="truncate">
|
||||
<span class="font-light">{{ item.host }}</span> / {{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</o-autocomplete>
|
||||
<distance-time :date="item.created" class="ml-auto text-xs font-light" />
|
||||
<a @click.stop.prevent="addColumn(item)" :title="$t('tooltip.pin-column')" class="hover:text-secondary">
|
||||
<ic:sharp-keyboard-return v-if="index === selectedIndex" />
|
||||
<cil:columns v-else />
|
||||
</a>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Container } from "@/models/Container";
|
||||
import { useFuse } from "@vueuse/integrations/useFuse";
|
||||
|
||||
const { maxResults: resultLimit = 20 } = defineProps<{
|
||||
const { maxResults: resultLimit = 5 } = defineProps<{
|
||||
maxResults?: number;
|
||||
}>();
|
||||
|
||||
const close = defineEmit();
|
||||
|
||||
const query = ref("");
|
||||
const autocomplete = ref<HTMLElement>();
|
||||
const input = ref<HTMLInputElement>();
|
||||
const selectedIndex = ref(0);
|
||||
|
||||
const router = useRouter();
|
||||
const store = useContainerStore();
|
||||
const { containers } = storeToRefs(store);
|
||||
|
||||
const list = computed(() => {
|
||||
return containers.value.map(({ id, created, name, state, host }) => {
|
||||
return containers.value.map(({ id, created, name, state, hostLabel: host }) => {
|
||||
return {
|
||||
id,
|
||||
created,
|
||||
@@ -62,7 +70,11 @@ const list = computed(() => {
|
||||
});
|
||||
|
||||
const { results } = useFuse(query, list, {
|
||||
fuseOptions: { keys: ["name"], includeScore: true },
|
||||
fuseOptions: {
|
||||
keys: ["name", "host"],
|
||||
includeScore: true,
|
||||
useExtendedSearch: true,
|
||||
},
|
||||
resultLimit,
|
||||
matchAllWhenSearchEmpty: true,
|
||||
});
|
||||
@@ -82,62 +94,26 @@ const data = computed(() => {
|
||||
return 0;
|
||||
}
|
||||
})
|
||||
.map(({ item }) => item);
|
||||
.map(({ item }) => item)
|
||||
.slice(0, resultLimit);
|
||||
});
|
||||
watchOnce(autocomplete, () => autocomplete.value?.focus());
|
||||
|
||||
watch(query, (data) => {
|
||||
if (data.length > 0) {
|
||||
selectedIndex.value = 0;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => input.value?.focus());
|
||||
|
||||
function selected({ id }: { id: string }) {
|
||||
router.push({ name: "container-id", params: { id } });
|
||||
close();
|
||||
}
|
||||
function addColumn(container: Container) {
|
||||
function addColumn(container: { id: string }) {
|
||||
store.appendActiveContainer(container);
|
||||
close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.panel {
|
||||
min-height: 400px;
|
||||
width: 580px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.panel {
|
||||
min-height: 200px;
|
||||
width: auto;
|
||||
margin-left: 0.25rem !important;
|
||||
margin-right: 0.25rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.running {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.exited {
|
||||
color: var(--scheme-main-ter);
|
||||
}
|
||||
|
||||
.column-icon {
|
||||
&:hover {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(a.dropdown-item) {
|
||||
padding-right: 1em;
|
||||
|
||||
.media-right {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&:hover .media-right {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
<style scoped lang="postcss"></style>
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
<template>
|
||||
<div ref="root" class="infinte-loader">
|
||||
<div class="spinner" v-show="isLoading">
|
||||
<div class="bounce1"></div>
|
||||
<div class="bounce2"></div>
|
||||
<div class="bounce3"></div>
|
||||
</div>
|
||||
<div ref="root" class="flex min-h-[1px] justify-center">
|
||||
<span class="loading loading-bars loading-md mt-4 text-primary" v-show="isLoading"></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { onLoadMore = () => {}, enabled } = defineProps<{
|
||||
onLoadMore: () => void;
|
||||
const { onLoadMore = () => Promise.resolve(), enabled } = defineProps<{
|
||||
onLoadMore: () => Promise<void>;
|
||||
enabled: boolean;
|
||||
}>();
|
||||
|
||||
@@ -33,41 +29,3 @@ const observer = new IntersectionObserver(async (entries) => {
|
||||
onMounted(() => observer.observe(root.value!));
|
||||
onUnmounted(() => observer.disconnect());
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.infinte-loader {
|
||||
min-height: 1px;
|
||||
}
|
||||
.spinner {
|
||||
margin: 10px auto 0;
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
|
||||
& > div {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: var(--primary-color);
|
||||
border-radius: 100%;
|
||||
display: inline-block;
|
||||
animation: sk-bouncedelay 0.8s infinite ease-in-out both;
|
||||
}
|
||||
& .bounce1 {
|
||||
animation-delay: -0.32s;
|
||||
}
|
||||
|
||||
& .bounce2 {
|
||||
animation-delay: -0.16s;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-bouncedelay {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
transform: scale(0);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<span class="icon-text">
|
||||
<span class="icon mx-0" v-if="modifiers.includes('shift')">
|
||||
<carbon:mac-shift />
|
||||
</span>
|
||||
<span class="icon mx-0" v-if="modifiers.includes('meta')">
|
||||
<ph:command v-if="isMac" />
|
||||
<ph:control-bold v-else />
|
||||
</span>
|
||||
<kbd class="is-uppercase is-family-sans-serif ml-1">{{ char }}</kbd>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.userAgent);
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
const { modifiers = ["meta"], char } = defineProps<{ char: string; modifiers?: ("^" | "meta" | "shift")[] }>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.icon {
|
||||
width: unset;
|
||||
}
|
||||
</style>
|
||||
@@ -1,25 +1,24 @@
|
||||
<template>
|
||||
<div class="columns is-1 is-variable is-mobile">
|
||||
<div class="column is-narrow" v-if="showStd">
|
||||
<div class="flex gap-x-2">
|
||||
<div v-if="showStd">
|
||||
<log-std :std="logEntry.std"></log-std>
|
||||
</div>
|
||||
<div class="column is-narrow" v-if="showTimestamp">
|
||||
<div v-if="showTimestamp">
|
||||
<log-date :date="logEntry.date"></log-date>
|
||||
</div>
|
||||
<div class="column is-narrow is-flex">
|
||||
<div class="flex">
|
||||
<log-level :level="logEntry.level"></log-level>
|
||||
</div>
|
||||
<div class="column">
|
||||
<ul class="fields" :class="{ expanded }" @click="expandToggle()">
|
||||
<li v-for="(value, name) in validValues">
|
||||
<span class="has-text-grey">{{ name }}=</span
|
||||
><span class="has-text-weight-bold" v-if="value === null"><null></span>
|
||||
<div>
|
||||
<ul class="fields cursor-pointer space-x-4" :class="{ expanded }" @click="expandToggle()">
|
||||
<li v-for="(value, name) in validValues" class="inline-block">
|
||||
<span class="text-light">{{ name }}=</span><span class="font-bold" v-if="value === null"><null></span>
|
||||
<template v-else-if="Array.isArray(value)">
|
||||
<span class="has-text-weight-bold" v-html="markSearch(JSON.stringify(value))"> </span>
|
||||
<span class="font-bold" v-html="markSearch(JSON.stringify(value))"> </span>
|
||||
</template>
|
||||
<span class="has-text-weight-bold" v-html="markSearch(value)" v-else></span>
|
||||
<span class="font-bold" v-html="markSearch(value)" v-else></span>
|
||||
</li>
|
||||
<li class="has-text-grey" v-if="Object.keys(validValues).length === 0">all values are hidden</li>
|
||||
<li class="text-light" v-if="Object.keys(validValues).length === 0">all values are hidden</li>
|
||||
</ul>
|
||||
<field-list :fields="logEntry.unfilteredMessage" :expanded="expanded" :visible-keys="visibleKeys"></field-list>
|
||||
</div>
|
||||
@@ -42,19 +41,15 @@ const validValues = computed(() => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="postcss" scoped>
|
||||
.text-light {
|
||||
@apply text-base-content/70;
|
||||
}
|
||||
.fields {
|
||||
display: inline-block;
|
||||
list-style: none;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
&::after {
|
||||
content: "expand json";
|
||||
color: var(--secondary-color);
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
@apply ml-2 inline-block text-secondary;
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
}
|
||||
@@ -64,13 +59,5 @@ const validValues = computed(() => {
|
||||
content: "collapse json";
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
|
||||
& + li {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="icon is-small health" :health="health" v-if="health" :title="health">
|
||||
<div class="inline-flex h-4 w-4" :health="health" v-if="health" :title="health">
|
||||
<cil:check-circle v-if="health == 'healthy'" />
|
||||
<cil:x-circle v-else-if="health == 'unhealthy'" />
|
||||
<cil:circle v-else />
|
||||
@@ -14,14 +14,12 @@ defineProps<{
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.health {
|
||||
&[health="unhealthy"] {
|
||||
color: var(--red-color);
|
||||
}
|
||||
<style lang="postcss" scoped>
|
||||
[health="unhealthy"] {
|
||||
@apply text-red;
|
||||
}
|
||||
|
||||
&[health="healthy"] {
|
||||
color: var(--green-color);
|
||||
}
|
||||
[health="healthy"] {
|
||||
@apply text-green;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<template>
|
||||
<div>
|
||||
<span class="has-text-weight-light"> RUNNING </span>
|
||||
<span class="has-text-weight-semibold">
|
||||
<span class="font-light capitalize"> RUNNING </span>
|
||||
<span class="font-semibold">
|
||||
<distance-time :date="container.created" strict :suffix="false"></distance-time>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="has-text-weight-light"> LOAD </span>
|
||||
<span class="has-text-weight-semibold"> {{ container.stat.cpu }}% </span>
|
||||
<span class="font-light capitalize"> Load </span>
|
||||
<span class="font-semibold"> {{ container.stat.cpu }}% </span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="has-text-weight-light"> MEM </span>
|
||||
<span class="has-text-weight-semibold"> {{ formatBytes(container.stat.memoryUsage) }} </span>
|
||||
<span class="font-light capitalize"> MEM </span>
|
||||
<span class="font-semibold"> {{ formatBytes(container.stat.memoryUsage) }} </span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -22,5 +22,3 @@ const { container } = defineProps<{
|
||||
container: Container;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -1,25 +1,12 @@
|
||||
<template>
|
||||
<div class="is-size-7 is-uppercase columns is-marginless is-mobile is-vcentered" v-if="container.stat">
|
||||
<stat-monitor
|
||||
class="column is-narrow"
|
||||
:data="memoryData"
|
||||
label="mem"
|
||||
:stat-value="formatBytes(unref(container.stat).memoryUsage)"
|
||||
></stat-monitor>
|
||||
<stat-monitor
|
||||
class="column is-narrow"
|
||||
:data="cpuData"
|
||||
label="load"
|
||||
:stat-value="unref(container.stat).cpu + '%'"
|
||||
></stat-monitor>
|
||||
<div class="flex gap-4" v-if="container.stat">
|
||||
<stat-monitor :data="memoryData" label="mem" :stat-value="formatBytes(unref(container.stat).memoryUsage)" />
|
||||
<stat-monitor :data="cpuData" label="load" :stat-value="unref(container.stat).cpu + '%'" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Container } from "@/models/Container";
|
||||
import { type ComputedRef } from "vue";
|
||||
|
||||
const container = inject("container") as ComputedRef<Container>;
|
||||
const { container } = useContainerContext();
|
||||
|
||||
const cpuData = computedWithControl(
|
||||
() => container.value.stat,
|
||||
@@ -27,8 +14,8 @@ const cpuData = computedWithControl(
|
||||
const history = container.value.statHistory;
|
||||
const points: Point<unknown>[] = history.map((stat, i) => ({
|
||||
x: i,
|
||||
y: stat.snapshot.cpu,
|
||||
value: stat.snapshot.cpu + "%",
|
||||
y: Math.max(0, stat.snapshot.cpu),
|
||||
value: Math.max(0, stat.snapshot.cpu) + "%",
|
||||
}));
|
||||
return points;
|
||||
},
|
||||
@@ -47,24 +34,3 @@ const memoryData = computedWithControl(
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.has-border {
|
||||
border: 1px solid var(--primary-color);
|
||||
border-radius: 3px;
|
||||
padding: 1px 1px 0 1px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
|
||||
.has-background-body-color {
|
||||
background-color: var(--body-background-color);
|
||||
}
|
||||
|
||||
.is-top-left {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0.75em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,48 +1,37 @@
|
||||
<template>
|
||||
<div class="columns is-marginless has-text-weight-bold is-family-monospace">
|
||||
<div class="column is-ellipsis">
|
||||
<container-health :health="container.health" v-if="container.health"></container-health>
|
||||
<div class="name">
|
||||
<span>{{ container.name }}</span
|
||||
><span v-if="container.isSwarm" class="swarm-id">{{ container.swarmId }}</span>
|
||||
<div class="flex flex-1 items-center gap-2 truncate">
|
||||
<container-health :health="container.health" v-if="container.health"></container-health>
|
||||
<div class="inline-flex font-mono text-sm">
|
||||
<div v-if="config.hosts.length > 1" class="mobile-hidden font-thin">
|
||||
{{ container.hostLabel }}<span class="mx-2">/</span>
|
||||
</div>
|
||||
<div class="font-semibold">{{ container.name }}</div>
|
||||
<div
|
||||
class="mobile-hidden max-w-[1.5em] truncate transition-[max-width] hover:max-w-[400px]"
|
||||
v-if="container.isSwarm"
|
||||
>
|
||||
{{ container.swarmId }}
|
||||
</div>
|
||||
<tag class="is-hidden-mobile">{{ container.image.replace(/@sha.*/, "") }}</tag>
|
||||
<span class="icon is-clickable" @click="togglePinnedContainer(container.storageKey)">
|
||||
<carbon:star-filled v-if="pinned" />
|
||||
<carbon:star v-else />
|
||||
</span>
|
||||
</div>
|
||||
<tag class="mobile-hidden font-mono" size="small">{{ container.image.replace(/@sha.*/, "") }}</tag>
|
||||
<label class="swap swap-rotate">
|
||||
<input type="checkbox" v-model="pinned" />
|
||||
<carbon:star-filled class="swap-on text-secondary" />
|
||||
<carbon:star class="swap-off" />
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Container } from "@/models/Container";
|
||||
import { type ComputedRef } from "vue";
|
||||
|
||||
const container = inject("container") as ComputedRef<Container>;
|
||||
const pinned = computed(() => pinnedContainers.value.has(container.value.storageKey));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.name {
|
||||
display: inline-flex;
|
||||
.swarm-id {
|
||||
max-width: 0;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
transition: max-width 0.2s ease-in-out;
|
||||
will-change: max-width;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.swarm-id {
|
||||
max-width: 400px;
|
||||
const { container } = useContainerContext();
|
||||
const pinned = computed({
|
||||
get: () => pinnedContainers.value.has(container.value.storageKey),
|
||||
set: (value) => {
|
||||
if (value) {
|
||||
pinnedContainers.value.add(container.value.storageKey);
|
||||
} else {
|
||||
pinnedContainers.value.delete(container.value.storageKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -1,24 +1,73 @@
|
||||
<template>
|
||||
<span class="text" :data-event="logEntry.event" v-html="logEntry.message"></span>
|
||||
<div class="flex-1 font-sans text-[0.9rem]">
|
||||
<span class="whitespace-pre-wrap" :data-event="logEntry.event" v-html="logEntry.message"></span>
|
||||
<div
|
||||
class="alert alert-info mt-8 w-auto text-[1rem] md:mx-auto md:w-1/2"
|
||||
v-if="nextContainer && logEntry.event === 'container-stopped'"
|
||||
>
|
||||
<carbon:information class="h-6 w-6 shrink-0 stroke-current" />
|
||||
<div>
|
||||
<h3 class="text-lg font-bold">{{ $t("alert.similar-container-found.title") }}</h3>
|
||||
{{ $t("alert.similar-container-found.message", { containerId: nextContainer.id }) }}
|
||||
</div>
|
||||
<div>
|
||||
<TimedButton v-if="automaticRedirect" class="btn-primary btn-sm" @finished="redirectNow()">Cancel</TimedButton>
|
||||
<router-link
|
||||
:to="{ name: 'container-id', params: { id: nextContainer.id } }"
|
||||
class="btn btn-primary btn-sm"
|
||||
v-else
|
||||
>
|
||||
Redirect
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DockerEventLogEntry } from "@/models/LogEntry";
|
||||
const router = useRouter();
|
||||
const { showToast } = useToast();
|
||||
const { t } = useI18n();
|
||||
|
||||
defineProps<{
|
||||
const { logEntry } = defineProps<{
|
||||
logEntry: DockerEventLogEntry;
|
||||
}>();
|
||||
|
||||
const store = useContainerStore();
|
||||
const { containers } = storeToRefs(store);
|
||||
const { container } = useContainerContext();
|
||||
|
||||
const nextContainer = computed(
|
||||
() =>
|
||||
containers.value
|
||||
.filter(
|
||||
(c) =>
|
||||
c.host === container.value.host &&
|
||||
c.created > logEntry.date &&
|
||||
c.name === container.value.name &&
|
||||
c.state === "running",
|
||||
)
|
||||
.toSorted((a, b) => +a.created - +b.created)[0],
|
||||
);
|
||||
|
||||
function redirectNow() {
|
||||
showToast(
|
||||
{
|
||||
title: t("alert.redirected.title"),
|
||||
message: t("alert.redirected.message", { containerId: nextContainer.value.id }),
|
||||
type: "info",
|
||||
},
|
||||
{ expire: 5000 },
|
||||
);
|
||||
router.push({ name: "container-id", params: { id: nextContainer.value.id } });
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
span {
|
||||
&[data-event="container-stopped"] {
|
||||
color: #f14668;
|
||||
}
|
||||
&[data-event="container-started"] {
|
||||
color: hsl(141, 53%, 53%);
|
||||
}
|
||||
&.text {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
<style lang="postcss" scoped>
|
||||
[data-event="container-stopped"] {
|
||||
@apply text-red;
|
||||
}
|
||||
[data-event="container-started"] {
|
||||
@apply text-green;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<ul v-if="expanded" ref="root">
|
||||
<ul v-if="expanded" ref="root" class="ml-8">
|
||||
<li v-for="(value, name) in fields">
|
||||
<template v-if="isObject(value)">
|
||||
<span class="has-text-grey">{{ name }}=</span>
|
||||
<span class="text-light">{{ name }}=</span>
|
||||
<field-list
|
||||
:fields="value"
|
||||
:parent-key="parentKey.concat(name)"
|
||||
@@ -11,16 +11,20 @@
|
||||
></field-list>
|
||||
</template>
|
||||
<template v-else-if="Array.isArray(value)">
|
||||
<a @click="toggleField(name)"> {{ hasField(name) ? "remove" : "add" }} </a>
|
||||
<span class="has-text-grey">{{ name }}=</span>[
|
||||
<span class="has-text-weight-bold" v-for="(item, index) in value">
|
||||
<a @click="toggleField(name)" class="link-primary cursor-pointer">
|
||||
{{ hasField(name) ? "remove" : "add" }} </a
|
||||
>
|
||||
<span class="text-light">{{ name }}=</span>[
|
||||
<span class="font-bold" v-for="(item, index) in value">
|
||||
<span v-html="JSON.stringify(item)"></span><span v-if="index !== value.length - 1">,</span>
|
||||
</span>
|
||||
]
|
||||
</template>
|
||||
<template v-else>
|
||||
<a @click="toggleField(name)"> {{ hasField(name) ? "remove" : "add" }} </a>
|
||||
<span class="has-text-grey">{{ name }}=</span><span class="has-text-weight-bold" v-html="value"></span>
|
||||
<a @click="toggleField(name)" class="link-primary cursor-pointer">
|
||||
{{ hasField(name) ? "remove" : "add" }} </a
|
||||
>
|
||||
<span class="text-light">{{ name }}=</span><span class="font-bold" v-html="value"></span>
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -68,8 +72,8 @@ function fieldIndex(field: string) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
ul {
|
||||
margin-left: 2em;
|
||||
<style scoped lang="postcss">
|
||||
.text-light {
|
||||
@apply text-base-content/70;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,137 +1,89 @@
|
||||
<template>
|
||||
<dropdown-menu class="is-right">
|
||||
<template #trigger>
|
||||
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
|
||||
<span class="icon">
|
||||
<carbon:circle-solid class="is-red is-small" v-if="streamConfig.stderr" />
|
||||
<carbon:circle-solid class="is-blue is-small" v-if="streamConfig.stdout" />
|
||||
</span>
|
||||
</button>
|
||||
</template>
|
||||
<a class="dropdown-item" @click="clear()">
|
||||
<div class="level is-justify-content-start">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<octicon:trash-24 />
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right is-justify-content-space-between is-flex-grow-1">
|
||||
<div class="level-item">{{ $t("toolbar.clear") }}</div>
|
||||
<div class="level-item"><key-shortcut char="k" :modifiers="['shift', 'meta']"></key-shortcut></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="dropdown-item" :href="`${base}/api/logs/download/${container.host}/${container.id}`">
|
||||
<div class="level is-justify-content-start">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<octicon:download-24 />
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">{{ $t("toolbar.download") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<hr class="dropdown-divider" />
|
||||
<a class="dropdown-item" @click="showSearch = true">
|
||||
<div class="level is-justify-content-start">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<mdi:light-magnify />
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right is-justify-content-space-between is-flex-grow-1">
|
||||
<div class="level-item">{{ $t("toolbar.search") }}</div>
|
||||
<div class="level-item"><key-shortcut char="f"></key-shortcut></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<hr class="dropdown-divider" />
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
streamConfig.stdout = true;
|
||||
streamConfig.stderr = true;
|
||||
"
|
||||
>
|
||||
<div class="level is-justify-content-start">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<div class="dropdown dropdown-end dropdown-hover">
|
||||
<label tabindex="0" class="btn btn-ghost btn-sm gap-0.5 px-2">
|
||||
<carbon:circle-solid class="w-2.5 text-red" v-if="streamConfig.stderr" />
|
||||
<carbon:circle-solid class="w-2.5 text-blue" v-if="streamConfig.stdout" />
|
||||
</label>
|
||||
<ul tabindex="0" class="menu dropdown-content rounded-box z-50 w-52 bg-base p-1 shadow">
|
||||
<li>
|
||||
<a @click.prevent="clear()">
|
||||
<octicon:trash-24 /> {{ $t("toolbar.clear") }}
|
||||
<key-shortcut char="k" :modifiers="['shift', 'meta']"></key-shortcut>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a :href="`${base}/api/logs/download/${container.host}/${container.id}`" download>
|
||||
<octicon:download-24 /> {{ $t("toolbar.download") }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a @click.prevent="showSearch = true">
|
||||
<mdi:light-magnify /> {{ $t("toolbar.search") }}
|
||||
<key-shortcut char="f"></key-shortcut>
|
||||
</a>
|
||||
</li>
|
||||
<li class="line"></li>
|
||||
<li>
|
||||
<a
|
||||
@click="
|
||||
streamConfig.stdout = true;
|
||||
streamConfig.stderr = true;
|
||||
"
|
||||
>
|
||||
<div class="flex h-4 w-4 gap-0.5">
|
||||
<template v-if="streamConfig.stderr && streamConfig.stdout">
|
||||
<carbon:circle-solid class="is-red is-small" />
|
||||
<carbon:circle-solid class="is-blue is-small" />
|
||||
<carbon:circle-solid class="w-2 text-red" />
|
||||
<carbon:circle-solid class="w-2 text-blue" />
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
{{ $t("toolbar.show-all") }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
streamConfig.stdout = true;
|
||||
streamConfig.stderr = false;
|
||||
"
|
||||
>
|
||||
<div class="level is-justify-content-start">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<carbon:circle-solid class="is-blue is-small" v-if="!streamConfig.stderr && streamConfig.stdout" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
@click="
|
||||
streamConfig.stdout = true;
|
||||
streamConfig.stderr = false;
|
||||
"
|
||||
>
|
||||
<div class="flex h-4 w-4 flex-col gap-1">
|
||||
<carbon:circle-solid class="w-2 text-blue" v-if="!streamConfig.stderr && streamConfig.stdout" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
{{ $t("toolbar.show", { std: "STDOUT" }) }}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<a
|
||||
class="dropdown-item"
|
||||
@click="
|
||||
streamConfig.stdout = false;
|
||||
streamConfig.stderr = true;
|
||||
"
|
||||
>
|
||||
<div class="level is-justify-content-start">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<carbon:circle-solid class="is-red is-small" v-if="streamConfig.stderr && !streamConfig.stdout" />
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
@click="
|
||||
streamConfig.stdout = false;
|
||||
streamConfig.stderr = true;
|
||||
"
|
||||
>
|
||||
<div class="flex h-4 w-4 flex-col gap-1">
|
||||
<carbon:circle-solid class="w-2 text-red" v-if="streamConfig.stderr && !streamConfig.stdout" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">
|
||||
{{ $t("toolbar.show", { std: "STDERR" }) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</dropdown-menu>
|
||||
{{ $t("toolbar.show", { std: "STDERR" }) }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type ComputedRef } from "vue";
|
||||
import { Container } from "@/models/Container";
|
||||
|
||||
const { showSearch } = useSearchFilter();
|
||||
const { base } = config;
|
||||
|
||||
const clear = defineEmit();
|
||||
|
||||
const container = inject("container") as ComputedRef<Container>;
|
||||
const streamConfig = inject("stream-config") as { stdout: boolean; stderr: boolean };
|
||||
const { container, streamConfig } = useContainerContext();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.level-left .level-item {
|
||||
width: 2.2em;
|
||||
align-items: center;
|
||||
margin-right: 0.5em;
|
||||
<style scoped lang="postcss">
|
||||
li.line {
|
||||
@apply h-px bg-base-content/20;
|
||||
}
|
||||
|
||||
.is-small {
|
||||
width: 0.6em;
|
||||
a {
|
||||
@apply whitespace-nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,14 @@
|
||||
<template>
|
||||
<scrollable-view :scrollable="scrollable" v-if="container">
|
||||
<template #header v-if="showTitle">
|
||||
<div class="mr-0 columns is-mobile is-vcentered is-marginless has-boxshadow">
|
||||
<div class="column is-clipped is-paddingless">
|
||||
<container-title @close="$emit('close')" />
|
||||
</div>
|
||||
<div class="column is-narrow is-paddingless">
|
||||
<container-stat />
|
||||
</div>
|
||||
<div class="mx-2 flex items-center gap-2">
|
||||
<container-title @close="$emit('close')" />
|
||||
<container-stat class="ml-auto" />
|
||||
|
||||
<div class="mr-2 column is-narrow is-paddingless is-hidden-mobile">
|
||||
<log-actions-toolbar @clear="onClearClicked()" />
|
||||
</div>
|
||||
<div class="mr-2 column is-narrow is-paddingless" v-if="closable">
|
||||
<button class="delete is-medium" @click="close()"></button>
|
||||
</div>
|
||||
<log-actions-toolbar @clear="onClearClicked()" class="mobile-hidden" />
|
||||
<a class="btn btn-circle btn-xs" @click="close()" v-if="closable">
|
||||
<mdi:close />
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{ setLoading }">
|
||||
@@ -41,12 +35,8 @@ const {
|
||||
const close = defineEmit();
|
||||
|
||||
const store = useContainerStore();
|
||||
|
||||
const container = store.currentContainer($$(id));
|
||||
const config = reactive({ stdout: true, stderr: true });
|
||||
|
||||
provide("container", container);
|
||||
provide("stream-config", config);
|
||||
provideContainerContext(container);
|
||||
|
||||
const viewer = ref<InstanceType<typeof LogViewerWithSource>>();
|
||||
|
||||
@@ -61,18 +51,3 @@ onKeyStroke("k", (e) => {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
button.delete {
|
||||
background-color: var(--scheme-main-ter);
|
||||
opacity: 0.6;
|
||||
|
||||
&:after,
|
||||
&:before {
|
||||
background-color: var(--text-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,19 +1,10 @@
|
||||
<template>
|
||||
<date-time :date="date" class="date"></date-time>
|
||||
<tag size="small">
|
||||
<date-time :date="date" class="whitespace-nowrap text-blue"></date-time>
|
||||
</tag>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
defineProps<{
|
||||
date: Date;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.date {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
background-color: var(--scheme-main-ter);
|
||||
color: #258ccd;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,15 +4,16 @@ import { createTestingPinia } from "@pinia/testing";
|
||||
import EventSource, { sources } from "eventsourcemock";
|
||||
import LogEventSource from "./LogEventSource.vue";
|
||||
import LogViewer from "./LogViewer.vue";
|
||||
import { settings } from "../../composables/settings";
|
||||
import { settings } from "@/composables/settings";
|
||||
import { useSearchFilter } from "@/composables/search";
|
||||
import { vi, describe, expect, beforeEach, test, afterEach } from "vitest";
|
||||
import { computed, nextTick } from "vue";
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import { containerContext } from "@/composables/containerContext";
|
||||
|
||||
vi.mock("@/stores/config", () => ({
|
||||
__esModule: true,
|
||||
default: { base: "", hosts: ["localhost"] },
|
||||
default: { base: "", hosts: [{ name: "localhost", id: "localhost" }] },
|
||||
}));
|
||||
|
||||
/**
|
||||
@@ -71,8 +72,10 @@ describe("<LogEventSource />", () => {
|
||||
LogViewer,
|
||||
},
|
||||
provide: {
|
||||
container: computed(() => ({ id: "abc", image: "test:v123", host: "localhost" })),
|
||||
"stream-config": reactive({ stdout: true, stderr: true }),
|
||||
[containerContext as symbol]: {
|
||||
container: computed(() => ({ id: "abc", image: "test:v123", host: "localhost" })),
|
||||
streamConfig: reactive({ stdout: true, stderr: true }),
|
||||
},
|
||||
scrollingPaused: computed(() => false),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,14 +4,10 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Container } from "@/models/Container";
|
||||
import { type ComputedRef } from "vue";
|
||||
|
||||
const loadingMore = defineEmit<[value: boolean]>();
|
||||
|
||||
const container = inject("container") as ComputedRef<Container>;
|
||||
const config = inject("stream-config") as { stdout: boolean; stderr: boolean };
|
||||
const { messages, loadOlderLogs } = useLogStream(container, config);
|
||||
const { container, streamConfig } = useContainerContext();
|
||||
const { messages, loadOlderLogs } = useLogStream(container, streamConfig);
|
||||
|
||||
const beforeLoading = () => loadingMore(true);
|
||||
const afterLoading = () => loadingMore(false);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div :class="level" :data-position="position"></div>
|
||||
<div :data-level="level" :data-position="position" class="mt-1.5 h-2.5 w-2.5 flex-none rounded-lg"></div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { Position } from "@/models/LogEntry";
|
||||
@@ -10,54 +10,45 @@ defineProps<{
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
div {
|
||||
display: inline-block;
|
||||
width: 0.7em;
|
||||
height: 0.7em;
|
||||
border-radius: 0.5em;
|
||||
align-self: auto;
|
||||
margin-top: 0.4em;
|
||||
<style lang="postcss" scoped>
|
||||
[data-position="start"] {
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
height: 70%;
|
||||
margin-bottom: -0.4em;
|
||||
margin-top: auto;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
&[data-position="start"] {
|
||||
border-radius: 0.5em 0.5em 0 0;
|
||||
height: 70%;
|
||||
margin-bottom: -0.2em;
|
||||
margin-top: auto;
|
||||
align-self: flex-end;
|
||||
}
|
||||
[data-position="middle"] {
|
||||
border-radius: 0;
|
||||
height: auto;
|
||||
margin: -0.4em 0;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
&[data-position="middle"] {
|
||||
border-radius: 0;
|
||||
height: auto;
|
||||
margin: -0.2em 0;
|
||||
align-self: auto;
|
||||
}
|
||||
[data-position="end"] {
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
height: 70%;
|
||||
margin-top: -0.4em;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
&[data-position="end"] {
|
||||
border-radius: 0 0 0.5em 0.5em;
|
||||
height: 70%;
|
||||
margin-top: -0.2em;
|
||||
align-self: flex-start;
|
||||
}
|
||||
[data-level="debug"],
|
||||
[data-level="trace"] {
|
||||
@apply bg-purple;
|
||||
}
|
||||
|
||||
&.debug,
|
||||
&.trace {
|
||||
background-color: var(--purple-color);
|
||||
}
|
||||
[data-level="info"] {
|
||||
@apply bg-green;
|
||||
}
|
||||
|
||||
&.info {
|
||||
background-color: var(--green-color);
|
||||
}
|
||||
[data-level="error"],
|
||||
[data-level="fatal"] {
|
||||
@apply bg-red;
|
||||
}
|
||||
|
||||
&.error,
|
||||
&.fatal {
|
||||
background-color: var(--red-color);
|
||||
}
|
||||
|
||||
&.warn,
|
||||
&.warning {
|
||||
background-color: var(--orange-color);
|
||||
}
|
||||
[data-level="warn"],
|
||||
[data-level="warning"] {
|
||||
@apply bg-orange;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,14 +12,12 @@ defineProps<{
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tag {
|
||||
&[std="stdout"] {
|
||||
color: var(--blue-color);
|
||||
}
|
||||
<style lang="postcss" scoped>
|
||||
[std="stdout"] {
|
||||
@apply text-blue;
|
||||
}
|
||||
|
||||
&[std="stderr"] {
|
||||
color: var(--red-color);
|
||||
}
|
||||
[std="stderr"] {
|
||||
@apply text-red;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,43 +1,38 @@
|
||||
<template>
|
||||
<ul class="events" ref="events" :class="{ 'disable-wrap': !softWrap, [size]: true }">
|
||||
<ul class="events group py-4" :class="{ 'disable-wrap': !softWrap, [size]: true }">
|
||||
<li
|
||||
v-for="(item, index) in filtered"
|
||||
v-for="item in filtered"
|
||||
:key="item.id"
|
||||
:data-key="item.id"
|
||||
:class="{ selected: toRaw(item) === toRaw(lastSelectedItem) }"
|
||||
:class="{ 'border border-secondary': toRaw(item) === toRaw(lastSelectedItem) }"
|
||||
>
|
||||
<div class="line-options" v-show="isSearching()">
|
||||
<dropdown-menu :class="{ 'is-last': index === filtered.length - 1 }" class="is-top minimal">
|
||||
<a class="dropdown-item" @click="handleJumpLineSelected($event, item)" :href="`#${item.id}`">
|
||||
<div class="level is-justify-content-start">
|
||||
<div class="level-left">
|
||||
<div class="level-item">
|
||||
<cil:find-in-page class="mr-4" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="level-item">Jump to Context</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</dropdown-menu>
|
||||
</div>
|
||||
<component :is="item.getComponent()" :log-entry="item" :visible-keys="visibleKeys.value"></component>
|
||||
<a
|
||||
class="jump-context tooltip-primary tooltip tooltip-right"
|
||||
v-if="isSearching()"
|
||||
data-tip="Jump to Context"
|
||||
@click="handleJumpLineSelected($event, item)"
|
||||
:href="`#${item.id}`"
|
||||
>
|
||||
<ic:sharp-find-in-page />
|
||||
</a>
|
||||
<component :is="item.getComponent()" :log-entry="item" :visible-keys="visibleKeys.value" />
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { type ComputedRef, toRaw } from "vue";
|
||||
import { toRaw } from "vue";
|
||||
import { useRouteHash } from "@vueuse/router";
|
||||
import { Container } from "@/models/Container";
|
||||
|
||||
import { type JSONObject, LogEntry } from "@/models/LogEntry";
|
||||
|
||||
const props = defineProps<{
|
||||
messages: LogEntry<string | JSONObject>[];
|
||||
}>();
|
||||
|
||||
let visibleKeys = persistentVisibleKeys(inject("container") as ComputedRef<Container>);
|
||||
const { container } = useContainerContext();
|
||||
|
||||
let visibleKeys = persistentVisibleKeys(container);
|
||||
|
||||
const { filteredPayload } = useVisibleFilter(visibleKeys);
|
||||
const { filteredMessages, resetSearch, isSearching } = useSearchFilter();
|
||||
@@ -46,7 +41,6 @@ const { messages } = toRefs(props);
|
||||
const visible = filteredPayload(messages);
|
||||
const filtered = filteredMessages(visible);
|
||||
|
||||
const events = ref<HTMLElement>();
|
||||
let lastSelectedItem: LogEntry<string | JSONObject> | undefined = $ref(undefined);
|
||||
|
||||
function handleJumpLineSelected(e: Event, item: LogEntry<string | JSONObject>) {
|
||||
@@ -65,9 +59,8 @@ watch(
|
||||
{ immediate: true, flush: "post" },
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
<style scoped lang="postcss">
|
||||
.events {
|
||||
padding: 1em 0;
|
||||
font-family:
|
||||
ui-monospace,
|
||||
SFMono-Regular,
|
||||
@@ -78,41 +71,27 @@ watch(
|
||||
Menlo,
|
||||
monospace;
|
||||
|
||||
& > li {
|
||||
display: flex;
|
||||
word-wrap: break-word;
|
||||
padding: 0.2em 1em;
|
||||
|
||||
> li {
|
||||
@apply flex break-words px-4 py-1 last:snap-end odd:bg-gray-400/[0.07];
|
||||
&:last-child {
|
||||
scroll-snap-align: end;
|
||||
scroll-margin-block-end: 5rem;
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
background-color: rgba(125, 125, 125, 0.08);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
border: 1px var(--secondary-color) solid;
|
||||
}
|
||||
|
||||
& > .line-options {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
margin-right: 1em;
|
||||
.jump-context {
|
||||
@apply mr-2 flex items-center font-sans text-secondary hover:text-secondary-focus;
|
||||
}
|
||||
}
|
||||
|
||||
&.small {
|
||||
font-size: 60%;
|
||||
@apply text-[0.7em];
|
||||
}
|
||||
|
||||
&.medium {
|
||||
font-size: 80%;
|
||||
@apply text-[0.8em];
|
||||
}
|
||||
|
||||
&.large {
|
||||
font-size: 120%;
|
||||
@apply text-lg;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
<template>
|
||||
<div class="columns is-1 is-variable is-mobile">
|
||||
<div class="column is-narrow" v-if="showStd">
|
||||
<log-std :std="logEntry.std"></log-std>
|
||||
</div>
|
||||
<div class="column is-narrow" v-if="showTimestamp">
|
||||
<log-date :date="logEntry.date"></log-date>
|
||||
</div>
|
||||
<div class="column is-narrow is-flex">
|
||||
<log-level :level="logEntry.level" :position="logEntry.position"></log-level>
|
||||
</div>
|
||||
<div class="text column" v-html="colorize(logEntry.message)"></div>
|
||||
<div class="flex items-start gap-x-2">
|
||||
<log-std :std="logEntry.std" v-if="showStd" />
|
||||
<log-date :date="logEntry.date" v-if="showTimestamp" />
|
||||
<log-level class="flex" :level="logEntry.level" :position="logEntry.position" />
|
||||
<div class="whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap" v-html="colorize(logEntry.message)"></div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
@@ -24,15 +18,3 @@ defineProps<{
|
||||
const { markSearch } = useSearchFilter();
|
||||
const colorize = (value: string) => markSearch(ansiConvertor.toHtml(value));
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.disable-wrap {
|
||||
.text {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<template>
|
||||
<div class="is-flex-grow-1 has-text-centered my-4">
|
||||
<div class="is-relative">
|
||||
<zig-zag class="is-overlay mt-2"></zig-zag>
|
||||
<span class="text is-relative py-2 px-4">{{ $t("error.logs-skipped", { total: logEntry.totalSkipped }) }}</span>
|
||||
<div class="my-4 flex-1 text-center">
|
||||
<div class="relative">
|
||||
<zig-zag class="absolute inset-0 mt-2"></zig-zag>
|
||||
<span class="relative whitespace-pre-wrap bg-base px-4 py-2 font-bold">{{
|
||||
$t("error.logs-skipped", { total: logEntry.totalSkipped })
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -13,11 +15,3 @@ defineProps<{
|
||||
logEntry: SkippedLogsEntry;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.text {
|
||||
font-weight: bold;
|
||||
white-space: pre-wrap;
|
||||
background-color: var(--body-background-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<template>
|
||||
<div class="has-text-centered is-relative host" @mouseenter="mouseOver = true" @mouseleave="mouseOver = false">
|
||||
<div class="has-border has-boxshadow is-hidden-mobile">
|
||||
<div class="relative hover:text-secondary" @mouseenter="mouseOver = true" @mouseleave="mouseOver = false">
|
||||
<div class="mobile-hidden flex overflow-hidden rounded-sm border border-primary px-px pb-px pt-1">
|
||||
<stat-sparkline :data="data" @selected-point="onSelectedPoint"></stat-sparkline>
|
||||
</div>
|
||||
<div class="has-background-body-color is-top-left">
|
||||
<span class="has-text-weight-light">{{ label }}</span>
|
||||
<span class="has-text-weight-bold">
|
||||
<div class="inline-flex gap-1 rounded bg-base p-px text-xs lg:absolute lg:-left-0.5 lg:-top-2">
|
||||
<div class="font-light uppercase">{{ label }}</div>
|
||||
<div class="select-none font-bold">
|
||||
{{ mouseOver ? selectedPoint?.value ?? selectedPoint?.y ?? statValue : statValue }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -23,30 +23,3 @@ function onSelectedPoint(point: Point<unknown>) {
|
||||
|
||||
let mouseOver = $ref(false);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.has-border {
|
||||
border: 1px solid var(--primary-color);
|
||||
border-radius: 3px;
|
||||
padding: 1px 1px 0 1px;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
padding-top: 0.25em;
|
||||
}
|
||||
|
||||
.has-background-body-color {
|
||||
background-color: var(--body-background-color);
|
||||
}
|
||||
|
||||
.host:hover span {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
.is-top-left {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0.75em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<svg :width="width" :height="height" @mousemove="onMove">
|
||||
<path :d="path" class="area" />
|
||||
<line :x1="lineX" y1="0" :x2="lineX" :y2="height" class="line" />
|
||||
<svg :width="width" :height="height" @mousemove="onMove" class="group">
|
||||
<path :d="path" class="fill-primary" />
|
||||
<line :x1="lineX" y1="0" :x2="lineX" :y2="height" class="invisible stroke-secondary stroke-2 group-hover:visible" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@@ -42,19 +42,3 @@ function onMove(e: MouseEvent) {
|
||||
selectedPoint(point);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.area) {
|
||||
fill: var(--primary-color);
|
||||
}
|
||||
|
||||
:deep(.line) {
|
||||
stroke: var(--secondary-color);
|
||||
stroke-width: 2;
|
||||
display: none;
|
||||
}
|
||||
|
||||
svg:hover :deep(.line) {
|
||||
display: unset;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,18 +2,10 @@
|
||||
<svg width="100%" height="8" class="zigzag">
|
||||
<defs>
|
||||
<pattern id="zigzag" x="0" y="0" width="30" height="8" patternUnits="userSpaceOnUse">
|
||||
<line x1="0" y1="0" x2="15" y2="8" class="line" />
|
||||
<line x1="15" y1="8" x2="30" y2="0" class="line" />
|
||||
<line x1="0" y1="0" x2="15" y2="8" class="stroke-primary stroke-1" />
|
||||
<line x1="15" y1="8" x2="30" y2="0" class="stroke-primary stroke-1" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="100%" height="100%" fill="url(#zigzag)"></rect>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.line {
|
||||
stroke: var(--primary-color);
|
||||
stroke-width: 1;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,237 +1,105 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<LogEventSource /> > render html correctly > should render dates with 12 hour style 1`] = `
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
|
||||
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
|
||||
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
|
||||
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
|
||||
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
|
||||
</svg></div>
|
||||
</div>
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
|
||||
</div>
|
||||
</div>
|
||||
</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
|
||||
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
|
||||
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
|
||||
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"text column\\"><test>foo bar</test></div>
|
||||
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
|
||||
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><test>foo bar</test></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`<LogEventSource /> > render html correctly > should render dates with 24 hour style 1`] = `
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
|
||||
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
|
||||
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
|
||||
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
|
||||
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
|
||||
</svg></div>
|
||||
</div>
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
|
||||
</div>
|
||||
</div>
|
||||
</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
|
||||
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42</time></div>
|
||||
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
|
||||
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42</time></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
|
||||
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"text column\\"><test>foo bar</test></div>
|
||||
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
|
||||
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><test>foo bar</test></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages 1`] = `
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
|
||||
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
|
||||
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
|
||||
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
|
||||
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
|
||||
</svg></div>
|
||||
</div>
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
|
||||
</div>
|
||||
</div>
|
||||
</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
|
||||
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
|
||||
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
|
||||
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"text column\\">This is a message.</div>
|
||||
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
|
||||
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\">This is a message.</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages with color 1`] = `
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
|
||||
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
|
||||
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
|
||||
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
|
||||
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
|
||||
</svg></div>
|
||||
</div>
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
|
||||
</div>
|
||||
</div>
|
||||
</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
|
||||
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
|
||||
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
|
||||
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"text column\\"><span style=\\"color:#000\\">black<span style=\\"color:#AAA\\">white</span></span></div>
|
||||
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
|
||||
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><span style=\\"color:#000\\">black<span style=\\"color:#AAA\\">white</span></span></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages with filter 1`] = `
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
|
||||
<li data-v-2e92daca=\\"\\" data-key=\\"2\\" class=\\"\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"line-options\\">
|
||||
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#2\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
|
||||
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
|
||||
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
|
||||
</svg></div>
|
||||
</div>
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
|
||||
</div>
|
||||
</div>
|
||||
</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
|
||||
<li data-v-2e92daca=\\"\\" data-key=\\"2\\" class=\\"\\"><a data-v-2e92daca=\\"\\" class=\\"jump-context tooltip-primary tooltip tooltip-right\\" data-tip=\\"Jump to Context\\" href=\\"#2\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\">
|
||||
<path fill=\\"currentColor\\" d=\\"M20 19.59V8l-6-6H4v20l15.57-.02l-4.81-4.81c-.8.52-1.74.83-2.76.83c-2.76 0-5-2.24-5-5s2.24-5 5-5s5 2.24 5 5c0 1.02-.31 1.96-.83 2.75L20 19.59zM9 13c0 1.66 1.34 3 3 3s3-1.34 3-3s-1.34-3-3-3s-3 1.34-3 3z\\"></path>
|
||||
</svg></a>
|
||||
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
|
||||
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
|
||||
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
|
||||
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"text column\\"><mark>test</mark> bar</div>
|
||||
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
|
||||
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><mark>test</mark> bar</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages with html entities 1`] = `
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events medium\\">
|
||||
"<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\">
|
||||
<li data-v-2e92daca=\\"\\" data-key=\\"1\\" class=\\"\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"line-options\\" style=\\"display: none;\\">
|
||||
<div data-v-539164cb=\\"\\" data-v-2e92daca=\\"\\" class=\\"dropdown is-hoverable is-last is-top minimal\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-trigger\\"><button data-v-539164cb=\\"\\" class=\\"button\\" aria-haspopup=\\"true\\" aria-controls=\\"dropdown-menu\\"><span data-v-539164cb=\\"\\" class=\\"icon\\"><svg data-v-539164cb=\\"\\" viewBox=\\"0 0 24 24\\" width=\\"1.2em\\" height=\\"1.2em\\"><path fill=\\"currentColor\\" d=\\"M12 16a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2m0-6a2 2 0 0 1 2 2a2 2 0 0 1-2 2a2 2 0 0 1-2-2a2 2 0 0 1 2-2Z\\"></path></svg></span></button></div>
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-menu\\" id=\\"dropdown-menu\\" role=\\"menu\\">
|
||||
<div data-v-539164cb=\\"\\" class=\\"dropdown-content\\"><a data-v-2e92daca=\\"\\" class=\\"dropdown-item\\" href=\\"#1\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level is-justify-content-start\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-left\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\"><svg data-v-2e92daca=\\"\\" viewBox=\\"0 0 512 512\\" width=\\"1.2em\\" height=\\"1.2em\\" class=\\"mr-4\\">
|
||||
<path fill=\\"currentColor\\" d=\\"M334.627 16H48v480h424V153.373ZM440 464H80V48h241.373L440 166.627Z\\"></path>
|
||||
<path fill=\\"currentColor\\" d=\\"M239.861 152a95.861 95.861 0 1 0 53.624 175.284l68.03 68.029l22.627-22.626l-67.5-67.5A95.816 95.816 0 0 0 239.861 152ZM176 247.861a63.862 63.862 0 1 1 63.861 63.861A63.933 63.933 0 0 1 176 247.861Z\\"></path>
|
||||
</svg></div>
|
||||
</div>
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-right\\">
|
||||
<div data-v-2e92daca=\\"\\" class=\\"level-item\\">Jump to Context</div>
|
||||
</div>
|
||||
</div>
|
||||
</a></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" data-v-2e92daca=\\"\\" class=\\"columns is-1 is-variable is-mobile\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca=\\"\\" class=\\"flex items-start gap-x-2\\" visible-keys=\\"\\">
|
||||
<!--v-if-->
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow\\">
|
||||
<div data-v-de513450=\\"\\" data-v-a49e52d4=\\"\\" class=\\"date\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"is-hidden-mobile\\">06/12/2019</time> <time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
<div data-v-961504e7=\\"\\" class=\\"inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm\\" size=\\"small\\">
|
||||
<div class=\\"inline-flex gap-2 whitespace-nowrap text-blue\\"><time datetime=\\"2019-06-12T10:55:42.459Z\\" class=\\"mobile-hidden\\">06/12/2019</time><time datetime=\\"2019-06-12T10:55:42.459Z\\">10:55:42 AM</time></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"column is-narrow is-flex\\">
|
||||
<div data-v-e625cddd=\\"\\" data-v-a49e52d4=\\"\\" class=\\"\\"></div>
|
||||
</div>
|
||||
<div data-v-a49e52d4=\\"\\" class=\\"text column\\"><test>foo bar</test></div>
|
||||
<div data-v-e625cddd=\\"\\" class=\\"mt-1.5 h-2.5 w-2.5 flex-none rounded-lg flex\\"></div>
|
||||
<div class=\\"whitespace-pre-wrap group-[.disable-wrap]:whitespace-nowrap\\"><test>foo bar</test></div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`<LogEventSource /> > renders correctly 1`] = `
|
||||
"<div data-v-1cd63c6e=\\"\\" class=\\"infinte-loader\\">
|
||||
<div data-v-1cd63c6e=\\"\\" class=\\"spinner\\" style=\\"display: none;\\">
|
||||
<div data-v-1cd63c6e=\\"\\" class=\\"bounce1\\"></div>
|
||||
<div data-v-1cd63c6e=\\"\\" class=\\"bounce2\\"></div>
|
||||
<div data-v-1cd63c6e=\\"\\" class=\\"bounce3\\"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul data-v-2e92daca=\\"\\" class=\\"events medium\\"></ul>"
|
||||
"<div class=\\"flex min-h-[1px] justify-center\\"><span class=\\"loading loading-bars loading-md mt-4 text-primary\\" style=\\"display: none;\\"></span></div>
|
||||
<ul data-v-2e92daca=\\"\\" class=\\"events group py-4 medium\\"></ul>"
|
||||
`;
|
||||
|
||||
exports[`<LogEventSource /> > should parse messages 1`] = `
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
<template>
|
||||
<div @mouseenter="onMouseEnter" @mouseleave="onMouseLeave" ref="trigger"><slot></slot></div>
|
||||
<Teleport to="body">
|
||||
<Transition name="fade">
|
||||
<div v-show="show && (delayedShow || glopbalShow)" class="content" ref="content">
|
||||
<slot></slot>
|
||||
<teleport to="body">
|
||||
<transition name="fade">
|
||||
<div
|
||||
v-show="show && (delayedShow || glopbalShow)"
|
||||
class="fixed z-50 rounded border border-secondary/50 bg-base-lighter p-4 shadow"
|
||||
ref="content"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</transition>
|
||||
</teleport>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -17,9 +21,8 @@ let show = ref(glopbalShow.value);
|
||||
let delayedShow = refDebounced(show, 1000);
|
||||
|
||||
let content: HTMLElement | null = $ref(null);
|
||||
let trigger: HTMLElement | null = $ref(null);
|
||||
|
||||
function onMouseEnter(e: MouseEvent) {
|
||||
const onMouseEnter = (e: Event) => {
|
||||
show.value = true;
|
||||
glopbalShow.value = true;
|
||||
if (e.target && content && e.target instanceof Element) {
|
||||
@@ -30,22 +33,26 @@ function onMouseEnter(e: MouseEvent) {
|
||||
content.style.left = `${x}px`;
|
||||
content.style.top = `${y}px`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onMouseLeave() {
|
||||
const onMouseLeave = () => {
|
||||
show.value = false;
|
||||
glopbalShow.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const el = useCurrentElement();
|
||||
useEventListener(() => el.value?.nextElementSibling, "mouseenter", onMouseEnter);
|
||||
useEventListener(() => el.value?.nextElementSibling, "mouseleave", onMouseLeave);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
position: fixed;
|
||||
z-index: 9999;
|
||||
background: var(--scheme-main-ter);
|
||||
border-radius: 0.5em;
|
||||
padding: 1em;
|
||||
box-shadow: 0 0 0.5em rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid var(--border-color);
|
||||
<style scoped lang="postcss">
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
@apply transition-opacity;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
@apply opacity-0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<template>
|
||||
<transition name="fade">
|
||||
<div class="scroll-progress" ref="root" v-show="!autoHide || show">
|
||||
<transition name="fadeout">
|
||||
<div class="pointer-events-none relative inline-block" ref="root" v-show="!autoHide || show">
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" :class="{ indeterminate }">
|
||||
<circle r="44" cx="50" cy="50" />
|
||||
<circle r="44" cx="50" cy="50" class="fill-base-darker stroke-primary" />
|
||||
</svg>
|
||||
<div class="is-overlay columns is-vcentered is-centered has-text-weight-light">
|
||||
<div class="absolute inset-0 flex items-center justify-center font-light">
|
||||
<template v-if="indeterminate">
|
||||
<div class="column is-narrow is-paddingless is-size-2">∞</div>
|
||||
<div class="text-4xl">∞</div>
|
||||
</template>
|
||||
<template v-else-if="!isNaN(scrollProgress)">
|
||||
<span class="column is-narrow is-paddingless is-size-2">
|
||||
<span class="text-4xl">
|
||||
{{ Math.ceil(scrollProgress * 100) }}
|
||||
</span>
|
||||
<span class="column is-narrow is-paddingless"> % </span>
|
||||
<span> % </span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,36 +54,28 @@ watchPostEffect(() => {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.scroll-progress {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
pointer-events: none;
|
||||
<style scoped lang="postcss">
|
||||
svg {
|
||||
filter: drop-shadow(0px 1px 1px rgba(0, 0, 0, 0.2));
|
||||
margin-top: 5px;
|
||||
&.indeterminate {
|
||||
animation: 2s linear infinite svg-animation;
|
||||
|
||||
svg {
|
||||
filter: drop-shadow(0px 1px 1px rgba(0, 0, 0, 0.2));
|
||||
margin-top: 5px;
|
||||
&.indeterminate {
|
||||
animation: 2s linear infinite svg-animation;
|
||||
|
||||
circle {
|
||||
animation: 1.4s ease-in-out infinite both circle-animation;
|
||||
}
|
||||
}
|
||||
circle {
|
||||
fill: var(--scheme-main-ter);
|
||||
fill-opacity: 0.8;
|
||||
transition: stroke-dashoffset 250ms ease-out;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: 50% 50%;
|
||||
stroke: var(--primary-color);
|
||||
stroke-dashoffset: calc(276.32px - v-bind(scrollProgress) * 276.32px);
|
||||
stroke-dasharray: 276.32px 276.32px;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 3;
|
||||
will-change: stroke-dashoffset;
|
||||
animation: 1.4s ease-in-out infinite both circle-animation;
|
||||
}
|
||||
}
|
||||
circle {
|
||||
fill-opacity: 0.75;
|
||||
transition: stroke-dashoffset 250ms ease-out;
|
||||
transform: rotate(-90deg);
|
||||
transform-origin: 50% 50%;
|
||||
stroke-dashoffset: calc(276.32px - v-bind(scrollProgress) * 276.32px);
|
||||
stroke-dasharray: 276.32px 276.32px;
|
||||
stroke-linecap: round;
|
||||
stroke-width: 3;
|
||||
will-change: stroke-dashoffset;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes svg-animation {
|
||||
@@ -113,11 +105,11 @@ watchPostEffect(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
.fadeout-leave-active {
|
||||
@apply transition-opacity;
|
||||
}
|
||||
|
||||
.fade-leave-to {
|
||||
.fadeout-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,24 +1,27 @@
|
||||
<template>
|
||||
<section :class="{ 'is-full-height-scrollable': scrollable }">
|
||||
<header v-if="$slots.header">
|
||||
<section :class="{ 'h-screen min-h-0': scrollable }" class="flex flex-col">
|
||||
<header
|
||||
v-if="$slots.header"
|
||||
class="sticky top-[70px] z-[2] border-b border-base-content/10 bg-base py-2 shadow-[1px_1px_2px_0_rgb(0,0,0,0.05)] md:top-0"
|
||||
>
|
||||
<slot name="header"></slot>
|
||||
</header>
|
||||
<main :data-scrolling="scrollable ? true : undefined">
|
||||
<div class="is-scrollbar-progress is-hidden-mobile">
|
||||
<scroll-progress v-if="paused" :indeterminate="loading" :auto-hide="!loading"></scroll-progress>
|
||||
<main :data-scrolling="scrollable ? true : undefined" class="snap-y overflow-auto">
|
||||
<div class="invisible mr-28 text-right md:visible" v-show="paused">
|
||||
<scroll-progress :indeterminate="loading" :auto-hide="!loading" class="z-2 !fixed top-16" />
|
||||
</div>
|
||||
<div ref="scrollableContent">
|
||||
<slot :setLoading="setLoading"></slot>
|
||||
</div>
|
||||
|
||||
<div ref="scrollObserver" class="is-scroll-observer"></div>
|
||||
<div ref="scrollObserver" class="h-px"></div>
|
||||
</main>
|
||||
|
||||
<div class="is-scrollbar-notification">
|
||||
<div class="mr-16 text-right">
|
||||
<transition name="fade">
|
||||
<button
|
||||
class="button has-boxshadow"
|
||||
:class="hasMore ? 'has-more' : ''"
|
||||
class="fixed bottom-8 rounded bg-primary p-3 text-primary-content shadow transition-colors hover:bg-primary-focus"
|
||||
:class="hasMore ? 'animate-bounce-fast bg-secondary text-secondary-content hover:bg-secondary-focus' : ''"
|
||||
@click="scrollToBottom()"
|
||||
v-show="paused"
|
||||
>
|
||||
@@ -71,106 +74,20 @@ function setLoading(value: boolean) {
|
||||
loading = value;
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
<style scoped lang="postcss">
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
@apply transition-opacity;
|
||||
}
|
||||
|
||||
header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: var(--body-background-color);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
z-index: 1;
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
top: 70px;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-full-height-scrollable {
|
||||
height: 100vh;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
scroll-snap-type: y proximity;
|
||||
}
|
||||
|
||||
.is-scrollbar-progress {
|
||||
text-align: right;
|
||||
margin-right: 110px;
|
||||
|
||||
.scroll-progress {
|
||||
position: fixed;
|
||||
top: 60px;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
.is-scroll-observer {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.is-scrollbar-notification {
|
||||
text-align: right;
|
||||
margin-right: 65px;
|
||||
|
||||
button {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
background-color: var(--primary-color);
|
||||
transition: background-color 0.24s ease-out;
|
||||
border: none !important;
|
||||
color: #eee;
|
||||
|
||||
&.has-more {
|
||||
background-color: var(--secondary-color);
|
||||
animation-name: bounce;
|
||||
animation-duration: 1000ms;
|
||||
animation-fill-mode: both;
|
||||
|
||||
color: #222;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounce {
|
||||
0%,
|
||||
20%,
|
||||
50%,
|
||||
80%,
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: translateY(-15px);
|
||||
}
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s ease-out !important;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
@apply opacity-0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style>
|
||||
@media screen and (max-width: 768px) {
|
||||
.splitpanes__pane {
|
||||
overflow: unset !important;
|
||||
}
|
||||
.splitpanes__pane {
|
||||
overflow: unset !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
<template>
|
||||
<div class="search columns is-gapless is-vcentered" v-show="showSearch" v-if="search">
|
||||
<div class="column">
|
||||
<p class="control has-icons-left">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Find / RegEx"
|
||||
ref="input"
|
||||
v-model="searchFilter"
|
||||
@keyup.esc="resetSearch()"
|
||||
/>
|
||||
<span class="icon is-left">
|
||||
<mdi:light-magnify />
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="column is-1 has-text-centered">
|
||||
<button class="delete is-medium" @click="resetSearch()"></button>
|
||||
<div
|
||||
class="fixed -right-px -top-px z-10 flex w-96 items-center gap-4 rounded-bl border border-secondary/20 bg-base-darker p-4 shadow"
|
||||
v-show="showSearch"
|
||||
v-if="search"
|
||||
>
|
||||
<div class="input input-primary flex h-auto items-center">
|
||||
<mdi:light-magnify />
|
||||
<input
|
||||
class="input flex-1"
|
||||
type="text"
|
||||
placeholder="Find / RegEx"
|
||||
ref="input"
|
||||
v-model="searchFilter"
|
||||
@keyup.esc="resetSearch()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-circle btn-xs" @click="resetSearch()"> <mdi:close /></a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -35,46 +34,3 @@ onKeyStroke("f", (e) => {
|
||||
|
||||
onUnmounted(() => resetSearch());
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search {
|
||||
width: 350px;
|
||||
position: fixed;
|
||||
padding: 10px;
|
||||
background: var(--scheme-main-ter);
|
||||
top: 0;
|
||||
right: 0;
|
||||
border-radius: 0 0 0 5px;
|
||||
z-index: 10;
|
||||
box-shadow:
|
||||
0 1px 3px rgba(0, 0, 0, 0.12),
|
||||
0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
|
||||
button.delete {
|
||||
margin-left: 1em;
|
||||
background-color: var(--scheme-main-ter);
|
||||
opacity: 0.6;
|
||||
|
||||
&:after,
|
||||
&:before {
|
||||
background-color: var(--text-color);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding: 10px 3px;
|
||||
}
|
||||
|
||||
.input {
|
||||
color: var(--body-color);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--border-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,50 +1,51 @@
|
||||
<template>
|
||||
<div v-if="ready">
|
||||
<nav class="breadcrumb menu-label" aria-label="breadcrumbs">
|
||||
<ul v-if="sessionHost">
|
||||
<li>
|
||||
<a href="#" @click.prevent="setHost(null)">{{ hosts[sessionHost].name }}</a>
|
||||
<div v-if="ready" data-testid="side-menu">
|
||||
<div class="breadcrumbs">
|
||||
<ul>
|
||||
<li><a @click.prevent="setHost(null)" class="link-primary">Hosts</a></li>
|
||||
<li v-if="sessionHost && hosts[sessionHost]" class="cursor-default">
|
||||
{{ hosts[sessionHost].name }}
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-else>
|
||||
<li>Hosts</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
<transition :name="sessionHost ? 'slide-left' : 'slide-right'" mode="out-in">
|
||||
<ul class="menu-list" v-if="!sessionHost">
|
||||
<ul class="menu p-0" v-if="!sessionHost">
|
||||
<li v-for="host in config.hosts">
|
||||
<a @click.prevent="setHost(host.id)">{{ host.name }}</a>
|
||||
<a @click.prevent="setHost(host.id)">
|
||||
<ph:computer-tower />
|
||||
{{ host.name }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<transition-group tag="ul" name="list" class="menu-list" v-else>
|
||||
<li v-for="item in menuItems" :key="item.id" :class="item.state" :data-label="item.id">
|
||||
<div class="menu-label mt-4 mb-3" v-if="isLabel(item)">
|
||||
<transition-group tag="ul" name="list" class="containers menu p-0 [&_li.menu-title]:px-0" v-else>
|
||||
<li
|
||||
v-for="item in menuItems"
|
||||
:key="item.id"
|
||||
:class="isLabel(item) ? 'menu-title' : item.state"
|
||||
:data-testid="item.id"
|
||||
>
|
||||
<template v-if="isLabel(item)">
|
||||
{{ item.label }}
|
||||
</div>
|
||||
</template>
|
||||
<popup v-else>
|
||||
<router-link
|
||||
:to="{ name: 'container-id', params: { id: item.id } }"
|
||||
active-class="is-active"
|
||||
active-class="active-primary"
|
||||
:title="item.name"
|
||||
>
|
||||
<div class="container is-flex is-align-items-center">
|
||||
<div class="is-flex-grow-1 is-ellipsis">
|
||||
<span>{{ item.name }}</span
|
||||
><span class="has-text-weight-light has-light-opacity" v-if="item.isSwarm">{{ item.swarmId }}</span>
|
||||
</div>
|
||||
<div class="is-flex-shrink-1 is-flex icons">
|
||||
<div
|
||||
class="icon is-small pin"
|
||||
@click.stop.prevent="store.appendActiveContainer(item)"
|
||||
v-show="!activeContainersById[item.id]"
|
||||
:title="$t('tooltip.pin-column')"
|
||||
>
|
||||
<cil:columns />
|
||||
</div>
|
||||
|
||||
<container-health :health="item.health"></container-health>
|
||||
</div>
|
||||
<div class="truncate">
|
||||
{{ item.name }}<span class="font-light opacity-70" v-if="item.isSwarm">{{ item.swarmId }}</span>
|
||||
</div>
|
||||
<span
|
||||
class="pin"
|
||||
@click.stop.prevent="store.appendActiveContainer(item)"
|
||||
v-show="!activeContainersById[item.id]"
|
||||
:title="$t('tooltip.pin-column')"
|
||||
>
|
||||
<cil:columns />
|
||||
</span>
|
||||
|
||||
<container-health :health="item.health"></container-health>
|
||||
</router-link>
|
||||
<template #content>
|
||||
<container-popup :container="item"></container-popup>
|
||||
@@ -54,9 +55,10 @@
|
||||
</transition-group>
|
||||
</transition>
|
||||
</div>
|
||||
<ul class="menu-list is-hidden-mobile has-light-opacity" v-else>
|
||||
<li v-for="index in 7" class="my-4"><o-skeleton animated size="large" :key="index"></o-skeleton></li>
|
||||
</ul>
|
||||
<div role="status" class="flex animate-pulse flex-col gap-4" v-else>
|
||||
<div class="h-3 w-full rounded-full bg-base-content/50 opacity-50" v-for="_ in 9"></div>
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -104,7 +106,7 @@ const groupedContainers = computed(() =>
|
||||
|
||||
type MenuLabel = { label: string; id: string; state: string };
|
||||
const pinnedLabel = { label: t("label.pinned"), id: "pinned", state: "label" } as MenuLabel;
|
||||
const allLabel = { label: t("label.containers"), id: "all", state: "label" } as MenuLabel;
|
||||
const allLabel = { label: t("label.containers"), id: "containers", state: "label" } as MenuLabel;
|
||||
|
||||
function isLabel(item: Container | MenuLabel): item is MenuLabel {
|
||||
return (item as MenuLabel).label !== undefined;
|
||||
@@ -138,35 +140,29 @@ const activeContainersById = computed(() =>
|
||||
),
|
||||
);
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.has-light-opacity {
|
||||
opacity: 0.5;
|
||||
<style scoped lang="postcss">
|
||||
.menu {
|
||||
@apply text-[0.95rem];
|
||||
}
|
||||
|
||||
li.exited a,
|
||||
li.dead a {
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.icons {
|
||||
column-gap: 0.35em;
|
||||
align-items: baseline;
|
||||
}
|
||||
a {
|
||||
.containers a {
|
||||
@apply auto-cols-[auto_max-content];
|
||||
.pin {
|
||||
display: none;
|
||||
|
||||
&:hover {
|
||||
color: var(--secondary-color);
|
||||
@apply text-secondary;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.pin {
|
||||
display: block;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
li.exited {
|
||||
@apply opacity-50;
|
||||
}
|
||||
|
||||
.slide-left-enter-active,
|
||||
.slide-left-leave-active,
|
||||
@@ -177,22 +173,22 @@ a {
|
||||
|
||||
.slide-left-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.slide-right-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%);
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
.slide-left-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%);
|
||||
transform: translateX(-20px);
|
||||
}
|
||||
|
||||
.slide-right-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(100%);
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.list-move,
|
||||
|
||||
@@ -1,48 +1,40 @@
|
||||
<template>
|
||||
<aside>
|
||||
<div class="columns is-marginless">
|
||||
<div class="column is-paddingless">
|
||||
<h1>
|
||||
<router-link :to="{ name: 'index' }">
|
||||
<svg class="logo">
|
||||
<use href="#logo"></use>
|
||||
</svg>
|
||||
</router-link>
|
||||
<aside class="fixed h-screen w-[inherit] overflow-auto p-3" data-testid="navigation">
|
||||
<h1>
|
||||
<router-link :to="{ name: 'index' }">
|
||||
<svg class="h-14 w-28 fill-secondary">
|
||||
<use href="#logo"></use>
|
||||
</svg>
|
||||
</router-link>
|
||||
|
||||
<small class="subtitle is-6 is-block mb-4" v-if="hostname">
|
||||
{{ hostname }}
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
<div class="columns is-marginless">
|
||||
<div class="column is-narrow py-0 pl-0 pr-1">
|
||||
<button class="button is-rounded is-small" @click="$emit('search')" :title="$t('tooltip.search')">
|
||||
<span class="icon">
|
||||
<mdi:light-magnify />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column is-narrow py-0" :class="secured ? 'pl-0 pr-1' : 'px-0'">
|
||||
<router-link
|
||||
:to="{ name: 'settings' }"
|
||||
active-class="is-active"
|
||||
class="button is-rounded is-small"
|
||||
:aria-label="$t('title.settings')"
|
||||
>
|
||||
<span class="icon">
|
||||
<mdi:light-cog />
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="column is-narrow py-0 px-0" v-if="secured">
|
||||
<a class="button is-rounded is-small" :href="`${base}/logout`" :title="$t('button.logout')">
|
||||
<span class="icon">
|
||||
<mdi:light-logout />
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
<small class="mb-4 block text-xs font-light" v-if="hostname">
|
||||
{{ hostname }}
|
||||
</small>
|
||||
</h1>
|
||||
|
||||
<div class="flex gap-4">
|
||||
<router-link
|
||||
:to="{ name: 'settings' }"
|
||||
:aria-label="$t('title.settings')"
|
||||
data-testid="settings"
|
||||
class="btn btn-circle btn-sm"
|
||||
>
|
||||
<mdi:light-cog />
|
||||
</router-link>
|
||||
<a :href="`${base}/logout`" :title="$t('button.logout')" v-if="secured" class="btn btn-circle btn-sm">
|
||||
<mdi:light-logout />
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
class="input input-sm mt-4 inline-flex cursor-pointer items-center gap-2 font-light hover:border-primary"
|
||||
@click="$emit('search')"
|
||||
:title="$t('tooltip.search')"
|
||||
>
|
||||
<mdi:light-magnify />
|
||||
Search
|
||||
<key-shortcut char="k"></key-shortcut>
|
||||
</a>
|
||||
|
||||
<side-menu class="mt-4"></side-menu>
|
||||
</aside>
|
||||
</template>
|
||||
@@ -50,22 +42,3 @@
|
||||
<script lang="ts" setup>
|
||||
const { base, secured, hostname } = config;
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
aside {
|
||||
padding: 1em;
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
width: inherit;
|
||||
|
||||
.is-hidden-mobile.is-active {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 122px;
|
||||
height: 54px;
|
||||
fill: var(--logo-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<time :datetime="date.toISOString()" class="is-hidden-mobile">{{ dateStr }}</time>
|
||||
<div class="inline-flex gap-2">
|
||||
<time :datetime="date.toISOString()" class="mobile-hidden">{{ dateStr }}</time>
|
||||
<time :datetime="date.toISOString()">{{ timeStr }}</time>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<details class="dropdown" ref="details" v-on-click-outside="close">
|
||||
<summary class="btn btn-primary flex-nowrap" v-bind="$attrs">
|
||||
<slot name="trigger"> {{ values[modelValue] ?? defaultLabel }} <carbon:caret-down /></slot>
|
||||
</summary>
|
||||
<ul class="menu dropdown-content rounded-box z-50 w-52 bg-base p-2 shadow">
|
||||
<slot>
|
||||
<li v-for="item in options">
|
||||
<a @click="modelValue = item.value">
|
||||
<mdi-light:check class="w-4" v-if="modelValue == item.value" />
|
||||
<div v-else class="w-4"></div>
|
||||
{{ item.label }}
|
||||
</a>
|
||||
</li>
|
||||
</slot>
|
||||
</ul>
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { vOnClickOutside } from "@vueuse/components";
|
||||
type DropdownItem = {
|
||||
label: string;
|
||||
value: string;
|
||||
};
|
||||
const { options = [], defaultLabel = "" } = defineProps<{ options?: DropdownItem[]; defaultLabel?: string }>();
|
||||
const { modelValue } = defineModels<{
|
||||
modelValue: string;
|
||||
}>();
|
||||
|
||||
const values = computed(() =>
|
||||
options.reduce(
|
||||
(acc, curr) => {
|
||||
acc[curr.value] = curr.label;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>,
|
||||
),
|
||||
);
|
||||
|
||||
const details = ref<HTMLElement | null>(null);
|
||||
const close = () => details.value?.removeAttribute("open");
|
||||
watch(modelValue, () => close());
|
||||
</script>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<div class="inline-flex items-center">
|
||||
<template v-if="modifiers.includes('shift')">
|
||||
<carbon:mac-shift />
|
||||
</template>
|
||||
<template v-if="modifiers.includes('meta')">
|
||||
<ph:command v-if="isMac" class="h-4 w-4" />
|
||||
<ph:control-bold v-else class="h-4 w-4" />
|
||||
</template>
|
||||
<kbd class="uppercase">{{ char }}</kbd>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
const isMac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.userAgent);
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
const { modifiers = ["meta"], char } = defineProps<{ char: string; modifiers?: ("^" | "meta" | "shift")[] }>();
|
||||
</script>
|
||||
@@ -1,88 +1,58 @@
|
||||
<template>
|
||||
<aside>
|
||||
<div class="columns is-marginless is-gapless is-mobile is-vcentered">
|
||||
<div class="column is-narrow">
|
||||
<router-link :to="{ name: 'index' }">
|
||||
<svg class="logo">
|
||||
<use href="#logo"></use>
|
||||
</svg>
|
||||
</router-link>
|
||||
</div>
|
||||
<nav class="fixed top-0 z-10 w-full border-b border-base-content/20 bg-base p-2" data-testid="navigation">
|
||||
<div class="flex items-center">
|
||||
<router-link :to="{ name: 'index' }">
|
||||
<svg class="h-14 w-28 fill-secondary">
|
||||
<use href="#logo"></use>
|
||||
</svg>
|
||||
</router-link>
|
||||
|
||||
<div class="column is-narrow push-right">
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger burger is-hidden-tablet is-pulled-right"
|
||||
@click="showNav = !showNav"
|
||||
:class="{ 'is-active': showNav }"
|
||||
>
|
||||
<span></span> <span></span> <span></span>
|
||||
<div class="ml-auto flex items-center gap-2">
|
||||
<a class="btn btn-circle flex" @click="$emit('search')" :title="$t('tooltip.search')">
|
||||
<mdi:light-magnify class="h-5 w-5" />
|
||||
</a>
|
||||
<label class="btn btn-circle swap swap-rotate" data-testid="hamburger">
|
||||
<input type="checkbox" v-model="show" />
|
||||
<mdi:close class="swap-on" />
|
||||
<mdi:hamburger-menu class="swap-off" />
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="menu-label level is-mobile is-hidden-mobile" :class="{ 'is-active': showNav }">
|
||||
<div v-if="config.hosts.length > 1">
|
||||
<o-dropdown v-model="sessionHost" aria-role="list">
|
||||
<template #trigger>
|
||||
<o-button variant="primary" type="button" size="small">
|
||||
<span>{{ sessionHost ? hosts[sessionHost].name : "" }}</span>
|
||||
<span class="icon">
|
||||
<carbon:caret-down />
|
||||
</span>
|
||||
</o-button>
|
||||
</template>
|
||||
|
||||
<o-dropdown-item :value="value.id" aria-role="listitem" v-for="value in config.hosts" :key="value">
|
||||
<span>{{ value.name }}</span>
|
||||
</o-dropdown-item>
|
||||
</o-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<button class="button is-small is-rounded" @click="$emit('search')" :title="$t('tooltip.search')">
|
||||
<span class="icon">
|
||||
<mdi:light-magnify />
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<router-link :to="{ name: 'settings' }" active-class="is-active" class="button is-small is-rounded">
|
||||
<span class="icon">
|
||||
<mdi:light-cog />
|
||||
</span>
|
||||
<transition name="fade">
|
||||
<div v-show="show">
|
||||
<div class="mt-4 flex items-center justify-center gap-2">
|
||||
<dropdown
|
||||
v-model="sessionHost"
|
||||
:options="hosts"
|
||||
defaultLabel="Hosts"
|
||||
class="btn-sm"
|
||||
v-if="config.hosts.length > 1"
|
||||
/>
|
||||
<router-link :to="{ name: 'settings' }" class="btn btn-outline btn-sm">
|
||||
<mdi:light-cog /> {{ $t("button.settings") }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-item has-text-centered" v-if="secured">
|
||||
<div>
|
||||
<a class="button is-small is-rounded" :href="`${base}/logout`" :title="$t('button.logout')">
|
||||
<span class="icon">
|
||||
<mdi:light-logout />
|
||||
</span>
|
||||
<a class="btn btn-outline btn-sm" :href="`${base}/logout`" :title="$t('button.logout')" v-if="secured">
|
||||
<mdi:light-logout /> {{ $t("button.logout") }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="menu-label is-hidden-mobile" :class="{ 'is-active': showNav }">{{ $t("label.containers") }}</p>
|
||||
<ul class="menu-list is-hidden-mobile" :class="{ 'is-active': showNav }">
|
||||
<li v-for="item in sortedContainers" :key="item.id">
|
||||
<router-link
|
||||
:to="{ name: 'container-id', params: { id: item.id } }"
|
||||
active-class="is-active"
|
||||
:title="item.name"
|
||||
>
|
||||
<div class="is-ellipsis">
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</aside>
|
||||
<ul class="menu">
|
||||
<li class="menu-title">{{ $t("label.containers") }}</li>
|
||||
<li v-for="item in sortedContainers" :key="item.id">
|
||||
<router-link
|
||||
:to="{ name: 'container-id', params: { id: item.id } }"
|
||||
active-class="active-primary"
|
||||
class="truncate"
|
||||
:title="item.name"
|
||||
>
|
||||
{{ item.name }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</transition>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@@ -92,10 +62,10 @@ const store = useContainerStore();
|
||||
const route = useRoute();
|
||||
const { visibleContainers } = storeToRefs(store);
|
||||
|
||||
let showNav = $ref(false);
|
||||
const show = ref(false);
|
||||
|
||||
watch(route, () => {
|
||||
showNav = false;
|
||||
show.value = false;
|
||||
});
|
||||
|
||||
const sortedContainers = computed(() =>
|
||||
@@ -112,59 +82,26 @@ const sortedContainers = computed(() =>
|
||||
}),
|
||||
);
|
||||
|
||||
const hosts = computed(() =>
|
||||
config.hosts.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.id] = item;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, { name: string; id: string }>,
|
||||
),
|
||||
);
|
||||
const hosts = computed(() => config.hosts.map(({ id, name }) => ({ value: id, label: name })));
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
aside {
|
||||
padding: 1em;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--scheme-main-ter);
|
||||
z-index: 10;
|
||||
max-height: 100vh;
|
||||
overflow: auto;
|
||||
<style scoped lang="postcss">
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
@apply transition-opacity;
|
||||
}
|
||||
|
||||
.level.is-hidden-mobile.is-active {
|
||||
display: flex !important;
|
||||
}
|
||||
.fade-enter-active .menu,
|
||||
.fade-leave-active .menu {
|
||||
@apply transition-transform;
|
||||
}
|
||||
|
||||
.menu-label {
|
||||
margin-top: 1em;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
@apply opacity-0;
|
||||
}
|
||||
|
||||
.title {
|
||||
text-shadow: 0px 1px 1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.burger {
|
||||
color: var(--body-color);
|
||||
}
|
||||
|
||||
.is-hidden-mobile.is-active {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.navbar-burger {
|
||||
height: 2.35rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 82px;
|
||||
height: 36px;
|
||||
fill: var(--logo-color);
|
||||
}
|
||||
|
||||
.column.push-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
.fade-enter-from .menu,
|
||||
.fade-leave-to .menu {
|
||||
@apply -translate-y-2;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="tag" :size="size">
|
||||
<div class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" :size="size">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
@@ -8,12 +8,8 @@
|
||||
const { size = undefined } = defineProps<{ size?: "small" | undefined }>();
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.tag {
|
||||
background-color: var(--scheme-main-ter);
|
||||
border: 1px solid var(--border-color);
|
||||
&[size="small"] {
|
||||
font-size: 0.61rem;
|
||||
}
|
||||
<style scoped lang="postcss">
|
||||
[size="small"] {
|
||||
@apply text-xs;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<button class="btn relative overflow-hidden" @click="cancel()">
|
||||
<div class="absolute inset-0 origin-left bg-white/30" ref="progress"></div>
|
||||
<div class="z-10">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const progress = ref<HTMLElement>();
|
||||
const finished = defineEmit();
|
||||
const cancelled = defineEmit();
|
||||
let animation: Animation | undefined;
|
||||
|
||||
onMounted(async () => {
|
||||
animation = progress.value?.animate([{ transform: "scaleX(0)" }, { transform: "scaleX(1)" }], {
|
||||
duration: 4000,
|
||||
easing: "linear",
|
||||
fill: "forwards",
|
||||
});
|
||||
try {
|
||||
await animation?.finished;
|
||||
finished();
|
||||
} catch (e) {
|
||||
progress.value?.animate([{ transform: "scaleX(1)" }, { transform: "scaleX(0)" }], {
|
||||
duration: 0,
|
||||
fill: "forwards",
|
||||
});
|
||||
cancelled();
|
||||
}
|
||||
});
|
||||
|
||||
const cancel = () => {
|
||||
animation?.cancel();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="postcss"></style>
|
||||
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<label class="label inline-flex cursor-pointer gap-4 font-normal">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="toggle toggle-primary hover:bg-primary-focus"
|
||||
:checked="modelValue"
|
||||
@click="toggle()"
|
||||
/>
|
||||
<slot />
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { modelValue } = defineModels<{
|
||||
modelValue: boolean;
|
||||
}>();
|
||||
|
||||
const toggle = useToggle(modelValue);
|
||||
</script>
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Container } from "@/models/Container";
|
||||
|
||||
type ContainerContext = {
|
||||
container: Ref<Container>;
|
||||
streamConfig: { stdout: boolean; stderr: boolean };
|
||||
};
|
||||
|
||||
export const containerContext = Symbol("containerContext") as InjectionKey<ContainerContext>;
|
||||
|
||||
export const provideContainerContext = (container: Ref<Container>) => {
|
||||
provide(containerContext, {
|
||||
container,
|
||||
streamConfig: reactive({ stdout: true, stderr: true }),
|
||||
});
|
||||
};
|
||||
|
||||
export const useContainerContext = () => {
|
||||
const context = inject(containerContext);
|
||||
if (!context) {
|
||||
throw new Error("No container context provided");
|
||||
}
|
||||
return context;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type ComputedRef, type Ref } from "vue";
|
||||
import { type Ref } from "vue";
|
||||
import { encodeXML } from "entities";
|
||||
import debounce from "lodash.debounce";
|
||||
import {
|
||||
@@ -26,7 +26,7 @@ type LogStreamConfig = {
|
||||
stderr: boolean;
|
||||
};
|
||||
|
||||
export function useLogStream(container: ComputedRef<Container>, streamConfig: LogStreamConfig) {
|
||||
export function useLogStream(container: Ref<Container>, streamConfig: LogStreamConfig) {
|
||||
let messages: LogEntry<string | JSONObject>[] = $ref([]);
|
||||
let buffer: LogEntry<string | JSONObject>[] = $ref([]);
|
||||
const scrollingPaused = $ref(inject("scrollingPaused") as Ref<boolean>);
|
||||
|
||||
@@ -13,6 +13,7 @@ export const DEFAULT_SETTINGS: {
|
||||
hourStyle: "auto" | "24" | "12";
|
||||
softWrap: boolean;
|
||||
collapseNav: boolean;
|
||||
automaticRedirect: boolean;
|
||||
} = {
|
||||
search: true,
|
||||
size: "medium",
|
||||
@@ -25,6 +26,7 @@ export const DEFAULT_SETTINGS: {
|
||||
hourStyle: "auto",
|
||||
softWrap: true,
|
||||
collapseNav: false,
|
||||
automaticRedirect: true,
|
||||
};
|
||||
|
||||
export const settings = useStorage(DOZZLE_SETTINGS_KEY, DEFAULT_SETTINGS);
|
||||
@@ -42,4 +44,5 @@ export const {
|
||||
menuWidth,
|
||||
size,
|
||||
search,
|
||||
automaticRedirect,
|
||||
} = toRefs(settings);
|
||||
|
||||
@@ -7,17 +7,9 @@ if (config.hosts.length === 1 && !sessionHost.value) {
|
||||
sessionHost.value = config.hosts[0].id;
|
||||
}
|
||||
|
||||
export function persistentVisibleKeys(container: ComputedRef<Container>) {
|
||||
export function persistentVisibleKeys(container: Ref<Container>) {
|
||||
return computed(() => useStorage(container.value.storageKey, []));
|
||||
}
|
||||
|
||||
const DOZZLE_PINNED_CONTAINERS = "DOZZLE_PINNED_CONTAINERS";
|
||||
export const pinnedContainers = useStorage(DOZZLE_PINNED_CONTAINERS, new Set<string>());
|
||||
|
||||
export function togglePinnedContainer(id: string) {
|
||||
if (pinnedContainers.value.has(id)) {
|
||||
pinnedContainers.value.delete(id);
|
||||
} else {
|
||||
pinnedContainers.value.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const { hostname } = config;
|
||||
let subtitle = $ref("");
|
||||
const title = $computed(() => `${subtitle} - Dozzle` + (hostname ? ` @ ${hostname}` : ""));
|
||||
const title = $computed(() => (subtitle ? `${subtitle} - ` : "") + "Dozzle" + (hostname ? ` @ ${hostname}` : ""));
|
||||
|
||||
useTitle($$(title));
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
type Toast = {
|
||||
id: string;
|
||||
createdAt: Date;
|
||||
title?: string;
|
||||
message: string;
|
||||
type: "success" | "error" | "warning" | "info";
|
||||
};
|
||||
|
||||
type ToastOptions = {
|
||||
expire?: number;
|
||||
once?: boolean;
|
||||
};
|
||||
|
||||
const toasts = ref<Toast[]>([]);
|
||||
|
||||
const showToast = (
|
||||
toast: Omit<Toast, "id" | "createdAt"> & { id?: string },
|
||||
{ expire = -1, once = false }: ToastOptions = { expire: -1, once: false },
|
||||
) => {
|
||||
if (once && toasts.value.some((t) => t.id === toast.id)) {
|
||||
return;
|
||||
}
|
||||
toasts.value.push({
|
||||
id: Date.now().toString(),
|
||||
createdAt: new Date(),
|
||||
...toast,
|
||||
});
|
||||
if (expire > 0) {
|
||||
setTimeout(() => {
|
||||
removeToast(toasts.value[0].id);
|
||||
}, expire);
|
||||
}
|
||||
};
|
||||
|
||||
const removeToast = (id: Toast["id"]) => {
|
||||
toasts.value = toasts.value.filter((toast) => toast.id !== id);
|
||||
};
|
||||
|
||||
export const useToast = () => {
|
||||
return {
|
||||
toasts,
|
||||
showToast,
|
||||
removeToast,
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<main v-if="!authorizationNeeded">
|
||||
<div v-if="!authorizationNeeded">
|
||||
<mobile-menu v-if="isMobile" @search="showFuzzySearch"></mobile-menu>
|
||||
<splitpanes @resized="onResized($event)">
|
||||
<pane min-size="10" :size="menuWidth" v-if="!isMobile && !collapseNav">
|
||||
@@ -7,7 +7,7 @@
|
||||
</pane>
|
||||
<pane min-size="10">
|
||||
<splitpanes>
|
||||
<pane class="has-min-height router-view">
|
||||
<pane class="router-view min-h-screen">
|
||||
<router-view></router-view>
|
||||
</pane>
|
||||
<template v-if="!isMobile">
|
||||
@@ -24,37 +24,64 @@
|
||||
</splitpanes>
|
||||
</pane>
|
||||
</splitpanes>
|
||||
<button
|
||||
@click="collapse"
|
||||
class="button is-small is-rounded"
|
||||
:class="{ collapsed: collapseNav }"
|
||||
id="hide-nav"
|
||||
<label
|
||||
class="btn btn-circle swap swap-rotate fixed bottom-8 left-4"
|
||||
:class="{ '!-left-3': collapseNav }"
|
||||
v-if="!isMobile"
|
||||
>
|
||||
<span class="icon ml-2" v-if="collapseNav">
|
||||
<mdi:light-chevron-right />
|
||||
</span>
|
||||
<span class="icon" v-else>
|
||||
<mdi:light-chevron-left />
|
||||
</span>
|
||||
</button>
|
||||
</main>
|
||||
<input type="checkbox" v-model="collapseNav" />
|
||||
<mdi:light-chevron-right class="swap-on text-secondary" />
|
||||
<mdi:light-chevron-left class="swap-off" />
|
||||
</label>
|
||||
</div>
|
||||
<dialog ref="modal" class="modal items-start bg-white/20 backdrop:backdrop-blur-sm" @close="open = false">
|
||||
<div class="modal-box max-w-2xl bg-transparent pt-20 shadow-none">
|
||||
<FuzzySearchModal @close="open = false" v-if="open" />
|
||||
</div>
|
||||
<form method="dialog" class="modal-backdrop">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
||||
<div class="toast toast-end whitespace-normal">
|
||||
<div
|
||||
class="alert max-w-xl"
|
||||
v-for="toast in toasts"
|
||||
:key="toast.id"
|
||||
:class="{ 'alert-error': toast.type === 'error', 'alert-info': toast.type === 'info' }"
|
||||
>
|
||||
<carbon:information class="h-6 w-6 shrink-0 stroke-current" v-if="toast.type === 'info'" />
|
||||
<carbon:warning class="h-6 w-6 shrink-0 stroke-current" v-else-if="toast.type === 'error'" />
|
||||
<div>
|
||||
<h3 class="text-lg font-bold" v-if="toast.title">{{ toast.title }}</h3>
|
||||
{{ toast.message }}
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-circle btn-xs" @click="removeToast(toast.id)"><mdi:close /></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// @ts-ignore - splitpanes types are not available
|
||||
import { Splitpanes, Pane } from "splitpanes";
|
||||
import { useProgrammatic } from "@oruga-ui/oruga-next";
|
||||
import FuzzySearchModal from "@/components/FuzzySearchModal.vue";
|
||||
|
||||
const { oruga } = useProgrammatic();
|
||||
import { collapseNav } from "@/composables/settings";
|
||||
const { authorizationNeeded } = config;
|
||||
|
||||
const containerStore = useContainerStore();
|
||||
const { activeContainers, visibleContainers } = storeToRefs(containerStore);
|
||||
const { activeContainers } = storeToRefs(containerStore);
|
||||
|
||||
watchEffect(() => {
|
||||
setTitle(`${visibleContainers.value.length} containers`);
|
||||
const { toasts, removeToast } = useToast();
|
||||
|
||||
const modal = ref<HTMLDialogElement>();
|
||||
const open = ref(false);
|
||||
|
||||
watch(open, () => {
|
||||
if (open.value) {
|
||||
modal.value?.showModal();
|
||||
} else {
|
||||
modal.value?.close();
|
||||
}
|
||||
});
|
||||
|
||||
onKeyStroke("k", (e) => {
|
||||
@@ -65,17 +92,9 @@ onKeyStroke("k", (e) => {
|
||||
});
|
||||
|
||||
function showFuzzySearch() {
|
||||
oruga.modal.open({
|
||||
// parent: this,
|
||||
component: FuzzySearchModal,
|
||||
animation: "false",
|
||||
width: 600,
|
||||
active: true,
|
||||
});
|
||||
}
|
||||
function collapse() {
|
||||
collapseNav.value = !collapseNav.value;
|
||||
open.value = true;
|
||||
}
|
||||
|
||||
function onResized(e: any) {
|
||||
if (e.length == 2) {
|
||||
menuWidth.value = e[0].size;
|
||||
@@ -83,14 +102,9 @@ function onResized(e: any) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style scoped lang="postcss">
|
||||
:deep(.splitpanes--vertical > .splitpanes__splitter) {
|
||||
min-width: 3px;
|
||||
background: var(--border-color);
|
||||
|
||||
&:hover {
|
||||
background: var(--border-hover-color);
|
||||
}
|
||||
@apply min-w-[3px] bg-base-lighter hover:bg-secondary;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
@@ -98,30 +112,4 @@ function onResized(e: any) {
|
||||
padding-top: 75px;
|
||||
}
|
||||
}
|
||||
|
||||
.button.has-no-border {
|
||||
border-color: transparent !important;
|
||||
}
|
||||
|
||||
.has-min-height {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
#hide-nav {
|
||||
position: fixed;
|
||||
left: 10px;
|
||||
bottom: 10px;
|
||||
|
||||
&.collapsed {
|
||||
left: -40px;
|
||||
width: 60px;
|
||||
padding-left: 40px;
|
||||
color: var(--text-strong-color);
|
||||
background: var(--scheme-main);
|
||||
|
||||
&:hover {
|
||||
left: -25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<template>
|
||||
<main>
|
||||
<router-view></router-view>
|
||||
</main>
|
||||
<div class="hero min-h-screen">
|
||||
<div class="hero-content">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
@import "splitpanes/dist/splitpanes.css";
|
||||
|
||||
@define-mixin light {
|
||||
--base-lighter-color: 0 0% 100%;
|
||||
--base-color: 0 0% 97%;
|
||||
--base-darker-color: 0 0% 90%;
|
||||
--base-content-color: 0 0% 21%;
|
||||
--primary-color: 171 100% 41%;
|
||||
--primary-focus-color: 171 100% 31%;
|
||||
--secondary-color: 34 95% 53%;
|
||||
--secondary-focus-color: 34 95% 43%;
|
||||
}
|
||||
|
||||
@define-mixin dark {
|
||||
--base-lighter-color: 0 0% 14%;
|
||||
--base-color: 0 0% 7%;
|
||||
--base-darker-color: 0 0% 4%;
|
||||
--base-content-color: 0 0% 86%;
|
||||
--primary-color: 171 100% 41%;
|
||||
--primary-focus-color: 171 100% 31%;
|
||||
--secondary-color: 44 100% 77%;
|
||||
--secondary-focus-color: 44 100% 67%;
|
||||
}
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--p: var(--primary-color);
|
||||
--pf: var(--primary-focus-color);
|
||||
--pc: 0 0% 100%;
|
||||
--s: var(--secondary-color);
|
||||
--sf: var(--secondary-focus-color);
|
||||
--sc: 0 0% 4%;
|
||||
--b1: var(--base-lighter-color);
|
||||
--b2: var(--base-color);
|
||||
--b3: var(--base-darker-color);
|
||||
--bc: var(--base-content-color);
|
||||
--in: 207 90% 54%;
|
||||
--inc: 207 90% 94%;
|
||||
--er: 4 90% 58%;
|
||||
--erc: 4 90% 98%;
|
||||
}
|
||||
html[data-theme="dark"] {
|
||||
@mixin dark;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
@mixin dark;
|
||||
}
|
||||
}
|
||||
html[data-theme="light"] {
|
||||
@mixin light;
|
||||
}
|
||||
@media (prefers-color-scheme: light) {
|
||||
html {
|
||||
@mixin light;
|
||||
}
|
||||
}
|
||||
@media screen and (max-device-width: 480px) {
|
||||
body {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-3xl;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-2xl;
|
||||
}
|
||||
|
||||
h3 {
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
mark {
|
||||
@apply inline-block rounded-sm bg-secondary;
|
||||
animation: pops 200ms ease-out;
|
||||
}
|
||||
|
||||
@keyframes pops {
|
||||
0% {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@layer components {
|
||||
.input {
|
||||
@apply focus:outline-none;
|
||||
}
|
||||
|
||||
.btn {
|
||||
@apply font-normal normal-case;
|
||||
}
|
||||
|
||||
.btn:not(.btn-primary):not(.btn-secondary) {
|
||||
@apply hover:bg-base-lighter;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
@media (max-width: 768px) {
|
||||
.mobile-hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.active-primary {
|
||||
--n: var(--p);
|
||||
--nc: var(--pc);
|
||||
@apply active;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import "./styles.scss";
|
||||
// import "./styles.scss";
|
||||
import "./main.css";
|
||||
import { createApp, App as VueApp } from "vue";
|
||||
import App from "./App.vue";
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { describe, expect, test, vi } from "vitest";
|
||||
import { Container } from "./Container";
|
||||
|
||||
vi.mock("@/stores/config", () => ({
|
||||
__esModule: true,
|
||||
default: { base: "", hosts: [{ name: "localhost", id: "localhost" }] },
|
||||
}));
|
||||
|
||||
describe("Container", () => {
|
||||
const names = [
|
||||
[
|
||||
|
||||
@@ -7,6 +7,16 @@ type Stat = Omit<ContainerStat, "id">;
|
||||
|
||||
const SWARM_ID_REGEX = /(\.[a-z0-9]{25})+$/i;
|
||||
|
||||
const hosts = computed(() =>
|
||||
config.hosts.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.id] = item;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, { name: string; id: string }>,
|
||||
),
|
||||
);
|
||||
|
||||
export class Container {
|
||||
private _stat: Ref<Stat>;
|
||||
private readonly throttledStatHistory: UseThrottledRefHistoryReturn<Stat, Stat>;
|
||||
@@ -49,6 +59,10 @@ export class Container {
|
||||
return unref(this._stat);
|
||||
}
|
||||
|
||||
get hostLabel() {
|
||||
return hosts.value[this.host]?.name;
|
||||
}
|
||||
|
||||
get storageKey() {
|
||||
return `${stripVersion(this.image)}:${this.command}`;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,6 @@ import SimpleLogItem from "@/components/LogViewer/SimpleLogItem.vue";
|
||||
import DockerEventLogItem from "@/components/LogViewer/DockerEventLogItem.vue";
|
||||
import SkippedEntriesLogItem from "@/components/LogViewer/SkippedEntriesLogItem.vue";
|
||||
|
||||
export interface HasComponent {
|
||||
getComponent(): Component;
|
||||
}
|
||||
|
||||
export type JSONValue = string | number | boolean | JSONObject | Array<JSONValue>;
|
||||
export type JSONObject = { [x: string]: JSONValue };
|
||||
export type Position = "start" | "end" | "middle" | undefined;
|
||||
@@ -22,7 +18,7 @@ export interface LogEvent {
|
||||
readonly s: "stdout" | "stderr" | "unknown";
|
||||
}
|
||||
|
||||
export abstract class LogEntry<T extends string | JSONObject> implements HasComponent {
|
||||
export abstract class LogEntry<T extends string | JSONObject> {
|
||||
protected readonly _message: T;
|
||||
constructor(
|
||||
message: T,
|
||||
@@ -102,7 +98,7 @@ export class DockerEventLogEntry extends LogEntry<string> {
|
||||
constructor(
|
||||
message: string,
|
||||
date: Date,
|
||||
public readonly event: string,
|
||||
public readonly event: "container-stopped" | "container-started",
|
||||
) {
|
||||
super(message, date.getTime(), date, "stderr", "info");
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { type App } from "vue";
|
||||
import { createI18n } from "vue-i18n";
|
||||
|
||||
const messages = Object.fromEntries(
|
||||
Object.entries(import.meta.glob<{ default: any }>("../../locales/*.y(a)?ml", { eager: true })).map(([key, value]) => {
|
||||
const yaml = key.endsWith(".yaml");
|
||||
return [key.slice(14, yaml ? -5 : -4), value.default];
|
||||
}),
|
||||
);
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: navigator.language.slice(0, 2),
|
||||
fallbackLocale: "en",
|
||||
messages,
|
||||
});
|
||||
export const install = (app: App) => {
|
||||
const messages = Object.fromEntries(
|
||||
Object.entries(import.meta.glob<{ default: any }>("../../locales/*.y(a)?ml", { eager: true })).map(
|
||||
([key, value]) => {
|
||||
const yaml = key.endsWith(".yaml");
|
||||
return [key.slice(14, yaml ? -5 : -4), value.default];
|
||||
},
|
||||
),
|
||||
);
|
||||
const i18n = createI18n({
|
||||
legacy: false,
|
||||
locale: navigator.language.slice(0, 2),
|
||||
fallbackLocale: "en",
|
||||
messages,
|
||||
});
|
||||
app.use(i18n);
|
||||
};
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { type App } from "vue";
|
||||
import { Autocomplete, Button, Dropdown, Switch, Skeleton, Field, Table, Modal, Config } from "@oruga-ui/oruga-next";
|
||||
import { bulmaConfig } from "@oruga-ui/theme-bulma";
|
||||
|
||||
export const install = (app: App) => {
|
||||
app
|
||||
.use(Autocomplete)
|
||||
.use(Button)
|
||||
.use(Dropdown)
|
||||
.use(Switch)
|
||||
.use(Modal)
|
||||
.use(Field)
|
||||
.use(Skeleton)
|
||||
.use(Table)
|
||||
.use(Config, bulmaConfig);
|
||||
};
|
||||
@@ -1,11 +1,8 @@
|
||||
<template>
|
||||
<div class="hero is-halfheight">
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<h1 class="title">
|
||||
404.
|
||||
<small class="subtitle">{{ $t("error.page-not-found") }}</small>
|
||||
</h1>
|
||||
<div class="hero min-h-screen bg-base-200">
|
||||
<div class="hero-content text-center">
|
||||
<div class="max-w-md">
|
||||
<p class="py-6 text-2xl font-bold">{{ $t("error.page-not-found") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<template>
|
||||
<search></search>
|
||||
<log-container :id="id" show-title :scrollable="activeContainers.length > 0" v-if="currentContainer"></log-container>
|
||||
<div v-else-if="ready" class="notification is-warning is-light m-6">
|
||||
<h1 class="title">
|
||||
{{ $t("error.container-not-found") }}
|
||||
</h1>
|
||||
<log-container
|
||||
:id="id"
|
||||
:show-title="true"
|
||||
:scrollable="activeContainers.length > 0"
|
||||
v-if="currentContainer"
|
||||
></log-container>
|
||||
<div v-else-if="ready" class="hero min-h-screen bg-base-200">
|
||||
<div class="hero-content text-center">
|
||||
<div class="max-w-md">
|
||||
<p class="py-6 text-2xl font-bold">{{ $t("error.container-not-found") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -16,11 +23,13 @@ const { id } = defineProps<{ id: string }>();
|
||||
const currentContainer = store.currentContainer($$(id));
|
||||
const { activeContainers, ready } = storeToRefs(store);
|
||||
|
||||
setTitle("loading");
|
||||
|
||||
onMounted(() => {
|
||||
setTitle(currentContainer.value?.name);
|
||||
watchEffect(() => {
|
||||
if (ready.value) {
|
||||
if (currentContainer.value) {
|
||||
setTitle(currentContainer.value.name);
|
||||
} else {
|
||||
setTitle("Not Found");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => setTitle(currentContainer.value?.name));
|
||||
</script>
|
||||
|
||||
@@ -1,62 +1,45 @@
|
||||
<template>
|
||||
<div class="section tile is-ancestor">
|
||||
<div class="tile is-parent">
|
||||
<div class="tile is-child box">
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<p class="title">{{ runningContainers.length }} / {{ containers.length }}</p>
|
||||
<p class="heading">{{ $t("label.running") }} / {{ $t("label.total-containers") }}</p>
|
||||
</div>
|
||||
<div class="flex flex-col gap-16 px-4 pt-8 md:px-8">
|
||||
<section>
|
||||
<div class="stats grid bg-base-lighter shadow">
|
||||
<div class="stat">
|
||||
<div class="stat-value">{{ runningContainers.length }} / {{ containers.length }}</div>
|
||||
<div class="stat-title">{{ $t("label.running") }} / {{ $t("label.total-containers") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile is-parent">
|
||||
<div class="tile is-child box">
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<p class="title">{{ totalCpu }}%</p>
|
||||
<p class="heading">{{ $t("label.total-cpu-usage") }}</p>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-value">{{ totalCpu }}%</div>
|
||||
<div class="stat-title">{{ $t("label.total-cpu-usage") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile is-parent">
|
||||
<div class="tile is-child box">
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<p class="title">{{ formatBytes(totalMem) }}</p>
|
||||
<p class="heading">{{ $t("label.total-mem-usage") }}</p>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-value">{{ formatBytes(totalMem) }}</div>
|
||||
<div class="stat-title">{{ $t("label.total-mem-usage") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tile is-parent">
|
||||
<div class="tile is-child box">
|
||||
<div class="level-item has-text-centered">
|
||||
<div>
|
||||
<p class="title">{{ version }}</p>
|
||||
<p class="heading">{{ $t("label.dozzle-version") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section class="section table-container">
|
||||
<div class="box">
|
||||
<div class="stat">
|
||||
<div class="stat-value">{{ version }}</div>
|
||||
<div class="stat-title">{{ $t("label.dozzle-version") }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<container-table :containers="runningContainers"></container-table>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { Container } from "@/models/Container";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { version } = config;
|
||||
const containerStore = useContainerStore();
|
||||
const { containers } = storeToRefs(containerStore) as { containers: unknown } as { containers: Ref<Container[]> };
|
||||
const { containers, ready } = storeToRefs(containerStore) as unknown as {
|
||||
containers: Ref<Container[]>;
|
||||
ready: Ref<boolean>;
|
||||
};
|
||||
|
||||
const mostRecentContainers = $computed(() => [...containers.value].sort((a, b) => +b.created - +a.created));
|
||||
const mostRecentContainers = $computed(() => containers.value.toSorted((a, b) => +b.created - +a.created));
|
||||
const runningContainers = $computed(() => mostRecentContainers.filter((c) => c.state === "running"));
|
||||
|
||||
let totalCpu = $ref(0);
|
||||
@@ -76,59 +59,31 @@ useIntervalFn(
|
||||
1000,
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watchEffect(() => {
|
||||
if (ready.value) {
|
||||
setTitle(t("title.dashboard", { count: runningContainers.length }));
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.panel {
|
||||
border: 1px solid var(--border-color);
|
||||
|
||||
.panel-block,
|
||||
.panel-tabs {
|
||||
border-color: var(--border-color);
|
||||
|
||||
.is-active {
|
||||
border-color: var(--border-hover-color);
|
||||
}
|
||||
|
||||
.name {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.status {
|
||||
margin-left: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.pb-0-is-mobile {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.pt-0-is-mobile {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
padding: 10px 3px;
|
||||
}
|
||||
|
||||
.bar-chart {
|
||||
height: 1.5em;
|
||||
.bar-text {
|
||||
font-size: 0.9em;
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
:deep(tr td) {
|
||||
padding-top: 1em;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.stat > div {
|
||||
@apply text-center;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
@apply font-light;
|
||||
}
|
||||
|
||||
.stat-title {
|
||||
@apply font-light;
|
||||
}
|
||||
|
||||
.section + .section {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
@@ -1,50 +1,39 @@
|
||||
<template>
|
||||
<div class="hero is-halfheight">
|
||||
<div class="hero-body">
|
||||
<div class="container">
|
||||
<section class="columns is-centered section">
|
||||
<div class="column is-4">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<form action="" method="post" @submit.prevent="onLogin" ref="form">
|
||||
<div class="field">
|
||||
<label class="label">{{ $t("label.username") }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
v-model="username"
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">{{ $t("label.password") }}</label>
|
||||
<div class="control">
|
||||
<input
|
||||
class="input"
|
||||
type="password"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
v-model="password"
|
||||
/>
|
||||
</div>
|
||||
<p class="help is-danger" v-if="error">{{ $t("error.invalid-auth") }}</p>
|
||||
</div>
|
||||
<div class="field is-grouped is-grouped-centered mt-5">
|
||||
<p class="control">
|
||||
<button class="button is-primary" type="submit">{{ $t("button.login") }}</button>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="card w-96 flex-shrink-0 bg-base-lighter shadow-2xl">
|
||||
<div class="card-body">
|
||||
<form action="" method="post" @submit.prevent="onLogin" ref="form">
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text"> {{ $t("label.username") }} </span>
|
||||
</label>
|
||||
<input
|
||||
class="input input-bordered"
|
||||
type="text"
|
||||
name="username"
|
||||
autocomplete="username"
|
||||
v-model="username"
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label class="label">
|
||||
<span class="label-text">Password</span>
|
||||
</label>
|
||||
<input
|
||||
class="input input-bordered"
|
||||
type="password"
|
||||
name="password"
|
||||
autocomplete="current-password"
|
||||
v-model="password"
|
||||
/>
|
||||
</div>
|
||||
<label class="label text-red" v-if="error">
|
||||
{{ $t("error.invalid-auth") }}
|
||||
</label>
|
||||
<div class="form-control mt-6">
|
||||
<button class="btn btn-primary" type="submit">{{ $t("button.login") }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div>
|
||||
<section class="section">
|
||||
<div class="mt-10 flex flex-col gap-8 px-4 md:px-8">
|
||||
<section>
|
||||
<div class="has-underline">
|
||||
<h2 class="title is-4">{{ $t("settings.about") }}</h2>
|
||||
<h2>{{ $t("settings.about") }}</h2>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -14,120 +14,75 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<section class="flex flex-col gap-4">
|
||||
<div class="has-underline">
|
||||
<h2 class="title is-4">{{ $t("settings.display") }}</h2>
|
||||
<h2>{{ $t("settings.display") }}</h2>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<o-switch v-model="smallerScrollbars"> {{ $t("settings.small-scrollbars") }} </o-switch>
|
||||
<div>
|
||||
<toggle v-model="smallerScrollbars"> {{ $t("settings.small-scrollbars") }} </toggle>
|
||||
</div>
|
||||
<div class="item">
|
||||
<o-switch v-model="showTimestamp"> {{ $t("settings.show-timesamps") }} </o-switch>
|
||||
<div>
|
||||
<toggle v-model="showTimestamp">{{ $t("settings.show-timesamps") }}</toggle>
|
||||
</div>
|
||||
<div class="item">
|
||||
<o-switch v-model="showStd"> {{ $t("settings.show-std") }} </o-switch>
|
||||
<div>
|
||||
<toggle v-model="showStd">{{ $t("settings.show-std") }}</toggle>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<o-switch v-model="softWrap"> {{ $t("settings.soft-wrap") }}</o-switch>
|
||||
<div>
|
||||
<toggle v-model="softWrap">{{ $t("settings.soft-wrap") }}</toggle>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column is-narrow">
|
||||
<o-field>
|
||||
<o-dropdown v-model="hourStyle" aria-role="list">
|
||||
<template #trigger>
|
||||
<o-button variant="primary" type="button">
|
||||
<span class="is-capitalized">{{ hourStyle }}</span>
|
||||
<span class="icon">
|
||||
<carbon:caret-down />
|
||||
</span>
|
||||
</o-button>
|
||||
</template>
|
||||
|
||||
<o-dropdown-item :value="value" aria-role="listitem" v-for="value in ['auto', '12', '24']" :key="value">
|
||||
<span class="is-capitalized">{{ value }}</span>
|
||||
</o-dropdown-item>
|
||||
</o-dropdown>
|
||||
</o-field>
|
||||
</div>
|
||||
<div class="column">
|
||||
{{ $t("settings.12-24-format") }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<dropdown
|
||||
v-model="hourStyle"
|
||||
:options="[
|
||||
{ label: 'Auto', value: 'auto' },
|
||||
{ label: '12', value: '12' },
|
||||
{ label: '24', value: '24' },
|
||||
]"
|
||||
/>
|
||||
{{ $t("settings.12-24-format") }}
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column is-narrow">
|
||||
<o-field>
|
||||
<o-dropdown v-model="size" aria-role="list">
|
||||
<template #trigger>
|
||||
<o-button variant="primary" type="button">
|
||||
<span class="is-capitalized">{{ size }}</span>
|
||||
<span class="icon">
|
||||
<carbon:caret-down />
|
||||
</span>
|
||||
</o-button>
|
||||
</template>
|
||||
|
||||
<o-dropdown-item
|
||||
:value="value"
|
||||
aria-role="listitem"
|
||||
v-for="value in ['small', 'medium', 'large']"
|
||||
:key="value"
|
||||
>
|
||||
<span class="is-capitalized">{{ value }}</span>
|
||||
</o-dropdown-item>
|
||||
</o-dropdown>
|
||||
</o-field>
|
||||
</div>
|
||||
<div class="column">{{ $t("settings.font-size") }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<dropdown
|
||||
v-model="size"
|
||||
:options="[
|
||||
{ label: 'Small', value: 'small' },
|
||||
{ label: 'Medium', value: 'medium' },
|
||||
{ label: 'Large', value: 'large' },
|
||||
]"
|
||||
/>
|
||||
{{ $t("settings.font-size") }}
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="columns is-vcentered">
|
||||
<div class="column is-narrow">
|
||||
<o-field>
|
||||
<o-dropdown v-model="lightTheme" aria-role="list">
|
||||
<template #trigger>
|
||||
<o-button variant="primary" type="button">
|
||||
<span class="is-capitalized">{{ lightTheme }}</span>
|
||||
<span class="icon">
|
||||
<carbon:caret-down />
|
||||
</span>
|
||||
</o-button>
|
||||
</template>
|
||||
|
||||
<o-dropdown-item
|
||||
:value="value"
|
||||
aria-role="listitem"
|
||||
v-for="value in ['auto', 'dark', 'light']"
|
||||
:key="value"
|
||||
>
|
||||
<span class="is-capitalized">{{ value }}</span>
|
||||
</o-dropdown-item>
|
||||
</o-dropdown>
|
||||
</o-field>
|
||||
</div>
|
||||
<div class="column">{{ $t("settings.color-scheme") }}</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-6">
|
||||
<dropdown
|
||||
v-model="lightTheme"
|
||||
:options="[
|
||||
{ label: 'Auto', value: 'auto' },
|
||||
{ label: 'Dark', value: 'dark' },
|
||||
{ label: 'Light', value: 'light' },
|
||||
]"
|
||||
/>
|
||||
{{ $t("settings.color-scheme") }}
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<section class="flex flex-col gap-2">
|
||||
<div class="has-underline">
|
||||
<h2 class="title is-4">{{ $t("settings.options") }}</h2>
|
||||
<h2>{{ $t("settings.options") }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<toggle v-model="search">
|
||||
<div>{{ $t("settings.search") }} <key-shortcut char="f" class="align-top"></key-shortcut></div>
|
||||
</toggle>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<o-switch v-model="search">
|
||||
<span>{{ $t("settings.search") }} <key-shortcut char="f"></key-shortcut></span>
|
||||
</o-switch>
|
||||
<div>
|
||||
<toggle v-model="showAllContainers">{{ $t("settings.show-stopped-containers") }}</toggle>
|
||||
</div>
|
||||
|
||||
<div class="item">
|
||||
<o-switch v-model="showAllContainers"> {{ $t("settings.show-stopped-containers") }} </o-switch>
|
||||
<div>
|
||||
<toggle v-model="automaticRedirect">{{ $t("settings.automatic-redirect") }}</toggle>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -144,6 +99,7 @@ import {
|
||||
showAllContainers,
|
||||
size,
|
||||
softWrap,
|
||||
automaticRedirect,
|
||||
} from "@/composables/settings";
|
||||
|
||||
const { t } = useI18n();
|
||||
@@ -174,35 +130,12 @@ async function fetchNextRelease() {
|
||||
|
||||
fetchNextRelease();
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.title {
|
||||
color: var(--title-color);
|
||||
}
|
||||
|
||||
a.next-release {
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
|
||||
<style lang="postcss" scoped>
|
||||
.has-underline {
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
padding: 1em 0px;
|
||||
margin-bottom: 1em;
|
||||
@apply mb-4 border-b border-base-content/50 py-4;
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
code {
|
||||
border-radius: 4px;
|
||||
background-color: #444;
|
||||
:deep(a:not(.menu a)) {
|
||||
@apply text-primary underline-offset-4 hover:underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,11 @@ import { acceptHMRUpdate, defineStore } from "pinia";
|
||||
import { Ref, UnwrapNestedRefs } from "vue";
|
||||
import type { ContainerHealth, ContainerJson, ContainerStat } from "@/types/Container";
|
||||
import { Container } from "@/models/Container";
|
||||
import i18n from "@/modules/i18n";
|
||||
|
||||
const { showToast } = useToast();
|
||||
// @ts-ignore
|
||||
const { t } = i18n.global;
|
||||
|
||||
export const useContainerStore = defineStore("container", () => {
|
||||
const containers: Ref<Container[]> = ref([]);
|
||||
@@ -30,6 +35,19 @@ export const useContainerStore = defineStore("container", () => {
|
||||
es?.close();
|
||||
ready.value = false;
|
||||
es = new EventSource(`${config.base}/api/events/stream`);
|
||||
es.addEventListener("error", (e) => {
|
||||
if (es?.readyState === EventSource.CLOSED) {
|
||||
showToast(
|
||||
{
|
||||
id: "events-stream",
|
||||
message: t("error.events-stream.message"),
|
||||
title: t("error.events-stream.title"),
|
||||
type: "error",
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
es.addEventListener("containers-changed", (e: Event) =>
|
||||
updateContainers(JSON.parse((e as MessageEvent).data) as ContainerJson[]),
|
||||
@@ -63,6 +81,22 @@ export const useContainerStore = defineStore("container", () => {
|
||||
|
||||
connect();
|
||||
|
||||
(async function () {
|
||||
try {
|
||||
await until(ready).toBe(true, { timeout: 8000, throwOnTimeout: true });
|
||||
} catch (e) {
|
||||
showToast(
|
||||
{
|
||||
id: "events-timeout",
|
||||
message: t("error.events-timeout.message"),
|
||||
title: t("error.events-timeout.title"),
|
||||
type: "error",
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
const updateContainers = (containersPayload: ContainerJson[]) => {
|
||||
const existingContainers = containersPayload.filter((c) => allContainersById.value[c.id]);
|
||||
const newContainers = containersPayload.filter((c) => !allContainersById.value[c.id]);
|
||||
@@ -93,8 +127,8 @@ export const useContainerStore = defineStore("container", () => {
|
||||
};
|
||||
|
||||
const currentContainer = (id: Ref<string>) => computed(() => allContainersById.value[id.value]);
|
||||
const appendActiveContainer = ({ id }: Container) => activeContainerIds.value.push(id);
|
||||
const removeActiveContainer = ({ id }: Container) =>
|
||||
const appendActiveContainer = ({ id }: { id: string }) => activeContainerIds.value.push(id);
|
||||
const removeActiveContainer = ({ id }: { id: string }) =>
|
||||
activeContainerIds.value.splice(activeContainerIds.value.indexOf(id), 1);
|
||||
|
||||
return {
|
||||
@@ -110,8 +144,6 @@ export const useContainerStore = defineStore("container", () => {
|
||||
};
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
if (import.meta.hot) {
|
||||
// @ts-ignore
|
||||
import.meta.hot.accept(acceptHMRUpdate(useContainerStore, import.meta.hot));
|
||||
}
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
@charset "utf-8";
|
||||
@import "bulma/sass/utilities/initial-variables.sass";
|
||||
|
||||
$body-background-color: var(--body-background-color);
|
||||
|
||||
$scheme-main: var(--scheme-main);
|
||||
$scheme-main-bis: var(--scheme-main-bis);
|
||||
$scheme-main-ter: var(--scheme-main-ter);
|
||||
|
||||
$border: var(--border-color);
|
||||
$border-hover: var(--border-hover-color);
|
||||
|
||||
$menu-item-active-background-color: var(--menu-item-active-background-color);
|
||||
$menu-item-color: var(--menu-item-color);
|
||||
$menu-item-hover-background-color: var(--menu-item-hover-background-color);
|
||||
$menu-item-hover-color: var(--menu-item-hover-color);
|
||||
|
||||
$text-strong: var(--text-strong-color);
|
||||
$text: var(--text-color);
|
||||
$text-light: var(--text-light-color);
|
||||
|
||||
$panel-heading-background-color: var(--panel-heading-background-color);
|
||||
$panel-heading-color: var(--panel-heading-color);
|
||||
|
||||
$link: $turquoise;
|
||||
$link-active: $grey-dark;
|
||||
$link-hover: $yellow;
|
||||
|
||||
$dark-toolbar-color: rgba($black-bis, 0.7);
|
||||
$light-toolbar-color: rgba($grey-darker, 0.7);
|
||||
|
||||
@import "bulma/bulma";
|
||||
@import "@oruga-ui/theme-bulma/dist/scss/components/utils/all.scss";
|
||||
@import "@oruga-ui/theme-bulma/dist/scss/components/autocomplete.scss";
|
||||
@import "@oruga-ui/theme-bulma/dist/scss/components/button.scss";
|
||||
@import "@oruga-ui/theme-bulma/dist/scss/components/modal.scss";
|
||||
@import "@oruga-ui/theme-bulma/dist/scss/components/switch.scss";
|
||||
@import "@oruga-ui/theme-bulma/dist/scss/components/dropdown.scss";
|
||||
@import "@oruga-ui/theme-bulma/dist/scss/components/skeleton.scss";
|
||||
@import "splitpanes/dist/splitpanes.css";
|
||||
|
||||
@mixin dark {
|
||||
--scheme-main: #{$black};
|
||||
--scheme-main-bis: #{$black-bis};
|
||||
--scheme-main-ter: #{$black-ter};
|
||||
|
||||
--border-color: #{$grey-darker};
|
||||
--border-hover-color: var(--secondary-color);
|
||||
--logo-color: var(--secondary-color);
|
||||
|
||||
--primary-color: #{$turquoise};
|
||||
--secondary-color: #{$yellow};
|
||||
|
||||
--body-background-color: #{$black-bis};
|
||||
--action-toolbar-background-color: #{$dark-toolbar-color};
|
||||
--body-color: #{$grey-lighter};
|
||||
|
||||
--menu-item-active-background-color: var(--primary-color);
|
||||
--menu-item-color: hsl(0, 6%, 87%);
|
||||
--menu-item-hover-background-color: #{$white-ter};
|
||||
--menu-item-hover-color: #{$black-ter};
|
||||
|
||||
--panel-heading-background-color: var(--secondary-color);
|
||||
--panel-heading-color: var(--scheme-main-bis);
|
||||
|
||||
--text-strong-color: #{$grey-lightest};
|
||||
--text-color: #{$grey-lighter};
|
||||
--text-light-color: #{$grey};
|
||||
}
|
||||
|
||||
@mixin light {
|
||||
--scheme-main: #{$white};
|
||||
--scheme-main-bis: #{$white-bis};
|
||||
--scheme-main-ter: #{$white-ter};
|
||||
|
||||
--border-color: #{$grey-lighter};
|
||||
--border-hover-color: var(--secondary-color);
|
||||
--logo-color: var(--secondary-color);
|
||||
|
||||
--primary-color: #{$turquoise};
|
||||
--secondary-color: rgb(249 115 22);
|
||||
|
||||
--body-background-color: #{$white-bis};
|
||||
--action-toolbar-background-color: #{$light-toolbar-color};
|
||||
--body-color: #{$grey-darker};
|
||||
|
||||
--menu-item-active-background-color: var(--primary-color);
|
||||
--menu-item-color: #{$grey-dark};
|
||||
--menu-item-hover-background-color: #eee8e7;
|
||||
--menu-item-hover-color: #{$black-ter};
|
||||
|
||||
--panel-heading-background-color: var(--secondary-color);
|
||||
--panel-heading-color: var(--text-strong-color);
|
||||
|
||||
--text-strong-color: #{$grey-dark};
|
||||
--text-color: #{$grey-darker};
|
||||
--text-light-color: #{$grey};
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
@include dark;
|
||||
}
|
||||
|
||||
[data-theme="light"] {
|
||||
@include light;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
@include dark;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
html {
|
||||
@include light;
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
--green-color: #00b5ad;
|
||||
--red-color: #f44336;
|
||||
--purple-color: #9c27b0;
|
||||
--orange-color: #ff9800;
|
||||
--blue-color: #2196f3;
|
||||
}
|
||||
|
||||
.is-red {
|
||||
color: var(--red-color);
|
||||
}
|
||||
|
||||
.is-green {
|
||||
color: var(--green-color);
|
||||
}
|
||||
|
||||
.is-purple {
|
||||
color: var(--purple-color);
|
||||
}
|
||||
|
||||
.is-orange {
|
||||
color: var(--orange-color);
|
||||
}
|
||||
|
||||
.is-blue {
|
||||
color: var(--blue-color);
|
||||
}
|
||||
|
||||
html {
|
||||
overflow-x: unset;
|
||||
overflow-y: unset;
|
||||
scroll-snap-type: y proximity;
|
||||
}
|
||||
|
||||
html.has-custom-scrollbars {
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
display: content;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(128, 128, 128, 0.33);
|
||||
outline: 1px solid slategrey;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background-color: rgba(64, 64, 64, 0.33);
|
||||
}
|
||||
|
||||
section main {
|
||||
scrollbar-color: #353535 transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 770px) {
|
||||
.splitpanes__pane {
|
||||
overflow: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-device-width: 480px) {
|
||||
body {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.splitpanes__splitter {
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.is-ellipsis {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal {
|
||||
z-index: 1000;
|
||||
.modal-background {
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
}
|
||||
|
||||
.button .button-wrapper > span {
|
||||
display: contents;
|
||||
}
|
||||
|
||||
.has-dropshadow {
|
||||
filter: drop-shadow(0 1px 2px rgb(0 0 0 / 0.1)) drop-shadow(0 1px 1px rgb(0 0 0 / 0.06));
|
||||
}
|
||||
|
||||
.has-boxshadow {
|
||||
box-shadow:
|
||||
0 1px 3px 0 rgb(0 0 0 / 0.1),
|
||||
0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
mark {
|
||||
border-radius: 2px;
|
||||
background-color: var(--secondary-color);
|
||||
animation: pops 200ms ease-out;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@keyframes pops {
|
||||
0% {
|
||||
transform: scale(1.5);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
.button.is-rounded:hover {
|
||||
color: var(--text-strong-color);
|
||||
background: var(--scheme-main-ter);
|
||||
}
|
||||
|
||||
a {
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ services:
|
||||
|
||||
playwright:
|
||||
container_name: playwright
|
||||
image: mcr.microsoft.com/playwright:v1.37.1-jammy
|
||||
image: mcr.microsoft.com/playwright:v1.38.0-jammy
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- .:/app
|
||||
|
||||
@@ -157,10 +157,14 @@ func (d *Client) ListContainers() ([]Container, error) {
|
||||
|
||||
var containers = make([]Container, 0, len(list))
|
||||
for _, c := range list {
|
||||
name := "no name"
|
||||
if len(c.Names) > 0 {
|
||||
name = strings.TrimPrefix(c.Names[0], "/")
|
||||
}
|
||||
container := Container{
|
||||
ID: c.ID[:12],
|
||||
Names: c.Names,
|
||||
Name: strings.TrimPrefix(c.Names[0], "/"),
|
||||
Name: name,
|
||||
Image: c.Image,
|
||||
ImageID: c.ImageID,
|
||||
Command: c.Command,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"name": "docs",
|
||||
"devDependencies": {
|
||||
"@netlify/functions": "^1.6.0",
|
||||
"@unocss/preset-typography": "^0.55.2",
|
||||
"@unocss/reset": "^0.55.2",
|
||||
"@unocss/transformer-directives": "^0.55.2",
|
||||
"@netlify/functions": "^2.1.0",
|
||||
"@unocss/preset-typography": "^0.56.4",
|
||||
"@unocss/reset": "^0.56.4",
|
||||
"@unocss/transformer-directives": "^0.56.4",
|
||||
"dozzle": "workspace:*",
|
||||
"sitemap": "^7.1.1",
|
||||
"unocss": "^0.55.2"
|
||||
"unocss": "^0.56.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,17 +6,17 @@ settings:
|
||||
|
||||
devDependencies:
|
||||
'@netlify/functions':
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0
|
||||
specifier: ^2.1.0
|
||||
version: 2.1.0
|
||||
'@unocss/preset-typography':
|
||||
specifier: ^0.55.2
|
||||
version: 0.55.2
|
||||
specifier: ^0.56.4
|
||||
version: 0.56.4
|
||||
'@unocss/reset':
|
||||
specifier: ^0.55.2
|
||||
version: 0.55.2
|
||||
specifier: ^0.56.4
|
||||
version: 0.56.4
|
||||
'@unocss/transformer-directives':
|
||||
specifier: ^0.55.2
|
||||
version: 0.55.2
|
||||
specifier: ^0.56.4
|
||||
version: 0.56.4
|
||||
dozzle:
|
||||
specifier: workspace:*
|
||||
version: link:..
|
||||
@@ -24,8 +24,8 @@ devDependencies:
|
||||
specifier: ^7.1.1
|
||||
version: 7.1.1
|
||||
unocss:
|
||||
specifier: ^0.55.2
|
||||
version: 0.55.2(postcss@8.4.27)(vite@4.4.9)
|
||||
specifier: ^0.56.4
|
||||
version: 0.56.4(postcss@8.4.30)(vite@4.4.9)
|
||||
|
||||
packages:
|
||||
|
||||
@@ -44,12 +44,12 @@ packages:
|
||||
find-up: 5.0.0
|
||||
dev: true
|
||||
|
||||
/@antfu/utils@0.7.5:
|
||||
resolution: {integrity: sha512-dlR6LdS+0SzOAPx/TPRhnoi7hE251OVeT2Snw0RguNbBSbjUHdWr0l3vcUUDg26rEysT89kCbtw1lVorBXLLCg==}
|
||||
/@antfu/utils@0.7.6:
|
||||
resolution: {integrity: sha512-pvFiLP2BeOKA/ZOS6jxx4XhKzdVLHDhGlFEaZ2flWWYf2xOqVniqpk38I04DFRyz+L0ASggl7SkItTc+ZLju4w==}
|
||||
dev: true
|
||||
|
||||
/@esbuild/android-arm64@0.18.19:
|
||||
resolution: {integrity: sha512-4+jkUFQxZkQfQOOxfGVZB38YUWHMJX2ihZwF+2nh8m7bHdWXpixiurgGRN3c/KMSwlltbYI0/i929jwBRMFzbA==}
|
||||
/@esbuild/android-arm64@0.18.20:
|
||||
resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
@@ -57,8 +57,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-arm@0.18.19:
|
||||
resolution: {integrity: sha512-1uOoDurJYh5MNqPqpj3l/TQCI1V25BXgChEldCB7D6iryBYqYKrbZIhYO5AI9fulf66sM8UJpc3UcCly2Tv28w==}
|
||||
/@esbuild/android-arm@0.18.20:
|
||||
resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
@@ -66,8 +66,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/android-x64@0.18.19:
|
||||
resolution: {integrity: sha512-ae5sHYiP/Ogj2YNrLZbWkBmyHIDOhPgpkGvFnke7XFGQldBDWvc/AyYwSLpNuKw9UNkgnLlB/jPpnBmlF3G9Bg==}
|
||||
/@esbuild/android-x64@0.18.20:
|
||||
resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
@@ -75,8 +75,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-arm64@0.18.19:
|
||||
resolution: {integrity: sha512-HIpQvNQWFYROmWDANMRL+jZvvTQGOiTuwWBIuAsMaQrnStedM+nEKJBzKQ6bfT9RFKH2wZ+ej+DY7+9xHBTFPg==}
|
||||
/@esbuild/darwin-arm64@0.18.20:
|
||||
resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
@@ -84,8 +84,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/darwin-x64@0.18.19:
|
||||
resolution: {integrity: sha512-m6JdvXJQt0thNLIcWOeG079h2ivhYH4B5sVCgqb/B29zTcFd7EE8/J1nIUHhdtwGeItdUeqKaqqb4towwxvglQ==}
|
||||
/@esbuild/darwin-x64@0.18.20:
|
||||
resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
@@ -93,8 +93,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-arm64@0.18.19:
|
||||
resolution: {integrity: sha512-G0p4EFMPZhGn/xVNspUyMQbORH3nlKTV0bFNHPIwLraBuAkTeMyxNviTe0ZXUbIXQrR1lrwniFjNFU4s+x7veQ==}
|
||||
/@esbuild/freebsd-arm64@0.18.20:
|
||||
resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
@@ -102,8 +102,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/freebsd-x64@0.18.19:
|
||||
resolution: {integrity: sha512-hBxgRlG42+W+j/1/cvlnSa+3+OBKeDCyO7OG2ICya1YJaSCYfSpuG30KfOnQHI7Ytgu4bRqCgrYXxQEzy0zM5Q==}
|
||||
/@esbuild/freebsd-x64@0.18.20:
|
||||
resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
@@ -111,8 +111,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm64@0.18.19:
|
||||
resolution: {integrity: sha512-X8g33tczY0GsJq3lhyBrjnFtaKjWVpp1gMq5IlF9BQJ3TUfSK74nQnz9mRIEejmcV+OIYn6bkOJeUaU1Knrljg==}
|
||||
/@esbuild/linux-arm64@0.18.20:
|
||||
resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
@@ -120,8 +120,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-arm@0.18.19:
|
||||
resolution: {integrity: sha512-qtWyoQskfJlb9MD45mvzCEKeO4uCnDZ7lPFeNqbfaaJHqBiH9qA5Vu2EuckqYZuFMJWy1l4dxTf9NOulCVfUjg==}
|
||||
/@esbuild/linux-arm@0.18.20:
|
||||
resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
@@ -129,8 +129,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ia32@0.18.19:
|
||||
resolution: {integrity: sha512-SAkRWJgb+KN+gOhmbiE6/wu23D6HRcGQi15cB13IVtBZZgXxygTV5GJlUAKLQ5Gcx0gtlmt+XIxEmSqA6sZTOw==}
|
||||
/@esbuild/linux-ia32@0.18.20:
|
||||
resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
@@ -138,8 +138,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-loong64@0.18.19:
|
||||
resolution: {integrity: sha512-YLAslaO8NsB9UOxBchos82AOMRDbIAWChwDKfjlGrHSzS3v1kxce7dGlSTsrb0PJwo1KYccypN3VNjQVLtz7LA==}
|
||||
/@esbuild/linux-loong64@0.18.20:
|
||||
resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
@@ -147,8 +147,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-mips64el@0.18.19:
|
||||
resolution: {integrity: sha512-vSYFtlYds/oTI8aflEP65xo3MXChMwBOG1eWPGGKs/ev9zkTeXVvciU+nifq8J1JYMz+eQ4J9JDN0O2RKF8+1Q==}
|
||||
/@esbuild/linux-mips64el@0.18.20:
|
||||
resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
@@ -156,8 +156,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-ppc64@0.18.19:
|
||||
resolution: {integrity: sha512-tgG41lRVwlzqO9tv9l7aXYVw35BxKXLtPam1qALScwSqPivI8hjkZLNH0deaaSCYCFT9cBIdB+hUjWFlFFLL9A==}
|
||||
/@esbuild/linux-ppc64@0.18.20:
|
||||
resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
@@ -165,8 +165,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-riscv64@0.18.19:
|
||||
resolution: {integrity: sha512-EgBZFLoN1S5RuB4cCJI31pBPsjE1nZ+3+fHRjguq9Ibrzo29bOLSBcH1KZJvRNh5qtd+fcYIGiIUia8Jw5r1lQ==}
|
||||
/@esbuild/linux-riscv64@0.18.20:
|
||||
resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
@@ -174,8 +174,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-s390x@0.18.19:
|
||||
resolution: {integrity: sha512-q1V1rtHRojAzjSigZEqrcLkpfh5K09ShCoIsdTakozVBnM5rgV58PLFticqDp5UJ9uE0HScov9QNbbl8HBo6QQ==}
|
||||
/@esbuild/linux-s390x@0.18.20:
|
||||
resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
@@ -183,8 +183,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/linux-x64@0.18.19:
|
||||
resolution: {integrity: sha512-D0IiYjpZRXxGZLQfsydeAD7ZWqdGyFLBj5f2UshJpy09WPs3qizDCsEr8zyzcym6Woj/UI9ZzMIXwvoXVtyt0A==}
|
||||
/@esbuild/linux-x64@0.18.20:
|
||||
resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
@@ -192,8 +192,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/netbsd-x64@0.18.19:
|
||||
resolution: {integrity: sha512-3tt3SOS8L3D54R8oER41UdDshlBIAjYhdWRPiZCTZ1E41+shIZBpTjaW5UaN/jD1ENE/Ok5lkeqhoNMbxstyxw==}
|
||||
/@esbuild/netbsd-x64@0.18.20:
|
||||
resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
@@ -201,8 +201,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/openbsd-x64@0.18.19:
|
||||
resolution: {integrity: sha512-MxbhcuAYQPlfln1EMc4T26OUoeg/YQc6wNoEV8xvktDKZhLtBxjkoeESSo9BbPaGKhAPzusXYj5n8n5A8iZSrA==}
|
||||
/@esbuild/openbsd-x64@0.18.20:
|
||||
resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
@@ -210,8 +210,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/sunos-x64@0.18.19:
|
||||
resolution: {integrity: sha512-m0/UOq1wj25JpWqOJxoWBRM9VWc3c32xiNzd+ERlYstUZ6uwx5SZsQUtkiFHaYmcaoj+f6+Tfcl7atuAz3idwQ==}
|
||||
/@esbuild/sunos-x64@0.18.20:
|
||||
resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
@@ -219,8 +219,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-arm64@0.18.19:
|
||||
resolution: {integrity: sha512-L4vb6pcoB1cEcXUHU6EPnUhUc4+/tcz4OqlXTWPcSQWxegfmcOprhmIleKKwmMNQVc4wrx/+jB7tGkjjDmiupg==}
|
||||
/@esbuild/win32-arm64@0.18.20:
|
||||
resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
@@ -228,8 +228,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-ia32@0.18.19:
|
||||
resolution: {integrity: sha512-rQng7LXSKdrDlNDb7/v0fujob6X0GAazoK/IPd9C3oShr642ri8uIBkgM37/l8B3Rd5sBQcqUXoDdEy75XC/jg==}
|
||||
/@esbuild/win32-ia32@0.18.20:
|
||||
resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
@@ -237,8 +237,8 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@esbuild/win32-x64@0.18.19:
|
||||
resolution: {integrity: sha512-z69jhyG20Gq4QL5JKPLqUT+eREuqnDAFItLbza4JCmpvUnIlY73YNjd5djlO7kBiiZnvTnJuAbOjIoZIOa1GjA==}
|
||||
/@esbuild/win32-x64@0.18.20:
|
||||
resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -250,11 +250,11 @@ packages:
|
||||
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
|
||||
dev: true
|
||||
|
||||
/@iconify/utils@2.1.7:
|
||||
resolution: {integrity: sha512-P8S3z/L1LcV4Qem9AoCfVAaTFGySEMzFEY4CHZLkfRj0Fv9LiR+AwjDgrDrzyI93U2L2mg9JHsbTJ52mF8suNw==}
|
||||
/@iconify/utils@2.1.10:
|
||||
resolution: {integrity: sha512-0/+5hxjzCZ9RoYpqxnOzbnpQyMdZRuHcMxPJeuX+x/aZkAAD/N4TajDjAPT7LpX+M0bfLExj/p0bbDkUfp0lrg==}
|
||||
dependencies:
|
||||
'@antfu/install-pkg': 0.1.1
|
||||
'@antfu/utils': 0.7.5
|
||||
'@antfu/utils': 0.7.6
|
||||
'@iconify/types': 2.0.0
|
||||
debug: 4.3.4
|
||||
kolorist: 1.8.0
|
||||
@@ -293,13 +293,27 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: true
|
||||
|
||||
/@netlify/functions@1.6.0:
|
||||
resolution: {integrity: sha512-6G92AlcpFrQG72XU8YH8pg94eDnq7+Q0YJhb8x4qNpdGsvuzvrfHWBmqFGp/Yshmv4wex9lpsTRZOocdrA2erQ==}
|
||||
/@netlify/functions@2.1.0:
|
||||
resolution: {integrity: sha512-QWuyj1Q6z6Cqcq3qCcOB75nxjYOxlNmv/d9/nnX4asSRQcWhM/ehXl/KDL6Sl9aufMRe6uQY10s6i9zCDNMRjg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
dependencies:
|
||||
'@netlify/serverless-functions-api': 1.7.3
|
||||
is-promise: 4.0.0
|
||||
dev: true
|
||||
|
||||
/@netlify/node-cookies@0.1.0:
|
||||
resolution: {integrity: sha512-OAs1xG+FfLX0LoRASpqzVntVV/RpYkgpI0VrUnw2u0Q1qiZUzcPffxRK8HF3gc4GjuhG5ahOEMJ9bswBiZPq0g==}
|
||||
engines: {node: ^14.16.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/@netlify/serverless-functions-api@1.7.3:
|
||||
resolution: {integrity: sha512-n6/7cJlSWvvbBlUOEAbkGyEld80S6KbG/ldQI9OhLfe1lTatgKmrTNIgqVNpaWpUdTgP2OHWFjmFBzkxxBWs5w==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
dependencies:
|
||||
'@netlify/node-cookies': 0.1.0
|
||||
urlpattern-polyfill: 8.0.2
|
||||
dev: true
|
||||
|
||||
/@nodelib/fs.scandir@2.1.5:
|
||||
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -321,12 +335,12 @@ packages:
|
||||
fastq: 1.15.0
|
||||
dev: true
|
||||
|
||||
/@polka/url@1.0.0-next.21:
|
||||
resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
|
||||
/@polka/url@1.0.0-next.23:
|
||||
resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==}
|
||||
dev: true
|
||||
|
||||
/@rollup/pluginutils@5.0.3:
|
||||
resolution: {integrity: sha512-hfllNN4a80rwNQ9QCxhxuHCGHMAvabXqxNdaChUSSadMre7t4iEUI6fFAhBOn/eIYTgYVhBv7vCLsAJ4u3lf3g==}
|
||||
/@rollup/pluginutils@5.0.4:
|
||||
resolution: {integrity: sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
rollup: ^1.20.0||^2.0.0||^3.0.0
|
||||
@@ -334,13 +348,13 @@ packages:
|
||||
rollup:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/estree': 1.0.1
|
||||
'@types/estree': 1.0.2
|
||||
estree-walker: 2.0.2
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/@types/estree@1.0.1:
|
||||
resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==}
|
||||
/@types/estree@1.0.2:
|
||||
resolution: {integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==}
|
||||
dev: true
|
||||
|
||||
/@types/node@17.0.45:
|
||||
@@ -357,195 +371,208 @@ packages:
|
||||
'@types/node': 18.14.2
|
||||
dev: true
|
||||
|
||||
/@unocss/astro@0.55.2(vite@4.4.9):
|
||||
resolution: {integrity: sha512-cSzBKPEveZZQDZp5bq0UlL8CVvzB/1LsgZmZufxi9oMMjMJYqzfTkKg5z65GcP82Xp5c0N3KKkl/R6I+/7Iwvw==}
|
||||
/@unocss/astro@0.56.4(vite@4.4.9):
|
||||
resolution: {integrity: sha512-2op/0y5UjiE0PRzKwHSEcOFtjDTkeIoCJR9FAYFC+xTx95Sp7+S4yClwaURZtohPnxQmGug0eGpOgsFzoJwmOQ==}
|
||||
peerDependencies:
|
||||
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
|
||||
peerDependenciesMeta:
|
||||
vite:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/reset': 0.55.2
|
||||
'@unocss/vite': 0.55.2(vite@4.4.9)
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/reset': 0.56.4
|
||||
'@unocss/vite': 0.56.4(vite@4.4.9)
|
||||
vite: 4.4.9
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
dev: true
|
||||
|
||||
/@unocss/cli@0.55.2:
|
||||
resolution: {integrity: sha512-ZJ8aBhm+3WjGCA5HcOQ4C3mbtJwkgMX2gpjjJ0MPh/iZOz3+/zmHlrXJCS3jIFouRYSwxxanWdrGUuLIQLqPhQ==}
|
||||
/@unocss/cli@0.56.4:
|
||||
resolution: {integrity: sha512-WyqqJWfOVb7hvWU5nxtSFAGiDp/Peeohj1f/Xt0KlmgOPS8RA7kmLjRYruuuRuK5yR677IB+0C4kJDFkRpRlcA==}
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.2.1
|
||||
'@rollup/pluginutils': 5.0.3
|
||||
'@unocss/config': 0.55.2
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/preset-uno': 0.55.2
|
||||
'@rollup/pluginutils': 5.0.4
|
||||
'@unocss/config': 0.56.4
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/preset-uno': 0.56.4
|
||||
cac: 6.7.14
|
||||
chokidar: 3.5.3
|
||||
colorette: 2.0.20
|
||||
consola: 3.2.3
|
||||
fast-glob: 3.3.1
|
||||
magic-string: 0.30.2
|
||||
magic-string: 0.30.3
|
||||
pathe: 1.1.1
|
||||
perfect-debounce: 1.0.0
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
dev: true
|
||||
|
||||
/@unocss/config@0.55.2:
|
||||
resolution: {integrity: sha512-RYDv9QzhUeBz9BY+Pty0xc9vk/m4LGBNMiBghcItW6zXN554JbSuoPD55DmnvO2iXrIYujBZdB/Kob6GLCZpqw==}
|
||||
/@unocss/config@0.56.4:
|
||||
resolution: {integrity: sha512-e8pdkoTkKxqkimAttZ5ELiDXn3oYteNDOxNANygWUhw3h6DA1sKh831dQkhTu3Kigsy0/gQXmFXbekrlywMkKw==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
unconfig: 0.3.10
|
||||
dev: true
|
||||
|
||||
/@unocss/core@0.55.2:
|
||||
resolution: {integrity: sha512-ZLEES8RDgWoK/vttUzl3PM2bZqL3HvhLgj8xdDa09Xw+JiTlR4c66s+hLn52oCoJTnT9lGsD2j7tTGN9ToSiTA==}
|
||||
/@unocss/core@0.56.4:
|
||||
resolution: {integrity: sha512-9DL+2adfjWWnFWp2QOJTq2OcfKKo++agaXnCX7CzeD8KgfWCBqxs2RDoxP3HrDbTflhDHdbjz23B+vJoVykhZw==}
|
||||
dev: true
|
||||
|
||||
/@unocss/extractor-arbitrary-variants@0.55.2:
|
||||
resolution: {integrity: sha512-mHEoFx+ITe3OgFoIUhkCQxRgUjvOJeHtI1Z3Sm8NDMy2vTqOlkSf7NLWEyFfQsSFYqpWGTkaW1XiMZujGMoB/g==}
|
||||
/@unocss/extractor-arbitrary-variants@0.56.4:
|
||||
resolution: {integrity: sha512-Q93Bu3wZld+Vj5dqfJYH6phcmjm7fy6mEul64RexLWF00As9WrRzWEXbLVYmmVwUm9e5xcxZO6s98gKq1nTH6Q==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/inspector@0.55.2:
|
||||
resolution: {integrity: sha512-AMNZ7FsBFhQCMuAQugCk7d+3uoHDN2VFwCzSxk0ITgG51J90jfVgAo9mJf28W/AM4g0qVHScveJDPKzA+2o+Vg==}
|
||||
/@unocss/inspector@0.56.4:
|
||||
resolution: {integrity: sha512-DY3kmX5nGTb8QsbqcGYDvyXXTPhGw7PoVN48wA8o0c+KkcfjjfsoZRTFDpj6P5JcW8Z68gIBOLyTnql6tIMesg==}
|
||||
dependencies:
|
||||
'@unocss/rule-utils': 0.56.4
|
||||
gzip-size: 6.0.0
|
||||
sirv: 2.0.3
|
||||
dev: true
|
||||
|
||||
/@unocss/postcss@0.55.2(postcss@8.4.27):
|
||||
resolution: {integrity: sha512-HJLGINNlQ3DGL9zRGuctX+mOVW2w7o8Wj89v3/2qTcqXBDpwfn1+KlxSjU9rsEPdE4Ur3MIcVXcJC0wz4+EwEA==}
|
||||
/@unocss/postcss@0.56.4(postcss@8.4.30):
|
||||
resolution: {integrity: sha512-lc0+BjW1gY+86tvtUFppHg++Tw8I0V7hlldloUy7W4Fg2Mu8VAi3I/KaSSRvqMr5SNlfaOPv0XuB/BNkDE65Gw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
postcss: ^8.4.21
|
||||
dependencies:
|
||||
'@unocss/config': 0.55.2
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/config': 0.56.4
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/rule-utils': 0.56.4
|
||||
css-tree: 2.3.1
|
||||
fast-glob: 3.3.1
|
||||
magic-string: 0.30.2
|
||||
postcss: 8.4.27
|
||||
magic-string: 0.30.3
|
||||
postcss: 8.4.30
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-attributify@0.55.2:
|
||||
resolution: {integrity: sha512-jn5ulsKpAipsX3Gf2/iSZydgI0eP1ENeoS6rrNBL8zl1mRihnZYFegS75rGYjO6sEfEHrhkBiSHOw7Uv5KtLbw==}
|
||||
/@unocss/preset-attributify@0.56.4:
|
||||
resolution: {integrity: sha512-mjs9NDniycAdipiLCHpGS75mItfFq3ruX6w4ypcvGxVAa8cGOhotWAAxqgHKSJobdDcoFTjnCaK1SA0svBplUQ==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-icons@0.55.2:
|
||||
resolution: {integrity: sha512-NK9LcTlBZv6zO8Qbu+VA9HblzYc5ebuFwaQMfQcYj2Z6dBOT27Ki41LY1qjEXzzMPXb44Q14Rlk0tJc8LtJIpQ==}
|
||||
/@unocss/preset-icons@0.56.4:
|
||||
resolution: {integrity: sha512-N02I6ZYt2cAThE4pINTRY5tUeHNhcUQYeoqtQhQUk7YFfwRMosWA5JbtH7LTzQpYbE2GsbjXkKdII4bkTWNwhA==}
|
||||
dependencies:
|
||||
'@iconify/utils': 2.1.7
|
||||
'@unocss/core': 0.55.2
|
||||
ofetch: 1.1.1
|
||||
'@iconify/utils': 2.1.10
|
||||
'@unocss/core': 0.56.4
|
||||
ofetch: 1.3.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-mini@0.55.2:
|
||||
resolution: {integrity: sha512-jwUsrwtPwMvFVJUP+FVFjq+sp+xQPyFLRPSb89ZI34F1a3EwJ2wioDICLqWjOjY7zei9UgtSY0owBM9vwxw/kg==}
|
||||
/@unocss/preset-mini@0.56.4:
|
||||
resolution: {integrity: sha512-FCKs3sUUiHLXQ/ON/ZCQc2JZQox4tv0W8Du+8Y1mRZ6w1yazqqBhR2yDPaEZ51yKcHmDf6ndiTvcJ2tPIz/o8g==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/extractor-arbitrary-variants': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/extractor-arbitrary-variants': 0.56.4
|
||||
'@unocss/rule-utils': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-tagify@0.55.2:
|
||||
resolution: {integrity: sha512-m8/9wBtUQSwnwsLANhUOc7sukF8ReHJ7ZC6fCfTozRMOhwu+bDcf9G7pguXdNC4DdZXI15cvbZzkYF2l733qUw==}
|
||||
/@unocss/preset-tagify@0.56.4:
|
||||
resolution: {integrity: sha512-CURDiJ+wzjzIARS2vF413hZ94u0EVK0+miY6l1eN6RrlW/YfU8LOaakbC/b0Cxl7hOhLrZuXFTgymcS9Ep4TMw==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-typography@0.55.2:
|
||||
resolution: {integrity: sha512-Y4JEihpKPDlXWXxnnMZbQclqZ4+DUD8RVFk46ERe9CLNEYkFObd4LG7yfSurr/C01zuU/GhEMyOWqSGsSyCxKg==}
|
||||
/@unocss/preset-typography@0.56.4:
|
||||
resolution: {integrity: sha512-/rwCZ0SHa8ht/Chb2rCoENL/S/CWspnTtvK4pPxALEcx/Mqi8Amk45YipU7gXmLsQ3eKwY/s0V1EgHF0DOccHg==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/preset-mini': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/preset-mini': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-uno@0.55.2:
|
||||
resolution: {integrity: sha512-8VJXC6+f5YBjUaTkf+EGAembDYMleb0zjkb4hwXxjPIsO+mXixdZC2icCiN/12DLlwH4FzEvObLKns3CGEAZZw==}
|
||||
/@unocss/preset-uno@0.56.4:
|
||||
resolution: {integrity: sha512-E4VbUe1nIeJ7D1en84osGzwP79IBJ1P9v37Pcx2PvaRAQTKf6WRF7EXzmzEm6HPF+skyGOX9RWuXs9QFODsW8A==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/preset-mini': 0.55.2
|
||||
'@unocss/preset-wind': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/preset-mini': 0.56.4
|
||||
'@unocss/preset-wind': 0.56.4
|
||||
'@unocss/rule-utils': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-web-fonts@0.55.2:
|
||||
resolution: {integrity: sha512-kRnrfZPDkU2r9tp507rsh4kwhUzZ76XBTZLmElYm8tlP6HZzIHcFF8fdW15J4nh81b/IGw8ZOS7aQmqtHu3A8A==}
|
||||
/@unocss/preset-web-fonts@0.56.4:
|
||||
resolution: {integrity: sha512-H9Vy4nYpx1jpQvSBHBTSiB83zU85CtehoK03EbzUrz/i69EjBWiw52+mP/vrtMK0M/5QghcpNf64i8pObFId+w==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
ofetch: 1.1.1
|
||||
'@unocss/core': 0.56.4
|
||||
ofetch: 1.3.3
|
||||
dev: true
|
||||
|
||||
/@unocss/preset-wind@0.55.2:
|
||||
resolution: {integrity: sha512-th/aOokb10ApaiVLNI093mvko4XryJ70oEhzz4tHdSuhnQWf5eY7+k7y9EEYFz8i1OOrKuer0HzUV27llZaufw==}
|
||||
/@unocss/preset-wind@0.56.4:
|
||||
resolution: {integrity: sha512-llQYQxrA531kl1juYMyaDEEl/IVdHoIPXaaJTdNHbGkSbMqrpZSRzLPsGE+CpaOBKTNqa93UyU1qlUGSr9ZB+A==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/preset-mini': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/preset-mini': 0.56.4
|
||||
'@unocss/rule-utils': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/reset@0.55.2:
|
||||
resolution: {integrity: sha512-paInTGIhtI96fcJGZWbkPLW/7qiTlHxSbEIs1HGHcbf3WbwNuKrJUvKlQAhUs2HILNKhvsTXQl05Os8gtinLEA==}
|
||||
/@unocss/reset@0.56.4:
|
||||
resolution: {integrity: sha512-7o3Jpog49ORWW+Rpz7bzYY+CStfLMyBXAeDOzhM2PQjftuQl4ZQOvIdXi6zrXDEN2SJQsi279s2t50ERZN8YcA==}
|
||||
dev: true
|
||||
|
||||
/@unocss/scope@0.55.2:
|
||||
resolution: {integrity: sha512-o1b86ejgaFDqfC712mUZqZDQNf6o1xDzm6+bgHySdiltR8Quo6l8RcoZjZrCvEogtPbko4/XJ374t1NQMUQf4g==}
|
||||
dev: true
|
||||
|
||||
/@unocss/transformer-attributify-jsx-babel@0.55.2:
|
||||
resolution: {integrity: sha512-pmfF546i8pKfMNeYZOJz2UzbuUwj0v7GqcoP5fClyRUzBMUfXdJwBSdFaYkdWR5Q/O1sv+pI0S8r/G9T7QuldA==}
|
||||
/@unocss/rule-utils@0.56.4:
|
||||
resolution: {integrity: sha512-pZmTPqi/Tb+vbwjNXu9+PwxEh7PVe+FwP+pzUbfNSUpE5Hg9CCbl8hpsXRE/r8kdaoKhmw2ryxvjocOgKXG/ag==}
|
||||
engines: {node: '>=14'}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/transformer-attributify-jsx@0.55.2:
|
||||
resolution: {integrity: sha512-WerdaNagorTtYDvbhlZEmeuBrQ5lmPE0vG9r20bPR/vLy9UmbIFPpzt6b/hSLqOUnZnaEfbrpNUlpBZgUXpvsg==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
/@unocss/scope@0.56.4:
|
||||
resolution: {integrity: sha512-fS8HEWFUeuNhE3fSBwgSfcgC8JS80sYY692uvpwWUN29gFawQDyEmAkGbqDZoBuvfFtKN2IA+evVJ5iED1vtuA==}
|
||||
dev: true
|
||||
|
||||
/@unocss/transformer-compile-class@0.55.2:
|
||||
resolution: {integrity: sha512-zKeJtAirFrgj8TheKplgdKrPV9hPN3i2gEy/aQ+CrHHImcQtxZ1FJzmJT1yV77MOXOdeRJOhiePNOe2TE1A4tw==}
|
||||
/@unocss/transformer-attributify-jsx-babel@0.56.4:
|
||||
resolution: {integrity: sha512-ftkW+HX8tgtSG8bL3Ek9NeSHJfLLdIhokyP/31Kx+DZoOXcwGFN2oBfOs/tkPRdLXUDeEh/9+xLZyXSfrq2Kjw==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/transformer-directives@0.55.2:
|
||||
resolution: {integrity: sha512-IJKL5clOiv2RjvHYr4xumS4eFScPsi3Vg4vGugsmn43PZ1FsApp8UElHfhuhBsEEiffnsgTD+N5u/EiPpyI0Gw==}
|
||||
/@unocss/transformer-attributify-jsx@0.56.4:
|
||||
resolution: {integrity: sha512-GWiiCMI4W5MaF5y7GejnvIjRzgpTj6xouQYXurMVyq8GaXX34oSzin09Vh7F2/9lMD582UV5/ISu54A78RtswA==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/transformer-compile-class@0.56.4:
|
||||
resolution: {integrity: sha512-/sPAamamhYBwg29ITrS9FOPF4Mt5r6H0ST0XdAaDVqt0JM3QmMHYUAn5YqvMS1z7c+A6FwsuZ5L0Sn55auXa+g==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/transformer-directives@0.56.4:
|
||||
resolution: {integrity: sha512-L+h0pPgukk12TuqGaZjvGI0y6i3tFfl0dXJnJ2W6eeK2Y2CMirfhCo9Cb2uF81gfIdFKJqXGY6qujK+AS0ji/Q==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/rule-utils': 0.56.4
|
||||
css-tree: 2.3.1
|
||||
dev: true
|
||||
|
||||
/@unocss/transformer-variant-group@0.55.2:
|
||||
resolution: {integrity: sha512-BIAigftn+mfUeQT7sPzJNgvvbrmLj0gmYmeK4U7/8NxUuOuC0ROTNSw+MKU7yDiPYHqb1kxVZ47LZ3GdUcNPRA==}
|
||||
/@unocss/transformer-variant-group@0.56.4:
|
||||
resolution: {integrity: sha512-4ebNLWgspeoaHDLQ6qW8ZEWl7rzXURxBJLaw1hUqMwfbM/HEc5gPwUy7k7W+4TJyB426UpBb6kWLgO2WRrJXWw==}
|
||||
dependencies:
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/core': 0.56.4
|
||||
dev: true
|
||||
|
||||
/@unocss/vite@0.55.2(vite@4.4.9):
|
||||
resolution: {integrity: sha512-JEyEaJt8D+Ed3Z8GDQ0hMWqKsB47/DoS+aPzDoXSIVozgi8seHtfSChBOBUSgcCrozfBVp42YHbYYyloDkb2Yw==}
|
||||
/@unocss/vite@0.56.4(vite@4.4.9):
|
||||
resolution: {integrity: sha512-hswOJk2C7scwQioLIGDnT+H/bbC1EEkq+MhN+VDiVlRUYwaIgLITmSp0q6y6JK2RKD7VhDVxLNMYw4LxxcJuzA==}
|
||||
peerDependencies:
|
||||
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.2.1
|
||||
'@rollup/pluginutils': 5.0.3
|
||||
'@unocss/config': 0.55.2
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/inspector': 0.55.2
|
||||
'@unocss/scope': 0.55.2
|
||||
'@unocss/transformer-directives': 0.55.2
|
||||
'@rollup/pluginutils': 5.0.4
|
||||
'@unocss/config': 0.56.4
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/inspector': 0.56.4
|
||||
'@unocss/scope': 0.56.4
|
||||
'@unocss/transformer-directives': 0.56.4
|
||||
chokidar: 3.5.3
|
||||
fast-glob: 3.3.1
|
||||
magic-string: 0.30.2
|
||||
magic-string: 0.30.3
|
||||
vite: 4.4.9
|
||||
transitivePeerDependencies:
|
||||
- rollup
|
||||
@@ -598,7 +625,7 @@ packages:
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/colorette@2.0.20:
|
||||
@@ -651,34 +678,34 @@ packages:
|
||||
resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
|
||||
dev: true
|
||||
|
||||
/esbuild@0.18.19:
|
||||
resolution: {integrity: sha512-ra3CaIKCzJp5bU5BDfrCc0FRqKj71fQi+gbld0aj6lN0ifuX2fWJYPgLVLGwPfA+ruKna+OWwOvf/yHj6n+i0g==}
|
||||
/esbuild@0.18.20:
|
||||
resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
|
||||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
optionalDependencies:
|
||||
'@esbuild/android-arm': 0.18.19
|
||||
'@esbuild/android-arm64': 0.18.19
|
||||
'@esbuild/android-x64': 0.18.19
|
||||
'@esbuild/darwin-arm64': 0.18.19
|
||||
'@esbuild/darwin-x64': 0.18.19
|
||||
'@esbuild/freebsd-arm64': 0.18.19
|
||||
'@esbuild/freebsd-x64': 0.18.19
|
||||
'@esbuild/linux-arm': 0.18.19
|
||||
'@esbuild/linux-arm64': 0.18.19
|
||||
'@esbuild/linux-ia32': 0.18.19
|
||||
'@esbuild/linux-loong64': 0.18.19
|
||||
'@esbuild/linux-mips64el': 0.18.19
|
||||
'@esbuild/linux-ppc64': 0.18.19
|
||||
'@esbuild/linux-riscv64': 0.18.19
|
||||
'@esbuild/linux-s390x': 0.18.19
|
||||
'@esbuild/linux-x64': 0.18.19
|
||||
'@esbuild/netbsd-x64': 0.18.19
|
||||
'@esbuild/openbsd-x64': 0.18.19
|
||||
'@esbuild/sunos-x64': 0.18.19
|
||||
'@esbuild/win32-arm64': 0.18.19
|
||||
'@esbuild/win32-ia32': 0.18.19
|
||||
'@esbuild/win32-x64': 0.18.19
|
||||
'@esbuild/android-arm': 0.18.20
|
||||
'@esbuild/android-arm64': 0.18.20
|
||||
'@esbuild/android-x64': 0.18.20
|
||||
'@esbuild/darwin-arm64': 0.18.20
|
||||
'@esbuild/darwin-x64': 0.18.20
|
||||
'@esbuild/freebsd-arm64': 0.18.20
|
||||
'@esbuild/freebsd-x64': 0.18.20
|
||||
'@esbuild/linux-arm': 0.18.20
|
||||
'@esbuild/linux-arm64': 0.18.20
|
||||
'@esbuild/linux-ia32': 0.18.20
|
||||
'@esbuild/linux-loong64': 0.18.20
|
||||
'@esbuild/linux-mips64el': 0.18.20
|
||||
'@esbuild/linux-ppc64': 0.18.20
|
||||
'@esbuild/linux-riscv64': 0.18.20
|
||||
'@esbuild/linux-s390x': 0.18.20
|
||||
'@esbuild/linux-x64': 0.18.20
|
||||
'@esbuild/netbsd-x64': 0.18.20
|
||||
'@esbuild/openbsd-x64': 0.18.20
|
||||
'@esbuild/sunos-x64': 0.18.20
|
||||
'@esbuild/win32-arm64': 0.18.20
|
||||
'@esbuild/win32-ia32': 0.18.20
|
||||
'@esbuild/win32-x64': 0.18.20
|
||||
dev: true
|
||||
|
||||
/estree-walker@2.0.2:
|
||||
@@ -732,8 +759,8 @@ packages:
|
||||
path-exists: 4.0.0
|
||||
dev: true
|
||||
|
||||
/fsevents@2.3.2:
|
||||
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
|
||||
/fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
@@ -801,8 +828,8 @@ packages:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
dev: true
|
||||
|
||||
/jiti@1.19.1:
|
||||
resolution: {integrity: sha512-oVhqoRDaBXf7sjkll95LHVS6Myyyb1zaunVwk4Z0+WPSW4gjS0pl01zYKHScTuyEhQsFxV5L4DR5r+YqSyqyyg==}
|
||||
/jiti@1.20.0:
|
||||
resolution: {integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
@@ -826,8 +853,8 @@ packages:
|
||||
p-locate: 5.0.0
|
||||
dev: true
|
||||
|
||||
/magic-string@0.30.2:
|
||||
resolution: {integrity: sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==}
|
||||
/magic-string@0.30.3:
|
||||
resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
@@ -859,13 +886,13 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/mlly@1.4.0:
|
||||
resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==}
|
||||
/mlly@1.4.2:
|
||||
resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
pathe: 1.1.1
|
||||
pkg-types: 1.0.3
|
||||
ufo: 1.2.0
|
||||
ufo: 1.3.0
|
||||
dev: true
|
||||
|
||||
/mrmime@1.0.1:
|
||||
@@ -883,8 +910,8 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/node-fetch-native@1.2.0:
|
||||
resolution: {integrity: sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ==}
|
||||
/node-fetch-native@1.4.0:
|
||||
resolution: {integrity: sha512-F5kfEj95kX8tkDhUCYdV8dg3/8Olx/94zB8+ZNthFs6Bz31UpUi8Xh40TN3thLwXgrwXry1pEg9lJ++tLWTcqA==}
|
||||
dev: true
|
||||
|
||||
/normalize-path@3.0.0:
|
||||
@@ -899,12 +926,12 @@ packages:
|
||||
path-key: 3.1.1
|
||||
dev: true
|
||||
|
||||
/ofetch@1.1.1:
|
||||
resolution: {integrity: sha512-SSMoktrp9SNLi20BWfB/BnnKcL0RDigXThD/mZBeQxkIRv1xrd9183MtLdsqRYLYSqW0eTr5t8w8MqjNhvoOQQ==}
|
||||
/ofetch@1.3.3:
|
||||
resolution: {integrity: sha512-s1ZCMmQWXy4b5K/TW9i/DtiN8Ku+xCiHcjQ6/J/nDdssirrQNOoB165Zu8EqLMA2lln1JUth9a0aW9Ap2ctrUg==}
|
||||
dependencies:
|
||||
destr: 2.0.1
|
||||
node-fetch-native: 1.2.0
|
||||
ufo: 1.2.0
|
||||
node-fetch-native: 1.4.0
|
||||
ufo: 1.3.0
|
||||
dev: true
|
||||
|
||||
/onetime@5.1.2:
|
||||
@@ -959,12 +986,12 @@ packages:
|
||||
resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==}
|
||||
dependencies:
|
||||
jsonc-parser: 3.2.0
|
||||
mlly: 1.4.0
|
||||
mlly: 1.4.2
|
||||
pathe: 1.1.1
|
||||
dev: true
|
||||
|
||||
/postcss@8.4.27:
|
||||
resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==}
|
||||
/postcss@8.4.30:
|
||||
resolution: {integrity: sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
dependencies:
|
||||
nanoid: 3.3.6
|
||||
@@ -988,12 +1015,12 @@ packages:
|
||||
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/rollup@3.27.2:
|
||||
resolution: {integrity: sha512-YGwmHf7h2oUHkVBT248x0yt6vZkYQ3/rvE5iQuVBh3WO8GcJ6BNeOkpoX1yMHIiBm18EMLjBPIoUDkhgnyxGOQ==}
|
||||
/rollup@3.29.3:
|
||||
resolution: {integrity: sha512-T7du6Hum8jOkSWetjRgbwpM6Sy0nECYrYRSmZjayFcOddtKJWU4d17AC3HNUk7HRuqy4p+G7aEZclSHytqUmEg==}
|
||||
engines: {node: '>=14.18.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/run-parallel@1.2.0:
|
||||
@@ -1026,7 +1053,7 @@ packages:
|
||||
resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==}
|
||||
engines: {node: '>= 10'}
|
||||
dependencies:
|
||||
'@polka/url': 1.0.0-next.21
|
||||
'@polka/url': 1.0.0-next.23
|
||||
mrmime: 1.0.1
|
||||
totalist: 3.0.1
|
||||
dev: true
|
||||
@@ -1064,24 +1091,24 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
/ufo@1.2.0:
|
||||
resolution: {integrity: sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==}
|
||||
/ufo@1.3.0:
|
||||
resolution: {integrity: sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==}
|
||||
dev: true
|
||||
|
||||
/unconfig@0.3.10:
|
||||
resolution: {integrity: sha512-tj317lhIq2iZF/NXrJnU1t2UaGUKKz1eL1sK2t63Oq66V9BxqvZV12m55fp/fpQJ+DDmVlLgo7cnLVOZkhlO/A==}
|
||||
dependencies:
|
||||
'@antfu/utils': 0.7.5
|
||||
'@antfu/utils': 0.7.6
|
||||
defu: 6.1.2
|
||||
jiti: 1.19.1
|
||||
mlly: 1.4.0
|
||||
jiti: 1.20.0
|
||||
mlly: 1.4.2
|
||||
dev: true
|
||||
|
||||
/unocss@0.55.2(postcss@8.4.27)(vite@4.4.9):
|
||||
resolution: {integrity: sha512-+C8tFUFIEv40DpEhjA/Yv+RB5HZumkWiON2OlPyrbzapQ8x60F9TUwUS3pw7MlpxI6GfTCYwXKEE6DTGCm1SLA==}
|
||||
/unocss@0.56.4(postcss@8.4.30)(vite@4.4.9):
|
||||
resolution: {integrity: sha512-RcaZHLSlj7BAupx6Y9jJwOSRJ+Lu1pnLwRZuppMfD4GgWWNymDRJ7HF1uNqCCYhxJa1LgU1irOEfO8pzK8NYkw==}
|
||||
engines: {node: '>=14'}
|
||||
peerDependencies:
|
||||
'@unocss/webpack': 0.55.2
|
||||
'@unocss/webpack': 0.56.4
|
||||
vite: ^2.9.0 || ^3.0.0-0 || ^4.0.0
|
||||
peerDependenciesMeta:
|
||||
'@unocss/webpack':
|
||||
@@ -1089,26 +1116,26 @@ packages:
|
||||
vite:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@unocss/astro': 0.55.2(vite@4.4.9)
|
||||
'@unocss/cli': 0.55.2
|
||||
'@unocss/core': 0.55.2
|
||||
'@unocss/extractor-arbitrary-variants': 0.55.2
|
||||
'@unocss/postcss': 0.55.2(postcss@8.4.27)
|
||||
'@unocss/preset-attributify': 0.55.2
|
||||
'@unocss/preset-icons': 0.55.2
|
||||
'@unocss/preset-mini': 0.55.2
|
||||
'@unocss/preset-tagify': 0.55.2
|
||||
'@unocss/preset-typography': 0.55.2
|
||||
'@unocss/preset-uno': 0.55.2
|
||||
'@unocss/preset-web-fonts': 0.55.2
|
||||
'@unocss/preset-wind': 0.55.2
|
||||
'@unocss/reset': 0.55.2
|
||||
'@unocss/transformer-attributify-jsx': 0.55.2
|
||||
'@unocss/transformer-attributify-jsx-babel': 0.55.2
|
||||
'@unocss/transformer-compile-class': 0.55.2
|
||||
'@unocss/transformer-directives': 0.55.2
|
||||
'@unocss/transformer-variant-group': 0.55.2
|
||||
'@unocss/vite': 0.55.2(vite@4.4.9)
|
||||
'@unocss/astro': 0.56.4(vite@4.4.9)
|
||||
'@unocss/cli': 0.56.4
|
||||
'@unocss/core': 0.56.4
|
||||
'@unocss/extractor-arbitrary-variants': 0.56.4
|
||||
'@unocss/postcss': 0.56.4(postcss@8.4.30)
|
||||
'@unocss/preset-attributify': 0.56.4
|
||||
'@unocss/preset-icons': 0.56.4
|
||||
'@unocss/preset-mini': 0.56.4
|
||||
'@unocss/preset-tagify': 0.56.4
|
||||
'@unocss/preset-typography': 0.56.4
|
||||
'@unocss/preset-uno': 0.56.4
|
||||
'@unocss/preset-web-fonts': 0.56.4
|
||||
'@unocss/preset-wind': 0.56.4
|
||||
'@unocss/reset': 0.56.4
|
||||
'@unocss/transformer-attributify-jsx': 0.56.4
|
||||
'@unocss/transformer-attributify-jsx-babel': 0.56.4
|
||||
'@unocss/transformer-compile-class': 0.56.4
|
||||
'@unocss/transformer-directives': 0.56.4
|
||||
'@unocss/transformer-variant-group': 0.56.4
|
||||
'@unocss/vite': 0.56.4(vite@4.4.9)
|
||||
vite: 4.4.9
|
||||
transitivePeerDependencies:
|
||||
- postcss
|
||||
@@ -1116,6 +1143,10 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/urlpattern-polyfill@8.0.2:
|
||||
resolution: {integrity: sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==}
|
||||
dev: true
|
||||
|
||||
/vite@4.4.9:
|
||||
resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
@@ -1144,11 +1175,11 @@ packages:
|
||||
terser:
|
||||
optional: true
|
||||
dependencies:
|
||||
esbuild: 0.18.19
|
||||
postcss: 8.4.27
|
||||
rollup: 3.27.2
|
||||
esbuild: 0.18.20
|
||||
postcss: 8.4.30
|
||||
rollup: 3.29.3
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.2
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/which@2.0.2:
|
||||
|
||||
@@ -4,6 +4,6 @@ test("authentication", async ({ page }) => {
|
||||
await page.goto("http://auth:8080/");
|
||||
await page.locator('input[name="username"]').fill("foo");
|
||||
await page.locator('input[name="password"]').fill("bar");
|
||||
await page.getByRole("button", { name: "Login" }).click();
|
||||
await expect(page.locator("[data-label=all].label")).toHaveText("Containers");
|
||||
await page.locator('button[type="submit"]').click();
|
||||
await expect(page.getByTestId("containers")).toHaveText("Containers");
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ test("has dashboard text", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("click on settings button", async ({ page }) => {
|
||||
await page.getByRole("link", { name: "Settings" }).click();
|
||||
await page.getByTestId("settings").click();
|
||||
await expect(page.getByRole("heading", { name: "About" })).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -34,6 +34,6 @@ test.describe("es locale", () => {
|
||||
test.use({ locale: "es" });
|
||||
|
||||
test("translated text", async ({ page }) => {
|
||||
await expect(page.locator("[data-label=all].label")).toHaveText("Contenedores");
|
||||
await expect(page.getByTestId("containers")).toHaveText("Contenedores");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ test("has right title", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("select running container", async ({ page }) => {
|
||||
await page.locator("ul.menu-list").getByRole("link", { name: "dozzle" }).click();
|
||||
await page.getByTestId("side-menu").getByRole("link", { name: "dozzle" }).click();
|
||||
await expect(page).toHaveURL(/\/container/);
|
||||
await expect(page.getByText("Accepting connections")).toBeVisible();
|
||||
});
|
||||
|
||||
@@ -5,14 +5,20 @@ test.beforeEach(async ({ page }) => {
|
||||
});
|
||||
|
||||
test.describe("default", () => {
|
||||
test("homepage", async ({ page }) => {
|
||||
await expect(page.locator("aside")).toHaveScreenshot({});
|
||||
test("homepage", async ({ page, isMobile }) => {
|
||||
if (isMobile) {
|
||||
await page.getByTestId("hamburger").click();
|
||||
}
|
||||
await expect(page.getByTestId("navigation")).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("dark", () => {
|
||||
test.use({ colorScheme: "dark" });
|
||||
test("homepage", async ({ page }) => {
|
||||
await expect(page.locator("aside")).toHaveScreenshot({});
|
||||
test("homepage", async ({ page, isMobile }) => {
|
||||
if (isMobile) {
|
||||
await page.getByTestId("hamburger").click();
|
||||
}
|
||||
await expect(page.getByTestId("navigation")).toHaveScreenshot();
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
@@ -5,7 +5,7 @@ require (
|
||||
github.com/alexflint/go-arg v1.4.3
|
||||
github.com/beme/abide v0.0.0-20190723115211-635a09831760
|
||||
github.com/docker/distribution v2.8.2+incompatible // indirect
|
||||
github.com/docker/docker v24.0.5+incompatible
|
||||
github.com/docker/docker v24.0.6+incompatible
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
|
||||
@@ -65,6 +65,8 @@ github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m3
|
||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
||||
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
|
||||
github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
|
||||
@@ -12,6 +12,7 @@ label:
|
||||
total-mem-usage: Gesamte Speicher Auslastung
|
||||
dozzle-version: Dozzle Version
|
||||
all: Alle
|
||||
host: Host
|
||||
password: Passwort
|
||||
username: Benutzername
|
||||
container-name: Container Name
|
||||
@@ -27,13 +28,35 @@ error:
|
||||
invalid-auth: Benutzername und Passwort sind ungültig.
|
||||
logs-skipped: \{total} Einträge übersprungen
|
||||
container-not-found: Container nicht gefunden.
|
||||
events-stream:
|
||||
title: Unerwarteter Fehler
|
||||
message: >-
|
||||
Dozzle UI konnte keine Verbindung zur API herstellen. Bitte überprüfen Sie
|
||||
Ihre Netzwerkeinstellungen. Wenn Sie einen Reverse-Proxy verwenden,
|
||||
stellen Sie bitte sicher, dass er ordnungsgemäß konfiguriert ist.
|
||||
events-timeout:
|
||||
title: Etwas stimmt nicht
|
||||
message: >-
|
||||
Dozzle UI hat beim Verbinden mit der API ein Timeout. Bitte überprüfen Sie
|
||||
die Netzwerkverbindung und versuchen Sie es erneut.
|
||||
alert:
|
||||
redirected:
|
||||
title: Zu neuem Container umgeleitet
|
||||
message: Dozzle hat Sie automatisch zu neuem Container {containerId} umgeleitet.
|
||||
similar-container-found:
|
||||
title: Ähnlicher Container gefunden
|
||||
message: >-
|
||||
Dozzle hat einen ähnlichen Container {containerId} gefunden, der auf dem
|
||||
gleichen Host ausgeführt wird. Möchten Sie zu ihm wechseln?
|
||||
title:
|
||||
page-not-found: Seite nicht gefunden
|
||||
login: Authentifizierung erforderlich
|
||||
dashboard: 1 Container | {count} Container
|
||||
settings: Einstellungen
|
||||
button:
|
||||
logout: Ausloggen
|
||||
login: Anmeldung
|
||||
settings: Einstellungen
|
||||
placeholder:
|
||||
search-containers: Suche Container (⌘ + k, ⌃k)
|
||||
settings:
|
||||
@@ -52,6 +75,6 @@ settings:
|
||||
search: Aktiviere die Suche mit Dozzle mit
|
||||
using-version: Du verwendest Dozzle {version}.
|
||||
update-available: >-
|
||||
Eine neue Version ist verfügbar! Aktualisiere auf <a href="{href}" class="next-release"
|
||||
target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
Eine neue Version ist verfügbar! Aktualisiere auf <a href="{href}" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
show-std: Zeige stdout und stderr Labels
|
||||
automatic-redirect: Automatische Weiterleitung zu neuen Containern mit demselben Namen
|
||||
|
||||
@@ -12,6 +12,7 @@ label:
|
||||
total-mem-usage: Total Mem Usage
|
||||
dozzle-version: Dozzle Version
|
||||
all: All
|
||||
host: Host
|
||||
password: Password
|
||||
username: Username
|
||||
container-name: Container Name
|
||||
@@ -24,17 +25,39 @@ tooltip:
|
||||
search: Search containers (⌘ + k, ⌃k)
|
||||
pin-column: Pin as column
|
||||
error:
|
||||
page-not-found: This page does not exist.
|
||||
invalid-auth: Username and password are not valid.
|
||||
page-not-found: This page does not exist
|
||||
invalid-auth: Username or password are not valid
|
||||
logs-skipped: Skipped {total} entries
|
||||
container-not-found: Container not found.
|
||||
container-not-found: Container not found
|
||||
events-stream:
|
||||
title: Unexpected Error
|
||||
message: >-
|
||||
Dozzle UI wasn't able to connect API. Please check your network settings.
|
||||
If you are using a reverse proxy, please make sure it is configured
|
||||
properly.
|
||||
events-timeout:
|
||||
title: Something is not right
|
||||
message: >-
|
||||
Dozzle UI timeed out while connecting to API. Please check network
|
||||
connection and try again.
|
||||
alert:
|
||||
redirected:
|
||||
title: Redirected to new container
|
||||
message: Dozzle automatically redirected you to new container {containerId}.
|
||||
similar-container-found:
|
||||
title: Similar container found
|
||||
message: >-
|
||||
Dozzle found a similar container {containerId} that is running on the same
|
||||
host. Do you want to switch to it?
|
||||
title:
|
||||
page-not-found: Page not found
|
||||
login: Authentication Required
|
||||
dashboard: 1 container | {count} containers
|
||||
settings: Settings
|
||||
button:
|
||||
logout: Logout
|
||||
login: Login
|
||||
settings: Settings
|
||||
placeholder:
|
||||
search-containers: Search containers (⌘ + k, ⌃k)
|
||||
settings:
|
||||
@@ -53,6 +76,7 @@ settings:
|
||||
search: Enable searching with Dozzle using
|
||||
using-version: You are using Dozzle {version}.
|
||||
update-available: >-
|
||||
New version is available! Update to <a href="{href}" class="next-release"
|
||||
target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
New version is available! Update to <a href="{href}" target="_blank"
|
||||
rel="noreferrer noopener">{nextVersion}</a>.
|
||||
show-std: Show stdout and stderr labels
|
||||
automatic-redirect: Automatically redirect to new containers with the same name
|
||||
|
||||
@@ -12,6 +12,7 @@ label:
|
||||
total-mem-usage: Uso total de la Memoria
|
||||
dozzle-version: Versión de Dozzle
|
||||
all: Todo
|
||||
host: Host
|
||||
password: Contraseña
|
||||
username: Nombre de usuario
|
||||
container-name: Nombre del contenedor
|
||||
@@ -27,13 +28,32 @@ error:
|
||||
invalid-auth: El nombre de usuario y la contraseña no son válidos.
|
||||
logs-skipped: Omitidas {total} entrada/s
|
||||
container-not-found: Contenedor no encontrado.
|
||||
events-stream:
|
||||
title: Error inesperado
|
||||
message: >-
|
||||
Dozzle UI no pudo conectar con la API. Por favor, compruebe la configuración de su red.
|
||||
Si está utilizando un proxy inverso, por favor, asegúrese de que está configurado correctamente.
|
||||
events-timeout:
|
||||
title: Algo no está bien
|
||||
message: >-
|
||||
Dozzle UI se agotó el tiempo de espera al conectarse a la API. Por favor, compruebe la conexión de red y vuelva a intentarlo.
|
||||
alert:
|
||||
redirected:
|
||||
title: Redirigido a nuevo contenedor
|
||||
message: Dozzle le redirigió automáticamente al nuevo contenedor {containerId}.
|
||||
similar-container-found:
|
||||
title: Contenedor similar encontrado
|
||||
message: >-
|
||||
Dozzle encontró un contenedor similar {containerId} que se está ejecutando en el mismo host. ¿Quieres cambiar a él?
|
||||
title:
|
||||
page-not-found: Página no encontrada
|
||||
login: Autenticación requerida
|
||||
dashboard: 1 contenedor | {count} contenedores
|
||||
settings: Configuración
|
||||
button:
|
||||
logout: Cerrar la sesión
|
||||
login: Iniciar sesión
|
||||
settings: Configuración
|
||||
placeholder:
|
||||
search-containers: Buscar contenedores (⌘ + K, CTRL + K)
|
||||
settings:
|
||||
@@ -53,5 +73,6 @@ settings:
|
||||
using-version: Estás usando Dozzle {version}.
|
||||
update-available: >-
|
||||
¡La nueva versión está disponible! Actualizar a la
|
||||
<a href="{href}" class="next-release" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
<a href="{href}" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
show-std: Mostrar etiquetas de salida estándar y salida de error estándar
|
||||
automatic-redirect: Redireccionar automáticamente a nuevos contenedores con el mismo nombre
|
||||
|
||||
@@ -12,6 +12,7 @@ label:
|
||||
total-mem-usage: Total de utilização da memória
|
||||
dozzle-version: Versão Dozzle
|
||||
all: Tudo
|
||||
host: Anfitrião
|
||||
password: Senha
|
||||
username: Nome de usuário
|
||||
container-name: Nome do contentor
|
||||
@@ -27,13 +28,32 @@ error:
|
||||
invalid-auth: O nome de usuário e a senha não são válidos.
|
||||
logs-skipped: Saltado {total} entradas
|
||||
container-not-found: Contentor não encontrado.
|
||||
events-stream:
|
||||
title: Erro inesperado
|
||||
message: >-
|
||||
Dozzle UI não conseguiu ligar à API. Por favor, verifique as suas definições de rede.
|
||||
Se estiver a utilizar um proxy reverso, certifique-se de que está configurado correctamente.
|
||||
events-timeout:
|
||||
title: Algo não está bem
|
||||
message: >-
|
||||
Dozzle UI esgotou o tempo limite ao ligar à API. Por favor, verifique a ligação de rede e tente novamente.
|
||||
alert:
|
||||
redirected:
|
||||
title: Redirecionado para novo contentor
|
||||
message: Dozzle redirecionou-o automaticamente para o novo contentor {containerId}.
|
||||
similar-container-found:
|
||||
title: Contentor semelhante encontrado
|
||||
message: >-
|
||||
Dozzle encontrou um contentor semelhante {containerId} que está a ser executado no mesmo anfitrião. Quer mudar para ele?
|
||||
title:
|
||||
page-not-found: Página não encontrada
|
||||
login: Autenticação Requerida
|
||||
dashboard: 1 contentor | {count} contentores
|
||||
settings: Configurações
|
||||
button:
|
||||
logout: Terminar sessão
|
||||
login: Iniciar sessão
|
||||
settings: Configurações
|
||||
placeholder:
|
||||
search-containers: Pesquisar contentores (⌘ + K, CTRL + K)
|
||||
settings:
|
||||
@@ -53,5 +73,6 @@ settings:
|
||||
using-version: Está a usar o Dozzle {version}.
|
||||
update-available: >-
|
||||
Está disponível uma nova versão! Actualização para
|
||||
<a href="{href}" class="next-release" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
<a href="{href}" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
show-std: Mostrar etiquetas de saída padrão e saída de erro padrão
|
||||
automatic-redirect: Redirecionar automaticamente para novos contentores com o mesmo nome
|
||||
|
||||
@@ -12,6 +12,7 @@ label:
|
||||
total-mem-usage: Использование памяти
|
||||
dozzle-version: Версия Dozzle
|
||||
all: Все
|
||||
host: Хост
|
||||
password: Пароль
|
||||
username: Имя пользователя
|
||||
container-name: Имя контейнера
|
||||
@@ -27,13 +28,32 @@ error:
|
||||
invalid-auth: Имя пользователя или пароль неверны.
|
||||
logs-skipped: Пропущено {total} записей
|
||||
container-not-found: Контейнер не найден.
|
||||
events-stream:
|
||||
title: Неожиданная ошибка
|
||||
message: >-
|
||||
Dozzle UI не смог подключиться к API. Пожалуйста, проверьте настройки сети.
|
||||
Если вы используете обратный прокси, убедитесь, что он настроен правильно.
|
||||
events-timeout:
|
||||
title: Что-то не так
|
||||
message: >-
|
||||
Dozzle UI превысил время ожидания при подключении к API. Пожалуйста, проверьте сетевое подключение и повторите попытку.
|
||||
alert:
|
||||
redirected:
|
||||
title: Перенаправлен на новый контейнер
|
||||
message: Dozzle автоматически перенаправил вас на новый контейнер {containerId}.
|
||||
similar-container-found:
|
||||
title: Найден похожий контейнер
|
||||
message: >-
|
||||
Dozzle нашел похожий контейнер {containerId}, который работает на том же хосте. Хотите переключиться на него?
|
||||
title:
|
||||
page-not-found: Страница не найдена
|
||||
login: Требуется авторизация
|
||||
dashboard: 1 контейнер | {count} контейнеров
|
||||
settings: Настройки
|
||||
button:
|
||||
logout: Выйти
|
||||
login: Войти
|
||||
settings: Настройки
|
||||
placeholder:
|
||||
search-containers: Поиск контейнеров (⌘ + k, ⌃k)
|
||||
settings:
|
||||
@@ -51,6 +71,6 @@ settings:
|
||||
search: Включить поиск с помощью Dozzle, используя
|
||||
using-version: Вы используете версию Dozzle {version}.
|
||||
update-available: >-
|
||||
Доступна новая версия! Обновить до <a href="{href}" class="next-release"
|
||||
target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
Доступна новая версия! Обновить до <a href="{href}" target="_blank" rel="noreferrer noopener">{nextVersion}</a>.
|
||||
show-std: Показывать метки stdout и stderr
|
||||
automatic-redirect: Автоматическое перенаправление на новые контейнеры с тем же именем.
|
||||
|
||||
@@ -12,6 +12,7 @@ label:
|
||||
total-mem-usage: 所有内存使用率
|
||||
dozzle-version: Dozzle版本
|
||||
all: 全部
|
||||
host: 主机
|
||||
password: 密码
|
||||
username: 用户名
|
||||
container-name: 容器名称
|
||||
@@ -27,13 +28,32 @@ error:
|
||||
invalid-auth: 用户名和密码无效。
|
||||
logs-skipped: 跳过 {total} 条记录
|
||||
container-not-found: 容器未找到。
|
||||
events-stream:
|
||||
title: 意外错误
|
||||
message: >-
|
||||
Dozzle UI无法连接到API。请检查您的网络设置。
|
||||
如果您使用反向代理,请确保其正确配置。
|
||||
events-timeout:
|
||||
title: 出了点问题
|
||||
message: >-
|
||||
Dozzle UI在连接到API时超时。请检查网络连接并重试。
|
||||
alert:
|
||||
redirected:
|
||||
title: 重定向到新容器
|
||||
message: Dozzle自动将您重定向到新容器 {containerId}。
|
||||
similar-container-found:
|
||||
title: 找到相似的容器
|
||||
message: >-
|
||||
Dozzle发现了一个相似的容器 {containerId},它在同一主机上运行。您想切换到它吗?d
|
||||
title:
|
||||
page-not-found: 页面未找到
|
||||
login: 需要身份验证
|
||||
dashboard: 1 容器 | {count} 容器
|
||||
settings: 设置
|
||||
button:
|
||||
logout: 退出
|
||||
login: 登录
|
||||
settings: 设置
|
||||
placeholder:
|
||||
search-containers: 搜索容器 (⌘ + k, ⌃k)
|
||||
settings:
|
||||
@@ -51,6 +71,6 @@ settings:
|
||||
search: 使用Dozzle启用搜索
|
||||
using-version: 您正在使用Dozzle {version}。
|
||||
update-available: >-
|
||||
新版本可用!更新到 <a href="{href}" class="next-release"
|
||||
target="_blank" rel="noreferrer noopener">{nextVersion}</a>。
|
||||
新版本可用!更新到 <a href="{href}" rel="noreferrer noopener">{nextVersion}</a>。
|
||||
show-std: 显示stdout和stderr标签
|
||||
automatic-redirect: 自动重定向到同名的新容器
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "dozzle",
|
||||
"version": "4.11.2",
|
||||
"version": "5.0.2",
|
||||
"description": "Realtime log viewer for docker containers. ",
|
||||
"homepage": "https://github.com/amir20/dozzle#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/amir20/dozzle/issues"
|
||||
},
|
||||
"packageManager": "pnpm@8.6.12",
|
||||
"packageManager": "pnpm@8.7.6",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/amir20/dozzle.git"
|
||||
@@ -27,73 +27,81 @@
|
||||
"docs:preview": "vitepress preview docs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@iconify-json/carbon": "^1.1.19",
|
||||
"@iconify-json/carbon": "^1.1.21",
|
||||
"@iconify-json/cil": "^1.1.5",
|
||||
"@iconify-json/mdi": "^1.1.54",
|
||||
"@iconify-json/mdi-light": "^1.1.7",
|
||||
"@iconify-json/octicon": "^1.1.46",
|
||||
"@intlify/unplugin-vue-i18n": "^0.12.3",
|
||||
"@oruga-ui/oruga-next": "^0.6.0",
|
||||
"@oruga-ui/theme-bulma": "^0.2.11",
|
||||
"@vueuse/core": "^10.3.0",
|
||||
"@vueuse/integrations": "^10.3.0",
|
||||
"@vueuse/router": "^10.3.0",
|
||||
"@iconify-json/octicon": "^1.1.49",
|
||||
"@iconify-json/ph": "^1.1.6",
|
||||
"@intlify/unplugin-vue-i18n": "^1.2.0",
|
||||
"@vueuse/components": "^10.4.1",
|
||||
"@vueuse/core": "^10.4.1",
|
||||
"@vueuse/integrations": "^10.4.1",
|
||||
"@vueuse/math": "^10.4.1",
|
||||
"@vueuse/router": "^10.4.1",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"bulma": "^0.9.4",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"d3-array": "^3.2.4",
|
||||
"d3-ease": "^3.0.1",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-shape": "^3.2.0",
|
||||
"d3-transition": "^3.0.1",
|
||||
"daisyui": "^3.8.1",
|
||||
"date-fns": "^2.30.0",
|
||||
"entities": "^4.5.0",
|
||||
"fuse.js": "^6.6.2",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"pinia": "^2.1.6",
|
||||
"postcss": "^8.4.30",
|
||||
"postcss-mixins": "^9.0.4",
|
||||
"splitpanes": "^3.1.5",
|
||||
"tailwindcss": "^3.3.3",
|
||||
"vite": "4.4.9",
|
||||
"vite-plugin-pages": "^0.31.0",
|
||||
"vite-plugin-vue-layouts": "^0.8.0",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "^9.2.2",
|
||||
"vue-router": "^4.2.4"
|
||||
"vue-i18n": "^9.4.1",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/ph": "^1.1.6",
|
||||
"@iconify-json/ic": "^1.1.14",
|
||||
"@pinia/testing": "^0.1.3",
|
||||
"@playwright/test": "^1.37.1",
|
||||
"@types/d3-array": "^3.0.5",
|
||||
"@playwright/test": "^1.38.1",
|
||||
"@types/d3-array": "^3.0.8",
|
||||
"@types/d3-ease": "^3.0.0",
|
||||
"@types/d3-scale": "^4.0.3",
|
||||
"@types/d3-selection": "^3.0.5",
|
||||
"@types/d3-shape": "^3.1.1",
|
||||
"@types/d3-transition": "^3.0.3",
|
||||
"@types/d3-scale": "^4.0.5",
|
||||
"@types/d3-selection": "^3.0.7",
|
||||
"@types/d3-shape": "^3.1.3",
|
||||
"@types/d3-transition": "^3.0.5",
|
||||
"@types/lodash.debounce": "^4.0.7",
|
||||
"@types/node": "^20.5.1",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@vitejs/plugin-vue": "4.3.1",
|
||||
"@types/node": "^20.7.1",
|
||||
"@types/semver": "^7.5.3",
|
||||
"@vitejs/plugin-vue": "4.3.4",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"@vue/test-utils": "^2.4.1",
|
||||
"bumpp": "^9.2.0",
|
||||
"c8": "^8.0.1",
|
||||
"concurrently": "^8.2.0",
|
||||
"concurrently": "^8.2.1",
|
||||
"eventsourcemock": "^2.0.0",
|
||||
"jest-serializer-vue": "^3.1.0",
|
||||
"jsdom": "^22.1.0",
|
||||
"lint-staged": "^14.0.0",
|
||||
"prettier": "^3.0.2",
|
||||
"sass": "^1.66.1",
|
||||
"lint-staged": "^14.0.1",
|
||||
"prettier": "^3.0.3",
|
||||
"prettier-plugin-tailwindcss": "^0.5.4",
|
||||
"simple-git-hooks": "^2.9.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.1.6",
|
||||
"typescript": "^5.2.2",
|
||||
"unplugin-auto-import": "^0.16.6",
|
||||
"unplugin-icons": "^0.16.5",
|
||||
"unplugin-vue-components": "^0.25.1",
|
||||
"unplugin-vue-macros": "^2.4.8",
|
||||
"unplugin-icons": "^0.17.0",
|
||||
"unplugin-vue-components": "^0.25.2",
|
||||
"unplugin-vue-macros": "^2.6.0",
|
||||
"vite": "4.4.9",
|
||||
"vite-plugin-pages": "^0.31.0",
|
||||
"vite-plugin-vue-layouts": "^0.8.0",
|
||||
"vitepress": "1.0.0-rc.4",
|
||||
"vitest": "^0.34.2",
|
||||
"vue-tsc": "^1.8.8"
|
||||
"vitepress": "1.0.0-rc.20",
|
||||
"vitest": "^0.34.5",
|
||||
"vue-tsc": "^1.8.15"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,vue,css,ts,html,md}": [
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
"postcss-mixins": {},
|
||||
"tailwindcss/nesting": {},
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<html class="bg-base text-base-content">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
@@ -26,7 +26,7 @@
|
||||
<body>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="is-hidden"
|
||||
class="hidden"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
import DaisyUI from "daisyui";
|
||||
|
||||
export default {
|
||||
content: ["./assets/**/*.{vue,js,ts}", "./public/index.html"],
|
||||
theme: {
|
||||
extend: {
|
||||
animation: {
|
||||
"bounce-fast": "bounce 0.5s 2 both",
|
||||
},
|
||||
colors: {
|
||||
green: "hsl(177 100% 35%)",
|
||||
red: "hsl(4 90% 58%)",
|
||||
purple: "hsl(291 64% 42%)",
|
||||
blue: "hsl(207 90% 54%)",
|
||||
orange: "hsl(25 95% 53%)",
|
||||
base: "hsl(var(--base-color) / <alpha-value>)",
|
||||
"base-darker": "hsl(var(--base-darker-color) / <alpha-value>)",
|
||||
"base-lighter": "hsl(var(--base-lighter-color) / <alpha-value>)",
|
||||
"base-content": "hsl(var(--base-content-color) / <alpha-value>)",
|
||||
|
||||
primary: "hsl(var(--primary-color) / <alpha-value>)",
|
||||
"primary-focus": "hsl(var(--primary-focus-color) / <alpha-value>)",
|
||||
secondary: "hsl(var(--secondary-color) / <alpha-value>)",
|
||||
"secondary-focus": "hsl(var(--secondary-focus-color) / <alpha-value>)",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [DaisyUI],
|
||||
daisyui: {
|
||||
themes: [],
|
||||
base: false,
|
||||
logs: false,
|
||||
},
|
||||
} satisfies Config;
|
||||
@@ -28,13 +28,7 @@ export default defineConfig(() => ({
|
||||
plugins: [
|
||||
VueMacros({
|
||||
plugins: {
|
||||
vue: Vue({
|
||||
template: {
|
||||
compilerOptions: {
|
||||
whitespace: "preserve",
|
||||
},
|
||||
},
|
||||
}),
|
||||
vue: Vue(),
|
||||
},
|
||||
}),
|
||||
Icons({
|
||||
|
||||