This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new e487300810aa chore: camel-tui - plain mode for source editor to easily
copy/paste
e487300810aa is described below
commit e487300810aa3fbaee49bd7561238ba75e50a280
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Aug 6 17:20:40 2026 +0200
chore: camel-tui - plain mode for source editor to easily copy/paste
chore: camel-tui - update F1 help with plain mode and word wrap keys
Co-Authored-By: Claude Opus 4.6 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../dsl/jbang/core/commands/tui/RoutesTab.java | 2 +
.../dsl/jbang/core/commands/tui/SourceTab.java | 8 +++
.../dsl/jbang/core/commands/tui/SourceViewer.java | 60 +++++++++++++++++++---
3 files changed, 63 insertions(+), 7 deletions(-)
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
index ec2fca008d8a..8ca40eaf89be 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/RoutesTab.java
@@ -2602,6 +2602,8 @@ class RoutesTab extends AbstractTab {
- `←→` — horizontal scroll
- `PgUp/PgDn` — page jump
- `Home/End` — go to top/bottom
+ - `w` — toggle word wrap
+ - `p` — toggle plain mode (hides line numbers and borders for
easy copy/paste)
- `Enter` — select the closest diagram node at cursor line
- `Esc/c` — close source view
""";
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
index a67e4ce45234..0df23241f51b 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceTab.java
@@ -276,6 +276,13 @@ class SourceTab extends AbstractTab {
return;
}
+ if (sourceViewer.isPlainMode() && sourceViewer.isVisible()) {
+ leftArea = null;
+ rightArea = area;
+ renderSourcePanel(frame, area);
+ return;
+ }
+
if (leftPanelWidth < 0) {
leftPanelWidth = Math.max(25, Math.min(35, area.width() * 25 /
100));
}
@@ -349,6 +356,7 @@ class SourceTab extends AbstractTab {
- **h** — highlight text
- **n/N** — next/previous match
- **w** — toggle word wrap
+ - **p** — toggle plain mode (hides line numbers, borders, and
file panel for easy copy/paste)
- **Esc/c** — close source viewer
## Edit Mode (Tab Completion)
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
index d97978584b0c..3ad8bf3d02c1 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SourceViewer.java
@@ -132,6 +132,7 @@ class SourceViewer {
private Style titleStyle;
private Style borderStyle;
private boolean focused = true;
+ private boolean plainMode;
/** Local file path when content was loaded via {@link #loadFile(Path)}
and is writable. */
private Path editableFile;
@@ -266,6 +267,10 @@ class SourceViewer {
return editableFile != null;
}
+ boolean isPlainMode() {
+ return plainMode;
+ }
+
/**
* True when the viewer is consuming typed input (search box or plain-text
edit mode). Used by the monitor to avoid
* treating digit/letter keys as global shortcuts.
@@ -416,6 +421,10 @@ class SourceViewer {
scrollX = 0;
return true;
}
+ if (ke.isChar('p')) {
+ plainMode = !plainMode;
+ return true;
+ }
if (ke.isKey(KeyCode.UP) && ke.hasCtrl()) {
scrollY = Math.max(0, scrollY - 1);
} else if (ke.isKey(KeyCode.DOWN) && ke.hasCtrl()) {
@@ -1751,8 +1760,16 @@ class SourceViewer {
}
Block.Builder blockBuilder = Block.builder()
- .borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(buildTitle());
+ .borderType(BorderType.ROUNDED);
+ if (plainMode) {
+ int lineNum = selectedLine + 1;
+ Title posTitle = Title.from(
+ Line.from(Span.styled(" line:" + lineNum + " ",
Style.EMPTY.dim()))).right();
+ blockBuilder.borders(java.util.EnumSet.of(Borders.TOP,
Borders.BOTTOM))
+ .titleBottom(posTitle);
+ } else {
+ blockBuilder.borders(Borders.ALL).title(buildTitle());
+ }
if (borderStyle != null) {
blockBuilder.borderStyle(borderStyle);
}
@@ -1872,6 +1889,14 @@ class SourceViewer {
Overflow overflow = wordWrap ? Overflow.WRAP_WORD : Overflow.CLIP;
frame.renderWidget(Paragraph.builder().text(Text.from(visible)).overflow(overflow).build(),
hChunks.get(0));
+ if (plainMode && selectedLine >= scrollY && selectedLine < scrollY +
visibleLines) {
+ int relRow = selectedLine - scrollY;
+ int screenY = inner.top() + relRow;
+ Rect lineRect = new Rect(inner.left(), screenY, inner.width(), 1);
+ Style selBg = focused ? Theme.selectionBg() :
Theme.selectionBg().dim();
+ frame.buffer().setStyle(lineRect, selBg);
+ }
+
int totalDocLines = quickDocEnabled ?
quickDocEntries.values().stream().mapToInt(List::size).sum() : 0;
int totalContentLines = lines.size() + totalDocLines;
if (totalContentLines > visibleLines) {
@@ -1899,9 +1924,15 @@ class SourceViewer {
Title posTitle = Title.from(
Line.from(Span.styled(" row:" + row + " col:" + col + " ",
Style.EMPTY.dim()))).right();
Block.Builder blockBuilder = Block.builder()
- .borderType(BorderType.ROUNDED).borders(Borders.ALL)
- .title(Title.from(Line.from(titleSpans)))
- .titleBottom(posTitle);
+ .borderType(BorderType.ROUNDED);
+ if (plainMode) {
+ blockBuilder.borders(java.util.EnumSet.of(Borders.TOP,
Borders.BOTTOM))
+ .titleBottom(posTitle);
+ } else {
+ blockBuilder.borders(Borders.ALL)
+ .title(Title.from(Line.from(titleSpans)))
+ .titleBottom(posTitle);
+ }
if (borderStyle != null) {
blockBuilder.borderStyle(borderStyle);
}
@@ -1913,7 +1944,7 @@ class SourceViewer {
TextArea textArea = TextArea.builder()
.cursorStyle(Style.EMPTY.reversed())
- .showLineNumbers(true)
+ .showLineNumbers(!plainMode)
.lineNumberStyle(Style.EMPTY.dim())
.build();
textArea.renderWithCursor(inner, frame.buffer(), editState, frame);
@@ -2089,6 +2120,7 @@ class SourceViewer {
}
search.renderSearchHints(spans);
TuiHelper.hint(spans, "w", "wrap" + (wordWrap ? " [on]" : " [off]"));
+ TuiHelper.hint(spans, "p", "plain" + (plainMode ? " [on]" : " [off]"));
if (onLineSelected != null) {
TuiHelper.hint(spans, "Enter", "select node");
}
@@ -2552,7 +2584,21 @@ class SourceViewer {
List<Span> spans = new ArrayList<>();
Style selBg = focused ? Theme.selectionBg() :
Theme.selectionBg().dim();
- if (isSelected) {
+ if (plainMode) {
+ // strip line-number prefix (spaces, digits, 2 separator spaces)
but keep code indentation
+ int pos = 0;
+ while (pos < raw.length() && raw.charAt(pos) == ' ') {
+ pos++;
+ }
+ while (pos < raw.length() && Character.isDigit(raw.charAt(pos))) {
+ pos++;
+ }
+ if (pos + 1 < raw.length() && raw.charAt(pos) == ' ' &&
raw.charAt(pos + 1) == ' ') {
+ pos += 2;
+ }
+ String plainCode = raw.substring(pos);
+ spans.addAll(SyntaxHighlighter.highlightLine(plainCode,
language).spans());
+ } else if (isSelected) {
spans.add(Span.styled(">> ", focused ? Theme.label().bold() :
Theme.label().dim()));
if (!prefix.isEmpty()) {
spans.add(Span.styled(prefix, (focused ? Theme.label().bold()
: Theme.label().dim()).patch(selBg)));