(async () => {
const sleep = (ms)=>new Promise(r=>setTimeout(r,ms));
// 1) 무한 스크롤로 전체 로드
let last=-1, same=0;
for (let i=0;i<2000;i++){
window.scrollBy(0,2000);
await sleep(160);
const h=document.documentElement.scrollHeight;
if(h===last){ if(++same>=5) break; } else { same=0; last=h; }
}
window.scrollTo(0,0);
// 2) "Show more/더보기" 자동 펼치기
const openTexts=['더보기','전체 보기','펼치기','Show more','Show More','Read more','Expand','View more'];
document.querySelectorAll('button,a,[role="button"]').forEach(el=>{
const t=(el.innerText||el.ariaLabel||'').trim().toLowerCase();
if(t && openTexts.some(x=>t.includes(x.toLowerCase()))){ try{el.click();}catch{} }
});
// 3) 최상위 Turn만 선택
const turns = Array.from(document.querySelectorAll('article[data-testid^="conversation-turn-"], [data-testid^="conversation-turn-"]'))
.filter(el => el.offsetParent !== null);
if (!turns.length) { alert('대화 메시지를 찾지 못했습니다.'); return; }
// 4) 새 문서
const w = window.open('', '_blank'); if (!w){ alert('팝업 차단 해제 후 재시도'); return; }
const css = `
@page { margin: 12mm; }
body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Apple SD Gothic Neo,Malgun Gothic,Arial,sans-serif;
background:#fff;color:#111;-webkit-print-color-adjust:exact;print-color-adjust:exact;margin:0;}
.wrap{max-width:900px;margin:0 auto;padding:8mm 0;}
.msg{margin:14px 0;page-break-inside:avoid;break-inside:avoid;}
.from{font-weight:700;margin:2px 0 6px;}
.bubble{border:1px solid #ddd;border-radius:10px;padding:12px 14px;}
/* 코드/프리포맷은 '원본 유지'를 위해 white-space 강제 변경하지 않음 */
img,video,canvas,svg{max-width:100%;height:auto;display:block;}
a[href]::after{content:"";}
.sr-only,[aria-hidden="true"],[hidden]{display:none !important;}
/* 리스트 간격 최소화 */
ul, ol { margin: 0 0 0 1.5em !important; padding: 0 !important; }
li { margin: 0 !important; padding: 0 !important; }
li + li { margin-top: 0.2em !important; }
`;
w.document.write(`<!doctype html><html><head><meta charset="utf-8"><title>ChatGPT Export</title><style>${css}</style></head><body><div class="wrap" id="root"></div></body></html>`);
const root = w.document.getElementById('root');
// 5) 유틸
const isUserTurn = (el) =>
el.getAttribute('data-message-author-role') === 'user' ||
!!el.querySelector('[data-message-author-role="user"]');
const isAssistantTurn = (el) =>
el.getAttribute('data-message-author-role') === 'assistant' ||
!!el.querySelector('[data-message-author-role="assistant"]');
const contentSelectors = [
'.markdown',
'[data-message-author-role] .whitespace-pre-wrap',
'[data-testid="message-content"]',
'.prose','.content','.text-base'
];
const isVisible = (node) => {
const s = window.getComputedStyle(node);
if (s.display === 'none' || s.visibility === 'hidden' || parseFloat(s.opacity) === 0) return false;
const r = node.getBoundingClientRect(); return (r.width>0 && r.height>0);
};
const pickContent = (turn) => {
for (const sel of contentSelectors) {
const n = turn.querySelector(sel);
if (n && n.innerHTML.trim() && isVisible(n)) return n;
}
return turn;
};
// AI reasoning/trace 패널 제거
function stripReasoning(clone) {
clone.querySelectorAll('[data-testid*="reasoning"],[data-testid*="chain"],[data-testid*="work"]').forEach(n=>n.remove());
const keywords = ['reasoning','chain-of-thought','show work','scratchpad','추론','사고 과정','작업 내역','생각'];
clone.querySelectorAll('details, section, div').forEach(node=>{
const sum = node.querySelector?.('summary');
const label = (sum?.innerText || node.getAttribute?.('aria-label') || '').toLowerCase();
if (label && keywords.some(k=>label.includes(k))) node.remove();
});
clone.querySelectorAll('[style*="height: 0"],[style*="opacity: 0"]').forEach(n=>{
if (!n.querySelector('.markdown') && (n.innerText||'').trim().length<10) n.remove();
});
return clone;
}
const seenIds = new Set();
const seenHashes = new Set();
const hash = (s) => { let h=0; for (let i=0;i<s.length;i++){ h=(h<<5)-h + s.charCodeAt(i); h|=0; } return h; };
// 6) 순서대로 삽입
for (const turn of turns) {
const msgId = turn.getAttribute('data-message-id') || turn.dataset?.messageId;
if (msgId && seenIds.has(msgId)) continue;
const role = isUserTurn(turn) ? 'You' : (isAssistantTurn(turn) ? 'AI' : 'AI');
const node = pickContent(turn);
let clone = node.cloneNode(true);
if (role === 'AI') clone = stripReasoning(clone);
// 버튼류 제거
clone.querySelectorAll('button,[role="button"],[data-testid*="toolbar"],[data-testid*="actions"]').forEach(b=>b.remove());
let html;
if (role === 'You') {
// ✅ YOU: 전체 줄바꿈 보존, 단 코드블록은 예외(원본 유지)
const codeBlocks = [];
clone.querySelectorAll('pre, code').forEach((el,i)=>{
codeBlocks[i] = el.outerHTML; // 원본 보관
el.outerHTML = `%%%CODEBLOCK_${i}%%%`; // 마커로 대체
});
html = clone.innerHTML.split('\n').map(line=>line||' ').join('<br>');
codeBlocks.forEach((code,i)=>{
html = html.replace(`%%%CODEBLOCK_${i}%%%`, code); // 원복
});
} else {
// ✅ AI: '원본 그대로' 유지 → 코드블록도 변환하지 않음
html = clone.innerHTML.trim();
}
const h = hash(html);
if (seenHashes.has(h)) continue;
if (msgId) seenIds.add(msgId);
seenHashes.add(h);
const sec = w.document.createElement('section'); sec.className='msg';
const from = w.document.createElement('div'); from.className='from'; from.textContent = role;
const bubble = w.document.createElement('div'); bubble.className='bubble'; bubble.innerHTML = html;
sec.appendChild(from); sec.appendChild(bubble); root.appendChild(sec);
}
// 7) 인쇄
try { await w.document.fonts.ready; } catch {}
await Promise.allSettled(Array.from(w.document.images).map(img => img.decode?.().catch(()=>{})));
w.focus(); w.print();
})();
(async () => {
const sleep = (ms)=>new Promise(r=>setTimeout(r,ms));
// 1) 무한 스크롤 로드
let last=-1, same=0;
for (let i=0;i<2000;i++){
window.scrollBy(0,2000);
await sleep(150);
const h=document.documentElement.scrollHeight;
if(h===last){ if(++same>=5) break; } else { same=0; last=h; }
}
window.scrollTo(0,0);
// 2) "더보기/Show more" 자동 펼치기
const openTexts=['더보기','전체 보기','펼치기','Show more','Show More','Read more','Expand','View more'];
for (const el of document.querySelectorAll('button,a,[role="button"]')) {
const t=(el.innerText||el.ariaLabel||'').trim();
if (t && openTexts.some(x=>t.includes(x))) { try{ el.click(); await sleep(30); }catch{} }
}
// 3) 새 문서
const w = window.open('', '_blank');
if (!w) { alert('팝업 차단을 해제하세요.'); return; }
const css = `
@page { margin: 12mm; }
body { font-family: system-ui,-apple-system,Segoe UI,Roboto,Apple SD Gothic Neo,Malgun Gothic,Arial,sans-serif;
background:#fff;color:#111;-webkit-print-color-adjust:exact;print-color-adjust:exact;margin:0; }
.wrap { max-width: 900px; margin: 0 auto; padding: 8mm 0; }
.msg { margin: 14px 0; page-break-inside: avoid; break-inside: avoid; }
.from { font-weight: 700; margin: 2px 0 6px; }
.bubble { border: 1px solid #ddd; border-radius: 10px; padding: 12px 14px; }
pre, code { white-space: pre-wrap; word-break: break-word; }
img,video,canvas,svg { max-width:100%; height:auto; display:block; }
a[href]::after { content:""; }
/* 리스트 간격 최소화 */
ul, ol { margin: 0 0 0 1.5em !important; padding: 0 !important; }
li { margin: 0 !important; padding: 0 !important; }
li + li { margin-top: 0.2em !important; }
/* === 가로 스크롤 방지 === */
.wrap, .bubble { max-width: 100% !important; overflow-x: hidden !important; }
pre, code, table {
max-width: 100% !important;
white-space: pre-wrap !important;
word-wrap: break-word !important;
overflow-x: visible !important;
}
img, svg { max-width: 100% !important; height: auto !important; }
`;
w.document.write(`<!doctype html><html><head><meta charset="utf-8"><title>Claude Export</title><style>${css}</style></head><body><div class="wrap" id="root"></div></body></html>`);
const root = w.document.getElementById('root');
// 4) 본문 루트
const srcRoot = document.querySelector('main, [role="main"], .conversation, .messages, .messages-container') || document.body;
// 5) AI 추론/계획 패널 제거
function stripReasoning(clone) {
clone.querySelectorAll('button[class*="group/row"]').forEach(btn=>{
const box = btn.closest('.transition-all, .rounded-lg, .flex, .font-ui') || btn.parentElement;
if (box && box.parentElement) box.remove();
});
clone.querySelectorAll('.overflow-hidden.shrink-0, [tabindex="-1"][style*="height: 0"]').forEach(el=>el.remove());
clone.querySelectorAll('[data-testid*="trace"], .trace, .reasoning, .hidden-trace').forEach(el=>el.remove());
return clone;
}
// 6) 순서대로 메시지 복제
const walker = document.createTreeWalker(srcRoot, NodeFilter.SHOW_ELEMENT, null);
while (walker.nextNode()) {
const el = walker.currentNode;
if (!el.matches) continue;
if (!el.matches('[data-testid="user-message"], .font-claude-response')) continue;
const isUser = el.matches('[data-testid="user-message"]');
let clone = el.cloneNode(true);
if (!isUser) clone = stripReasoning(clone);
let html;
if (isUser) {
// ✅ YOU: 무조건 줄바꿈 보존, 단 코드블록 예외
const codeBlocks = [];
clone.querySelectorAll('pre, code').forEach((el,i)=>{
codeBlocks[i] = el.outerHTML;
el.outerHTML = `%%%CODEBLOCK_${i}%%%`;
});
html = clone.innerHTML.split('\n').map(line=>line||' ').join('<br>');
codeBlocks.forEach((code,i)=>{
html = html.replace(`%%%CODEBLOCK_${i}%%%`, code);
});
} else {
// ✅ AI: 현상태 유지, 코드블록만 줄바꿈 보존
clone.querySelectorAll('pre, code').forEach(codeEl => {
const raw = codeEl.textContent;
if (raw && raw.includes('\n')) {
const replaced = raw.split('\n').map(line => line || ' ').join('<br>');
codeEl.innerHTML = replaced;
}
});
html = clone.innerHTML.trim();
}
const sec = w.document.createElement('section'); sec.className='msg';
const from = w.document.createElement('div'); from.className='from'; from.textContent = isUser ? 'You' : 'AI';
const bubble = w.document.createElement('div'); bubble.className='bubble'; bubble.innerHTML = html;
sec.appendChild(from); sec.appendChild(bubble); root.appendChild(sec);
}
// 7) 인쇄
try { await w.document.fonts.ready; } catch {}
await Promise.allSettled(Array.from(w.document.images).map(img => img.decode?.().catch(()=>{})));
w.focus(); w.print();
})();
클로드 exporter가 횟수 제한 걸려서... GPT한테 대화 내용 추출하는 스크립트 만들라고 시켰다.
F12 + console 창에 위 스크립트를 실행시키면 PDF 파일로 저장이 가능하다.
단, CSS 유지는 되지 않는다.