Revert "Chat: Add markdown preview toggle with client-side rendering and user preference"

This reverts commit ffe9f30c0a.
jocadbz
Joca 2026-01-15 23:33:03 -03:00
parent ffe9f30c0a
commit 9c959f6412
Signed by: jocadbz
GPG Key ID: B1836DCE2F50BDF7
3 changed files with 9 additions and 311 deletions

View File

@ -223,21 +223,12 @@ func ChatHandler(app *App) http.HandlerFunc {
}
allUsernamesJSON, _ := json.Marshal(allUsernames)
// Get user preferences for markdown preview default
prefs, err := models.GetUserPreferences(app.DB, userID)
if err != nil {
log.Printf("Error fetching user preferences: %v", err)
// Create default if not found
prefs, _ = models.CreateDefaultPreferences(app.DB, userID)
}
data := struct {
PageData
Board models.Board
Messages []models.ChatMessage
AllUsernames template.JS
CurrentUsername string
MarkdownPreviewDefault string
}{
PageData: PageData{
Title: "ThreadR Chat - " + board.Name,
@ -252,7 +243,6 @@ func ChatHandler(app *App) http.HandlerFunc {
Messages: messages,
AllUsernames: template.JS(allUsernamesJSON),
CurrentUsername: currentUsername,
MarkdownPreviewDefault: prefs.MarkdownPreviewDefault,
}
if err := app.Tmpl.ExecuteTemplate(w, "chat", data); err != nil {
log.Printf("Error executing template in ChatHandler: %v", err)

View File

@ -371,118 +371,3 @@ function showDraftIndicator(content, timestamp, onRestore, onDiscard) {
return indicator;
}
// ============================================
// Markdown Preview Functions
// ============================================
// Escape HTML to prevent XSS
function escapeHTML(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// Process inline markdown (bold, italic, code, mentions)
function processInlineMarkdown(line) {
// Inline code first (to avoid processing markdown inside code)
line = line.replace(/`([^`]+)`/g, '<code>$1</code>');
// Bold: **text** or __text__
line = line.replace(/\*\*([^\*]+)\*\*/g, '<strong>$1</strong>');
line = line.replace(/__([^_]+)__/g, '<strong>$1</strong>');
// Italic: *text* or _text_
line = line.replace(/\*([^\*]+)\*/g, '<em>$1</em>');
line = line.replace(/_([^_]+)_/g, '<em>$1</em>');
// Mentions: @username
line = line.replace(/@(\w+)/g, '<span class="chat-message-mention">@$1</span>');
return line;
}
// Render markdown to HTML (matching Go implementation)
function renderMarkdownPreview(content) {
let html = '';
// Extract code blocks first and replace with placeholders
const codeBlocks = [];
let codeBlockCounter = 0;
content = content.replace(/```(\w*)\n([\s\S]*?)\n```/g, (match, lang, code) => {
const escapedCode = escapeHTML(code);
let renderedBlock;
if (lang) {
renderedBlock = `<pre><code class="language-${lang}">${escapedCode}</code></pre>`;
} else {
renderedBlock = `<pre><code>${escapedCode}</code></pre>`;
}
const placeholder = `<!--CODEBLOCK_${codeBlockCounter}-->`;
codeBlocks[codeBlockCounter] = renderedBlock;
codeBlockCounter++;
return placeholder;
});
// Process lines
const lines = content.split('\n');
let inList = false;
for (const line of lines) {
const trimmedLine = line.trim();
// Headers
if (trimmedLine.startsWith('### ')) {
if (inList) { html += '</ul>\n'; inList = false; }
html += '<h3>' + processInlineMarkdown(trimmedLine.substring(4)) + '</h3>\n';
continue;
} else if (trimmedLine.startsWith('## ')) {
if (inList) { html += '</ul>\n'; inList = false; }
html += '<h2>' + processInlineMarkdown(trimmedLine.substring(3)) + '</h2>\n';
continue;
} else if (trimmedLine.startsWith('# ')) {
if (inList) { html += '</ul>\n'; inList = false; }
html += '<h1>' + processInlineMarkdown(trimmedLine.substring(2)) + '</h1>\n';
continue;
}
// Lists
if (trimmedLine.startsWith('* ') || trimmedLine.startsWith('- ')) {
if (!inList) {
html += '<ul>\n';
inList = true;
}
const listContent = trimmedLine.substring(2);
html += '<li>' + processInlineMarkdown(listContent) + '</li>\n';
continue;
}
// Close list if we're not in a list item anymore
if (inList) {
html += '</ul>\n';
inList = false;
}
// Regular paragraphs
if (trimmedLine !== '') {
html += '<p>' + processInlineMarkdown(trimmedLine) + '</p>\n';
} else {
html += '\n';
}
}
// Close list if still open
if (inList) {
html += '</ul>\n';
}
// Replace code block placeholders
codeBlocks.forEach((block, index) => {
html = html.replace(`<!--CODEBLOCK_${index}-->`, block);
});
// Clean up excessive newlines
html = html.replace(/\n{3,}/g, '\n\n');
return html;
}

View File

@ -247,99 +247,6 @@
padding: 6px 12px;
font-size: 0.9em;
}
.markdown-tabs {
display: flex;
gap: 0;
margin-bottom: 0;
border-bottom: 1px solid #001858;
}
.markdown-tab {
flex: 1;
padding: 8px 16px;
border: none;
background-color: #f3d2c1;
color: #001858;
font-family: monospace;
font-size: 0.9em;
cursor: pointer;
border-right: 1px solid #001858;
transition: background-color 0.2s ease;
}
.markdown-tab:last-child {
border-right: none;
}
.markdown-tab:hover {
background-color: #fef6e4;
}
.markdown-tab.active {
background-color: #8bd3dd;
color: #001858;
font-weight: bold;
}
.markdown-content-container {
position: relative;
min-height: 50px;
}
.chat-input textarea,
.markdown-preview {
display: none;
resize: none;
height: 50px;
margin-bottom: 8px;
font-size: 0.9em;
width: 100%;
box-sizing: border-box;
}
.chat-input textarea.markdown-visible,
.markdown-preview.markdown-visible {
display: block;
}
.markdown-preview {
border: 1px solid #001858;
border-radius: 5px;
padding: 8px;
background-color: #fef6e4;
color: #001858;
min-height: 50px;
max-height: 200px;
overflow-y: auto;
font-family: monospace;
}
.markdown-preview h1 {
font-size: 1.5em;
margin: 0.5em 0;
}
.markdown-preview h2 {
font-size: 1.3em;
margin: 0.5em 0;
}
.markdown-preview h3 {
font-size: 1.1em;
margin: 0.5em 0;
}
.markdown-preview p {
margin: 0.5em 0;
}
.markdown-preview ul {
margin: 0.5em 0;
padding-left: 2em;
}
.markdown-preview code {
background-color: #f3d2c1;
padding: 2px 4px;
border-radius: 3px;
}
.markdown-preview pre {
background-color: #f3d2c1;
padding: 8px;
border-radius: 5px;
overflow-x: auto;
margin: 0.5em 0;
}
.markdown-preview pre code {
background-color: transparent;
padding: 0;
}
.post-actions {
position: absolute;
top: 5px;
@ -503,29 +410,6 @@
border-color: #f582ae;
}
}
.markdown-tab {
background-color: #555;
color: #fef6e4;
border-color: #fef6e4;
}
.markdown-tab:hover {
background-color: #666;
}
.markdown-tab.active {
background-color: #8bd3dd;
color: #001858;
}
.markdown-preview {
background-color: #333;
color: #fef6e4;
border-color: #fef6e4;
}
.markdown-preview code {
background-color: #555;
}
.markdown-preview pre {
background-color: #555;
}
}
</style>
</head>
@ -582,14 +466,7 @@
<span id="reply-username">Replying to </span>
<button onclick="cancelReply()">X</button>
</div>
<div class="markdown-tabs">
<button class="markdown-tab active" data-tab="edit" onclick="switchMarkdownTab('edit')">Edit</button>
<button class="markdown-tab" data-tab="preview" onclick="switchMarkdownTab('preview')">Preview</button>
</div>
<div class="markdown-content-container">
<textarea id="chat-input-text" placeholder="Type a message..." class="markdown-visible"></textarea>
<div id="chat-preview" class="markdown-preview"></div>
</div>
<textarea id="chat-input-text" placeholder="Type a message..."></textarea>
<button onclick="sendMessage()">Send</button>
</div>
</div>
@ -952,57 +829,6 @@
}
});
// Markdown preview functionality
let currentTab = '{{.MarkdownPreviewDefault}}' || 'edit';
let previewUpdateTimeout;
function switchMarkdownTab(tab) {
currentTab = tab;
const textarea = document.getElementById('chat-input-text');
const preview = document.getElementById('chat-preview');
const tabs = document.querySelectorAll('.markdown-tab');
// Update tab styling
tabs.forEach(t => {
if (t.dataset.tab === tab) {
t.classList.add('active');
} else {
t.classList.remove('active');
}
});
// Show/hide content
if (tab === 'edit') {
textarea.classList.add('markdown-visible');
preview.classList.remove('markdown-visible');
textarea.focus();
} else {
textarea.classList.remove('markdown-visible');
preview.classList.add('markdown-visible');
updatePreview();
}
}
function updatePreview() {
const textarea = document.getElementById('chat-input-text');
const preview = document.getElementById('chat-preview');
const content = textarea.value;
if (content.trim() === '') {
preview.innerHTML = '<p style="opacity: 0.5; font-style: italic;">Nothing to preview. Type some markdown in the Edit tab.</p>';
} else {
preview.innerHTML = renderMarkdownPreview(content);
}
}
// Update preview on input (debounced)
document.getElementById('chat-input-text').addEventListener('input', () => {
if (currentTab === 'preview') {
clearTimeout(previewUpdateTimeout);
previewUpdateTimeout = setTimeout(updatePreview, 300);
}
});
window.onload = function() {
connectWebSocket();
const messagesContainer = document.getElementById('chat-messages');
@ -1080,9 +906,6 @@
originalSendMessage();
};
// Initialize markdown preview tab based on user preference
switchMarkdownTab(currentTab);
// Initial check for scroll position
checkScrollPosition();
};