├── presets.txt ├── .DS_Store ├── handle_NDI_Advanced_SDK.sh ├── download_NDI_SDK.sh ├── preinstall.sh ├── jack2ndi.service ├── ndi2jack.service ├── easy-install-x86_64.sh ├── easy-install-rpi3-armhf.sh ├── easy-install-rpi4-aarch64.sh ├── easy-install-rpi4-armhf.sh ├── easy-install-generic-aarch64.sh ├── easy-install-generic-armhf.sh ├── install.sh ├── LICENSE ├── .vscode └── settings.json ├── README.md ├── assets ├── main.css └── index.html ├── include ├── mjson.h └── mongoose.h ├── jack2ndi.cpp ├── ndi2jack.cpp └── mjson.c /presets.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lplassman/NDI-to-JACK/HEAD/.DS_Store -------------------------------------------------------------------------------- /handle_NDI_Advanced_SDK.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | #extract Advanced NDI SDK 4 | yes y | bash ./Install_NDI_Advanced_SDK_v5_Linux.sh > /dev/null -------------------------------------------------------------------------------- /download_NDI_SDK.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | #download and extract NDI 4 | curl -s https://downloads.ndi.tv/SDK/NDI_SDK_Linux/Install_NDI_SDK_v6_Linux.tar.gz | tar xvz -C /tmp/ 5 | yes y | bash /tmp/Install_NDI_SDK_v6_Linux.sh > /dev/null 6 | -------------------------------------------------------------------------------- /preinstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | apt-get update 4 | 5 | #install prerequisites 6 | apt-get -y install --no-install-recommends build-essential libjack-jackd2-dev qjackctl jackd avahi-daemon avahi-discover avahi-utils libssl-dev libconfig++-dev g++ curl 7 | 8 | -------------------------------------------------------------------------------- /jack2ndi.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=JACK to NDI Converter 3 | After=network-online.target jackd.service 4 | 5 | [Service] 6 | User=root 7 | LimitRTPRIO=infinity 8 | LimitMEMLOCK=infinity 9 | CPUSchedulingPolicy=rr 10 | CPUSchedulingPriority=80 11 | ExecStart=/opt/ndi2jack/bin/jack2ndi 12 | Restart=always 13 | RestartSec=5 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /ndi2jack.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=NDI to JACK Converter 3 | After=network-online.target jackd.service 4 | 5 | [Service] 6 | User=root 7 | LimitRTPRIO=infinity 8 | LimitMEMLOCK=infinity 9 | CPUSchedulingPolicy=rr 10 | CPUSchedulingPriority=80 11 | ExecStart=/opt/ndi2jack/bin/ndi2jack 12 | Restart=always 13 | RestartSec=5 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /easy-install-x86_64.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | echo "Starting install of NDI to JACK for x86_64-bit..." 6 | 7 | echo "Installing prerequisites..." 8 | sudo bash ./preinstall.sh 9 | 10 | echo "Downloading NDI libraries..." 11 | sudo bash ./download_NDI_SDK.sh 12 | 13 | echo "Building executable for x86_64-bit..." 14 | sudo bash ./build_x86_64.sh 15 | 16 | echo "Installing in final directory..." 17 | sudo bash ./install.sh 18 | 19 | echo "Done" 20 | -------------------------------------------------------------------------------- /easy-install-rpi3-armhf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | echo "Starting install of NDI to JACK for Raspberry Pi 3 32-bit..." 6 | 7 | echo "Installing prerequisites..." 8 | sudo bash ./preinstall.sh 9 | 10 | echo "Downloading NDI libraries..." 11 | sudo bash ./download_NDI_SDK.sh 12 | 13 | echo "Building executable for Raspberry Pi 3 32-bit..." 14 | sudo bash ./build_rpi3_armhf.sh 15 | 16 | echo "Installing in final directory..." 17 | sudo bash ./install.sh 18 | 19 | echo "Done" 20 | -------------------------------------------------------------------------------- /easy-install-rpi4-aarch64.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | echo "Starting install of NDI to JACK for Raspberry Pi 4 64-bit..." 6 | 7 | echo "Installing prerequisites..." 8 | sudo bash ./preinstall.sh 9 | 10 | echo "Downloading NDI libraries..." 11 | sudo bash ./download_NDI_SDK.sh 12 | 13 | echo "Building executable for Raspberry Pi 4 64-bit..." 14 | sudo bash ./build_rpi4_arm64.sh 15 | 16 | echo "Installing in final directory..." 17 | sudo bash ./install.sh 18 | 19 | echo "Done" 20 | -------------------------------------------------------------------------------- /easy-install-rpi4-armhf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | echo "Starting install of NDI to JACK for Raspberry Pi 4 32-bit..." 6 | 7 | echo "Installing prerequisites..." 8 | sudo bash ./preinstall.sh 9 | 10 | echo "Downloading NDI libraries..." 11 | sudo bash ./download_NDI_SDK.sh 12 | 13 | echo "Building executable for Raspberry Pi 4 32-bit..." 14 | sudo bash ./build_rpi4_armhf.sh 15 | 16 | echo "Installing in final directory..." 17 | sudo bash ./install.sh 18 | 19 | echo "Done" 20 | -------------------------------------------------------------------------------- /easy-install-generic-aarch64.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | echo "Starting install of NDI to JACK for Generic ARM 64-bit..." 6 | 7 | echo "Installing prerequisites..." 8 | sudo bash ./preinstall.sh 9 | 10 | echo "Extracting NDI libraries..." 11 | sudo bash ./handle_NDI_Advanced_SDK.sh 12 | 13 | echo "Building executable for Generic ARM 64-bit..." 14 | sudo bash ./build_generic_arm64.sh 15 | 16 | echo "Installing in final directory..." 17 | sudo bash ./install.sh 18 | 19 | echo "Done" 20 | -------------------------------------------------------------------------------- /easy-install-generic-armhf.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -eu 4 | 5 | echo "Starting install of NDI to JACK for Generic ARM 32-bit..." 6 | 7 | echo "Installing prerequisites..." 8 | sudo bash ./preinstall.sh 9 | 10 | echo "Extracting NDI libraries..." 11 | sudo bash ./handle_NDI_Advanced_SDK.sh 12 | 13 | echo "Building executable for Generic ARM 32-bit..." 14 | sudo bash ./build_generic_armhf.sh 15 | 16 | echo "Installing in final directory..." 17 | sudo bash ./install.sh 18 | 19 | echo "Done" 20 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | INSTALL_DIR="/opt/ndi2jack" 4 | BIN_DIR="$INSTALL_DIR/bin" 5 | ASSETS_DIR="$INSTALL_DIR/assets" 6 | LIB_DIR="/usr/lib" 7 | 8 | rm -R "$INSTALL_DIR" 9 | rm -R "$LIB_DIR/libndi*" 10 | 11 | if [ ! -d "$INSTALL_DIR" ]; then 12 | mkdir "$INSTALL_DIR" 13 | fi 14 | 15 | if [ ! -d "$LIB_DIR" ]; then 16 | mkdir "$LIB_DIR" 17 | fi 18 | 19 | if [ ! -d "$BIN_DIR" ]; then 20 | mkdir "$BIN_DIR" 21 | fi 22 | 23 | if [ ! -d "$ASSETS_DIR" ]; then 24 | mkdir "$ASSETS_DIR" 25 | fi 26 | 27 | cp lib/* "$LIB_DIR" 28 | 29 | cp build/ndi2jack "$BIN_DIR" 30 | cp build/jack2ndi "$BIN_DIR" 31 | 32 | cp assets/* "$ASSETS_DIR" 33 | 34 | chmod +x "$BIN_DIR/ndi2jack" 35 | chmod +x "$BIN_DIR/jack2ndi" 36 | 37 | #symlink to the /usr/bin directory 38 | ln -s "$BIN_DIR/ndi2jack" /usr/bin/ 39 | ln -s "$BIN_DIR/jack2ndi" /usr/bin/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Luke Plassman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "ostream": "cpp", 4 | "array": "cpp", 5 | "atomic": "cpp", 6 | "bit": "cpp", 7 | "*.tcc": "cpp", 8 | "cctype": "cpp", 9 | "chrono": "cpp", 10 | "clocale": "cpp", 11 | "cmath": "cpp", 12 | "compare": "cpp", 13 | "concepts": "cpp", 14 | "condition_variable": "cpp", 15 | "csignal": "cpp", 16 | "cstdarg": "cpp", 17 | "cstddef": "cpp", 18 | "cstdint": "cpp", 19 | "cstdio": "cpp", 20 | "cstdlib": "cpp", 21 | "ctime": "cpp", 22 | "cwchar": "cpp", 23 | "cwctype": "cpp", 24 | "deque": "cpp", 25 | "string": "cpp", 26 | "unordered_map": "cpp", 27 | "vector": "cpp", 28 | "exception": "cpp", 29 | "algorithm": "cpp", 30 | "functional": "cpp", 31 | "iterator": "cpp", 32 | "memory": "cpp", 33 | "memory_resource": "cpp", 34 | "numeric": "cpp", 35 | "random": "cpp", 36 | "ratio": "cpp", 37 | "string_view": "cpp", 38 | "system_error": "cpp", 39 | "tuple": "cpp", 40 | "type_traits": "cpp", 41 | "utility": "cpp", 42 | "fstream": "cpp", 43 | "initializer_list": "cpp", 44 | "iosfwd": "cpp", 45 | "iostream": "cpp", 46 | "istream": "cpp", 47 | "limits": "cpp", 48 | "new": "cpp", 49 | "numbers": "cpp", 50 | "semaphore": "cpp", 51 | "stdexcept": "cpp", 52 | "stop_token": "cpp", 53 | "streambuf": "cpp", 54 | "thread": "cpp", 55 | "cinttypes": "cpp", 56 | "typeinfo": "cpp" 57 | } 58 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NDI to JACK 2 | 3 | NDI to JACK is an application that connects an NDI audio source to JACK for use as an audio output into other JACK compatible applications. 4 | The JACK to NDI application, which is included, creates an NDI source from a JACK input. 5 | 6 | ## Features 7 | - Manage NDI connections using the integrated web server 8 | - Support for up to 30 simultaneous 2 channel unique NDI audio sources 9 | - Uses the latest version of NDI - NDI 5 10 | - Nearly zero latency 11 | 12 | ## Supported devices 13 | 14 | This software is tested with a Raspberry Pi 4 (32 and 64-bit). This software does not require much CPU power and so other lower power devices could possibly work. 15 | 16 | ### Download required installation files 17 | 18 | Make sure git is installed. 19 | 20 | ``` 21 | sudo apt update 22 | sudo apt install git 23 | ``` 24 | Clone this repository and `cd` into it. 25 | 26 | ``` 27 | git clone https://github.com/windows10luke/NDI-to-JACK.git && cd NDI-to-JACK 28 | ``` 29 | 30 | ### Install on Raspberry Pi 4 64-bit 31 | 32 | Run this compile and install script 33 | 34 | ``` 35 | sudo bash ./easy-install-rpi4-aarch64.sh 36 | ``` 37 | Installation is now complete! 38 | 39 | 40 | ### Install on Raspberry Pi 4 32-bit 41 | 42 | Run this compile and install script 43 | 44 | ``` 45 | sudo bash ./easy-install-rpi4-armhf.sh 46 | ``` 47 | Installation is now complete! 48 | 49 | 50 | ### Install on Raspberry Pi 3 32-bit 51 | 52 | Run this compile and install script 53 | 54 | ``` 55 | sudo bash ./easy-install-rpi3-armhf.sh 56 | ``` 57 | Installation is now complete! 58 | 59 | 60 | 61 | ### Install on x86_64 bit (Intel/AMD) 62 | 63 | Run this compile and install script 64 | 65 | ``` 66 | sudo bash ./easy-install-x86_64.sh 67 | ``` 68 | Installation is now complete! 69 | 70 | 71 | ### Install on generic ARM64 72 | 73 | Compiling on generic ARM64 requires use of the NDI Advanced SDK. Due to licensing restrictions, the NDI Advanced SDK must be downloaded manually from NDI's website: [ndi.tv](https://ndi.tv) 74 | Extract the downloaded NDI Advanced SDK .tar file and copy it to the NDI-to-JACK directory on the target device. This can be achieved by using FTP, SCP, or Samba. 75 | 76 | Compile and install 77 | 78 | ``` 79 | sudo bash ./easy-install-generic-aarch64.sh 80 | ``` 81 | Installation is now complete! 82 | 83 | ### Install on generic ARM32 84 | 85 | Compiling on generic ARM32 requires use of the NDI Advanced SDK. Due to licensing restrictions, the NDI Advanced SDK must be downloaded manually from NDI's website: [ndi.tv](https://ndi.tv) 86 | Extract the downloaded NDI Advanced SDK .tar file and copy it to the NDI-to-JACK directory on the target device. This can be achieved by using FTP, SCP, or Samba. 87 | 88 | Compile and install 89 | 90 | ``` 91 | sudo bash ./easy-install-generic-armhf.sh 92 | ``` 93 | Installation is now complete! 94 | 95 | 96 | ## Usage for NDI to JACK converter 97 | 98 | Once the installation process is complete, it will create an executable file located at /opt/ndi2jack/bin/ndi2jack 99 | 100 | The installer also creates a symlink to /usr/bin so that it can be run from a normal terminal. 101 | 102 | To run and start the web server: 103 | 104 | ``` 105 | sudo ndi2jack 106 | ``` 107 | 108 | ## Usage for JACK to NDI converter 109 | 110 | Once the installation process is complete, it will create an executable file located at /opt/ndi2jack/bin/jack2ndi 111 | 112 | The installer also creates a symlink to /usr/bin so that it can be run from a normal terminal. 113 | 114 | To run (multiple instances can be run with different options for multiple NDI send instances): 115 | 116 | ``` 117 | sudo jack2ndi 118 | ``` 119 | 120 | ## Install service file for starting ndi2jack on boot 121 | 122 | By default this service file runs ndi2jack as the root user with realtime CPU scheduling. This also assumes that JACK is running as a service as the root user. 123 | 124 | ``` 125 | sudo cp ./ndi2jack.service /etc/systemd/system/ 126 | sudo systemctl enable ndi2jack.service 127 | sudo systemctl start ndi2jack.service 128 | ``` 129 | 130 | ## Install service file for starting jack2ndi on boot 131 | 132 | By default this service file runs jack2ndi as the root user with realtime CPU scheduling. This also assumes that JACK is running as a service as the root user. 133 | 134 | ``` 135 | sudo cp ./jack2ndi.service /etc/systemd/system/ 136 | sudo systemctl enable jack2ndi.service 137 | sudo systemctl start jack2ndi.service 138 | ``` 139 | 140 | ## To upgrade, run the following commands 141 | 142 | ``` 143 | sudo service jackd stop 144 | sudo rm -R NDI-to-JACK 145 | git clone https://github.com/windows10luke/NDI-to-JACK.git && cd NDI-to-JACK 146 | ``` 147 | Run the easy install for the CPU type it is running on (e.g. `sudo bash ./easy-install-rpi4-aarch64.sh`). Reboot when finished 148 | 149 | ## Helpful links 150 | 151 | https://wiki.linuxaudio.org/wiki/list_of_jack_frame_period_settings_ideal_for_usb_interface 152 | -------------------------------------------------------------------------------- /assets/main.css: -------------------------------------------------------------------------------- 1 | /* loader code start */ 2 | #loader { 3 | position: absolute; 4 | left: 50%; 5 | top: 50%; 6 | z-index: 1; 7 | width: 150px; 8 | height: 150px; 9 | margin: -75px 0 0 -75px; 10 | border: 16px solid #f3f3f3; 11 | border-radius: 50%; 12 | border-top: 16px solid #1f6feb; 13 | width: 120px; 14 | height: 120px; 15 | -webkit-animation: spin 2s linear infinite; 16 | animation: spin 2s linear infinite; 17 | } 18 | 19 | @-webkit-keyframes spin { 20 | 0% { -webkit-transform: rotate(0deg); } 21 | 100% { -webkit-transform: rotate(360deg); } 22 | } 23 | 24 | @keyframes spin { 25 | 0% { transform: rotate(0deg); } 26 | 100% { transform: rotate(360deg); } 27 | } 28 | 29 | html, body { 30 | margin:0; 31 | height:100%; 32 | min-height:100%; 33 | } 34 | 35 | body { 36 | margin:0; 37 | display: flex; 38 | flex-direction: column; 39 | font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; 40 | font-size: 14px; 41 | } 42 | .templateContainer { 43 | display: flex; 44 | flex-direction: column; 45 | overflow-y: auto; 46 | overflow-x: hidden; 47 | height: 100% 48 | } 49 | 50 | .navbarTopContainer { 51 | display: flex; 52 | flex-direction: row; 53 | justify-content: space-between; 54 | min-height: 40px; 55 | box-shadow: 0px 1px 10px rgb(0,0,0); 56 | background: #161b22; /* Default color if no customization value is provided */ 57 | } 58 | 59 | .navbarTopBrand { 60 | padding: 10px 12px; 61 | color: #f0f6fc; 62 | background-color: #1f6feb; 63 | cursor: pointer; 64 | } 65 | 66 | .navbarTopContainer > .leftContainer { 67 | display: flex; 68 | flex-direction: row; 69 | } 70 | .navbarTopContainer > .rightContainer { 71 | display: flex; 72 | flex-direction: row; 73 | } 74 | 75 | .header-link { 76 | font-weight: 600; 77 | color: #f0f6fc; 78 | white-space: nowrap; 79 | padding: 10px 12px; 80 | cursor: pointer; 81 | } 82 | 83 | .totalAppContainer { 84 | display: flex; 85 | flex-direction: row; 86 | overflow-y: auto; 87 | overflow-x: hidden; 88 | height: 100%; 89 | } 90 | 91 | .navbar.left, .navbar.right { 92 | overflow-y: auto; /*this makes content scrollable if content goes beyond bottom of window size*/ 93 | overflow-x: hidden; 94 | background: #0d1117; /* Default color if no customization value is provided */ 95 | } 96 | 97 | .navbar-header { 98 | display: flex; 99 | font-size: 14px; 100 | padding: 10px; 101 | align-items: center; 102 | justify-content: center; 103 | color: #ffffff; 104 | background-color: #111c41; 105 | border: 1px solid #30363d; 106 | } 107 | 108 | .navbar.left { 109 | z-index: 5; 110 | display: block; 111 | left: 0; 112 | width: 200px; 113 | height: 100%; 114 | } 115 | 116 | .navbar.right { 117 | z-index: 4; 118 | display: block; 119 | right: 0; 120 | max-width: 120px; 121 | } 122 | 123 | .leftnav { 124 | flex: 0 0 auto; 125 | order: 0; 126 | overflow-y: auto; 127 | } 128 | 129 | .border-right { 130 | border-right: 1px solid #30363d; 131 | } 132 | 133 | .border-left { 134 | border-left: 1px solid #30363d; 135 | } 136 | /* Container where the app is rendered */ 137 | .appContainer { 138 | width: 100%; 139 | background: #010409; 140 | overflow-y: auto; 141 | overflow-x: hidden; 142 | } 143 | 144 | .dropdown-menu-sw { 145 | right: 0; 146 | left: auto; 147 | } 148 | 149 | .dropdown-menu { 150 | position: absolute; 151 | z-index: 100; 152 | width: 160px; 153 | padding-top: 4px; 154 | padding-bottom: 4px; 155 | margin-top: 2px; 156 | list-style: none; 157 | background-color: #161b22; 158 | background-clip: padding-box; 159 | border: 1px solid #30363d; 160 | border-radius: 6px; 161 | display: none; 162 | } 163 | 164 | .dropdown-item { 165 | display: block; 166 | padding: 4px 8px 4px 16px; 167 | overflow: hidden; 168 | color: #c9d1d9; 169 | text-overflow: ellipsis; 170 | white-space: nowrap; 171 | text-decoration: none; 172 | cursor: pointer; 173 | } 174 | 175 | .info-container { 176 | display: flex; 177 | flex-direction: row; 178 | flex-wrap: wrap; 179 | align-content: center; 180 | justify-content: center; 181 | align-items: center; 182 | } 183 | 184 | .d-box { 185 | display: flex; 186 | flex-direction: column; 187 | padding: 32px; 188 | margin: 10px; 189 | background-color: #161b22; 190 | background-clip: padding-box; 191 | border: 1px solid #30363d; 192 | border-radius: 6px; 193 | } 194 | 195 | .d-box > .header { 196 | display: flex; 197 | font-size: 14px; 198 | align-items: center; 199 | justify-content: space-between; 200 | color: #c9d1d9; 201 | } 202 | 203 | .d-box-container { 204 | display: flex; 205 | flex-direction: row; 206 | } 207 | 208 | .form-control { 209 | background-color: #010409; 210 | padding: 5px 12px; 211 | font-size: 14px; 212 | line-height: 20px; 213 | vertical-align: middle; 214 | color: #c9d1d9; 215 | border: 1px solid #30363d; 216 | border-radius: 6px; 217 | margin-bottom: 7px; 218 | } 219 | 220 | .button-primary { 221 | padding: 3px 12px; 222 | font-size: 12px; 223 | color: #ffffff; 224 | background-color: #1f6feb; 225 | cursor: pointer; 226 | border: 1px solid; 227 | border-radius: 6px; 228 | } 229 | 230 | .button-big { 231 | display: flex; 232 | font-size: 14px; 233 | align-items: center; 234 | justify-content: center; 235 | 236 | padding: 20px 0px; 237 | margin: 15px 5px; 238 | font-size: 16px; 239 | color: #ffffff; 240 | background-color: #161b22; 241 | cursor: pointer; 242 | border: 1px solid #30363d; 243 | border-radius: 6px; 244 | } 245 | 246 | .button-big.selected { 247 | background-color: #7e0a0a; 248 | } 249 | 250 | label { 251 | padding: 5px 3px; 252 | font-size: 14px; 253 | color: #c9d1d9; 254 | } -------------------------------------------------------------------------------- /assets/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 |