LLMClient / script.js
SenY's picture
Upload 2 files
d131a6f verified
raw
history blame
No virus
10.9 kB
let lastSaveTimestamp = 0;
function formatText(){
const textOrg = document.getElementById('novelContent1').value;
let text = textOrg.replace(/[」。)]/g, '$&\n');
while (text.includes('\n\n')) {
text = text.replace(/\n\n/g, '\n');
}
text = text.replace(/「([^」\n]*)\n([^」\n]*)」/g, '「$1$2」');
text = text.replace(/(([^)\n]*)\n([^)\n]*))/g, '($1$2)');
while (text.search(/「[^「\n]*。\n/) >= 0) {
text = text.replace(/「([^「\n]*。)\n/, '「$1');
}
text = text.replace(/\n/g, "\n\n");
text = text.replace(/\n#/g, "\n\n#");
document.getElementById('novelContent1').value = text;
}
function unmalform(text) {
let result = null;
while (!result && text) {
try {
result = decodeURI(text);
} catch (error) {
text = text.slice(0, -1);
}
}
return result || '';
}
function partialEncodeURI(text) {
if (!document.getElementById("partialEncodeToggle").checked) {
return text;
}
let length = document.getElementById("encodeLength").value;
const chunks = [];
for (let i = 0; i < text.length; i += 1) {
chunks.push(text.slice(i, i + 1));
}
const encodedChunks = chunks.map((chunk, index) => {
if (index % length === 0) {
return encodeURI(chunk);
}
return chunk;
});
return encodedChunks.join('');
}
function saveToJson() {
const novelContent1 = document.getElementById('novelContent1').value;
const novelContent2 = document.getElementById('novelContent2').value;
const generatePrompt = document.getElementById('generatePrompt').value;
const nextPrompt = document.getElementById('nextPrompt').value;
const savedTitle = document.getElementById('savedTitle').value;
const jsonData = JSON.stringify({
novelContent1: novelContent1,
novelContent2: novelContent2,
generatePrompt: generatePrompt,
nextPrompt: nextPrompt,
savedTitle: savedTitle
});
const blob = new Blob([jsonData], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'novel_data.json';
if (savedTitle) {
a.download = savedTitle + '.json';
}
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
function loadFromJson() {
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json';
fileInput.style.display = 'none';
document.body.appendChild(fileInput);
fileInput.addEventListener('change', function (event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function (e) {
try {
const jsonData = JSON.parse(e.target.result);
if (jsonData.novelContent1) {
document.getElementById('novelContent1').value = jsonData.novelContent1;
}
if (jsonData.novelContent2) {
document.getElementById('novelContent2').value = jsonData.novelContent2;
}
if (jsonData.generatePrompt) {
document.getElementById('generatePrompt').value = jsonData.generatePrompt;
}
if (jsonData.nextPrompt) {
document.getElementById('nextPrompt').value = jsonData.nextPrompt;
}
if (jsonData.savedTitle) {
document.getElementById('savedTitle').value = jsonData.savedTitle;
}
alert('JSONファイルを正常に読み込みました。');
} catch (error) {
alert('無効なJSONファイルです。');
}
};
reader.readAsText(file);
}
});
fileInput.click();
}
function saveToUserStorage(force = false) {
const currentTime = Date.now();
if (!force && currentTime - lastSaveTimestamp < 5000) {
return;
}
const content = document.getElementById('novelContent1').value;
const prompt = document.getElementById('generatePrompt').value;
const nextPrompt = document.getElementById('nextPrompt').value;
const savedTitle = document.getElementById('savedTitle').value;
localStorage.setItem('savedNovelContent', content);
localStorage.setItem('savedGeneratePrompt', prompt);
localStorage.setItem('savedNextPrompt', nextPrompt);
['endpoint', 'headers', 'jsonBody'].forEach(id => {
localStorage.setItem(`saved${id}`, document.getElementById(id).value);
});
if (savedTitle) {
localStorage.setItem('savedTitle', savedTitle);
}
lastSaveTimestamp = currentTime;
}
// 60秒ごとに自動保存を実行
setInterval(() => {
saveToUserStorage();
}, 60000);
const savedContent = localStorage.getItem('savedNovelContent');
const savedPrompt = localStorage.getItem('savedGeneratePrompt');
const savedNextPrompt = localStorage.getItem('savedNextPrompt');
const savedTitle = localStorage.getItem('savedTitle');
if (savedContent) {
document.getElementById('novelContent1').value = savedContent;
}
if (savedPrompt) {
document.getElementById('generatePrompt').value = savedPrompt;
}
if (savedNextPrompt) {
document.getElementById('nextPrompt').value = savedNextPrompt;
}
if (savedTitle) {
document.getElementById('savedTitle').value = savedTitle;
}
['endpoint', 'headers', 'jsonBody'].forEach(id => {
document.getElementById(id).value = localStorage.getItem(`saved${id}`);
});
['novelContent1', 'generatePrompt', 'nextPrompt'].forEach(id => {
document.getElementById(id).addEventListener('input', saveToUserStorage);
});
['endpoint', 'headers', 'jsonBody'].forEach(id => {
document.getElementById(id).addEventListener('input', () => {
saveToUserStorage(true);
});
});
document.querySelectorAll('[data-modal-text]').forEach(element => {
element.addEventListener('click', function() {
document.querySelectorAll(".modal-text").forEach(el => {
el.classList.add("d-none");
if(el.classList.contains(this.getAttribute('data-modal-text'))) {
el.classList.remove("d-none");
}
});
});
});
let controller;
function Request() {
let ENDPOINT;
let HEADERS;
let jsonBody;
try {
ENDPOINT = document.getElementById("endpoint").value;
HEADERS = JSON.parse(document.getElementById("headers").value);
jsonBody = JSON.parse(document.getElementById("jsonBody").value);
if (!ENDPOINT) {
throw new Error("エンドポイントが設定されていません。");
}
} catch (e) {
console.error(e);
document.querySelector(".modal-header").classList.add("bg-danger");
document.querySelector('[data-modal-text="modal-text-1"]').click();
return;
}
document.querySelector(".modal-header").classList.remove("bg-danger");
const novelContent1 = document.getElementById('novelContent1');
const novelContent2 = document.getElementById('novelContent2');
const text = novelContent1.value;
const lines = text.split('\n').filter(x => x);
let lastPart = lines.pop() || '';
let removedPart;
let messages = [
{
"content": document.getElementById('generatePrompt').value || ".",
"role": "system"
},
{
"content": ".",
"role": "user"
},
{
"content": partialEncodeURI(lines.join("\n")) || ".",
"role": "assistant"
}
];
messages = messages.concat([
{
"content": `続きを${document.getElementById('characterCountInput').value}文字程度で書いてください。${partialEncodeURI(nextPrompt.value)}`,
"role": "user"
},
{
"content": lastPart,
"role": "assistant"
}
]);
document.getElementById('requestButton').disabled = true;
let stream = document.getElementById('streamToggle').checked;
jsonBody.messages = messages;
jsonBody.stream = stream;
let payload = {
method: 'POST',
headers: HEADERS,
body: JSON.stringify(jsonBody)
}
if (stream === true) {
controller = new AbortController();
payload.signal = controller.signal;
}
console.debug(messages, JSON.stringify(messages).length);
fetch(ENDPOINT, payload)
.then(response => {
if (stream === true) {
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
function readStream() {
reader.read().then(({ done, value }) => {
if (done) {
document.getElementById('stopButton').classList.add('d-none');
return;
}
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop();
lines.forEach(line => {
if (line.startsWith('data: ')) {
const data = JSON.parse(line.slice(6));
if (data.choices && data.choices[0].delta.content) {
novelContent2.value += data.choices[0].delta.content;
novelContent2.scrollTop = novelContent2.scrollHeight;
}
}
});
readStream();
});
}
readStream();
} else {
return response.json();
}
})
.then(data => {
if (data) {
novelContent2.value += data.choices[0].message.content;
}
})
.catch(error => {
if (error.name === 'AbortError') {
console.debug('生成が中止されました');
} else {
console.error('エラー:', error);
}
})
.finally(() => {
document.getElementById('requestButton').disabled = false;
});
if (stream === true) {
document.getElementById('stopButton').classList.remove('d-none');
}
novelContent2.value = '';
}
function stopGeneration() {
if (controller) {
controller.abort();
controller = null;
}
document.getElementById('stopButton').classList.add('d-none');
}