mirror of
https://github.com/woodchen-ink/ChatGPT-Tailwind.git
synced 2025-07-18 14:02:00 +08:00
351 lines
19 KiB
HTML
351 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-cmn-Hans">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>ChatGPT-Tailwind</title>
|
||
<meta name="viewport"
|
||
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
|
||
<link rel="shortcut icon" href="https://cdn-img-qiniu.czl.net/2023/08/03/64cb6db97b0bf.png">
|
||
<link href="https://cdn.staticfile.org/daisyui/3.9.2/full.min.css" rel="stylesheet" type="text/css" />
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
<script src="https://cdn.staticfile.org/markdown-it/13.0.2/markdown-it.min.js"></script>
|
||
<link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/11.8.0/styles/vs2015.min.css" />
|
||
<style>
|
||
body {
|
||
font-family: system-ui, -apple-system, "Microsoft YaHei", Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, sans-serif !important;
|
||
}
|
||
</style>
|
||
<link href="https://cdn-r2.czl.net/frame/tailwind/typography.css" rel="stylesheet" type="text/css">
|
||
</head>
|
||
|
||
<body class="pb-72 pt-20 h-full">
|
||
<!-- 头部展示 -->
|
||
<div class="w-full fixed top-0 bg-gray-700 text-center py-5 z-50">
|
||
<h1 class="text-white text-lg font-bold">ChatGPT-Tailwind</h1>
|
||
</div>
|
||
|
||
<!-- 填写地址和密钥 -->
|
||
<div class="container mx-auto bg-blue-300 px-4 py-5 shadow-md hover:shadow-blue-500/50 -mt-5">
|
||
<div class="flex flex-wrap items-center justify-evenly ">
|
||
<div class="w-full md:w-1/3 mb-4 px-4">
|
||
<div class="flex flex-col">
|
||
<div class="flex items-center mb-1">
|
||
<b>网址:</b>
|
||
<input
|
||
class="border-2 border-gray-300 bg-white h-10 px-2 rounded-lg text-sm flex-grow focus:outline-none"
|
||
placeholder="api.openai.com" type="text" id="proxyurl" required value="oapi.czl.net" />
|
||
</div>
|
||
|
||
<p class="text-xs italics ">*代理域名,无需"https://"</p>
|
||
</div>
|
||
</div>
|
||
<div class="w-full md:w-1/3 mb-4 px-4">
|
||
<div class="flex flex-col">
|
||
<div class="flex items-center mb-1">
|
||
<b>Key:</b>
|
||
<input
|
||
class="border-2 border-gray-300 bg-white h-10 px-2 rounded-lg text-sm flex-grow focus:outline-none"
|
||
placeholder="sk-xxxxxxxxxxx" type="text" id="key" required />
|
||
</div>
|
||
<div class="text-xs italics">*填写你的key,<a href="https://oapi.czl.net" target="_blank"
|
||
class="link link-error">获取国内可用key</a></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="w-full md:w-1/3 mb-4 px-4">
|
||
<div class="flex flex-col">
|
||
<div class="flex items-center mb-1">
|
||
<b>模型:</b>
|
||
<select
|
||
class="border-2 border-gray-300 bg-white h-10 px-2 rounded-lg text-sm flex-grow focus:outline-none select-primary"
|
||
id="model">
|
||
<!-- your options here -->
|
||
<option value="gpt-3.5-turbo">gpt-3.5-turbo</option>
|
||
<option value="gpt-3.5-turbo-0301">gpt-3.5-turbo-0301</option>
|
||
<option value="gpt-3.5-turbo-0613">gpt-3.5-turbo-0613</option>
|
||
<option value="gpt-3.5-turbo-16k">gpt-3.5-turbo-16k</option>
|
||
<option value="gpt-3.5-turbo-16k-0613">gpt-3.5-turbo-16k-0613</option>
|
||
<option value="gpt-3.5-turbo-instruct">gpt-3.5-turbo-instruct</option>
|
||
<option value="gpt-4">gpt-4</option>
|
||
<option value="gpt-4-0314">gpt-4-0314</option>
|
||
<option value="gpt-4-0613">gpt-4-0613</option>
|
||
<option value="gpt-4-32k">gpt-4-32k</option>
|
||
<option value="gpt-4-32k-0314">gpt-4-32k-0314</option>
|
||
<option value="gpt-4-32k-0613">gpt-4-32k-0613</option>
|
||
<option value="text-ada-001">text-ada-001</option>
|
||
<option value="text-babbage-001">text-babbage-001</option>
|
||
<option value="text-curie-001">text-curie-001</option>
|
||
<option value="text-davinci-002">text-davinci-002</option>
|
||
<option value="text-davinci-003">text-davinci-003</option>
|
||
<option value="text-davinci-edit-001">text-davinci-edit-001</option>
|
||
<option value="code-davinci-edit-001">code-davinci-edit-001</option>
|
||
<option value="whisper-1">whisper-1</option>
|
||
<option value="davinci">davinci</option>
|
||
<option value="curie">curie</option>
|
||
<option value="babbage">babbage</option>
|
||
<option value="ada">ada</option>
|
||
<option value="text-embedding-ada-002">text-embedding-ada-002</option>
|
||
<option value="text-search-ada-doc-001">text-search-ada-doc-001</option>
|
||
<option value="text-moderation-stable">text-moderation-stable</option>
|
||
<option value="text-moderation-latest">text-moderation-latest</option>
|
||
<option value="dall-e">dall-e</option>
|
||
<option value="claude-instant-1">[Claude]claude-instant-1</option>
|
||
<option value="claude-2">[Claude]claude-2</option>
|
||
<option value="ERNIE-Bot">[百度]ERNIE-Bot</option>
|
||
<option value="ERNIE-Bot-turbo">[百度]ERNIE-Bot-turbo</option>
|
||
<option value="Embedding-V1">[百度]Embedding-V1</option>
|
||
<option value="PaLM-2">[google]PaLM-2</option>
|
||
<option value="chatglm_pro">[智谱]chatglm_pro</option>
|
||
<option value="chatglm_std">[智谱]chatglm_std</option>
|
||
<option value="chatglm_lite">[智谱]chatglm_lite</option>
|
||
<option value="qwen-turbo">[阿里]qwen-turbo</option>
|
||
<option value="qwen-plus">[阿里]qwen-plus</option>
|
||
<option value="text-embedding-v1">[阿里]text-embedding-v1</option>
|
||
<option value="SparkDesk">[讯飞]SparkDesk</option>
|
||
<option value="360GPT_S2_V9">[360]360GPT_S2_V9</option>
|
||
<option value="embedding-bert-512-v1">[360]embedding-bert-512-v1</option>
|
||
<option value="embedding_s1_v1">[360]embedding_s1_v1</option>
|
||
<option value="semantic_similarity_s1_v1">[360]semantic_similarity_s1_v1</option>
|
||
<option value="360GPT_S2_V9.4">[360]360GPT_S2_V9.4</option>
|
||
<option value="hunyuan">[腾讯]hunyuan</option>
|
||
|
||
</select>
|
||
</div>
|
||
<div class="text-xs italics">*选择模型</div>
|
||
</div>
|
||
</div>
|
||
<div class="w-full ">
|
||
<div class="flex items-center">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="text-center py-2 bg-blue-100">
|
||
本项目由<a href="https://woodchen.ink" target="_blank" class="link link-accent">woodchen</a>开源于<a
|
||
class="link link-accent" href="https://github.com/woodchen-ink/ChatGPT-Tailwind"
|
||
target="_blank">Github</a>, 欢迎给个star
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 对话展示 -->
|
||
<div class="container mx-auto" id="chatbox">
|
||
</div>
|
||
|
||
<!-- 底部固定 -->
|
||
<div class="w-full fixed bottom-0 bg-gray-700 py-4">
|
||
<div class="container mx-auto">
|
||
<textarea id="userInput" class="w-full textarea textarea-inf" type="text" rows="3"
|
||
placeholder="在这里输入问题...换行请按Shift+Enter"></textarea>
|
||
<button id="sendButton" class="btn btn-info btn-block">询问</button>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
<script src="https://cdn.staticfile.org/jquery/3.7.1/jquery.min.js"></script>
|
||
<script src="https://cdn.staticfile.org/clipboard.js/2.0.11/clipboard.min.js"></script>
|
||
<script>
|
||
const url = new URL(window.location.href);
|
||
const chatbox = $("#chatbox");
|
||
const userInput = $("#userInput");
|
||
const sendButton = $("#sendButton");
|
||
$(document).ready(function () {
|
||
const messages = [];
|
||
|
||
sendButton.on("click", () => {
|
||
const message = userInput.val();
|
||
if (message) {
|
||
messages.push({
|
||
"role": "user",
|
||
"content": message
|
||
});
|
||
chatbox.append(`<div class="chat chat-end"><div class="chat-image avatar">
|
||
<div class="w-10 rounded-full">
|
||
<img src="https://cdn-img-r2.czl.net/2023/08/10/64d3b992cf86e.png" />
|
||
</div>
|
||
</div>
|
||
<div class="chat-header">You</div><div class="chat-bubble chat-bubble-info">${message}</div><div>`);
|
||
userInput.val("");
|
||
sendButton.html('<span class="loading loading-infinity loading-lg"></span>');
|
||
sendButton.removeClass('btn-info').addClass('btn-error');
|
||
// sendButton.prop("disabled", true);
|
||
fetchMessages();
|
||
}
|
||
});
|
||
|
||
userInput.on("keydown", (event) => {
|
||
if (event.keyCode === 13 && !event.ctrlKey && !event.shiftKey) {
|
||
event.preventDefault();
|
||
sendButton.click();
|
||
} else if (event.keyCode === 13 && (event.ctrlKey || event.shiftKey)) {
|
||
event.preventDefault();
|
||
const cursorPosition = userInput.prop("selectionStart");
|
||
const currentValue = userInput.val();
|
||
userInput.val(
|
||
currentValue.slice(0, cursorPosition) +
|
||
"\n" +
|
||
currentValue.slice(cursorPosition)
|
||
);
|
||
userInput.prop("selectionStart", cursorPosition + 1);
|
||
userInput.prop("selectionEnd", cursorPosition + 1);
|
||
}
|
||
});
|
||
|
||
function fetchMessages() {
|
||
let proxyurl = document.getElementById("proxyurl").value;
|
||
let key = document.getElementById("key").value;
|
||
let modelElement = document.getElementById("model");
|
||
let model = modelElement.options[modelElement.selectedIndex].value;
|
||
$("#proxyurl").val();
|
||
$("#key").val();
|
||
|
||
try {
|
||
var settings = {
|
||
"url": "https://" + proxyurl + "/v1/chat/completions",
|
||
"method": "POST",
|
||
"timeout": 0,
|
||
"headers": {
|
||
"Authorization": "Bearer " + key,
|
||
"Content-Type": "application/json"
|
||
},
|
||
"data": JSON.stringify({
|
||
"model": model,
|
||
"messages": messages
|
||
}),
|
||
};
|
||
|
||
$.ajax(settings).done(function (response) {
|
||
console.log(response)
|
||
|
||
const message = response.choices[0].message
|
||
messages.push({
|
||
role: message.role,
|
||
content: message.content,
|
||
})
|
||
|
||
let htmlText = window.markdownit().render(message.content);
|
||
htmlText = htmlText.replace(/<pre>/g, '<pre class="pre-with-button">');
|
||
|
||
const chatBubbleClass =
|
||
message.role === 'user'
|
||
? 'chat-bubble-info'
|
||
: 'chat-bubble-accent'
|
||
const avatarURL =
|
||
message.role === 'user'
|
||
? 'https://cdn-img-r2.czl.net/2023/08/10/64d3b992cf86e.png'
|
||
: 'https://cdn-img-r2.czl.net/2023/08/10/64d3b8c9819c7.png'
|
||
const chatHeader = message.role === 'user' ? 'You' : 'AI'
|
||
|
||
let myID = 'codeSnippet-' + Date.now()
|
||
let preID = 'pre-' + Date.now()
|
||
if (htmlText.includes('<code')) {
|
||
|
||
htmlText = htmlText.replace('<pre', '<pre id="' + preID + '"')
|
||
htmlText = htmlText.replace('<code', '<code id="' + myID + '"')
|
||
}
|
||
console.log('htmlText', htmlText)
|
||
const chatHTML = `
|
||
<div class="chat chat-start">
|
||
<div class="chat-image avatar">
|
||
<div class="w-10 rounded-full">
|
||
<img src="${avatarURL}" />
|
||
</div>
|
||
</div>
|
||
<div class="chat-header">${chatHeader}</div>
|
||
<div class="chat-bubble ${chatBubbleClass} prose prose-slate">${htmlText}</div>
|
||
</div>`
|
||
|
||
$('#chatbox').append(chatHTML);
|
||
$('.pre-with-button').each(function (index) {
|
||
if ($(this).find('code').length > 0) {
|
||
let preElement = $(this);
|
||
let codeElement = $(this).find('code')[0];
|
||
var copyButton = document.createElement('button');
|
||
copyButton.classList.add('absolute', 'right-5', 'mt-[-20px]', 'mr-1', 'btn', 'btn-active', 'btn-xs');
|
||
copyButton.innerText = 'COPY';
|
||
var clipboard = new ClipboardJS(copyButton, {
|
||
text: function () {
|
||
return codeElement.innerText;
|
||
},
|
||
});
|
||
|
||
clipboard.on('success', function (e) {
|
||
console.log('Success');
|
||
let alertBox = document.createElement('div');
|
||
|
||
alertBox.classList.add('fixed', 'top-20', 'left-1/2', 'transform', '-translate-x-1/2', 'z-50');
|
||
alertBox.innerHTML = `
|
||
<div class="alert alert-success">
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||
</svg>
|
||
<span>Success! Your text has been copied.</span>
|
||
</div>`;
|
||
document.body.appendChild(alertBox);
|
||
setTimeout(function () {
|
||
document.body.removeChild(alertBox);
|
||
}, 500);
|
||
});
|
||
|
||
clipboard.on('error', function (e) {
|
||
console.log('Failed');
|
||
let alertBox = document.createElement('div');
|
||
|
||
alertBox.classList.add('fixed', 'top-20', 'left-1/2', 'transform', '-translate-x-1/2', 'z-50');
|
||
alertBox.innerHTML = `
|
||
<div class="alert alert-error">
|
||
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none" viewBox="0 0 24 24">
|
||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||
</svg>
|
||
<span>Error! Copy failed.</span>
|
||
</div>`;
|
||
document.body.appendChild(alertBox);
|
||
setTimeout(function () {
|
||
document.body.removeChild(alertBox);
|
||
}, 500);
|
||
})
|
||
|
||
preElement.append(copyButton);
|
||
}
|
||
});
|
||
hljs.highlightAll();
|
||
sendButton.html('<span>询问</span>')
|
||
sendButton.removeClass('btn-error').addClass('btn-info')
|
||
|
||
}).fail(function (jqXHR, textStatus, errorThrown) {
|
||
sendButton.html('<span>询问</span>');
|
||
sendButton.removeClass('btn-error').addClass('btn-info');
|
||
var errorMessage;
|
||
if (jqXHR.status === 0) {
|
||
errorMessage = "Network error, please check your internet connection.";
|
||
} else {
|
||
var response = JSON.parse(jqXHR.responseText);
|
||
errorMessage = response.error.message;
|
||
}
|
||
chatbox.append(`<div class="chat chat-start"><div class="chat-image avatar">
|
||
<div class="w-10 rounded-full">
|
||
<img src="https://cdn-img-r2.czl.net/2023/08/10/64d3b8c9819c7.png" />
|
||
</div>
|
||
</div>
|
||
<div class="chat-header">AI</div><div class="chat-bubble chat-bubble-error prose prose-slate">Error: ${errorMessage}</div><div>`);
|
||
});
|
||
|
||
} catch (error) {
|
||
sendButton.html('<span>询问</span>');
|
||
sendButton.removeClass('btn-error').addClass('btn-info');
|
||
chatbox.append(`<div class="chat chat-start"><div class="chat-image avatar">
|
||
<div class="w-10 rounded-full">
|
||
<img src="https://cdn-img-r2.czl.net/2023/08/10/64d3b8c9819c7.png" />
|
||
</div>
|
||
</div>
|
||
<div class="chat-header">AI</div><div class="chat-bubble chat-bubble-error prose prose-slate">Error: ${error.message}</div><div>`);
|
||
}
|
||
}
|
||
});
|
||
</script>
|
||
<script src="https://cdn.staticfile.org/highlight.js/11.8.0/highlight.min.js" defer></script>
|
||
|
||
</body>
|
||
|
||
</html> |