fix: migrate CI branch references from master to main and add comments created_at index
This commit is contained in:
@@ -1,48 +1,33 @@
|
||||
.attachment-upload-zone {
|
||||
border: 2px dashed var(--border-light);
|
||||
.attachment-upload-container button.attachment-upload-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.375rem 0.5rem;
|
||||
border-radius: var(--radius);
|
||||
padding: 1.5rem 1rem;
|
||||
text-align: center;
|
||||
font-size: 1.125rem;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
background: var(--bg-input);
|
||||
position: relative;
|
||||
background: none;
|
||||
border: 1px solid transparent;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.attachment-upload-zone:hover {
|
||||
.attachment-upload-container button.attachment-upload-btn:hover {
|
||||
background: var(--bg-card-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.attachment-upload-container button.attachment-upload-btn.dragover {
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-light);
|
||||
}
|
||||
|
||||
.attachment-upload-zone.dragover {
|
||||
border-color: var(--accent);
|
||||
background: var(--accent-light);
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.attachment-upload-zone input[type="file"] {
|
||||
.attachment-upload-container button.attachment-upload-btn input[type="file"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.attachment-upload-zone .upload-icon {
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.attachment-upload-zone .upload-text {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.attachment-upload-zone .upload-hint {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
margin-top: 0.375rem;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.attachment-upload-zone.uploading {
|
||||
.attachment-upload-container button.attachment-upload-btn.uploading {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
}
|
||||
@@ -52,6 +37,13 @@
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin-top: 0.75rem;
|
||||
flex: 0 0 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.attachment-preview-list:empty,
|
||||
.attachment-upload-error:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.attachment-preview {
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.25rem;
|
||||
padding: 1rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,9 @@
|
||||
font-size: 0.6875rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: auto;
|
||||
padding-left: 0.75rem;
|
||||
white-space: nowrap;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.post-topic {
|
||||
@@ -122,7 +124,7 @@
|
||||
font-size: 0.875rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.65;
|
||||
margin-bottom: 0.75rem;
|
||||
margin-bottom: 1rem;
|
||||
word-break: break-word;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 4;
|
||||
@@ -133,12 +135,13 @@
|
||||
.post-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding-top: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.post-action-btn {
|
||||
line-height: 1;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
|
||||
@@ -172,6 +172,7 @@
|
||||
padding: 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.post-detail-header {
|
||||
@@ -33,6 +33,8 @@
|
||||
font-size: 0.8125rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: auto;
|
||||
padding-left: 0.75rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.post-detail-title {
|
||||
@@ -45,13 +47,14 @@
|
||||
font-size: 0.9375rem;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.7;
|
||||
margin-top: 0.625rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.post-detail-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
gap: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
@@ -60,7 +63,7 @@
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 1.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.comments-section h3 {
|
||||
@@ -72,7 +75,7 @@
|
||||
.comment {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
padding: 0.75rem 0;
|
||||
padding: 1rem 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@@ -160,23 +163,37 @@
|
||||
margin-top: 1rem;
|
||||
padding-top: 1rem;
|
||||
border-top: 1px solid var(--border);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.comment-form > a {
|
||||
flex-shrink: 0;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.comment-form textarea {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 44px;
|
||||
max-height: 120px;
|
||||
}
|
||||
|
||||
.comment-form-actions {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 0.25rem;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.comment-form-actions button {
|
||||
padding: 0.5rem;
|
||||
padding: 0.375rem 0.5rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
color: var(--text-muted);
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
@@ -184,16 +201,16 @@
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.comment-form-submit {
|
||||
padding: 0.5rem 1rem;
|
||||
button.comment-form-submit {
|
||||
padding: 0.375rem 0.5rem;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
border-radius: var(--radius);
|
||||
font-weight: 600;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.comment-form-submit:hover {
|
||||
button.comment-form-submit:hover {
|
||||
background: var(--accent-hover);
|
||||
}
|
||||
|
||||
@@ -210,6 +227,7 @@
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.post-action-btn:hover {
|
||||
|
||||
@@ -22,7 +22,7 @@ class Application {
|
||||
this.initStopPropagation();
|
||||
this.initMessageThread();
|
||||
this.initPlatformTags();
|
||||
this.initAttachmentUploaders();
|
||||
this.initAttachmentManagers();
|
||||
}
|
||||
|
||||
loadCSS(href) {
|
||||
@@ -253,8 +253,7 @@ class Application {
|
||||
}
|
||||
|
||||
initCommentReply() {
|
||||
document.querySelectorAll(".comment-action-btn").forEach((btn) => {
|
||||
if (btn.textContent.trim() !== "Reply") return;
|
||||
document.querySelectorAll("[data-action='reply']").forEach((btn) => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
const comment = btn.closest(".comment");
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
class AttachmentUploader {
|
||||
constructor(form) {
|
||||
this.form = form;
|
||||
this.attachments = [];
|
||||
this.maxSize = parseInt(form.dataset.maxSize || "10", 10) * 1024 * 1024;
|
||||
this.maxFiles = parseInt(form.dataset.maxFiles || "10", 10);
|
||||
this.allowedTypes = (form.dataset.allowedTypes || "").split(",").map((t) => t.trim()).filter(Boolean);
|
||||
this.container = form.querySelector(".attachment-upload-container");
|
||||
if (!this.container) return;
|
||||
this.uploadedUidsInput = null;
|
||||
this.initZone();
|
||||
}
|
||||
|
||||
initZone() {
|
||||
this.button = document.createElement("button");
|
||||
this.button.type = "button";
|
||||
this.button.className = "attachment-upload-btn";
|
||||
this.button.innerHTML = "📎";
|
||||
this.button.title = "Attach files";
|
||||
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.multiple = true;
|
||||
input.addEventListener("change", () => this.handleFiles(input.files));
|
||||
this.button.appendChild(input);
|
||||
|
||||
this.button.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
input.click();
|
||||
});
|
||||
|
||||
this.button.addEventListener("dragover", (e) => {
|
||||
e.preventDefault();
|
||||
this.button.classList.add("dragover");
|
||||
});
|
||||
this.button.addEventListener("dragleave", () => {
|
||||
this.button.classList.remove("dragover");
|
||||
});
|
||||
this.button.addEventListener("drop", (e) => {
|
||||
e.preventDefault();
|
||||
this.button.classList.remove("dragover");
|
||||
this.handleFiles(e.dataTransfer.files);
|
||||
});
|
||||
|
||||
this.previewList = document.createElement("div");
|
||||
this.previewList.className = "attachment-preview-list";
|
||||
this.errorEl = document.createElement("div");
|
||||
this.errorEl.className = "attachment-upload-error";
|
||||
|
||||
this.container.appendChild(this.button);
|
||||
|
||||
this.container.after(this.errorEl);
|
||||
this.container.after(this.previewList);
|
||||
|
||||
this.uploadedUidsInput = document.createElement("input");
|
||||
this.uploadedUidsInput.type = "hidden";
|
||||
this.uploadedUidsInput.name = "attachment_uids";
|
||||
this.form.appendChild(this.uploadedUidsInput);
|
||||
}
|
||||
|
||||
async handleFiles(files) {
|
||||
this.errorEl.textContent = "";
|
||||
const remaining = this.maxFiles - this.attachments.length;
|
||||
if (files.length > remaining) {
|
||||
this.showError(`You can only add ${remaining} more file(s).`);
|
||||
return;
|
||||
}
|
||||
for (const file of files) {
|
||||
if (file.size > this.maxSize) {
|
||||
this.showError(`"${file.name}" exceeds the ${this.form.dataset.maxSize || 10}MB limit.`);
|
||||
continue;
|
||||
}
|
||||
if (this.allowedTypes.length > 0) {
|
||||
const ext = "." + file.name.split(".").pop().toLowerCase();
|
||||
if (!this.allowedTypes.includes(ext)) {
|
||||
this.showError(`"${file.name}" type is not allowed.`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
await this.uploadFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
async uploadFile(file) {
|
||||
this.button.classList.add("uploading");
|
||||
const preview = this.createPreview(file);
|
||||
this.previewList.appendChild(preview);
|
||||
const progressBar = preview.querySelector(".attachment-progress-bar");
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
try {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("POST", "/uploads/upload");
|
||||
xhr.upload.addEventListener("progress", (e) => {
|
||||
if (e.lengthComputable && progressBar) {
|
||||
const pct = Math.round((e.loaded / e.total) * 100);
|
||||
progressBar.style.width = pct + "%";
|
||||
}
|
||||
});
|
||||
|
||||
const result = await new Promise((resolve, reject) => {
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
try { resolve(JSON.parse(xhr.responseText)); }
|
||||
catch { reject(new Error("Invalid response")); }
|
||||
} else {
|
||||
reject(new Error(xhr.responseText || "Upload failed"));
|
||||
}
|
||||
};
|
||||
xhr.onerror = () => reject(new Error("Network error"));
|
||||
xhr.send(formData);
|
||||
});
|
||||
|
||||
if (result && result.uid) {
|
||||
this.attachments.push(result);
|
||||
this.updateUids();
|
||||
preview.dataset.uid = result.uid;
|
||||
if (result.thumbnail_url) {
|
||||
const img = preview.querySelector("img");
|
||||
if (img) img.src = result.thumbnail_url;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
this.showError(err.message);
|
||||
preview.remove();
|
||||
} finally {
|
||||
this.button.classList.remove("uploading");
|
||||
}
|
||||
}
|
||||
|
||||
createPreview(file) {
|
||||
const div = document.createElement("div");
|
||||
div.className = "attachment-preview";
|
||||
const isImage = file.type && file.type.startsWith("image/");
|
||||
if (isImage) {
|
||||
const img = document.createElement("img");
|
||||
img.src = URL.createObjectURL(file);
|
||||
img.alt = file.name;
|
||||
div.appendChild(img);
|
||||
} else {
|
||||
const iconDiv = document.createElement("div");
|
||||
iconDiv.className = "file-icon";
|
||||
iconDiv.innerHTML = `<span class="icon">📎</span><span>${file.name}</span>`;
|
||||
div.appendChild(iconDiv);
|
||||
}
|
||||
const remove = document.createElement("button");
|
||||
remove.type = "button";
|
||||
remove.className = "attachment-remove";
|
||||
remove.innerHTML = "×";
|
||||
remove.addEventListener("click", () => {
|
||||
const idx = this.attachments.findIndex((a) => a.uid === div.dataset.uid);
|
||||
if (idx !== -1) {
|
||||
this.attachments.splice(idx, 1);
|
||||
this.updateUids();
|
||||
}
|
||||
div.remove();
|
||||
});
|
||||
div.appendChild(remove);
|
||||
const progress = document.createElement("div");
|
||||
progress.className = "attachment-progress";
|
||||
progress.innerHTML = '<div class="attachment-progress-bar" style="width:0%"></div>';
|
||||
div.appendChild(progress);
|
||||
return div;
|
||||
}
|
||||
|
||||
updateUids() {
|
||||
const uids = this.attachments.map((a) => a.uid);
|
||||
this.uploadedUidsInput.value = uids.join(",");
|
||||
}
|
||||
|
||||
showError(msg) {
|
||||
this.errorEl.textContent = msg;
|
||||
setTimeout(() => { if (this.errorEl.textContent === msg) this.errorEl.textContent = ""; }, 5000);
|
||||
}
|
||||
}
|
||||
|
||||
window.AttachmentUploader = AttachmentUploader;
|
||||
@@ -1,13 +1,15 @@
|
||||
class GistEditor {
|
||||
constructor() {
|
||||
constructor(textareaId, langSelectId) {
|
||||
this.editor = null;
|
||||
this.textareaId = textareaId || "gist-source-editor";
|
||||
this.langSelectId = langSelectId || "gist-language";
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
if (typeof CodeMirror === "undefined") return;
|
||||
|
||||
const textarea = document.getElementById("gist-source-editor");
|
||||
const textarea = document.getElementById(this.textareaId);
|
||||
if (!textarea || textarea.dataset.cminit) return;
|
||||
textarea.dataset.cminit = "1";
|
||||
|
||||
@@ -31,7 +33,7 @@ class GistEditor {
|
||||
|
||||
this.editor.setSize(null, 400);
|
||||
|
||||
const langSelect = document.getElementById("gist-language");
|
||||
const langSelect = document.getElementById(this.langSelectId);
|
||||
if (langSelect) {
|
||||
langSelect.addEventListener("change", () => {
|
||||
let mode = langSelect.value;
|
||||
|
||||
Reference in New Issue
Block a user