diff --git a/pom.xml b/pom.xml
index 1940059..e5f1a4f 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
@@ -77,7 +77,7 @@
org.projectlombok
lombok
provided
- 1.18.34
+ 1.18.42
@@ -86,6 +86,12 @@
true
+
+ com.flowingcode.vaadin
+ json-migration-helper
+ 0.9.2
+
+
org.slf4j
slf4j-simple
@@ -95,6 +101,12 @@
com.vaadin
vaadin-testbench
test
+
+
+ com.flowingcode.vaadin.test
+ testbench-rpc
+ 1.4.0
+ test
org.hamcrest
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 {
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
new file mode 100644
index 0000000..af669a8
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/IntegrationView.java
@@ -0,0 +1,76 @@
+/*-
+ * #%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.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;
+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();
+
+ 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();
+ 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..6c9c170
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/xterm/integration/SampleClientTerminalAddonIT.java
@@ -0,0 +1,43 @@
+/*-
+ * #%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);
+
+ @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