diff --git a/serial.go b/serial.go index 02301822..5d35147f 100644 --- a/serial.go +++ b/serial.go @@ -326,7 +326,9 @@ type SerialSettings struct { EnableEcho bool `json:"enableEcho"` // Whether to echo received characters back to the sender NormalizeMode string `json:"normalizeMode"` // Normalization mode: "carret", "names", "hex" NormalizeLineEnd string `json:"normalizeLineEnd"` // Line ending normalization: "keep", "lf", "cr", "crlf" + TabRender string `json:"tabRender"` // How to render tabs: "spaces", "arrow", "pipe" PreserveANSI bool `json:"preserveANSI"` // Whether to preserve ANSI escape codes + ShowNLTag bool `json:"showNLTag"` // Whether to show a special tag for new lines Buttons []QuickButton `json:"buttons"` // Custom quick buttons } @@ -438,7 +440,7 @@ func getSerialSettings() (SerialSettings, error) { if consoleBroker != nil { norm := NormOptions{ - Mode: normalizeMode, CRLF: crlfMode, TabRender: "", PreserveANSI: serialConfig.PreserveANSI, + Mode: normalizeMode, CRLF: crlfMode, TabRender: serialConfig.TabRender, PreserveANSI: serialConfig.PreserveANSI, ShowNLTag: serialConfig.ShowNLTag, } consoleBroker.SetNormOptions(norm) } @@ -533,7 +535,7 @@ func setSerialSettings(newSettings SerialSettings) error { if consoleBroker != nil { norm := NormOptions{ - Mode: normalizeMode, CRLF: crlfMode, TabRender: "", PreserveANSI: serialConfig.PreserveANSI, + Mode: normalizeMode, CRLF: crlfMode, TabRender: serialConfig.TabRender, PreserveANSI: serialConfig.PreserveANSI, ShowNLTag: serialConfig.ShowNLTag, } consoleBroker.SetNormOptions(norm) } diff --git a/serial_console_helpers.go b/serial_console_helpers.go index 4d6e2bb9..849d080d 100644 --- a/serial_console_helpers.go +++ b/serial_console_helpers.go @@ -46,6 +46,7 @@ type NormOptions struct { CRLF CRLFMode TabRender string // e.g. " " or "" to keep '\t' PreserveANSI bool + ShowNLTag bool // <- NEW: also print a visible tag for CR/LF } func normalize(in []byte, opt NormOptions) string { @@ -88,42 +89,68 @@ func normalize(in []byte, opt NormOptions) string { } } - // CR/LF normalization + // CR/LF normalization (emit real newline(s), optionally tag them visibly) if b == '\r' || b == '\n' { + // detect pair (CRLF or LFCR) + isPair := i+1 < len(in) && + ((b == '\r' && in[i+1] == '\n') || (b == '\n' && in[i+1] == '\r')) + + // optional visible tag of what we *saw* + if opt.ShowNLTag { + if isPair { + if b == '\r' { // saw CRLF + out.WriteString("") + } else { // saw LFCR + out.WriteString("") + } + } else { + if b == '\r' { + out.WriteString("") + } else { + out.WriteString("") + } + } + } + + // now emit the actual newline(s) per the normalization mode switch opt.CRLF { case CRLFAsIs: - out.WriteByte(b) - i++ + if isPair { + out.WriteByte(b) + out.WriteByte(in[i+1]) + i += 2 + } else { + out.WriteByte(b) + i++ + } case CRLF_LF: - if i+1 < len(in) && ((b == '\r' && in[i+1] == '\n') || (b == '\n' && in[i+1] == '\r')) { + if isPair { i += 2 } else { i++ } out.WriteByte('\n') case CRLF_CR: - if i+1 < len(in) && ((b == '\r' && in[i+1] == '\n') || (b == '\n' && in[i+1] == '\r')) { + if isPair { i += 2 } else { i++ } out.WriteByte('\r') case CRLF_CRLF: - if i+1 < len(in) && ((b == '\r' && in[i+1] == '\n') || (b == '\n' && in[i+1] == '\r')) { - out.WriteString("\n") + if isPair { i += 2 } else { - out.WriteString("\n") i++ } + out.WriteString("\r\n") // (fixed to actually write CRLF) case CRLF_LFCR: - if i+1 < len(in) && ((b == '\r' && in[i+1] == '\n') || (b == '\n' && in[i+1] == '\r')) { - out.WriteString("\r") + if isPair { i += 2 } else { - out.WriteString("\r") i++ } + out.WriteString("\n\r") } continue } diff --git a/ui/src/components/extensions/SerialConsole.tsx b/ui/src/components/extensions/SerialConsole.tsx index da641e98..88f53bc9 100644 --- a/ui/src/components/extensions/SerialConsole.tsx +++ b/ui/src/components/extensions/SerialConsole.tsx @@ -33,7 +33,9 @@ interface SerialSettings { enableEcho: boolean; // future use normalizeMode: string; // future use normalizeLineEnd: string; // future use + tabRender: string; // future use preserveANSI: boolean; // future use + showNLTag: boolean; // future use buttons: QuickButton[]; } @@ -56,7 +58,9 @@ export function SerialConsole() { enableEcho: false, normalizeMode: "names", normalizeLineEnd: "keep", + tabRender: "", preserveANSI: true, + showNLTag: true, buttons: [], }); @@ -247,7 +251,7 @@ export function SerialConsole() { {/* Serial settings (collapsible) */} {!buttonConfig.hideSerialSettings && ( <> -
+
+
+ tag", value: "hide" }, + { label: "Show tag", value: "show" }, + ]} + value={buttonConfig.showNLTag ? "show" : "hide"} + onChange={(e) => { + handleSerialSettingsChange("showNLTag", e.target.value === "show") + }} + /> +
+
+ { + handleSerialSettingsChange("tabRender", e.target.value) + }} + /> +
+ Empty for no replacement +
+