to:sync
This commit is contained in:
parent
9b2676e8a0
commit
190c5f0113
3
main.go
3
main.go
@ -56,13 +56,16 @@ func main() {
|
|||||||
|
|
||||||
//登录界面
|
//登录界面
|
||||||
e.GET("/", static.Login)
|
e.GET("/", static.Login)
|
||||||
|
e.GET("/favicon.ico", static.Favicon)
|
||||||
e.GET("/chat", static.Chat)
|
e.GET("/chat", static.Chat)
|
||||||
e.GET("/chat1", static.Chat1)
|
e.GET("/chat1", static.Chat1)
|
||||||
e.GET("/chat2", static.Chat2)
|
e.GET("/chat2", static.Chat2)
|
||||||
|
e.GET("/chat3", static.Chat3)
|
||||||
|
|
||||||
//user
|
//user
|
||||||
apps.InitUserGroup(e.Group("/v1/user/"))
|
apps.InitUserGroup(e.Group("/v1/user/"))
|
||||||
|
|
||||||
|
//static
|
||||||
static.InitStaticGroup(e.Group("/v1/static/"))
|
static.InitStaticGroup(e.Group("/v1/static/"))
|
||||||
|
|
||||||
//ai
|
//ai
|
||||||
|
@ -129,10 +129,10 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="left-panel">
|
<div class="left-panel">
|
||||||
<h2>智能问答助手</h2>
|
<h2>AI文本助手</h2>
|
||||||
<h3>常用问题</h3>
|
<h3>常用问题</h3>
|
||||||
<div class="recommendation">帮我写一个Java快速排序</div>
|
<div class="recommendation">帮我写一个Java快速排序</div>
|
||||||
<div class="recommendation">Java 8有什么新特性</div>
|
<div class="recommendation">Java 21有什么新特性</div>
|
||||||
<div class="recommendation">JVM优化建议</div>
|
<div class="recommendation">JVM优化建议</div>
|
||||||
<div class="recommendation">内存占用高,如何优化</div>
|
<div class="recommendation">内存占用高,如何优化</div>
|
||||||
<div class="recommendation">MySQL优化建议</div>
|
<div class="recommendation">MySQL优化建议</div>
|
||||||
@ -148,7 +148,7 @@
|
|||||||
发送
|
发送
|
||||||
</button>
|
</button>
|
||||||
<button style="margin-left: 20px;width: 100px;background-color: red" onclick="clearChat()"
|
<button style="margin-left: 20px;width: 100px;background-color: red" onclick="clearChat()"
|
||||||
class="submit-button">清除记录
|
class="submit-button">clear
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -156,8 +156,7 @@
|
|||||||
<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// 聊天历史记录
|
/*! @license DOMPurify 3.0.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.5/LICENSE */
|
||||||
var messageHistory = [];
|
|
||||||
|
|
||||||
// 添加AI信息
|
// 添加AI信息
|
||||||
function addAIMessage(message) {
|
function addAIMessage(message) {
|
||||||
@ -183,9 +182,6 @@
|
|||||||
chatlog.scrollTop = chatlog.scrollHeight;
|
chatlog.scrollTop = chatlog.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 历史消息
|
|
||||||
// const messagesList = [];
|
|
||||||
|
|
||||||
// 调用api
|
// 调用api
|
||||||
function chatApi(msg) {
|
function chatApi(msg) {
|
||||||
slideBottom();
|
slideBottom();
|
||||||
@ -203,7 +199,7 @@
|
|||||||
data: body,
|
data: body,
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
let res1=res?.choices[0]?.message?.content;
|
let res1=res?.choices[0]?.message?.content;
|
||||||
res1 = marked.parse(res1);
|
res1 = DOMPurify.sanitize(marked.parse(res1));
|
||||||
addAIMessage(res1);
|
addAIMessage(res1);
|
||||||
|
|
||||||
// if (res.code === 200) {
|
// if (res.code === 200) {
|
||||||
@ -236,7 +232,6 @@
|
|||||||
// 清空聊天记录
|
// 清空聊天记录
|
||||||
function clearChat() {
|
function clearChat() {
|
||||||
$("#chat-log").empty();
|
$("#chat-log").empty();
|
||||||
messageHistory = [];
|
|
||||||
addAIMessage("你好,请输入你想问的问题。");
|
addAIMessage("你好,请输入你想问的问题。");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
static/css/bootstrap.min.css
vendored
Normal file
6
static/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
85
static/css/chat.css
Normal file
85
static/css/chat.css
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
|
||||||
|
body {
|
||||||
|
/* background-color: #0b3d91; */
|
||||||
|
color: #f5f5f5;
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-message {
|
||||||
|
background-color: rgb(86, 144, 163);
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: 0;
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for received messages */
|
||||||
|
.response-message {
|
||||||
|
background-color: rgb(62, 62, 62);
|
||||||
|
color: white;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding-right: 20px;
|
||||||
|
position: relative;
|
||||||
|
margin-right: auto;
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.response-message p {
|
||||||
|
margin-right: 40px;
|
||||||
|
/* Add more styles here */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for the chat container */
|
||||||
|
#chat-container {
|
||||||
|
display: none;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.host-address-select {
|
||||||
|
background-color: red;
|
||||||
|
color: white;
|
||||||
|
padding: 1000px;
|
||||||
|
border-radius: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat-history {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 5px;
|
||||||
|
right: 5px;
|
||||||
|
margin: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#scroll-wrapper {
|
||||||
|
padding-top: 180px;
|
||||||
|
padding-bottom: 5.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#input-area {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-input {
|
||||||
|
overflow-y: auto;
|
||||||
|
resize: none; /* Prevent user from manually resizing */
|
||||||
|
height: 40px;
|
||||||
|
max-height: 200px; /* Set your desired max height here */
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) { /* Adjust 600px to the breakpoint you desire */
|
||||||
|
.d-flex h1 {
|
||||||
|
display: none; /* This will hide the h1 when the window width is 600px or less */
|
||||||
|
}
|
||||||
|
}
|
BIN
static/img/favicon.ico
Normal file
BIN
static/img/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 120 KiB |
125
static/index_ollama.back
Normal file
125
static/index_ollama.back
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Chat with Llama2</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<link href="/v1/static/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
<script src="/v1/static/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="/v1/static/js/marked.min.js"
|
||||||
|
integrity="sha384-dZulhREgb+hCgQMhZ2VG0l37VQj5pJBq2w0h7Jn3tdMn36aXNepF1+FMLBB4O649"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="/v1/static/js/purify.min.js" integrity="sha256-QigBQMy2be3IqJD2ezKJUJ5gycSmyYlRHj2VGBuITpU="
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="/v1/static/js/jquery-3.7.1.min.js"
|
||||||
|
integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
|
<link rel="stylesheet" href="/v1/static/css/chat.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body data-bs-theme="dark">
|
||||||
|
<div class="container">
|
||||||
|
<div id="scroll-wrapper">
|
||||||
|
<div id="chat-container" class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="chat-history"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="position-fixed w-100"
|
||||||
|
style="z-index: 9999; top: 0px; height: 200px; background: linear-gradient(180deg, black, transparent);">
|
||||||
|
|
||||||
|
<div class="position-fixed w-100"
|
||||||
|
style="z-index: 9999; top: 0px; height: 200px; background: linear-gradient(180deg, black, transparent);">
|
||||||
|
<div class="d-flex justify-content-between align-items-center m-0" style="padding: 16px;">
|
||||||
|
<h1>Chat with Ollama</h1>
|
||||||
|
<!-- Model dropdown -->
|
||||||
|
<div class="d-flex align-items-center m-1">
|
||||||
|
<div class="p-2 flex-grow-1 bd-highlight">
|
||||||
|
<label for="system-prompt" class="me-2" style="font-size: normal;">System Prompt</label>
|
||||||
|
<input id="system-prompt" class="form-control" type="text" placeholder="You are a helpful assistant"
|
||||||
|
style="width: auto;"></select>
|
||||||
|
<label for="host-address" class="me-2" style="font-size: normal;">Hostname</label>
|
||||||
|
<input id="host-address" class="form-control" type="text" placeholder="http://localhost:11434"
|
||||||
|
style="width: auto;"></select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="d-flex align-items-center m-2">
|
||||||
|
<div class="p-2 flex-grow-1 bd-highlight">
|
||||||
|
<label for="model-select" class="me-2" style="font-size: normal;">Model:</label>
|
||||||
|
<select class="form-select me-5" id="model-select" style="width: auto;"></select>
|
||||||
|
<label for="chat-select" class="me-2" style="font-size: normal;">History:</label>
|
||||||
|
<select id="chat-select" class="form-select me-2" style="width: auto;">
|
||||||
|
<option value="" disabled selected>Select a chat</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="p-auto flex-grow-1 bd-highlight">
|
||||||
|
<div class="d-flex flex-column">
|
||||||
|
<button id="new-chat" class="btn btn-dark mb-2" type="button">Reset</button>
|
||||||
|
<button id="save-chat" class="btn btn-secondary mb-2" type="button" data-bs-toggle="modal"
|
||||||
|
data-bs-target="#nameModal">Save
|
||||||
|
</button>
|
||||||
|
<button id="delete-chat" class="btn btn-danger" type="button">Delete</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container p-2 card" id="input-area">
|
||||||
|
<div class="input-group">
|
||||||
|
<textarea class="form-control" id="user-input" placeholder="Type your question here..."
|
||||||
|
oninput="autoGrow(this)"></textarea>
|
||||||
|
<button id="send-button" class="btn btn-primary">Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="nameModal" tabindex="-1" aria-labelledby="nameModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="nameModalLabel">Enter Your Name</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="text" class="form-control" id="userName" placeholder="Your Name">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="saveName">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="errorModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Unable to access Ollama server</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p id="errorText"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/v1/static/js/api.js"></script>
|
||||||
|
<script src="/v1/static/js/chat.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
125
static/index_ollama.html
Normal file
125
static/index_ollama.html
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Chat with Llama2</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
<link href="/v1/static/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-4bw+/aepP/YC94hEpVNVgiZdgIC5+VKNBQNGCHeKRQN+PtmoHDEXuppvnDJzQIu9"
|
||||||
|
crossorigin="anonymous">
|
||||||
|
<script src="/v1/static/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="/v1/static/js/marked.min.js"
|
||||||
|
integrity="sha384-dZulhREgb+hCgQMhZ2VG0l37VQj5pJBq2w0h7Jn3tdMn36aXNepF1+FMLBB4O649"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="/v1/static/js/purify.min.js" integrity="sha256-QigBQMy2be3IqJD2ezKJUJ5gycSmyYlRHj2VGBuITpU="
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
<script src="/v1/static/js/jquery-3.7.1.min.js"
|
||||||
|
integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<!-- -->
|
||||||
|
<link rel="stylesheet" href="/v1/static/css/chat.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body data-bs-theme="dark">
|
||||||
|
<div class="container">
|
||||||
|
<div id="scroll-wrapper">
|
||||||
|
<div id="chat-container" class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="chat-history"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="position-fixed w-100"
|
||||||
|
style="z-index: 9999; top: 0px; height: 200px; background: linear-gradient(180deg, black, transparent);">
|
||||||
|
|
||||||
|
<div class="position-fixed w-100"
|
||||||
|
style="z-index: 9999; top: 0px; height: 200px; background: linear-gradient(180deg, black, transparent);">
|
||||||
|
<div class="d-flex justify-content-between align-items-center m-0" style="padding: 16px;">
|
||||||
|
<h1>Chat with AI</h1>
|
||||||
|
<!-- Model dropdown -->
|
||||||
|
<div class="d-flex align-items-center m-1">
|
||||||
|
<div class="p-2 flex-grow-1 bd-highlight">
|
||||||
|
<label for="system-prompt" class="me-2" style="font-size: normal;">System Prompt</label>
|
||||||
|
<input id="system-prompt" class="form-control" type="text" placeholder="You are a helpful assistant"
|
||||||
|
style="width: auto;"></select>
|
||||||
|
<!-- <label for="host-address" class="me-2" style="font-size: normal;">Hostname</label>-->
|
||||||
|
<!-- <input id="host-address" class="form-control" type="text" placeholder="http://localhost:11434"-->
|
||||||
|
<!-- style="width: auto;"></select>-->
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- <div class="d-flex align-items-center m-2">-->
|
||||||
|
<!-- <div class="p-2 flex-grow-1 bd-highlight">-->
|
||||||
|
<!-- <label for="model-select" class="me-2" style="font-size: normal;">Model:</label>-->
|
||||||
|
<!-- <select class="form-select me-5" id="model-select" style="width: auto;"></select>-->
|
||||||
|
<!-- <label for="chat-select" class="me-2" style="font-size: normal;">History:</label>-->
|
||||||
|
<!-- <select id="chat-select" class="form-select me-2" style="width: auto;">-->
|
||||||
|
<!-- <option value="" disabled selected>Select a chat</option>-->
|
||||||
|
<!-- </select>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="p-auto flex-grow-1 bd-highlight">-->
|
||||||
|
<!-- <div class="d-flex flex-column">-->
|
||||||
|
<!-- <button id="new-chat" class="btn btn-dark mb-2" type="button">Reset</button>-->
|
||||||
|
<!-- <button id="save-chat" class="btn btn-secondary mb-2" type="button" data-bs-toggle="modal"-->
|
||||||
|
<!-- data-bs-target="#nameModal">Save-->
|
||||||
|
<!-- </button>-->
|
||||||
|
<!-- <button id="delete-chat" class="btn btn-danger" type="button">Delete</button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="container p-2 card" id="input-area">
|
||||||
|
<div class="input-group">
|
||||||
|
<textarea class="form-control" id="user-input" placeholder="Type your question here..."
|
||||||
|
oninput="autoGrow(this)"></textarea>
|
||||||
|
<button id="send-button" class="btn btn-primary">Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="nameModal" tabindex="-1" aria-labelledby="nameModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="nameModalLabel">Enter Your Name</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<input type="text" class="form-control" id="userName" placeholder="Your Name">
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="saveName">Save</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="errorModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Unable to access Ollama server</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p id="errorText"></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/v1/static/js/api.js"></script>
|
||||||
|
<script src="/v1/static/js/chat.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -10,15 +10,19 @@ import _ "embed"
|
|||||||
//go:embed *.html
|
//go:embed *.html
|
||||||
var pageList embed.FS
|
var pageList embed.FS
|
||||||
|
|
||||||
//go:embed img/*.jpeg
|
//go:embed img/*.*
|
||||||
var imgList embed.FS
|
var imgList embed.FS
|
||||||
|
|
||||||
//go:embed css/*.css
|
//go:embed css/*.css
|
||||||
var cssList embed.FS
|
var cssList embed.FS
|
||||||
|
|
||||||
|
//go:embed js/*.js
|
||||||
|
var jsList embed.FS
|
||||||
|
|
||||||
var pageMap = initPageMap()
|
var pageMap = initPageMap()
|
||||||
var imgMap = initImgMap
|
var imgMap = initImgMap()
|
||||||
var cssMap = initCssMap()
|
var cssMap = initCssMap()
|
||||||
|
var jsMap = initJsMap()
|
||||||
|
|
||||||
func initPageMap() map[string][]byte {
|
func initPageMap() map[string][]byte {
|
||||||
list, err := pageList.ReadDir(".")
|
list, err := pageList.ReadDir(".")
|
||||||
@ -37,23 +41,57 @@ func initPageMap() map[string][]byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func initImgMap() map[string][]byte {
|
func initImgMap() map[string][]byte {
|
||||||
var dataMap = make(map[string][]byte, 8)
|
list, err := imgList.ReadDir("img")
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(-1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
//login.html
|
var dataMap = make(map[string][]byte, len(list))
|
||||||
data, err := imgList.ReadFile("img/003.jpeg")
|
for _, file := range list {
|
||||||
|
//读取配置文件
|
||||||
|
data, err := imgList.ReadFile("img/" + file.Name())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
dataMap["003.jpeg"] = data
|
dataMap[file.Name()] = data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataMap
|
return dataMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func initCssMap() map[string][]byte {
|
func initCssMap() map[string][]byte {
|
||||||
var dataMap = make(map[string][]byte, 8)
|
list, err := cssList.ReadDir("css")
|
||||||
//login.html
|
if err != nil {
|
||||||
data, err := cssList.ReadFile("css/login.css")
|
os.Exit(-1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataMap = make(map[string][]byte, len(list))
|
||||||
|
for _, file := range list {
|
||||||
|
//读取配置文件
|
||||||
|
data, err := cssList.ReadFile("css/" + file.Name())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
dataMap["login.css"] = data
|
dataMap[file.Name()] = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dataMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func initJsMap() map[string][]byte {
|
||||||
|
list, err := jsList.ReadDir("js")
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(-1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var dataMap = make(map[string][]byte, len(list))
|
||||||
|
for _, file := range list {
|
||||||
|
//读取配置文件
|
||||||
|
data, err := jsList.ReadFile("js/" + file.Name())
|
||||||
|
if err == nil {
|
||||||
|
dataMap[file.Name()] = data
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dataMap
|
return dataMap
|
||||||
|
112
static/js/api.js
Normal file
112
static/js/api.js
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
var rebuildRules = undefined;
|
||||||
|
if (typeof chrome !== "undefined" && chrome.runtime && chrome.runtime.id) {
|
||||||
|
rebuildRules = async function (domain) {
|
||||||
|
const domains = [domain];
|
||||||
|
/** @type {chrome.declarativeNetRequest.Rule[]} */
|
||||||
|
const rules = [{
|
||||||
|
id: 1,
|
||||||
|
condition: {
|
||||||
|
requestDomains: domains
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
type: 'modifyHeaders',
|
||||||
|
requestHeaders: [{
|
||||||
|
header: 'origin',
|
||||||
|
operation: 'set',
|
||||||
|
value: `http://${domain}`,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
}];
|
||||||
|
await chrome.declarativeNetRequest.updateDynamicRules({
|
||||||
|
removeRuleIds: rules.map(r => r.id),
|
||||||
|
addRules: rules,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ollama_host = localStorage.getItem("host-address");
|
||||||
|
if (!ollama_host){
|
||||||
|
ollama_host = 'http://localhost:11434'
|
||||||
|
} else {
|
||||||
|
document.getElementById("host-address").value = ollama_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ollama_system_prompt = localStorage.getItem("system-prompt");
|
||||||
|
if (ollama_system_prompt){
|
||||||
|
document.getElementById("system-prompt").value = ollama_system_prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rebuildRules){
|
||||||
|
rebuildRules(ollama_host);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHostAddress(){
|
||||||
|
ollama_host = document.getElementById("host-address").value;
|
||||||
|
localStorage.setItem("host-address", ollama_host);
|
||||||
|
populateModels();
|
||||||
|
if (rebuildRules){
|
||||||
|
rebuildRules(ollama_host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSystemPrompt(){
|
||||||
|
const systemPrompt = document.getElementById("system-prompt").value;
|
||||||
|
localStorage.setItem("system-prompt", systemPrompt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async function getModels(){
|
||||||
|
const response = await fetch(`${ollama_host}/api/tags`);
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Function to send a POST request to the API
|
||||||
|
function postRequest(data, signal) {
|
||||||
|
// const URL = `${ollama_host}/api/generate`;
|
||||||
|
const URL = `https://openrouter.ai/api/v1/chat/completions`;
|
||||||
|
const body = JSON.stringify({model: "google/gemma-7b-it:free", messages: [{role: "user", content: data}]});
|
||||||
|
alert(body);
|
||||||
|
return fetch(URL, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer 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'
|
||||||
|
},
|
||||||
|
// body: JSON.stringify(data),
|
||||||
|
body: body,
|
||||||
|
signal: signal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to stream the response from the server
|
||||||
|
async function getResponse(response, callback) {
|
||||||
|
const reader = response.body.getReader();
|
||||||
|
let partialLine = '';
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Decode the received value and split by lines
|
||||||
|
const textChunk = new TextDecoder().decode(value);
|
||||||
|
const lines = (partialLine + textChunk).split('\n');
|
||||||
|
partialLine = lines.pop(); // The last line might be incomplete
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.trim() === '') continue;
|
||||||
|
const parsedResponse = JSON.parse(line);
|
||||||
|
callback(parsedResponse); // Process each response word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle any remaining line
|
||||||
|
if (partialLine.trim() !== '') {
|
||||||
|
const parsedResponse = JSON.parse(partialLine);
|
||||||
|
callback(parsedResponse);
|
||||||
|
}
|
||||||
|
}
|
7
static/js/bootstrap.bundle.min.js
vendored
Normal file
7
static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
337
static/js/chat.js
Normal file
337
static/js/chat.js
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
const faqString = `
|
||||||
|
**How can I expose the Ollama server?**
|
||||||
|
|
||||||
|
By default, Ollama allows cross origin requests from 127.0.0.1 and 0.0.0.0.
|
||||||
|
|
||||||
|
To support more origins, you can use the OLLAMA_ORIGINS environment variable:
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
OLLAMA_ORIGINS=${window.location.origin} ollama serve
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
Also see: https://github.com/jmorganca/ollama/blob/main/docs/faq.md
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const clipboardIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16">
|
||||||
|
<path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/>
|
||||||
|
<path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/>
|
||||||
|
</svg>`
|
||||||
|
const textBoxBaseHeight = 40; // This should match the default height set in CSS
|
||||||
|
|
||||||
|
// change settings of marked from default to remove deprecation warnings
|
||||||
|
// see conversation here: https://github.com/markedjs/marked/issues/2793
|
||||||
|
marked.use({
|
||||||
|
mangle: false,
|
||||||
|
headerIds: false
|
||||||
|
});
|
||||||
|
|
||||||
|
function autoFocusInput() {
|
||||||
|
const userInput = document.getElementById('user-input');
|
||||||
|
userInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
takes in model as a string
|
||||||
|
updates the query parameters of page url to include model name
|
||||||
|
*/
|
||||||
|
function updateModelInQueryString(model) {
|
||||||
|
// make sure browser supports features
|
||||||
|
if (window.history.replaceState && 'URLSearchParams' in window) {
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
searchParams.set("model", model);
|
||||||
|
// replace current url without reload
|
||||||
|
const newPathWithQuery = `${window.location.pathname}?${searchParams.toString()}`
|
||||||
|
window.history.replaceState(null, '', newPathWithQuery);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch available models and populate the dropdown
|
||||||
|
async function populateModels() {
|
||||||
|
document.getElementById('send-button').addEventListener('click', submitRequest);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await getModels();
|
||||||
|
|
||||||
|
const selectElement = document.getElementById('model-select');
|
||||||
|
|
||||||
|
// set up handler for selection
|
||||||
|
selectElement.onchange = (() => updateModelInQueryString(selectElement.value));
|
||||||
|
|
||||||
|
data.models.forEach((model) => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = model.name;
|
||||||
|
option.innerText = model.name;
|
||||||
|
selectElement.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
// select option present in url parameter if present
|
||||||
|
const queryParams = new URLSearchParams(window.location.search);
|
||||||
|
const requestedModel = queryParams.get('model');
|
||||||
|
// update the selection based on if requestedModel is a value in options
|
||||||
|
if ([...selectElement.options].map(o => o.value).includes(requestedModel)) {
|
||||||
|
selectElement.value = requestedModel;
|
||||||
|
}
|
||||||
|
// otherwise set to the first element if exists and update URL accordingly
|
||||||
|
else if (selectElement.options.length) {
|
||||||
|
selectElement.value = selectElement.options[0].value;
|
||||||
|
updateModelInQueryString(selectElement.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
document.getElementById('errorText').innerHTML =
|
||||||
|
DOMPurify.sanitize(marked.parse(
|
||||||
|
`Ollama-ui was unable to communitcate with Ollama due to the following error:\n\n`
|
||||||
|
+ `\`\`\`${error.message}\`\`\`\n\n---------------------\n`
|
||||||
|
+ faqString));
|
||||||
|
let modal = new bootstrap.Modal(document.getElementById('errorModal'));
|
||||||
|
modal.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// adjusts the padding at the bottom of scrollWrapper to be the height of the input box
|
||||||
|
function adjustPadding() {
|
||||||
|
const inputBoxHeight = document.getElementById('input-area').offsetHeight;
|
||||||
|
const scrollWrapper = document.getElementById('scroll-wrapper');
|
||||||
|
scrollWrapper.style.paddingBottom = `${inputBoxHeight + 15}px`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sets up padding resize whenever input box has its height changed
|
||||||
|
const autoResizePadding = new ResizeObserver(() => {
|
||||||
|
adjustPadding();
|
||||||
|
});
|
||||||
|
autoResizePadding.observe(document.getElementById('input-area'));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Function to get the selected model
|
||||||
|
function getSelectedModel() {
|
||||||
|
return document.getElementById('model-select').value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// variables to handle auto-scroll
|
||||||
|
// we only need one ResizeObserver and isAutoScrollOn variable globally
|
||||||
|
// no need to make a new one for every time submitRequest is called
|
||||||
|
const scrollWrapper = document.getElementById('scroll-wrapper');
|
||||||
|
let isAutoScrollOn = true;
|
||||||
|
// autoscroll when new line is added
|
||||||
|
const autoScroller = new ResizeObserver(() => {
|
||||||
|
if (isAutoScrollOn) {
|
||||||
|
scrollWrapper.scrollIntoView({behavior: "smooth", block: "end"});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// event listener for scrolling
|
||||||
|
let lastKnownScrollPosition = 0;
|
||||||
|
let ticking = false;
|
||||||
|
document.addEventListener("scroll", (event) => {
|
||||||
|
// if user has scrolled up and autoScroll is on we turn it off
|
||||||
|
if (!ticking && isAutoScrollOn && window.scrollY < lastKnownScrollPosition) {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
isAutoScrollOn = false;
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
// if user has scrolled nearly all the way down and autoScroll is disabled, re-enable
|
||||||
|
else if (!ticking && !isAutoScrollOn &&
|
||||||
|
window.scrollY > lastKnownScrollPosition && // make sure scroll direction is down
|
||||||
|
window.scrollY >= document.documentElement.scrollHeight - window.innerHeight - 30 // add 30px of space--no need to scroll all the way down, just most of the way
|
||||||
|
) {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
isAutoScrollOn = true;
|
||||||
|
ticking = false;
|
||||||
|
});
|
||||||
|
ticking = true;
|
||||||
|
}
|
||||||
|
lastKnownScrollPosition = window.scrollY;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Function to handle the user input and call the API functions
|
||||||
|
async function submitRequest() {
|
||||||
|
document.getElementById('chat-container').style.display = 'block';
|
||||||
|
|
||||||
|
const input = document.getElementById('user-input').value;
|
||||||
|
const selectedModel = getSelectedModel();
|
||||||
|
const context = document.getElementById('chat-history').context;
|
||||||
|
const systemPrompt = document.getElementById('system-prompt').value;
|
||||||
|
const data = { model: selectedModel, prompt: input, context: context, system: systemPrompt };
|
||||||
|
|
||||||
|
// Create user message element and append to chat history
|
||||||
|
let chatHistory = document.getElementById('chat-history');
|
||||||
|
let userMessageDiv = document.createElement('div');
|
||||||
|
userMessageDiv.className = 'mb-2 user-message';
|
||||||
|
userMessageDiv.innerText = input;
|
||||||
|
chatHistory.appendChild(userMessageDiv);
|
||||||
|
|
||||||
|
// Create response container
|
||||||
|
let responseDiv = document.createElement('div');
|
||||||
|
responseDiv.className = 'response-message mb-2 text-start';
|
||||||
|
responseDiv.style.minHeight = '3em'; // make sure div does not shrink if we cancel the request when no text has been generated yet
|
||||||
|
spinner = document.createElement('div');
|
||||||
|
spinner.className = 'spinner-border text-light';
|
||||||
|
spinner.setAttribute('role', 'status');
|
||||||
|
responseDiv.appendChild(spinner);
|
||||||
|
chatHistory.appendChild(responseDiv);
|
||||||
|
|
||||||
|
// create button to stop text generation
|
||||||
|
let interrupt = new AbortController();
|
||||||
|
let stopButton = document.createElement('button');
|
||||||
|
stopButton.className = 'btn btn-danger';
|
||||||
|
stopButton.innerHTML = 'Stop';
|
||||||
|
stopButton.onclick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
interrupt.abort('Stop button pressed');
|
||||||
|
}
|
||||||
|
// add button after sendButton
|
||||||
|
const sendButton = document.getElementById('send-button');
|
||||||
|
sendButton.insertAdjacentElement('beforebegin', stopButton);
|
||||||
|
|
||||||
|
// change autoScroller to keep track of our new responseDiv
|
||||||
|
autoScroller.observe(responseDiv);
|
||||||
|
|
||||||
|
postRequest(data, interrupt.signal)
|
||||||
|
.then(async response => {
|
||||||
|
await getResponse(response, parsedResponse => {
|
||||||
|
let word = parsedResponse.response;
|
||||||
|
if (parsedResponse.done) {
|
||||||
|
chatHistory.context = parsedResponse.context;
|
||||||
|
// Copy button
|
||||||
|
let copyButton = document.createElement('button');
|
||||||
|
copyButton.className = 'btn btn-secondary copy-button';
|
||||||
|
copyButton.innerHTML = clipboardIcon;
|
||||||
|
copyButton.onclick = () => {
|
||||||
|
navigator.clipboard.writeText(responseDiv.hidden_text).then(() => {
|
||||||
|
console.log('Text copied to clipboard');
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('Failed to copy text:', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
responseDiv.appendChild(copyButton);
|
||||||
|
}
|
||||||
|
// add word to response
|
||||||
|
if (word != undefined && word != "") {
|
||||||
|
if (responseDiv.hidden_text == undefined){
|
||||||
|
responseDiv.hidden_text = "";
|
||||||
|
}
|
||||||
|
responseDiv.hidden_text += word;
|
||||||
|
responseDiv.innerHTML = DOMPurify.sanitize(marked.parse(responseDiv.hidden_text)); // Append word to response container
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
stopButton.remove(); // Remove stop button from DOM now that all text has been generated
|
||||||
|
spinner.remove();
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
if (error !== 'Stop button pressed') {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
stopButton.remove();
|
||||||
|
spinner.remove();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear user input
|
||||||
|
const element = document.getElementById('user-input');
|
||||||
|
element.value = '';
|
||||||
|
$(element).css("height", textBoxBaseHeight + "px");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listener for Ctrl + Enter or CMD + Enter
|
||||||
|
document.getElementById('user-input').addEventListener('keydown', function (e) {
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||||
|
submitRequest();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
window.onload = () => {
|
||||||
|
updateChatList();
|
||||||
|
populateModels();
|
||||||
|
adjustPadding();
|
||||||
|
autoFocusInput();
|
||||||
|
|
||||||
|
document.getElementById("delete-chat").addEventListener("click", deleteChat);
|
||||||
|
document.getElementById("new-chat").addEventListener("click", startNewChat);
|
||||||
|
document.getElementById("saveName").addEventListener("click", saveChat);
|
||||||
|
document.getElementById("chat-select").addEventListener("change", loadSelectedChat);
|
||||||
|
document.getElementById("host-address").addEventListener("change", setHostAddress);
|
||||||
|
document.getElementById("system-prompt").addEventListener("change", setSystemPrompt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteChat() {
|
||||||
|
const selectedChat = document.getElementById("chat-select").value;
|
||||||
|
localStorage.removeItem(selectedChat);
|
||||||
|
updateChatList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to save chat with a unique name
|
||||||
|
function saveChat() {
|
||||||
|
const chatName = document.getElementById('userName').value;
|
||||||
|
|
||||||
|
// Close the modal
|
||||||
|
const bootstrapModal = bootstrap.Modal.getInstance(document.getElementById('nameModal'));
|
||||||
|
bootstrapModal.hide();
|
||||||
|
|
||||||
|
if (chatName === null || chatName.trim() === "") return;
|
||||||
|
const history = document.getElementById("chat-history").innerHTML;
|
||||||
|
const context = document.getElementById('chat-history').context;
|
||||||
|
const systemPrompt = document.getElementById('system-prompt').value;
|
||||||
|
const model = getSelectedModel();
|
||||||
|
localStorage.setItem(chatName, JSON.stringify({"history":history, "context":context, system: systemPrompt, "model": model}));
|
||||||
|
updateChatList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to load selected chat from dropdown
|
||||||
|
function loadSelectedChat() {
|
||||||
|
const selectedChat = document.getElementById("chat-select").value;
|
||||||
|
const obj = JSON.parse(localStorage.getItem(selectedChat));
|
||||||
|
document.getElementById("chat-history").innerHTML = obj.history;
|
||||||
|
document.getElementById("chat-history").context = obj.context;
|
||||||
|
document.getElementById("system-prompt").value = obj.system;
|
||||||
|
updateModelInQueryString(obj.model)
|
||||||
|
document.getElementById('chat-container').style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function startNewChat() {
|
||||||
|
document.getElementById("chat-history").innerHTML = null;
|
||||||
|
document.getElementById("chat-history").context = null;
|
||||||
|
document.getElementById('chat-container').style.display = 'none';
|
||||||
|
updateChatList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to update chat list dropdown
|
||||||
|
function updateChatList() {
|
||||||
|
const chatList = document.getElementById("chat-select");
|
||||||
|
chatList.innerHTML = '<option value="" disabled selected>Select a chat</option>';
|
||||||
|
for (let i = 0; i < localStorage.length; i++) {
|
||||||
|
const key = localStorage.key(i);
|
||||||
|
if (key === "host-address" || key == "system-prompt") continue;
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = key;
|
||||||
|
option.text = key;
|
||||||
|
chatList.add(option);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoGrow(element) {
|
||||||
|
const maxHeight = 200; // This should match the max-height set in CSS
|
||||||
|
|
||||||
|
// Count the number of lines in the textarea based on newline characters
|
||||||
|
const numberOfLines = $(element).val().split('\n').length;
|
||||||
|
|
||||||
|
// Temporarily reset the height to auto to get the actual scrollHeight
|
||||||
|
$(element).css("height", "auto");
|
||||||
|
let newHeight = element.scrollHeight;
|
||||||
|
|
||||||
|
// If content is one line, set the height to baseHeight
|
||||||
|
if (numberOfLines === 1) {
|
||||||
|
newHeight = textBoxBaseHeight;
|
||||||
|
} else if (newHeight > maxHeight) {
|
||||||
|
newHeight = maxHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(element).css("height", newHeight + "px");
|
||||||
|
}
|
2
static/js/jquery-3.7.1.min.js
vendored
Normal file
2
static/js/jquery-3.7.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
15
static/js/marked.min.js
vendored
Normal file
15
static/js/marked.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
static/js/purify.min.js
vendored
Normal file
3
static/js/purify.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -5,8 +5,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Favicon(c echo.Context) error {
|
||||||
|
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
||||||
|
return c.Blob(http.StatusOK, "image/png", imgMap["favicon.ico"])
|
||||||
|
}
|
||||||
|
|
||||||
func Login(c echo.Context) error {
|
func Login(c echo.Context) error {
|
||||||
c.Response().Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
||||||
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
||||||
|
|
||||||
return c.Blob(http.StatusOK, "text/html; charset=utf-8", pageMap["login.html"])
|
return c.Blob(http.StatusOK, "text/html; charset=utf-8", pageMap["login.html"])
|
||||||
@ -33,6 +37,13 @@ func Chat2(c echo.Context) error {
|
|||||||
return c.Blob(http.StatusOK, "text/html; charset=utf-8", pageMap["ai-chat2.html"])
|
return c.Blob(http.StatusOK, "text/html; charset=utf-8", pageMap["ai-chat2.html"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Chat3(c echo.Context) error {
|
||||||
|
c.Response().Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
||||||
|
|
||||||
|
return c.Blob(http.StatusOK, "text/html; charset=utf-8", pageMap["index_ollama.html"])
|
||||||
|
}
|
||||||
|
|
||||||
func InitStaticGroup(g *echo.Group) {
|
func InitStaticGroup(g *echo.Group) {
|
||||||
|
|
||||||
g.GET("login.html", func(c echo.Context) error {
|
g.GET("login.html", func(c echo.Context) error {
|
||||||
@ -42,19 +53,29 @@ func InitStaticGroup(g *echo.Group) {
|
|||||||
return c.Blob(http.StatusOK, "text/html; charset=utf-8", pageMap["login.html"])
|
return c.Blob(http.StatusOK, "text/html; charset=utf-8", pageMap["login.html"])
|
||||||
})
|
})
|
||||||
|
|
||||||
g.GET("css/login.css", func(c echo.Context) error {
|
g.GET("css/:name", func(c echo.Context) error {
|
||||||
|
cssName := c.Param("name")
|
||||||
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
||||||
data, _ := cssMap["login.css"]
|
data, _ := cssMap[cssName]
|
||||||
return c.Blob(http.StatusOK, "text/css; charset=utf-8", data)
|
return c.Blob(http.StatusOK, "text/css; charset=utf-8", data)
|
||||||
})
|
})
|
||||||
|
|
||||||
g.GET("img/003.jpeg", func(c echo.Context) error {
|
g.GET("img/:name", func(c echo.Context) error {
|
||||||
|
imgName := c.Param("name")
|
||||||
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
||||||
return c.Blob(http.StatusOK, "image/jpeg", imgMap()["003.jpeg"])
|
return c.Blob(http.StatusOK, "image/jpeg", imgMap[imgName])
|
||||||
})
|
})
|
||||||
|
|
||||||
g.GET("/aa", func(c echo.Context) error {
|
g.GET("js/:name", func(c echo.Context) error {
|
||||||
c.Response().Header().Set("Content-Type", "text/html; charset=utf-8")
|
jsName := c.Param("name")
|
||||||
return c.String(http.StatusOK, `<p>html代码</p>`)
|
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
||||||
|
return c.Blob(http.StatusOK, "text/javaScript", jsMap[jsName])
|
||||||
|
})
|
||||||
|
|
||||||
|
g.GET("page/:name", func(c echo.Context) error {
|
||||||
|
pageName := c.Param("name")
|
||||||
|
c.Response().Header().Set("Cache-Control", "max-age=3600")
|
||||||
|
data, _ := pageMap[pageName]
|
||||||
|
return c.Blob(http.StatusOK, "text/html; charset=utf-8", data)
|
||||||
})
|
})
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user