|
let lastSaveTimestamp = 0; |
|
let controller; |
|
|
|
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 = parseInt(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; |
|
}); |
|
const result = encodedChunks.join(''); |
|
return result; |
|
} |
|
|
|
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) { |
|
console.debug('saveToUserStorage', force); |
|
const currentTime = Date.now(); |
|
if (!force && currentTime - lastSaveTimestamp < 5000) { |
|
return; |
|
} |
|
console.debug('セーブを実行します'); |
|
|
|
const geminiClientData = { |
|
|
|
novelContent1: document.getElementById('novelContent1').value, |
|
novelContent2: document.getElementById('novelContent2').value, |
|
generatePrompt: document.getElementById('generatePrompt').value, |
|
nextPrompt: document.getElementById('nextPrompt').value, |
|
savedTitle: document.getElementById('savedTitle').value, |
|
}; |
|
|
|
if (force) { |
|
|
|
geminiClientData.memo = document.getElementById('memo').value; |
|
geminiClientData.geminiApiKey = document.getElementById('geminiApiKey').value; |
|
geminiClientData.endpointSelect = document.getElementById('endpointSelect').value; |
|
geminiClientData.openaiEndpoint = document.getElementById('openaiEndpoint').value; |
|
geminiClientData.openaiHeaders = document.getElementById('openaiHeaders').value; |
|
geminiClientData.openaiJsonBody = document.getElementById('openaiJsonBody').value; |
|
geminiClientData.characterCount = document.getElementById('characterCount').value; |
|
geminiClientData.partialEncodeToggle = document.getElementById('partialEncodeToggle').checked; |
|
geminiClientData.encodeLength = document.getElementById('encodeLength').value; |
|
geminiClientData.streamToggle = document.getElementById('streamToggle').checked; |
|
} |
|
|
|
localStorage.setItem('geminiClient', JSON.stringify(geminiClientData)); |
|
lastSaveTimestamp = currentTime; |
|
} |
|
|
|
function loadFromUserStorage() { |
|
const savedData = localStorage.getItem('geminiClient'); |
|
if (savedData) { |
|
const geminiClientData = JSON.parse(savedData); |
|
Object.keys(geminiClientData).forEach(key => { |
|
const elem = document.getElementById(key); |
|
if (elem) { |
|
if (elem.type === 'checkbox') { |
|
elem.checked = geminiClientData[key]; |
|
} else { |
|
elem.value = geminiClientData[key]; |
|
} |
|
|
|
|
|
if (key === 'characterCount' || key === 'encodeLength' || key === 'contentWidth') { |
|
const inputElem = document.getElementById(`${key}Input`); |
|
if (inputElem) { |
|
inputElem.value = geminiClientData[key]; |
|
} |
|
} |
|
} else { |
|
console.debug(`要素が見つかりません: ${key}`); |
|
} |
|
}); |
|
} |
|
} |
|
|
|
function createPayload() { |
|
const novelContent1 = document.getElementById('novelContent1'); |
|
const text = novelContent1.value; |
|
const lines = text.split('\n').filter(x => x); |
|
|
|
|
|
let systemPrompt = `${partialEncodeURI(document.getElementById('generatePrompt').value)}`; |
|
let messages = [ |
|
{ |
|
"role": "user", |
|
"parts": [{ "text": "." }] |
|
}, |
|
{ |
|
"role": "model", |
|
"parts": [{ "text": partialEncodeURI(lines.join("\n")) }] |
|
}, |
|
{ |
|
"role": "user", |
|
"parts": [{ "text": `続きを書いて。${partialEncodeURI(document.getElementById('nextPrompt').value)} ${document.getElementById('characterCountInput').value}文字程度。${systemPrompt}` }] |
|
} |
|
]; |
|
|
|
return { |
|
method: 'POST', |
|
headers: {}, |
|
body: JSON.stringify({ |
|
contents: messages, |
|
"generationConfig": { |
|
"temperature": 1.0, |
|
"max_output_tokens": 4096 |
|
}, |
|
safetySettings: [ |
|
{ |
|
"category": "HARM_CATEGORY_HATE_SPEECH", |
|
"threshold": "BLOCK_NONE" |
|
}, |
|
{ |
|
"category": "HARM_CATEGORY_DANGEROUS_CONTENT", |
|
"threshold": "BLOCK_NONE" |
|
}, |
|
{ |
|
"category": "HARM_CATEGORY_HARASSMENT", |
|
"threshold": "BLOCK_NONE" |
|
}, |
|
{ |
|
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", |
|
"threshold": "BLOCK_NONE" |
|
} |
|
] |
|
}), |
|
mode: 'cors' |
|
}; |
|
} |
|
|
|
function debugPrompt() { |
|
console.log({ |
|
"gemini": JSON.parse(createPayload().body), |
|
"openai": JSON.parse(createOpenAIPayload().body) |
|
}); |
|
} |
|
|
|
function fetchStream(ENDPOINT, payload) { |
|
const novelContent2 = document.getElementById('novelContent2'); |
|
const requestButton = document.getElementById('requestButton'); |
|
controller = new AbortController(); |
|
const signal = controller.signal; |
|
|
|
fetch(ENDPOINT, { ...payload, signal }) |
|
.then(response => { |
|
if (!response.ok) { |
|
throw new Error('ネットワークの応答が正常ではありません'); |
|
} |
|
const reader = response.body.getReader(); |
|
const decoder = new TextDecoder(); |
|
let buffer = ''; |
|
|
|
function readStream() { |
|
reader.read().then(({ done, value }) => { |
|
if (done) { |
|
console.debug('ストリームが完了しました'); |
|
document.getElementById('stopButton').classList.add('d-none'); |
|
requestButton.disabled = false; |
|
return; |
|
} |
|
|
|
const chunk = decoder.decode(value, { stream: true }); |
|
buffer += chunk; |
|
console.debug('チャンクを受信しました:', chunk); |
|
|
|
|
|
let startIndex = 0; |
|
while (true) { |
|
const endIndex = buffer.indexOf('\n', startIndex); |
|
if (endIndex === -1) break; |
|
|
|
const line = buffer.slice(startIndex, endIndex).trim(); |
|
startIndex = endIndex + 1; |
|
|
|
if (line.startsWith('data: ')) { |
|
const jsonString = line.slice(5); |
|
if (jsonString === '[DONE]') { |
|
console.debug('Received [DONE] signal'); |
|
break; |
|
} |
|
try { |
|
const data = JSON.parse(jsonString); |
|
console.debug('解析されたJSON:', data); |
|
if (data.candidates && data.candidates[0].content && data.candidates[0].content.parts) { |
|
data.candidates[0].content.parts.forEach(part => { |
|
if (part.text) { |
|
console.debug('出力にテキストを追加:', part.text); |
|
novelContent2.value += part.text; |
|
novelContent2.scrollTop = novelContent2.scrollHeight; |
|
} |
|
}); |
|
} |
|
|
|
if (data.candidates && data.candidates[0]) { |
|
if (data.candidates[0].finishReason) { |
|
if (data.candidates[0].finishReason === 'STOP') { |
|
requestButton.classList.add('green-flash-bg'); |
|
setTimeout(() => { |
|
requestButton.classList.remove('green-flash-bg'); |
|
}, 2000); |
|
}else{ |
|
requestButton.classList.add('red-flash-bg'); |
|
setTimeout(() => { |
|
requestButton.classList.remove('red-flash-bg'); |
|
}, 2000); |
|
} |
|
} |
|
if (data.candidates[0].blockReason) { |
|
requestButton.classList.add('red-flash-bg'); |
|
setTimeout(() => { |
|
requestButton.classList.remove('red-flash-bg'); |
|
}, 2000); |
|
} |
|
} |
|
} catch (error) { |
|
console.error('JSONパースエラー:', error); |
|
} |
|
} |
|
} |
|
|
|
|
|
buffer = buffer.slice(startIndex); |
|
|
|
readStream(); |
|
}).catch(error => { |
|
if (error.name === 'AbortError') { |
|
console.log('フェッチがユーザーによって中止されました'); |
|
} else { |
|
console.error('ストリーム読み取りエラー:', error); |
|
} |
|
document.getElementById('stopButton').classList.add('d-none'); |
|
requestButton.disabled = false; |
|
}); |
|
} |
|
|
|
readStream(); |
|
}) |
|
.catch(error => { |
|
if (error.name === 'AbortError') { |
|
console.log('フェッチがユーザーよって中止されました'); |
|
} else { |
|
console.error('フェッチエラー:', error); |
|
} |
|
requestButton.disabled = false; |
|
}); |
|
} |
|
|
|
function fetchNonStream(ENDPOINT, payload) { |
|
const novelContent2 = document.getElementById('novelContent2'); |
|
fetch(ENDPOINT, payload) |
|
.then(response => response.json()) |
|
.then(data => { |
|
if (data && data.candidates && data.candidates[0].content && data.candidates[0].content.parts && data.candidates[0].content.parts[0].text) { |
|
novelContent2.value += data.candidates[0].content.parts[0].text; |
|
novelContent2.scrollTop = novelContent2.scrollHeight; |
|
} |
|
}) |
|
.catch(error => { |
|
console.error('エラー:', error); |
|
}) |
|
.finally(() => { |
|
document.getElementById('requestButton').disabled = false; |
|
}); |
|
} |
|
|
|
function createOpenAIPayload() { |
|
const novelContent1 = document.getElementById('novelContent1'); |
|
const text = novelContent1.value; |
|
const lines = text.split('\n').filter(x => x); |
|
let lastPart = lines.pop() || ''; |
|
|
|
let messages = [ |
|
{ |
|
"content": document.getElementById('generatePrompt').value || ".", |
|
"role": "system" |
|
}, |
|
{ |
|
"content": ".", |
|
"role": "user" |
|
}, |
|
{ |
|
"content": partialEncodeURI(lines.join("\n")) || ".", |
|
"role": "assistant" |
|
}, |
|
{ |
|
"content": `続きを${document.getElementById('characterCountInput').value}文字程度で書いてください。${partialEncodeURI(document.getElementById('nextPrompt').value)}`, |
|
"role": "user" |
|
}, |
|
{ |
|
"content": lastPart, |
|
"role": "assistant" |
|
} |
|
]; |
|
|
|
let jsonBody = JSON.parse(document.getElementById('openaiJsonBody').value); |
|
jsonBody.messages = messages; |
|
jsonBody.stream = document.getElementById('streamToggle').checked; |
|
|
|
return { |
|
method: 'POST', |
|
headers: JSON.parse(document.getElementById('openaiHeaders').value), |
|
body: JSON.stringify(jsonBody), |
|
mode: 'cors', |
|
credentials: 'same-origin' |
|
}; |
|
} |
|
|
|
function fetchOpenAIStream(ENDPOINT, payload) { |
|
const novelContent2 = document.getElementById('novelContent2'); |
|
const requestButton = document.getElementById('requestButton'); |
|
controller = new AbortController(); |
|
const signal = controller.signal; |
|
|
|
fetch(ENDPOINT, { |
|
...payload, |
|
signal, |
|
mode: 'cors', |
|
credentials: 'same-origin' |
|
}) |
|
.then(response => { |
|
if (!response.ok) { |
|
throw new Error('ネットワークの応答が正常ではありません'); |
|
} |
|
const reader = response.body.getReader(); |
|
const decoder = new TextDecoder(); |
|
let buffer = ''; |
|
|
|
function readStream() { |
|
reader.read().then(({ done, value }) => { |
|
if (done) { |
|
console.debug('ストリームが完了しました'); |
|
document.getElementById('stopButton').classList.add('d-none'); |
|
requestButton.disabled = false; |
|
return; |
|
} |
|
|
|
const chunk = decoder.decode(value, { stream: true }); |
|
buffer += chunk; |
|
|
|
const lines = buffer.split('\n'); |
|
buffer = lines.pop(); |
|
|
|
lines.forEach(line => { |
|
if (line.startsWith('data: ')) { |
|
const jsonString = line.slice(6); |
|
if (jsonString === '[DONE]') { |
|
console.debug('Received [DONE] signal'); |
|
return; |
|
} |
|
try { |
|
const data = JSON.parse(jsonString); |
|
if (data.choices && data.choices[0].delta && data.choices[0].delta.content) { |
|
novelContent2.value += data.choices[0].delta.content; |
|
novelContent2.scrollTop = novelContent2.scrollHeight; |
|
} |
|
} catch (error) { |
|
console.error('JSONパースエラー:', error); |
|
} |
|
} |
|
}); |
|
|
|
readStream(); |
|
}).catch(error => { |
|
if (error.name === 'AbortError') { |
|
console.log('フェッチがユーザーによって中止されました'); |
|
} else { |
|
console.error('ストリーム読み取りエラー:', error); |
|
} |
|
document.getElementById('stopButton').classList.add('d-none'); |
|
requestButton.disabled = false; |
|
}); |
|
} |
|
|
|
readStream(); |
|
}) |
|
.catch(error => { |
|
if (error.name === 'AbortError') { |
|
console.log('フェッチがユーザーよって中止されました'); |
|
} else { |
|
console.error('フェッチエラー:', error); |
|
} |
|
requestButton.disabled = false; |
|
}); |
|
} |
|
|
|
function fetchOpenAINonStream(ENDPOINT, payload) { |
|
const novelContent2 = document.getElementById('novelContent2'); |
|
fetch(ENDPOINT, { |
|
...payload, |
|
mode: 'cors', |
|
credentials: 'same-origin' |
|
}) |
|
.then(response => response.json()) |
|
.then(data => { |
|
if (data && data.choices && data.choices[0].message && data.choices[0].message.content) { |
|
novelContent2.value += data.choices[0].message.content; |
|
novelContent2.scrollTop = novelContent2.scrollHeight; |
|
} |
|
}) |
|
.catch(error => { |
|
console.error('エラー:', error); |
|
}) |
|
.finally(() => { |
|
document.getElementById('requestButton').disabled = false; |
|
}); |
|
} |
|
|
|
function Request() { |
|
const selectedEndpoint = document.getElementById('endpointSelect').value; |
|
let ENDPOINT; |
|
let payload; |
|
|
|
if (selectedEndpoint.startsWith('models/gemini')) { |
|
ENDPOINT = `https://generativelanguage.googleapis.com/v1beta/${selectedEndpoint}:generateContent?key=` + document.getElementById('geminiApiKey').value; |
|
payload = createPayload(); |
|
} else { |
|
ENDPOINT = document.getElementById('openaiEndpoint').value; |
|
payload = createOpenAIPayload(); |
|
} |
|
|
|
document.getElementById('requestButton').disabled = true; |
|
let stream = document.getElementById('streamToggle').checked; |
|
document.getElementById('novelContent2').value = ''; |
|
|
|
if (stream) { |
|
if (selectedEndpoint.startsWith('models/gemini')) { |
|
ENDPOINT = ENDPOINT.replace(':generateContent', ':streamGenerateContent') + '&alt=sse'; |
|
fetchStream(ENDPOINT, payload); |
|
} else { |
|
fetchOpenAIStream(ENDPOINT, payload); |
|
} |
|
document.getElementById('stopButton').classList.remove('d-none'); |
|
} else { |
|
if (selectedEndpoint.startsWith('models/gemini')) { |
|
fetchNonStream(ENDPOINT, payload); |
|
} else { |
|
fetchOpenAINonStream(ENDPOINT, payload); |
|
} |
|
} |
|
|
|
const outputAccordion = document.querySelector('#content2Collapse'); |
|
if (outputAccordion) { |
|
const bsCollapse = new bootstrap.Collapse(outputAccordion, { toggle: false }); |
|
bsCollapse.show(); |
|
} |
|
} |
|
|
|
function stopGeneration() { |
|
if (controller) { |
|
controller.abort(); |
|
controller = null; |
|
} |
|
document.getElementById('stopButton').classList.add('d-none'); |
|
document.getElementById('requestButton').disabled = false; |
|
} |
|
|
|
|
|
function handleKeyPress(event) { |
|
if (event.ctrlKey && event.key === 'Enter') { |
|
Request(); |
|
} |
|
} |
|
|
|
function syncInputs() { |
|
const inputs = document.querySelectorAll('input[type="range"], input[type="number"]'); |
|
inputs.forEach(input => { |
|
const baseId = input.id.replace('Input', ''); |
|
const pairedInput = document.getElementById(baseId + (input.type === 'range' ? 'Input' : '')); |
|
|
|
if (pairedInput) { |
|
input.addEventListener('input', function () { |
|
pairedInput.value = this.value; |
|
}); |
|
} |
|
}); |
|
} |
|
|
|
function openNextAccordion() { |
|
const accordions = document.querySelectorAll('#mainAccordion .accordion-item'); |
|
let currentIndex = -1; |
|
|
|
|
|
for (let i = 0; i < accordions.length; i++) { |
|
if (!accordions[i].querySelector('.accordion-button').classList.contains('collapsed')) { |
|
currentIndex = i; |
|
break; |
|
} |
|
} |
|
|
|
|
|
if (currentIndex < accordions.length - 1) { |
|
new bootstrap.Collapse(accordions[currentIndex].querySelector('.accordion-collapse')).hide(); |
|
new bootstrap.Collapse(accordions[currentIndex + 1].querySelector('.accordion-collapse')).show(); |
|
} else { |
|
|
|
const nextButton = document.getElementById('nextAccordion'); |
|
nextButton.classList.add('red-flash-bg'); |
|
setTimeout(() => { |
|
nextButton.classList.remove('red-flash-bg'); |
|
}, 2000); |
|
} |
|
} |
|
|
|
function openPreviousAccordion() { |
|
const accordions = document.querySelectorAll('#mainAccordion .accordion-item'); |
|
let currentIndex = -1; |
|
|
|
|
|
for (let i = 0; i < accordions.length; i++) { |
|
if (!accordions[i].querySelector('.accordion-button').classList.contains('collapsed')) { |
|
currentIndex = i; |
|
break; |
|
} |
|
} |
|
|
|
|
|
if (currentIndex > 0) { |
|
new bootstrap.Collapse(accordions[currentIndex].querySelector('.accordion-collapse')).hide(); |
|
new bootstrap.Collapse(accordions[currentIndex - 1].querySelector('.accordion-collapse')).show(); |
|
} else { |
|
|
|
const prevButton = document.getElementById('prevAccordion'); |
|
prevButton.classList.add('red-flash-bg'); |
|
setTimeout(() => { |
|
prevButton.classList.remove('red-flash-bg'); |
|
}, 2000); |
|
} |
|
} |
|
|
|
function moveToInput() { |
|
const content1 = document.getElementById('novelContent1'); |
|
const content2 = document.getElementById('novelContent2'); |
|
|
|
let content1Lines = content1.value.trim().split('\n'); |
|
let content2Lines = content2.value.trim().split('\n'); |
|
|
|
|
|
if (content1Lines[content1Lines.length - 1] === content2Lines[0]) { |
|
content2Lines.shift(); |
|
} else { |
|
|
|
const lastLine = content1Lines[content1Lines.length - 1]; |
|
const firstLine = content2Lines[0]; |
|
const overlapIndex = firstLine.indexOf(lastLine); |
|
if (overlapIndex !== -1) { |
|
content2Lines[0] = firstLine.slice(overlapIndex + lastLine.length).trim(); |
|
if (content2Lines[0] === '') { |
|
content2Lines.shift(); |
|
} |
|
} |
|
} |
|
|
|
|
|
content1.value = content1Lines.join('\n') + '\n' + content2Lines.join('\n'); |
|
|
|
|
|
content2.value = ''; |
|
|
|
|
|
const content1Collapse = new bootstrap.Collapse(document.getElementById('content1Collapse'), { |
|
show: true |
|
}); |
|
} |
|
|
|
function updateNavbarBrand() { |
|
const endpointSelect = document.getElementById('endpointSelect'); |
|
const navbarBrand = document.querySelector('.navbar-brand'); |
|
const googleIcon = navbarBrand.querySelector('.fa-google'); |
|
const robotIcon = navbarBrand.querySelector('.fa-robot'); |
|
|
|
if (endpointSelect.value.startsWith('models/gemini')) { |
|
navbarBrand.style.color = '#4285F4'; |
|
googleIcon.classList.remove('d-none'); |
|
robotIcon.classList.add('d-none'); |
|
} else { |
|
navbarBrand.style.color = '#00FF00'; |
|
googleIcon.classList.add('d-none'); |
|
robotIcon.classList.remove('d-none'); |
|
} |
|
} |
|
|
|
document.addEventListener('DOMContentLoaded', function () { |
|
|
|
loadFromUserStorage(); |
|
|
|
|
|
['novelContent1', 'novelContent2', 'generatePrompt', 'nextPrompt', 'savedTitle'].forEach(id => { |
|
document.getElementById(id).addEventListener('input', () => { |
|
saveToUserStorage(false); |
|
}); |
|
}); |
|
|
|
|
|
['memo', 'geminiApiKey', 'endpointSelect', 'openaiEndpoint', 'openaiHeaders', 'openaiJsonBody', 'characterCount', 'encodeLength'].forEach(id => { |
|
document.getElementById(id).addEventListener('input', () => { |
|
saveToUserStorage(true); |
|
}); |
|
}); |
|
|
|
['partialEncodeToggle', 'streamToggle'].forEach(id => { |
|
document.getElementById(id).addEventListener('change', () => { |
|
saveToUserStorage(true); |
|
}); |
|
}); |
|
|
|
document.getElementById('novelContent1').addEventListener('keydown', handleKeyPress); |
|
|
|
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"); |
|
} |
|
}); |
|
}); |
|
}); |
|
|
|
syncInputs(); |
|
|
|
|
|
setInterval(() => { |
|
saveToUserStorage(); |
|
}, 60000); |
|
|
|
|
|
const basicSettingsAccordion = document.querySelector('#promptsCollapse'); |
|
if (basicSettingsAccordion) { |
|
new bootstrap.Collapse(basicSettingsAccordion).show(); |
|
} |
|
|
|
|
|
document.getElementById('prevAccordion').addEventListener('click', openPreviousAccordion); |
|
document.getElementById('nextAccordion').addEventListener('click', openNextAccordion); |
|
|
|
|
|
document.getElementById('endpointSelect').addEventListener('change', updateNavbarBrand); |
|
|
|
|
|
updateNavbarBrand(); |
|
}); |