Spaces:
Running
Running
Upload 6 files
Browse files- control.js +22 -0
- history.js +120 -0
- index.html +178 -409
- prompt.js +156 -0
- storage.js +30 -0
- translation.js +184 -0
control.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
updateHistoryList();
|
| 2 |
+
loadFromUserStorage();
|
| 3 |
+
setInterval(() => {
|
| 4 |
+
saveToUserStorage();
|
| 5 |
+
}, 60000);
|
| 6 |
+
// 入力フォームの内容を保存する
|
| 7 |
+
document.querySelectorAll('input, textarea').forEach(input => {
|
| 8 |
+
input.addEventListener('input', saveToUserStorage);
|
| 9 |
+
});
|
| 10 |
+
// Ctrl+Enterでプロンプト生成を実行する
|
| 11 |
+
document.addEventListener('keydown', function (event) {
|
| 12 |
+
if (event.ctrlKey && event.key === 'Enter') {
|
| 13 |
+
event.preventDefault(); // デフォルトの動作を防ぐ
|
| 14 |
+
generatePrompt(); // プロンプト生成関数を呼び出す
|
| 15 |
+
}
|
| 16 |
+
});
|
| 17 |
+
|
| 18 |
+
// サイドバーの切り替え機能を追加
|
| 19 |
+
document.getElementById('sidebarToggle').addEventListener('click', function () {
|
| 20 |
+
document.getElementById('sidebar').classList.toggle('active');
|
| 21 |
+
document.getElementById('content').classList.toggle('active');
|
| 22 |
+
});
|
history.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function saveToHistory() {
|
| 2 |
+
const historyItem = {
|
| 3 |
+
query: document.getElementById('query').value,
|
| 4 |
+
promptEn: document.getElementById('promptEn').value,
|
| 5 |
+
promptMyLanguage: document.getElementById('promptMyLanguage').value,
|
| 6 |
+
danbooruTags: document.getElementById('danbooruTags').value,
|
| 7 |
+
timestamp: new Date().toISOString()
|
| 8 |
+
};
|
| 9 |
+
|
| 10 |
+
let history = JSON.parse(localStorage.getItem('gemini_prompt_history') || '[]');
|
| 11 |
+
history.unshift(historyItem);
|
| 12 |
+
history = history.slice(0, 10); // 最新の10件のみを保持
|
| 13 |
+
localStorage.setItem('gemini_prompt_history', JSON.stringify(history));
|
| 14 |
+
|
| 15 |
+
updateHistoryList();
|
| 16 |
+
}
|
| 17 |
+
function updateHistoryList() {
|
| 18 |
+
const historyList = document.getElementById('historyList');
|
| 19 |
+
const noHistoryMessage = document.getElementById('noHistoryMessage');
|
| 20 |
+
historyList.innerHTML = '';
|
| 21 |
+
|
| 22 |
+
const history = JSON.parse(localStorage.getItem('gemini_prompt_history') || '[]');
|
| 23 |
+
|
| 24 |
+
if (history.length === 0) {
|
| 25 |
+
noHistoryMessage.classList.remove('d-none');
|
| 26 |
+
historyList.classList.add('d-none');
|
| 27 |
+
} else {
|
| 28 |
+
noHistoryMessage.classList.add('d-none');
|
| 29 |
+
historyList.classList.remove('d-none');
|
| 30 |
+
|
| 31 |
+
history.forEach((item, index) => {
|
| 32 |
+
const li = document.createElement('li');
|
| 33 |
+
li.className = 'list-group-item list-group-item-action d-flex justify-content-between align-items-start';
|
| 34 |
+
|
| 35 |
+
const contentDiv = document.createElement('div');
|
| 36 |
+
contentDiv.className = 'ms-2 me-auto';
|
| 37 |
+
contentDiv.style.width = 'calc(100% - 40px)';
|
| 38 |
+
contentDiv.style.cursor = 'pointer';
|
| 39 |
+
contentDiv.onclick = () => loadHistoryItem(index);
|
| 40 |
+
|
| 41 |
+
const queryDiv = document.createElement('div');
|
| 42 |
+
queryDiv.className = 'text-truncate';
|
| 43 |
+
queryDiv.textContent = item.query;
|
| 44 |
+
|
| 45 |
+
const dateDiv = document.createElement('div');
|
| 46 |
+
dateDiv.className = 'small text-muted';
|
| 47 |
+
dateDiv.textContent = new Date(item.timestamp).toLocaleString();
|
| 48 |
+
|
| 49 |
+
contentDiv.appendChild(queryDiv);
|
| 50 |
+
contentDiv.appendChild(dateDiv);
|
| 51 |
+
|
| 52 |
+
const deleteButton = document.createElement('button');
|
| 53 |
+
deleteButton.className = 'btn btn-danger btn-sm';
|
| 54 |
+
deleteButton.innerHTML = '<i class="fas fa-trash"></i>';
|
| 55 |
+
deleteButton.onclick = (e) => {
|
| 56 |
+
e.stopPropagation();
|
| 57 |
+
deleteHistoryItem(index);
|
| 58 |
+
};
|
| 59 |
+
|
| 60 |
+
li.appendChild(contentDiv);
|
| 61 |
+
li.appendChild(deleteButton);
|
| 62 |
+
historyList.appendChild(li);
|
| 63 |
+
});
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
function deleteHistoryItem(index) {
|
| 67 |
+
if (confirm('この履歴項目を削除してもよろしいですか?')) {
|
| 68 |
+
let history = JSON.parse(localStorage.getItem('gemini_prompt_history') || '[]');
|
| 69 |
+
history.splice(index, 1);
|
| 70 |
+
localStorage.setItem('gemini_prompt_history', JSON.stringify(history));
|
| 71 |
+
updateHistoryList();
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
function loadHistoryItem(index) {
|
| 75 |
+
const history = JSON.parse(localStorage.getItem('gemini_prompt_history') || '[]');
|
| 76 |
+
const item = history[index];
|
| 77 |
+
|
| 78 |
+
document.getElementById('query').value = item.query;
|
| 79 |
+
document.getElementById('promptEn').value = item.promptEn;
|
| 80 |
+
document.getElementById('promptMyLanguage').value = item.promptMyLanguage;
|
| 81 |
+
document.getElementById('danbooruTags').value = item.danbooruTags;
|
| 82 |
+
|
| 83 |
+
saveToUserStorage(true);
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
function clearHistory() {
|
| 87 |
+
if (confirm('本当に履歴をすべて削除してもよろしいですか?')) {
|
| 88 |
+
localStorage.removeItem('gemini_prompt_history');
|
| 89 |
+
updateHistoryList();
|
| 90 |
+
}
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
function createHistoryItem(item, index) {
|
| 94 |
+
const li = document.createElement('li');
|
| 95 |
+
li.className = 'list-group-item d-flex justify-content-between align-items-center';
|
| 96 |
+
li.textContent = item.query;
|
| 97 |
+
|
| 98 |
+
const buttonsContainer = document.createElement('div');
|
| 99 |
+
|
| 100 |
+
const useButton = document.createElement('button');
|
| 101 |
+
useButton.className = 'btn btn-sm btn-primary me-2';
|
| 102 |
+
useButton.innerHTML = '<i class="fas fa-redo"></i>';
|
| 103 |
+
useButton.onclick = () => useHistoryItem(index);
|
| 104 |
+
|
| 105 |
+
const deleteButton = document.createElement('button');
|
| 106 |
+
deleteButton.className = 'btn btn-sm btn-danger';
|
| 107 |
+
deleteButton.innerHTML = '<i class="fas fa-trash"></i>';
|
| 108 |
+
deleteButton.onclick = () => {
|
| 109 |
+
// 個別の履歴項目削除時にも確認ポップアップを表示
|
| 110 |
+
if (confirm('この履歴項目を削除してもよろしいですか?')) {
|
| 111 |
+
deleteHistoryItem(index);
|
| 112 |
+
}
|
| 113 |
+
};
|
| 114 |
+
|
| 115 |
+
buttonsContainer.appendChild(useButton);
|
| 116 |
+
buttonsContainer.appendChild(deleteButton);
|
| 117 |
+
li.appendChild(buttonsContainer);
|
| 118 |
+
|
| 119 |
+
return li;
|
| 120 |
+
}
|
index.html
CHANGED
|
@@ -8,16 +8,6 @@
|
|
| 8 |
<link href="https://unpkg.com/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
| 9 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
|
| 10 |
crossorigin="anonymous">
|
| 11 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/json5/2.2.3/index.min.js"
|
| 12 |
-
integrity="sha512-44jdhc+R2TFfzBflS3/dGNEABiNUxBkkrqwO7GWTvGsj3HkQNr3GESvI9PUvAxmqxSnTosR0Ij9y3+o+6J1hig=="
|
| 13 |
-
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
| 14 |
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/i18next/23.14.0/i18next.min.js"
|
| 15 |
-
integrity="sha512-8ANNUVMWPf6aWGXZqDhS4OXJWBCRxfjlW7lKfupuiG1FZah0ST6LiI2qnEb1L5mp05v/+0hn3s2FO4EwIbIgfA=="
|
| 16 |
-
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
| 17 |
-
<script
|
| 18 |
-
src="https://cdnjs.cloudflare.com/ajax/libs/i18next-browser-languagedetector/8.0.0/i18nextBrowserLanguageDetector.min.js"
|
| 19 |
-
integrity="sha512-8/RTkAM23B3lQzi6fmPs+Yf9qhIHzrzRpeSZsBsQ8OEmo95mbVp+68dB647VDCuyQIBbF+OIbS9b30aTWUkoog=="
|
| 20 |
-
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
| 21 |
<style>
|
| 22 |
#query {
|
| 23 |
min-height: 40vh;
|
|
@@ -27,12 +17,155 @@
|
|
| 27 |
#promptMyLanguage {
|
| 28 |
min-height: 20vh;
|
| 29 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
</style>
|
| 31 |
</head>
|
| 32 |
|
| 33 |
<body data-bs-theme="dark">
|
| 34 |
-
<div
|
| 35 |
-
<div class="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
<div class="row">
|
| 37 |
<div id="inputQuery" class="col-md-6 mb-4">
|
| 38 |
<div class="card">
|
|
@@ -73,421 +206,57 @@
|
|
| 73 |
<textarea class="form-control" id="promptMyLanguage" disabled
|
| 74 |
placeholder="日本訳がここに表示されます"></textarea>
|
| 75 |
</div>
|
| 76 |
-
|
| 77 |
-
</div>
|
| 78 |
-
</div>
|
| 79 |
-
<div class="col-12 mb-4">
|
| 80 |
-
<div class="card">
|
| 81 |
-
<div class="card-header bg-primary text-white">
|
| 82 |
-
<h5 class="mb-0" id="settingsTitle">設定</h5>
|
| 83 |
-
</div>
|
| 84 |
-
<div class="card-body">
|
| 85 |
-
<div class="form-group mb-3">
|
| 86 |
<label for="danbooruTags" class="form-label" id="danbooruTagsLabel">
|
| 87 |
danbooru tags
|
| 88 |
</label>
|
| 89 |
<input type="text" class="form-control" id="danbooruTags" placeholder="danbooru tags"
|
| 90 |
readonly>
|
| 91 |
</div>
|
| 92 |
-
<div class="form-group mb-3">
|
| 93 |
-
<label for="apiKey" class="form-label" id="apiKeyLabel">
|
| 94 |
-
<a href="https://aistudio.google.com/app/apikey?hl=ja" target="_blank">APIキー</a>
|
| 95 |
-
</label>
|
| 96 |
-
<input type="text" class="form-control" id="apiKey" placeholder="APIキーを入力してください">
|
| 97 |
-
</div>
|
| 98 |
-
<div class="form-group">
|
| 99 |
-
<label for="characterCount" class="form-label" id="characterCountLabel">文字数</label>
|
| 100 |
-
<input type="number" value="320" class="form-control" id="characterCount"
|
| 101 |
-
placeholder="生成するプロンプトの文字数を入力してください">
|
| 102 |
-
</div>
|
| 103 |
-
<div class="form-group">
|
| 104 |
-
<label for="languageSelect" class="form-label" id="languageSelectLabel">Language</label>
|
| 105 |
-
<select class="form-select" id="languageSelect">
|
| 106 |
-
<option value="ja">日本語</option>
|
| 107 |
-
<option value="en">English</option>
|
| 108 |
-
<option value="zh">中文</option>
|
| 109 |
-
<option value="ko">한국어</option>
|
| 110 |
-
<option value="fr">Français</option>
|
| 111 |
-
<option value="es">Español</option>
|
| 112 |
-
<option value="de">Deutsch</option>
|
| 113 |
-
<option value="it">Italiano</option>
|
| 114 |
-
</select>
|
| 115 |
-
</div>
|
| 116 |
</div>
|
| 117 |
</div>
|
| 118 |
</div>
|
| 119 |
</div>
|
| 120 |
</div>
|
| 121 |
</div>
|
| 122 |
-
<script>
|
| 123 |
-
let language;
|
| 124 |
-
const translations = {
|
| 125 |
-
ja: {
|
| 126 |
-
inputQueryTitle: "入力クエリ",
|
| 127 |
-
generateButtonText: "プロンプト生成",
|
| 128 |
-
splitStrings: "分割送信",
|
| 129 |
-
outputPromptTitle: "生成されたプロンプト",
|
| 130 |
-
settingsTitle: "設定",
|
| 131 |
-
apiKeyLabel: "APIキー",
|
| 132 |
-
characterCountLabel: "文字数",
|
| 133 |
-
languageSelectLabel: "言語",
|
| 134 |
-
promptEnPlaceholder: "英語のプロンプトがここに表示されます",
|
| 135 |
-
promptMyLanguagePlaceholder: "日本訳がここに表示されます",
|
| 136 |
-
apiKeyPlaceholder: "APIキーを入力してください",
|
| 137 |
-
characterCountPlaceholder: "生成するプロンプトの文字数を入力してください"
|
| 138 |
-
},
|
| 139 |
-
en: {
|
| 140 |
-
inputQueryTitle: "Input Query",
|
| 141 |
-
generateButtonText: "Generate Prompt",
|
| 142 |
-
splitStrings: "Split Strings",
|
| 143 |
-
outputPromptTitle: "Generated Prompt",
|
| 144 |
-
settingsTitle: "Settings",
|
| 145 |
-
apiKeyLabel: "API Key",
|
| 146 |
-
characterCountLabel: "Character Count",
|
| 147 |
-
languageSelectLabel: "Language",
|
| 148 |
-
promptEnPlaceholder: "English prompt will be displayed here",
|
| 149 |
-
promptMyLanguagePlaceholder: "Translation will be displayed here",
|
| 150 |
-
apiKeyPlaceholder: "Enter your API key",
|
| 151 |
-
characterCountPlaceholder: "Enter the number of characters for the generated prompt"
|
| 152 |
-
},
|
| 153 |
-
zh: {
|
| 154 |
-
inputQueryTitle: "输入查询",
|
| 155 |
-
generateButtonText: "生成提示",
|
| 156 |
-
splitStrings: "分割字符串",
|
| 157 |
-
outputPromptTitle: "生成的提示",
|
| 158 |
-
settingsTitle: "设置",
|
| 159 |
-
apiKeyLabel: "API密钥",
|
| 160 |
-
characterCountLabel: "字符数",
|
| 161 |
-
languageSelectLabel: "语言",
|
| 162 |
-
promptEnPlaceholder: "英文提示将显示在这里",
|
| 163 |
-
promptMyLanguagePlaceholder: "翻译将显示在这里",
|
| 164 |
-
apiKeyPlaceholder: "请输入您的API密钥",
|
| 165 |
-
characterCountPlaceholder: "请输入生成提示的字符数"
|
| 166 |
-
},
|
| 167 |
-
ko: {
|
| 168 |
-
inputQueryTitle: "입력 쿼리",
|
| 169 |
-
generateButtonText: "프롬프트 생성",
|
| 170 |
-
splitStrings: "문자열 분할",
|
| 171 |
-
outputPromptTitle: "생성된 프롬프트",
|
| 172 |
-
settingsTitle: "설정",
|
| 173 |
-
apiKeyLabel: "API 키",
|
| 174 |
-
characterCountLabel: "문자 수",
|
| 175 |
-
languageSelectLabel: "언어",
|
| 176 |
-
promptEnPlaceholder: "영어 프롬프트가 여기에 표시됩니다",
|
| 177 |
-
promptMyLanguagePlaceholder: "번역이 여기에 표시됩니다",
|
| 178 |
-
apiKeyPlaceholder: "API 키를 입력하세요",
|
| 179 |
-
characterCountPlaceholder: "생성할 프롬프트의 문자 수를 입력하세요"
|
| 180 |
-
},
|
| 181 |
-
fr: {
|
| 182 |
-
inputQueryTitle: "Requête d'entrée",
|
| 183 |
-
generateButtonText: "Générer le prompt",
|
| 184 |
-
splitStrings: "Diviser les chaînes",
|
| 185 |
-
outputPromptTitle: "Prompt généré",
|
| 186 |
-
settingsTitle: "Paramètres",
|
| 187 |
-
apiKeyLabel: "Clé API",
|
| 188 |
-
characterCountLabel: "Nombre de caractères",
|
| 189 |
-
languageSelectLabel: "Langue",
|
| 190 |
-
promptEnPlaceholder: "Le prompt en anglais s'affichera ici",
|
| 191 |
-
promptMyLanguagePlaceholder: "La traduction s'affichera ici",
|
| 192 |
-
apiKeyPlaceholder: "Entrez votre clé API",
|
| 193 |
-
characterCountPlaceholder: "Entrez le nombre de caractères pour le prompt généré"
|
| 194 |
-
},
|
| 195 |
-
es: {
|
| 196 |
-
inputQueryTitle: "Consulta de entrada",
|
| 197 |
-
generateButtonText: "Generar prompt",
|
| 198 |
-
splitStrings: "Dividir cadenas",
|
| 199 |
-
outputPromptTitle: "Prompt generado",
|
| 200 |
-
settingsTitle: "Configuración",
|
| 201 |
-
apiKeyLabel: "Clave API",
|
| 202 |
-
characterCountLabel: "Recuento de caracteres",
|
| 203 |
-
languageSelectLabel: "Idioma",
|
| 204 |
-
promptEnPlaceholder: "El prompt en inglés se mostrará aquí",
|
| 205 |
-
promptMyLanguagePlaceholder: "La traducción se mostrará aquí",
|
| 206 |
-
apiKeyPlaceholder: "Ingrese su clave API",
|
| 207 |
-
characterCountPlaceholder: "Ingrese el número de caracteres para el prompt generado"
|
| 208 |
-
},
|
| 209 |
-
de: {
|
| 210 |
-
inputQueryTitle: "Eingabeabfrage",
|
| 211 |
-
generateButtonText: "Prompt generieren",
|
| 212 |
-
splitStrings: "Zeichenketten aufteilen",
|
| 213 |
-
outputPromptTitle: "Generierter Prompt",
|
| 214 |
-
settingsTitle: "Einstellungen",
|
| 215 |
-
apiKeyLabel: "API-Schlüssel",
|
| 216 |
-
characterCountLabel: "Zeichenanzahl",
|
| 217 |
-
languageSelectLabel: "Sprache",
|
| 218 |
-
promptEnPlaceholder: "Der englische Prompt wird hier angezeigt",
|
| 219 |
-
promptMyLanguagePlaceholder: "Die Übersetzung wird hier angezeigt",
|
| 220 |
-
apiKeyPlaceholder: "Geben Sie Ihren API-Schlüssel ein",
|
| 221 |
-
characterCountPlaceholder: "Geben Sie die Anzahl der Zeichen für den generierten Prompt ein"
|
| 222 |
-
},
|
| 223 |
-
it: {
|
| 224 |
-
inputQueryTitle: "Query di input",
|
| 225 |
-
generateButtonText: "Genera prompt",
|
| 226 |
-
splitStrings: "Dividi stringhe",
|
| 227 |
-
outputPromptTitle: "Prompt generato",
|
| 228 |
-
settingsTitle: "Impostazioni",
|
| 229 |
-
apiKeyLabel: "Chiave API",
|
| 230 |
-
characterCountLabel: "Conteggio caratteri",
|
| 231 |
-
languageSelectLabel: "Lingua",
|
| 232 |
-
promptEnPlaceholder: "Il prompt in inglese verrà visualizzato qui",
|
| 233 |
-
promptMyLanguagePlaceholder: "La traduzione verrà visualizzata qui",
|
| 234 |
-
apiKeyPlaceholder: "Inserisci la tua chiave API",
|
| 235 |
-
characterCountPlaceholder: "Inserisci il numero di caratteri per il prompt generato"
|
| 236 |
-
}
|
| 237 |
-
}
|
| 238 |
-
const resources = {
|
| 239 |
-
ja: {
|
| 240 |
-
translation: translations.ja
|
| 241 |
-
},
|
| 242 |
-
en: {
|
| 243 |
-
translation: translations.en
|
| 244 |
-
},
|
| 245 |
-
zh: {
|
| 246 |
-
translation: translations.zh
|
| 247 |
-
},
|
| 248 |
-
ko: {
|
| 249 |
-
translation: translations.ko
|
| 250 |
-
},
|
| 251 |
-
fr: {
|
| 252 |
-
translation: translations.fr
|
| 253 |
-
},
|
| 254 |
-
es: {
|
| 255 |
-
translation: translations.es
|
| 256 |
-
},
|
| 257 |
-
de: {
|
| 258 |
-
translation: translations.de
|
| 259 |
-
},
|
| 260 |
-
it: {
|
| 261 |
-
translation: translations.it
|
| 262 |
-
}
|
| 263 |
-
}
|
| 264 |
-
|
| 265 |
-
// 既存のスクリプトの前に追加
|
| 266 |
-
document.addEventListener('DOMContentLoaded', function () {
|
| 267 |
-
i18next
|
| 268 |
-
.use(i18nextBrowserLanguageDetector)
|
| 269 |
-
.init({
|
| 270 |
-
fallbackLng: 'ja', // デフォルト言語
|
| 271 |
-
resources: resources
|
| 272 |
-
})
|
| 273 |
-
.then(function (t) {
|
| 274 |
-
document.getElementById('languageSelect').value = i18next.language;
|
| 275 |
-
document.getElementById('languageSelect').dispatchEvent(new Event('change'));
|
| 276 |
-
});
|
| 277 |
-
});
|
| 278 |
-
|
| 279 |
-
function updateContent() {
|
| 280 |
-
// 各要素のテキストを更新
|
| 281 |
-
document.getElementById('inputQueryTitle').textContent = i18next.t('inputQueryTitle');
|
| 282 |
-
document.getElementById('generateButtonText').textContent = i18next.t('generateButtonText');
|
| 283 |
-
document.getElementById('splitStrings').textContent = i18next.t('splitStrings');
|
| 284 |
-
document.getElementById('outputPromptTitle').textContent = i18next.t('outputPromptTitle');
|
| 285 |
-
document.getElementById('settingsTitle').textContent = i18next.t('settingsTitle');
|
| 286 |
-
document.querySelector('#apiKeyLabel > a').textContent = i18next.t('apiKeyLabel');
|
| 287 |
-
document.getElementById('characterCountLabel').textContent = i18next.t('characterCountLabel');
|
| 288 |
-
document.getElementById('languageSelectLabel').textContent = i18next.t('languageSelectLabel');
|
| 289 |
-
|
| 290 |
-
// プレースホルダーを更新
|
| 291 |
-
document.getElementById('promptEn').placeholder = i18next.t('promptEnPlaceholder');
|
| 292 |
-
document.getElementById('promptMyLanguage').placeholder = i18next.t('promptMyLanguagePlaceholder');
|
| 293 |
-
document.getElementById('apiKey').placeholder = i18next.t('apiKeyPlaceholder');
|
| 294 |
-
document.getElementById('characterCount').placeholder = i18next.t('characterCountPlaceholder');
|
| 295 |
-
}
|
| 296 |
-
|
| 297 |
-
// 言語切り替え関数
|
| 298 |
-
function changeLang(language) {
|
| 299 |
-
i18next.changeLanguage(language, (err, t) => {
|
| 300 |
-
if (err) return console.error('言語切り替えエラー', err);
|
| 301 |
-
updateContent();
|
| 302 |
-
});
|
| 303 |
-
}
|
| 304 |
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
|
| 310 |
<script>
|
| 311 |
-
function
|
| 312 |
-
if (!document.getElementById('apiKey').value) {
|
| 313 |
-
alert("APIキーを入力してください");
|
| 314 |
-
return;
|
| 315 |
-
}
|
| 316 |
-
let query = document.getElementById('query').value;
|
| 317 |
-
let textFormat = 'str';
|
| 318 |
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
//textFormat = 'array, # テキストは1character(not word)ずつ格納した配列にして返すこと。 Example: ["I", "t", " ", "i", "s", " ", "a", " ", "p", "e", "n", "."], ["こ", "れ", "は", " ", "ペ", "ン", "で", "す", "。"], ';
|
| 322 |
-
}
|
| 323 |
-
|
| 324 |
-
let anotherLanguage = "";
|
| 325 |
-
|
| 326 |
-
if (!["en", "ja"].includes(i18next.language)) {
|
| 327 |
-
anotherLanguage = `,
|
| 328 |
-
{
|
| 329 |
-
"language": "${i18next.language}",
|
| 330 |
-
"text": ${textFormat}
|
| 331 |
-
}`;
|
| 332 |
-
}
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
const text = `「 ${query} 」をテーマに画像生成AIに送るプロンプトを考えてください。
|
| 336 |
-
背景や小物のディテイール、構図、視覚効果など視覚的な情報のみに言及すること。
|
| 337 |
-
その上で長文を日本語と英語で返信してください。
|
| 338 |
-
返答は以下のフォーマットのjson形式でのみ行う。json以外の内容をレスポンスに含めないこと。また、出力されるjsonには改行コード(\\n)や空白は含めないこと。
|
| 339 |
-
\`\`\`json
|
| 340 |
-
{
|
| 341 |
-
"results": [
|
| 342 |
-
{
|
| 343 |
-
"language": "en",
|
| 344 |
-
"text": ${textFormat} # ${document.getElementById('characterCount').value}文字程度,
|
| 345 |
-
},
|
| 346 |
-
{
|
| 347 |
-
"language": "danbooru",
|
| 348 |
-
"tags": [str],
|
| 349 |
-
},
|
| 350 |
-
{
|
| 351 |
-
"language": "ja",
|
| 352 |
-
"text": ${textFormat}
|
| 353 |
-
}${anotherLanguage}
|
| 354 |
-
]
|
| 355 |
-
}
|
| 356 |
-
\`\`\`
|
| 357 |
-
`;
|
| 358 |
-
const url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-exp-0827:generateContent?key=" + document.getElementById('apiKey').value;
|
| 359 |
-
const payload = {
|
| 360 |
-
contents: [
|
| 361 |
-
{
|
| 362 |
-
parts: [
|
| 363 |
-
{ text: text }
|
| 364 |
-
]
|
| 365 |
-
}
|
| 366 |
-
],
|
| 367 |
-
generation_config: {
|
| 368 |
-
max_output_tokens: 4095,
|
| 369 |
-
temperature: 1,
|
| 370 |
-
top_p: 1,
|
| 371 |
-
top_k: 32
|
| 372 |
-
},
|
| 373 |
-
safetySettings: [
|
| 374 |
-
{
|
| 375 |
-
category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
| 376 |
-
threshold: "BLOCK_NONE"
|
| 377 |
-
}
|
| 378 |
-
]
|
| 379 |
-
};
|
| 380 |
-
|
| 381 |
-
console.debug(text);
|
| 382 |
-
|
| 383 |
-
// fetchを使用してリクエストを送信
|
| 384 |
-
// ローディングアイコンを表示
|
| 385 |
-
document.getElementById('loading').classList.remove('d-none');
|
| 386 |
-
// generatePromptボタンを無効化
|
| 387 |
-
document.getElementById('generatePromptButton').disabled = true;
|
| 388 |
-
document.getElementById('generatePromptButton').classList.remove('btn-primary');
|
| 389 |
-
document.getElementById('generatePromptButton').classList.remove('btn-danger');
|
| 390 |
-
document.getElementById('generatePromptButton').classList.add('btn-secondary');
|
| 391 |
-
fetch(url, {
|
| 392 |
-
method: 'POST',
|
| 393 |
-
headers: {
|
| 394 |
-
'Content-Type': 'application/json'
|
| 395 |
-
},
|
| 396 |
-
body: JSON.stringify(payload)
|
| 397 |
-
})
|
| 398 |
-
.then(response => response.json())
|
| 399 |
-
.then(data => {
|
| 400 |
-
console.debug(data.candidates[0].content);
|
| 401 |
-
// レスポンスからテキストを抽出
|
| 402 |
-
const responseText = data.candidates[0].content.parts[0].text
|
| 403 |
-
console.debug(responseText);
|
| 404 |
-
|
| 405 |
-
let jsonString = responseText.replace(/```json|```/g, '').trim();
|
| 406 |
-
jsonString = jsonString.replace(/\\n/g, '');
|
| 407 |
-
console.debug(jsonString);
|
| 408 |
-
// JSONをパース
|
| 409 |
-
const parsedData = JSON5.parse(jsonString);
|
| 410 |
-
|
| 411 |
-
// 結果を表示
|
| 412 |
-
console.debug(parsedData);
|
| 413 |
-
let promptEn = parsedData.results.find(x => x.language === 'en').text;
|
| 414 |
-
let promptMyLanguage = parsedData.results.find(x => x.language === i18next.language).text;
|
| 415 |
-
[promptEn, promptMyLanguage] = [promptEn, promptMyLanguage].map(x => {
|
| 416 |
-
return Array.isArray(x) ? x.join("") : x;
|
| 417 |
-
});
|
| 418 |
-
document.getElementById('promptEn').value = promptEn.replace(/\. ?/g, '.\n\n');
|
| 419 |
-
document.getElementById('promptEn').value = document.getElementById('promptEn').value.replace(/^ */g, '');
|
| 420 |
-
document.getElementById('promptMyLanguage').value = promptMyLanguage.replace(/\。 ?/g, '。\n\n');
|
| 421 |
-
|
| 422 |
-
let danbooruTags = parsedData.results.find(x => x.language === 'danbooru');
|
| 423 |
-
if(danbooruTags.tags){
|
| 424 |
-
danbooruTags = danbooruTags.tags.map(x => x.replace("_", " "));
|
| 425 |
-
document.getElementById('danbooruTags').value = danbooruTags.join(", ");
|
| 426 |
-
}
|
| 427 |
-
if(danbooruTags.text){
|
| 428 |
-
danbooruTags = danbooruTags.text.map(x => x.replace("_", " "));
|
| 429 |
-
document.getElementById('danbooruTags').value = danbooruTags.join(", ");
|
| 430 |
-
}
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
// ローディングアイコンを非表示
|
| 434 |
-
document.getElementById('loading').classList.add('d-none');
|
| 435 |
-
document.getElementById('generatePromptButton').disabled = false;
|
| 436 |
-
document.getElementById('generatePromptButton').classList.remove('btn-secondary');
|
| 437 |
-
document.getElementById('generatePromptButton').classList.add('btn-primary');
|
| 438 |
-
saveToUserStorage(true);
|
| 439 |
-
})
|
| 440 |
-
.catch(error => {
|
| 441 |
-
console.error(error);
|
| 442 |
-
// エラー時もローディングアイコンを非表示
|
| 443 |
-
document.getElementById('loading').classList.add('d-none');
|
| 444 |
-
document.getElementById('generatePromptButton').disabled = false;
|
| 445 |
-
document.getElementById('generatePromptButton').classList.remove('btn-secondary');
|
| 446 |
-
document.getElementById('generatePromptButton').classList.add('btn-danger');
|
| 447 |
-
});
|
| 448 |
-
};
|
| 449 |
-
let lastSaveTimestamp = new Date();
|
| 450 |
-
function saveToUserStorage(force = false) {
|
| 451 |
-
const currentTime = new Date();
|
| 452 |
-
if (!force && currentTime - lastSaveTimestamp < 5000) {
|
| 453 |
-
return;
|
| 454 |
-
}
|
| 455 |
-
const data = {};
|
| 456 |
-
document.querySelectorAll('input, textarea').forEach(input => {
|
| 457 |
-
data[input.id] = input.value;
|
| 458 |
-
});
|
| 459 |
-
localStorage.setItem('gemini_prompt', JSON.stringify(data));
|
| 460 |
-
lastSaveTimestamp = currentTime;
|
| 461 |
-
return true;
|
| 462 |
-
}
|
| 463 |
-
function loadFromUserStorage() {
|
| 464 |
-
const data = JSON.parse(localStorage.getItem('gemini_prompt')) || {};
|
| 465 |
-
document.querySelectorAll('input, textarea').forEach(input => {
|
| 466 |
-
let v = data[input.id] || "";
|
| 467 |
-
if (v) {
|
| 468 |
-
if (input.type === "number") {
|
| 469 |
-
v = parseInt(v);
|
| 470 |
-
}
|
| 471 |
-
input.value = v;
|
| 472 |
-
}
|
| 473 |
-
});
|
| 474 |
-
}
|
| 475 |
-
loadFromUserStorage();
|
| 476 |
-
// 60秒ごとに自動保存を実行
|
| 477 |
-
setInterval(() => {
|
| 478 |
-
saveToUserStorage();
|
| 479 |
-
}, 60000);
|
| 480 |
-
document.querySelectorAll('input, textarea').forEach(input => {
|
| 481 |
-
input.addEventListener('input', saveToUserStorage);
|
| 482 |
-
});
|
| 483 |
-
// Ctrl+Enterでプロンプト生成を実行する
|
| 484 |
-
document.addEventListener('keydown', function (event) {
|
| 485 |
-
if (event.ctrlKey && event.key === 'Enter') {
|
| 486 |
-
event.preventDefault(); // デフォルトの動作を防ぐ
|
| 487 |
-
generatePrompt(); // プロンプト生成関数を呼び出す
|
| 488 |
-
}
|
| 489 |
});
|
| 490 |
</script>
|
|
|
|
| 491 |
</body>
|
| 492 |
|
| 493 |
</html>
|
|
|
|
| 8 |
<link href="https://unpkg.com/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
|
| 9 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
|
| 10 |
crossorigin="anonymous">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
<style>
|
| 12 |
#query {
|
| 13 |
min-height: 40vh;
|
|
|
|
| 17 |
#promptMyLanguage {
|
| 18 |
min-height: 20vh;
|
| 19 |
}
|
| 20 |
+
|
| 21 |
+
#sidebar {
|
| 22 |
+
position: fixed;
|
| 23 |
+
top: 0;
|
| 24 |
+
left: -250px;
|
| 25 |
+
width: 250px;
|
| 26 |
+
height: 100%;
|
| 27 |
+
background-color: #343a40;
|
| 28 |
+
transition: 0.3s;
|
| 29 |
+
z-index: 1000;
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
#sidebar.active {
|
| 33 |
+
left: 0;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
#content {
|
| 37 |
+
transition: margin-left 0.3s;
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
#content.active {
|
| 41 |
+
margin-left: 250px;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
#historyContainer::-webkit-scrollbar {
|
| 45 |
+
width: 5px;
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
#historyContainer::-webkit-scrollbar-track {
|
| 49 |
+
background: #f1f1f1;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
#historyContainer::-webkit-scrollbar-thumb {
|
| 53 |
+
background: #888;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
#historyContainer::-webkit-scrollbar-thumb:hover {
|
| 57 |
+
background: #555;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
.comic-animation {
|
| 61 |
+
width: 100%;
|
| 62 |
+
height: auto;
|
| 63 |
+
max-width: 680px;
|
| 64 |
+
max-height: 680px;
|
| 65 |
+
margin: 0 auto;
|
| 66 |
+
background-size: contain;
|
| 67 |
+
background-repeat: no-repeat;
|
| 68 |
+
background-position: center;
|
| 69 |
+
aspect-ratio: 1 / 1;
|
| 70 |
+
animation: comic-frame-switch 6s step-end infinite;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
@media (min-width: 768px) {
|
| 74 |
+
.modal-dialog.modal-large {
|
| 75 |
+
max-width: 700px;
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
@media (max-width: 767px) {
|
| 80 |
+
.comic-animation {
|
| 81 |
+
width: 100%;
|
| 82 |
+
height: auto;
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
@keyframes comic-frame-switch {
|
| 87 |
+
|
| 88 |
+
0%,
|
| 89 |
+
50% {
|
| 90 |
+
background-image: url("https://i.imgur.com/4A7K3TC.png");
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
50.01%,
|
| 94 |
+
100% {
|
| 95 |
+
background-image: url("https://i.imgur.com/LqLqwJi.png");
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
</style>
|
| 99 |
</head>
|
| 100 |
|
| 101 |
<body data-bs-theme="dark">
|
| 102 |
+
<div id="sidebar">
|
| 103 |
+
<div class="p-3">
|
| 104 |
+
<h3 id="settingsTitle" class="text-white">設定</h3>
|
| 105 |
+
<div class="form-group mb-3">
|
| 106 |
+
<label for="apiKey" class="form-label" id="apiKeyLabel">
|
| 107 |
+
|
| 108 |
+
<a href="https://aistudio.google.com/app/apikey?hl=ja" target="_blank">APIキー</a>
|
| 109 |
+
</label>
|
| 110 |
+
<input type="text" class="form-control" id="apiKey" placeholder="APIキーを入力してください">
|
| 111 |
+
</div>
|
| 112 |
+
<div class="form-group">
|
| 113 |
+
<label for="characterCount" class="form-label" id="characterCountLabel">文字数</label>
|
| 114 |
+
<input type="number" value="320" class="form-control" id="characterCount"
|
| 115 |
+
placeholder="生成するプロンプトの文字数を入力してください">
|
| 116 |
+
</div>
|
| 117 |
+
<div class="form-group">
|
| 118 |
+
<label for="languageSelect" class="form-label" id="languageSelectLabel">Language</label>
|
| 119 |
+
<select class="form-select" id="languageSelect">
|
| 120 |
+
<option value="ja">日本語</option>
|
| 121 |
+
<option value="en">English</option>
|
| 122 |
+
<option value="zh">中文</option>
|
| 123 |
+
<option value="ko">한국어</option>
|
| 124 |
+
<option value="fr">Français</option>
|
| 125 |
+
<option value="es">Español</option>
|
| 126 |
+
<option value="de">Deutsch</option>
|
| 127 |
+
<option value="it">Italiano</option>
|
| 128 |
+
</select>
|
| 129 |
+
</div>
|
| 130 |
+
<div class="form-group mb-3">
|
| 131 |
+
<label for="endpointSelect" class="form-label" id="endpointSelectLabel">エンドポイント</label>
|
| 132 |
+
<select class="form-select" id="endpointSelect">
|
| 133 |
+
<option value="gemini-1.5-pro-exp-0827">gemini-1.5-pro-exp-0827</option>
|
| 134 |
+
<option value="gemini-1.5-flash-exp-0827">gemini-1.5-flash-exp-0827</option>
|
| 135 |
+
<option value="gemini-1.5-pro-latest">gemini-1.5-pro-latest</option>
|
| 136 |
+
<option value="gemini-1.5-flash-latest">gemini-1.5-flash-latest</option>
|
| 137 |
+
|
| 138 |
+
</select>
|
| 139 |
+
</div>
|
| 140 |
+
|
| 141 |
+
<h3 class="text-white mt-4">履歴</h3>
|
| 142 |
+
<div id="historyContainer" style="max-height: 300px; overflow-y: auto;">
|
| 143 |
+
<ul id="historyList" class="list-group">
|
| 144 |
+
<!-- 履歴項目がここに動的に追加されます -->
|
| 145 |
+
</ul>
|
| 146 |
+
<p id="noHistoryMessage" class="text-white mt-2 d-none">履歴がありません。</p>
|
| 147 |
+
</div>
|
| 148 |
+
|
| 149 |
+
<!-- デバッグ用のモーダル表示ボタンを追加 -->
|
| 150 |
+
<div class="mt-4">
|
| 151 |
+
<button id="showLoadingModalButton" class="btn btn-secondary w-100">
|
| 152 |
+
ローディングモーダルを表示
|
| 153 |
+
</button>
|
| 154 |
+
</div>
|
| 155 |
+
</div>
|
| 156 |
+
</div>
|
| 157 |
+
|
| 158 |
+
<div id="content">
|
| 159 |
+
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
| 160 |
+
<div class="container-fluid">
|
| 161 |
+
<button id="sidebarToggle" class="btn btn-outline-light me-2">
|
| 162 |
+
<i class="fas fa-bars"></i>
|
| 163 |
+
</button>
|
| 164 |
+
<a class="navbar-brand" href="#">Gemini Prompt Generator</a>
|
| 165 |
+
</div>
|
| 166 |
+
</nav>
|
| 167 |
+
|
| 168 |
+
<div class="container mt-3">
|
| 169 |
<div class="row">
|
| 170 |
<div id="inputQuery" class="col-md-6 mb-4">
|
| 171 |
<div class="card">
|
|
|
|
| 206 |
<textarea class="form-control" id="promptMyLanguage" disabled
|
| 207 |
placeholder="日本訳がここに表示されます"></textarea>
|
| 208 |
</div>
|
| 209 |
+
<div class="form-group mt-3">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
<label for="danbooruTags" class="form-label" id="danbooruTagsLabel">
|
| 211 |
danbooru tags
|
| 212 |
</label>
|
| 213 |
<input type="text" class="form-control" id="danbooruTags" placeholder="danbooru tags"
|
| 214 |
readonly>
|
| 215 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
</div>
|
| 217 |
</div>
|
| 218 |
</div>
|
| 219 |
</div>
|
| 220 |
</div>
|
| 221 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
|
| 223 |
+
<div class="modal fade" id="loadingModal" tabindex="-1" aria-labelledby="loadingModalLabel" aria-hidden="true">
|
| 224 |
+
<div class="modal-dialog modal-dialog-centered modal-large">
|
| 225 |
+
<div class="modal-content bg-dark text-white">
|
| 226 |
+
<div class="modal-body text-center">
|
| 227 |
+
<h5 id="loadingModalLabel">プロンプト生成中...</h5>
|
| 228 |
+
<div class="comic-animation"></div>
|
| 229 |
+
</div>
|
| 230 |
+
</div>
|
| 231 |
+
</div>
|
| 232 |
+
</div>
|
| 233 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/json5/2.2.3/index.min.js"
|
| 234 |
+
integrity="sha512-44jdhc+R2TFfzBflS3/dGNEABiNUxBkkrqwO7GWTvGsj3HkQNr3GESvI9PUvAxmqxSnTosR0Ij9y3+o+6J1hig=="
|
| 235 |
+
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
| 236 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/i18next/23.14.0/i18next.min.js"
|
| 237 |
+
integrity="sha512-8ANNUVMWPf6aWGXZqDhS4OXJWBCRxfjlW7lKfupuiG1FZah0ST6LiI2qnEb1L5mp05v/+0hn3s2FO4EwIbIgfA=="
|
| 238 |
+
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
| 239 |
+
<script
|
| 240 |
+
src="https://cdnjs.cloudflare.com/ajax/libs/i18next-browser-languagedetector/8.0.0/i18nextBrowserLanguageDetector.min.js"
|
| 241 |
+
integrity="sha512-8/RTkAM23B3lQzi6fmPs+Yf9qhIHzrzRpeSZsBsQ8OEmo95mbVp+68dB647VDCuyQIBbF+OIbS9b30aTWUkoog=="
|
| 242 |
+
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
| 243 |
+
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"
|
| 244 |
+
integrity="sha384-HwwvtgBNo3bZJJLYd8oVXjrBZt8cqVSpeBNS5n7C8IVInixGAoxmnlMuBnhbgrkm"
|
| 245 |
+
crossorigin="anonymous"></script>
|
| 246 |
+
<script src="translation.js"></script>
|
| 247 |
+
<script src="prompt.js"></script>
|
| 248 |
+
<script src="storage.js"></script>
|
| 249 |
+
<script src="history.js"></script>
|
| 250 |
+
<script src="control.js"></script>
|
| 251 |
|
| 252 |
<script>
|
| 253 |
+
document.getElementById('showLoadingModalButton').addEventListener('click', function () {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
|
| 255 |
+
var loadingModal = new bootstrap.Modal(document.getElementById('loadingModal'));
|
| 256 |
+
loadingModal.show();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
});
|
| 258 |
</script>
|
| 259 |
+
|
| 260 |
</body>
|
| 261 |
|
| 262 |
</html>
|
prompt.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function generatePrompt() {
|
| 2 |
+
if (!document.getElementById('apiKey').value) {
|
| 3 |
+
alert("APIキーを入力してください");
|
| 4 |
+
return;
|
| 5 |
+
}
|
| 6 |
+
let query = document.getElementById('query').value;
|
| 7 |
+
let textFormat = 'str';
|
| 8 |
+
|
| 9 |
+
if (document.getElementById('splitStringsSwitch').checked) {
|
| 10 |
+
query = Array.from(document.getElementById('query').value).join(":::");
|
| 11 |
+
//textFormat = 'array, # テキストは1character(not word)ずつ格納した配列にして返すこと。 Example: ["I", "t", " ", "i", "s", " ", "a", " ", "p", "e", "n", "."], ["こ", "れ", "は", " ", "ペ", "ン", "で", "す", "。"], ';
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
let anotherLanguage = "";
|
| 15 |
+
|
| 16 |
+
if (!["en", "ja"].includes(i18next.language)) {
|
| 17 |
+
anotherLanguage = `,
|
| 18 |
+
{
|
| 19 |
+
"language": "${i18next.language}",
|
| 20 |
+
"text": ${textFormat}
|
| 21 |
+
}`;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
const selectedEndpoint = document.getElementById('endpointSelect').value;
|
| 25 |
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${selectedEndpoint}:generateContent?key=` + document.getElementById('apiKey').value;
|
| 26 |
+
|
| 27 |
+
const text = `「 ${query} 」をテーマに画像生成AIに送るプロンプトを考えてください。
|
| 28 |
+
背景や小物のディテイール、構図、視覚効果など視覚的な情報のみに言及すること。
|
| 29 |
+
その上で長文を日本語と英語で返信してください。
|
| 30 |
+
返答は以下のフォーマットのjson形式でのみ行う。json以外の内容をレスポンスに含めないこと。
|
| 31 |
+
\`\`\`json
|
| 32 |
+
{
|
| 33 |
+
"results": [
|
| 34 |
+
{
|
| 35 |
+
"language": "en",
|
| 36 |
+
"text": ${textFormat} # ${document.getElementById('characterCount').value}文字程度,
|
| 37 |
+
},
|
| 38 |
+
{
|
| 39 |
+
"language": "danbooru",
|
| 40 |
+
"tags": [str],
|
| 41 |
+
},
|
| 42 |
+
{
|
| 43 |
+
"language": "ja",
|
| 44 |
+
"text": ${textFormat}
|
| 45 |
+
}${anotherLanguage}
|
| 46 |
+
]
|
| 47 |
+
}
|
| 48 |
+
\`\`\`
|
| 49 |
+
`;
|
| 50 |
+
const payload = {
|
| 51 |
+
contents: [
|
| 52 |
+
{
|
| 53 |
+
parts: [
|
| 54 |
+
{ text: text }
|
| 55 |
+
]
|
| 56 |
+
}
|
| 57 |
+
],
|
| 58 |
+
generation_config: {
|
| 59 |
+
max_output_tokens: 4095,
|
| 60 |
+
temperature: 1,
|
| 61 |
+
top_p: 1,
|
| 62 |
+
top_k: 32
|
| 63 |
+
},
|
| 64 |
+
safetySettings: [
|
| 65 |
+
{
|
| 66 |
+
category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
| 67 |
+
threshold: "BLOCK_NONE"
|
| 68 |
+
}
|
| 69 |
+
]
|
| 70 |
+
};
|
| 71 |
+
|
| 72 |
+
console.debug(text);
|
| 73 |
+
|
| 74 |
+
// モーダルを表示
|
| 75 |
+
const loadingModal = new bootstrap.Modal(document.getElementById('loadingModal'));
|
| 76 |
+
loadingModal.show();
|
| 77 |
+
|
| 78 |
+
// ローディングアイコンを表示
|
| 79 |
+
document.getElementById('loading').classList.remove('d-none');
|
| 80 |
+
// generatePromptボタンを無効化
|
| 81 |
+
document.getElementById('generatePromptButton').disabled = true;
|
| 82 |
+
document.getElementById('generatePromptButton').classList.remove('btn-primary');
|
| 83 |
+
document.getElementById('generatePromptButton').classList.remove('btn-danger');
|
| 84 |
+
document.getElementById('generatePromptButton').classList.add('btn-secondary');
|
| 85 |
+
fetch(url, {
|
| 86 |
+
method: 'POST',
|
| 87 |
+
headers: {
|
| 88 |
+
'Content-Type': 'application/json'
|
| 89 |
+
},
|
| 90 |
+
body: JSON.stringify(payload)
|
| 91 |
+
})
|
| 92 |
+
.then(response => response.json())
|
| 93 |
+
.then(data => {
|
| 94 |
+
console.debug(data.candidates[0].content);
|
| 95 |
+
// レスポンスからテキストを抽出
|
| 96 |
+
const responseText = data.candidates[0].content.parts[0].text
|
| 97 |
+
console.debug(responseText);
|
| 98 |
+
|
| 99 |
+
let jsonString = responseText.replace(/```json|```/g, '').trim();
|
| 100 |
+
jsonString = jsonString.replace(/\n/g, '');
|
| 101 |
+
console.debug(jsonString);
|
| 102 |
+
// JSONをパース
|
| 103 |
+
const parsedData = JSON5.parse(jsonString);
|
| 104 |
+
|
| 105 |
+
// 結果を表示
|
| 106 |
+
console.debug(parsedData);
|
| 107 |
+
let promptEn = parsedData.results.find(x => x.language === 'en').text;
|
| 108 |
+
let promptMyLanguage = parsedData.results.find(x => x.language === i18next.language).text;
|
| 109 |
+
[promptEn, promptMyLanguage] = [promptEn, promptMyLanguage].map(x => {
|
| 110 |
+
return Array.isArray(x) ? x.join("") : x;
|
| 111 |
+
});
|
| 112 |
+
document.getElementById('promptEn').value = promptEn.replace(/\. ?/g, '.\n\n');
|
| 113 |
+
document.getElementById('promptEn').value = document.getElementById('promptEn').value.replace(/^ */g, '');
|
| 114 |
+
document.getElementById('promptMyLanguage').value = promptMyLanguage.replace(/\。 ?/g, '。\n\n');
|
| 115 |
+
|
| 116 |
+
let danbooruTags = parsedData.results.find(x => x.language === 'danbooru');
|
| 117 |
+
if (danbooruTags.tags) {
|
| 118 |
+
danbooruTags = danbooruTags.tags.map(x => x.replace("_", " "));
|
| 119 |
+
document.getElementById('danbooruTags').value = danbooruTags.join(", ");
|
| 120 |
+
}
|
| 121 |
+
if (danbooruTags.text) {
|
| 122 |
+
danbooruTags = danbooruTags.text.map(x => x.replace("_", " "));
|
| 123 |
+
document.getElementById('danbooruTags').value = danbooruTags.join(", ");
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
// ローディングアイコンを非表示
|
| 128 |
+
document.getElementById('loading').classList.add('d-none');
|
| 129 |
+
document.getElementById('generatePromptButton').disabled = false;
|
| 130 |
+
document.getElementById('generatePromptButton').classList.remove('btn-secondary');
|
| 131 |
+
document.getElementById('generatePromptButton').classList.add('btn-primary');
|
| 132 |
+
saveToUserStorage(true);
|
| 133 |
+
saveToHistory(); // 履歴に保存
|
| 134 |
+
})
|
| 135 |
+
.catch(error => {
|
| 136 |
+
console.error(error);
|
| 137 |
+
// エラー時の処理
|
| 138 |
+
document.getElementById('generatePromptButton').classList.remove('btn-secondary');
|
| 139 |
+
document.getElementById('generatePromptButton').classList.add('btn-danger');
|
| 140 |
+
})
|
| 141 |
+
.finally(() => {
|
| 142 |
+
// ローディングアイコンを非表示
|
| 143 |
+
document.getElementById('loading').classList.add('d-none');
|
| 144 |
+
document.getElementById('generatePromptButton').disabled = false;
|
| 145 |
+
document.getElementById('generatePromptButton').classList.remove('btn-secondary');
|
| 146 |
+
if (!document.getElementById('generatePromptButton').classList.contains('btn-danger')) {
|
| 147 |
+
document.getElementById('generatePromptButton').classList.add('btn-primary');
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
// モーダルを非表示
|
| 151 |
+
setTimeout(() => {
|
| 152 |
+
loadingModal.hide();
|
| 153 |
+
}, 500);
|
| 154 |
+
});
|
| 155 |
+
};
|
| 156 |
+
|
storage.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
function saveToUserStorage(force = false) {
|
| 2 |
+
const currentTime = new Date().getTime();
|
| 3 |
+
const lastSaveTimestamp = parseInt(localStorage.getItem('lastSaveTimestamp') || '0');
|
| 4 |
+
if (!force && currentTime - lastSaveTimestamp < 5000) {
|
| 5 |
+
return;
|
| 6 |
+
}
|
| 7 |
+
const data = {};
|
| 8 |
+
document.querySelectorAll('input, textarea, select').forEach(input => {
|
| 9 |
+
data[input.id] = input.value;
|
| 10 |
+
});
|
| 11 |
+
localStorage.setItem('gemini_prompt', JSON.stringify(data));
|
| 12 |
+
localStorage.setItem('lastSaveTimestamp', currentTime.toString());
|
| 13 |
+
return true;
|
| 14 |
+
}
|
| 15 |
+
function loadFromUserStorage() {
|
| 16 |
+
const data = JSON.parse(localStorage.getItem('gemini_prompt')) || {};
|
| 17 |
+
document.querySelectorAll('input, textarea, select').forEach(input => {
|
| 18 |
+
let v = data[input.id] || "";
|
| 19 |
+
if (v) {
|
| 20 |
+
if (input.type === "number") {
|
| 21 |
+
v = parseInt(v);
|
| 22 |
+
}
|
| 23 |
+
input.value = v;
|
| 24 |
+
}
|
| 25 |
+
});
|
| 26 |
+
// エンドポイントが保存されていない場合、デフォルト値を設定
|
| 27 |
+
if (!data.endpointSelect) {
|
| 28 |
+
document.getElementById('endpointSelect').value = "gemini-1.5-pro-exp-0827";
|
| 29 |
+
}
|
| 30 |
+
}
|
translation.js
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const translations = {
|
| 2 |
+
ja: {
|
| 3 |
+
inputQueryTitle: "入力クエリ",
|
| 4 |
+
generateButtonText: "プロンプト生成",
|
| 5 |
+
splitStrings: "分割送信",
|
| 6 |
+
outputPromptTitle: "生成されたプロンプト",
|
| 7 |
+
settingsTitle: "設定",
|
| 8 |
+
apiKeyLabel: "APIキー",
|
| 9 |
+
characterCountLabel: "文字数",
|
| 10 |
+
languageSelectLabel: "言語",
|
| 11 |
+
promptEnPlaceholder: "英語のプロンプトがここに表示されます",
|
| 12 |
+
promptMyLanguagePlaceholder: "日本訳がここに表示されます",
|
| 13 |
+
apiKeyPlaceholder: "APIキーを入力してください",
|
| 14 |
+
characterCountPlaceholder: "生成するプロンプトの文字数を入力してください"
|
| 15 |
+
},
|
| 16 |
+
en: {
|
| 17 |
+
inputQueryTitle: "Input Query",
|
| 18 |
+
generateButtonText: "Generate Prompt",
|
| 19 |
+
splitStrings: "Split Strings",
|
| 20 |
+
outputPromptTitle: "Generated Prompt",
|
| 21 |
+
settingsTitle: "Settings",
|
| 22 |
+
apiKeyLabel: "API Key",
|
| 23 |
+
characterCountLabel: "Character Count",
|
| 24 |
+
languageSelectLabel: "Language",
|
| 25 |
+
promptEnPlaceholder: "English prompt will be displayed here",
|
| 26 |
+
promptMyLanguagePlaceholder: "Translation will be displayed here",
|
| 27 |
+
apiKeyPlaceholder: "Enter your API key",
|
| 28 |
+
characterCountPlaceholder: "Enter the number of characters for the generated prompt"
|
| 29 |
+
},
|
| 30 |
+
zh: {
|
| 31 |
+
inputQueryTitle: "输入查询",
|
| 32 |
+
generateButtonText: "生成提示",
|
| 33 |
+
splitStrings: "分割字符串",
|
| 34 |
+
outputPromptTitle: "生成的提示",
|
| 35 |
+
settingsTitle: "设置",
|
| 36 |
+
apiKeyLabel: "API密钥",
|
| 37 |
+
characterCountLabel: "字符数",
|
| 38 |
+
languageSelectLabel: "语言",
|
| 39 |
+
promptEnPlaceholder: "英文提示将显示在这里",
|
| 40 |
+
promptMyLanguagePlaceholder: "翻译将显示在这里",
|
| 41 |
+
apiKeyPlaceholder: "请输入您的API密钥",
|
| 42 |
+
characterCountPlaceholder: "请输入生成提示的字符数"
|
| 43 |
+
},
|
| 44 |
+
ko: {
|
| 45 |
+
inputQueryTitle: "입력 쿼리",
|
| 46 |
+
generateButtonText: "프롬프트 생성",
|
| 47 |
+
splitStrings: "문자열 분할",
|
| 48 |
+
outputPromptTitle: "생성된 프롬프트",
|
| 49 |
+
settingsTitle: "설정",
|
| 50 |
+
apiKeyLabel: "API 키",
|
| 51 |
+
characterCountLabel: "문자 수",
|
| 52 |
+
languageSelectLabel: "언어",
|
| 53 |
+
promptEnPlaceholder: "영어 프롬프트가 여기에 표시됩니다",
|
| 54 |
+
promptMyLanguagePlaceholder: "번역이 여기에 표시됩니다",
|
| 55 |
+
apiKeyPlaceholder: "API 키를 입력하세요",
|
| 56 |
+
characterCountPlaceholder: "생성할 프롬프트의 문자 수를 입력하세요"
|
| 57 |
+
},
|
| 58 |
+
fr: {
|
| 59 |
+
inputQueryTitle: "Requête d'entrée",
|
| 60 |
+
generateButtonText: "Générer le prompt",
|
| 61 |
+
splitStrings: "Diviser les chaînes",
|
| 62 |
+
outputPromptTitle: "Prompt généré",
|
| 63 |
+
settingsTitle: "Paramètres",
|
| 64 |
+
apiKeyLabel: "Clé API",
|
| 65 |
+
characterCountLabel: "Nombre de caractères",
|
| 66 |
+
languageSelectLabel: "Langue",
|
| 67 |
+
promptEnPlaceholder: "Le prompt en anglais s'affichera ici",
|
| 68 |
+
promptMyLanguagePlaceholder: "La traduction s'affichera ici",
|
| 69 |
+
apiKeyPlaceholder: "Entrez votre clé API",
|
| 70 |
+
characterCountPlaceholder: "Entrez le nombre de caractères pour le prompt généré"
|
| 71 |
+
},
|
| 72 |
+
es: {
|
| 73 |
+
inputQueryTitle: "Consulta de entrada",
|
| 74 |
+
generateButtonText: "Generar prompt",
|
| 75 |
+
splitStrings: "Dividir cadenas",
|
| 76 |
+
outputPromptTitle: "Prompt generado",
|
| 77 |
+
settingsTitle: "Configuración",
|
| 78 |
+
apiKeyLabel: "Clave API",
|
| 79 |
+
characterCountLabel: "Recuento de caracteres",
|
| 80 |
+
languageSelectLabel: "Idioma",
|
| 81 |
+
promptEnPlaceholder: "El prompt en inglés se mostrará aquí",
|
| 82 |
+
promptMyLanguagePlaceholder: "La traducción se mostrará aquí",
|
| 83 |
+
apiKeyPlaceholder: "Ingrese su clave API",
|
| 84 |
+
characterCountPlaceholder: "Ingrese el número de caracteres para el prompt generado"
|
| 85 |
+
},
|
| 86 |
+
de: {
|
| 87 |
+
inputQueryTitle: "Eingabeabfrage",
|
| 88 |
+
generateButtonText: "Prompt generieren",
|
| 89 |
+
splitStrings: "Zeichenketten aufteilen",
|
| 90 |
+
outputPromptTitle: "Generierter Prompt",
|
| 91 |
+
settingsTitle: "Einstellungen",
|
| 92 |
+
apiKeyLabel: "API-Schlüssel",
|
| 93 |
+
characterCountLabel: "Zeichenanzahl",
|
| 94 |
+
languageSelectLabel: "Sprache",
|
| 95 |
+
promptEnPlaceholder: "Der englische Prompt wird hier angezeigt",
|
| 96 |
+
promptMyLanguagePlaceholder: "Die Übersetzung wird hier angezeigt",
|
| 97 |
+
apiKeyPlaceholder: "Geben Sie Ihren API-Schlüssel ein",
|
| 98 |
+
characterCountPlaceholder: "Geben Sie die Anzahl der Zeichen für den generierten Prompt ein"
|
| 99 |
+
},
|
| 100 |
+
it: {
|
| 101 |
+
inputQueryTitle: "Query di input",
|
| 102 |
+
generateButtonText: "Genera prompt",
|
| 103 |
+
splitStrings: "Dividi stringhe",
|
| 104 |
+
outputPromptTitle: "Prompt generato",
|
| 105 |
+
settingsTitle: "Impostazioni",
|
| 106 |
+
apiKeyLabel: "Chiave API",
|
| 107 |
+
characterCountLabel: "Conteggio caratteri",
|
| 108 |
+
languageSelectLabel: "Lingua",
|
| 109 |
+
promptEnPlaceholder: "Il prompt in inglese verrà visualizzato qui",
|
| 110 |
+
promptMyLanguagePlaceholder: "La traduzione verrà visualizzata qui",
|
| 111 |
+
apiKeyPlaceholder: "Inserisci la tua chiave API",
|
| 112 |
+
characterCountPlaceholder: "Inserisci il numero di caratteri per il prompt generato"
|
| 113 |
+
}
|
| 114 |
+
}
|
| 115 |
+
const resources = {
|
| 116 |
+
ja: {
|
| 117 |
+
translation: translations.ja
|
| 118 |
+
},
|
| 119 |
+
en: {
|
| 120 |
+
translation: translations.en
|
| 121 |
+
},
|
| 122 |
+
zh: {
|
| 123 |
+
translation: translations.zh
|
| 124 |
+
},
|
| 125 |
+
ko: {
|
| 126 |
+
translation: translations.ko
|
| 127 |
+
},
|
| 128 |
+
fr: {
|
| 129 |
+
translation: translations.fr
|
| 130 |
+
},
|
| 131 |
+
es: {
|
| 132 |
+
translation: translations.es
|
| 133 |
+
},
|
| 134 |
+
de: {
|
| 135 |
+
translation: translations.de
|
| 136 |
+
},
|
| 137 |
+
it: {
|
| 138 |
+
translation: translations.it
|
| 139 |
+
}
|
| 140 |
+
}
|
| 141 |
+
|
| 142 |
+
// 既存のスクリプトの前に追加
|
| 143 |
+
document.addEventListener('DOMContentLoaded', function () {
|
| 144 |
+
i18next
|
| 145 |
+
.use(i18nextBrowserLanguageDetector)
|
| 146 |
+
.init({
|
| 147 |
+
fallbackLng: 'ja', // デフォルト言語
|
| 148 |
+
resources: resources
|
| 149 |
+
})
|
| 150 |
+
.then(function (t) {
|
| 151 |
+
document.getElementById('languageSelect').value = i18next.language;
|
| 152 |
+
document.getElementById('languageSelect').dispatchEvent(new Event('change'));
|
| 153 |
+
});
|
| 154 |
+
});
|
| 155 |
+
|
| 156 |
+
function updateContent() {
|
| 157 |
+
// 各要素のテキストを更新
|
| 158 |
+
document.getElementById('inputQueryTitle').textContent = i18next.t('inputQueryTitle');
|
| 159 |
+
document.getElementById('generateButtonText').textContent = i18next.t('generateButtonText');
|
| 160 |
+
document.getElementById('splitStrings').textContent = i18next.t('splitStrings');
|
| 161 |
+
document.getElementById('outputPromptTitle').textContent = i18next.t('outputPromptTitle');
|
| 162 |
+
document.getElementById('settingsTitle').textContent = i18next.t('settingsTitle');
|
| 163 |
+
document.querySelector('#apiKeyLabel > a').textContent = i18next.t('apiKeyLabel');
|
| 164 |
+
document.getElementById('characterCountLabel').textContent = i18next.t('characterCountLabel');
|
| 165 |
+
document.getElementById('languageSelectLabel').textContent = i18next.t('languageSelectLabel');
|
| 166 |
+
|
| 167 |
+
// プレースホルダーを更新
|
| 168 |
+
document.getElementById('promptEn').placeholder = i18next.t('promptEnPlaceholder');
|
| 169 |
+
document.getElementById('promptMyLanguage').placeholder = i18next.t('promptMyLanguagePlaceholder');
|
| 170 |
+
document.getElementById('apiKey').placeholder = i18next.t('apiKeyPlaceholder');
|
| 171 |
+
document.getElementById('characterCount').placeholder = i18next.t('characterCountPlaceholder');
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
// 言語切り替え関数
|
| 175 |
+
function changeLang(language) {
|
| 176 |
+
i18next.changeLanguage(language, (err, t) => {
|
| 177 |
+
if (err) return console.error('言語切り替えエラー', err);
|
| 178 |
+
updateContent();
|
| 179 |
+
});
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
document.getElementById('languageSelect').addEventListener('change', function () {
|
| 183 |
+
changeLang(this.value);
|
| 184 |
+
});
|