Files
AriWeb-Suite/modules/webmaker/public/index.html

533 lines
61 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>AI Webmaker</title>
<link rel="stylesheet" href="/style.css">
<link rel="stylesheet" href="/theme.css">
</head>
<body>
<!-- [Ariweb Suite 변경] 포털 SSO 로 들어온 세션의 배너: 어느 그룹·어느 운영 사이트의 "프로젝트" 를 편집 중인지 항상 보인다(§9.1). public/app.js 가 /api/config 의 ariweb 값으로 채운다 -->
<div id="ariwebBanner" class="ariweb-banner hidden" role="status"></div>
<!-- =====================================================================================================
v1.6.40 (요구 1) 사용자 화면 전면 재구성 — "작업 흐름" 중심의 세 칸 구조
① 왼쪽 레일(.rail) : 제품명 · 작업 중인 사이트 선택 · 작업 흐름 내비게이션(홈 / 사이트: 만들기·수정·배포·운영 / 주소·메일: 도메인·DNS·메일 / 계정: 캐시·충전·설정)
각 항목 오른쪽에 그 작업의 상태 점·짧은 배지가 붙어, 어디까지 되었고 다음이 무엇인지 한눈에 보인다 (public/flow-ui.js 가 채움).
② 가운데 작업 칸(.left): 위(.top-pane) = 화면 이름·설명 + 진행 상태·메시지창(모든 화면 공통), 아래(.bottom-pane) = 고른 화면의 작업 카드.
③ 오른쪽 캔버스(.right): 미리보기(+소스 편집기) 또는 운영 화면에서는 사이트 관리 창.
1.4.14~1.6.39 의 "생성/수정/관리/도메인/설정" 다섯 탭은 아래처럼 나뉘고 늘어났다 (카드의 요소 ID 는 그대로라 wizard.js·modules-ui.js 등 기존 화면 코드는 바뀌지 않는다):
생성 → 만들기(create) · 수정 → 수정(edit) + 배포(deploy: 발행·원본 서버 반영·검색 등록) · 관리 → 운영(manage)
도메인 → 도메인(domains: 구매·등록·연결) + DNS(dns: 레코드·전파·확인) + 메일(mail: MX 자동 설정·웹메일) · 설정 → 캐시·충전(credits) + 설정(settings) · 새로 홈(home: 작업 흐름 지도)
===================================================================================================== -->
<div class="app">
<div class="rail" id="rail">
<div class="rail-brand"><span class="rail-logo" aria-hidden="true"></span><h1>AI Webmaker <span class="ver" id="verBadge"></span></h1></div>
<!-- 작업 중인 사이트: 여기서 고르면 가운데 작업 칸과 오른쪽 캔버스가 그 사이트로 바뀐다 (v1.2.0 사이트 선택 상자, 1.6.40 에서 레일로 이동) -->
<div class="rail-site">
<div class="rail-label">작업 중인 사이트 <button type="button" class="help right" data-tip="작업할 사이트를 여기서 고릅니다. 생성 중인 사이트는 만들기 화면, 완성된 사이트는 보고 있던 화면(홈·수정·배포·운영)에서 이어집니다. '새 사이트 만들기'를 고르면 만들기 화면에서 새로 시작합니다.">?</button></div>
<div class="site-picker"><select id="siteSelect" title="작업할 사이트"><option value="">+ 새 사이트 만들기</option></select></div>
<!-- v1.4.14 (요구 4): 상태·운영 주소 줄은 항상 같은 높이 (사이트를 고르기 전에는 안내 한 줄) -->
<div class="site-meta-top" id="siteMetaBox"><b id="siteTitle"></b><span class="pill idle small" id="siteStatePill"></span><span class="muted small" id="siteLiveLine">사이트를 고르면 상태와 운영 주소가 여기에 표시됩니다</span></div>
</div>
<!-- 작업 흐름 내비게이션. data-tab 값은 app.js TABS 와 같다. .nb(배지)·data-state(점 색) 는 flow-ui.js 가 채운다 -->
<nav class="rail-nav" id="mainTabs" aria-label="작업 흐름">
<button type="button" data-tab="home"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M3 9.5 10 3l7 6.5V17H12v-5H8v5H3z"/></svg><span>홈</span><em class="nb"></em></button>
<div class="rail-group">사이트</div>
<button type="button" data-tab="create"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M10 3v14M3 10h14"/></svg><span>만들기</span><em class="nb"></em></button>
<button type="button" data-tab="edit"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M4 14.5 13.5 5l1.5 1.5L5.5 16H4zM12 6.5l1.5 1.5"/></svg><span>수정</span><em class="nb"></em></button>
<button type="button" data-tab="deploy"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M10 15V5M5.5 9.5 10 5l4.5 4.5M4 17h12"/></svg><span>배포</span><em class="nb"></em></button>
<button type="button" data-tab="manage"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M4 16V9M10 16V4M16 16v-6"/></svg><span>운영</span><em class="nb"></em></button>
<div class="rail-group">주소 · 메일</div>
<button type="button" data-tab="domains"><svg viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="7"/><path d="M3 10h14M10 3c2.5 2.5 2.5 11.5 0 14M10 3c-2.5 2.5-2.5 11.5 0 14"/></svg><span>도메인</span><em class="nb"></em></button>
<button type="button" data-tab="dns"><svg viewBox="0 0 20 20" aria-hidden="true"><rect x="3" y="4" width="14" height="4.5" rx="1.2"/><rect x="3" y="11.5" width="14" height="4.5" rx="1.2"/><path d="M6 6.2h.01M6 13.7h.01"/></svg><span>DNS</span><em class="nb"></em></button>
<button type="button" data-tab="mail"><svg viewBox="0 0 20 20" aria-hidden="true"><rect x="3" y="5" width="14" height="10" rx="1.5"/><path d="m3.5 6 6.5 5 6.5-5"/></svg><span>메일</span><em class="nb"></em></button>
<div class="rail-group">계정</div>
<button type="button" data-tab="credits"><svg viewBox="0 0 20 20" aria-hidden="true"><rect x="3" y="5.5" width="14" height="9" rx="1.5"/><path d="M3 9h14"/></svg><span>캐시 · 충전</span><em class="nb"></em></button>
<button type="button" data-tab="settings"><svg viewBox="0 0 20 20" aria-hidden="true"><circle cx="10" cy="10" r="2.6"/><path d="M10 3v2M10 15v2M3 10h2M15 10h2M5.2 5.2l1.4 1.4M13.4 13.4l1.4 1.4M5.2 14.8l1.4-1.4M13.4 6.6l1.4-1.4"/></svg><span>설정</span><em class="nb"></em></button>
</nav>
<div class="rail-foot">
<!-- 잔여 캐시: 누르면 캐시·충전 화면. 작업 중에는 실시간으로 바뀐다 (app.js renderRunTokens) -->
<button type="button" class="credit-pill" id="creditPill" title="잔여 캐시 — 누르면 캐시·충전 화면이 열립니다"><span>캐시</span><b id="creditBadge">-</b></button>
<span class="muted small" id="creditNote"></span>
<div class="rail-user"><span class="user"><b id="userName"></b> <span class="muted" id="userId"></span></span></div>
<div class="rail-actions"><a id="adminLink" class="btn ghost small-btn hidden" href="/admin">관리자</a><button id="btnFeedback" class="ghost small-btn" title="사용 문의, 개선 제안, 오류를 운영팀에 보냅니다">문의 · 제안 · 오류</button><span class="lang-switch" style="display:none"><select id="langSelect" title="화면 언어 / Language"></select></span><button id="btnLogout" class="ghost small-btn">로그아웃</button></div>
</div>
</div>
<aside class="left">
<!-- ===== 위쪽: 화면 이름·설명(레일에서 고른 화면) + 진행 상태·메시지창 (모든 화면 공통) ===== -->
<div class="top-pane">
<header class="hdr v12 pane-head">
<div class="hdr-line"><h1 class="pane-title" id="paneTitle">홈</h1><span class="spacer"></span><span id="paneSite" class="muted small pane-site"></span></div>
<p class="muted small pane-desc" id="paneDesc"></p>
</header>
<!-- 현재 상태: 어느 탭에서 지시를 내려도 여기서 즉시 바뀐다 (생성·수정·배포·검증 공통) -->
<section class="status-strip" id="logCard">
<div class="ss-head"><span class="muted small">진행 상태</span><span id="ssSite" class="ss-site muted small"></span><span id="statusPill" class="pill idle">대기</span></div>
<ol class="steps-strip hidden" id="progressSteps"><li>지시 전달</li><li>AI 작업</li><li>버전 저장</li><li>완료</li></ol>
<!-- v1.5.31 (요구 1·3): 진행 상자는 한 줄 — 아이콘 · 상태(progressText) · 오른쪽 정렬 보조 문구(progressSub). 진행 중 메시지(progressMsg)만 아래 작은 줄.
배포 결과 상자(progressDeploy)는 없애고 결과는 메시지창에 시각과 함께 쌓인다. -->
<div id="progress" class="progress idle">
<div class="pi"><span class="spinner"></span><span class="check">✓</span><span class="bang">!</span><span class="dot"></span></div>
<div class="pt">
<div class="pt-line"><span id="progressText">대기 중</span><span id="progressSub" class="muted small"></span></div>
<div id="progressMsg" class="pmsg"></div>
</div>
<button id="btnCancel" class="ghost small-btn cancel hidden" title="실행 중인 AI 작업을 중단합니다">작업 중단</button>
</div>
<!-- v1.5.27 (요구 1·2): 메시지창 — 아래 작업 영역에서 한 일의 결과(배포·발행 취소·도메인·문의 메일 등)가 여기에 쌓인다. 크기 고정, 최신이 맨 위, 지난 메시지는 아래로 스크롤.
메시지마다 × 로 지우고 "모두 지우기" 로 비운다. 1.5.19 까지의 경고 한 줄 상자(progressWarn)는 지워지지 않고 계속 남아 있었다. -->
<div id="msgBox" class="msg-box hidden">
<div class="mb-head"><span class="muted small">메시지</span><span class="muted small" id="msgCount"></span><span class="spacer"></span><button type="button" class="ghost small-btn" id="msgClear" title="메시지를 모두 지웁니다">모두 지우기</button></div>
<ul id="msgList" class="mb-list"></ul>
</div>
</section>
</div>
<!-- ===== 아래쪽: 작업 영역 (스크롤) ===== -->
<div class="bottom-pane">
<!-- ===== 홈 (v1.6.40): 작업 흐름 지도 — 만들기 → 수정 → 도메인 → DNS·메일 → 배포 → 운영 의 상태와 다음 할 일. public/flow-ui.js 가 그린다 ===== -->
<div class="tabpane" id="pane-home"><div id="homeBody"></div></div>
<!-- ===== 만들기(create): 새 사이트 생성 ~ 생성 완료 (수정·운영 화면의 사이트 선택과 무관) ===== -->
<div class="tabpane" id="pane-create">
<div class="model-row">
<span class="muted small">모델 등급 <button type="button" class="help" data-tip="기본: HTML5, CSS3, 자바스크립트 구조의 정적 웹페이지 생성. 중급: 웹페이지의 시각적 완성도(UI/UX)와 심미성 우수. 고급: 복잡한 UI/UX의 프런트엔드 아키텍쳐 구현. 등급별 예상 비용은 아래 표에 있습니다.">?</button></span>
<select id="tierSelect" title="이 사이트 생성에 사용할 AI 모델 등급"></select>
</div>
<div class="tier-guide" id="tierGuideBox"></div>
<section class="card" id="modeCard">
<h2>1. 어떻게 만들까요? <button type="button" class="help" data-tip="AI에게 맡기기: 설명만 적으면 AI 가 분석 후 필요한 정보만 묻고 끝까지 자동 생성합니다. 내가 고르기: 업종·목적·페이지·디자인·문구를 단계별로 직접 고릅니다. 기존 사이트의 모양을 살려 새로 만들려면 아래 '2. 기존 사이트 모양으로 시작'을 고르세요.">?</button></h2>
<div class="mode-grid">
<button type="button" class="mode-opt" data-mode="ai"><span class="mode-title">AI에게 맡기기</span><span class="mode-desc">설명 + 추가 정보 입력 → 자동 생성</span></button>
<button type="button" class="mode-opt" data-mode="manual"><span class="mode-title">내가 고르기</span><span class="mode-desc">구성·디자인·문구를 직접 선택</span></button>
<button type="button" class="mode-opt" data-mode="template"><span class="mode-title">템플릿에서 시작</span><span class="mode-desc">업종별·해시태그로 고른 완성 사이트를 바탕으로</span></button>
</div>
</section>
<!-- v1.3.4: 기존 사이트로 시작하기 — "사이트 복사하기" 만 남김 ("사이트 관리하기(서버에서 가져오기)" 는 삭제) -->
<section class="card" id="existingCard">
<h2>2. 기존 사이트 모양으로 시작 <button type="button" class="help" data-tip="이미 있는 사이트의 모양(디자인·구성·문구)을 최대한 살려 새 사이트를 만들 때 씁니다. 웹 주소만 입력하면 화면을 이루는 HTML·CSS·JS·이미지를 복사해 오며, 로그인·게시판·신청·결제 같은 기능(서버 프로그램)은 복사되지 않습니다. 복사한 뒤에는 새로 만든 사이트처럼 수정·발행합니다.">?</button></h2>
<div class="mode-grid">
<button type="button" class="mode-opt" data-mode="copy"><span class="mode-title">사이트 복사하기</span><span class="mode-desc">웹 주소만 입력 → 화면(HTML 페이지)만 복사해 새 사이트로</span></button>
</div>
</section>
<section class="card hidden" id="copyStartCard">
<h2>사이트 복사하기 <button type="button" class="ghost small-btn" id="btnBackMode4">← 방식 변경</button></h2>
<label>사이트 주소 <span class="req">*</span> <button type="button" class="help" data-tip="복사할 사이트의 첫 페이지 주소입니다. 같은 도메인 안의 페이지와 그 페이지가 쓰는 CSS·JS·이미지·글꼴을 따라가며 내려받고, 링크를 이 빌더 안의 경로로 바꿉니다. 다른 도메인(CDN 등)의 자원은 원래 주소를 그대로 씁니다. 사이트 크기가 서버 용량의 10% 를 넘으면 중지됩니다.">?</button><input id="cpUrl" placeholder="https://www.example.com" autocomplete="off"></label>
<label>사이트 이름 <span class="muted small">(비우면 페이지 제목)</span><input id="cpTitle" autocomplete="off"></label>
<label>문의 수신 이메일 <span class="muted small">(선택)</span><input id="cpEmail" type="email" autocomplete="off" placeholder="info@example.com"></label>
<!-- v1.3.4: 무엇이 복사되고 무엇이 안 되는지, 왜 있는 기능인지 상세 설명 -->
<div class="copy-explain">
<p><b>이 기능은 무엇인가요?</b> 기존 사이트의 <b>모양은 최대한 유지하면서 새로운 사이트를 만들 때</b> 쓰는 기능입니다. 원래 사이트를 옮기거나 그대로 운영하는 기능이 아닙니다.</p>
<ul>
<li><b>복사되는 것</b> — 웹 주소로 열리는 기본 HTML 페이지와, 그 페이지가 쓰는 CSS·JS·이미지·글꼴 (같은 도메인 안의 페이지를 따라가며 최대 300쪽)</li>
<li><b>복사되지 않는 것</b> — 로그인·회원, 게시판 글 저장, 문의·신청·예약 접수, 결제, 검색처럼 서버에서 동작하는 기능과 그 데이터(DB), 로그인해야 보이는 화면</li>
<li><b>복사한 뒤</b> — 새로 만든 사이트로 취급되어 수정 지시·소스 편집·발행을 그대로 쓸 수 있습니다. 필요한 기능은 수정 지시로 빌더의 공통 기능 모듈(회원·게시판·문의·예약 등)로 새로 붙입니다.</li>
</ul>
<p class="muted small">본인 또는 권한이 있는 사이트만 복사하세요.</p>
</div>
<!-- v1.3.5: 검색·AI 최적화 생성 옵션 — 생성이 끝나면 AI 초안 → 수정 탭에서 검토 후 반영 -->
<div class="seo-opts" data-seo-opts>
<div class="field-label">검색·AI 최적화 <button type="button" class="help" data-tip="SEO: 검색 결과에 보이는 페이지별 제목·설명·키워드, 공유 미리보기, 사이트맵. AEO: 질문형 검색(AI·음성 답변)에 맞춘 자주 묻는 질문과 짧은 답, 구조화 데이터. GEO: 생성형 AI 검색이 정확히 인용하도록 사업체 정보 구조화 데이터·핵심 사실·llms.txt. 생성이 끝나면 AI 가 초안을 만들고(기본 등급, 캐시 차감) 수정 탭에서 확인·수정한 뒤 반영합니다. 바로 사이트에 들어가지 않습니다.">?</button></div>
<label class="chk"><input type="checkbox" data-opt="seo"> SEO AI 생성 <span class="muted small">— 페이지별 검색 제목·설명·키워드, 공유 미리보기, 사이트맵</span></label>
<label class="chk"><input type="checkbox" data-opt="aeo"> AEO 생성 <span class="muted small">— 질문형 FAQ·핵심 답변 (AI·음성 답변용)</span></label>
<label class="chk"><input type="checkbox" data-opt="geo"> GEO 생성 <span class="muted small">— 사업체 정보·인용용 사실·llms.txt (생성형 AI 검색용)</span></label>
</div>
<button id="cpStart" class="primary">사이트 복사 시작</button>
</section>
<!-- v1.3.10: 템플릿에서 시작 — 관리자가 사용을 허용한 템플릿 (public/templates-ui.js) -->
<section class="card hidden" id="templateStartCard">
<h2>템플릿에서 시작 <button type="button" class="ghost small-btn" id="btnBackMode5">← 방식 변경</button> <button type="button" class="help" data-tip="운영팀이 만든 완성 사이트(템플릿)를 바탕으로 새 사이트를 만듭니다. 업종 디렉터리나 #해시태그로 찾고 미리보기로 확인한 뒤 고르세요. 시작하면 템플릿의 페이지·디자인이 그대로 복사되며, 그 뒤에는 수정 지시로 내용을 바꿉니다.">?</button></h2>
<input id="tplQ" placeholder="#카페 #미니멀 처럼 해시태그나 낱말로 찾기" autocomplete="off">
<div class="tpl-tags" id="tplTags"></div>
<div class="tpl-layout"><div class="tpl-tree" id="tplTree"></div><div class="tpl-list" id="tplList"></div></div>
<p class="small" id="tplChosen"></p>
<label>사이트 이름 <span class="muted small">(비우면 템플릿 이름)</span><input id="tplTitle" autocomplete="off"></label>
<label>문의 수신 이메일 <span class="muted small">(선택)</span><input id="tplEmail" type="email" autocomplete="off" placeholder="info@example.com"></label>
<div class="seo-opts" data-seo-opts>
<div class="field-label">검색·AI 최적화</div>
<label class="chk"><input type="checkbox" data-opt="seo" checked> SEO AI 생성</label>
<label class="chk"><input type="checkbox" data-opt="aeo"> AEO 생성</label>
<label class="chk"><input type="checkbox" data-opt="geo"> GEO 생성</label>
</div>
<button id="tplStart" class="primary" disabled>이 템플릿으로 시작</button>
</section>
<section class="card hidden" id="aiStartCard">
<h2>AI에게 맡기기 <button type="button" class="ghost small-btn" id="btnBackMode">← 방식 변경</button></h2>
<label class="ai-main-label">만들고 싶은 사이트 <span class="req">*</span> <button type="button" class="help" data-tip="업종, 위치, 주요 고객, 꼭 들어가야 할 내용(예: 예약, 가격표, 강사 소개)을 자유롭게 적습니다. 많이 적을수록 AI 가 정확히 이해합니다. 결과: AI 가 업종·목표·페이지 구성을 정리해 추가로 필요한 정보만 묻습니다.">?</button>
<textarea id="aiText" class="ai-main-input" rows="10" placeholder="예: 강남역 근처 필라테스 스튜디오. 1:1 수업 중심, 30대 직장인 여성 대상. 예약을 많이 받고 싶고, 강사 소개와 가격표가 들어갔으면 함."></textarea>
</label>
<div class="field-label">디자인 컨셉 (선택) <button type="button" class="help" data-tip="주 색상은 버튼·강조색에 쓰이고, 톤은 전체 분위기(여백·글꼴·이미지 스타일)를 정합니다. 방향에는 '사진 크게', '둥근 버튼' 같은 요구를 적습니다. 비우면 업종에 맞게 AI 가 추천합니다.">?</button></div>
<div class="two">
<label>주 색상<div class="color-row"><input id="aiColor" type="color" value="#2f6fed"><input id="aiColorText" placeholder="#2f6fed 또는 '네이비'" autocomplete="off"></div></label>
<label>분위기(톤)<select id="aiTone"><option value="">AI 추천</option></select></label>
</div>
<label>디자인 방향<input id="aiDesignDir" placeholder="예: 여백 많은 고급스러운 느낌, 사진 크게" autocomplete="off"></label>
<label>페이지 수 <button type="button" class="help" data-tip="홈을 포함한 페이지 개수입니다. '자동'은 AI 가 업종과 목표에 맞게 3~6개를 정합니다. 1개를 고르면 모든 내용을 한 페이지에 섹션으로 배치하는 원페이지 사이트가 됩니다. 페이지가 많을수록 생성 비용과 시간이 늘어납니다.">?</button><select id="aiPageCount"><option value="0">자동 (AI 가 결정, 보통 3~6개)</option><option value="1">1개 (원페이지)</option><option value="2">2개</option><option value="3">3개</option><option value="4">4개</option><option value="5">5개</option><option value="6">6개</option><option value="8">8개</option><option value="10">10개</option></select></label>
<label>기존 사이트 URL <button type="button" class="help" data-tip="이미 사이트가 있으면 주소를 적습니다. AI 가 본문을 읽어 사업 정보와 전화·주소 같은 사실정보를 출처와 함께 가져옵니다.">?</button><input id="aiExistingUrl" placeholder="https://"></label>
<div class="field-label">첨부 <button type="button" class="help" data-tip="소개서(PDF·문서)는 내용에, 로고·사진은 페이지 이미지에 쓰입니다. 첨부가 없는 자리는 AI 가 그라데이션·도형으로 채웁니다.">?</button></div>
<div class="dropzone" id="dzCreate" data-target="create">
<div class="dz-text">파일을 끌어다 놓거나 <label class="dz-pick" for="fFiles">클릭해서 선택</label></div>
<input id="fFiles" type="file" multiple class="visually-hidden">
<ul class="pending" id="pendingCreate"></ul>
</div>
<div class="field-label">참고 사이트 (최대 10개) <button type="button" class="help" data-tip="참고할 사이트 주소와 '어떤 점을 반영할지'(메뉴 구성, 색감, 가격표 형식 등)를 함께 적습니다. 디자인·구조 제안에 반영됩니다.">?</button></div>
<div id="refsAi" class="refs"></div>
<!-- v1.3.5: 검색·AI 최적화 생성 옵션 — 생성이 끝나면 AI 초안 → 수정 탭에서 검토 후 반영 -->
<div class="seo-opts" data-seo-opts>
<div class="field-label">검색·AI 최적화 <button type="button" class="help" data-tip="SEO: 검색 결과에 보이는 페이지별 제목·설명·키워드, 공유 미리보기, 사이트맵. AEO: 질문형 검색(AI·음성 답변)에 맞춘 자주 묻는 질문과 짧은 답, 구조화 데이터. GEO: 생성형 AI 검색이 정확히 인용하도록 사업체 정보 구조화 데이터·핵심 사실·llms.txt. 생성이 끝나면 AI 가 초안을 만들고(기본 등급, 캐시 차감) 수정 탭에서 확인·수정한 뒤 반영합니다. 바로 사이트에 들어가지 않습니다.">?</button></div>
<label class="chk"><input type="checkbox" data-opt="seo" checked> SEO AI 생성 <span class="muted small">— 페이지별 검색 제목·설명·키워드, 공유 미리보기, 사이트맵</span></label>
<label class="chk"><input type="checkbox" data-opt="aeo" checked> AEO 생성 <span class="muted small">— 질문형 FAQ·핵심 답변 (AI·음성 답변용)</span></label>
<label class="chk"><input type="checkbox" data-opt="geo" checked> GEO 생성 <span class="muted small">— 사업체 정보·인용용 사실·llms.txt (생성형 AI 검색용)</span></label>
</div>
<!-- v1.3.10: 다국어 생성 옵션 — 언어마다 /ko/ /en/ 폴더, 기본 언어 페이지와 같은 구조로 번역 (app.js fillLangOpts) -->
<div class="lang-opts" data-lang-opts>
<div class="field-label">사이트 언어 <button type="button" class="help" data-tip="추가 언어를 고르면 다국어 사이트로 만듭니다. 주소는 언어마다 /ko/, /en/ 처럼 나뉘고, 다른 언어 페이지는 기본 언어 페이지와 같은 구조(페이지·섹션·순서)로 번역됩니다. 날짜·숫자는 언어별 형식으로, 아랍어 등은 오른쪽→왼쪽으로 표시되며, 언어마다 검색·AI 최적화 값이 따로 만들어집니다. 번역은 기본 등급 모델로 캐시가 차감됩니다. 나중에 수정 탭에서 언어를 더할 수도 있습니다.">?</button></div>
<label class="lang-default">기본 언어<select data-lang-default></select></label>
<div class="lang-chips" data-lang-extra></div>
<!-- v1.3.16: x-default — 어떤 언어에도 맞지 않는 방문자에게 보일 언어 (추가 언어를 골랐을 때만 보임) -->
<label class="lang-default lang-xdef hidden">x-default (다른 언어 방문자에게 보일 언어) <button type="button" class="help" data-tip="검색엔진의 hreflang x-default 와 사이트 맨 위 주소(언어 선택 페이지)의 자동 이동에 쓰입니다. 브라우저 언어가 사이트 언어 중 어느 것과도 맞지 않는 방문자는 이 언어로 갑니다. '언어 선택 페이지' 를 고르면 자동 이동 없이 선택 페이지를 보여 줍니다.">?</button><select data-lang-xdefault></select></label>
</div>
<button id="btnAiStart" class="primary">AI 분석 시작</button>
</section>
<section class="card hidden" id="manualStartCard">
<h2>내가 고르기 <button type="button" class="ghost small-btn" id="btnBackMode2">← 방식 변경</button></h2>
<label>상호명 <span class="req">*</span><input id="mName" placeholder="예: 한강 카페 로스터스"></label>
<div class="two">
<label>업종 대분류 <button type="button" class="help" data-tip="업종을 고르면 목적·기능·페이지·디자인 기본값이 미리 선택됩니다. 각 단계에서 바꿀 수 있습니다.">?</button><select id="mMajor"></select></label>
<label>소분류<select id="mMinor"></select></label>
</div>
<div class="two">
<label>지역 시·도<select id="mSido"></select></label>
<label>시·군·구<select id="mSigungu"></select></label>
</div>
<div class="field-label">참고 사이트 (최대 10개) <button type="button" class="help" data-tip="참고할 사이트 주소와 반영할 점을 적습니다. 디자인·구조에 반영됩니다.">?</button></div>
<div id="refsManual" class="refs"></div>
<!-- v1.3.5: 검색·AI 최적화 생성 옵션 — 생성이 끝나면 AI 초안 → 수정 탭에서 검토 후 반영 -->
<div class="seo-opts" data-seo-opts>
<div class="field-label">검색·AI 최적화 <button type="button" class="help" data-tip="SEO: 검색 결과에 보이는 페이지별 제목·설명·키워드, 공유 미리보기, 사이트맵. AEO: 질문형 검색(AI·음성 답변)에 맞춘 자주 묻는 질문과 짧은 답, 구조화 데이터. GEO: 생성형 AI 검색이 정확히 인용하도록 사업체 정보 구조화 데이터·핵심 사실·llms.txt. 생성이 끝나면 AI 가 초안을 만들고(기본 등급, 캐시 차감) 수정 탭에서 확인·수정한 뒤 반영합니다. 바로 사이트에 들어가지 않습니다.">?</button></div>
<label class="chk"><input type="checkbox" data-opt="seo" checked> SEO AI 생성 <span class="muted small">— 페이지별 검색 제목·설명·키워드, 공유 미리보기, 사이트맵</span></label>
<label class="chk"><input type="checkbox" data-opt="aeo" checked> AEO 생성 <span class="muted small">— 질문형 FAQ·핵심 답변 (AI·음성 답변용)</span></label>
<label class="chk"><input type="checkbox" data-opt="geo" checked> GEO 생성 <span class="muted small">— 사업체 정보·인용용 사실·llms.txt (생성형 AI 검색용)</span></label>
</div>
<!-- v1.3.10: 다국어 생성 옵션 — 언어마다 /ko/ /en/ 폴더, 기본 언어 페이지와 같은 구조로 번역 (app.js fillLangOpts) -->
<div class="lang-opts" data-lang-opts>
<div class="field-label">사이트 언어 <button type="button" class="help" data-tip="추가 언어를 고르면 다국어 사이트로 만듭니다. 주소는 언어마다 /ko/, /en/ 처럼 나뉘고, 다른 언어 페이지는 기본 언어 페이지와 같은 구조(페이지·섹션·순서)로 번역됩니다. 날짜·숫자는 언어별 형식으로, 아랍어 등은 오른쪽→왼쪽으로 표시되며, 언어마다 검색·AI 최적화 값이 따로 만들어집니다. 번역은 기본 등급 모델로 캐시가 차감됩니다. 나중에 수정 탭에서 언어를 더할 수도 있습니다.">?</button></div>
<label class="lang-default">기본 언어<select data-lang-default></select></label>
<div class="lang-chips" data-lang-extra></div>
<!-- v1.3.16: x-default — 어떤 언어에도 맞지 않는 방문자에게 보일 언어 (추가 언어를 골랐을 때만 보임) -->
<label class="lang-default lang-xdef hidden">x-default (다른 언어 방문자에게 보일 언어) <button type="button" class="help" data-tip="검색엔진의 hreflang x-default 와 사이트 맨 위 주소(언어 선택 페이지)의 자동 이동에 쓰입니다. 브라우저 언어가 사이트 언어 중 어느 것과도 맞지 않는 방문자는 이 언어로 갑니다. '언어 선택 페이지' 를 고르면 자동 이동 없이 선택 페이지를 보여 줍니다.">?</button><select data-lang-xdefault></select></label>
</div>
<button id="btnManualStart" class="primary">기본값으로 시작하기</button>
</section>
<!-- v1.3.4: "사이트 관리하기(서버 접속으로 가져오기)" 시작 카드 삭제 -->
<!-- 기획 진행 카드 (AI / 내가 고르기) -->
<section class="card hidden" id="planCard">
<h2><span id="planTitle">기획</span> <span id="planStage" class="pill idle"></span> <span class="spacer"></span><button type="button" class="ghost small-btn" id="btnAbortCreate" title="진행 중인 생성 작업을 멈추고 새 사이트 만들기로 돌아갑니다">중단하고 새로 만들기</button></h2>
<div id="planBody"></div>
</section>
</div>
<!-- ===== 수정(edit, v1.4.14 → 1.6.40): 수정 지시·기본 사항·다국어·검색 최적화·버전·첨부. 발행·배포는 "배포" 화면으로 ===== -->
<div class="tabpane hidden" id="pane-edit">
<p class="muted small" id="editHint"></p>
<!-- v1.6.40: 발행·배포 / 원본 서버 반영 / 검색사이트 등록 카드는 "배포" 화면(#pane-deploy)으로 옮겼다. 수정 화면은 사이트를 고치는 일에만 집중한다. -->
<!-- 수정 완료 안내 (v1.1.2): 마지막 수정 작업의 결과 요약 + 상세 보기 -->
<section class="card done-banner hidden" id="doneBanner">
<div class="db-head"><span class="db-icon">✓</span><div><b id="dbTitle">수정 완료</b><div class="muted small" id="dbMeta"></div></div><span class="spacer"></span><button type="button" class="ghost small-btn" id="dbDetail">상세 내역</button><button type="button" class="ghost small-btn" id="dbClose" title="닫기">×</button></div>
<div class="small hidden" id="dbSummary"></div>
</section>
<!-- 수정 지시: 수정 탭의 핵심 -->
<section class="card update-card hidden" id="updateCard">
<h2>수정 지시 <span class="muted small" id="editLimitNote"></span> <button type="button" class="help" data-tip="바꾸고 싶은 내용을 문장으로 적으면 AI 가 해당 부분만 고칩니다. 예: '첫 화면 배너 문구를 더 짧게', '문의 폼에 회사명 항목 추가', '게시판 페이지 추가'. 기본 사항·이전 지시·설정은 그대로 유지됩니다. 수정이 끝나면 미리보기에 바로 반영되고 버전이 남습니다.">?</button></h2>
<div id="editLockBox" class="progress-warn hidden"></div>
<textarea id="fInstruction" rows="7" placeholder="무엇을 어떻게 바꿀지 적어 주세요.&#10;예) 첫 화면 배너 문구를 '당신의 하루를 바꾸는 한 시간'으로 바꾸고, 가격표에 10회권 35만원을 추가해 주세요."></textarea>
<div class="update-tools">
<div class="model-row inline-row"><span class="muted small">이번 수정 모델</span><select id="editTierSelect" title="이번 수정 지시에 사용할 모델 등급"></select><button type="button" class="help" data-tip="수정마다 등급을 다르게 고를 수 있습니다. 모델이 바뀌어도 사이트 기본 사항과 이전 지시 내용은 그대로 유지되어 이어서 작업합니다.">?</button></div>
<div class="muted small" id="editCostLine"></div>
</div>
<div class="dropzone slim" id="dzUpdate" data-target="update">
<div class="dz-text">함께 보낼 파일을 끌어다 놓거나 <label class="dz-pick" for="fUpdFiles">선택</label> <button type="button" class="help" data-tip="이 지시와 함께 쓸 사진·문서입니다. 이미지는 페이지에, 문서는 내용에 반영됩니다.">?</button></div>
<input id="fUpdFiles" type="file" multiple class="visually-hidden">
<ul class="pending" id="pendingUpdate"></ul>
</div>
<button id="btnUpdate" class="primary big">수정 요청</button>
<details id="basicsBox" class="basics">
<summary>홈페이지 기본 사항 보기 / 수정 <span id="basicsState" class="basics-state"></span> <button type="button" class="help" data-tip="제목·목적·내용·디자인 방향·기타 요구사항·문의 수신 이메일. 저장하면 다음 수정 지시 때 AI 에게 함께 전달되어 페이지 전반에 반영됩니다.">?</button></summary>
<label>페이지 제목 <span class="req">*</span><input id="bTitle"></label>
<label>목적<textarea id="bPurpose" rows="2"></textarea></label>
<label>주요 컨텐츠<textarea id="bContent" rows="3"></textarea></label>
<label>디자인 방향<textarea id="bDesign" rows="2"></textarea></label>
<label>기타 요구사항<textarea id="bExtra" rows="3"></textarea></label>
<label>문의 수신 이메일<input id="bEmail" type="email" placeholder="예: info@example.com"></label>
<div class="row">
<button id="btnSaveBasics" class="primary small-btn" disabled>기본 사항 저장</button>
<button id="btnRevertBasics" class="ghost small-btn" disabled>되돌리기</button>
</div>
</details>
</section>
<!-- v1.3.10: 다국어 — 언어 추가·빼기, 단일 언어 사이트를 다국어 구조로 옮기기 (public/optimize.js LangUI) -->
<section class="card hidden" id="langCard">
<h2>다국어 <span class="pill small" id="langState"></span> <button type="button" class="help" data-tip="사이트를 여러 언어로 제공합니다. 주소는 언어마다 /ko/, /en/ 처럼 나뉘고, 사이트 맨 위 주소는 방문자 브라우저 언어로 안내하는 언어 선택 페이지가 됩니다. 기본 언어 페이지를 수정하면 다른 언어 페이지도 같은 구조로 다시 번역됩니다(바뀐 문장만 번역, 기본 등급 모델로 캐시 차감). 한 언어 사이트를 다국어로 바꿀 때는 AI 가 페이지를 기본 언어 폴더로 옮기는 수정 작업을 먼저 합니다.">?</button></h2>
<div id="langBody"></div>
</section>
<!-- v1.3.5: 검색·AI 최적화 (SEO·AEO·GEO) — AI 초안을 검토·수정한 뒤 반영 (public/optimize.js) -->
<section class="card hidden" id="optimizeCard">
<h2>검색·AI 최적화 <span class="pill small" id="optState"></span> <button type="button" class="help" data-tip="SEO(검색 결과 제목·설명·키워드·공유 미리보기·사이트맵), AEO(질문형 FAQ·핵심 답변, AI·음성 답변용), GEO(사업체 정보 구조화 데이터·인용용 사실·llms.txt, 생성형 AI 검색용) 값을 AI 가 초안으로 만들면 여기서 확인·수정해 반영합니다. 반영하면 버전이 저장되고, 다음 배포 때 운영 주소 기준(canonical·사이트맵)으로 함께 올라갑니다.">?</button></h2>
<div id="optBody"></div>
</section>
<section class="card hidden" id="versionsCard">
<h2>이전 버전으로 되돌리기 <span class="muted small" id="versionsCount"></span> <button type="button" class="help" data-tip="생성·수정·저장마다 버전이 남습니다. 되돌리면 그 시점 파일로 복원되고 복원도 새 버전으로 기록됩니다. 배포가 완료되면 이전 버전은 정리되고 배포본 하나만 남습니다.">?</button></h2>
<ul id="versionsList" class="git-list"></ul>
</section>
<section class="card hidden" id="attachCard">
<h2>첨부 파일 <span class="muted small" id="attachCount"></span> <button type="button" class="help" data-tip="파일당 최대 25MB. 이미지는 uploads/파일명 경로로 페이지에 삽입되고, 문서는 AI 가 읽어 내용에 반영합니다. 허용 형식은 아래 드롭존에 표시됩니다.">?</button></h2>
<ul id="attachList" class="attach-list"></ul>
<div class="dropzone slim" id="dzNow" data-target="now">
<div class="dz-text">파일을 놓으면 바로 업로드 · <label class="dz-pick" for="fAttachNow">선택</label> <span class="muted small">(<span id="attachExts"></span>)</span></div>
<input id="fAttachNow" type="file" multiple class="visually-hidden">
</div>
</section>
</div>
<!-- ===== 배포(deploy, v1.6.40 신설): 이전 단계(사이트 완성·도메인·DNS)가 갖춰졌는지 점검표(#deployChecklist, flow-ui.js) + 발행·배포 · 원본 서버 반영 · 검색사이트 등록 =====
배포는 다른 작업이 모두 끝나야 하는 마지막 작업이지만, 한 번 배포된 뒤의 수정·관리는 도메인·DNS 와 직접 묶이지 않는다 — 그래서 수정 화면과 분리했다. -->
<div class="tabpane hidden" id="pane-deploy">
<div id="deployChecklist"></div>
<section class="card hidden" id="publishCard">
<h2>발행 · 배포 <span id="publishState" class="pill idle"></span> <button type="button" class="help" data-tip="발행은 점검(입력 필요 항목·누락 페이지·규제 표현) 후 운영자가 지정한 배포 서버로 사이트를 전송합니다. 배포가 끝나면 상단 주소가 운영 주소로 바뀝니다. 발행 취소를 하면 운영 주소에는 '사이트 수정 중' 안내만 보이고, 다시 발행하면 정상 내용이 올라갑니다.">?</button></h2>
<div id="publishBody"></div>
</section>
<section class="card hidden" id="importCard">
<h2>원본 서버에 반영 <span id="imState" class="pill idle"></span> <button type="button" class="help" data-tip="여기서 수정한 파일을 원본 서버의 같은 경로에 덮어씁니다. 가져온 뒤 삭제한 파일은 서버에서도 지워지고, 서버에만 있는 파일(업로드·캐시)은 건드리지 않습니다. SSH 방식은 반영 전에 서버에 백업을 만듭니다.">?</button></h2>
<div id="imInfo" class="small"></div>
<div class="row">
<button id="imBtnDeploy" class="primary small-btn">수정 내용을 원본 서버에 반영 (배포)</button>
<button id="imBtnCreds" class="ghost small-btn">접속 정보 변경</button>
</div>
<div id="imDeployMsg" class="small"></div>
<div id="imCredsBox" class="hidden">
<div class="two">
<label>접속 계정(ID)<input id="imcUser" autocomplete="off"></label>
<label>포트<input id="imcPort" inputmode="numeric" autocomplete="off"></label>
</div>
<label>새 비밀번호 <span class="muted small">(바꿀 때만)</span><input id="imcPw" type="password" autocomplete="new-password"></label>
<label>사이트 파일 경로<input id="imcDocroot" autocomplete="off"></label>
<label>사이트 주소<input id="imcUrl" autocomplete="off"></label>
<div class="row"><button type="button" id="imcSave" class="primary small-btn">확인 후 저장</button><span id="imcMsg" class="muted small"></span></div>
</div>
</section>
<!-- v1.3.5: 검색사이트 등록 (구글·네이버, IndexNow) — 배포가 끝난 사이트만 -->
<section class="card hidden" id="searchCard">
<h2>검색사이트 등록 <button type="button" class="help" data-tip="구글 Search Console 과 네이버 서치어드바이저에 사이트를 등록합니다. 검색사이트가 주는 확인 코드를 넣으면 첫 페이지에 확인 태그를 넣고 다시 배포하며, 검색사이트에서 '확인'·사이트맵 제출 같은 사람이 해야 하는 단계는 체크리스트로 보여 줍니다(관리자 콘솔에서도 진행 가능). IndexNow 로 빙·네이버 등에 새 페이지 색인을 바로 요청할 수 있습니다. 배포가 끝난 사이트만 쓸 수 있고, 생성·수정·배포 중에는 쓸 수 없습니다.">?</button></h2>
<div id="searchBody"></div>
</section>
</div>
<!-- ===== 운영(manage, v1.4.14 "관리" → 1.6.40 "운영"): 기능 모듈·사이트 관리자 페이지·메일, 문의 접수, 사이트 현황, 현황 분석, 캐시 사용, 사이트 설정 =====
이 화면을 고르면 오른쪽 캔버스가 "사이트 관리 창"(사이트 관리자 페이지 + 현황 요약)으로 바뀐다 (public/manage-ui.js). -->
<div class="tabpane hidden" id="pane-manage">
<p class="muted small" id="manageHint"></p>
<section class="card hidden" id="moduleCard">
<h2>사이트 기능 모듈 <button type="button" class="ghost small-btn" id="btnModuleReload">새로고침</button> <button type="button" class="help" data-tip="게시판·문의폼·설문·예약·갤러리·지도·후기·FAQ·회원 기능입니다. 켜면 관리자 페이지가 생기고, 수정 지시에 '게시판을 추가해 주세요'처럼 적으면 표준 코드로 페이지에 들어갑니다. 글·예약·응답 같은 데이터는 사이트 관리자 페이지에서 관리합니다.">?</button></h2>
<div id="moduleList" class="module-list"></div>
<div class="row"><a id="btnSiteAdmin" class="btn ghost small-btn" target="_blank" rel="noopener">사이트 관리자 페이지 열기</a><span class="muted small">— 오른쪽 사이트 관리 창에서도 바로 관리할 수 있습니다</span></div>
<!-- v1.3.2: 이 사이트의 메일 시스템 상태 (운영자가 켜면 "메일시스템 설정중" → DNS 확인 후 "사용 가능") -->
<div class="mail-line hidden" id="mailLine"><span class="muted small">메일</span> <span class="pill small" id="mailLabel"></span> <span class="muted small" id="mailMsg"></span> <a id="mailOpen" class="btn ghost small-btn hidden" target="_blank" rel="noopener">웹메일 열기</a></div>
</section>
<section class="card hidden" id="inquiryCard">
<h2>문의 접수 내역 <span class="muted small" id="inquiryCount"></span> <button type="button" class="ghost small-btn" id="btnInquiryReload">새로고침</button> <button type="button" class="help" data-tip="사이트 문의 폼으로 들어온 내용입니다. 문의 수신 이메일이 설정되어 있으면 메일로도 발송되며, 미발송 건은 '재발송'으로 다시 보낼 수 있습니다.">?</button></h2>
<p class="muted small" id="inquiryNote"></p>
<ul id="inquiryList" class="git-list"></ul>
</section>
<!-- v1.7.2 (요구 2): 사이트 정보는 오른쪽 캔버스의 "사이트 정보" 페이지(public/site-info.js)로 옮겼다. 여기에는 요약 세 줄과 "자세히 보기" 만 남는다 (app.js renderStats) -->
<section class="card hidden" id="siteInfoCard">
<h2>사이트 정보 <span class="muted small" id="statsUpdated"></span> <button type="button" class="ghost small-btn" id="btnStatsReload">새로고침</button> <button type="button" class="help" data-tip="접속 통계·네트워크 사용량(송신/수신)·디스크 사용량(웹·DB·메일)을 그래프로 보는 사이트 정보 페이지는 오른쪽 창에 열립니다. 방문자·페이지뷰·네트워크는 사이트가 올라간 서버의 접속 로그(5분 주기, 봇 제외)로 집계하고, 로그를 읽을 수 없는 서버는 통계 스크립트의 보고(추정)를 씁니다.">?</button></h2>
<div class="totals stat-grid" id="statVisits"></div>
<div class="row"><button type="button" class="primary small-btn" id="btnSiteInfoOpen">오른쪽 창에서 자세히 보기 (그래프)</button></div>
</section>
<!-- 사이트 관리하기·복사하기: 현황 분석 (v1.2.0) -->
<section class="card hidden" id="analysisCard">
<h2>사이트 현황 분석 <span class="muted small" id="anAt"></span> <button type="button" class="ghost small-btn" id="btnReanalyze">다시 분석</button> <button type="button" class="help" data-tip="가져오거나 복사한 사이트의 파일 통계(종류별 개수·크기, 페이지, 사용 기술)와 웹서버 정보, AI 요약(구성·기술·수정 난이도·문제점·개선 제안)입니다. AI 요약은 '기본' 등급으로 실행되며 캐시가 차감됩니다.">?</button></h2>
<div id="anBody" class="small"></div>
</section>
<section class="card hidden" id="usageCard">
<h2>캐시 사용 내역 <span class="muted small" id="usageScope"></span> <button type="button" class="ghost small-btn" id="btnUsageDetail">상세 보기</button> <button type="button" class="help" data-tip="작업 단위(생성·수정·검증 등)별 사용 캐시입니다. 작업이 끝날 때 잔액에서 차감되며 중단된 회차(*)는 추정값입니다. 최근 5건이 보이고 표 안에서 스크롤하면 나머지가 보입니다. 상세 보기에서 전체 이용 내역과 일간·주간·월간 사용량을 볼 수 있습니다.">?</button></h2>
<div class="totals">
<div class="tot"><span class="k">이 사이트 사용 캐시</span><span class="v" id="tKrw">0원</span></div>
<div class="tot"><span class="k">잔여 캐시</span><span class="v" id="tBalance">-</span></div>
</div>
<div class="table-wrap usage-scroll">
<table id="usageTable"><thead><tr><th>#</th><th>종류</th><th>지시</th><th>캐시</th><th>시간</th></tr></thead><tbody></tbody></table>
</div>
<ul id="creditLedger" class="hidden"></ul>
</section>
<!-- v1.3.10: 관리자 빌더 전용 — 이 사이트를 템플릿으로 등록 -->
<section class="card hidden" id="tplRegisterCard">
<h2>템플릿으로 등록 <span class="pill small">관리자</span> <button type="button" class="help" data-tip="지금 사이트의 파일을 템플릿으로 담습니다(이후 사이트를 고쳐도 템플릿은 그대로이며, 관리자 콘솔 › 템플릿 에서 '다시 담기' 로 바꿉니다). 사용자에게 보이게 하려면 관리자 콘솔에서 '사용자 사용 허용' 을 켭니다.">?</button></h2>
<label>템플릿 이름<input id="tplRegTitle" autocomplete="off"></label>
<div class="two"><label>디렉터리<select id="tplRegDir"></select></label><label>해시태그<input id="tplRegTags" placeholder="#카페 #미니멀" autocomplete="off"></label></div>
<label>설명<textarea id="tplRegDesc" rows="2"></textarea></label>
<div class="row"><button type="button" id="tplRegGo" class="primary small-btn">템플릿으로 등록</button><span class="muted small" id="tplRegMsg"></span></div>
</section>
<section class="card hidden" id="siteDangerCard">
<h2>사이트 설정</h2>
<div class="small muted">사이트 ID <code id="siteId"></code></div>
<div class="row" style="margin-top:6px"><button type="button" class="ghost small-btn danger hidden" id="btnDeleteSite">사이트 폐기</button><button type="button" class="help" data-tip="사이트 폐기는 배포된 파일·도메인 설정·버전·통계·문의 데이터를 모두 지우며 되돌릴 수 없습니다. 가져온 사이트의 원본 서버 파일은 지우지 않습니다.">?</button></div>
</section>
</div>
<!-- ===== 도메인(domains, v1.4.0 → 1.6.40 분리): 서브 도메인 등록 / 도메인 검색·구매(장바구니) / 이미 가진 도메인 등록 / 사이트에 연결 — public/domains-ui.js ===== -->
<div class="tabpane hidden" id="pane-domains"><div id="domainsBody"></div></div>
<!-- ===== DNS(dns, v1.6.40 신설): 도메인별 DNS 레코드·전파·직접 관리 도메인의 필요한 설정 확인 — 배포·메일과 연결되는 중간 단계. public/domains-ui.js (view=dns) ===== -->
<div class="tabpane hidden" id="pane-dns"><div id="dnsBody"></div></div>
<!-- ===== 메일(mail, v1.6.40 신설): 도메인별 메일(MX) 자동 설정·상태, 사이트 메일 시스템·웹메일 — public/domains-ui.js (view=mail) ===== -->
<div class="tabpane hidden" id="pane-mail"><div id="mailBody"></div></div>
<!-- ===== 캐시·충전(credits, v1.6.40 신설): 잔액·사용 요약, 선불 충전 상품 선택 → 결제 진행(PG 연동 전: 주문 접수만), 내 주문 — public/credits-ui.js ===== -->
<div class="tabpane hidden" id="pane-credits"><div id="creditsBody"></div></div>
<!-- ===== 설정(settings): 계정 ===== -->
<div class="tabpane hidden" id="pane-settings">
<section class="card" id="profileCard">
<h2>계정 정보</h2>
<label>아이디<input id="pfId" readonly></label>
<label>이름 (회사명)<input id="pfName" autocomplete="organization"></label>
<div class="row"><button type="button" id="pfSave" class="primary small-btn">저장</button><span id="pfMsg" class="muted small"></span></div>
<p class="muted small" id="pfCreditLine"></p>
</section>
<section class="card" id="myFeedbackCard">
<h2>내 문의 내역 <button type="button" class="ghost small-btn" id="btnFeedback2">새 글</button> <button type="button" class="help" data-tip="사용 방법 문의, 불편한 점이나 오류, 바라는 기능을 운영팀에 보냅니다. 올리면 AI 가 내용을 분석해 운영팀에 전달하고, 운영팀 답변이 오면 여기에 표시됩니다.">?</button></h2>
<ul id="myFeedbackList" class="git-list stat-list"></ul>
</section>
<section class="card" id="pwCard">
<h2>비밀번호 변경</h2>
<label>현재 비밀번호<input id="pwCur" type="password" autocomplete="current-password"></label>
<label data-pw-rule="label">새 비밀번호 (6자 이상)<input id="pwNew" type="password" autocomplete="new-password"></label>
<label>새 비밀번호 확인<input id="pwNew2" type="password" autocomplete="new-password"></label>
<div class="row"><button type="button" id="pwSave" class="primary small-btn">변경</button><span id="pwMsg" class="muted small"></span></div>
</section>
</div>
</div>
</aside>
<main class="right">
<div class="toolbar">
<span class="url" id="siteUrl">아직 생성된 페이지가 없습니다</span>
<select id="fileSelect" class="hidden" title="페이지 선택"></select>
<button id="btnMobile" class="ghost" disabled title="모바일 화면 크기로 미리보기">모바일 미리보기</button>
<button id="btnReload" class="ghost" disabled>새로고침</button>
<a id="btnOpen" class="btn ghost disabled" target="_blank" rel="noopener">새 탭에서 열기</a>
<button id="btnCopy" class="ghost" disabled>URL 복사</button>
<button id="btnToggleSource" class="ghost" disabled title="우측 화면을 위(미리보기)/아래(소스) 로 분할">소스 보기</button>
<!-- v1.8.0 직접 편집(시각 편집기): 미리보기 화면에서 글을 바로 고치고 블록을 끌어 옮기고 구성 요소를 놓는다. 사이트와 HTML 페이지가 골라져 있을 때만 켜진다 (public/visual-editor-ui.js) -->
<button id="btnVisual" class="ghost" disabled aria-pressed="false" title="미리보기 화면에서 글을 바로 고치고 블록을 끌어 옮기는 직접 편집 모드 (AI 캐시를 쓰지 않습니다)">직접 편집</button>
</div>
<div class="preview" id="previewArea">
<!-- v1.8.0 직접 편집 도구 줄: 저장 상태(aria-live) · 실행 취소 · 다시 실행 · 버리고 다시 불러오기 · 저장(Ctrl+S) · 편집 끝내기 -->
<div id="veBar" class="ve-bar hidden" role="toolbar" aria-label="직접 편집 도구">
<span class="ve-title">직접 편집</span>
<span id="veState" class="ve-state clean" aria-live="polite">변경 없음</span>
<span class="spacer"></span>
<button type="button" id="veUndo" class="ghost small-btn" disabled title="실행 취소 (Ctrl+Z)" aria-keyshortcuts="Control+Z">실행 취소</button>
<button type="button" id="veRedo" class="ghost small-btn" disabled title="다시 실행 (Ctrl+Shift+Z)" aria-keyshortcuts="Control+Shift+Z">다시 실행</button>
<button type="button" id="veDiscard" class="ghost small-btn" disabled title="저장하지 않은 변경을 버리고 저장된 페이지를 다시 불러옵니다">버리고 다시 불러오기</button>
<button type="button" id="veSave" class="primary small-btn" disabled title="선택한 HTML 파일에 저장하고 버전으로 남깁니다 (Ctrl+S)" aria-keyshortcuts="Control+S">저장 (Ctrl+S)</button>
<button type="button" id="veClose" class="ghost small-btn" title="직접 편집을 끝내고 미리보기로 돌아갑니다">편집 끝내기</button>
</div>
<div id="emptyState" class="empty">
<div class="empty-inner">
<h3>미리보기</h3>
<p>왼쪽 <b>만들기</b>에서 사이트를 만들면 이 영역에 결과가 표시됩니다. 만들어 둔 사이트는 왼쪽 레일의 사이트 선택 상자에서 고르고 <b>수정</b>에서 고칩니다.</p>
</div>
</div>
<!-- v1.4.15 사용자 첫 화면 페이지: 사이트를 고르기 전 오른쪽 창에만 보이는 관리자 지정 페이지 (app.js showWelcome) -->
<div id="welcomeBox" class="welcome-box hidden"><iframe id="welcomeFrame" class="welcome-frame" title="첫 화면" sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox"></iframe></div>
<!-- v1.8.0 직접 편집 무대: 왼쪽 구성 요소 팔레트 · 가운데 미리보기(캔버스) · 오른쪽 속성 창. 팔레트·속성 창은 캔버스를 가리지 않고 옆(좁은 화면에서는 아래)에 놓이며 접을 수 있다 -->
<div class="ve-stage" id="veStage">
<aside id="vePalette" class="ve-side ve-palette hidden" role="region" aria-label="구성 요소 팔레트">
<div class="ve-side-head"><button type="button" id="vePaletteToggle" class="ve-toggle" aria-expanded="true" aria-controls="vePaletteList" title="팔레트 접기/펼치기">▾</button><b>구성 요소</b> <button type="button" class="help" data-tip="항목을 미리보기로 끌어다 놓으면 그 자리에 들어갑니다. 마우스를 쓰기 어려우면 '추가' 버튼으로 선택한 요소 뒤(선택이 없으면 페이지 끝)에 넣을 수 있습니다.">?</button></div>
<ul id="vePaletteList" class="ve-palette-list" aria-label="구성 요소 목록"></ul>
</aside>
<div class="frame-wrap" id="frameWrap"><iframe id="frame" class="hidden" title="생성된 페이지 미리보기" sandbox="allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-modals allow-downloads"></iframe><!-- v1.3.17 (S02): 미리보기는 별도 출처(sandbox, allow-same-origin 없음) — 사이트 스크립트가 빌더 세션·저장소를 쓰지 못한다. v1.8.0 직접 편집 캔버스도 같은 iframe(srcdoc)을 쓴다 --></div>
<aside id="veInspector" class="ve-side ve-inspector hidden" role="region" aria-label="선택한 요소 속성">
<div class="ve-side-head"><button type="button" id="veInspectorToggle" class="ve-toggle" aria-expanded="true" aria-controls="veInspectorBody" title="속성 창 접기/펼치기">▾</button><b>선택한 요소</b></div>
<div id="veInspectorBody" class="ve-inspector-body"></div>
</aside>
</div>
<div id="splitter" class="splitter hidden" title="드래그하여 크기 조절"></div>
<div id="editorPane" class="editor hidden">
<div class="editor-bar">
<select id="editFile" title="편집할 파일"></select>
<span id="editState" class="edit-state">저장됨</span>
<span class="spacer"></span>
<label class="inline"><input type="checkbox" id="liveApply" checked> 편집 즉시 미리보기 반영</label>
<button id="btnRevert" class="ghost small-btn">되돌리기</button>
<button id="btnSave" class="primary small-btn">파일에 저장 (Ctrl+S)</button>
</div>
<div class="editor-body">
<pre id="lineNos" class="line-nos" aria-hidden="true"></pre>
<textarea id="editor" spellcheck="false" wrap="off"></textarea>
</div>
</div>
</div>
<!-- ===== v1.4.14 (요구 2) 관리 탭의 오른쪽 창: 미리보기 대신 "사이트 관리 창" =====
위: 사이트 관리자 페이지(회원·게시판·문의·예약 등 모듈 데이터 관리, /site-admin/<ID>/?embed=1) 와 현황 보기 전환
아래: 관리자 페이지 iframe 또는 현황 대시보드(접속·저장 공간·문의·모듈·메일·배포 상태). 그리기는 public/manage-ui.js -->
<div class="manage-view hidden" id="manageView">
<div class="toolbar mv-bar">
<span class="url" id="mvTitle">사이트 관리</span>
<button type="button" id="mvTabAdmin" class="ghost active" title="회원·게시판·문의·예약 등 모듈 데이터를 관리하는 사이트 관리자 페이지">사이트 관리자</button>
<button type="button" id="mvTabStatus" class="ghost" title="사이트 상태·접속 통계 그래프·네트워크 송수신 그래프·디스크 사용량 (v1.7.2)">사이트 정보</button>
<button type="button" id="mvReload" class="ghost" disabled>새로고침</button>
<a id="mvOpen" class="btn ghost disabled" target="_blank" rel="noopener">새 탭에서 열기</a>
</div>
<div class="mv-body">
<div id="mvEmpty" class="empty"><div class="empty-inner"><h3>사이트 관리</h3><p>왼쪽 레일의 사이트 선택 상자에서 사이트를 고르면 여기에 <b>사이트 관리자 페이지</b>와 <b>사이트 정보</b>(통계·네트워크·디스크 그래프)가 표시됩니다.</p></div></div>
<iframe id="mvAdminFrame" class="hidden" title="사이트 관리자 페이지"></iframe>
<div id="mvStatus" class="mv-status hidden"></div>
</div>
</div>
</main>
</div>
<div id="dropOverlay" class="drop-overlay hidden"><div class="drop-overlay-box" id="dropOverlayText">여기에 파일을 놓으세요</div></div>
<dialog id="fbDialog" class="wm-dialog"><form method="dialog"><h2>문의 · 제안 · 오류</h2>
<label>종류<select id="fbType"><option value="inquiry">사용 문의</option><option value="suggestion">개선 제안</option><option value="bug">오류 보고</option><option value="request">요구 사항</option></select></label>
<label>제목<input id="fbTitle" maxlength="120" placeholder="한 줄 요약"></label>
<label>내용 <span class="muted small">(무엇을 하려 했고, 무엇이 기대와 달랐는지)</span><textarea id="fbBody" rows="6" maxlength="5000"></textarea></label>
<p class="muted small" id="fbContext"></p>
<div class="fb-env-note small">보낼 때 문제 해결을 위해 <b>사용 환경 정보</b>가 함께 전송됩니다: 브라우저·운영체제, 화면 해상도와 창 크기, 화면 배율, 언어, 시간대, 서비스 버전, 현재 탭과 사이트 상태. 비밀번호나 입력 내용은 보내지 않습니다. <button type="button" class="ghost small-btn" id="fbEnvToggle">보낼 정보 보기</button><pre class="hidden" id="fbEnvPreview"></pre></div>
<div class="row"><button type="button" class="primary small-btn" id="fbSend">보내기</button><button type="button" class="ghost small-btn" id="fbCancel">취소</button><span class="muted small" id="fbMsg"></span></div>
</form></dialog>
<dialog id="usageDialog" class="wm-dialog wide"><form method="dialog"><h2>캐시 상세 내역</h2><div id="usageDialogBody"></div><div class="row"><button type="button" class="ghost small-btn" id="usageDialogClose">닫기</button></div></form></dialog>
<dialog id="domDialog" class="wm-dialog"><form method="dialog"><h2 id="domDialogTitle">도메인</h2><div id="domDialogBody"></div></form></dialog>
<div id="helpTip" class="help-tip hidden" role="tooltip"></div>
<dialog id="detailDialog" class="wm-dialog wide"><form method="dialog"><h2 id="ddTitle2">작업 상세 내역</h2><div id="ddBody2"></div><div class="row"><button type="button" class="ghost small-btn" id="ddClose2">닫기</button></div></form></dialog>
<script src="/numfmt.js"></script><!-- v1.4.5 숫자 칸 천 단위 쉼표 -->
<script src="/i18n.js"></script>
<script src="/pwrule.js"></script>
<script src="/app.js"></script>
<script src="/wizard.js"></script>
<script src="/import.js"></script>
<script src="/modules-ui.js"></script>
<script src="/optimize.js"></script>
<script src="/templates-ui.js"></script>
<script src="/domains-ui.js"></script>
<script src="/manage-ui.js"></script><!-- v1.4.14 관리 탭 오른쪽 사이트 관리 창 -->
<script src="/flow-ui.js"></script><!-- v1.6.40 홈(작업 흐름 지도)·레일 상태 배지·배포 점검표 -->
<script src="/credits-ui.js"></script><!-- v1.6.40 캐시·충전 화면(선불 충전 상품·주문) -->
<script src="/site-info.js"></script><!-- v1.7.2 오른쪽 캔버스 사이트 정보 페이지(그래프) -->
<script src="/visual-editor-core.js"></script><!-- v1.8.0 직접 편집 공용 핵심(실행 취소·저장 상태·메시지 규약·구성 요소) -->
<script src="/visual-editor-ui.js"></script><!-- v1.8.0 직접 편집 빌더 쪽 제어(팔레트·속성 창·저장) -->
</body>
</html>