diff --git a/QuickLook.Plugin/QuickLook.Plugin.TextViewer/TextViewerPanel.cs b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/TextViewerPanel.cs
index 98606ab27..4657eb1e5 100644
--- a/QuickLook.Plugin/QuickLook.Plugin.TextViewer/TextViewerPanel.cs
+++ b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/TextViewerPanel.cs
@@ -163,9 +163,9 @@ private void Viewer_KeyDown(object sender, KeyEventArgs e)
// RTL: Ctrl + RShift
// LTR: Ctrl + LShift
if (Keyboard.IsKeyDown(Key.RightShift))
- FlowDirection = System.Windows.FlowDirection.RightToLeft;
+ FlowDirection = FlowDirection.RightToLeft;
else if (Keyboard.IsKeyDown(Key.LeftShift))
- FlowDirection = System.Windows.FlowDirection.LeftToRight;
+ FlowDirection = FlowDirection.LeftToRight;
}
else if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
{
diff --git a/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Themes/HighlightingDefinitions/Dark/JSONLHighlightingDefinition.cs b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Themes/HighlightingDefinitions/Dark/JSONLHighlightingDefinition.cs
new file mode 100644
index 000000000..352def1f7
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Themes/HighlightingDefinitions/Dark/JSONLHighlightingDefinition.cs
@@ -0,0 +1,47 @@
+// Copyright © 2017-2026 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using ICSharpCode.AvalonEdit.Highlighting;
+using ICSharpCode.AvalonEdit.Rendering;
+using QuickLook.Plugin.TextViewer.Themes.HighlightingDefinitions.Light;
+using System.Collections.Generic;
+
+namespace QuickLook.Plugin.TextViewer.Themes.HighlightingDefinitions.Dark;
+
+public class JSONLHighlightingDefinition : DarkHighlightingDefinition
+{
+ public override string Name => "JSONL";
+
+ public override string Extension => ".jsonl";
+
+ public override HighlightingRuleSet MainRuleSet => new();
+
+ public override HighlightingColor GetNamedColor(string name) => null;
+
+ public override IEnumerable NamedHighlightingColors => [];
+
+ public override DocumentColorizingTransformer[] LineTransformers { get; } =
+ [
+ new JsonLineHighlighter(
+ keyColor: "#9CDCF0", // field names (VSCode default)
+ stringColor: "#CE9178", // string values (VSCode default)
+ numberColor: "#B5CEA8", // numbers (VSCode default)
+ boolNullColor:"#569CD6", // true/false/null (VSCode default)
+ braceColor: "#DA66BE", // { }
+ bracketColor: "#FFD710") // [ ]
+ ];
+}
diff --git a/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Themes/HighlightingDefinitions/Light/JSONLHighlightingDefinition.cs b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Themes/HighlightingDefinitions/Light/JSONLHighlightingDefinition.cs
new file mode 100644
index 000000000..4568154f8
--- /dev/null
+++ b/QuickLook.Plugin/QuickLook.Plugin.TextViewer/Themes/HighlightingDefinitions/Light/JSONLHighlightingDefinition.cs
@@ -0,0 +1,211 @@
+// Copyright © 2017-2026 QL-Win Contributors
+//
+// This file is part of QuickLook program.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+using ICSharpCode.AvalonEdit.Document;
+using ICSharpCode.AvalonEdit.Highlighting;
+using ICSharpCode.AvalonEdit.Rendering;
+using System.Collections.Generic;
+
+namespace QuickLook.Plugin.TextViewer.Themes.HighlightingDefinitions.Light;
+
+public class JSONLHighlightingDefinition : LightHighlightingDefinition
+{
+ public override string Name => "JSONL";
+
+ public override string Extension => ".jsonl";
+
+ public override HighlightingRuleSet MainRuleSet => new();
+
+ public override HighlightingColor GetNamedColor(string name) => null;
+
+ public override IEnumerable NamedHighlightingColors => [];
+
+ public override DocumentColorizingTransformer[] LineTransformers { get; } =
+ [
+ new JsonLineHighlighter(
+ keyColor: "#0451A5", // field names (VSCode light default)
+ stringColor: "#A31515", // string values (VSCode light default)
+ numberColor: "#098658", // numbers (VSCode light default)
+ boolNullColor:"#0000FF", // true/false/null (VSCode light default)
+ braceColor: "#800000", // { }
+ bracketColor: "#0451A5") // [ ]
+ ];
+}
+
+///
+/// Per-line JSON tokenizer for JSONL (JSON Lines) files.
+/// Each call to ColorizeLine processes one independent JSON object/array.
+///
+public class JsonLineHighlighter(
+ string keyColor,
+ string stringColor,
+ string numberColor,
+ string boolNullColor,
+ string braceColor,
+ string bracketColor) : DocumentColorizingTransformer
+{
+ protected override void ColorizeLine(DocumentLine line)
+ {
+ var text = CurrentContext.Document.GetText(line);
+ if (string.IsNullOrWhiteSpace(text))
+ return;
+
+ ColorizeJsonLine(line, text);
+ }
+
+ private void ColorizeJsonLine(DocumentLine line, string text)
+ {
+ // Stack: true = currently inside an object (next unquoted string is a key),
+ // false = currently inside an array (all strings are values).
+ var contextStack = new Stack();
+ bool expectKey = false;
+
+ int i = 0;
+ while (i < text.Length)
+ {
+ char c = text[i];
+
+ if (char.IsWhiteSpace(c)) { i++; continue; }
+
+ switch (c)
+ {
+ case '{':
+ Colorize(line, i, i + 1, braceColor);
+ contextStack.Push(true);
+ expectKey = true;
+ i++;
+ break;
+
+ case '}':
+ Colorize(line, i, i + 1, braceColor);
+ if (contextStack.Count > 0) contextStack.Pop();
+ expectKey = false;
+ i++;
+ break;
+
+ case '[':
+ Colorize(line, i, i + 1, bracketColor);
+ contextStack.Push(false);
+ expectKey = false;
+ i++;
+ break;
+
+ case ']':
+ Colorize(line, i, i + 1, bracketColor);
+ if (contextStack.Count > 0) contextStack.Pop();
+ expectKey = false;
+ i++;
+ break;
+
+ case ':':
+ // Colon separates key from value; next token is a value.
+ expectKey = false;
+ i++;
+ break;
+
+ case ',':
+ // In object context the next string is a key; in array it is a value.
+ expectKey = contextStack.Count > 0 && contextStack.Peek();
+ i++;
+ break;
+
+ case '"':
+ case '\'':
+ i = TokenizeString(line, text, i, c, expectKey);
+ if (expectKey)
+ expectKey = false; // key consumed, colon follows next
+ break;
+
+ default:
+ if (c == '-' || char.IsDigit(c))
+ {
+ i = TokenizeNumber(line, text, i);
+ expectKey = false;
+ }
+ else if (char.IsLetter(c))
+ {
+ i = TokenizeKeyword(line, text, i);
+ expectKey = false;
+ }
+ else
+ {
+ i++;
+ }
+ break;
+ }
+ }
+ }
+
+ private int TokenizeString(DocumentLine line, string text, int start, char quote, bool isKey)
+ {
+ int i = start + 1; // skip opening quote
+ while (i < text.Length)
+ {
+ if (text[i] == '\\')
+ {
+ i += 2; // skip escape sequence
+ continue;
+ }
+ if (text[i] == quote)
+ {
+ i++; // include closing quote
+ break;
+ }
+ i++;
+ }
+ Colorize(line, start, i, isKey ? keyColor : stringColor);
+ return i;
+ }
+
+ private int TokenizeNumber(DocumentLine line, string text, int start)
+ {
+ int i = start;
+ if (i < text.Length && text[i] == '-') i++; // optional leading minus
+ while (i < text.Length && char.IsDigit(text[i])) i++;
+ if (i < text.Length && text[i] == '.')
+ {
+ i++;
+ while (i < text.Length && char.IsDigit(text[i])) i++;
+ }
+ if (i < text.Length && (text[i] == 'e' || text[i] == 'E'))
+ {
+ i++;
+ if (i < text.Length && (text[i] == '+' || text[i] == '-')) i++;
+ while (i < text.Length && char.IsDigit(text[i])) i++;
+ }
+ Colorize(line, start, i, numberColor);
+ return i;
+ }
+
+ private int TokenizeKeyword(DocumentLine line, string text, int start)
+ {
+ int i = start;
+ while (i < text.Length && char.IsLetter(text[i])) i++;
+ string keyword = text.Substring(start, i - start);
+ if (keyword is "true" or "false" or "null")
+ Colorize(line, start, i, boolNullColor);
+ return i;
+ }
+
+ private void Colorize(DocumentLine line, int from, int to, string hexColor)
+ {
+ ChangeLinePart(line.Offset + from, line.Offset + to, el =>
+ {
+ el.TextRunProperties.SetForegroundBrush(hexColor.ToBrush());
+ });
+ }
+}
diff --git a/SUPPORTED_FORMATS.md b/SUPPORTED_FORMATS.md
index b41df9393..6d103bd96 100644
--- a/SUPPORTED_FORMATS.md
+++ b/SUPPORTED_FORMATS.md
@@ -7,7 +7,7 @@ Update not completed yet...
- `.rtf` (Rich Text Format)
- `.log` (Log file)
- `.ini` (Initialization/config file)
-- `.json` (JavaScript Object Notation)
+- `.json`, `.jsonl` (JavaScript Object Notation / JSON Lines)
- `.xml` (Extensible Markup Language)
- `.yaml`, `.yml` (YAML Ain't Markup Language)
- `.md`, `.markdown`, `.mdx`, `.mmd`, `.mkd`, `.mdwn`, `.mdown`, `.mdc`, `.qmd`, `.rmd`, `.rmarkdown`, `.apib`, `.mdtxt`, `.mdtext` (Markdown and variants)