|
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; |
|
} |
|
|
|
|
|
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'); |
|
} |