diff --git a/spring-shell-docs/readme.txt b/spring-shell-docs/readme.txt
new file mode 100644
index 00000000..c62f65a1
--- /dev/null
+++ b/spring-shell-docs/readme.txt
@@ -0,0 +1,23 @@
+Some info how screen recordings were made
+
+asciinema rec spring-shell-docs/src/main/asciidoc/asciinema/component-text-input-1.cast
+asciinema rec spring-shell-docs/src/main/asciidoc/asciinema/component-path-input-1.cast
+asciinema rec spring-shell-docs/src/main/asciidoc/asciinema/component-confirmation-1.cast
+asciinema rec spring-shell-docs/src/main/asciidoc/asciinema/component-single-select-1.cast
+asciinema rec spring-shell-docs/src/main/asciidoc/asciinema/component-multi-select-1.cast
+
+svg-term \
+ --in spring-shell-docs/src/main/asciidoc/asciinema/component-text-input-1.cast \
+ --out spring-shell-docs/src/main/asciidoc/images/component-text-input-1.svg
+svg-term \
+ --in spring-shell-docs/src/main/asciidoc/asciinema/component-path-input-1.cast \
+ --out spring-shell-docs/src/main/asciidoc/images/component-path-input-1.svg
+svg-term \
+ --in spring-shell-docs/src/main/asciidoc/asciinema/component-confirmation-1.cast \
+ --out spring-shell-docs/src/main/asciidoc/images/component-confirmation-1.svg
+svg-term \
+ --in spring-shell-docs/src/main/asciidoc/asciinema/component-single-select-1.cast \
+ --out spring-shell-docs/src/main/asciidoc/images/component-single-select-1.svg
+svg-term \
+ --in spring-shell-docs/src/main/asciidoc/asciinema/component-multi-select-1.cast \
+ --out spring-shell-docs/src/main/asciidoc/images/component-multi-select-1.svg
diff --git a/spring-shell-docs/src/main/asciidoc/asciinema/component-confirmation-1.cast b/spring-shell-docs/src/main/asciidoc/asciinema/component-confirmation-1.cast
new file mode 100644
index 00000000..084b89a6
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/asciinema/component-confirmation-1.cast
@@ -0,0 +1,35 @@
+{"version": 2, "width": 85, "height": 8, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
+[1.590847, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
+[2.774407, "o", "\r\n"]
+[4.560309, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[5.138757, "o", "\u001b[31mc\u001b[0m"]
+[5.267214, "o", "\u001b[31mo\u001b[0m"]
+[5.467126, "o", "\u001b[31mm\u001b[0m"]
+[5.674049, "o", "\u001b[31mp\u001b[0m"]
+[5.752663, "o", "\u001b[31mo\u001b[0m"]
+[5.917741, "o", "\u001b[31mn\u001b[0m"]
+[6.040844, "o", "\u001b[31me\u001b[0m"]
+[6.123068, "o", "\u001b[31mn\u001b[0m"]
+[6.277935, "o", "\u001b[31mt\u001b[0m"]
+[7.050283, "o", "\u001b[31m \u001b[0m"]
+[7.413917, "o", "\u001b[31mc\u001b[0m"]
+[7.568894, "o", "\u001b[31mo\u001b[0m"]
+[7.664856, "o", "\u001b[31mn\u001b[0m"]
+[7.853492, "o", "\u001b[13D\u001b[1mcomponent confirmation\u001b[0m \u001b[K"]
+[8.698242, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
+[8.745928, "o", "\u001b[?1h\u001b=\u001b[?25l"]
+[8.803411, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[2m(Y/n)\u001b[0m\r"]
+[13.769588, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
+[13.779021, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34mtrue\u001b[0m\r\n"]
+[13.779319, "o", "Got value true\r\n"]
+[13.781245, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[15.955396, "o", "\u001b[1mcomponent confirmation\u001b[0m"]
+[16.743244, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l"]
+[16.743555, "o", "\u001b[?2004l"]
+[16.748638, "o", "\u001b[?1h\u001b=\u001b[?25l"]
+[16.754861, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[2m(Y/n)\u001b[0m\r"]
+[19.347907, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
+[19.355493, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34mfalse\u001b[0m\r\n"]
+[19.356618, "o", "Got value false\r\n"]
+[19.3586, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[21.3586, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
diff --git a/spring-shell-docs/src/main/asciidoc/asciinema/component-multi-select-1.cast b/spring-shell-docs/src/main/asciidoc/asciinema/component-multi-select-1.cast
new file mode 100644
index 00000000..a67305c0
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/asciinema/component-multi-select-1.cast
@@ -0,0 +1,42 @@
+{"version": 2, "width": 85, "height": 9, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
+[1.746447, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
+[2.881033, "o", "\r\n"]
+[4.603396, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[5.105471, "o", "\u001b[31mc\u001b[0m"]
+[5.202878, "o", "\u001b[31mo\u001b[0m"]
+[5.428705, "o", "\u001b[31mm\u001b[0m"]
+[5.630033, "o", "\u001b[31mp\u001b[0m"]
+[5.692044, "o", "\u001b[31mo\u001b[0m"]
+[5.86652, "o", "\u001b[31mn\u001b[0m"]
+[6.018765, "o", "\u001b[31me\u001b[0m"]
+[6.089776, "o", "\u001b[31mn\u001b[0m"]
+[6.251781, "o", "\u001b[31mt\u001b[0m"]
+[7.126694, "o", "\u001b[31m \u001b[0m"]
+[7.319912, "o", "\u001b[31mm\u001b[0m"]
+[7.550772, "o", "\u001b[31mu\u001b[0m"]
+[7.80087, "o", "\u001b[12D\u001b[1mcomponent multi\u001b[0m \u001b[K"]
+[8.291497, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
+[8.338879, "o", "\u001b[?1h\u001b=\u001b[?25l"]
+[8.398742, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m [Use arrows to move], type to filter\u001b[54C \u001b[96;1m>\u001b[0m\u001b[96;1m \u001b[39m[ ]\u001b[0m key1\u001b[93C \u001b[2m[ ]\u001b[0m \u001b[2mkey2\u001b[0m\u001b[93C \u001b[1m[ ]\u001b[0m key3\u001b[3A\r"]
+[12.265649, "o", "\u001b[35Cfiltering 'k'\u001b[K\r"]
+[12.507991, "o", "\u001b[47C\u001b[1@e\r"]
+[12.945812, "o", "\u001b[48C\u001b[1@y\r"]
+[13.35949, "o", "\u001b[49C\u001b[1@1\r\r\n\u001b[103C \u001b[K\r\r\r\n\u001b[K\r\r\n\u001b[K\u001b[3A"]
+[14.187299, "o", "\u001b[49C\u001b[P\r\r\n\u001b[103C \u001b[2m[ ]\u001b[0m \u001b[2mkey2\u001b[0m\u001b[93C \u001b[1m[ ]\u001b[0m key3\u001b[3A\r"]
+[14.356992, "o", "\u001b[48C\u001b[P\r"]
+[14.495579, "o", "\u001b[47C\u001b[P\r"]
+[14.642467, "o", "\u001b[35Ctype to filter\u001b[K\r"]
+[15.763904, "o", "\r\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[A\r"]
+[16.496138, "o", "\r\r\n \r\r\n\u001b[96;1m> \u001b[0m\u001b[2A\r"]
+[16.678991, "o", "\r\r\n\r\n \r\r\n\u001b[96;1m> \u001b[0m\u001b[3A\r"]
+[17.979077, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[3A\r"]
+[18.809587, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[1m[ ]\u001b[0m\u001b[3A\r"]
+[20.452942, "o", "\r\r\n\r\n\u001b[96;1m> \u001b[0m\r\r\n \u001b[3A\r"]
+[21.705426, "o", "\r\r\n\r\n \r\r\n\u001b[96;1m> \u001b[0m\u001b[3A\r"]
+[22.207898, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[32m[x]\u001b[0m\u001b[3A\r"]
+[23.30633, "o", "\r\r\n\r\n\r\n\u001b[2C\u001b[1m[ ]\u001b[0m\u001b[3A\r"]
+[24.642701, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[3A"]
+[24.64692, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m \u001b[34mvalue1\u001b[0m\r\n"]
+[24.647546, "o", "Got value value1\r\n"]
+[24.649365, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[26.649365, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
diff --git a/spring-shell-docs/src/main/asciidoc/asciinema/component-path-input-1.cast b/spring-shell-docs/src/main/asciidoc/asciinema/component-path-input-1.cast
new file mode 100644
index 00000000..79b21c8f
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/asciinema/component-path-input-1.cast
@@ -0,0 +1,35 @@
+{"version": 2, "width": 85, "height": 6, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
+[1.276071, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
+[1.975169, "o", "\r\n"]
+[3.730642, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[4.157171, "o", "\u001b[31mc\u001b[0m"]
+[4.259876, "o", "\u001b[31mo\u001b[0m"]
+[4.480644, "o", "\u001b[31mm\u001b[0m"]
+[4.675743, "o", "\u001b[31mp\u001b[0m"]
+[4.746389, "o", "\u001b[31mo\u001b[0m"]
+[4.899817, "o", "\u001b[31mn\u001b[0m"]
+[5.064803, "o", "\u001b[31me\u001b[0m"]
+[5.151957, "o", "\u001b[31mn\u001b[0m"]
+[5.295573, "o", "\u001b[31mt\u001b[0m"]
+[5.626209, "o", "\u001b[31m \u001b[0m"]
+[5.949778, "o", "\u001b[31mp\u001b[0m"]
+[6.052068, "o", "\u001b[31ma\u001b[0m"]
+[6.270888, "o", "\u001b[31mt\u001b[0m"]
+[6.342701, "o", "\u001b[13D\u001b[1mcomponent path\u001b[0m\u001b[K"]
+[6.866598, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
+[6.918336, "o", "\u001b[?1h\u001b=\u001b[?25l"]
+[6.979593, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \r"]
+[8.74751, "o", "\u001b[14C/\u001b[88C \u001b[31m>\u001b[0m\u001b[31m>>\u001b[0m \u001b[31mDirectory exists\u001b[0m\u001b[A\r"]
+[10.825765, "o", "\u001b[15Ct\r\r\n\u001b[32m>\u001b[0m \u001b[32mPath ok\u001b[0m\u001b[K\u001b[A\r"]
+[10.96141, "o", "\u001b[16Cm\r"]
+[11.050615, "o", "\u001b[17Cp\r\r\n\u001b[31m>>>\u001b[0m \u001b[31mDirectory exists\u001b[0m\u001b[K\u001b[A\r"]
+[11.812886, "o", "\u001b[18C/\r"]
+[11.930461, "o", "\u001b[19Cd\r\r\n\u001b[32m>\u001b[0m \u001b[32mPath ok\u001b[0m\u001b[K\u001b[A\r"]
+[12.128981, "o", "\u001b[20Ce\r"]
+[12.176119, "o", "\u001b[21Cm\r"]
+[12.28765, "o", "\u001b[22Co\r"]
+[13.464196, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\u001b[A"]
+[13.471408, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34m/tmp/demo\u001b[0m\r\n"]
+[13.471875, "o", "Got value /tmp/demo\r\n"]
+[13.475016, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[15.475016, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
diff --git a/spring-shell-docs/src/main/asciidoc/asciinema/component-single-select-1.cast b/spring-shell-docs/src/main/asciidoc/asciinema/component-single-select-1.cast
new file mode 100644
index 00000000..e2c1c576
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/asciinema/component-single-select-1.cast
@@ -0,0 +1,38 @@
+{"version": 2, "width": 85, "height": 8, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
+[2.193321, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
+[3.11524, "o", "\r\n"]
+[4.904714, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[5.372714, "o", "\u001b[31mc\u001b[0m"]
+[5.484943, "o", "\u001b[31mo\u001b[0m"]
+[5.70511, "o", "\u001b[31mm\u001b[0m"]
+[5.913092, "o", "\u001b[31mp\u001b[0m"]
+[5.992808, "o", "\u001b[31mo\u001b[0m"]
+[6.159092, "o", "\u001b[31mn\u001b[0m"]
+[6.281094, "o", "\u001b[31me\u001b[0m"]
+[6.353925, "o", "\u001b[31mn\u001b[0m"]
+[6.49373, "o", "\u001b[31mt\u001b[0m"]
+[6.677988, "o", "\u001b[31m \u001b[0m"]
+[6.825018, "o", "\u001b[31ms\u001b[0m"]
+[6.95602, "o", "\u001b[31mi\u001b[0m"]
+[7.121719, "o", "\u001b[31mn\u001b[0m"]
+[7.309007, "o", "\u001b[13D\u001b[1mcomponent single\u001b[0m \u001b[K"]
+[7.972392, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
+[8.019885, "o", "\u001b[?1h\u001b=\u001b[?25l"]
+[8.080156, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m [Use arrows to move], type to filter\u001b[54C \u001b[96;1m>\u001b[0m\u001b[96;1m key1\u001b[0m\u001b[97C key2\u001b[2A\r"]
+[9.181662, "o", "\r\r\n key1\r\r\n\u001b[96;1m> key2\u001b[0m\u001b[2A\r"]
+[9.701431, "o", "\r\r\n\u001b[96;1m> key1\u001b[0m\r\r\n key2\u001b[2A\r"]
+[10.380557, "o", "\r\r\n key1\r\r\n\u001b[96;1m> key2\u001b[0m\u001b[2A\r"]
+[12.157001, "o", "\u001b[35Cfiltering 'k'\u001b[K\r"]
+[14.605973, "o", "\u001b[47C\u001b[1@e\r\r\n\u001b[96;1m> key1\u001b[0m\r\r\n key2\u001b[2A\r"]
+[14.92268, "o", "\u001b[48C\u001b[1@y\r"]
+[15.320008, "o", "\u001b[49C\u001b[1@1\r\r\n\u001b[103C \u001b[K\r\r\r\n\u001b[K\u001b[2A"]
+[16.215448, "o", "\u001b[49C\u001b[P\r\r\n\u001b[103C key2\u001b[2A\r"]
+[16.559377, "o", "\u001b[48C\u001b[P\r"]
+[16.784486, "o", "\u001b[47C\u001b[P\r"]
+[17.004921, "o", "\u001b[35Ctype to filter\u001b[K\r"]
+[17.790542, "o", "\r\r\n key1\r\r\n\u001b[96;1m> key2\u001b[0m\u001b[2A\r"]
+[18.340142, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K\r\r\n\u001b[K\r\r\n\u001b[K\u001b[2A"]
+[18.347511, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mtestSimple\u001b[0m \u001b[34mvalue2\u001b[0m\r\n"]
+[18.34852, "o", "Got value value2\r\n"]
+[18.35067, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[20.35067, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
diff --git a/spring-shell-docs/src/main/asciidoc/asciinema/component-text-input-1.cast b/spring-shell-docs/src/main/asciidoc/asciinema/component-text-input-1.cast
new file mode 100644
index 00000000..2e9ea6e5
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/asciinema/component-text-input-1.cast
@@ -0,0 +1,30 @@
+{"version": 2, "width": 85, "height": 6, "timestamp": 1645645867, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
+[1.262977, "o", "java -jar spring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jar"]
+[2.045105, "o", "\r\n"]
+[3.846992, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[4.717927, "o", "\u001b[31mc\u001b[0m"]
+[4.824399, "o", "\u001b[31mo\u001b[0m"]
+[5.014061, "o", "\u001b[31mm\u001b[0m"]
+[5.703137, "o", "\u001b[31mp\u001b[0m"]
+[5.802386, "o", "\u001b[31mo\u001b[0m"]
+[6.434988, "o", "\u001b[31mn\u001b[0m"]
+[6.653321, "o", "\u001b[31me\u001b[0m"]
+[6.756821, "o", "\u001b[31mn\u001b[0m"]
+[7.293975, "o", "\u001b[31mt\u001b[0m"]
+[7.409984, "o", "\u001b[31m \u001b[0m"]
+[7.604973, "o", "\u001b[31ms\u001b[0m"]
+[7.775599, "o", "\u001b[31mt\u001b[0m"]
+[7.928792, "o", "\u001b[31mr\u001b[0m"]
+[7.979401, "o", "\u001b[31mi\u001b[0m"]
+[8.051232, "o", "\u001b[31mn\u001b[0m"]
+[8.15451, "o", "\u001b[15D\u001b[1mcomponent string\u001b[0m\u001b[K"]
+[8.892861, "o", "\r\r\n\u001b[?1l\u001b>\u001b[?1000l\u001b[?2004l"]
+[8.951185, "o", "\u001b[?1h\u001b=\u001b[?25l"]
+[9.013964, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34m[Default myvalue]\u001b[0m\r"]
+[10.304694, "o", "\u001b[14Ch\u001b[K\r"]
+[10.533832, "o", "\u001b[15Ci\r"]
+[11.088255, "o", "\u001b[?1l\u001b>\u001b[?12;25h\u001b[K"]
+[11.097044, "o", "\u001b[32;1m?\u001b[0m \u001b[97;1mEnter value\u001b[0m \u001b[34mhi\u001b[0m\r\n"]
+[11.097492, "o", "Got value hi\r\n"]
+[11.100226, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
+[13.100226, "o", "\u001b[?1h\u001b=\u001b[?2004h\u001b[33mmy-shell:>\u001b[0m"]
diff --git a/spring-shell-docs/src/main/asciidoc/images/component-confirmation-1.svg b/spring-shell-docs/src/main/asciidoc/images/component-confirmation-1.svg
new file mode 100644
index 00000000..32bd94cf
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/images/component-confirmation-1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/spring-shell-docs/src/main/asciidoc/images/component-multi-select-1.svg b/spring-shell-docs/src/main/asciidoc/images/component-multi-select-1.svg
new file mode 100644
index 00000000..a9928f51
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/images/component-multi-select-1.svg
@@ -0,0 +1 @@
+java-jarspring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jarmy-shell:>my-shell:>componentmy-shell:>componentmulti?testSimple[Usearrowstomove],typetofilter>[]key1[]key2[]key3?testSimple[Usearrowstomove],filtering'k'?testSimple[Usearrowstomove],filtering'ke'?testSimple[Usearrowstomove],filtering'key'[x]key1>[]key2>[]key3>[x]key3?testSimplevalue1Gotvaluevalue1my-shell:>cmy-shell:>comy-shell:>commy-shell:>compmy-shell:>compomy-shell:>componmy-shell:>componemy-shell:>componenmy-shell:>componentmmy-shell:>componentmu?testSimple[Usearrowstomove],filtering'key1'>[x]key1my-shell:>my-shell:>
\ No newline at end of file
diff --git a/spring-shell-docs/src/main/asciidoc/images/component-path-input-1.svg b/spring-shell-docs/src/main/asciidoc/images/component-path-input-1.svg
new file mode 100644
index 00000000..54f76402
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/images/component-path-input-1.svg
@@ -0,0 +1 @@
+java-jarspring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jarmy-shell:>my-shell:>componentmy-shell:>componentpath>>>Directoryexists>Pathok?Entervalue/tmp/demoGotvalue/tmp/demomy-shell:>cmy-shell:>comy-shell:>commy-shell:>compmy-shell:>compomy-shell:>componmy-shell:>componemy-shell:>componenmy-shell:>componentpmy-shell:>componentpamy-shell:>componentpat?Entervalue?Entervalue/?Entervalue/t?Entervalue/tm?Entervalue/tmp?Entervalue/tmp/?Entervalue/tmp/d?Entervalue/tmp/de?Entervalue/tmp/dem?Entervalue/tmp/demomy-shell:>my-shell:>
\ No newline at end of file
diff --git a/spring-shell-docs/src/main/asciidoc/images/component-single-select-1.svg b/spring-shell-docs/src/main/asciidoc/images/component-single-select-1.svg
new file mode 100644
index 00000000..42f46112
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/images/component-single-select-1.svg
@@ -0,0 +1 @@
+java-jarspring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jarmy-shell:>my-shell:>componentmy-shell:>componentsingle?testSimple[Usearrowstomove],typetofilter>key1key2key1>key2?testSimple[Usearrowstomove],filtering'k'?testSimple[Usearrowstomove],filtering'ke'?testSimple[Usearrowstomove],filtering'key'?testSimplevalue2Gotvaluevalue2my-shell:>cmy-shell:>comy-shell:>commy-shell:>compmy-shell:>compomy-shell:>componmy-shell:>componemy-shell:>componenmy-shell:>componentsmy-shell:>componentsimy-shell:>componentsin?testSimple[Usearrowstomove],filtering'key1'my-shell:>my-shell:>
\ No newline at end of file
diff --git a/spring-shell-docs/src/main/asciidoc/images/component-text-input-1.svg b/spring-shell-docs/src/main/asciidoc/images/component-text-input-1.svg
new file mode 100644
index 00000000..9f01dd37
--- /dev/null
+++ b/spring-shell-docs/src/main/asciidoc/images/component-text-input-1.svg
@@ -0,0 +1 @@
+java-jarspring-shell-samples/target/spring-shell-samples-2.1.0-SNAPSHOT.jarmy-shell:>my-shell:>componentmy-shell:>componentstring?EntervaluehiGotvaluehimy-shell:>cmy-shell:>comy-shell:>commy-shell:>compmy-shell:>compomy-shell:>componmy-shell:>componemy-shell:>componenmy-shell:>componentsmy-shell:>componentstmy-shell:>componentstrmy-shell:>componentstrimy-shell:>componentstrin?Entervalue[Defaultmyvalue]?Entervalueh?Entervaluehimy-shell:>my-shell:>
\ No newline at end of file
diff --git a/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc b/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc
index f01b4570..d8e457d5 100644
--- a/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc
+++ b/spring-shell-docs/src/main/asciidoc/using-spring-shell.adoc
@@ -1024,6 +1024,374 @@ AttributedStyle style = themeResolver.resolveStyle(resolvedStyle);
----
====
+[[uicomponents]]
+=== UI Components
+
+Starting from _2.1.x_ there is a new component model which provides
+easier way to create higher level user interaction for usual use cases
+like asking input in a various forms. These usually are just plain text
+input or choosing something from a list.
+
+Templates for build-in components are in classpath under
+_org/springframework/shell/component_.
+
+Build-in components generally follow logic:
+
+* Enter run loop for user input
+* Generate component related context
+* Render runtime status of a component state
+* Exit
+* Render final status of a component state
+
+==== Component Render
+
+There are two ways to implement component rendering, firstly fully
+programmatically or secondly using a _ANTLR Stringtemplate_. Though
+strictly speaking there is just a simple `Function` renderer interface
+which takes `Context` as an input and outputs a list of `AttributedString`
+but this allows to choose between _templating_ and _code_.
+
+Templating is a good choice if you don't need to anything complex or
+you just want to slightly modify existing component layouts. Rendering
+via code then gives you flexibility to do whatever you need.
+
+Programmatic way to render is simple as to create a `Function`:
+
+====
+[source, java]
+----
+class StringInputCustomRenderer implements Function> {
+ @Override
+ public List apply(StringInputContext context) {
+ AttributedStringBuilder builder = new AttributedStringBuilder();
+ builder.append(context.getName());
+ builder.append(" ");
+ if (context.getResultValue() != null) {
+ builder.append(context.getResultValue());
+ }
+ else {
+ String input = context.getInput();
+ if (StringUtils.hasText(input)) {
+ builder.append(input);
+ }
+ else {
+ builder.append("[Default " + context.getDefaultValue() + "]");
+ }
+ }
+ return Arrays.asList(builder.toAttributedString());
+ }
+}
+----
+====
+
+And then hook it with a component:
+
+====
+[source, java]
+----
+@ShellMethod(key = "component stringcustom", value = "String input", group = "Components")
+public String stringInputCustom(boolean mask) {
+ StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue",
+ new StringInputCustomRenderer());
+ component.setResourceLoader(getResourceLoader());
+ component.setTemplateExecutor(getTemplateExecutor());
+ if (mask) {
+ component.setMaskCharater('*');
+ }
+ StringInputContext context = component.run(StringInputContext.empty());
+ return "Got value " + context.getResultValue();
+}
+----
+====
+
+Component have their own context but usually shares some functionality
+from a parent component types, those context variables are shown below.
+
+[[textcomponentcontext-template-variables]]
+.TextComponentContext Template Variables
+|===
+|Key |Description
+
+|resultValue
+|Value after component renders its result.
+
+|name
+|Name of a component, aka its title.
+
+|message
+|Possible message set for component.
+
+|messageLevel
+|Level of a message, either INFO, WARN or ERROR
+
+|hasMessageLevelInfo
+|Return true if level is INFO, false otherwise.
+
+|hasMessageLevelWarn
+|Return true if level is WARN, false otherwise.
+
+|hasMessageLevelError
+|Return true if level is ERROR, false otherwise.
+
+|input
+|Raw user input.
+
+|===
+
+
+[[selectorcomponentcontext-template-variables]]
+.SelectorComponentContext Template Variables
+|===
+|Key |Description
+
+|name
+|Name of a component, aka title.
+
+|input
+|Raw user input, mostly for filter.
+
+|itemStates
+|Full list of item states.
+
+|itemStateView
+|Visible list of item states.
+
+|isResult
+|Return if context is in a result mode.
+
+|cursorRow
+|Current cursor row in a selector
+
+|===
+
+
+==== Build-in Components
+
+===== String Input
+
+Used to ask a simple text input from a user, optionally masking values
+if content contains something sensitive.
+
+====
+[source, java]
+----
+@ShellComponent
+public class ComponentCommands extends AbstractShellComponent {
+
+ @ShellMethod(key = "component string", value = "String input", group = "Components")
+ public String stringInput(boolean mask) {
+ StringInput component = new StringInput(getTerminal(), "Enter value", "myvalue");
+ component.setResourceLoader(getResourceLoader());
+ component.setTemplateExecutor(getTemplateExecutor());
+ if (mask) {
+ component.setMaskCharater('*');
+ }
+ StringInputContext context = component.run(StringInputContext.empty());
+ return "Got value " + context.getResultValue();
+ }
+}
+----
+====
+
+image::images/component-text-input-1.svg[text input]
+
+Context object is `StringInputContext`.
+
+[[stringinputcontext-template-variables]]
+.StringInputContext Template Variables
+|===
+|Key |Description
+
+|defaultValue
+|Default value if set, null otherwise.
+
+|maskedInput
+|Masked input value
+
+|maskedResultValue
+|Masked result value
+
+|maskCharacter
+|Mask character if set, null otherwise.
+
+|hasMaskCharacter
+|Is true if mask character is set, false otherwise.
+
+|model
+|Parent context variables <>
+|===
+
+===== Path Input
+
+Used to ask a `Path` from a user and gives additional info about a path itself.
+
+====
+[source, java]
+----
+@ShellComponent
+public class ComponentCommands extends AbstractShellComponent {
+
+ @ShellMethod(key = "component path", value = "Path input", group = "Components")
+ public String pathInput() {
+ PathInput component = new PathInput(getTerminal(), "Enter value");
+ component.setResourceLoader(getResourceLoader());
+ component.setTemplateExecutor(getTemplateExecutor());
+ PathInputContext context = component.run(PathInputContext.empty());
+ return "Got value " + context.getResultValue();
+ }
+}
+----
+====
+
+image::images/component-path-input-1.svg[text input]
+
+Context object is `PathInputContext`.
+
+[[pathinputcontext-template-variables]]
+.PathInputContext Template Variables
+|===
+|Key |Description
+
+|model
+|Parent context variables <>
+|===
+
+===== Confirmation
+
+Used to ask a simple confirmation from a user and essentially is
+yes/no question.
+
+====
+[source, java]
+----
+@ShellComponent
+public class ComponentCommands extends AbstractShellComponent {
+
+ @ShellMethod(key = "component confirmation", value = "Confirmation input", group = "Components")
+ public String confirmationInput(boolean no) {
+ ConfirmationInput component = new ConfirmationInput(getTerminal(), "Enter value", !no);
+ component.setResourceLoader(getResourceLoader());
+ component.setTemplateExecutor(getTemplateExecutor());
+ ConfirmationInputContext context = component.run(ConfirmationInputContext.empty());
+ return "Got value " + context.getResultValue();
+ }
+}
+----
+====
+
+image::images/component-confirmation-1.svg[text input]
+
+Context object is `ConfirmationInputContext`.
+
+[[confirmationinputcontext-template-variables]]
+.ConfirmationInputContext Template Variables
+|===
+|Key |Description
+
+|defaultValue
+|Default value, either true or false.
+
+|model
+|Parent context variables <>
+|===
+
+===== Single Select
+
+Used to ask an item from a list and is essentially similar to simple
+dropbox implementation.
+
+====
+[source, java]
+----
+@ShellComponent
+public class ComponentCommands extends AbstractShellComponent {
+
+ @ShellMethod(key = "component single", value = "Single selector", group = "Components")
+ public String singleSelector() {
+ List> items = new ArrayList<>();
+ items.add(SelectorItem.of("key1", "value1"));
+ items.add(SelectorItem.of("key2", "value2"));
+ SingleItemSelector> component = new SingleItemSelector<>(getTerminal(),
+ items, "testSimple", null);
+ component.setResourceLoader(getResourceLoader());
+ component.setTemplateExecutor(getTemplateExecutor());
+ SingleItemSelectorContext> context = component
+ .run(SingleItemSelectorContext.empty());
+ String result = context.getResultItem().flatMap(si -> Optional.ofNullable(si.getItem())).get();
+ return "Got value " + result;
+ }
+}
+----
+====
+
+image::images/component-single-select-1.svg[text input]
+
+Context object is `SingleItemSelectorContext`.
+
+[[singleitemselectorcontext-template-variables]]
+.SingleItemSelectorContext Template Variables
+|===
+|Key |Description
+
+|value
+|Returned value when component exists.
+
+|rows
+|Visible items where rows list contains maps of name and selected items.
+
+|model
+|Parent context variables <>
+|===
+
+===== Multi Select
+
+Used to ask an items from a list.
+
+====
+[source, java]
+----
+@ShellComponent
+public class ComponentCommands extends AbstractShellComponent {
+
+ @ShellMethod(key = "component multi", value = "Multi selector", group = "Components")
+ public String multiSelector() {
+ List> items = new ArrayList<>();
+ items.add(SelectorItem.of("key1", "value1"));
+ items.add(SelectorItem.of("key2", "value2", false));
+ items.add(SelectorItem.of("key3", "value3"));
+ MultiItemSelector> component = new MultiItemSelector<>(getTerminal(),
+ items, "testSimple", null);
+ component.setResourceLoader(getResourceLoader());
+ component.setTemplateExecutor(getTemplateExecutor());
+ MultiItemSelectorContext> context = component
+ .run(MultiItemSelectorContext.empty());
+ String result = context.getResultItems().stream()
+ .map(si -> si.getItem())
+ .collect(Collectors.joining(","));
+ return "Got value " + result;
+ }
+}
+----
+====
+
+image::images/component-multi-select-1.svg[text input]
+
+Context object is `MultiItemSelectorContext`.
+
+[[multiitemselectorcontext-template-variables]]
+.MultiItemSelectorContext Template Variables
+|===
+|Key |Description
+
+|values
+|Returned values when component exists.
+
+|rows
+|Visible items where rows list contains maps of name, selected, onrow and enabled items.
+
+|model
+|Parent context variables <>
+|===
=== Customizing the Shell