diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 353b046..68e240a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -216,6 +216,8 @@ jobs: rsync -amv --include='*/' --include='*.html' + --include='*.css' + --include='*.mjs' --include='*.js' --include='*.wasm' --exclude='*' diff --git a/.gitignore b/.gitignore index 708338e..735ed65 100644 --- a/.gitignore +++ b/.gitignore @@ -34,14 +34,16 @@ !doc/**/*.md # examples directory: -!examples/**/*.txt !examples/**/*.cpp +!examples/**/*.css !examples/**/*.hpp -!examples/**/*.ipp !examples/**/*.html -!examples/**/*.py -!examples/**/*.js !examples/**/*.html.disabled +!examples/**/*.ipp +!examples/**/*.js +!examples/**/*.mjs +!examples/**/*.py +!examples/**/*.txt # include directory: !include/ftxui/**/*.hpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index dbea321..953efa9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -21,6 +21,8 @@ if (EMSCRIPTEN) get_property(EXAMPLES GLOBAL PROPERTY FTXUI::EXAMPLES) foreach(file "index.html" + "index.mjs" + "index.css" "sw.js" "run_webassembly.py") configure_file(${file} ${file}) diff --git a/examples/index.css b/examples/index.css new file mode 100644 index 0000000..32b4475 --- /dev/null +++ b/examples/index.css @@ -0,0 +1,107 @@ +@import url(https://fonts.googleapis.com/css?family=Khula:700); + +body { + background-color:#EEE; + padding:0px; + margin:0px; + font-family: Khula, Helvetica, sans-serif; + font-size: 130%; +} + +.page { + max-width:1300px; + margin: auto; + padding: 10px; +} + +a { + box-shadow: inset 0 0 0 0 #54b3d6; + color: #0087b9; + margin: 0 -.25rem; + padding: 0 .25rem; + transition: color .3s ease-in-out, + box-shadow .3s ease-in-out; +} + +a:hover { + box-shadow: inset 120px 0 0 0 #54b3d6; + color: white; +} + +h1 { + text-decoration: underline; + width:100%; + background-color: rgba(100,100,255,0.5); + padding: 10px; + margin: 0; +} + + +#selectExample { + flex:1; +} + +#selectExample, #selectExample option { + font-size: 16px; + font-family: sans-serif; + font-weight: 700; + line-height: 1.3; + border:0px; + background-color: #bbb; + color:black; +} + +#selectExample:focus { + outline:none; +} + +#terminal { + width:100%; + height 500px; + height: calc(clamp(200px, 100vh - 300px, 900px)); + overflow: hidden; + border:none; + background-color:black; +} + +#terminalContainer { + overflow: hidden; + border-radius: 10px; + box-shadow: 0px 2px 10px 0px rgba(0,0,0,0.75), + 0px 2px 80px 0px rgba(0,0,0,0.50); +} + +.fakeButtons { + height: 10px; + width: 10px; + border-radius: 50%; + border: 1px solid #000; + margin:6px; + background-color: #ff3b47; + border-color: #9d252b; + display: inline-block; +} + +.fakeMinimize { + left: 11px; + background-color: #ffc100; + border-color: #9d802c; +} + +.fakeZoom { + left: 16px; + background-color: #00d742; + border-color: #049931; +} + +.fakeMenu { + display:flex; + flex-direction: row; + width:100%; + box-sizing: border-box; + height: 25px; + background-color: #bbb; + color:black; + margin: 0 auto; + overflow: hidden; +} diff --git a/examples/index.html b/examples/index.html index a5e0a48..8e5498c 100644 --- a/examples/index.html +++ b/examples/index.html @@ -1,173 +1,32 @@ - + + FTXUI examples WebAssembly - - - + + - - + +
-

FTXUI WebAssembly Example

- FTXUI is a single - C++ library for terminal user interface. + FTXUI is a simple + functional C++ library for terminal user interface.
+ This showcase the: ./example/ folder.

-

- On this page, you can try all the examples contained in: ./example/ - Those are compiled using WebAssembly. -

- -
+ +
+
+
+
+
+ +
+
+
- - - - diff --git a/examples/index.mjs b/examples/index.mjs new file mode 100644 index 0000000..722b224 --- /dev/null +++ b/examples/index.mjs @@ -0,0 +1,102 @@ +import xterm from 'https://cdn.jsdelivr.net/npm/xterm@4.18.0/+esm' +import xterm_addon_webgl from 'https://cdn.jsdelivr.net/npm/xterm-addon-webgl@0.11.4/+esm' +import xterm_addon_fit from 'https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.5.0/+esm' + +// Add COOP/COEP via a ServiceWorker to use SharedArrayBuffer +if ("serviceWorker" in navigator && !window.crossOriginIsolated) { + const url_sw = new URL("./sw.js", location.href); + const registration = await navigator.serviceWorker.register(url_sw); + if (registration.active && !navigator.serviceWorker.controller) { + window.location.reload(); // Reload to ensure the COOP/COEP headers are set. + } +} + +const example_list = "@EXAMPLES@".split(";"); +const url_search_params = new URLSearchParams(window.location.search); + +const select = document.getElementById("selectExample"); +for(const example of example_list) { + const option = document.createElement("option"); + option.textContent = example; + option.value = example; + select.appendChild(option); +} +const example = url_search_params.get("file") || "dom/color_gallery"; +select.selectedIndex = example_list.findIndex(path => path == example) || 0; +select.addEventListener("change", () => { + history.pushState({}, "", "?file=" + example_list[select.selectedIndex]); + location.reload(); +}); + +const term_element = document.querySelector('#terminal'); +const term = new xterm.Terminal(); +term.options.scrollback = 0; +term.open(term_element); +const fit_addon = new xterm_addon_fit.FitAddon(); +const webgl_addon = new xterm_addon_webgl.WebglAddon(); +term.loadAddon(webgl_addon); +term.loadAddon(fit_addon); + +const stdin_buffer = []; +const stdout_buffer = []; +const stderr_buffer = []; + +const stdin = () => { + return stdin_buffer.shift() || 0; +} + +const stdout = code => { + if (code == 0) { + term.write(new Uint8Array(stdout_buffer)); + stdout_buffer.length = 0; + } else { + stdout_buffer.push(code) + } +} + +const stderr = code => { + if (code == 0 || code == 10) { + console.error(String.fromCodePoint(...stderr_buffer)); + stderr_buffer = []; + } else { + stderr_buffer.push(code) + } +} + +const onBinary = e => { + for(const c of e) + stdin_buffer.push(c.charCodeAt(0)); +} + +term.onBinary(onBinary); +term.onData(onBinary) +term.resize(140,43); + +window.Module = { + preRun: () => { + FS.init(stdin, stdout, stderr); + }, + postRun: [], + onRuntimeInitialized: () => { + if (window.Module._ftxui_on_resize == undefined) + return; + fit_addon.fit(); + + const resize_handler = () => { + const {cols, rows} = fit_addon.proposeDimensions(); + term.resize(cols, rows); + window.Module._ftxui_on_resize(cols, rows); + fit_addon.fit(); + }; + const resize_observer = new ResizeObserver(resize_handler); + resize_observer.observe(term_element); + resize_handler(); + + // Disable scrollbar + //term.write('\x1b[?47h') + }, +}; + +const words = example.split('/') +words[1] = "ftxui_example_" + words[1] + ".js" +document.querySelector("#example_script").src = words.join('/');