Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0a6a9ce3f | ||
| 87f4d71cc9 | |||
|
|
53a047217a | ||
|
|
3dbd19ee3b | ||
| c0415561ee | |||
|
|
7b9143c26d | ||
|
|
a21db87b98 | ||
| f1c5792a12 | |||
| d163160eb6 | |||
|
|
1c7e793fb2 | ||
|
|
cb1d34fd09 | ||
| 8a212a5044 | |||
| 09473f627b | |||
|
|
fbfd23d335 | ||
|
|
0ccbd9dd0b | ||
| b79c2b1e42 | |||
|
|
f3f788ea51 | ||
|
|
cd5417d1d1 | ||
| f3e8432452 | |||
| 1eae3a8bbf | |||
|
|
dd1497c432 | ||
|
|
867883b1e7 | ||
| af04783685 | |||
| 9a9aa95cd1 | |||
| 9e32ed95d1 | |||
|
|
8089bbeebd |
BIN
.github/media/preview.gif
vendored
BIN
.github/media/preview.gif
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.6 MiB |
27
.github/workflows/ci.yaml
vendored
27
.github/workflows/ci.yaml
vendored
@@ -43,7 +43,14 @@ jobs:
|
||||
name: Version
|
||||
run: echo "VERSION=$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout)" >> ${GITHUB_OUTPUT}
|
||||
|
||||
- name: Build Container
|
||||
- name: Build unstable Container
|
||||
if: github.ref_name != 'main'
|
||||
run: |
|
||||
echo ${{secrets.PACKAGES_TOKEN}} | docker login --username ${{ secrets.PACKAGES_USER }} --password-stdin git.arindy.de
|
||||
docker build -f src/main/docker/Dockerfile.native-micro -t git.arindy.de/arindy/dice-tower:unstable -t git.arindy.de/arindy/dice-tower:${{ steps.version.outputs.VERSION }} .
|
||||
docker push git.arindy.de/arindy/dice-tower:unstable
|
||||
|
||||
- name: Build stable Container
|
||||
if: github.ref_name == 'main'
|
||||
run: |
|
||||
echo ${{secrets.PACKAGES_TOKEN}} | docker login --username ${{ secrets.PACKAGES_USER }} --password-stdin git.arindy.de
|
||||
@@ -51,10 +58,28 @@ jobs:
|
||||
docker push git.arindy.de/arindy/dice-tower:${{ steps.version.outputs.VERSION }}
|
||||
docker push git.arindy.de/arindy/dice-tower:latest
|
||||
|
||||
- name: Prepare deploy
|
||||
run: |
|
||||
echo ${{secrets.SSH_KNOWN_HOSTS}} >> ~/.ssh/known_hosts
|
||||
base64 -d <<< ${{secrets.SSH_KEY}} > ./.key
|
||||
chmod 600 ./.key
|
||||
|
||||
- name: Deploy unstable
|
||||
if: github.ref_name != 'main'
|
||||
run: "ssh -i ./.key dice-tower@${{secrets.SSH_HOST}} 'docker compose -f compose.unstable.yml pull && docker compose -f compose.unstable.yml up -d' "
|
||||
|
||||
- name: Deploy
|
||||
if: github.ref_name == 'main'
|
||||
run: "ssh -i ./.key dice-tower@${{secrets.SSH_HOST}} 'docker compose -f compose.yml pull && docker compose -f compose.yml up -d' "
|
||||
|
||||
- name: Deploy local
|
||||
if: github.ref_name == 'main'
|
||||
run: "docker compose up -d"
|
||||
|
||||
- name: clean up
|
||||
run: |
|
||||
rm ./.key
|
||||
|
||||
- name: create tag
|
||||
if: github.ref_name == 'main'
|
||||
run: |
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -124,3 +124,5 @@ fabric.properties
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
.key
|
||||
.key/
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.arindy</groupId>
|
||||
<artifactId>dice-tower</artifactId>
|
||||
<version>1.0.6</version>
|
||||
<version>1.1.5</version>
|
||||
|
||||
<properties>
|
||||
<compiler-plugin.version>3.13.0</compiler-plugin.version>
|
||||
|
||||
40
src/main/kotlin/de/arindy/dicetower/ChatOverlayResource.kt
Normal file
40
src/main/kotlin/de/arindy/dicetower/ChatOverlayResource.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
package de.arindy.dicetower
|
||||
|
||||
import io.quarkus.qute.TemplateInstance
|
||||
import jakarta.ws.rs.GET
|
||||
import jakarta.ws.rs.Path
|
||||
import jakarta.ws.rs.PathParam
|
||||
import jakarta.ws.rs.Produces
|
||||
import jakarta.ws.rs.QueryParam
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
|
||||
@Path("chatoverlay")
|
||||
class ChatOverlayResource {
|
||||
|
||||
@GET
|
||||
@Path("/{channel}")
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
fun get(
|
||||
@PathParam("channel") channel: String,
|
||||
@QueryParam("scale") scale: Int? = 7,
|
||||
@QueryParam("maxDice") maxDice: Int? = 20,
|
||||
@QueryParam("modsAllowed") modsAllowed: Boolean = false,
|
||||
@QueryParam("vipAllowed") vipAllowed: Boolean = false,
|
||||
@QueryParam("subsAllowed") subsAllowed: Boolean = false,
|
||||
@QueryParam("cmd") cmd: String? = "roll",
|
||||
@QueryParam("theme") theme: String? = "default",
|
||||
@QueryParam("themeColor") themeColor: String? = "default",
|
||||
@QueryParam("clearAfter") clearAfter: Long? = -1,
|
||||
@QueryParam("timeout") timeout: Long? = -1
|
||||
): TemplateInstance {
|
||||
return Templates.chatoverlay(channel, scale ?: 7, maxDice ?: 20, modsAllowed, vipAllowed, subsAllowed, cmd ?: "roll", theme ?: "default", themeColor ?: "ff0202", clearAfter ?: 10, timeout ?: 60)
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
fun config(
|
||||
): TemplateInstance {
|
||||
return Templates.chatoverlayconfig()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +1,12 @@
|
||||
package de.arindy.dicetower
|
||||
|
||||
import io.quarkus.qute.TemplateInstance
|
||||
import io.quarkus.runtime.annotations.RegisterForReflection
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.ws.rs.Consumes
|
||||
import jakarta.ws.rs.GET
|
||||
import jakarta.ws.rs.POST
|
||||
import jakarta.ws.rs.Path
|
||||
import jakarta.ws.rs.PathParam
|
||||
import jakarta.ws.rs.Produces
|
||||
import jakarta.ws.rs.QueryParam
|
||||
import jakarta.ws.rs.core.Context
|
||||
import jakarta.ws.rs.core.MediaType
|
||||
import jakarta.ws.rs.sse.OutboundSseEvent
|
||||
import jakarta.ws.rs.sse.Sse
|
||||
import jakarta.ws.rs.sse.SseBroadcaster
|
||||
import jakarta.ws.rs.sse.SseEventSink
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty
|
||||
import java.util.UUID
|
||||
|
||||
@Path("/")
|
||||
@ApplicationScoped
|
||||
|
||||
@@ -7,8 +7,28 @@ import io.quarkus.qute.TemplateInstance
|
||||
object Templates {
|
||||
@JvmStatic
|
||||
external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun results(room: String, name: String?, user: String?): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun index(version: String): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun chatoverlayconfig(): TemplateInstance
|
||||
|
||||
@JvmStatic
|
||||
external fun chatoverlay(
|
||||
channel: String,
|
||||
scale: Int?,
|
||||
maxDice: Int?,
|
||||
modsAllowed: Boolean,
|
||||
vipAllowed: Boolean,
|
||||
subsAllowed: Boolean,
|
||||
cmd: String?,
|
||||
theme: String?,
|
||||
themeColor: String?,
|
||||
clearAfter: Long?,
|
||||
timeout: Long?
|
||||
): TemplateInstance
|
||||
}
|
||||
|
||||
186
src/main/resources/META-INF/resources/app.js
Normal file
186
src/main/resources/META-INF/resources/app.js
Normal file
@@ -0,0 +1,186 @@
|
||||
function addDice() {
|
||||
let amount = +document.getElementById('dice-amount').innerText
|
||||
document.getElementById('dice-amount').innerText = `${amount + 1}`
|
||||
}
|
||||
|
||||
function removeDice() {
|
||||
let amount = +document.getElementById('dice-amount').innerText
|
||||
if (amount > 1) {
|
||||
document.getElementById('dice-amount').innerText = `${amount - 1}`
|
||||
}
|
||||
}
|
||||
|
||||
function url() {
|
||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
function start(event = undefined) {
|
||||
if ((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
|
||||
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=7&clearAfter=30';
|
||||
document.getElementById('resultsId').value = url() + '/overlay/' + document.getElementById('room').value + '/results';
|
||||
document.getElementById('myResultsId').value = document.getElementById('resultsId').value + '?name=' + encodeURIComponent(document.getElementById('name').value) + '&user=' + localStorage.getItem('userId');
|
||||
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
|
||||
document.getElementById('roll').hidden = false;
|
||||
document.getElementById('start-container').hidden = true;
|
||||
document.getElementById('options-container').hidden = false;
|
||||
document.getElementById('dice-tower').hidden = false;
|
||||
document.getElementById('name').hidden = true;
|
||||
document.getElementById('room').hidden = true;
|
||||
document.getElementById('how-to').hidden = true;
|
||||
document.getElementById('chatOverlay').hidden = true;
|
||||
document.getElementById('results').hidden = false;
|
||||
document.getElementById('all-results').hidden = !document.getElementById('gm').checked;
|
||||
document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none';
|
||||
document.getElementById('nameH').innerHTML = '<strong style="font-size:x-large;">' + document.getElementById('name').value + '</strong>';
|
||||
document.getElementById('save-dice-hint-name').innerHTML = '<strong>' + document.getElementById('name').value + '</strong>';
|
||||
document.getElementById('roomLabel').hidden = true;
|
||||
document.getElementById('nameLabel').hidden = true;
|
||||
document.getElementById('nameH').hidden = false;
|
||||
document.getElementById('room-hint').innerHTML = '<p>Room: <strong style="font-size:medium;">' + document.getElementById('room').value + '</strong></p>';
|
||||
document.getElementById('overlayLabel').innerHTML = 'Dice-Overlay for <strong>' + document.getElementById('name').value + '</strong>';
|
||||
document.title = document.getElementById('name').value + ' - Dice-Tower';
|
||||
|
||||
localStorage.setItem('last-name', document.getElementById('name').value)
|
||||
localStorage.setItem('last-room', document.getElementById('room').value)
|
||||
localStorage.setItem('last-gm', document.getElementById('gm').checked)
|
||||
|
||||
if (localStorage.getItem(document.getElementById('name').value + "-theme")) {
|
||||
document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme")
|
||||
}
|
||||
if (localStorage.getItem(document.getElementById('name').value + "-themeColor")) {
|
||||
document.getElementById('themeColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeColor"));
|
||||
}
|
||||
|
||||
if (!localStorage.getItem(document.getElementById('name').value + '-started')) {
|
||||
document.getElementById('urls-overlay').showPopover();
|
||||
}
|
||||
|
||||
localStorage.setItem(document.getElementById('name').value + '-started', "true")
|
||||
|
||||
let httpRequest = new XMLHttpRequest();
|
||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register')
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||
httpRequest.send(JSON.stringify({
|
||||
name: document.getElementById('name').value,
|
||||
overlay: document.getElementById('overlayId').value,
|
||||
id: document.getElementById('room').value + ':' + localStorage.getItem('userId')
|
||||
}))
|
||||
if (document.getElementById('gm').checked) {
|
||||
document.getElementById('resultSwitch').checked = true;
|
||||
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
||||
const evtSource = new EventSource(url() + '/dice/' + document.getElementById('room').value + '/users');
|
||||
evtSource.addEventListener('message', function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
if (data.id !== document.getElementById('room').value + ':' + localStorage.getItem('userId')) {
|
||||
let overlays = document.getElementById('overlay-urls');
|
||||
let newOverlay = document.getElementById(data.id) ?? document.createElement('div');
|
||||
newOverlay.replaceChildren(...[]);
|
||||
newOverlay.id = data.id;
|
||||
newOverlay.style.display = "flex";
|
||||
newOverlay.style.flexDirection = "row";
|
||||
newOverlay.style.justifyContent = "space-between";
|
||||
newOverlay.style.alignItems = "baseline";
|
||||
let newLabel = document.createElement('label');
|
||||
newLabel.for = data.id + 'url';
|
||||
newLabel.innerHTML = "Dice-Overlay for <strong>" + data.name + "</strong>";
|
||||
let newInput = document.getElementById('overlayId').cloneNode();
|
||||
newInput.type = "text";
|
||||
newInput.readOnly = true;
|
||||
newInput.id = data.id + 'url';
|
||||
newInput.style.flexGrow = '1';
|
||||
newInput.value = data.overlay;
|
||||
newInput.onclick = () => copyToClipboard(newInput.id)
|
||||
newOverlay.appendChild(newLabel);
|
||||
newOverlay.appendChild(newInput);
|
||||
overlays.appendChild(newOverlay);
|
||||
}
|
||||
configurePopover();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rollEasy(dice) {
|
||||
document.getElementById('command').value = document.getElementById('dice-amount').innerText + dice;
|
||||
roll();
|
||||
}
|
||||
|
||||
function roll(event) {
|
||||
if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) {
|
||||
let httpRequest = new XMLHttpRequest();
|
||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`))
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||
httpRequest.send(JSON.stringify({
|
||||
name: document.getElementById('name').value,
|
||||
command: document.getElementById('command').value,
|
||||
themeColor: document.getElementById('themeColor').value,
|
||||
theme: document.getElementById('theme').value
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
function saveDice() {
|
||||
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
|
||||
localStorage.setItem(document.getElementById('name').value + "-themeColor", document.getElementById('themeColor').value)
|
||||
}
|
||||
|
||||
function configurePopover() {
|
||||
|
||||
const popover = document.querySelectorAll("[popovertarget][data-trigger='hover']");
|
||||
popover.forEach((e) => {
|
||||
|
||||
const target = document.querySelector("#" + e.getAttribute("popovertarget"));
|
||||
e.addEventListener("mouseover", () => {
|
||||
showSnackbar(target.innerHTML);
|
||||
});
|
||||
e.addEventListener("mouseout", () => {
|
||||
hideSnackbar();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function copyToClipboard(id) {
|
||||
let copyText = document.getElementById(id);
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
navigator.clipboard.writeText(copyText.value).then(() => {
|
||||
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
|
||||
})
|
||||
}
|
||||
|
||||
function showSnackbar(message) {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.innerHTML = message;
|
||||
snackbar.className = "show";
|
||||
snackbarContainer.className = "show";
|
||||
}
|
||||
|
||||
function hideSnackbar() {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.className = snackbar.className.replace("show", "");
|
||||
snackbarContainer.className = snackbarContainer.className.replace("show", "");
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) {
|
||||
document.getElementById('name').value = localStorage.getItem('last-name');
|
||||
document.getElementById('room').value = localStorage.getItem('last-room');
|
||||
document.getElementById('gm').checked = localStorage.getItem('last-gm') === 'true';
|
||||
}
|
||||
|
||||
document.getElementById('resultSwitch').addEventListener('change', function () {
|
||||
if (!this.checked) {
|
||||
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
|
||||
} else {
|
||||
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
||||
}
|
||||
})
|
||||
|
||||
document.getElementById('chatOverlayLink').href = url() + '/chatoverlay'
|
||||
|
||||
configurePopover();
|
||||
})
|
||||
|
||||
15
src/main/resources/META-INF/resources/dice-preview.js
Normal file
15
src/main/resources/META-INF/resources/dice-preview.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
document.getElementById('preview').onclick = async () => {
|
||||
document.getElementById('dice-box').replaceChildren(...[])
|
||||
const diceBox = new DiceBox("#dice-box", {
|
||||
assetPath: "/vendor/assets/",
|
||||
theme: document.getElementById('theme').value,
|
||||
themeColor: document.getElementById('themeColor').value,
|
||||
scale: 14
|
||||
});
|
||||
await diceBox.init()
|
||||
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
|
||||
}
|
||||
})
|
||||
36
src/main/resources/META-INF/resources/overlay/style.css
Normal file
36
src/main/resources/META-INF/resources/overlay/style.css
Normal file
@@ -0,0 +1,36 @@
|
||||
html,
|
||||
body {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
#dice-box {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#dice-box canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
border-radius: 0.5rem;
|
||||
padding: 15px;
|
||||
max-width: 80%;
|
||||
font-size: 400%;
|
||||
color: #fff; !important;
|
||||
background-color: #333333dd; !important
|
||||
}
|
||||
BIN
src/main/resources/META-INF/resources/rich.png
Normal file
BIN
src/main/resources/META-INF/resources/rich.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 206 KiB |
209
src/main/resources/META-INF/resources/style.css
Normal file
209
src/main/resources/META-INF/resources/style.css
Normal file
@@ -0,0 +1,209 @@
|
||||
.w3-theme-l6 {
|
||||
color: #000 !important;
|
||||
background-color: #999999 !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.w3-theme-l4 {
|
||||
color: #fff !important;
|
||||
background-color: #666666 !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.w3-theme-l1 {
|
||||
color: #fff !important;
|
||||
background-color: #333333 !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px;
|
||||
border: #333333 3px solid;
|
||||
border-radius: 10px;
|
||||
background: #333333;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* The switch - the box around the slider */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #333333;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
border-radius: 0.5rem;
|
||||
padding: 15px;
|
||||
color: #fff !important;
|
||||
background-color: #333333dd !important
|
||||
}
|
||||
|
||||
#dice-box {
|
||||
position: relative;
|
||||
justify-self: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#dice-box canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
position: relative;
|
||||
padding-left: 50px;
|
||||
margin-bottom: 12px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.checkbox input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
margin-left: 25px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.checkbox:hover input ~ .checkmark {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
/* When the checkbox is checked, add a blue background */
|
||||
.checkbox input:checked ~ .checkmark {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
/* Create the checkmark/indicator (hidden when not checked) */
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show the checkmark when checked */
|
||||
.checkbox input:checked ~ .checkmark:after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Style the checkmark/indicator */
|
||||
.checkbox .checkmark:after {
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 3px 3px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
#snackbar-container {
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
bottom: 10%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#snackbar {
|
||||
visibility: hidden;
|
||||
text-align: center;
|
||||
border-radius: 0.5rem;
|
||||
padding: 15px;
|
||||
color: #fff !important;
|
||||
border: #fff 5px solid;
|
||||
background-color: #333333dd !important
|
||||
}
|
||||
|
||||
#snackbar-container.show {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#snackbar.show {
|
||||
visibility: visible;
|
||||
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
}
|
||||
1
src/main/resources/META-INF/resources/vendor/comfy.js/comfy.min.js
vendored
Normal file
1
src/main/resources/META-INF/resources/vendor/comfy.js/comfy.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
70
src/main/resources/templates/chatoverlay.html
Normal file
70
src/main/resources/templates/chatoverlay.html
Normal file
@@ -0,0 +1,70 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dice-Tower - Overlay</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<link rel="stylesheet" href="/overlay/style.css">
|
||||
<script src="/vendor/comfy.js/comfy.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="dice-box"></div>
|
||||
<div popover id="results" class="tooltip">
|
||||
</div>
|
||||
<script type="module">
|
||||
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||
|
||||
const diceBox = new DiceBox("#dice-box", {
|
||||
assetPath: "/vendor/assets/",
|
||||
theme: '{theme}',
|
||||
themeColor: '{themeColor}',
|
||||
scale: {scale}
|
||||
});
|
||||
diceBox.init()
|
||||
ComfyJS.Init('{channel}');
|
||||
|
||||
//maxDice
|
||||
|
||||
ComfyJS.onCommand = async (user, command, message, flags) => {
|
||||
if ((
|
||||
flags.broadcaster || ({modsAllowed} && flags.mod) || ({vipAllowed} && flags.vip) || ({subsAllowed} && flags.subscriber
|
||||
)) && '{cmd}' === command && !shouldWait() && message.match(/^\d+d(4|6|8|10|12|20|100)$/) && (+message.split('d')[0] <= {maxDice})
|
||||
) {
|
||||
toggleWait(true);
|
||||
|
||||
diceBox.onRollComplete = (rollResult) => {
|
||||
rollResult.forEach(result => {
|
||||
let values = []
|
||||
result.rolls.forEach(roll => {
|
||||
values.push(roll.value);
|
||||
})
|
||||
document.getElementById('results').innerHTML = '<strong>' + user + '</strong> rolls <strong>' + message + '</strong>:<br/> [' + values.map(value => value === 1 ? '<strong style="text-shadow: 2px 2px 10px red">' + value + '</strong>' : value === result.sides ? '<strong style="text-shadow: 2px 2px 10px green">' + value + '</strong>' : value).join(' + ') + (result.modifier > 0 ? ' <a style="text-decoration: underline">+' + result.modifier + '</a>' : result.modifier < 0 ? ' <a style="text-decoration: underline">' + result.modifier + '</a>' : '') + '] = <strong>' + result.value + '</strong> '
|
||||
})
|
||||
document.getElementById('results').showPopover()
|
||||
setTimeout(() => {
|
||||
diceBox.clear();
|
||||
document.getElementById('results').hidePopover()
|
||||
}, {clearAfter} * 1000)
|
||||
}
|
||||
|
||||
diceBox.roll(message);
|
||||
setTimeout(() => {
|
||||
toggleWait(false);
|
||||
}, {clearAfter} * 1000 + {timeout} * 1000)
|
||||
} else {
|
||||
console.log('Not a valid command or not ready yet');
|
||||
}
|
||||
};
|
||||
|
||||
let wait;
|
||||
|
||||
function shouldWait() {
|
||||
return wait;
|
||||
}
|
||||
|
||||
function toggleWait(value) {
|
||||
wait = value;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
142
src/main/resources/templates/chatoverlayconfig.html
Normal file
142
src/main/resources/templates/chatoverlayconfig.html
Normal file
@@ -0,0 +1,142 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dice-Tower - Chatoverlay</title>
|
||||
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<script src="/vendor/color-picker.js"></script>
|
||||
</head>
|
||||
<body class="w3-theme-l1">
|
||||
|
||||
<div class="w3-container w3-content"
|
||||
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
|
||||
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower - Chatoverlay <i
|
||||
class="fa-solid fa-dice-d20"></i></h1>
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; text-align: center;">
|
||||
<p>Allows Chat to roll a given amount of one dice</p>
|
||||
<p>Example: "!roll 5d20"</p>
|
||||
</div>
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
||||
style="padding: 25px; text-align: center; margin-bottom: auto;">
|
||||
<label for="theme">Theme </label>
|
||||
<select name="theme" id="theme" style="margin: 25px">
|
||||
<option value="default">Default</option>
|
||||
<option value="blueGreenMetal">Blue-Green Metal</option>
|
||||
<option value="diceOfRolling">Dice of Rolling</option>
|
||||
<option value="gemstone">Gemstone</option>
|
||||
<option value="gemstoneMarble">Marble Gemstone</option>
|
||||
<option value="rock">Rock</option>
|
||||
<option value="rust">Rust</option>
|
||||
<option value="smooth">Smooth</option>
|
||||
<option value="wooden">Wooden</option>
|
||||
</select>
|
||||
<color-picker id="themeColor"></color-picker>
|
||||
<div>
|
||||
<label for="channel">Channel </label>
|
||||
<input type="text" id="channel" style="width: 400px; margin-top: 20px" value="arindy"/>
|
||||
<label for="cmd">Command !</label>
|
||||
<input type="text" id="cmd" style="width: 100px; margin-top: 20px; margin-left: 0" value="roll"/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="checkbox" id="modsAllowed-container">Allow mods to roll
|
||||
<input type="checkbox" id="modsAllowed">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="checkbox" id="vipAllowed-container">Allow VIPs to roll
|
||||
<input type="checkbox" id="vipAllowed">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<label class="checkbox" id="subsAllowed-container">Allow subs to roll
|
||||
<input type="checkbox" id="subsAllowed">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="scale">Dice-Scale </label>
|
||||
<input type="number" id="scale" style="width: 50px; margin-top: 20px" value="9"/>
|
||||
<label for="maxDice">Max number of dice </label>
|
||||
<input type="number" id="maxDice" style="width: 50px; margin-top: 20px" value="20"/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="clearAfter">Clear dice after (in seconds)</label>
|
||||
<input type="number" id="clearAfter" style="width: 50px; margin-top: 20px" value="10"/>
|
||||
<label for="timeout">Command-timeout (in seconds)</label>
|
||||
<input type="number" id="timeout" style="width: 50px; margin-top: 20px" value="60"/>
|
||||
</div>
|
||||
<div id="dice-box" style="height: 400px"></div>
|
||||
<button style="margin: 10px" id="preview">Preview <i class="fa-solid fa-magnifying-glass"></i></button>
|
||||
<button style="margin: 10px" id="generate">Generate overlay-link <i class="fa-solid fa-link"></i></button>
|
||||
<div>
|
||||
<input type="text" readonly id="link" style="width: 80%; margin-top: 20px"
|
||||
onclick="copyToClipboard(this.id)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="snackbar-container">
|
||||
<div id="snackbar">.. they see them rolling</div>
|
||||
</div>
|
||||
<script>
|
||||
function copyToClipboard(id) {
|
||||
let copyText = document.getElementById(id)
|
||||
if (copyText.value.length > 0) {
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
navigator.clipboard.writeText(copyText.value).then(() => {
|
||||
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function showSnackbar(message) {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.innerHTML = message;
|
||||
snackbar.className = "show";
|
||||
snackbarContainer.className = "show";
|
||||
setTimeout(() => {
|
||||
snackbar.className = "";
|
||||
snackbarContainer.className = "";
|
||||
}, 3000)
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||
|
||||
function url() {
|
||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
document.getElementById('preview').onclick = async () => {
|
||||
document.getElementById('dice-box').replaceChildren(...[])
|
||||
const diceBox = new DiceBox("#dice-box", {
|
||||
assetPath: "/vendor/assets/",
|
||||
theme: document.getElementById('theme').value,
|
||||
themeColor: document.getElementById('themeColor').value,
|
||||
scale: +document.getElementById('scale').value
|
||||
});
|
||||
await diceBox.init()
|
||||
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
|
||||
}
|
||||
document.getElementById('generate').onclick = async () => {
|
||||
document.getElementById('link').value = url() +
|
||||
"/chatoverlay/" + document.getElementById('channel').value +
|
||||
"?cmd=" + document.getElementById('cmd').value +
|
||||
"&theme=" + document.getElementById('theme').value +
|
||||
"&themeColor=" + encodeURIComponent(document.getElementById('themeColor').value) +
|
||||
"&scale=" + document.getElementById('scale').value +
|
||||
"&maxDice=" + document.getElementById('maxDice').value +
|
||||
"&clearAfter=" + document.getElementById('clearAfter').value +
|
||||
"&timeout=" + document.getElementById('timeout').value +
|
||||
"&modsAllowed=" + document.getElementById('modsAllowed').checked +
|
||||
"&vipAllowed=" + document.getElementById('vipAllowed').checked +
|
||||
"&subsAllowed=" + document.getElementById('subsAllowed').checked
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,243 +3,42 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Dice-Tower</title>
|
||||
<meta name="version" content="{version}">
|
||||
|
||||
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
|
||||
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
|
||||
<link rel="stylesheet" href="/style.css">
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<script src="/vendor/color-picker.js"></script>
|
||||
<style>
|
||||
.w3-theme-l6 {
|
||||
color: #000 !important;
|
||||
background-color: #999999 !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
<script type="module" src="/dice-preview.js"></script>
|
||||
<script type="text/javascript" src="/app.js"></script>
|
||||
|
||||
.w3-theme-l4 {
|
||||
color: #fff !important;
|
||||
background-color: #666666 !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.w3-theme-l1 {
|
||||
color: #fff !important;
|
||||
background-color: #333333 !important;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px;
|
||||
border: #333333 3px solid;
|
||||
border-radius: 10px;
|
||||
background: #333333;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: #444444;
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: #222222;
|
||||
}
|
||||
|
||||
input {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
/* The switch - the box around the slider */
|
||||
.switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
/* Hide default HTML checkbox */
|
||||
.switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
/* The slider */
|
||||
.slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #ccc;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
.slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 26px;
|
||||
left: 4px;
|
||||
bottom: 4px;
|
||||
background-color: white;
|
||||
-webkit-transition: .4s;
|
||||
transition: .4s;
|
||||
}
|
||||
|
||||
input:checked + .slider {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
input:focus + .slider {
|
||||
box-shadow: 0 0 1px #333333;
|
||||
}
|
||||
|
||||
input:checked + .slider:before {
|
||||
-webkit-transform: translateX(26px);
|
||||
-ms-transform: translateX(26px);
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.overlayButton {
|
||||
background: transparent;
|
||||
border: none;
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
font-size: large;
|
||||
color: #000
|
||||
}
|
||||
|
||||
|
||||
.overlayButton:hover {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
position: fixed;
|
||||
border-radius: 0.5rem;
|
||||
padding: 15px;
|
||||
color: #fff !important;
|
||||
background-color: #333333dd !important
|
||||
}
|
||||
|
||||
#dice-box {
|
||||
position: relative;
|
||||
justify-self: center;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#dice-box canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
position: relative;
|
||||
padding-left: 50px;
|
||||
margin-bottom: 12px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.checkbox input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.checkmark {
|
||||
margin-left: 25px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.checkbox:hover input ~ .checkmark {
|
||||
background-color: #ccc;
|
||||
}
|
||||
|
||||
/* When the checkbox is checked, add a blue background */
|
||||
.checkbox input:checked ~ .checkmark {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
/* Create the checkmark/indicator (hidden when not checked) */
|
||||
.checkmark:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show the checkmark when checked */
|
||||
.checkbox input:checked ~ .checkmark:after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Style the checkmark/indicator */
|
||||
.checkbox .checkmark:after {
|
||||
left: 5px;
|
||||
top: 2px;
|
||||
width: 5px;
|
||||
height: 10px;
|
||||
border: solid white;
|
||||
border-width: 0 3px 3px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
#snackbar-container {
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
bottom: 200px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#snackbar {
|
||||
visibility: hidden;
|
||||
text-align: center;
|
||||
border-radius: 0.5rem;
|
||||
padding: 15px;
|
||||
color: #fff !important;
|
||||
border: #fff 5px solid;
|
||||
background-color: #333333dd !important
|
||||
}
|
||||
|
||||
#snackbar-container.show {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
#snackbar.show {
|
||||
visibility: visible;
|
||||
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
animation: fadein 0.5s, fadeout 0.5s 2.5s;
|
||||
}
|
||||
|
||||
</style>
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Dice-Tower">
|
||||
<meta name="twitter:image" content="{http:request.scheme}://{http:request.authority}/rich.png">
|
||||
<meta name="description" content="Easy to use online dice rolling with customizable overlays.">
|
||||
<meta name="twitter:description" content="Easy to use online dice rolling with customizable overlays.">
|
||||
<meta property="og:url" content="{http:request.absoluteURI}">
|
||||
<meta property="og:image" content="{http:request.scheme}://{http:request.authority}/rich.png">
|
||||
<meta property="og:description" content="Easy to use online dice rolling with customizable overlays.">
|
||||
<meta property="og:title" content="Dice-Tower">
|
||||
<meta property="og:site_name" content="Dice-Tower">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="twitter:url" content="{http:request.absoluteURI}">
|
||||
<meta property="twitter:domain" content="{http:request.authority}">
|
||||
</head>
|
||||
<body class="w3-theme-l1">
|
||||
<script>
|
||||
if (!localStorage.getItem("userId")) {
|
||||
localStorage.setItem("userId", self.crypto.randomUUID());
|
||||
}
|
||||
</script>
|
||||
<div class="w3-container w3-content"
|
||||
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
|
||||
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower <i class="fa-solid fa-dice-d20"></i>
|
||||
</h1>
|
||||
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
|
||||
style="padding: 25px; text-align: center; margin-bottom: auto;">
|
||||
<h2 id="nameH" popovertarget="room-hint" data-trigger="hover" style="margin: 0" hidden>Name</h2>
|
||||
@@ -373,8 +172,8 @@
|
||||
<p>How is your character called?</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px; flex-grow: 1" id="how-to">
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; margin-bottom: auto">
|
||||
<div style="margin-top: 20px" id="how-to">
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px;">
|
||||
<h2 style="text-align: center">How-To</h2>
|
||||
<ul>
|
||||
<li>
|
||||
@@ -397,213 +196,18 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px; flex-grow: 1" id="chatOverlay">
|
||||
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; margin-bottom: auto">
|
||||
If you are looking for a way to let your twitch chat roll click <a href target="_blank" id="chatOverlayLink">here</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="snackbar-container">
|
||||
<div id="snackbar">.. they see them rolling</div>
|
||||
</div>
|
||||
<script>
|
||||
function addDice() {
|
||||
let amount = +document.getElementById('dice-amount').innerText
|
||||
document.getElementById('dice-amount').innerText = amount + 1
|
||||
}
|
||||
|
||||
function removeDice() {
|
||||
let amount = +document.getElementById('dice-amount').innerText
|
||||
if (amount > 1) {
|
||||
document.getElementById('dice-amount').innerText = amount - 1
|
||||
}
|
||||
}
|
||||
|
||||
function url() {
|
||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
function start(event) {
|
||||
if ((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
|
||||
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=7&clearAfter=30';
|
||||
document.getElementById('resultsId').value = url() + '/overlay/' + document.getElementById('room').value + '/results';
|
||||
document.getElementById('myResultsId').value = document.getElementById('resultsId').value + '?name=' + encodeURIComponent(document.getElementById('name').value) + '&user=' + localStorage.getItem('userId');
|
||||
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
|
||||
document.getElementById('roll').hidden = false;
|
||||
document.getElementById('start-container').hidden = true;
|
||||
document.getElementById('options-container').hidden = false;
|
||||
document.getElementById('dice-tower').hidden = false;
|
||||
document.getElementById('name').hidden = true;
|
||||
document.getElementById('room').hidden = true;
|
||||
document.getElementById('how-to').hidden = true;
|
||||
document.getElementById('results').hidden = false;
|
||||
document.getElementById('all-results').hidden = !document.getElementById('gm').checked;
|
||||
document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none';
|
||||
document.getElementById('nameH').innerHTML = '<strong style="font-size:x-large;">' + document.getElementById('name').value + '</strong>';
|
||||
document.getElementById('save-dice-hint-name').innerHTML = '<strong>' + document.getElementById('name').value + '</strong>';
|
||||
document.getElementById('roomLabel').hidden = true;
|
||||
document.getElementById('nameLabel').hidden = true;
|
||||
document.getElementById('nameH').hidden = false;
|
||||
document.getElementById('room-hint').innerHTML = '<p>Room: <strong style="font-size:medium;">' + document.getElementById('room').value + '</strong></p>';
|
||||
document.getElementById('overlayLabel').innerHTML = 'Dice-Overlay for <strong>' + document.getElementById('name').value + '</strong>';
|
||||
document.title = document.getElementById('name').value + ' - Dice-Tower';
|
||||
|
||||
localStorage.setItem('last-name', document.getElementById('name').value)
|
||||
localStorage.setItem('last-room', document.getElementById('room').value)
|
||||
localStorage.setItem('last-gm', document.getElementById('gm').checked)
|
||||
|
||||
if (localStorage.getItem(document.getElementById('name').value + "-theme")) {
|
||||
document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme")
|
||||
}
|
||||
if (localStorage.getItem(document.getElementById('name').value + "-themeColor")) {
|
||||
document.getElementById('themeColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeColor"));
|
||||
}
|
||||
|
||||
document.getElementById('urls-overlay').showPopover();
|
||||
|
||||
let httpRequest = new XMLHttpRequest();
|
||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register')
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||
httpRequest.send(JSON.stringify({
|
||||
name: document.getElementById('name').value,
|
||||
overlay: document.getElementById('overlayId').value,
|
||||
id: document.getElementById('room').value + ':' + localStorage.getItem('userId')
|
||||
}))
|
||||
if (document.getElementById('gm').checked) {
|
||||
document.getElementById('resultSwitch').checked = true;
|
||||
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
||||
const evtSource = new EventSource(url() + '/dice/' + document.getElementById('room').value + '/users');
|
||||
evtSource.addEventListener('message', function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
if (data.id !== document.getElementById('room').value + ':' + localStorage.getItem('userId')) {
|
||||
let overlays = document.getElementById('overlay-urls');
|
||||
let newOverlay = document.getElementById(data.id) ?? document.createElement('div');
|
||||
newOverlay.replaceChildren(...[]);
|
||||
newOverlay.id = data.id;
|
||||
newOverlay.style.display = "flex";
|
||||
newOverlay.style.flexDirection = "row";
|
||||
newOverlay.style.justifyContent = "space-between";
|
||||
newOverlay.style.alignItems = "baseline";
|
||||
let newLabel = document.createElement('label');
|
||||
newLabel.for = data.id + 'url';
|
||||
newLabel.innerHTML = "Dice-Overlay for <strong>" + data.name + "</strong>";
|
||||
let newInput = document.getElementById('overlayId').cloneNode();
|
||||
newInput.type = "text";
|
||||
newInput.readOnly = true;
|
||||
newInput.id = data.id + 'url';
|
||||
newInput.style.flexGrow = '1';
|
||||
newInput.value = data.overlay;
|
||||
newInput.onclick = () => copyToClipboard(newInput.id)
|
||||
newOverlay.appendChild(newLabel);
|
||||
newOverlay.appendChild(newInput);
|
||||
overlays.appendChild(newOverlay);
|
||||
}
|
||||
configurePopover();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function rollEasy(dice) {
|
||||
document.getElementById('command').value = document.getElementById('dice-amount').innerText + dice;
|
||||
roll();
|
||||
}
|
||||
|
||||
function roll(event) {
|
||||
if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) {
|
||||
let httpRequest = new XMLHttpRequest();
|
||||
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`))
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||
httpRequest.send(JSON.stringify({
|
||||
name: document.getElementById('name').value,
|
||||
command: document.getElementById('command').value,
|
||||
themeColor: document.getElementById('themeColor').value,
|
||||
theme: document.getElementById('theme').value
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
function saveDice() {
|
||||
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
|
||||
localStorage.setItem(document.getElementById('name').value + "-themeColor", document.getElementById('themeColor').value)
|
||||
}
|
||||
|
||||
if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) {
|
||||
document.getElementById('name').value = localStorage.getItem('last-name');
|
||||
document.getElementById('room').value = localStorage.getItem('last-room');
|
||||
document.getElementById('gm').checked = localStorage.getItem('last-gm') === 'true';
|
||||
}
|
||||
|
||||
function configurePopover() {
|
||||
const popover = document.querySelectorAll("[popovertarget][data-trigger='hover']");
|
||||
|
||||
popover.forEach((e) => {
|
||||
const target = document.querySelector("#" + e.getAttribute("popovertarget"));
|
||||
e.addEventListener("mouseover", () => {
|
||||
showSnackbar(target.innerHTML);
|
||||
});
|
||||
|
||||
e.addEventListener("mouseout", () => {
|
||||
hideSnackbar();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
configurePopover();
|
||||
|
||||
document.getElementById('resultSwitch').addEventListener('change', function () {
|
||||
if (!this.checked) {
|
||||
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
|
||||
} else {
|
||||
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
|
||||
}
|
||||
})
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
if (!localStorage.getItem("userId")) {
|
||||
localStorage.setItem("userId", self.crypto.randomUUID());
|
||||
}
|
||||
})
|
||||
|
||||
function copyToClipboard(id) {
|
||||
let copyText = document.getElementById(id);
|
||||
copyText.select();
|
||||
copyText.setSelectionRange(0, 99999);
|
||||
navigator.clipboard.writeText(copyText.value);
|
||||
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
|
||||
}
|
||||
|
||||
function showSnackbar(message) {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.innerHTML = message;
|
||||
snackbar.className = "show";
|
||||
snackbarContainer.className = "show";
|
||||
}
|
||||
|
||||
function hideSnackbar() {
|
||||
let snackbar = document.getElementById("snackbar");
|
||||
let snackbarContainer = document.getElementById("snackbar-container");
|
||||
snackbar.className = snackbar.className.replace("show", "");
|
||||
snackbarContainer.className = snackbarContainer.className.replace("show", "");
|
||||
}
|
||||
</script>
|
||||
<script type="module">
|
||||
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
document.getElementById('preview').onclick = async () => {
|
||||
document.getElementById('dice-box').replaceChildren(...[])
|
||||
const diceBox = new DiceBox("#dice-box", {
|
||||
assetPath: "/vendor/assets/",
|
||||
theme: document.getElementById('theme').value,
|
||||
themeColor: document.getElementById('themeColor').value,
|
||||
scale: 14
|
||||
});
|
||||
await diceBox.init()
|
||||
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
<footer class="w3-theme-l1 w3-center">
|
||||
Version {version} ::
|
||||
Version {version} —
|
||||
<a href="https://git.arindy.de/arindy/dice-tower" target="_blank" class="w3-hover-text-black"><i class="fa-solid fa-code"></i> Source on GitTea</a>
|
||||
</footer>
|
||||
</html>
|
||||
|
||||
@@ -4,35 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Dice-Tower - Overlay</title>
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
#dice-box {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparent;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
#dice-box canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
<link rel="stylesheet" href="/overlay/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="dice-box"></div>
|
||||
@@ -40,7 +12,9 @@
|
||||
function url() {
|
||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
import DiceBox from "/vendor/dice-box/dice-box.es.js";
|
||||
|
||||
const evtSource = new EventSource(url() + "/dice/{diceid??}/stream");
|
||||
const diceBox = new DiceBox("#dice-box", {
|
||||
assetPath: "/vendor/assets/",
|
||||
@@ -59,7 +33,7 @@
|
||||
scale: {scale}
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async() => {
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
await diceBox.init()
|
||||
|
||||
let timeout = 0;
|
||||
@@ -68,20 +42,27 @@
|
||||
let data = JSON.parse(event.data);
|
||||
diceBox.onRollComplete = (rollResult) => {
|
||||
let httpRequest = new XMLHttpRequest();
|
||||
httpRequest.open('POST',url() + '/dice/' + data.room + '/results')
|
||||
httpRequest.open('POST', url() + '/dice/' + data.room + '/results')
|
||||
httpRequest.setRequestHeader('Content-Type', 'application/json')
|
||||
httpRequest.send(JSON.stringify({
|
||||
name: data.name,
|
||||
user: data.user,
|
||||
themeColor: data.themeColor,
|
||||
results: rollResult,
|
||||
} ))
|
||||
if ({clearAfter} > 0) {
|
||||
timeout = setTimeout(() => diceBox.clear(), {clearAfter} * 1000)
|
||||
}))
|
||||
if ({clearAfter} >
|
||||
0
|
||||
)
|
||||
{
|
||||
timeout = setTimeout(() => diceBox.clear(), {clearAfter} * 1000
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
diceBox.roll(data.roll, { theme: data.theme?.length > 0 ? data.theme : 'default', themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF' });
|
||||
diceBox.roll(data.roll, {
|
||||
theme: data.theme?.length > 0 ? data.theme : 'default',
|
||||
themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF'
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<div id="results" style="font-size: x-large">
|
||||
</div>
|
||||
<div id="results" style="font-size: x-large"></div>
|
||||
<script type="module">
|
||||
function url() {
|
||||
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
|
||||
}
|
||||
|
||||
const evtSource = new EventSource(url() + '/dice/{room}/results');
|
||||
evtSource.addEventListener('message', function (event) {
|
||||
let data = JSON.parse(event.data);
|
||||
|
||||
Reference in New Issue
Block a user