mirror of
https://github.com/amir20/dozzle.git
synced 2025-12-26 07:13:41 +01:00
feat: Moves Jump-To-Context right (#2584)
This commit is contained in:
4
assets/auto-imports.d.ts
vendored
4
assets/auto-imports.d.ts
vendored
@@ -68,6 +68,7 @@ declare global {
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const lightTheme: typeof import('./stores/settings')['lightTheme']
|
||||
const logSearchContext: typeof import('./composable/logSearchContext')['logSearchContext']
|
||||
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
@@ -237,6 +238,7 @@ declare global {
|
||||
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
|
||||
const useLink: typeof import('vue-router')['useLink']
|
||||
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||
const useLogSearchContext: typeof import('./composable/logSearchContext')['useLogSearchContext']
|
||||
const useLogStream: typeof import('./composable/eventsource')['useLogStream']
|
||||
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
|
||||
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
|
||||
@@ -588,6 +590,7 @@ declare module 'vue' {
|
||||
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
|
||||
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
|
||||
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
|
||||
readonly useLogSearchContext: UnwrapRef<typeof import('./composable/logSearchContext')['useLogSearchContext']>
|
||||
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
|
||||
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
|
||||
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
|
||||
@@ -932,6 +935,7 @@ declare module '@vue/runtime-core' {
|
||||
readonly useLastChanged: UnwrapRef<typeof import('@vueuse/core')['useLastChanged']>
|
||||
readonly useLink: UnwrapRef<typeof import('vue-router')['useLink']>
|
||||
readonly useLocalStorage: UnwrapRef<typeof import('@vueuse/core')['useLocalStorage']>
|
||||
readonly useLogSearchContext: UnwrapRef<typeof import('./composable/logSearchContext')['useLogSearchContext']>
|
||||
readonly useLogStream: UnwrapRef<typeof import('./composable/eventsource')['useLogStream']>
|
||||
readonly useMagicKeys: UnwrapRef<typeof import('@vueuse/core')['useMagicKeys']>
|
||||
readonly useManualRefHistory: UnwrapRef<typeof import('@vueuse/core')['useManualRefHistory']>
|
||||
|
||||
2
assets/components.d.ts
vendored
2
assets/components.d.ts
vendored
@@ -17,6 +17,7 @@ declare module 'vue' {
|
||||
'Carbon:restart': typeof import('~icons/carbon/restart')['default']
|
||||
'Carbon:rowCollapse': typeof import('~icons/carbon/row-collapse')['default']
|
||||
'Carbon:rowExpand': typeof import('~icons/carbon/row-expand')['default']
|
||||
'Carbon:searchLocate': typeof import('~icons/carbon/search-locate')['default']
|
||||
'Carbon:star': typeof import('~icons/carbon/star')['default']
|
||||
'Carbon:starFilled': typeof import('~icons/carbon/star-filled')['default']
|
||||
'Carbon:stopFilledAlt': typeof import('~icons/carbon/stop-filled-alt')['default']
|
||||
@@ -49,6 +50,7 @@ declare module 'vue' {
|
||||
LogDate: typeof import('./components/LogViewer/LogDate.vue')['default']
|
||||
LogEventSource: typeof import('./components/LogViewer/LogEventSource.vue')['default']
|
||||
LogLevel: typeof import('./components/LogViewer/LogLevel.vue')['default']
|
||||
LogMessageActions: typeof import('./components/LogViewer/LogMessageActions.vue')['default']
|
||||
LogStd: typeof import('./components/LogViewer/LogStd.vue')['default']
|
||||
LogViewer: typeof import('./components/LogViewer/LogViewer.vue')['default']
|
||||
LogViewerWithSource: typeof import('./components/LogViewer/LogViewerWithSource.vue')['default']
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
</ul>
|
||||
<field-list :fields="logEntry.unfilteredMessage" :expanded="expanded" :visible-keys="visibleKeys"></field-list>
|
||||
</div>
|
||||
<copy-log-message
|
||||
<log-message-actions
|
||||
class="duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100"
|
||||
:message="JSON.stringify(logEntry.message)"
|
||||
:log-entry="logEntry"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer"
|
||||
v-if="isSupported && message.trim() != ''"
|
||||
:title="t('copy_log.title')"
|
||||
>
|
||||
<span
|
||||
class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700"
|
||||
@click="copyLogMessageToClipBoard()"
|
||||
>
|
||||
<carbon:copy-file />
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const { message } = defineProps<{
|
||||
message: string;
|
||||
}>();
|
||||
|
||||
const { showToast } = useToast();
|
||||
const { copy, isSupported, copied } = useClipboard();
|
||||
const { t } = useI18n();
|
||||
|
||||
async function copyLogMessageToClipBoard() {
|
||||
await copy(message);
|
||||
|
||||
if (copied.value) {
|
||||
showToast(
|
||||
{
|
||||
title: t("toasts.copied.title"),
|
||||
message: t("toasts.copied.message"),
|
||||
type: "info",
|
||||
},
|
||||
{ expire: 2000 },
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
60
assets/components/LogViewer/LogMessageActions.vue
Normal file
60
assets/components/LogViewer/LogMessageActions.vue
Normal file
@@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<div class="flex gap-2">
|
||||
<div
|
||||
class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer"
|
||||
v-if="isSupported && message.trim() != ''"
|
||||
:title="t('log_actions.copy_log')"
|
||||
>
|
||||
<span
|
||||
class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700"
|
||||
@click="copyLogMessageToClipBoard()"
|
||||
>
|
||||
<carbon:copy-file />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer"
|
||||
:title="t('log_actions.jump_to_context')"
|
||||
v-if="isSearching()"
|
||||
>
|
||||
<a
|
||||
class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700"
|
||||
@click="handleJumpLineSelected($event, logEntry)"
|
||||
:href="`#${logEntry.id}`"
|
||||
>
|
||||
<carbon:search-locate />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { LogEntry, JSONObject } from "@/models/LogEntry";
|
||||
|
||||
const { message, logEntry } = defineProps<{
|
||||
message: string;
|
||||
logEntry: LogEntry<string | JSONObject>;
|
||||
}>();
|
||||
|
||||
const { showToast } = useToast();
|
||||
const { copy, isSupported, copied } = useClipboard();
|
||||
const { t } = useI18n();
|
||||
|
||||
const { isSearching } = useSearchFilter();
|
||||
const { handleJumpLineSelected } = useLogSearchContext();
|
||||
|
||||
async function copyLogMessageToClipBoard() {
|
||||
await copy(message);
|
||||
|
||||
if (copied.value) {
|
||||
showToast(
|
||||
{
|
||||
title: t("toasts.copied.title"),
|
||||
message: t("toasts.copied.message"),
|
||||
type: "info",
|
||||
},
|
||||
{ expire: 2000 },
|
||||
);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -7,15 +7,6 @@
|
||||
:class="{ 'border border-secondary': toRaw(item) === toRaw(lastSelectedItem) }"
|
||||
class="group/entry"
|
||||
>
|
||||
<a
|
||||
class="jump-context tooltip tooltip-right tooltip-primary"
|
||||
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" />
|
||||
</li>
|
||||
</ul>
|
||||
@@ -36,19 +27,13 @@ const { container } = useContainerContext();
|
||||
const visibleKeys = persistentVisibleKeys(container);
|
||||
|
||||
const { filteredPayload } = useVisibleFilter(visibleKeys);
|
||||
const { filteredMessages, resetSearch, isSearching } = useSearchFilter();
|
||||
const { filteredMessages } = useSearchFilter();
|
||||
|
||||
const { messages } = toRefs(props);
|
||||
const visible = filteredPayload(messages);
|
||||
const filtered = filteredMessages(visible);
|
||||
|
||||
let lastSelectedItem: LogEntry<string | JSONObject> | undefined = $ref(undefined);
|
||||
|
||||
function handleJumpLineSelected(e: Event, item: LogEntry<string | JSONObject>) {
|
||||
lastSelectedItem = item;
|
||||
resetSearch();
|
||||
}
|
||||
|
||||
const { lastSelectedItem } = useLogSearchContext();
|
||||
const routeHash = useRouteHash();
|
||||
watch(
|
||||
routeHash,
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
class="whitespace-pre-wrap [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"
|
||||
v-html="colorize(logEntry.message)"
|
||||
></div>
|
||||
<copy-log-message
|
||||
<log-message-actions
|
||||
class="duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100"
|
||||
:message="logEntry.message"
|
||||
:log-entry="logEntry"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
exports[`<LogEventSource /> > render html correctly > should render dates with 12 hour style 1`] = `
|
||||
"<ul data-v-2e92daca="" class="events group py-4 medium">
|
||||
<li data-v-2e92daca="" data-key="1" class="group/entry">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
|
||||
<!--v-if-->
|
||||
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
|
||||
@@ -11,7 +10,10 @@ exports[`<LogEventSource /> > render html correctly > should render dates with 1
|
||||
</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 [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><test>foo bar</test></div>
|
||||
<!--v-if-->
|
||||
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
@@ -20,7 +22,6 @@ exports[`<LogEventSource /> > render html correctly > should render dates with 1
|
||||
exports[`<LogEventSource /> > render html correctly > should render dates with 24 hour style 1`] = `
|
||||
"<ul data-v-2e92daca="" class="events group py-4 medium">
|
||||
<li data-v-2e92daca="" data-key="1" class="group/entry">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
|
||||
<!--v-if-->
|
||||
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
|
||||
@@ -28,7 +29,10 @@ exports[`<LogEventSource /> > render html correctly > should render dates with 2
|
||||
</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 [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><test>foo bar</test></div>
|
||||
<!--v-if-->
|
||||
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
@@ -37,7 +41,6 @@ exports[`<LogEventSource /> > render html correctly > should render dates with 2
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages 1`] = `
|
||||
"<ul data-v-2e92daca="" class="events group py-4 medium">
|
||||
<li data-v-2e92daca="" data-key="1" class="group/entry">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
|
||||
<!--v-if-->
|
||||
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
|
||||
@@ -45,7 +48,10 @@ exports[`<LogEventSource /> > render html correctly > should render messages 1`]
|
||||
</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 [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap">This is a message.</div>
|
||||
<!--v-if-->
|
||||
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
@@ -54,7 +60,6 @@ exports[`<LogEventSource /> > render html correctly > should render messages 1`]
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages with color 1`] = `
|
||||
"<ul data-v-2e92daca="" class="events group py-4 medium">
|
||||
<li data-v-2e92daca="" data-key="1" class="group/entry">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
|
||||
<!--v-if-->
|
||||
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
|
||||
@@ -62,7 +67,10 @@ exports[`<LogEventSource /> > render html correctly > should render messages wit
|
||||
</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 [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><span style="color:#000">black<span style="color:#AAA">white</span></span></div>
|
||||
<!--v-if-->
|
||||
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
@@ -70,9 +78,7 @@ exports[`<LogEventSource /> > render html correctly > should render messages wit
|
||||
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages with filter 1`] = `
|
||||
"<ul data-v-2e92daca="" class="events group py-4 medium">
|
||||
<li data-v-2e92daca="" data-key="2" class="group/entry"><a data-v-2e92daca="" class="jump-context tooltip tooltip-right tooltip-primary" 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>
|
||||
<li data-v-2e92daca="" data-key="2" class="group/entry">
|
||||
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
|
||||
<!--v-if-->
|
||||
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
|
||||
@@ -80,7 +86,12 @@ exports[`<LogEventSource /> > render html correctly > should render messages wit
|
||||
</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 [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><mark>test</mark> bar</div>
|
||||
<!--v-if-->
|
||||
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
|
||||
<!--v-if-->
|
||||
<div class="flex min-w-[0.98rem] items-start justify-end align-bottom hover:cursor-pointer" title="log_actions.jump_to_context"><a class="rounded bg-slate-800/60 px-1.5 py-1 text-primary hover:bg-slate-700" href="#2"><svg viewBox="0 0 32 32" width="1.2em" height="1.2em">
|
||||
<path fill="currentColor" d="m30 28.586l-4.688-4.688a8.028 8.028 0 1 0-1.415 1.414L28.586 30zM19 25a6 6 0 1 1 6-6a6.007 6.007 0 0 1-6 6zM2 12h8v2H2zM2 2h16v2H2zm0 5h16v2H2z"></path>
|
||||
</svg></a></div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
@@ -89,7 +100,6 @@ exports[`<LogEventSource /> > render html correctly > should render messages wit
|
||||
exports[`<LogEventSource /> > render html correctly > should render messages with html entities 1`] = `
|
||||
"<ul data-v-2e92daca="" class="events group py-4 medium">
|
||||
<li data-v-2e92daca="" data-key="1" class="group/entry">
|
||||
<!--v-if-->
|
||||
<div data-v-2e92daca="" class="relative flex w-full items-start gap-x-2" visible-keys="">
|
||||
<!--v-if-->
|
||||
<div data-v-961504e7="" class="inline-flex items-center justify-center rounded bg-base-lighter px-2 py-[0.2em] text-sm" size="small">
|
||||
@@ -97,7 +107,10 @@ exports[`<LogEventSource /> > render html correctly > should render messages wit
|
||||
</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 [word-break:break-word] group-[.disable-wrap]:whitespace-nowrap"><test>foo bar</test></div>
|
||||
<!--v-if-->
|
||||
<div class="flex gap-2 duration-250 absolute -right-1 opacity-0 transition-opacity delay-150 group-hover/entry:opacity-100">
|
||||
<!--v-if-->
|
||||
<!--v-if-->
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>"
|
||||
|
||||
16
assets/composable/logSearchContext.ts
Normal file
16
assets/composable/logSearchContext.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { JSONObject, LogEntry } from "@/models/LogEntry";
|
||||
|
||||
const lastSelectedItem = ref<LogEntry<string | JSONObject> | undefined>(undefined);
|
||||
export const useLogSearchContext = () => {
|
||||
const { resetSearch } = useSearchFilter();
|
||||
|
||||
function handleJumpLineSelected(e: Event, item: LogEntry<string | JSONObject>) {
|
||||
lastSelectedItem.value = item;
|
||||
resetSearch();
|
||||
}
|
||||
|
||||
return {
|
||||
lastSelectedItem,
|
||||
handleJumpLineSelected,
|
||||
};
|
||||
};
|
||||
@@ -87,8 +87,8 @@ releases:
|
||||
two_parts: "{first} und {second}"
|
||||
latest: Latest
|
||||
no_releases: Du hast die aktuellste Version
|
||||
copy_log:
|
||||
title: Log kopieren
|
||||
log_actions:
|
||||
copy_log: Log kopieren
|
||||
toasts:
|
||||
copied:
|
||||
title: Kopiert
|
||||
|
||||
@@ -91,10 +91,10 @@ releases:
|
||||
two_parts: "{first} with {second}"
|
||||
latest: Latest
|
||||
no_releases: You have the latest version
|
||||
copy_log:
|
||||
title: Copy log
|
||||
log_actions:
|
||||
copy_log: Copy log
|
||||
jump_to_context: Jump to context
|
||||
toasts:
|
||||
copied:
|
||||
title: Copied
|
||||
message: Log copied to clipboard
|
||||
|
||||
|
||||
@@ -84,8 +84,8 @@ releases:
|
||||
two_parts: "{features} y {bugFixes}"
|
||||
latest: Último
|
||||
no_releases: No hay lanzamientos
|
||||
copy_log:
|
||||
title: Copiar registro
|
||||
log_actions:
|
||||
copy_log: Copiar registro
|
||||
toasts:
|
||||
copied:
|
||||
title: Copiado
|
||||
|
||||
@@ -74,5 +74,5 @@ settings:
|
||||
新版本可用!更新到 <a href="{href}" rel="noreferrer noopener">{nextVersion}</a>。
|
||||
show-std: 显示stdout和stderr标签
|
||||
automatic-redirect: 自动重定向到同名的新容器
|
||||
copy_log:
|
||||
title: 复制日志
|
||||
log_actions:
|
||||
copy_log: 复制日志
|
||||
|
||||
Reference in New Issue
Block a user