/**
* 메모 카드 컴포넌트
*/
import { escapeHTML, parseInternalLinks, fixImagePaths } from '../utils.js';
import { renderAttachmentBox } from './AttachmentBox.js';
import { Constants } from '../utils/Constants.js';
import { I18nManager } from '../utils/I18nManager.js';
/**
* 단일 메모 카드의 HTML 생성을 전담합니다.
*/
export function createMemoCardHtml(memo, isDone) {
const cardClass = `memo-card ${isDone ? 'done' : ''} ${memo.is_encrypted ? 'encrypted' : ''} glass-panel`;
const borderStyle = memo.color ? `style="border-left: 5px solid ${memo.color}"` : '';
let summaryHtml = '';
if (memo.summary) {
// 암호화된 메모가 잠긴 상태라면 AI 요약도 숨김 (정보 유출 방지)
const isLocked = memo.is_encrypted && (!memo.content || memo.content.includes('encrypted-block') || typeof memo.is_encrypted === 'number');
// 참고: app.js에서 해독 성공 시 memo.is_encrypted를 false로 바꿨으므로, is_encrypted가 true면 잠긴 상태임
if (!memo.is_encrypted) {
summaryHtml = `
${I18nManager.t('label_ai_summary')}: ${escapeHTML(memo.summary)}
`;
}
}
const titleHtml = memo.title ? `${escapeHTML(memo.title)}
` : '';
let htmlContent = '';
if (!isDone) {
if (memo.is_encrypted) {
htmlContent = `
🔒
${I18nManager.t('msg_encrypted_locked')}
`;
} else {
// 본문에서 하단 메타데이터 블록(--- 이후)을 제외하고 렌더링 (중복 표시 방지)
let content = memo.content || '';
const footerIndex = content.lastIndexOf('\n\n---\n');
const displayContent = footerIndex !== -1 ? content.substring(0, footerIndex) : content;
// marked로 파싱한 후 DOMPurify로 살균하여 XSS 방지
htmlContent = DOMPurify.sanitize(marked.parse(displayContent));
htmlContent = parseInternalLinks(htmlContent);
htmlContent = fixImagePaths(htmlContent);
}
}
const contentHtml = `${htmlContent}
`;
let metaHtml = '';
if (!isDone && memo.group_name && memo.group_name !== Constants.GROUPS.DEFAULT) {
const groupName = (Object.values(Constants.GROUPS).includes(memo.group_name))
? I18nManager.t(`groups.${memo.group_name}`)
: memo.group_name;
metaHtml += `📁 ${escapeHTML(groupName)}`;
}
if (memo.tags && memo.tags.length > 0) {
memo.tags.forEach(t => {
// 암호화된 메모가 잠긴 상태일 때 AI 태그만 선택적으로 숨김
if (memo.is_encrypted && t.source === 'ai') return;
const typeClass = t.source === 'ai' ? 'tag-ai' : 'tag-user';
metaHtml += `${t.source === 'ai' ? '🪄 ' : '#'}${escapeHTML(t.name)}`;
});
}
metaHtml += '
';
let linksHtml = '';
if (!isDone && memo.backlinks && memo.backlinks.length > 0) {
linksHtml = `🔗 ${I18nManager.t('label_mentioned')}: ` +
memo.backlinks.map(l => `#${escapeHTML(l.title || l.id.toString())}`).join(', ') +
'
';
}
// 암호화된 메모인 경우 해독 전까지 첨부파일 목록 숨김
const attachmentsHtml = !memo.is_encrypted ? renderAttachmentBox(memo.attachments) : '';
// 암호화된 메모가 잠긴 상태라면 하단 액션 버튼(수정, 삭제, AI 등)을 아예 보여주지 않음 (보안 및 UI 겹침 방지)
const isLocked = memo.is_encrypted && (!htmlContent || htmlContent.includes('encrypted-block'));
const actionsHtml = isLocked ? '' : `
${!isDone ? `` : ''}
`;
const idBadge = `#${memo.id}
`;
return {
className: cardClass,
style: borderStyle,
innerHtml: idBadge + summaryHtml + titleHtml + metaHtml + contentHtml + linksHtml + attachmentsHtml + actionsHtml
};
}