Merge branch 'web-layout'
* web-layout: Show max viewers Move to videojs and point to remote video on goth.land form functionailties progress. implement chat toggling fix msg container use app file from web-layout style message items Guard against the infinite that can take place when the ws server goes unavailable use css vars initial chat form layout mobile considerations add file initial layout Support local development of index.html
This commit is contained in:
commit
6d8e8a8849
@ -6,7 +6,9 @@
|
||||
href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<link href="./styles/layout.css" rel="stylesheet" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
|
||||
<!-- unpkg : use the latest version of Video.js -->
|
||||
<link href="//unpkg.com/video.js/dist/video-js.min.css" rel="stylesheet">
|
||||
<link
|
||||
@ -14,104 +16,127 @@
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script src="//unpkg.com/video.js/dist/video.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
|
||||
|
||||
<script src="//vjs.zencdn.net/7.8.2/video.min.js"></script>
|
||||
|
||||
<!-- Used for animating the scrolling of the chat div. Can that be done other ways? -->
|
||||
<script src="vendor/jquery-2.1.4.min.js"></script>
|
||||
|
||||
|
||||
<script src="vendor/autolink.js"></script>
|
||||
</head>
|
||||
|
||||
<div>
|
||||
<div class="flex">
|
||||
<div class="w-4/6">
|
||||
<video
|
||||
id="video"
|
||||
class="video-js vjs-theme-fantasy"
|
||||
preload="auto"
|
||||
poster="/thumbnail.png"
|
||||
autoplay
|
||||
controls
|
||||
style="width: 100%; height: 600px;"
|
||||
data-setup='{}'
|
||||
>
|
||||
<source src="hls/stream.m3u8" type="application/x-mpegURL"/>
|
||||
</video>
|
||||
<div id="app">
|
||||
{{ streamStatus }} {{ viewerCount }} {{ 'viewer' | plural(viewerCount) }}.
|
||||
Max {{ sessionMaxViewerCount }} {{ 'viewer' | plural(sessionMaxViewerCount) }},
|
||||
{{ overallMaxViewerCount }} overall.
|
||||
<body>
|
||||
<div id="app-container" class="flex no-chat">
|
||||
<header class="flex">
|
||||
<h1>
|
||||
😈 Owncast
|
||||
</h1>
|
||||
|
||||
<div id="user-options-container" class="flex">
|
||||
<div id="user-info">
|
||||
<div id="user-info-display" title="Click to update user name" class="flex">
|
||||
<img src="https://robohash.org/username123" id="username-avatar" class="rounded-full" />
|
||||
<span id="username-display">Random Username 123</span>
|
||||
</div>
|
||||
|
||||
<div id="user-info-change">
|
||||
<input type="text"
|
||||
id="username-change-input"
|
||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-black-500 rounded py-1 px-1 leading-tight focus:bg-white"
|
||||
value="Random Username 123"
|
||||
maxlength="100"
|
||||
placeholder="Update username"
|
||||
>
|
||||
<button id="button-update-username" class="bg-blue-500 hover:bg-blue-700 text-white py-1 px-1 rounded user-btn">Update</button>
|
||||
<button id="button-cancel-change" class="bg-gray-900 hover:bg-gray-800 py-1 px-2 rounded user-btn" title="cancel">X</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chat-toggle" class="flex">💬</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w-2/6">
|
||||
<div
|
||||
id="messages-container"
|
||||
class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
|
||||
style="height: 60vh; overflow-y: scroll;"
|
||||
>
|
||||
<div v-for="(message, index) in messages">
|
||||
<div class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
|
||||
<div class="flex items-center">
|
||||
<img
|
||||
v-bind:src="message.image"
|
||||
class="w-10 h-10 rounded-full mr-4 border-black-500"
|
||||
style="padding: 5px; background-color: #ececec;"
|
||||
/>
|
||||
</header>
|
||||
<div id="main-content-container" class="flex">
|
||||
<!-- LEFT CONTAINER SIDE-->
|
||||
<div class="flex main-cols left-col">
|
||||
|
||||
<div class="text-sm">
|
||||
<p class="text-700">{{ message.author }}</p>
|
||||
<p class="text-gray-600"v-html="message.linkedText()"></p>
|
||||
<div id="video-container" class="flex shadow-md">
|
||||
<video
|
||||
class="video-js vjs-theme-fantasy"
|
||||
id="video"
|
||||
preload="auto"
|
||||
controls
|
||||
autoplay
|
||||
muted
|
||||
poster="https://goth.land/thumbnail.png"
|
||||
data-setup='{}'
|
||||
>
|
||||
<source src="https://goth.land/hls/stream.m3u8" type="application/x-mpegURL"/>
|
||||
</video>
|
||||
</div>
|
||||
|
||||
<div id="stream-info">
|
||||
{{ streamStatus }} {{ viewerCount }} {{ 'viewer' | plural(viewerCount) }}.
|
||||
Max {{ sessionMaxViewerCount }} {{ 'viewer' | plural(sessionMaxViewerCount) }},
|
||||
{{ overallMaxViewerCount }} overall.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT CONTAINER SIDE-->
|
||||
<div class="flex main-cols right-col">
|
||||
|
||||
<div id="chat-container">
|
||||
<div id="messages-container">
|
||||
<div v-for="(message, index) in messages">
|
||||
<div class="message flex">
|
||||
<img
|
||||
v-bind:src="message.image"
|
||||
class="message-avatar rounded-full"
|
||||
/>
|
||||
<div class="message-content">
|
||||
<p class="message-author">{{ message.author }}</p>
|
||||
<p class="message-text"v-html="message.formatText()"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="message-input-container" class="shadow-md">
|
||||
<form id="message-form" class="flex" @submit="submitChatForm">
|
||||
|
||||
<input type="hidden" name="inputAuthor" id="self-message-author" v-model="message.author" />
|
||||
|
||||
<!-- Author -->
|
||||
<!-- <label class="control-label" for="inputAuthor">Author</label>
|
||||
<input
|
||||
id="inputAuthor"
|
||||
type="text"
|
||||
class="appearance-none bg-gray-200 text-gray-700 border border-black-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white"
|
||||
placeholder="Name"
|
||||
v-model="message.author"
|
||||
/> -->
|
||||
|
||||
|
||||
<textarea
|
||||
id="inputBody"
|
||||
placeholder="Message"
|
||||
v-model="message.body"
|
||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-black-500 rounded py-2 px-2 my-2 leading-tight focus:bg-white"
|
||||
></textarea>
|
||||
|
||||
<div id="message-form-actions" class="flex">
|
||||
<span id="message-form-warning"></span>
|
||||
<button
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-1 px-2 rounded"
|
||||
> Send
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form
|
||||
id="chatForm"
|
||||
@submit="submitChatForm"
|
||||
class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4"
|
||||
>
|
||||
<!-- Author -->
|
||||
<label class="control-label" for="inputAuthor">Author</label>
|
||||
<input
|
||||
id="inputAuthor"
|
||||
type="text"
|
||||
class="appearance-none bg-gray-200 text-gray-700 border border-black-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white"
|
||||
placeholder="Name"
|
||||
v-model="message.author"
|
||||
/>
|
||||
|
||||
<!-- Body -->
|
||||
<div>
|
||||
<label class="control-label" for="inputBody">Message</label>
|
||||
<div class="controls">
|
||||
<textarea
|
||||
id="inputBody"
|
||||
placeholder="Message"
|
||||
v-model="message.body"
|
||||
class="appearance-none block w-full bg-gray-200 text-gray-700 border border-black-500 rounded py-3 px-4 mb-3 leading-tight focus:outline-none focus:bg-white"
|
||||
>
|
||||
</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<button
|
||||
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/message.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/message.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,19 +1,17 @@
|
||||
function setupApp() {
|
||||
Vue.filter('plural', function (string, count) {
|
||||
if (count === 1) {
|
||||
return string
|
||||
return string;
|
||||
} else {
|
||||
return string + "s"
|
||||
return string + "s";
|
||||
}
|
||||
})
|
||||
|
||||
window.app = new Vue({
|
||||
el: "#app",
|
||||
el: "#stream-info",
|
||||
data: {
|
||||
streamStatus: "",
|
||||
viewerCount: 0,
|
||||
sessionMaxViewerCount: 0,
|
||||
overallMaxViewerCount: 0
|
||||
},
|
||||
});
|
||||
|
||||
@ -28,27 +26,30 @@ function setupApp() {
|
||||
el: "#chatForm",
|
||||
data: {
|
||||
message: {
|
||||
author: localStorage.author || "Viewer" + (Math.floor(Math.random() * 42) + 1),
|
||||
author: "",//localStorage.author || "Viewer" + (Math.floor(Math.random() * 42) + 1),
|
||||
body: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
submitChatForm: function (e) {
|
||||
const message = new Message(this.message)
|
||||
message.id = uuidv4()
|
||||
localStorage.author = message.author
|
||||
const messageJSON = JSON.stringify(message)
|
||||
window.ws.send(messageJSON)
|
||||
e.preventDefault()
|
||||
const message = new Message(this.message);
|
||||
message.id = uuidv4();
|
||||
localStorage.author = message.author;
|
||||
const messageJSON = JSON.stringify(message);
|
||||
window.ws.send(messageJSON);
|
||||
e.preventDefault();
|
||||
|
||||
this.message.body = ""
|
||||
this.message.body = "";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var appMessagingMisc = new Messaging();
|
||||
appMessagingMisc.init();
|
||||
}
|
||||
|
||||
async function getStatus() {
|
||||
const url = "/status";
|
||||
let url = "https://util.real-ity.com:8042/status";
|
||||
|
||||
try {
|
||||
const response = await fetch(url);
|
||||
@ -60,7 +61,7 @@ async function getStatus() {
|
||||
app.viewerCount = status.viewerCount
|
||||
app.sessionMaxViewerCount = status.sessionMaxViewerCount
|
||||
app.overallMaxViewerCount = status.overallMaxViewerCount
|
||||
|
||||
|
||||
} catch (e) {
|
||||
app.streamStatus = "Stream server is offline."
|
||||
app.viewerCount = 0
|
||||
@ -68,12 +69,12 @@ async function getStatus() {
|
||||
|
||||
}
|
||||
|
||||
var websocketReconnectTimer
|
||||
var websocketReconnectTimer;
|
||||
function setupWebsocket() {
|
||||
clearTimeout(websocketReconnectTimer)
|
||||
|
||||
const protocol = location.protocol == "https:" ? "wss" : "ws"
|
||||
var ws = new WebSocket(protocol + "://" + location.host + "/entry")
|
||||
var ws = new WebSocket("wss://util.real-ity.com:8042/entry")
|
||||
|
||||
ws.onmessage = (e) => {
|
||||
const model = JSON.parse(e.data)
|
||||
@ -108,10 +109,10 @@ function setupWebsocket() {
|
||||
setupApp()
|
||||
getStatus()
|
||||
setupWebsocket()
|
||||
setInterval(getStatus, 5000)
|
||||
// setInterval(getStatus, 5000)
|
||||
|
||||
function scrollSmoothToBottom(id) {
|
||||
const div = document.getElementById(id)
|
||||
const div = document.getElementById(id);
|
||||
$('#' + id).animate({
|
||||
scrollTop: div.scrollHeight - div.clientHeight
|
||||
}, 500)
|
||||
|
@ -6,8 +6,13 @@ class Message {
|
||||
this.id = model.id
|
||||
}
|
||||
|
||||
linkedText() {
|
||||
return autoLink(this.body, { embed: true })
|
||||
addNewlines(str) {
|
||||
return str.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
||||
|
||||
}
|
||||
formatText() {
|
||||
var linked = autoLink(this.body, { embed: true });
|
||||
return this.addNewlines(linked);
|
||||
}
|
||||
|
||||
toModel() {
|
||||
@ -18,4 +23,127 @@ class Message {
|
||||
id: this.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convert newlines to <br>s
|
||||
|
||||
class Messaging {
|
||||
constructor() {
|
||||
this.chatDisplayed = false;
|
||||
this.username = "";
|
||||
this.avatarSource = "https://robohash.org/";
|
||||
|
||||
this.messageCharCount = 0;
|
||||
this.maxMessageLength = 500;
|
||||
this.maxMessageBuffer = 20;
|
||||
|
||||
|
||||
this.tagChatToggle = document.querySelector("#chat-toggle");
|
||||
|
||||
this.tagUserInfoDisplay = document.querySelector("#user-info-display");
|
||||
this.tagUserInfoChanger = document.querySelector("#user-info-change");
|
||||
|
||||
this.tagUsernameDisplay = document.querySelector("#username-display");
|
||||
this.imgUsernameAvatar = document.querySelector("#username-avatar");
|
||||
|
||||
this.tagMessageAuthor = document.querySelector("#self-message-author");
|
||||
|
||||
this.tagMessageFormWarning = document.querySelector("#message-form-warning");
|
||||
|
||||
this.tagAppContainer = document.querySelector("#app-container");
|
||||
|
||||
this.inputChangeUserName = document.querySelector("#username-change-input");
|
||||
this.btnUpdateUserName = document.querySelector("#button-update-username");
|
||||
this.btnCancelUpdateUsername = document.querySelector("#button-cancel-change");
|
||||
|
||||
this.formMessageInput = document.querySelector("#inputBody");
|
||||
|
||||
}
|
||||
init() {
|
||||
this.tagChatToggle.addEventListener("click", this.handleChatToggle);
|
||||
this.tagUsernameDisplay.addEventListener("click", this.handleShowChangeNameForm);
|
||||
|
||||
this.btnUpdateUserName.addEventListener("click", this.handleUpdateUsername);
|
||||
this.btnCancelUpdateUsername.addEventListener("click", this.handleHideChangeNameForm);
|
||||
|
||||
this.inputChangeUserName.addEventListener("keydown", this.handleUsernameKeydown);
|
||||
this.formMessageInput.addEventListener("keydown", this.handleMessageInputKeydown);
|
||||
}
|
||||
|
||||
handleChatToggle = () => {
|
||||
if (this.chatDisplayed) {
|
||||
this.tagAppContainer.className = "flex no-chat";
|
||||
this.chatDisplayed = false;
|
||||
} else {
|
||||
this.tagAppContainer.className = "flex";
|
||||
this.chatDisplayed = true;
|
||||
}
|
||||
}
|
||||
handleShowChangeNameForm = () => {
|
||||
this.tagUserInfoDisplay.style.display = "none";
|
||||
this.tagUserInfoChanger.style.display = "flex";
|
||||
}
|
||||
handleHideChangeNameForm = () => {
|
||||
this.tagUserInfoDisplay.style.display = "flex";
|
||||
this.tagUserInfoChanger.style.display = "none";
|
||||
}
|
||||
handleUpdateUsername = () => {
|
||||
var newValue = this.inputChangeUserName.value;
|
||||
newValue = newValue.trim();
|
||||
// do other string cleanup?
|
||||
|
||||
if (newValue) {
|
||||
this.userName = newValue;
|
||||
this.inputChangeUserName.value = newValue;
|
||||
this.tagMessageAuthor.innerText = newValue;
|
||||
this.tagUsernameDisplay.innerText = newValue;
|
||||
this.imgUsernameAvatar.src = this.avatarSource + newValue;
|
||||
}
|
||||
this.handleHideChangeNameForm();
|
||||
}
|
||||
|
||||
handleUsernameKeydown = event => {
|
||||
if (event.keyCode === 13) { // enter
|
||||
this.handleUpdateUsername();
|
||||
} else if (event.keyCode === 27) { // esc
|
||||
this.handleHideChangeNameForm();
|
||||
}
|
||||
}
|
||||
|
||||
handleMessageInputKeydown = event => {
|
||||
var okCodes = [37,38,39,40,16,91,18,46,8];
|
||||
var value = this.formMessageInput.value.trim();
|
||||
var numCharsLeft = this.maxMessageLength - value.length;
|
||||
|
||||
if (event.keyCode === 13) { // enter
|
||||
if (!this.prepNewLine) {
|
||||
// submit()
|
||||
event.preventDefault();
|
||||
// clear out things.
|
||||
this.formMessageInput.value = "";
|
||||
this.tagMessageFormWarning.innerText = "";
|
||||
return;
|
||||
}
|
||||
this.prepNewLine = false;
|
||||
} else {
|
||||
this.prepNewLine = false;
|
||||
}
|
||||
if (event.keyCode === 16 || event.keyCode === 17) { // ctrl, shift
|
||||
this.prepNewLine = true;
|
||||
}
|
||||
|
||||
if (numCharsLeft <= this.maxMessageBuffer) {
|
||||
this.tagMessageFormWarning.innerText = numCharsLeft + " chars left";
|
||||
if (numCharsLeft <= 0 && !okCodes.includes(event.keyCode)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
this.tagMessageFormWarning.innerText = "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
309
webroot/styles/layout.css
Normal file
309
webroot/styles/layout.css
Normal file
@ -0,0 +1,309 @@
|
||||
/* variables */
|
||||
:root {
|
||||
--header-height: 3em;
|
||||
--right-col-width: 24em;
|
||||
|
||||
--chat-bg-color: rgba(11,0,33,.95);
|
||||
--header-bg-color: rgba(20,0,40,1);
|
||||
}
|
||||
|
||||
|
||||
body {
|
||||
font-size: 14px;
|
||||
background-color: #666;
|
||||
}
|
||||
::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#app-container {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
position: relative;
|
||||
|
||||
color: white;
|
||||
}
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: var(--header-height);
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: var(--header-bg-color);
|
||||
z-index: 10;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.25em;
|
||||
font-weight: 100;
|
||||
letter-spacing: 1.2;
|
||||
text-transform: uppercase;
|
||||
color: #ddd;
|
||||
padding: .5em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#chat-toggle {
|
||||
cursor: pointer;
|
||||
background-color: #555;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
width: 3em;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
#chat-toggle:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
|
||||
#user-options-container {
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#user-info-display {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
padding: .5em 1em;
|
||||
}
|
||||
|
||||
#username-avatar {
|
||||
height: 1.75em;
|
||||
width: 1.75em;
|
||||
margin-right: .5em;
|
||||
border: 1px solid rgba(255,255,255,.25)
|
||||
}
|
||||
#username-display {
|
||||
font-weight: bold;
|
||||
font-size: .75em;
|
||||
color: #516FEB
|
||||
|
||||
}
|
||||
#user-info-display:hover {
|
||||
transition: opacity .2s;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
|
||||
#user-info-change {
|
||||
display: none;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: .25em;
|
||||
}
|
||||
#username-change-input {
|
||||
font-size: .75em;
|
||||
}
|
||||
#button-update-username {
|
||||
font-size: .65em;
|
||||
text-transform: uppercase;
|
||||
height: 2.5em;
|
||||
}
|
||||
#button-cancel-change {
|
||||
color: rgba(255,255,255,.5);
|
||||
cursor: pointer;
|
||||
height: 2.5em;
|
||||
font-size: .65em;
|
||||
}
|
||||
.user-btn {
|
||||
margin: 0 .25em;
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
|
||||
#main-content-container {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
margin-top: var(--header-height);
|
||||
}
|
||||
|
||||
.main-cols {
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.left-col {
|
||||
width: calc(100vw - var(--right-col-width));
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
|
||||
#video-container {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
}
|
||||
#video-container video {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#stream-info {
|
||||
padding: .5em;
|
||||
text-align: center;
|
||||
font-family: monospace;
|
||||
font-size: .75em;
|
||||
background-color: rgba(0,0,0,.5);
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
|
||||
|
||||
#chat-container {
|
||||
position: fixed;
|
||||
z-index: 9;
|
||||
right: 0;
|
||||
height: 100%;
|
||||
width: var(--right-col-width);
|
||||
background-color: var(--chat-bg-color);
|
||||
height: calc(100vh - var(--header-height));
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#messages-container {
|
||||
overflow: auto;
|
||||
padding: 1em 0;
|
||||
}
|
||||
#message-input-container {
|
||||
width: 100%;
|
||||
border-top: 1px solid #eee;
|
||||
padding: 1em;
|
||||
background-color: #334;
|
||||
}
|
||||
|
||||
#message-form {
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
#message-form-actions {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
#message-form-warning {
|
||||
font-size: .75em;
|
||||
color: red;
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
|
||||
.message {
|
||||
padding: .85em;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.message-avatar {
|
||||
height: 2.5em;
|
||||
width: 2.5em;
|
||||
margin-right: .75em;
|
||||
background-color: rgba(0,0,0, .75);
|
||||
}
|
||||
.message-content {
|
||||
font-size: .85em;
|
||||
}
|
||||
.message-content a {
|
||||
color: #6699cc;
|
||||
}
|
||||
.message-content a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.message-author {
|
||||
font-weight: 600;
|
||||
}
|
||||
.message-text {
|
||||
color: #ccc;
|
||||
font-weight: 100;
|
||||
}
|
||||
/* ************************************************8 */
|
||||
|
||||
|
||||
.no-chat .left-col {
|
||||
width: 100vw;
|
||||
}
|
||||
.no-chat .right-col {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.no-chat #chat-toggle {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
/* ************************************************8 */
|
||||
|
||||
@media screen and (max-width: 860px) {
|
||||
:root {
|
||||
--right-col-width: 20em;
|
||||
}
|
||||
|
||||
#chat-container {
|
||||
width: var(--right-col-width);
|
||||
}
|
||||
.left-col {
|
||||
width: calc(100vw - var(--right-col-width));
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 640px ) and (orientation: portrait) {
|
||||
#main-content-container {
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: calc(100vh - var(--header-height));
|
||||
}
|
||||
|
||||
.main-cols {
|
||||
width: 100vw;
|
||||
}
|
||||
.left-col {
|
||||
flex-direction: column;
|
||||
justify-content: stretch;
|
||||
}
|
||||
.right-col {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
#info {
|
||||
display: none;
|
||||
overflow: auto;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
#chat-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.no-chat .left-col {
|
||||
height: 100%;
|
||||
}
|
||||
.no-chat .right-col {
|
||||
display: none;
|
||||
}
|
||||
.no-chat #info {
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
2
webroot/vendor/autolink.js
vendored
2
webroot/vendor/autolink.js
vendored
@ -113,7 +113,7 @@ AutoLink.prototype = {
|
||||
var text = this.options.removeHTTP ? removeHTTP(match) : match
|
||||
return (
|
||||
p1 +
|
||||
'<a href="' +
|
||||
'<a target="_blank" href="' +
|
||||
match +
|
||||
'"' +
|
||||
this.attrs +
|
||||
|
Loading…
x
Reference in New Issue
Block a user