// Inline Comment Panel Component
class InlineCommentPanel {
constructor(options = {}) {
this.container = null;
this.thread = null;
this.canResolve = options.canResolve || false;
this.onReply = options.onReply || (() => {});
this.onResolve = options.onResolve || (() => {});
this.currentUser = options.currentUser;
}
initialize(containerId) {
this.container = document.getElementById(containerId);
if (!this.container) {
throw new Error(`Container element with id "${containerId}" not found`);
}
}
setThread(thread) {
this.thread = thread;
this.render();
}
render() {
if (!this.container || !this.thread) return;
this.container.innerHTML = `
`;
this.attachEventListeners();
}
renderComments(comments) {
return comments.map(comment => `
`).join('');
}
renderCommentActions(comment) {
if (this.thread.is_resolved) return '';
return `
`;
}
formatCommentContent(content) {
// Replace @mentions with styled spans
content = content.replace(/@(\w+)/g, '@$1');
// Convert URLs to links
content = content.replace(
/(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/g,
'$1'
);
return content;
}
formatAnchor(anchor) {
const start = anchor.line_start;
const end = anchor.line_end;
const file = anchor.file_path.split('/').pop();
return end > start ?
`${file}:${start}-${end}` :
`${file}:${start}`;
}
formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString();
}
attachEventListeners() {
const replyInput = this.container.querySelector('.reply-input');
if (replyInput) {
replyInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {
this.submitReply();
}
});
}
}
async submitReply() {
const input = this.container.querySelector('.reply-input');
const content = input.value.trim();
if (!content) return;
try {
await this.onReply(content);
input.value = '';
} catch (error) {
console.error('Failed to submit reply:', error);
// Show error message to user
}
}
async resolveThread() {
try {
await this.onResolve();
this.render();
} catch (error) {
console.error('Failed to resolve thread:', error);
// Show error message to user
}
}
showReplyForm(commentId) {
const comment = this.container.querySelector(`[data-comment-id="${commentId}"]`);
if (!comment) return;
const replyForm = document.createElement('div');
replyForm.className = 'reply-form nested';
replyForm.innerHTML = `
`;
comment.appendChild(replyForm);
replyForm.querySelector('.reply-input').focus();
}
hideReplyForm(commentId) {
const comment = this.container.querySelector(`[data-comment-id="${commentId}"]`);
if (!comment) return;
const replyForm = comment.querySelector('.reply-form');
if (replyForm) {
replyForm.remove();
}
}
async submitNestedReply(parentId) {
const comment = this.container.querySelector(`[data-comment-id="${parentId}"]`);
if (!comment) return;
const input = comment.querySelector('.reply-input');
const content = input.value.trim();
if (!content) return;
try {
await this.onReply(content, parentId);
this.hideReplyForm(parentId);
} catch (error) {
console.error('Failed to submit reply:', error);
// Show error message to user
}
}
editComment(commentId) {
const comment = this.container.querySelector(`[data-comment-id="${commentId}"]`);
if (!comment) return;
const contentDiv = comment.querySelector('.comment-content');
const content = contentDiv.textContent;
contentDiv.innerHTML = `
`;
}
cancelEdit(commentId) {
// Refresh the entire thread to restore original content
this.render();
}
async saveEdit(commentId) {
const comment = this.container.querySelector(`[data-comment-id="${commentId}"]`);
if (!comment) return;
const input = comment.querySelector('.edit-input');
const content = input.value.trim();
if (!content) return;
try {
// Emit edit event
const event = new CustomEvent('comment-edited', {
detail: { commentId, content }
});
this.container.dispatchEvent(event);
// Refresh the thread
this.render();
} catch (error) {
console.error('Failed to edit comment:', error);
// Show error message to user
}
}
}
export default InlineCommentPanel;