Initial commit: AI platform app (server, views, lib, data, deploy docs)
Made-with: Cursor
This commit is contained in:
163
views/ai-prompts.ejs
Normal file
163
views/ai-prompts.ejs
Normal file
@@ -0,0 +1,163 @@
|
||||
<!doctype html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>프롬프트 라이브러리 - XAVIS</title>
|
||||
<link rel="stylesheet" href="/public/styles.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-shell">
|
||||
<%- include('partials/nav', { activeMenu: 'ai-explore' }) %>
|
||||
<div class="content-area">
|
||||
<header class="topbar">
|
||||
<h1>프롬프트</h1>
|
||||
<a class="top-action-link" href="/ai-explore">AI 목록</a>
|
||||
</header>
|
||||
<main class="container container-ai-full">
|
||||
<a href="/ai-explore" class="prompts-back" aria-label="AI 탐색으로 돌아가기">← AI</a>
|
||||
|
||||
<section class="prompts-hero">
|
||||
<h1>프롬프트 라이브러리</h1>
|
||||
<p class="prompts-lead">
|
||||
업무별로 자주 쓰는 기본 프롬프트를 골라 바로 복사해 사용하세요. ChatGPT·Claude·자비스 채팅 등 어디에든 붙여 넣을 수 있습니다.
|
||||
</p>
|
||||
<p class="prompts-stats">
|
||||
<%= prompts.length %>가지 템플릿 · 사내 업무 시나리오 중심 · 복사 후 [ ] 부분만 채워 완성
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<% if (!prompts.length) { %>
|
||||
<section class="panel">
|
||||
<p class="empty">프롬프트 데이터를 불러오지 못했습니다. 관리자에게 문의해 주세요.</p>
|
||||
</section>
|
||||
<% } else { %>
|
||||
<section class="panel prompts-layout">
|
||||
<div>
|
||||
<h2 class="prompts-grid-title">시나리오 선택</h2>
|
||||
<div class="prompts-grid" id="promptCardList" role="list">
|
||||
<% prompts.forEach(function (p) { %>
|
||||
<button
|
||||
type="button"
|
||||
class="prompt-template-card"
|
||||
role="listitem"
|
||||
data-prompt-id="<%= p.id %>"
|
||||
aria-pressed="false"
|
||||
>
|
||||
<h3><%= p.title %></h3>
|
||||
<p><%= p.description %></p>
|
||||
<span class="prompt-mini-tag"><%= p.tag %></span>
|
||||
</button>
|
||||
<% }); %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prompts-preview-panel">
|
||||
<h2 id="previewTitle">프롬프트 미리보기</h2>
|
||||
<p class="prompts-preview-empty" id="previewEmpty">왼쪽에서 카드를 선택하세요.</p>
|
||||
<div id="previewActive" hidden>
|
||||
<div class="prompts-preview-toolbar">
|
||||
<button type="button" class="prompts-copy-btn" id="copyPromptBtn" disabled>클립보드에 복사</button>
|
||||
</div>
|
||||
<label class="visually-hidden" for="promptBody">선택한 프롬프트 전문</label>
|
||||
<textarea id="promptBody" class="prompts-body-textarea" readonly spellcheck="false" aria-describedby="previewHint"></textarea>
|
||||
<p class="prompts-hint" id="previewHint">대괄호 [ ] 안은 상황에 맞게 수정한 뒤 사용하세요.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<% } %>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
<% if (prompts.length) { %>
|
||||
<script>
|
||||
(function () {
|
||||
var library = <%- JSON.stringify(prompts) %>;
|
||||
var byId = {};
|
||||
library.forEach(function (p) {
|
||||
byId[p.id] = p;
|
||||
});
|
||||
|
||||
var cards = document.querySelectorAll(".prompt-template-card");
|
||||
var titleEl = document.getElementById("previewTitle");
|
||||
var bodyEl = document.getElementById("promptBody");
|
||||
var copyBtn = document.getElementById("copyPromptBtn");
|
||||
var emptyEl = document.getElementById("previewEmpty");
|
||||
var activeWrap = document.getElementById("previewActive");
|
||||
var selectedId = null;
|
||||
|
||||
function setSelected(id) {
|
||||
selectedId = id;
|
||||
cards.forEach(function (btn) {
|
||||
var on = btn.getAttribute("data-prompt-id") === id;
|
||||
btn.classList.toggle("is-selected", on);
|
||||
btn.setAttribute("aria-pressed", on ? "true" : "false");
|
||||
});
|
||||
var p = byId[id];
|
||||
if (!p) {
|
||||
emptyEl.hidden = false;
|
||||
activeWrap.hidden = true;
|
||||
copyBtn.disabled = true;
|
||||
titleEl.textContent = "프롬프트 미리보기";
|
||||
return;
|
||||
}
|
||||
emptyEl.hidden = true;
|
||||
activeWrap.hidden = false;
|
||||
titleEl.textContent = p.title;
|
||||
bodyEl.value = p.body;
|
||||
copyBtn.disabled = false;
|
||||
}
|
||||
|
||||
cards.forEach(function (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
setSelected(btn.getAttribute("data-prompt-id"));
|
||||
});
|
||||
});
|
||||
|
||||
copyBtn.addEventListener("click", function () {
|
||||
if (!bodyEl.value) return;
|
||||
navigator.clipboard.writeText(bodyEl.value).then(
|
||||
function () {
|
||||
var t = copyBtn.textContent;
|
||||
copyBtn.textContent = "복사됨";
|
||||
setTimeout(function () {
|
||||
copyBtn.textContent = t;
|
||||
}, 1600);
|
||||
},
|
||||
function () {
|
||||
bodyEl.select();
|
||||
try {
|
||||
document.execCommand("copy");
|
||||
} catch (e) {}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
var params = new URLSearchParams(window.location.search);
|
||||
var initial = params.get("id");
|
||||
if (initial && byId[initial]) {
|
||||
setSelected(initial);
|
||||
} else if (library[0]) {
|
||||
setSelected(library[0].id);
|
||||
} else {
|
||||
emptyEl.hidden = false;
|
||||
activeWrap.hidden = true;
|
||||
copyBtn.disabled = true;
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<% } %>
|
||||
<style>
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user