From cc1a1379545ba44a54df2be0ab0e941151b97938 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:49:03 -0300 Subject: [PATCH 1/5] feat: add support for Vaadin 25 Close #104 Close #105 --- pom.xml | 6 +++ .../addons/xterm/ClientTerminalAddon.java | 40 +++++++++++++++++-- .../vaadin/addons/xterm/ITerminalConsole.java | 5 ++- .../vaadin/addons/xterm/XTermBase.java | 3 ++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 1940059..9aa0069 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,12 @@ true + + com.flowingcode.vaadin + json-migration-helper + 0.9.2 + + org.slf4j slf4j-simple diff --git a/src/main/java/com/flowingcode/vaadin/addons/xterm/ClientTerminalAddon.java b/src/main/java/com/flowingcode/vaadin/addons/xterm/ClientTerminalAddon.java index 51b1f37..2926a1c 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/xterm/ClientTerminalAddon.java +++ b/src/main/java/com/flowingcode/vaadin/addons/xterm/ClientTerminalAddon.java @@ -2,7 +2,7 @@ * #%L * XTerm Console Addon * %% - * Copyright (C) 2020 - 2025 Flowing Code + * Copyright (C) 2020 - 2026 Flowing Code * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,11 +19,17 @@ */ package com.flowingcode.vaadin.addons.xterm; +import com.flowingcode.vaadin.jsonmigration.JsonMigration; import com.vaadin.flow.dom.Element; -import com.vaadin.flow.internal.JsonCodec; +import com.vaadin.flow.server.Version; import elemental.json.Json; import elemental.json.JsonArray; +import elemental.json.JsonValue; import java.io.Serializable; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import lombok.SneakyThrows; /** * Represents an abstract base class for server-side terminal add-ons that have a corresponding @@ -96,11 +102,39 @@ protected final void executeJs(String expression, Serializable... parameters) { JsonArray args = Json.createArray(); for (int i = 0; i < parameters.length; i++) { - args.set(i, JsonCodec.encodeWithTypeInfo(parameters[i])); + args.set(i, encodeWithTypeInfo(parameters[i])); } expression = expression.replaceAll("\\$(\\d+)", "\\$1[$1]"); xterm.executeJs("(function(){" + expression + "}).apply(this.addons[$0],$1);", name, args); } + private static final MethodHandle encodeWithTypeInfo = lookup_encodeWithTypeInfo(); + + @SneakyThrows + private static MethodHandle lookup_encodeWithTypeInfo() { + MethodHandle handle; + if (Version.getMajorVersion() > 24) { + Class result = Class.forName("tools.jackson.databind.JsonNode"); + Class codec = Class.forName("com.vaadin.flow.internal.JacksonCodec"); + MethodType type = MethodType.methodType(result, Object.class); + handle = MethodHandles.lookup().findStatic(codec, "encodeWithTypeInfo", type); + } else { + Class codec = Class.forName("com.vaadin.flow.internal.JsonCodec"); + MethodType type = MethodType.methodType(JsonValue.class, Object.class); + handle = MethodHandles.lookup().findStatic(codec, "encodeWithTypeInfo", type); + } + return handle.asType(MethodType.methodType(Object.class, Object.class)); + } + + private static JsonValue encodeWithTypeInfo(Object obj) { + try { + return JsonMigration.convertToJsonValue(encodeWithTypeInfo.invokeExact(obj)); + } catch (RuntimeException | Error e) { + throw e; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + } diff --git a/src/main/java/com/flowingcode/vaadin/addons/xterm/ITerminalConsole.java b/src/main/java/com/flowingcode/vaadin/addons/xterm/ITerminalConsole.java index e545aaf..de34c22 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/xterm/ITerminalConsole.java +++ b/src/main/java/com/flowingcode/vaadin/addons/xterm/ITerminalConsole.java @@ -2,7 +2,7 @@ * #%L * XTerm Console Addon * %% - * Copyright (C) 2020 - 2023 Flowing Code + * Copyright (C) 2020 - 2026 Flowing Code * %% * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ */ package com.flowingcode.vaadin.addons.xterm; +import com.flowingcode.vaadin.jsonmigration.JsonMigration; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.ComponentEvent; import com.vaadin.flow.component.ComponentEventListener; @@ -30,12 +31,14 @@ import elemental.json.JsonValue; import java.util.concurrent.CompletableFuture; import lombok.Getter; +import lombok.experimental.ExtensionMethod; /** * Add console support to XTerm. This provides handling of cursor, home/end, insert, delete and * backspace keys, as well as a {@link #addLineListener(ComponentEventListener) line event}. */ @SuppressWarnings("serial") +@ExtensionMethod(value = JsonMigration.class, suppressBaseMethods = true) public interface ITerminalConsole extends HasElement { // TODO set cursor properties (blink, style, width) separately for insert and overwrite modes diff --git a/src/main/java/com/flowingcode/vaadin/addons/xterm/XTermBase.java b/src/main/java/com/flowingcode/vaadin/addons/xterm/XTermBase.java index de8ec8e..66fc659 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/xterm/XTermBase.java +++ b/src/main/java/com/flowingcode/vaadin/addons/xterm/XTermBase.java @@ -19,6 +19,7 @@ */ package com.flowingcode.vaadin.addons.xterm; +import com.flowingcode.vaadin.jsonmigration.JsonMigration; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.HasEnabled; import com.vaadin.flow.component.HasSize; @@ -55,12 +56,14 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; import lombok.experimental.Delegate; +import lombok.experimental.ExtensionMethod; /** Server-side component for the XTerm component. */ @SuppressWarnings("serial") @NpmPackage(value = "xterm", version = "5.1.0") @JsModule("./fc-xterm/xterm-element.ts") @CssImport("xterm/css/xterm.css") +@ExtensionMethod(value = JsonMigration.class, suppressBaseMethods = true) public abstract class XTermBase extends Component implements ITerminal, ITerminalOptions, HasSize, HasEnabled { From f539a5104a672de6a8c7e8d4cdb6706b8b974f59 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:49:49 -0300 Subject: [PATCH 2/5] build: set version to 3.3.0-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9aa0069..ac5543f 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ com.flowingcode.addons xterm-console - 3.2.1-SNAPSHOT + 3.3.0-SNAPSHOT XTerm Console Addon Integration of xterm.js for Vaadin Flow From f1ea434152781a0e836df6e15ca67137f7ff144f Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:50:47 -0300 Subject: [PATCH 3/5] build: upgrade lombok to 1.18.42 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index ac5543f..97df143 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ org.projectlombok lombok provided - 1.18.34 + 1.18.42 From bd175fd89006004a75aa874851975daf1cf97aa7 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:51:39 -0300 Subject: [PATCH 4/5] test: add integration test for ClientTerminalAddon --- pom.xml | 6 ++ .../xterm/integration/IntegrationView.java | 68 +++++++++++++++++++ .../integration/IntegrationViewCallables.java | 26 +++++++ .../SampleClientTerminalAddon.java | 56 +++++++++++++++ .../SampleClientTerminalAddonIT.java | 47 +++++++++++++ 5 files changed, 203 insertions(+) create mode 100644 src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java create mode 100644 src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationViewCallables.java create mode 100644 src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddon.java create mode 100644 src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java diff --git a/pom.xml b/pom.xml index 97df143..e5f1a4f 100644 --- a/pom.xml +++ b/pom.xml @@ -101,6 +101,12 @@ com.vaadin vaadin-testbench test + + + com.flowingcode.vaadin.test + testbench-rpc + 1.4.0 + test org.hamcrest diff --git a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java new file mode 100644 index 0000000..59f372c --- /dev/null +++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java @@ -0,0 +1,68 @@ +/*- + * #%L + * XTerm Console Addon + * %% + * Copyright (C) 2020 - 2026 Flowing Code + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.flowingcode.vaadin.addons.xterm.integration; + +import com.flowingcode.vaadin.addons.xterm.ITerminalOptions.CursorStyle; +import com.flowingcode.vaadin.addons.xterm.XTerm; +import com.vaadin.flow.component.ClientCallable; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; + +@SuppressWarnings("serial") +@Route(value = IntegrationView.ROUTE) +public class IntegrationView extends VerticalLayout implements IntegrationViewCallables { + + public static final String ROUTE = "xterm/it"; + + private XTerm xterm; + + private SampleClientTerminalAddon sampleClientTerminalAddon; + + public IntegrationView() { + setSizeFull(); + setPadding(false); + getElement().getStyle().set("background", "black"); + + xterm = new XTerm(); + xterm.setPrompt("[user@xterm ~]$ "); + + xterm.writeln("xterm add-on by Flowing Code S.A.\n\n"); + xterm.writePrompt(); + + xterm.setCursorBlink(true); + xterm.setCursorStyle(CursorStyle.UNDERLINE); + + xterm.setSizeFull(); + sampleClientTerminalAddon = new SampleClientTerminalAddon(xterm); + + xterm.addLineListener(ev -> xterm.writePrompt()); + + xterm.focus(); + xterm.fit(); + add(xterm); + } + + @Override + @ClientCallable + public void setSampleClientTerminalAddonValue(String value) { + sampleClientTerminalAddon.setValue(value); + } + +} diff --git a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationViewCallables.java b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationViewCallables.java new file mode 100644 index 0000000..cc4782a --- /dev/null +++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationViewCallables.java @@ -0,0 +1,26 @@ +/*- + * #%L + * XTerm Console Addon + * %% + * Copyright (C) 2020 - 2026 Flowing Code + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.flowingcode.vaadin.addons.xterm.integration; + +public interface IntegrationViewCallables { + + void setSampleClientTerminalAddonValue(String value); + +} diff --git a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddon.java b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddon.java new file mode 100644 index 0000000..ab898c6 --- /dev/null +++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddon.java @@ -0,0 +1,56 @@ +/*- + * #%L + * XTerm Console Addon + * %% + * Copyright (C) 2020 - 2026 Flowing Code + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.flowingcode.vaadin.addons.xterm.integration; + +import com.flowingcode.vaadin.addons.xterm.ClientTerminalAddon; +import com.flowingcode.vaadin.addons.xterm.XTermBase; + +@SuppressWarnings("serial") +public class SampleClientTerminalAddon extends ClientTerminalAddon { + + public final static String NAME = "sample-terminal-addon"; + + public SampleClientTerminalAddon(XTermBase xterm) { + super(xterm); + + // see https://github.com/FlowingCode/XTermConsoleAddon/issues/98 + xterm.getElement().executeJs(""" + window.Vaadin.Flow.fcXtermConnector = window.Vaadin.Flow.fcXtermConnector || {}; + window.Vaadin.Flow.fcXtermConnector.load_sample = (name, node) => { + const addon = { + activate: () => {}, + dispose: () => {} + }; + node.terminal.loadAddon(addon); + node.addons[name]=addon; + }; + Vaadin.Flow.fcXtermConnector.load_sample($0, this);""", NAME); + } + + public void setValue(String value) { + executeJs("this.value=$0;", value); + } + + @Override + public String getName() { + return NAME; + } + +} \ No newline at end of file diff --git a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java new file mode 100644 index 0000000..df1fd63 --- /dev/null +++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java @@ -0,0 +1,47 @@ +/*- + * #%L + * XTerm Console Addon + * %% + * Copyright (C) 2020 - 2026 Flowing Code + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package com.flowingcode.vaadin.addons.xterm.integration; + +import static org.junit.Assert.assertEquals; +import com.flowingcode.vaadin.testbench.rpc.HasRpcSupport; +import org.junit.Test; + +public class SampleClientTerminalAddonIT extends AbstractViewTest implements HasRpcSupport { + + private static final String BAR = "BAR"; + IntegrationViewCallables $server = createCallableProxy(IntegrationViewCallables.class); + + public SampleClientTerminalAddonIT() { + super(IntegrationView.ROUTE); + } + + @Test + public void testSelectionFeature1() throws InterruptedException { + XTermElement term = $(XTermElement.class).first(); + $server.setSampleClientTerminalAddonValue(BAR); + assertEquals(BAR, getSampleClientTerminalValue(term)); + } + + private static String getSampleClientTerminalValue(XTermElement term) { + return (String) term.executeScript("return this.addons[arguments[0]].value;", + SampleClientTerminalAddon.NAME); + } + +} \ No newline at end of file From eab1127103f3992b592af90f90a6a72584983987 Mon Sep 17 00:00:00 2001 From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:31:49 -0300 Subject: [PATCH 5/5] test: use IntegrationView as the global base for ITs --- .../addons/xterm/integration/AbstractViewTest.java | 2 +- .../addons/xterm/integration/IntegrationView.java | 10 +++++++++- .../xterm/integration/SampleClientTerminalAddonIT.java | 4 ---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/AbstractViewTest.java b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/AbstractViewTest.java index f3b7ad9..22bd67d 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/AbstractViewTest.java +++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/AbstractViewTest.java @@ -49,7 +49,7 @@ public abstract class AbstractViewTest extends ParallelTest { @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(this, true); public AbstractViewTest() { - this(""); + this(IntegrationView.ROUTE); } protected AbstractViewTest(String route) { diff --git a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java index 59f372c..af669a8 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java +++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java @@ -19,7 +19,9 @@ */ package com.flowingcode.vaadin.addons.xterm.integration; +import com.flowingcode.vaadin.addons.xterm.ITerminalClipboard.UseSystemClipboard; import com.flowingcode.vaadin.addons.xterm.ITerminalOptions.CursorStyle; +import com.flowingcode.vaadin.addons.xterm.TerminalHistory; import com.flowingcode.vaadin.addons.xterm.XTerm; import com.vaadin.flow.component.ClientCallable; import com.vaadin.flow.component.orderedlayout.VerticalLayout; @@ -50,8 +52,14 @@ public IntegrationView() { xterm.setCursorStyle(CursorStyle.UNDERLINE); xterm.setSizeFull(); - sampleClientTerminalAddon = new SampleClientTerminalAddon(xterm); + xterm.setCopySelection(true); + xterm.setUseSystemClipboard(UseSystemClipboard.READWRITE); + xterm.setPasteWithMiddleClick(true); + xterm.setPasteWithRightClick(true); + + sampleClientTerminalAddon = new SampleClientTerminalAddon(xterm); + TerminalHistory.extend(xterm); xterm.addLineListener(ev -> xterm.writePrompt()); xterm.focus(); diff --git a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java index df1fd63..6c9c170 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java +++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java @@ -28,10 +28,6 @@ public class SampleClientTerminalAddonIT extends AbstractViewTest implements Has private static final String BAR = "BAR"; IntegrationViewCallables $server = createCallableProxy(IntegrationViewCallables.class); - public SampleClientTerminalAddonIT() { - super(IntegrationView.ROUTE); - } - @Test public void testSelectionFeature1() throws InterruptedException { XTermElement term = $(XTermElement.class).first();