Last active
May 8, 2023 01:52
-
-
Save MattiasMartens/4bc8c9ca515a566fa1afd05355bfb239 to your computer and use it in GitHub Desktop.
Bluesky Threader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Tweet Threader</title> | |
<link | |
href="data:image/png;base64,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" | |
rel="icon" | |
type="image/x-icon" | |
/> | |
<style> | |
.col1, | |
.col2 { | |
display: inline-block; | |
width: 30em; | |
vertical-align: top; | |
} | |
.row { | |
margin: auto; | |
width: fit-content; | |
} | |
textarea { | |
width: 28em; | |
box-sizing: border-box; | |
padding: 0.5em; | |
margin-left: 1em; | |
} | |
#char-count { | |
display: inline-block; | |
float: right; | |
margin-right: 1em; | |
} | |
/** circle */ | |
#char-count .quiet-remaining:before { | |
content: "•"; | |
font-size: large; | |
color: #1da1f2; | |
cursor: pointer; | |
} | |
#char-count .quiet-remaining.near-edge:before { | |
color: red; | |
} | |
.tweet-block--content, | |
textarea, | |
.tweet-block--index, | |
.toast, | |
#char-count, | |
p { | |
font-family: "Lucida Console", Monaco, monospace; | |
font-size: 1em; | |
} | |
h1 { | |
font-family: "system-ui", -apple-system, BlinkMacSystemFont, "Segoe UI", | |
Roboto, "Helvetica Neue", Arial, sans-serif; | |
color: #1da1f2; | |
text-align: center; | |
margin-bottom: 0; | |
} | |
.subtitle { | |
text-align: center; | |
font-family: "system-ui", -apple-system, BlinkMacSystemFont, "Segoe UI", | |
Roboto, "Helvetica Neue", Arial, sans-serif; | |
} | |
h2 { | |
font-family: "system-ui", -apple-system, BlinkMacSystemFont, "Segoe UI", | |
Roboto, "Helvetica Neue", Arial, sans-serif; | |
color: #125580; | |
} | |
.tweet-block { | |
border: 1px solid #ccc; | |
position: relative; | |
margin-bottom: 1em; | |
background-color: #f7fcff; | |
width: 30em; | |
} | |
.tweet-block:hover { | |
background-color: #e8f5fe; | |
} | |
.tweet-block--index { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
color: #1da1f2; | |
} | |
.tweet-block--content { | |
width: 26em; | |
margin-left: 2em; | |
margin-top: 2.5em; | |
margin-bottom: 1.5em; | |
padding: 0.25em; | |
display: inline-block; | |
cursor: pointer; | |
cursor: copy; | |
transition: background-color 250ms; | |
word-wrap: break-word; | |
} | |
.tweet-block--content p { | |
margin-top: 1em; | |
} | |
.tweet-block--content:hover { | |
background-color: #9fd2f2; | |
} | |
.toast { | |
background-color: #1da1f2; | |
color: white; | |
padding: 1em; | |
position: fixed; | |
bottom: 1em; | |
right: 1em; | |
z-index: 1; | |
opacity: 0; | |
transition: opacity 250ms; | |
} | |
code { | |
background-color: #f7fcff; | |
padding: 0.25em; | |
margin: 0; | |
border-radius: 0.25em; | |
/** red code color */ | |
color: #e83e8c; | |
/** light grey border */ | |
border: 1px solid #d1d5da; | |
margin-left: -0.25em; | |
margin-right: -0.25em; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Tweet Threader</h1> | |
<div class="subtitle"> | |
By | |
<a href="https://twitter.com/mattiasinspace" _target="blank" | |
>@MattiasInSpace</a | |
> | |
</div> | |
<div class="row"> | |
<div class="col1"> | |
<h2>Enter text you want to make into a thread</h2> | |
<p> | |
Text will break into tweets every 280 characters. To add an explicit | |
tweet break, add the pipe character <code>|</code> on its own line. | |
</p> | |
<textarea id="input-text"></textarea> | |
<span id="char-count"></span> | |
</div> | |
<div class="col2"> | |
<div id="output"> | |
<h2>Rendered into tweet blocks</h2> | |
<div id="output-text"></div> | |
</div> | |
</div> | |
</div> | |
<script> | |
window.toggleAlwaysShowRemaining = function () { | |
const newAlwaysShowRemaining = !localStorage.getItem( | |
"alwaysShowRemaining" | |
); | |
const remainingElement = document.querySelector(".remaining"); | |
if (remainingElement) { | |
if (newAlwaysShowRemaining) { | |
remainingElement.style.display = "initial"; | |
} else if (!remainingElement.classList.contains("always-show")) { | |
remainingElement.style.display = "none"; | |
} | |
} | |
newAlwaysShowRemaining ? localStorage.setItem("alwaysShowRemaining", 'true') : localStorage.removeItem("alwaysShowRemaining"); | |
}; | |
function truncateTextWithEllipsis(text, maxLength) { | |
if (text.length <= maxLength) { | |
return text; | |
} | |
return text.slice(0, maxLength - 1) + "…"; | |
} | |
function showToast(message) { | |
// Create the toast element | |
const toast = document.createElement("div"); | |
toast.classList.add("toast"); | |
toast.innerHTML = message; | |
// Insert the toast element into the DOM | |
document.body.appendChild(toast); | |
// Show the toast | |
setTimeout(function () { | |
toast.style.opacity = 1; | |
}, 0); | |
// Show the toast for 3 seconds | |
setTimeout(function () { | |
toast.style.opacity = 0; | |
setTimeout(function () { | |
toast.remove(); | |
}, 250); | |
}, 3000); | |
} | |
function escapeHTML(html) { | |
return html.replace(/[&<>"]/g, function (char) { | |
switch (char) { | |
case "&": | |
return "&"; | |
case "<": | |
return "<"; | |
case ">": | |
return ">"; | |
case '"': | |
return """; | |
} | |
}); | |
} | |
const rerenderInput = function () { | |
// Break input text into Tweets | |
/** @type string */ const text = inputText.value; | |
const delimited = text.split("\n|\n"); | |
// Use newlines as paragraph breaks | |
const brokenLengthwise = delimited | |
.map((chunk) => { | |
const broken = chunk.match(/.{1,280}/gs); | |
if (!broken) { | |
return []; | |
} | |
return [...broken].map((chunk) => | |
chunk | |
.split("\n") | |
.map((innerChunk) => `${escapeHTML(innerChunk)}<br>`) | |
.join("") | |
); | |
}) | |
.flat(); | |
const outputHTML = outputText.innerHTML; | |
const marker = '<span style="color: red;">|</span>'; | |
outputText.innerHTML = brokenLengthwise | |
.map( | |
(tweet, index, { length }) => | |
`<div class="tweet-block"><div class="tweet-block--index">${ | |
index + 1 | |
}/${length}</div><div class="tweet-block--content" title="Click to copy">${tweet}</div></div>` | |
) | |
.join(""); | |
// Save previous input to local storage | |
localStorage.setItem("inputText", inputText.value); | |
// Update height of textarea input | |
inputText.style.height = "1px"; | |
inputText.style.height = 25 + inputText.scrollHeight + "px"; | |
// Update char count | |
const charCount = document.getElementById("char-count"); | |
charCount.innerHTML = ""; | |
const lastTweet = document.querySelector( | |
".tweet-block:last-child .tweet-block--content" | |
); | |
if (lastTweet) { | |
const innerTextLength = lastTweet.innerText.length - 1; | |
const remaining = 280 - innerTextLength; | |
const showRemaining = remaining <= 20; | |
const showRemainingMerged = showRemaining || !!localStorage.getItem("alwaysShowRemaining"); | |
const nearEdge = remaining <= 10; | |
const charText = `${innerTextLength}/280 (remaining: ${remaining})`; | |
const quietRemainingHtml = `<span class="quiet-remaining ${ | |
nearEdge ? "near-edge" : "" | |
}" title="${charText}" onclick="window.toggleAlwaysShowRemaining()"></span>`; | |
const color = nearEdge ? "red" : "#125580"; | |
const remainingHtml = `<span class="remaining ${showRemaining ? 'always-show' : ''}" style="color: ${color}; margin-left: 0.5em; display: ${ | |
showRemainingMerged ? "initial" : "none" | |
}">${charText}</span>`; | |
charCount.innerHTML = quietRemainingHtml + remainingHtml; | |
} | |
}; | |
const inputText = document.getElementById("input-text"); | |
const outputText = document.getElementById("output-text"); | |
inputText.addEventListener("input", rerenderInput); | |
// Load previous input from local storage | |
const previousInput = localStorage.getItem("inputText"); | |
if (previousInput) { | |
inputText.value = previousInput; | |
rerenderInput(); | |
} | |
// Add click-to-copy for tweet blocks | |
const col2 = document.querySelector(".col2"); | |
col2.addEventListener("click", (event) => { | |
const target = event.target; | |
const content = target.closest(".tweet-block--content"); | |
if (!!target) { | |
const range = document.createRange(); | |
range.selectNode(target); | |
window.getSelection().removeAllRanges(); | |
window.getSelection().addRange(range); | |
document.execCommand("copy"); | |
window.getSelection().removeAllRanges(); | |
showToast( | |
`Copied to clipboard: ${truncateTextWithEllipsis( | |
content.innerText, | |
32 | |
)}` | |
); | |
} | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment