changes
This commit is contained in:
parent
ef29695427
commit
7fae9df5b4
205
content/root.css
205
content/root.css
@ -1,47 +1,144 @@
|
|||||||
[data-theme="dark"] {
|
[data-theme="dark"] {
|
||||||
--radchat-color: #40a02b;
|
--radchat-color: #40a02b;
|
||||||
--main-bg-color: #11111b;
|
--main-bg-color: #11111b;
|
||||||
--pum-button-inactive-fg: #cdd6f4;
|
--pum-button-inactive-fg: #cdd6f4;
|
||||||
--pum-button-inactive-bg: #11111b;
|
--pum-button-inactive-bg: #11111b;
|
||||||
--pum-button-active-fg: #89b4fa;
|
--pum-button-active-fg: #89b4fa;
|
||||||
--pum-button-active-bg: #1e1e2e;
|
--pum-button-active-bg: #1e1e2e;
|
||||||
--pum-title-color: #cdd6f4;
|
--pum-title-color: #cdd6f4;
|
||||||
--pum-bg-color: #1e1e2e;
|
--pum-bg-color: #1e1e2e;
|
||||||
--user-color: #89b4fa;
|
--user-color: #89b4fa;
|
||||||
--timestamp-color: #313244;
|
--timestamp-color: #313244;
|
||||||
--separator-color: #181825;
|
--separator-color: #181825;
|
||||||
--message-color: #cdd6f4;
|
--message-color: #cdd6f4;
|
||||||
--message-bg-color: #1e1e2e;
|
--message-bg-color: #1e1e2e;
|
||||||
--input-bg-color: #181825;
|
--input-bg-color: #181825;
|
||||||
--input-text-color: #cdd6f4;
|
--input-text-color: #cdd6f4;
|
||||||
--input-button-inactive-bg: #b4befe;
|
--input-button-inactive-bg: #b4befe;
|
||||||
--input-button-inactive-fg: #11111b;
|
--input-button-inactive-fg: #11111b;
|
||||||
--input-button-active-bg: #89b4fa;
|
--input-button-active-bg: #89b4fa;
|
||||||
--input-button-active-fg: #11111b;
|
--input-button-active-fg: #11111b;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="light"] {
|
[data-theme="light"] {
|
||||||
--radchat-color: #40a02b;
|
--radchat-color: #40a02b;
|
||||||
--main-bg-color: #dce0e8;
|
--main-bg-color: #dce0e8;
|
||||||
--pum-button-inactive-fg: #4c4f69;
|
--pum-button-inactive-fg: #4c4f69;
|
||||||
--pum-button-inactive-bg: #dce0e8;
|
--pum-button-inactive-bg: #dce0e8;
|
||||||
--pum-button-active-fg: #1e66f5;
|
--pum-button-active-fg: #1e66f5;
|
||||||
--pum-button-active-bg: #eff1f5;
|
--pum-button-active-bg: #eff1f5;
|
||||||
--pum-title-color: #4c4f69;
|
--pum-title-color: #4c4f69;
|
||||||
--pum-bg-color: #eff1f5;
|
--pum-bg-color: #eff1f5;
|
||||||
--user-color: #1e66f5;
|
--user-color: #1e66f5;
|
||||||
--timestamp-color: #8c8fa1;
|
--timestamp-color: #8c8fa1;
|
||||||
--separator-color: #e6e9ef;
|
--separator-color: #e6e9ef;
|
||||||
--message-color: #4c4f69;
|
--message-color: #4c4f69;
|
||||||
--message-bg-color: #eff1f5;
|
--message-bg-color: #eff1f5;
|
||||||
--input-bg-color: #e6e9ef;
|
--input-bg-color: #e6e9ef;
|
||||||
--input-text-color: #4c4f69;
|
--input-text-color: #4c4f69;
|
||||||
--input-button-inactive-bg: #7287fd;
|
--input-button-inactive-bg: #7287fd;
|
||||||
--input-button-inactive-fg: #dce0e8;
|
--input-button-inactive-fg: #dce0e8;
|
||||||
--input-button-active-bg: #1e66f5;
|
--input-button-active-bg: #1e66f5;
|
||||||
--input-button-active-fg: #dce0e8;
|
--input-button-active-fg: #dce0e8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.search-trigger {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 20px;
|
||||||
|
right: 20px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
position: flex;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--pum-button-active-bg);
|
||||||
|
border-radius: 24px;
|
||||||
|
transition: width 0.2s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container.expanded {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
padding: 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--pum-button-active-bg);
|
||||||
|
color: var(--pum-button-inactive-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-button:hover {
|
||||||
|
background: var(--pum-button-active-bg);
|
||||||
|
color: var(--pum-button-active-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
display: none;
|
||||||
|
flex: 2;
|
||||||
|
border: none;
|
||||||
|
padding: 8px;
|
||||||
|
margin-left: 4px;
|
||||||
|
outline: none;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container.expanded .search-input {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-highlight {
|
||||||
|
background-color: yellow;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-highlight.current {
|
||||||
|
background-color: darkorange;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search icon using CSS */
|
||||||
|
.search-icon {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
border: 2px solid var(--pum-button-inactive-fg);
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-icon::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 2px;
|
||||||
|
height: 10px;
|
||||||
|
background: var(--pum-button-inactive-fg);
|
||||||
|
bottom: -8px;
|
||||||
|
right: -3px;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-count {
|
||||||
|
display: none;
|
||||||
|
margin-left: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--pum-title-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container.expanded .search-count {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Arial, sans-serif;
|
font-family: Arial, sans-serif;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@ -282,16 +379,16 @@ input {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
textarea {
|
textarea {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: var(--input-bg-color);
|
background-color: var(--input-bg-color);
|
||||||
color: var(--input-text-color);
|
color: var(--input-text-color);
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
resize: none; /* Prevents manual resizing */
|
resize: none; /* Prevents manual resizing */
|
||||||
min-height: 38px; /* Match your previous input height */
|
min-height: 38px; /* Match your previous input height */
|
||||||
line-height: 1.4;
|
line-height: 1.4;
|
||||||
font-family: Arial, sans-serif; /* Match your body font */
|
font-family: Arial, sans-serif; /* Match your body font */
|
||||||
}
|
}
|
||||||
button {
|
button {
|
||||||
padding: 10px 20px;
|
padding: 10px 20px;
|
||||||
@ -356,20 +453,23 @@ button.scroll:hover {
|
|||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radchat {
|
.search-container {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.radchat {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.current-user {
|
.current-user {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-controls {
|
.header-controls {
|
||||||
top: 10px;
|
top: 10px;
|
||||||
gap: 15px;
|
gap: 15px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-button, .users-button, .theme-button {
|
.settings-button, .users-button, .theme-button {
|
||||||
width: 36px;
|
width: 36px;
|
||||||
@ -451,6 +551,9 @@ button.scroll:hover {
|
|||||||
|
|
||||||
/* Small mobile devices */
|
/* Small mobile devices */
|
||||||
@media screen and (max-width: 380px) {
|
@media screen and (max-width: 380px) {
|
||||||
|
.search-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
.radchat {
|
.radchat {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
<!-- Users will be loaded here -->
|
<!-- Users will be loaded here -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="username-section" id="settings-panel">
|
<div class="username-section" id="settings-panel">
|
||||||
<h3>Username</h3>
|
<h3>Username</h3>
|
||||||
<div style="display: flex; gap: 10px;">
|
<div style="display: flex; gap: 10px;">
|
||||||
@ -67,6 +67,20 @@
|
|||||||
<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>
|
<path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/>
|
||||||
</svg></button>
|
</svg></button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="search-trigger">
|
||||||
|
<div class="search-container" id="searchContainer">
|
||||||
|
<button class="search-button" id="searchButton">
|
||||||
|
<div class="search-icon"></div>
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
class="search-input"
|
||||||
|
id="searchInput"
|
||||||
|
placeholder="Type to search page..."
|
||||||
|
>
|
||||||
|
<span class="search-count" id="searchCount"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="status"></div>
|
<div id="status"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
189
content/root.js
189
content/root.js
@ -495,6 +495,195 @@ async function initialize() {
|
|||||||
setInterval(loadUsers, 1000);
|
setInterval(loadUsers, 1000);
|
||||||
setInterval(pingCheck, 3000);
|
setInterval(pingCheck, 3000);
|
||||||
await loadMessages(true);
|
await loadMessages(true);
|
||||||
|
initializeSearchBox();
|
||||||
|
}
|
||||||
|
|
||||||
|
function initializeSearchBox() {
|
||||||
|
const searchContainer = document.getElementById("searchContainer");
|
||||||
|
const searchButton = document.getElementById("searchButton");
|
||||||
|
const searchInput = document.getElementById("searchInput");
|
||||||
|
const searchCount = document.getElementById("searchCount");
|
||||||
|
|
||||||
|
let currentMatchIndex = -1;
|
||||||
|
let matches = [];
|
||||||
|
|
||||||
|
function escapeRegExp(string) {
|
||||||
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearHighlights() {
|
||||||
|
// Remove all existing highlights
|
||||||
|
const highlights = document.querySelectorAll(
|
||||||
|
".search-highlight",
|
||||||
|
);
|
||||||
|
highlights.forEach((highlight) => {
|
||||||
|
const parent = highlight.parentNode;
|
||||||
|
parent.replaceChild(
|
||||||
|
document.createTextNode(highlight.textContent),
|
||||||
|
highlight,
|
||||||
|
);
|
||||||
|
parent.normalize();
|
||||||
|
});
|
||||||
|
matches = [];
|
||||||
|
currentMatchIndex = -1;
|
||||||
|
searchCount.textContent = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTextNodes(element, textNodes = []) {
|
||||||
|
// Skip certain elements
|
||||||
|
if (element.nodeType === Node.ELEMENT_NODE) { // Check if it's an element node first
|
||||||
|
if (
|
||||||
|
element.tagName === "SCRIPT" ||
|
||||||
|
element.tagName === "STYLE" ||
|
||||||
|
element.tagName === "NOSCRIPT" ||
|
||||||
|
(element.classList &&
|
||||||
|
element.classList.contains(
|
||||||
|
"search-container",
|
||||||
|
)) ||
|
||||||
|
(element.classList &&
|
||||||
|
element.classList.contains(
|
||||||
|
"search-highlight",
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
return textNodes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this node is a text node with non-whitespace content
|
||||||
|
if (
|
||||||
|
element.nodeType === Node.TEXT_NODE &&
|
||||||
|
element.textContent.trim()
|
||||||
|
) {
|
||||||
|
textNodes.push(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursively check all child nodes
|
||||||
|
const children = element.childNodes;
|
||||||
|
for (let i = 0; i < children.length; i++) {
|
||||||
|
findTextNodes(children[i], textNodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return textNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findAndHighlight(searchText) {
|
||||||
|
if (!searchText) {
|
||||||
|
clearHighlights();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearHighlights();
|
||||||
|
|
||||||
|
const searchRegex = new RegExp(escapeRegExp(searchText), "gi");
|
||||||
|
const textNodes = findTextNodes(document.body);
|
||||||
|
|
||||||
|
textNodes.forEach((node) => {
|
||||||
|
const matches = [
|
||||||
|
...node.textContent.matchAll(searchRegex),
|
||||||
|
];
|
||||||
|
if (matches.length > 0) {
|
||||||
|
const span = document.createElement("span");
|
||||||
|
span.innerHTML = node.textContent.replace(
|
||||||
|
searchRegex,
|
||||||
|
(match) =>
|
||||||
|
`<span class="search-highlight">${match}</span>`,
|
||||||
|
);
|
||||||
|
node.parentNode.replaceChild(span, node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Collect all highlights
|
||||||
|
matches = Array.from(
|
||||||
|
document.querySelectorAll(".search-highlight"),
|
||||||
|
);
|
||||||
|
if (matches.length > 0) {
|
||||||
|
currentMatchIndex = 0;
|
||||||
|
matches[0].classList.add("current");
|
||||||
|
matches[0].scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "center",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update count
|
||||||
|
searchCount.textContent = matches.length > 0
|
||||||
|
? `${currentMatchIndex + 1}/${matches.length}`
|
||||||
|
: "No matches";
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextMatch() {
|
||||||
|
if (matches.length === 0) return;
|
||||||
|
|
||||||
|
matches[currentMatchIndex].classList.remove("current");
|
||||||
|
currentMatchIndex = (currentMatchIndex + 1) % matches.length;
|
||||||
|
matches[currentMatchIndex].classList.add("current");
|
||||||
|
matches[currentMatchIndex].scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
block: "center",
|
||||||
|
});
|
||||||
|
searchCount.textContent = `${
|
||||||
|
currentMatchIndex + 1
|
||||||
|
}/${matches.length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
searchButton.addEventListener("click", () => {
|
||||||
|
if (!searchContainer.classList.contains("expanded")) {
|
||||||
|
searchContainer.classList.add("expanded");
|
||||||
|
searchInput.focus();
|
||||||
|
} else {
|
||||||
|
nextMatch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener("input", (e) => {
|
||||||
|
findAndHighlight(e.target.value.trim());
|
||||||
|
});
|
||||||
|
|
||||||
|
searchInput.addEventListener("keydown", (e) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
nextMatch();
|
||||||
|
} else if (e.key === "Escape") {
|
||||||
|
searchContainer.classList.remove("expanded");
|
||||||
|
searchInput.value = "";
|
||||||
|
clearHighlights();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Debug function to check search coverage (call from console: checkSearchCoverage())
|
||||||
|
window.checkSearchCoverage = function () {
|
||||||
|
const textNodes = findTextNodes(document.body);
|
||||||
|
console.log(
|
||||||
|
"Total searchable text nodes found:",
|
||||||
|
textNodes.length,
|
||||||
|
);
|
||||||
|
textNodes.forEach((node, i) => {
|
||||||
|
console.log(`Node ${i + 1}:`, {
|
||||||
|
text: node.textContent,
|
||||||
|
parent: node.parentElement.tagName,
|
||||||
|
path: getNodePath(node),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function getNodePath(node) {
|
||||||
|
const path = [];
|
||||||
|
while (node && node.parentElement) {
|
||||||
|
let index = Array.from(node.parentElement.childNodes)
|
||||||
|
.indexOf(node);
|
||||||
|
path.unshift(`${node.parentElement.tagName}[${index}]`);
|
||||||
|
node = node.parentElement;
|
||||||
|
}
|
||||||
|
return path.join(" > ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close search when clicking outside
|
||||||
|
document.addEventListener("click", (e) => {
|
||||||
|
if (!searchContainer.contains(e.target)) {
|
||||||
|
searchContainer.classList.remove("expanded");
|
||||||
|
searchInput.value = "";
|
||||||
|
clearHighlights();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize();
|
initialize();
|
||||||
|
@ -5,11 +5,12 @@
|
|||||||
### Mid Priority
|
### Mid Priority
|
||||||
- Other embeds (Twitter posts, spotify tracks, soundcloud, github repos, instagram posts, other video platforms)
|
- Other embeds (Twitter posts, spotify tracks, soundcloud, github repos, instagram posts, other video platforms)
|
||||||
### Low Priority
|
### Low Priority
|
||||||
- Nothing yet
|
- Reposition the search button
|
||||||
|
- Fix mobile views instead of hiding elements that you don't want to position properly
|
||||||
## Backend
|
## Backend
|
||||||
### High Priority
|
### High Priority
|
||||||
- Lazy load with pagination (frontend and backend)
|
- Lazy load with pagination (frontend and backend)
|
||||||
### Mid Priority
|
### Mid Priority
|
||||||
- Nothing yet
|
- Nothing yet
|
||||||
### Low Priority
|
### Low Priority
|
||||||
- Search functionality?
|
- Nothing yet
|
||||||
|
Loading…
x
Reference in New Issue
Block a user