feat: Add theme-aware ANSI color palette for log viewer (#4849)

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Amir Raminfar
2026-07-19 18:38:36 -07:00
committed by GitHub
parent e3f4566f6f
commit 95794779d6
6 changed files with 69 additions and 18 deletions
+2
View File
@@ -17,6 +17,7 @@ declare global {
const autoResetRef: typeof import('@vueuse/core').autoResetRef const autoResetRef: typeof import('@vueuse/core').autoResetRef
const automaticRedirect: typeof import('./stores/settings').automaticRedirect const automaticRedirect: typeof import('./stores/settings').automaticRedirect
const collapseNav: typeof import('./stores/settings').collapseNav const collapseNav: typeof import('./stores/settings').collapseNav
const colorize: typeof import('./utils/index').colorize
const compact: typeof import('./stores/settings').compact const compact: typeof import('./stores/settings').compact
const computed: typeof import('vue').computed const computed: typeof import('vue').computed
const computedAsync: typeof import('@vueuse/core').computedAsync const computedAsync: typeof import('@vueuse/core').computedAsync
@@ -461,6 +462,7 @@ declare module 'vue' {
readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']> readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>
readonly automaticRedirect: UnwrapRef<typeof import('./stores/settings')['automaticRedirect']> readonly automaticRedirect: UnwrapRef<typeof import('./stores/settings')['automaticRedirect']>
readonly collapseNav: UnwrapRef<typeof import('./stores/settings')['collapseNav']> readonly collapseNav: UnwrapRef<typeof import('./stores/settings')['collapseNav']>
readonly colorize: UnwrapRef<typeof import('./utils/index')['colorize']>
readonly compact: UnwrapRef<typeof import('./stores/settings')['compact']> readonly compact: UnwrapRef<typeof import('./stores/settings')['compact']>
readonly computed: UnwrapRef<typeof import('vue')['computed']> readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']> readonly computedAsync: UnwrapRef<typeof import('@vueuse/core')['computedAsync']>
@@ -14,13 +14,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { GroupedLogEntry, type Position } from "@/models/LogEntry"; import { GroupedLogEntry, type Position } from "@/models/LogEntry";
import AnsiConvertor from "ansi-to-html";
const ansiConvertor = new AnsiConvertor({
escapeXML: false,
fg: "var(--color-base-content)",
bg: "var(--color-base-100)",
});
const { logEntry } = defineProps<{ const { logEntry } = defineProps<{
logEntry: GroupedLogEntry; logEntry: GroupedLogEntry;
@@ -32,6 +25,4 @@ const getPosition = (index: number): Position => {
if (index === len - 1) return "end"; if (index === len - 1) return "end";
return "middle"; return "middle";
}; };
const colorize = (value: string) => ansiConvertor.toHtml(value);
</script> </script>
@@ -9,17 +9,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { SimpleLogEntry } from "@/models/LogEntry"; import { SimpleLogEntry } from "@/models/LogEntry";
import AnsiConvertor from "ansi-to-html";
const ansiConvertor = new AnsiConvertor({
escapeXML: false,
fg: "var(--color-base-content)",
bg: "var(--color-base-100)",
});
defineProps<{ defineProps<{
logEntry: SimpleLogEntry; logEntry: SimpleLogEntry;
}>(); }>();
const colorize = (value: string) => ansiConvertor.toHtml(value);
</script> </script>
+36
View File
@@ -57,6 +57,24 @@
--color-error: var(--color-red); --color-error: var(--color-red);
--color-error-content: oklch(98% 0.01 30); --color-error-content: oklch(98% 0.01 30);
/* ANSI log palette tuned for legibility on the dark background */
--ansi-black: oklch(45% 0 0);
--ansi-red: oklch(65% 0.2 25);
--ansi-green: oklch(72% 0.16 150);
--ansi-yellow: oklch(80% 0.13 85);
--ansi-blue: oklch(70% 0.14 250);
--ansi-magenta: oklch(68% 0.19 330);
--ansi-cyan: oklch(75% 0.11 200);
--ansi-white: oklch(89% 0 0);
--ansi-bright-black: oklch(60% 0 0);
--ansi-bright-red: oklch(72% 0.2 25);
--ansi-bright-green: oklch(82% 0.17 150);
--ansi-bright-yellow: oklch(88% 0.14 95);
--ansi-bright-blue: oklch(78% 0.13 250);
--ansi-bright-magenta: oklch(75% 0.19 330);
--ansi-bright-cyan: oklch(85% 0.11 200);
--ansi-bright-white: oklch(100% 0 0);
--radius-selector: 1rem; --radius-selector: 1rem;
--radius-field: 0.25rem; --radius-field: 0.25rem;
--radius-box: 0.5rem; --radius-box: 0.5rem;
@@ -97,6 +115,24 @@
--color-error: var(--color-red); --color-error: var(--color-red);
--color-error-content: oklch(98% 0.01 30); --color-error-content: oklch(98% 0.01 30);
/* ANSI log palette tuned for legibility on the light background */
--ansi-black: oklch(25% 0 0);
--ansi-red: oklch(52% 0.2 25);
--ansi-green: oklch(52% 0.15 150);
--ansi-yellow: oklch(60% 0.13 75);
--ansi-blue: oklch(50% 0.17 255);
--ansi-magenta: oklch(50% 0.2 330);
--ansi-cyan: oklch(55% 0.1 210);
--ansi-white: oklch(70% 0 0);
--ansi-bright-black: oklch(45% 0 0);
--ansi-bright-red: oklch(58% 0.21 25);
--ansi-bright-green: oklch(58% 0.16 150);
--ansi-bright-yellow: oklch(62% 0.13 85);
--ansi-bright-blue: oklch(55% 0.16 255);
--ansi-bright-magenta: oklch(56% 0.2 330);
--ansi-bright-cyan: oklch(60% 0.1 210);
--ansi-bright-white: oklch(85% 0 0);
--radius-selector: 1rem; --radius-selector: 1rem;
--radius-field: 0.25rem; --radius-field: 0.25rem;
--radius-box: 0.5rem; --radius-box: 0.5rem;
+30
View File
@@ -0,0 +1,30 @@
import AnsiConvertor from "ansi-to-html";
// Theme-aware ANSI palette. Values are CSS variables defined per DaisyUI theme in main.css,
// so log colors stay legible on both light and dark backgrounds. The `colors` map merges over
// the library's 256-color defaults, so indices 16-255 keep their standard values.
const ansiConvertor = new AnsiConvertor({
escapeXML: false,
fg: "var(--color-base-content)",
bg: "var(--color-base-100)",
colors: {
0: "var(--ansi-black)",
1: "var(--ansi-red)",
2: "var(--ansi-green)",
3: "var(--ansi-yellow)",
4: "var(--ansi-blue)",
5: "var(--ansi-magenta)",
6: "var(--ansi-cyan)",
7: "var(--ansi-white)",
8: "var(--ansi-bright-black)",
9: "var(--ansi-bright-red)",
10: "var(--ansi-bright-green)",
11: "var(--ansi-bright-yellow)",
12: "var(--ansi-bright-blue)",
13: "var(--ansi-bright-magenta)",
14: "var(--ansi-bright-cyan)",
15: "var(--ansi-bright-white)",
},
});
export const colorize = (value: string): string => ansiConvertor.toHtml(value);
+1
View File
@@ -2,3 +2,4 @@ export { formatDuration, toRelativeTime } from "./date";
export { getDeep, isObject, flattenJSON, flattenJSONToMap, arrayEquals } from "./object"; export { getDeep, isObject, flattenJSON, flattenJSONToMap, arrayEquals } from "./object";
export { useExponentialMovingAverage, useSimpleRefHistory } from "./reactive"; export { useExponentialMovingAverage, useSimpleRefHistory } from "./reactive";
export { formatBytes, stripVersion, hashCode } from "./format"; export { formatBytes, stripVersion, hashCode } from "./format";
export { colorize } from "./ansi";