gemini_prompter / index.html
SenY's picture
Upload index.html
9399d36 verified
raw
history blame
25.2 kB
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gemini Prompt Generator</title>
<link href="https://unpkg.com/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css"
crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/json5/2.2.3/index.min.js"
integrity="sha512-44jdhc+R2TFfzBflS3/dGNEABiNUxBkkrqwO7GWTvGsj3HkQNr3GESvI9PUvAxmqxSnTosR0Ij9y3+o+6J1hig=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/i18next/23.14.0/i18next.min.js"
integrity="sha512-8ANNUVMWPf6aWGXZqDhS4OXJWBCRxfjlW7lKfupuiG1FZah0ST6LiI2qnEb1L5mp05v/+0hn3s2FO4EwIbIgfA=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/i18next-browser-languagedetector/8.0.0/i18nextBrowserLanguageDetector.min.js"
integrity="sha512-8/RTkAM23B3lQzi6fmPs+Yf9qhIHzrzRpeSZsBsQ8OEmo95mbVp+68dB647VDCuyQIBbF+OIbS9b30aTWUkoog=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<style>
#query {
min-height: 40vh;
}
#promptEn,
#promptMyLanguage {
min-height: 20vh;
}
</style>
</head>
<body data-bs-theme="dark">
<div class="container">
<div class="row m-0 border-start border-end border-2">
<div class="row">
<div id="inputQuery" class="col-md-6 mb-4">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0" id="inputQueryTitle">入力クエリ</h5>
</div>
<div class="card-body">
<div class="form-group">
<textarea class="form-control" id="query" placeholder="ここにクエリを入力してください"></textarea>
</div>
</div>
<div class="card-footer d-flex align-items-center">
<button id="generatePromptButton" class="btn btn-primary flex-grow-1 me-2"
onclick="generatePrompt()">
<i class="fas fa-magic me-2"></i><span id="generateButtonText">プロンプト生成</span>
<i class="fas fa-spinner fa-spin me-2 d-none" id="loading"></i>
</button>
<div class="form-check form-switch" id="splitStringsSwitchWrapper">
<input class="form-check-input" type="checkbox" id="splitStringsSwitch">
<label class="form-check-label" for="splitStringsSwitch">
<i class="fas fa-shield-alt" id="splitStrings">分割送信</i>
</label>
</div>
</div>
</div>
</div>
<div id="outputPrompt" class="col-md-6 mb-4">
<div class="card">
<div class="card-header bg-success text-white">
<h5 class="mb-0" id="outputPromptTitle">生成されたプロンプト</h5>
</div>
<div class="card-body">
<div class="form-group">
<textarea class="form-control" id="promptEn"
placeholder="英語のプロンプトがここに表示されます"></textarea>
</div>
<div class="form-group mt-3">
<textarea class="form-control" id="promptMyLanguage" disabled
placeholder="日本訳がここに表示されます"></textarea>
</div>
</div>
</div>
</div>
<div class="col-12 mb-4">
<div class="card">
<div class="card-header bg-primary text-white">
<h5 class="mb-0" id="settingsTitle">設定</h5>
</div>
<div class="card-body">
<div class="form-group mb-3">
<label for="danbooruTags" class="form-label" id="danbooruTagsLabel">
danbooru tags
</label>
<input type="text" class="form-control" id="danbooruTags" placeholder="danbooru tags"
readonly>
</div>
<div class="form-group mb-3">
<label for="apiKey" class="form-label" id="apiKeyLabel">
<a href="https://aistudio.google.com/app/apikey?hl=ja" target="_blank">APIキー</a>
</label>
<input type="text" class="form-control" id="apiKey" placeholder="APIキーを入力してください">
</div>
<div class="form-group">
<label for="characterCount" class="form-label" id="characterCountLabel">文字数</label>
<input type="number" value="320" class="form-control" id="characterCount"
placeholder="生成するプロンプトの文字数を入力してください">
</div>
<div class="form-group">
<label for="languageSelect" class="form-label" id="languageSelectLabel">Language</label>
<select class="form-select" id="languageSelect">
<option value="ja">日本語</option>
<option value="en">English</option>
<option value="zh">中文</option>
<option value="ko">한국어</option>
<option value="fr">Français</option>
<option value="es">Español</option>
<option value="de">Deutsch</option>
<option value="it">Italiano</option>
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
let language;
const translations = {
ja: {
inputQueryTitle: "入力クエリ",
generateButtonText: "プロンプト生成",
splitStrings: "分割送信",
outputPromptTitle: "生成されたプロンプト",
settingsTitle: "設定",
apiKeyLabel: "APIキー",
characterCountLabel: "文字数",
languageSelectLabel: "言語",
promptEnPlaceholder: "英語のプロンプトがここに表示されます",
promptMyLanguagePlaceholder: "日本訳がここに表示されます",
apiKeyPlaceholder: "APIキーを入力してください",
characterCountPlaceholder: "生成するプロンプトの文字数を入力してください"
},
en: {
inputQueryTitle: "Input Query",
generateButtonText: "Generate Prompt",
splitStrings: "Split Strings",
outputPromptTitle: "Generated Prompt",
settingsTitle: "Settings",
apiKeyLabel: "API Key",
characterCountLabel: "Character Count",
languageSelectLabel: "Language",
promptEnPlaceholder: "English prompt will be displayed here",
promptMyLanguagePlaceholder: "Translation will be displayed here",
apiKeyPlaceholder: "Enter your API key",
characterCountPlaceholder: "Enter the number of characters for the generated prompt"
},
zh: {
inputQueryTitle: "输入查询",
generateButtonText: "生成提示",
splitStrings: "分割字符串",
outputPromptTitle: "生成的提示",
settingsTitle: "设置",
apiKeyLabel: "API密钥",
characterCountLabel: "字符数",
languageSelectLabel: "语言",
promptEnPlaceholder: "英文提示将显示在这里",
promptMyLanguagePlaceholder: "翻译将显示在这里",
apiKeyPlaceholder: "请输入您的API密钥",
characterCountPlaceholder: "请输入生成提示的字符数"
},
ko: {
inputQueryTitle: "입력 쿼리",
generateButtonText: "프롬프트 생성",
splitStrings: "문자열 분할",
outputPromptTitle: "생성된 프롬프트",
settingsTitle: "설정",
apiKeyLabel: "API 키",
characterCountLabel: "문자 수",
languageSelectLabel: "언어",
promptEnPlaceholder: "영어 프롬프트가 여기에 표시됩니다",
promptMyLanguagePlaceholder: "번역이 여기에 표시됩니다",
apiKeyPlaceholder: "API 키를 입력하세요",
characterCountPlaceholder: "생성할 프롬프트의 문자 수를 입력하세요"
},
fr: {
inputQueryTitle: "Requête d'entrée",
generateButtonText: "Générer le prompt",
splitStrings: "Diviser les chaînes",
outputPromptTitle: "Prompt généré",
settingsTitle: "Paramètres",
apiKeyLabel: "Clé API",
characterCountLabel: "Nombre de caractères",
languageSelectLabel: "Langue",
promptEnPlaceholder: "Le prompt en anglais s'affichera ici",
promptMyLanguagePlaceholder: "La traduction s'affichera ici",
apiKeyPlaceholder: "Entrez votre clé API",
characterCountPlaceholder: "Entrez le nombre de caractères pour le prompt généré"
},
es: {
inputQueryTitle: "Consulta de entrada",
generateButtonText: "Generar prompt",
splitStrings: "Dividir cadenas",
outputPromptTitle: "Prompt generado",
settingsTitle: "Configuración",
apiKeyLabel: "Clave API",
characterCountLabel: "Recuento de caracteres",
languageSelectLabel: "Idioma",
promptEnPlaceholder: "El prompt en inglés se mostrará aquí",
promptMyLanguagePlaceholder: "La traducción se mostrará aquí",
apiKeyPlaceholder: "Ingrese su clave API",
characterCountPlaceholder: "Ingrese el número de caracteres para el prompt generado"
},
de: {
inputQueryTitle: "Eingabeabfrage",
generateButtonText: "Prompt generieren",
splitStrings: "Zeichenketten aufteilen",
outputPromptTitle: "Generierter Prompt",
settingsTitle: "Einstellungen",
apiKeyLabel: "API-Schlüssel",
characterCountLabel: "Zeichenanzahl",
languageSelectLabel: "Sprache",
promptEnPlaceholder: "Der englische Prompt wird hier angezeigt",
promptMyLanguagePlaceholder: "Die Übersetzung wird hier angezeigt",
apiKeyPlaceholder: "Geben Sie Ihren API-Schlüssel ein",
characterCountPlaceholder: "Geben Sie die Anzahl der Zeichen für den generierten Prompt ein"
},
it: {
inputQueryTitle: "Query di input",
generateButtonText: "Genera prompt",
splitStrings: "Dividi stringhe",
outputPromptTitle: "Prompt generato",
settingsTitle: "Impostazioni",
apiKeyLabel: "Chiave API",
characterCountLabel: "Conteggio caratteri",
languageSelectLabel: "Lingua",
promptEnPlaceholder: "Il prompt in inglese verrà visualizzato qui",
promptMyLanguagePlaceholder: "La traduzione verrà visualizzata qui",
apiKeyPlaceholder: "Inserisci la tua chiave API",
characterCountPlaceholder: "Inserisci il numero di caratteri per il prompt generato"
}
}
const resources = {
ja: {
translation: translations.ja
},
en: {
translation: translations.en
},
zh: {
translation: translations.zh
},
ko: {
translation: translations.ko
},
fr: {
translation: translations.fr
},
es: {
translation: translations.es
},
de: {
translation: translations.de
},
it: {
translation: translations.it
}
}
// 既存のスクリプトの前に追加
document.addEventListener('DOMContentLoaded', function () {
i18next
.use(i18nextBrowserLanguageDetector)
.init({
fallbackLng: 'ja', // デフォルト言語
resources: resources
})
.then(function (t) {
document.getElementById('languageSelect').value = i18next.language;
document.getElementById('languageSelect').dispatchEvent(new Event('change'));
});
});
function updateContent() {
// 各要素のテキストを更新
document.getElementById('inputQueryTitle').textContent = i18next.t('inputQueryTitle');
document.getElementById('generateButtonText').textContent = i18next.t('generateButtonText');
document.getElementById('splitStrings').textContent = i18next.t('splitStrings');
document.getElementById('outputPromptTitle').textContent = i18next.t('outputPromptTitle');
document.getElementById('settingsTitle').textContent = i18next.t('settingsTitle');
document.querySelector('#apiKeyLabel > a').textContent = i18next.t('apiKeyLabel');
document.getElementById('characterCountLabel').textContent = i18next.t('characterCountLabel');
document.getElementById('languageSelectLabel').textContent = i18next.t('languageSelectLabel');
// プレースホルダーを更新
document.getElementById('promptEn').placeholder = i18next.t('promptEnPlaceholder');
document.getElementById('promptMyLanguage').placeholder = i18next.t('promptMyLanguagePlaceholder');
document.getElementById('apiKey').placeholder = i18next.t('apiKeyPlaceholder');
document.getElementById('characterCount').placeholder = i18next.t('characterCountPlaceholder');
}
// 言語切り替え関数
function changeLang(language) {
i18next.changeLanguage(language, (err, t) => {
if (err) return console.error('言語切り替えエラー', err);
updateContent();
});
}
document.getElementById('languageSelect').addEventListener('change', function () {
changeLang(this.value);
});
</script>
<script>
function generatePrompt() {
if (!document.getElementById('apiKey').value) {
alert("APIキーを入力してください");
return;
}
let query = document.getElementById('query').value;
let textFormat = 'str';
if (document.getElementById('splitStringsSwitch').checked) {
query = Array.from(document.getElementById('query').value).join(":::");
//textFormat = 'array, # テキストは1character(not word)ずつ格納した配列にして返すこと。 Example: ["I", "t", " ", "i", "s", " ", "a", " ", "p", "e", "n", "."], ["こ", "れ", "は", " ", "ペ", "ン", "で", "す", "。"], ';
}
let anotherLanguage = "";
if (!["en", "ja"].includes(i18next.language)) {
anotherLanguage = `,
{
"language": "${i18next.language}",
"text": ${textFormat}
}`;
}
const text = `「 ${query} 」をテーマに画像生成AIに送るプロンプトを考えてください。
背景や小物のディテイール、構図、視覚効果など視覚的な情報のみに言及すること。
その上で長文を日本語と英語で返信してください。
返答は以下のフォーマットのjson形式でのみ行う。json以外の内容をレスポンスに含めないこと。また、出力されるjsonには改行コード(\\n)や空白は含めないこと。
\`\`\`json
{
"results": [
{
"language": "en",
"text": ${textFormat} # ${document.getElementById('characterCount').value}文字程度,
},
{
"language": "danbooru",
"tags": [str],
},
{
"language": "ja",
"text": ${textFormat}
}${anotherLanguage}
]
}
\`\`\`
`;
const url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-exp-0827:generateContent?key=" + document.getElementById('apiKey').value;
const payload = {
contents: [
{
parts: [
{ text: text }
]
}
],
generation_config: {
max_output_tokens: 4095,
temperature: 1,
top_p: 1,
top_k: 32
},
safetySettings: [
{
category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
threshold: "BLOCK_NONE"
}
]
};
console.debug(text);
// fetchを使用してリクエストを送信
// ローディングアイコンを表示
document.getElementById('loading').classList.remove('d-none');
// generatePromptボタンを無効化
document.getElementById('generatePromptButton').disabled = true;
document.getElementById('generatePromptButton').classList.remove('btn-primary');
document.getElementById('generatePromptButton').classList.remove('btn-danger');
document.getElementById('generatePromptButton').classList.add('btn-secondary');
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
console.debug(data.candidates[0].content);
// レスポンスからテキストを抽出
const responseText = data.candidates[0].content.parts[0].text
console.debug(responseText);
let jsonString = responseText.replace(/```json|```/g, '').trim();
jsonString = jsonString.replace(/\\n/g, '');
console.debug(jsonString);
// JSONをパース
const parsedData = JSON5.parse(jsonString);
// 結果を表示
console.debug(parsedData);
let promptEn = parsedData.results.find(x => x.language === 'en').text;
let promptMyLanguage = parsedData.results.find(x => x.language === i18next.language).text;
[promptEn, promptMyLanguage] = [promptEn, promptMyLanguage].map(x => {
return Array.isArray(x) ? x.join("") : x;
});
document.getElementById('promptEn').value = promptEn.replace(/\. ?/g, '.\n\n');
document.getElementById('promptEn').value = document.getElementById('promptEn').value.replace(/^ */g, '');
document.getElementById('promptMyLanguage').value = promptMyLanguage.replace(/\。 ?/g, '。\n\n');
let danbooruTags = parsedData.results.find(x => x.language === 'danbooru');
if(danbooruTags.tags){
danbooruTags = danbooruTags.tags.map(x => x.replace("_", " "));
document.getElementById('danbooruTags').value = danbooruTags.join(", ");
}
if(danbooruTags.text){
danbooruTags = danbooruTags.text.map(x => x.replace("_", " "));
document.getElementById('danbooruTags').value = danbooruTags.join(", ");
}
// ローディングアイコンを非表示
document.getElementById('loading').classList.add('d-none');
document.getElementById('generatePromptButton').disabled = false;
document.getElementById('generatePromptButton').classList.remove('btn-secondary');
document.getElementById('generatePromptButton').classList.add('btn-primary');
saveToUserStorage(true);
})
.catch(error => {
console.error(error);
// エラー時もローディングアイコンを非表示
document.getElementById('loading').classList.add('d-none');
document.getElementById('generatePromptButton').disabled = false;
document.getElementById('generatePromptButton').classList.remove('btn-secondary');
document.getElementById('generatePromptButton').classList.add('btn-danger');
});
};
let lastSaveTimestamp = new Date();
function saveToUserStorage(force = false) {
const currentTime = new Date();
if (!force && currentTime - lastSaveTimestamp < 5000) {
return;
}
const data = {};
document.querySelectorAll('input, textarea').forEach(input => {
data[input.id] = input.value;
});
localStorage.setItem('gemini_prompt', JSON.stringify(data));
lastSaveTimestamp = currentTime;
return true;
}
function loadFromUserStorage() {
const data = JSON.parse(localStorage.getItem('gemini_prompt')) || {};
document.querySelectorAll('input, textarea').forEach(input => {
let v = data[input.id] || "";
if (v) {
if (input.type === "number") {
v = parseInt(v);
}
input.value = v;
}
});
}
loadFromUserStorage();
// 60秒ごとに自動保存を実行
setInterval(() => {
saveToUserStorage();
}, 60000);
document.querySelectorAll('input, textarea').forEach(input => {
input.addEventListener('input', saveToUserStorage);
});
// Ctrl+Enterでプロンプト生成を実行する
document.addEventListener('keydown', function (event) {
if (event.ctrlKey && event.key === 'Enter') {
event.preventDefault(); // デフォルトの動作を防ぐ
generatePrompt(); // プロンプト生成関数を呼び出す
}
});
</script>
</body>
</html>