mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-02-28 03:42:50 +01:00
ansiline implementation, new ansi tailwind colors (#1996)
This commit is contained in:
parent
119b1ecb92
commit
ce9775c00c
154
frontend/app/element/ansiline.tsx
Normal file
154
frontend/app/element/ansiline.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
export const ANSI_TAILWIND_MAP = {
|
||||
// Reset and modifiers
|
||||
0: "reset", // special: clear state
|
||||
1: "font-bold",
|
||||
2: "opacity-75",
|
||||
3: "italic",
|
||||
4: "underline",
|
||||
8: "invisible",
|
||||
9: "line-through",
|
||||
|
||||
// Foreground standard colors
|
||||
30: "text-ansi-black",
|
||||
31: "text-ansi-red",
|
||||
32: "text-ansi-green",
|
||||
33: "text-ansi-yellow",
|
||||
34: "text-ansi-blue",
|
||||
35: "text-ansi-magenta",
|
||||
36: "text-ansi-cyan",
|
||||
37: "text-ansi-white",
|
||||
|
||||
// Foreground bright colors
|
||||
90: "text-ansi-brightblack",
|
||||
91: "text-ansi-brightred",
|
||||
92: "text-ansi-brightgreen",
|
||||
93: "text-ansi-brightyellow",
|
||||
94: "text-ansi-brightblue",
|
||||
95: "text-ansi-brightmagenta",
|
||||
96: "text-ansi-brightcyan",
|
||||
97: "text-ansi-brightwhite",
|
||||
|
||||
// Background standard colors
|
||||
40: "bg-ansi-black",
|
||||
41: "bg-ansi-red",
|
||||
42: "bg-ansi-green",
|
||||
43: "bg-ansi-yellow",
|
||||
44: "bg-ansi-blue",
|
||||
45: "bg-ansi-magenta",
|
||||
46: "bg-ansi-cyan",
|
||||
47: "bg-ansi-white",
|
||||
|
||||
// Background bright colors
|
||||
100: "bg-ansi-brightblack",
|
||||
101: "bg-ansi-brightred",
|
||||
102: "bg-ansi-brightgreen",
|
||||
103: "bg-ansi-brightyellow",
|
||||
104: "bg-ansi-brightblue",
|
||||
105: "bg-ansi-brightmagenta",
|
||||
106: "bg-ansi-brightcyan",
|
||||
107: "bg-ansi-brightwhite",
|
||||
};
|
||||
|
||||
type InternalStateType = {
|
||||
modifiers: Set<string>;
|
||||
textColor: string | null;
|
||||
bgColor: string | null;
|
||||
reverse: boolean;
|
||||
};
|
||||
|
||||
type SegmentType = {
|
||||
text: string;
|
||||
classes: string;
|
||||
};
|
||||
|
||||
const makeInitialState: () => InternalStateType = () => ({
|
||||
modifiers: new Set<string>(),
|
||||
textColor: null,
|
||||
bgColor: null,
|
||||
reverse: false,
|
||||
});
|
||||
|
||||
const updateStateWithCodes = (state, codes) => {
|
||||
codes.forEach((code) => {
|
||||
if (code === 0) {
|
||||
// Reset state
|
||||
state.modifiers.clear();
|
||||
state.textColor = null;
|
||||
state.bgColor = null;
|
||||
state.reverse = false;
|
||||
return;
|
||||
}
|
||||
// Instead of swapping immediately, we set a flag
|
||||
if (code === 7) {
|
||||
state.reverse = true;
|
||||
return;
|
||||
}
|
||||
const tailwindClass = ANSI_TAILWIND_MAP[code];
|
||||
if (tailwindClass && tailwindClass !== "reset") {
|
||||
if (tailwindClass.startsWith("text-")) {
|
||||
state.textColor = tailwindClass;
|
||||
} else if (tailwindClass.startsWith("bg-")) {
|
||||
state.bgColor = tailwindClass;
|
||||
} else {
|
||||
state.modifiers.add(tailwindClass);
|
||||
}
|
||||
}
|
||||
});
|
||||
return state;
|
||||
};
|
||||
|
||||
const stateToClasses = (state: InternalStateType) => {
|
||||
const classes = [];
|
||||
classes.push(...Array.from(state.modifiers));
|
||||
|
||||
// Apply reverse: swap text and background colors if flag is set.
|
||||
let textColor = state.textColor;
|
||||
let bgColor = state.bgColor;
|
||||
if (state.reverse) {
|
||||
[textColor, bgColor] = [bgColor, textColor];
|
||||
}
|
||||
if (textColor) classes.push(textColor);
|
||||
if (bgColor) classes.push(bgColor);
|
||||
|
||||
return classes.join(" ");
|
||||
};
|
||||
|
||||
const ansiRegex = /\x1b\[([0-9;]+)m/g;
|
||||
|
||||
const AnsiLine = ({ line }) => {
|
||||
const segments: SegmentType[] = [];
|
||||
let lastIndex = 0;
|
||||
let currentState = makeInitialState();
|
||||
|
||||
let match: RegExpExecArray;
|
||||
while ((match = ansiRegex.exec(line)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
segments.push({
|
||||
text: line.substring(lastIndex, match.index),
|
||||
classes: stateToClasses(currentState),
|
||||
});
|
||||
}
|
||||
const codes = match[1].split(";").map(Number);
|
||||
updateStateWithCodes(currentState, codes);
|
||||
lastIndex = ansiRegex.lastIndex;
|
||||
}
|
||||
|
||||
if (lastIndex < line.length) {
|
||||
segments.push({
|
||||
text: line.substring(lastIndex),
|
||||
classes: stateToClasses(currentState),
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{segments.map((seg, idx) => (
|
||||
<span key={idx} className={seg.classes}>
|
||||
{seg.text}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnsiLine;
|
@ -41,4 +41,22 @@
|
||||
--text-default: 14px;
|
||||
|
||||
--radius: 8px;
|
||||
|
||||
/* ANSI Colors (Default Dark Palette) */
|
||||
--ansi-black: #757575;
|
||||
--ansi-red: #cc685c;
|
||||
--ansi-green: #76c266;
|
||||
--ansi-yellow: #cbca9b;
|
||||
--ansi-blue: #85aacb;
|
||||
--ansi-magenta: #cc72ca;
|
||||
--ansi-cyan: #74a7cb;
|
||||
--ansi-white: #c1c1c1;
|
||||
--ansi-brightblack: #727272;
|
||||
--ansi-brightred: #cc9d97;
|
||||
--ansi-brightgreen: #a3dd97;
|
||||
--ansi-brightyellow: #cbcaaa;
|
||||
--ansi-brightblue: #9ab6cb;
|
||||
--ansi-brightmagenta: #cc8ecb;
|
||||
--ansi-brightcyan: #b7b8cb;
|
||||
--ansi-brightwhite: #f0f0f0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user