782 lines
38 KiB
HTML
782 lines
38 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>글벗 - AI 문서 자동화 시스템</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@300;400;500;700;900&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="/static/css/editor.css">
|
|
<link rel="stylesheet" href="/static/css/main.css">
|
|
|
|
<style>
|
|
/* 목차 애니메이션 */
|
|
.toc-display-area {
|
|
padding: 20px 32px;
|
|
overflow-y: auto;
|
|
max-height: calc(100vh - 200px);
|
|
}
|
|
.toc-anim-item {
|
|
padding: 14px 18px;
|
|
background: var(--ui-surface, #2a2f38);
|
|
border: 1px solid var(--ui-border, #363a43);
|
|
border-left: 3px solid var(--ui-accent, #4fc3f7);
|
|
border-radius: 8px;
|
|
margin-bottom: 10px;
|
|
}
|
|
.toc-anim-num {
|
|
font-size: 12px;
|
|
color: var(--ui-accent, #4fc3f7);
|
|
font-weight: 600;
|
|
margin-bottom: 4px;
|
|
}
|
|
.toc-anim-title {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: var(--ui-text, #e8eaed);
|
|
margin-bottom: 6px;
|
|
}
|
|
.toc-anim-guide {
|
|
font-size: 12px;
|
|
color: var(--ui-dim, #9aa0a6);
|
|
line-height: 1.6;
|
|
}
|
|
.toc-anim-keywords {
|
|
display: flex;
|
|
gap: 6px;
|
|
flex-wrap: wrap;
|
|
margin-top: 8px;
|
|
}
|
|
.toc-anim-keyword {
|
|
font-size: 11px;
|
|
padding: 2px 8px;
|
|
background: rgba(79, 195, 247, 0.1);
|
|
color: var(--ui-accent, #4fc3f7);
|
|
border-radius: 4px;
|
|
}
|
|
.toc-anim-contents {
|
|
margin-top: 8px;
|
|
padding: 8px 10px;
|
|
background: rgba(255,255,255,0.03);
|
|
border-radius: 4px;
|
|
border: 1px solid rgba(79, 195, 247, 0.08);
|
|
}
|
|
.toc-anim-content-item {
|
|
font-size: 11px;
|
|
color: var(--ui-dim, #9aa0a6);
|
|
line-height: 1.7;
|
|
padding-left: 4px;
|
|
}
|
|
.toc-loading {
|
|
display: flex;
|
|
gap: 6px;
|
|
justify-content: center;
|
|
padding: 24px;
|
|
}
|
|
.toc-loading-dot {
|
|
width: 8px;
|
|
height: 8px;
|
|
background: var(--ui-accent, #4fc3f7);
|
|
border-radius: 50%;
|
|
animation: tocDotPulse 1.2s infinite;
|
|
}
|
|
.toc-loading-dot:nth-child(2) { animation-delay: 0.2s; }
|
|
.toc-loading-dot:nth-child(3) { animation-delay: 0.4s; }
|
|
@keyframes tocDotPulse {
|
|
0%, 60%, 100% { opacity: 0.3; transform: scale(0.8); }
|
|
30% { opacity: 1; transform: scale(1); }
|
|
}
|
|
|
|
/* 생성 진행률 */
|
|
.gen-progress-area {
|
|
padding: 16px 32px;
|
|
}
|
|
.gen-progress-header {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--ui-text, #e8eaed);
|
|
margin-bottom: 10px;
|
|
}
|
|
.gen-progress-bar-wrap {
|
|
height: 4px;
|
|
background: var(--ui-border, #363a43);
|
|
border-radius: 2px;
|
|
overflow: hidden;
|
|
}
|
|
.gen-progress-bar {
|
|
height: 100%;
|
|
background: linear-gradient(90deg, var(--ui-accent, #4fc3f7), #66bb6a);
|
|
border-radius: 2px;
|
|
transition: width 0.5s ease;
|
|
}
|
|
.gen-progress-text {
|
|
font-size: 12px;
|
|
color: var(--ui-dim, #9aa0a6);
|
|
margin-top: 8px;
|
|
}
|
|
|
|
/* AI 수정 버튼 활성 상태 */
|
|
#aiEditToolbarBtn.has-selection {
|
|
background: rgba(79, 195, 247, 0.15);
|
|
color: var(--ui-accent, #4fc3f7);
|
|
border-color: var(--ui-accent, #4fc3f7);
|
|
}
|
|
|
|
/* ===== 도메인 선택 ===== */
|
|
.domain-select-box {
|
|
background: var(--ui-surface, #2a2f38);
|
|
border: 1px solid var(--ui-border, #363a43);
|
|
border-radius: 8px; padding: 10px 12px; cursor: pointer;
|
|
}
|
|
.domain-select-box:hover { border-color: var(--ui-accent, #4fc3f7); }
|
|
.domain-display-text { font-size: 12px; color: var(--ui-text); margin-top: 6px; }
|
|
.domain-display-text.auto { color: var(--ui-dim, #9aa0a6); font-style: italic; }
|
|
.domain-display-text.selected { color: var(--ui-accent, #4fc3f7); }
|
|
#domainModal {
|
|
position: fixed; top:0; left:0; right:0; bottom:0;
|
|
background: rgba(0,0,0,0.65); display: none;
|
|
align-items: center; justify-content: center; z-index: 10000;
|
|
}
|
|
.domain-modal-content {
|
|
background: var(--ui-bg, #1e2228); border: 1px solid var(--ui-border);
|
|
border-radius: 14px; width: 560px; max-height: 80vh;
|
|
display: flex; flex-direction: column; box-shadow: 0 20px 60px rgba(0,0,0,0.4);
|
|
}
|
|
.domain-modal-header { display: flex; align-items: center; justify-content: space-between; padding: 18px 24px; border-bottom: 1px solid var(--ui-border); }
|
|
.domain-modal-title { font-size: 16px; font-weight: 700; color: var(--ui-text); }
|
|
.domain-modal-close { background: none; border: none; color: var(--ui-dim); font-size: 20px; cursor: pointer; }
|
|
.domain-modal-body { padding: 16px 24px; overflow-y: auto; flex: 1; }
|
|
.domain-modal-desc { font-size: 13px; color: var(--ui-dim); margin-bottom: 16px; line-height: 1.6; }
|
|
.domain-category { margin-bottom: 10px; }
|
|
.domain-cat-label {
|
|
display: flex; align-items: center; gap: 10px;
|
|
padding: 12px 14px; background: var(--ui-surface, #2a2f38);
|
|
border: 1px solid var(--ui-border); border-radius: 8px; cursor: pointer;
|
|
}
|
|
.domain-cat-label:hover { border-color: var(--ui-accent); }
|
|
.domain-cat-label.checked { background: rgba(79,195,247,0.08); border-color: var(--ui-accent); }
|
|
.domain-cat-label input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--ui-accent); }
|
|
.domain-cat-icon { font-size: 20px; }
|
|
.domain-cat-text { flex: 1; }
|
|
.domain-cat-name { display: block; font-size: 14px; font-weight: 600; color: var(--ui-text); }
|
|
.domain-cat-desc { display: block; font-size: 11px; color: var(--ui-dim); margin-top: 2px; }
|
|
.domain-cat-expand { font-size: 10px; color: var(--ui-dim); transition: transform 0.2s; }
|
|
.domain-cat-expand.open { transform: rotate(180deg); }
|
|
.domain-sub-panel { padding: 10px 14px 10px 40px; border-left: 2px solid var(--ui-accent); margin-left: 20px; margin-top: 4px; }
|
|
.domain-sub-group { margin-bottom: 10px; }
|
|
.domain-sub-group-label { font-size: 11px; font-weight: 600; color: var(--ui-dim); margin-bottom: 6px; }
|
|
.domain-sub-chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
.domain-chip {
|
|
display: inline-flex; align-items: center; gap: 4px;
|
|
padding: 5px 12px; background: var(--ui-surface);
|
|
border: 1px solid var(--ui-border); border-radius: 20px;
|
|
font-size: 12px; color: var(--ui-text); cursor: pointer;
|
|
}
|
|
.domain-chip:hover { border-color: var(--ui-accent); }
|
|
.domain-chip.selected { background: rgba(79,195,247,0.15); border-color: var(--ui-accent); color: var(--ui-accent); }
|
|
.domain-chip input[type="checkbox"] { display: none; }
|
|
.domain-modal-footer { padding: 14px 24px; border-top: 1px solid var(--ui-border); display: flex; align-items: center; justify-content: space-between; }
|
|
.domain-summary-text { font-size: 12px; color: var(--ui-accent); flex: 1; }
|
|
.domain-summary-text.empty { color: var(--ui-dim); font-style: italic; }
|
|
.domain-modal-actions { display: flex; gap: 8px; }
|
|
.domain-btn { padding: 8px 18px; border-radius: 6px; font-size: 13px; font-weight: 600; cursor: pointer; border: 1px solid var(--ui-border); background: var(--ui-surface); color: var(--ui-text); }
|
|
.domain-btn.primary { background: var(--ui-accent); color: #000; border-color: var(--ui-accent); }
|
|
</style>
|
|
|
|
</head>
|
|
<body>
|
|
<!-- 상단 툴바 -->
|
|
<div class="toolbar">
|
|
<div class="toolbar-logo">📝 글벗</div>
|
|
|
|
<div class="toolbar-spacer"></div>
|
|
|
|
<button class="toolbar-btn" id="editModeBtn" onclick="toggleEditMode()">✏️ 편집하기</button>
|
|
<button class="toolbar-btn" id="aiEditToolbarBtn" onclick="triggerAiEdit()" title="AI 수정 (텍스트를 먼저 선택하세요)">🤖 AI 수정</button>
|
|
|
|
<div class="toolbar-divider"></div>
|
|
|
|
<select class="zoom-select" id="zoomSelect" onchange="setZoom(this.value)">
|
|
<option value="50">50%</option>
|
|
<option value="75">75%</option>
|
|
<option value="100" selected>100%</option>
|
|
<option value="125">125%</option>
|
|
<option value="150">150%</option>
|
|
</select>
|
|
|
|
<div class="toolbar-divider"></div>
|
|
|
|
<button class="toolbar-btn" onclick="exportHwp()">📄 HWP 추출</button>
|
|
<button class="toolbar-btn" onclick="saveHtml()">💾 HTML 저장</button>
|
|
<button class="toolbar-btn" onclick="saveHtml()">📊 PPT 저장</button>
|
|
<button class="toolbar-btn" onclick="printDoc()">🖨️ PDF/인쇄</button>
|
|
</div>
|
|
|
|
<!-- 메인 앱 -->
|
|
<div class="app">
|
|
<!-- 좌측 사이드바: 입력 -->
|
|
<div class="sidebar">
|
|
<div class="sidebar-header">
|
|
<div class="sidebar-title">문서 업로드</div>
|
|
<button class="sidebar-btn" onclick="openFolderModal()">
|
|
<span class="icon">📁</span>
|
|
<span>폴더 위치</span>
|
|
</button>
|
|
<button class="sidebar-btn" onclick="openLinkModal()">
|
|
<span class="icon">🔗</span>
|
|
<span>외부 링크</span>
|
|
</button>
|
|
<button class="sidebar-btn" onclick="openHtmlModal()">
|
|
<span class="icon">📋</span>
|
|
<span>HTML 붙여넣기</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="sidebar-content">
|
|
<!-- 참고 파일 확인 -->
|
|
<div>
|
|
<div class="section-title">업로드 파일 검토</div>
|
|
<div class="file-check-box">
|
|
<div class="file-path empty" id="folderPathDisplay">폴더 경로가 설정되지 않음</div>
|
|
<div class="file-check-row">
|
|
<span class="file-check-label">전체 파일</span>
|
|
<span class="file-check-value" id="totalCount">0개</span>
|
|
</div>
|
|
<div class="file-check-row">
|
|
<span class="file-check-label">확인 (변환 가능)</span>
|
|
<span class="file-check-value ok" id="okCount">0개 ✓</span>
|
|
</div>
|
|
<div class="file-check-row">
|
|
<span class="file-check-label">미확인</span>
|
|
<span class="file-check-value warn" id="unknownCount" onclick="toggleUnknownFiles()">0개</span>
|
|
</div>
|
|
|
|
<div class="unknown-files" id="unknownFilesBox">
|
|
<div id="unknownFilesList"></div>
|
|
<button class="open-folder-btn" onclick="openFolder()">📂 폴더 열기</button>
|
|
</div>
|
|
|
|
<div class="file-check-row" style="border-top: 1px solid var(--ui-border); margin-top: 8px; padding-top: 8px;">
|
|
<span class="file-check-label">참고 링크</span>
|
|
<span class="file-check-value" id="linkCount">0개</span>
|
|
</div>
|
|
|
|
<div class="file-check-row">
|
|
<span class="file-check-label">HTML 입력</span>
|
|
<span class="file-check-value" id="htmlInputStatus">없음</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 도메인 지식 선택 -->
|
|
<div id="domainSection" style="display:none;">
|
|
<div class="section-title">도메인 지식 선택</div>
|
|
<div class="domain-select-box" onclick="openDomainModal()">
|
|
<div style="display:flex; justify-content:space-between; font-size:12px; color:var(--ui-dim);">
|
|
<span>📚 업로드 자료의 성격을 선택해주세요</span>
|
|
<span>▶</span>
|
|
</div>
|
|
<div class="domain-display-text auto" id="domainDisplayText">자동 분석 (AI 판단)</div>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- 작성 방식 -->
|
|
<div>
|
|
<div class="section-title">업로드 자료 활용 범위</div>
|
|
<div class="write-mode-box">
|
|
<div class="write-mode-tabs">
|
|
<label class="write-mode-tab" onclick="selectWriteMode('format')">
|
|
<input type="radio" name="writeMode" value="format">
|
|
<div class="write-mode-icon">📄</div>
|
|
<div class="write-mode-label">형식만 <br>변경</div>
|
|
</label>
|
|
<label class="write-mode-tab selected" onclick="selectWriteMode('restructure')">
|
|
<input type="radio" name="writeMode" value="restructure" checked>
|
|
<div class="write-mode-icon">🔄</div>
|
|
<div class="write-mode-label">내용의<br>재구성</div>
|
|
</label>
|
|
<label class="write-mode-tab" onclick="selectWriteMode('new')">
|
|
<input type="radio" name="writeMode" value="new">
|
|
<div class="write-mode-icon">✨</div>
|
|
<div class="write-mode-label">문서 참고 <br> 신규 작성</div>
|
|
</label>
|
|
</div>
|
|
<div class="write-mode-notice">
|
|
<span>⚠️</span>
|
|
<span>문서 유형에 적합하지 않은 콘텐츠 업로드시,<br> 자동 재구성 및 신규 콘텐츠 작성 </span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 진행 상태 -->
|
|
<div>
|
|
<div class="section-title">진행 상태</div>
|
|
<div class="step-list" id="stepList">
|
|
<div class="step-item pending" data-step="0">
|
|
<span class="status">○</span>
|
|
<span>참고 파일 확인</span>
|
|
</div>
|
|
<div class="step-divider"></div>
|
|
<div class="step-item pending" data-step="1">
|
|
<span class="status">○</span>
|
|
<span>Step 1: 파일 변환</span>
|
|
</div>
|
|
<div class="step-item pending" data-step="2">
|
|
<span class="status">○</span>
|
|
<span>Step 2: 텍스트 추출</span>
|
|
</div>
|
|
<div class="step-item pending" data-step="3">
|
|
<span class="status">○</span>
|
|
<span>Step 3: 도메인 분석</span>
|
|
</div>
|
|
<div class="step-item pending" data-step="4">
|
|
<span class="status">○</span>
|
|
<span>Step 4: 청킹/요약</span>
|
|
</div>
|
|
<div class="step-item pending" data-step="5">
|
|
<span class="status">○</span>
|
|
<span>Step 5: RAG 구축</span>
|
|
</div>
|
|
<div class="step-item pending" data-step="6">
|
|
<span class="status">○</span>
|
|
<span>Step 6: Corpus 생성</span>
|
|
</div>
|
|
<div class="step-item pending" data-step="7">
|
|
<span class="status">○</span>
|
|
<span>Step 7: 목차 구성</span>
|
|
</div>
|
|
<div class="step-divider"></div>
|
|
<div class="step-item pending" data-step="8">
|
|
<span class="status">○</span>
|
|
<span>Step 8: 콘텐츠 생성</span>
|
|
</div>
|
|
<div class="step-item pending" data-step="9">
|
|
<span class="status">○</span>
|
|
<span>Step 9: HTML 변환</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 가운데 뷰어 -->
|
|
<div class="main">
|
|
<div class="viewer" id="viewer">
|
|
<div class="a4-wrapper" id="a4Wrapper">
|
|
<div class="a4-preview" id="a4Preview">
|
|
<iframe id="previewFrame" class="preview-iframe"></iframe>
|
|
<div class="placeholder" id="placeholder">
|
|
<div class="icon">📄</div>
|
|
<div class="text">HTML을 입력하고 생성하세요</div>
|
|
<div class="sub-text">좌측에서 HTML 붙여넣기 또는 파일 업로드</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 목차 애니메이션 표시 영역 -->
|
|
<div class="toc-display-area" id="tocDisplayArea" style="display:none;"></div>
|
|
|
|
<!-- 생성 진행률 -->
|
|
<div class="gen-progress-area" id="genProgressArea" style="display:none;">
|
|
<div class="gen-progress-header">📝 문서 생성 중...</div>
|
|
<div class="gen-progress-bar-wrap">
|
|
<div class="gen-progress-bar" id="genProgressBar" style="width:0%"></div>
|
|
</div>
|
|
<div class="gen-progress-text" id="genProgressText">준비 중...</div>
|
|
</div>
|
|
|
|
<!-- 목차 확인 액션바 -->
|
|
<div class="toc-action-bar" id="tocActionBar">
|
|
<button class="toc-action-btn secondary" onclick="editToc()">✏️ 편집</button>
|
|
<button class="toc-action-btn primary" id="approveBtn" onclick="approveToc()">✅ 승인 & 생성하기</button>
|
|
</div>
|
|
|
|
<!-- 하단 피드백 바 -->
|
|
<div class="feedback-bar" id="feedbackBar">
|
|
<input type="text" class="feedback-input" id="feedbackInput" placeholder="수정 요청사항을 입력하세요... (예: 5 Step을 첨부로 이동해줘)">
|
|
<button class="feedback-btn primary" id="feedbackBtn" onclick="submitFeedback()">
|
|
<span id="feedbackBtnText">🔄 수정 반영</span>
|
|
<span id="feedbackSpinner" class="spinner" style="display:none;"></span>
|
|
</button>
|
|
<button class="feedback-btn secondary" onclick="regenerate()">🗑️ 다시 생성</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 우측 패널: 옵션 -->
|
|
<div class="right-panel">
|
|
<div class="panel-header">
|
|
<span class="panel-title">문서 설정</span>
|
|
</div>
|
|
<div class="panel-body">
|
|
<!-- ===== 문서 유형 (동적 로드) ===== -->
|
|
<div class="option-section">
|
|
<div class="option-title">문서 유형</div>
|
|
<div class="doc-type-list loading" id="docTypeList">
|
|
<!-- API에서 동적으로 로드 -->
|
|
</div>
|
|
|
|
<!-- 문서 유형 추가 버튼 -->
|
|
<button class="add-template-btn" onclick="openDocTypeModal()">+ 문서 유형 추가</button>
|
|
</div>
|
|
|
|
<!-- ===== 문서 유형별 옵션 (동적) ===== -->
|
|
<div id="docTypeOptionsContainer">
|
|
<!-- selectDocType() 호출 시 동적으로 렌더링 -->
|
|
</div>
|
|
|
|
<!-- ===== 템플릿 선택 ===== -->
|
|
<div class="option-section">
|
|
<div class="option-title">템플릿</div>
|
|
<div class="template-list">
|
|
<div class="template-item selected" data-template="default" onclick="selectTemplate('default')">
|
|
<input type="radio" name="template" checked>
|
|
<span class="label">📄 기본 템플릿</span>
|
|
</div>
|
|
<div id="userTemplatesListNew"></div>
|
|
</div>
|
|
<button class="add-template-btn" onclick="openTemplateModal()">+ 템플릿 추가</button>
|
|
|
|
<div id="templateElementOptions" class="template-elements" style="display:none;">
|
|
<div class="elements-title">적용 요소</div>
|
|
<div class="elements-list"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ===== 요청사항 ===== -->
|
|
<div class="option-section">
|
|
<div class="option-title">요청사항</div>
|
|
<textarea class="request-textarea" id="globalInstructionInput"
|
|
placeholder="예: 표를 더 상세하게 예: 전문 용어 풀어서 설명"></textarea>
|
|
</div>
|
|
|
|
<!-- ===== 생성 버튼 ===== -->
|
|
<button class="generate-btn" id="generateBtn" onclick="generate()" disabled>
|
|
<span id="generateBtnText">🚀 생성하기</span>
|
|
<div class="loading-spinner" id="generateSpinner" style="display:none;"></div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 상태바 -->
|
|
<div class="status-bar">
|
|
<div class="status-left">
|
|
<span class="status-dot" id="statusDot"></span>
|
|
<span id="statusMessage">준비됨</span>
|
|
</div>
|
|
<div id="statusRight">글벗 Light v2.1</div>
|
|
</div>
|
|
|
|
<!-- HTML 입력 모달 -->
|
|
<div class="modal-overlay" id="htmlModal">
|
|
<div class="modal">
|
|
<div class="modal-header">📋 HTML 붙여넣기</div>
|
|
<div class="modal-body">
|
|
<textarea class="modal-textarea" id="htmlContent" placeholder="HTML 코드를 붙여넣으세요..."></textarea>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="modal-btn" onclick="closeHtmlModal()">취소</button>
|
|
<button class="modal-btn primary" onclick="submitHtml()">확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 폴더 모달 -->
|
|
<div class="modal-overlay" id="folderModal">
|
|
<div class="modal">
|
|
<div class="modal-header">📁 폴더 위치 입력</div>
|
|
<div class="modal-body">
|
|
<input type="text" id="folderPath" class="modal-input" placeholder="C:\Users\...\Documents\참고자료" style="width:100%; padding:12px; border-radius:6px; border:1px solid var(--ui-border); background:var(--ui-bg); color:var(--ui-text); font-size:13px;">
|
|
<p style="margin-top:10px; font-size:11px; color:var(--ui-dim);">* 로컬 폴더 경로를 입력하세요. (Engine 실행 필요)</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="modal-btn" onclick="closeFolderModal()">취소</button>
|
|
<button class="modal-btn primary" onclick="submitFolder()">확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 참고 링크 모달 -->
|
|
<div class="modal-overlay" id="linkModal">
|
|
<div class="modal">
|
|
<div class="modal-header">🔗 참고 링크 입력</div>
|
|
<div class="modal-body">
|
|
<div id="linkInputList">
|
|
<input type="text" class="link-input" placeholder="https://..." style="width:100%; padding:10px; border-radius:6px; border:1px solid var(--ui-border); background:var(--ui-bg); color:var(--ui-text); font-size:12px; margin-bottom:8px;">
|
|
</div>
|
|
<button onclick="addLinkInput()" style="padding:8px 12px; border-radius:4px; border:1px dashed var(--ui-border); background:transparent; color:var(--ui-dim); font-size:12px; cursor:pointer; width:100%;">+ 링크 추가</button>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="modal-btn" onclick="closeLinkModal()">취소</button>
|
|
<button class="modal-btn primary" onclick="submitLinks()">확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- AI 부분 수정 팝업 -->
|
|
<div class="ai-edit-popup" id="aiEditPopup">
|
|
<button class="ai-edit-close" onclick="closeAiEditPopup()">✕</button>
|
|
<div class="ai-edit-header">🤖 AI로 수정하기</div>
|
|
<div class="ai-edit-selected">선택된 텍스트:</div>
|
|
<div class="ai-edit-selected-text" id="aiEditSelectedText"></div>
|
|
<textarea class="ai-edit-input" id="aiEditInput" rows="3" placeholder="예: 한 줄로 요약해줘 예: 표 형태로 만들어줘"></textarea>
|
|
<button class="ai-edit-btn" onclick="submitAiEdit()">✨ 수정하기</button>
|
|
</div>
|
|
|
|
<!-- 도메인 선택 모달 -->
|
|
<div id="domainModal">
|
|
<div class="domain-modal-content">
|
|
<div class="domain-modal-header">
|
|
<div class="domain-modal-title">📚 도메인 지식 선택</div>
|
|
<button class="domain-modal-close" onclick="closeDomainModal()">✕</button>
|
|
</div>
|
|
<div class="domain-modal-body">
|
|
<div class="domain-modal-desc">
|
|
업로드 자료의 분야를 선택하면 해당 전문 지식이 AI에 전달됩니다.<br>
|
|
선택하지 않으면 AI가 자동으로 분야를 판단합니다.
|
|
</div>
|
|
<div id="domainCategoryList"></div>
|
|
</div>
|
|
<div class="domain-modal-footer">
|
|
<div class="domain-summary-text empty" id="domainSummaryText">선택된 도메인이 없습니다.</div>
|
|
<div class="domain-modal-actions">
|
|
<button class="domain-btn" onclick="clearAllDomains()">초기화</button>
|
|
<button class="domain-btn primary" onclick="submitDomainSelection()">확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 템플릿 추가 모달 -->
|
|
<div class="template-modal" id="templateModal">
|
|
<div class="template-modal-content">
|
|
<div class="template-modal-header">
|
|
<div class="template-modal-title">📁 템플릿 추가</div>
|
|
<button class="template-modal-close" onclick="closeTemplateModal()">✕</button>
|
|
</div>
|
|
|
|
<div class="template-input-group">
|
|
<label class="template-input-label">템플릿 이름</label>
|
|
<input type="text" class="template-name-input" id="templateNameInput"
|
|
placeholder="예: 걸포4지구 교통영향평가">
|
|
</div>
|
|
|
|
<div class="template-input-group">
|
|
<label class="template-input-label">템플릿 파일</label>
|
|
<div class="template-dropzone" id="templateDropzone" onclick="document.getElementById('templateFileInput').click()">
|
|
<div class="template-dropzone-icon">📄</div>
|
|
<div class="template-dropzone-text">파일을 드래그하거나 클릭하여 선택</div>
|
|
<div class="template-dropzone-hint">HWPX, HWP, PDF 지원</div>
|
|
</div>
|
|
<input type="file" id="templateFileInput" accept=".hwpx,.hwp,.pdf" style="display:none" onchange="handleTemplateFile(this)">
|
|
<div class="template-dropzone-file" id="templateFileInfo">
|
|
<span class="filename" id="templateFileName"></span>
|
|
<span class="remove" onclick="removeTemplateFile()">✕</span>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="template-submit-btn" id="templateSubmitBtn" onclick="submitTemplate()" disabled>
|
|
<span class="spinner" id="templateSpinner"></span>
|
|
<span id="templateSubmitText">✨ 분석 및 추가</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 문서 유형 추가 모달 -->
|
|
<div class="modal-overlay" id="addDocTypeModal">
|
|
<div class="modal" style="min-width: 500px; max-width: 550px;">
|
|
<div class="modal-header">
|
|
<span id="addDocTypeModalTitle">📄 문서 유형 추가</span>
|
|
</div>
|
|
|
|
<!-- Step 1: 입력 -->
|
|
<div id="docTypeStep1" class="modal-body">
|
|
<div style="margin-bottom: 16px;">
|
|
<label style="font-size: 12px; font-weight: 600; color: var(--ui-dim); display: block; margin-bottom: 8px;">문서 유형 이름</label>
|
|
<input type="text" id="newDocTypeName" placeholder="예: 제안서, 회의록..." style="width:100%; padding:10px; border-radius:6px; border:1px solid var(--ui-border); background:var(--ui-bg); color:var(--ui-text); font-size:13px;">
|
|
</div>
|
|
<div style="margin-bottom: 16px;">
|
|
<label style="font-size: 12px; font-weight: 600; color: var(--ui-dim); display: block; margin-bottom: 8px;">설명 (선택)</label>
|
|
<input type="text" id="newDocTypeDesc" placeholder="문서 유형에 대한 간단한 설명" style="width:100%; padding:10px; border-radius:6px; border:1px solid var(--ui-border); background:var(--ui-bg); color:var(--ui-text); font-size:13px;">
|
|
</div>
|
|
<div style="margin-bottom: 16px;">
|
|
<label style="font-size: 12px; font-weight: 600; color: var(--ui-dim); display: block; margin-bottom: 8px;">샘플 문서 (HWPX 권장)</label>
|
|
<input type="file" id="newDocTypeFile" accept=".hwpx,.hwp,.pdf" style="width:100%; padding:10px; border-radius:6px; border:1px solid var(--ui-border); background:var(--ui-bg); color:var(--ui-text); font-size:12px;">
|
|
<p style="margin-top:8px; font-size:11px; color:var(--ui-dim);">샘플 문서를 분석하여 스타일과 구조를 추출합니다</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: 분석 진행 -->
|
|
<div id="docTypeStep2" class="modal-body" style="display:none;">
|
|
<div class="analysis-progress">
|
|
<div id="analysisSteps"></div>
|
|
<div class="progress-bar-container">
|
|
<div id="analysisProgressBar" class="progress-bar" style="width: 0%"></div>
|
|
</div>
|
|
<p id="analysisProgressText" style="text-align: center; margin-top: 12px; font-size: 12px; color: var(--ui-dim);">준비 중...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: 결과 확인 -->
|
|
<div id="docTypeStep3" class="modal-body" style="display:none;">
|
|
<div class="analysis-result">
|
|
<h4 style="margin-bottom: 16px; color: var(--ui-accent);">📋 분석 완료</h4>
|
|
<div id="analysisResultSummary" class="result-summary"></div>
|
|
<div id="analysisResultToc" class="result-toc" style="margin-top: 16px;"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer" id="docTypeModalFooter">
|
|
<button class="modal-btn" onclick="closeAddDocTypeModal()">취소</button>
|
|
<button class="modal-btn primary" id="docTypeActionBtn" onclick="startDocTypeAnalysis()">분석 시작</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// ===== 상태 변수 =====
|
|
let inputContent = '';
|
|
var generatedHTML = '';
|
|
var currentDocType = 'briefing';
|
|
let currentWriteMode = 'restructure';
|
|
let currentZoom = 100;
|
|
let folderPath = '';
|
|
let referenceLinks = [];
|
|
let currentTemplate = 'default';
|
|
let templateElements = {};
|
|
let analysisResult = null;
|
|
|
|
// ===== 문서 유형 데이터 =====
|
|
let docTypes = []; // 전체 문서 유형 (default + user)
|
|
let selectedDocTypeFile = null;
|
|
|
|
// ===== 템플릿 관련 =====
|
|
let userTemplates = [];
|
|
let selectedTemplateFile = null;
|
|
|
|
// ===== AI 부분 수정 =====
|
|
let selectedText = '';
|
|
let selectedRange = null;
|
|
|
|
|
|
// ===== 활용 범위 선택 =====
|
|
function selectWriteMode(mode) {
|
|
currentWriteMode = mode;
|
|
document.querySelectorAll('.write-mode-tab').forEach(tab => {
|
|
const radio = tab.querySelector('input[type="radio"]');
|
|
if (radio && radio.value === mode) {
|
|
tab.classList.add('selected');
|
|
radio.checked = true;
|
|
} else {
|
|
tab.classList.remove('selected');
|
|
}
|
|
});
|
|
updateDomainSectionVisibility();
|
|
}
|
|
|
|
function updateDomainSectionVisibility() {
|
|
const section = document.getElementById('domainSection');
|
|
if (!section) return;
|
|
const hasFolder = folderPath && folderPath.trim() !== '';
|
|
const hasLinks = referenceLinks && referenceLinks.length > 0;
|
|
section.style.display = (hasFolder || hasLinks) ? 'block' : 'none';
|
|
}
|
|
|
|
// ===== 썸네일 템플릿 =====
|
|
const thumbnailTemplates = {
|
|
briefing: `
|
|
<div class="page">
|
|
<div class="page-header"></div>
|
|
<div class="page-title"></div>
|
|
<div class="page-divider"></div>
|
|
<div class="page-lead"></div>
|
|
<div class="page-body"></div>
|
|
<div class="page-bottom"></div>
|
|
</div>
|
|
<div class="page">
|
|
<div class="page-header"></div>
|
|
<div class="page-attach">첨부</div>
|
|
<div class="page-body"></div>
|
|
<div class="page-body"></div>
|
|
<div class="page-bottom"></div>
|
|
</div>
|
|
`,
|
|
report: `
|
|
<div class="line h1"></div>
|
|
<div class="line body"></div>
|
|
<div class="line body" style="width:90%"></div>
|
|
<div class="line h2"></div>
|
|
<div class="line body" style="width:85%"></div>
|
|
`,
|
|
ppt: `
|
|
<div class="slide"><div class="slide-title">TITLE</div><div class="slide-body"></div></div>
|
|
<div class="slide"><div class="slide-title">CONTENT</div><div class="slide-body"></div></div>
|
|
`,
|
|
custom: `
|
|
<div class="line h1"></div>
|
|
<div class="line body"></div>
|
|
<div class="line h2"></div>
|
|
<div class="line body"></div>
|
|
`
|
|
};
|
|
|
|
// ===== 초기화 =====
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
setStatus('로딩 중...', false);
|
|
|
|
if (typeof loadDocTypes === 'function') loadDocTypes();
|
|
if (typeof loadUserTemplates === 'function') loadUserTemplates();
|
|
|
|
// 드롭존 이벤트
|
|
const dropzones = ['templateDropzone', 'docTypeDropzone'];
|
|
dropzones.forEach(id => {
|
|
const dropzone = document.getElementById(id);
|
|
if (dropzone) {
|
|
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
|
dropzone.addEventListener(eventName, (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
});
|
|
});
|
|
['dragenter', 'dragover'].forEach(eventName => {
|
|
dropzone.addEventListener(eventName, () => dropzone.classList.add('dragover'));
|
|
});
|
|
['dragleave', 'drop'].forEach(eventName => {
|
|
dropzone.addEventListener(eventName, () => dropzone.classList.remove('dragover'));
|
|
});
|
|
}
|
|
});
|
|
|
|
// 템플릿 입력 이벤트
|
|
const templateNameInput = document.getElementById('templateNameInput');
|
|
if (templateNameInput) {
|
|
if (typeof updateTemplateSubmitBtn === 'function') {
|
|
templateNameInput.addEventListener('input', updateTemplateSubmitBtn);
|
|
}
|
|
}
|
|
|
|
setStatus('준비됨', true);
|
|
});
|
|
|
|
// Enter 키로 피드백 제출
|
|
document.getElementById('feedbackInput').addEventListener('keypress', function(e) {
|
|
if (e.key === 'Enter') {
|
|
submitFeedback();
|
|
}
|
|
});
|
|
</script>
|
|
<script src="/static/js/ui.js"></script>
|
|
<script src="/static/js/doc_type.js"></script>
|
|
<script src="/static/js/template.js"></script>
|
|
<script src="/static/js/modals.js"></script>
|
|
<script src="/static/js/generator.js"></script>
|
|
<script src="/static/js/demo_mode.js"></script>
|
|
<script src="/static/js/export.js"></script>
|
|
<script src="/static/js/ai_edit.js"></script>
|
|
<script src="/static/js/editor.js"></script>
|
|
<script src="/static/js/domain_selector.js"></script>
|
|
|
|
|
|
</body>
|
|
</html> |