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