junkerstock
 画像プロンプト生成機 (簡素版) 

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Prompt Dashboard</title>
<style>
/* ベーススタイル */
body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Hiragino Kaku Gothic ProN", "Hiragino Sans", Arial, sans-serif;
padding: 20px;
background-color: #f4f6f9;
color: #333;
height: 100vh;
box-sizing: border-box;
overflow: hidden; /* 一画面に収めるためスクロール抑制 */
}

/* メインコンテナ */
.dashboard-container {
display: grid;
grid-template-columns: 260px 1fr; /* 左サイドバー固定、右可変 */
gap: 15px;
max-width: 1000px;
margin: 0 auto;
height: calc(100vh - 40px); /* 上下パディング分を引く */
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
overflow: hidden;
}

/* 共通パネルスタイル */
.panel { padding: 15px; overflow-y: auto; }

/* 左パネル(設定エリア) */
.left-panel {
background-color: #fafbfc;
border-right: 1px solid #e1e4e8;
display: flex;
flex-direction: column;
}

/* 右パネル(エディタエリア) */
.right-panel {
display: flex;
flex-direction: column;
gap: 10px;
padding-right: 20px;
}

/* 数値設定グリッド */
.settings-list {
display: flex;
flex-direction: column;
gap: 8px;
flex-grow: 1; /* ボタンを下に押しやる */
margin-bottom: 10px;
}
.setting-item {
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
border: 1px solid #ddd;
padding: 6px 10px;
border-radius: 6px;
}
.setting-item label {
font-size: 0.85em;
font-weight: bold;
color: #555;
cursor: pointer;
}
.setting-item input[type="number"] {
width: 50px;
padding: 4px;
text-align: center;
font-size: 1em;
border: 1px solid #ccc;
border-radius: 4px;
}

/* ボタン類のデザイン */
button { cursor: pointer; border: none; border-radius: 6px; font-weight: bold; transition: opacity 0.2s; }
button:hover { opacity: 0.9; }

.random-btn {
width: 100%;
padding: 12px;
font-size: 0.95em;
color: #fff;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}

/* 右側の各要素 */
.history-bar {
display: flex;
gap: 5px;
}
.history-select { flex-grow: 1; padding: 6px; border: 1px solid #ddd; border-radius: 4px; font-size: 0.85em; }
.clear-btn { font-size: 0.75em; color: #d9534f; background: #fff; border: 1px solid #d9534f; padding: 0 8px; }

.prompt-input {
width: 100%; padding: 10px; font-size: 1em; border: 1px solid #ccc; border-radius: 6px; box-sizing: border-box;
}
.prompt-input:focus, textarea:focus, select:focus {
outline: none; border-color: #007bff; box-shadow: 0 0 0 2px rgba(0,123,255,0.1);
}

textarea {
flex-grow: 1; /* 余った高さを全て使う */
width: 100%;
padding: 10px;
font-size: 0.95em;
line-height: 1.6;
border: 1px solid #ccc;
border-radius: 6px;
resize: none;
box-sizing: border-box;
}

.action-area {
display: flex;
gap: 8px;
background: #f8f9fa;
padding: 10px;
border-radius: 8px;
border: 1px solid #eee;
}
#aiSelector { padding: 8px; border: 1px solid #ccc; border-radius: 6px; flex-grow: 1; }

.copy-btn { padding: 0 20px; background-color: #28a745; color: white; white-space: nowrap; }
.send-btn { padding: 0 20px; background-color: #007bff; color: white; white-space: nowrap; }

</style>
</head>
<body>

<div class="dashboard-container">
<div class="panel left-panel">
<div class="settings-list">
<div class="setting-item"><label for="n_sf_bg">SF背景</label><input type="number" id="n_sf_bg" value="1" min="0"></div>
<div class="setting-item"><label for="n_sf_obj">SF物体</label><input type="number" id="n_sf_obj" value="0" min="0"></div>
<div class="setting-item"><label for="n_fantasy_bg">ファンタジー背景</label><input type="number" id="n_fantasy_bg" value="0" min="0"></div>
<div class="setting-item"><label for="n_fantasy_obj">ファンタジー物体</label><input type="number" id="n_fantasy_obj" value="0" min="0"></div>
<div class="setting-item"><label for="n_nature_bg">自然背景</label><input type="number" id="n_nature_bg" value="0" min="0"></div>
<div class="setting-item"><label for="n_nature_obj">自然物体</label><input type="number" id="n_nature_obj" value="0" min="0"></div>
<div class="setting-item"><label for="n_time">時間</label><input type="number" id="n_time" value="1" min="0"></div>
<div class="setting-item"><label for="n_weather">天候</label><input type="number" id="n_weather" value="1" min="0"></div>
<div class="setting-item"><label for="n_atm_bright">明るい雰囲気</label><input type="number" id="n_atm_bright" value="0" min="0"></div>
<div class="setting-item"><label for="n_atm_dark">暗い雰囲気</label><input type="number" id="n_atm_dark" value="0" min="0"></div>
</div>
<button class="random-btn" onclick="generateRandomPrompt()">🎲 設定値で生成</button>
</div>

<div class="panel right-panel">
<div class="history-bar">
<select id="historySelect" class="history-select" onchange="restoreHistory()">
<option value="" disabled selected>履歴...</option>
</select>
<button type="button" class="clear-btn" onclick="clearHistory()">削除</button>
</div>

<input type="text" id="instructionInput" class="prompt-input" value="360度パノラマ画像を作って。">

<textarea id="detailArea" placeholder="ここに生成結果が表示されます..."></textarea>

<div class="action-area">
<select id="aiSelector">
<option value="gemini">Google Gemini</option>
<option value="bing">Bing Image Creator</option>
<option value="firefly">Adobe Firefly</option>
<option value="chatgpt">ChatGPT (DALL-E 3)</option>
<option value="claude">Claude</option>
<option value="perplexity">Perplexity</option>
</select>
<button class="copy-btn" onclick="handleAction(false)">コピー</button>
<button class="send-btn" onclick="handleAction(true)">開始</button>
</div>
</div>
</div>

<script>
// ---------------------------------------------------------
// ■ データ定義 (変更なし)
// ---------------------------------------------------------
const promptData = {
sf_bg: [
"近未来都市の摩天楼", "浮遊する空中都市", "ディストピアな地下都市", "ホログラム広告が溢れる繁華街",
"海底に沈んだ未来都市の廃墟", "植物と機械が融合したソーラーパンクな庭園", "二つの月が浮かぶ異星の空",
"アステロイドベルトの採掘基地", "ブラックホールの事象の地平線", "未知の惑星の原生林", "氷の惑星の秘密基地",
"デジタル雨が降る街", "発光するキノコの森", "磁気嵐の吹き荒れる荒野", "垂直農場の緑",
"シールド越しに見る超新星爆発", "テラフォーミングされた惑星の地表", "惑星直列の夜", "バイオルミネセンスの海",
"月面クレーターの影", "異次元への裂け目", "スペースコロニーの円筒内部", "酸性雨が降り注ぐ工業地帯",
"クリスタルで覆われた渓谷", "人工太陽に照らされた巨大地下空洞", "オーロラが輝く極地の氷原", "雲海に突き出る尖塔群",
"リングワールドの地平線", "中性子星の磁場", "データストリームが流れる電脳空間", "廃棄された宇宙船の墓場",
"大気が燃える惑星の空", "重力が崩壊した破片のフィールド", "ナノマシン構成体でできた砂漠", "無限に続くサーバーファーム",
"赤い矮星に照らされた荒野", "水没した巨大データセンター", "成層圏プラットフォームからの眺め", "オニール・シリンダーの農村エリア",
"レトロフューチャーな真空管都市", "化学汚染された極彩色の沼地", "結晶化した植物の森", "恒星のコロナ",
"ワームホールの入り口", "銀河中心の超高密度星団", "軌道エレベーターのケーブルが見える空", "反重力で浮く岩山群",
"鏡面加工された金属の大地", "巨大なパイプラインが走る荒野", "スラム化した高層建築の谷間", "人工知能が設計した幾何学的な都市",
"厚い氷の下に広がる海", "メタンの海とオレンジの空", "彗星の尾の中", "崩壊する月を見上げる地表",
"巨大なファンが回る換気口内部", "放棄されたテラフォーミング施設", "無数の培養ポッドが並ぶ巨大プラント",
"サイバー空間のグリッド", "錆びた鉄屑の山脈", "雲を突き抜ける巨大なダム", "軌道上から見た夜の地球",
"巨大な歯車が噛み合う機械都市", "プラズマの滝", "空中ハイウェイのジャンクション", "人工的な多層構造の洞窟",
"放射能で変異したジャングル", "氷河期に入った未来の都市", "砂漠に埋もれた古代のランドマーク", "巨大な眼のような星雲",
"アクリルのような透明な海", "幾何学的なクリスタルの平原", "巨大な送電塔が続く風景", "ドローンが飛び交う配送センター",
"レーザー光線が交差する戦場", "惑星リングの影が落ちる場所", "有毒な霧が立ち込めるスラム", "巨大なホログラムクジラが泳ぐ空",
"ガラス張りの海底トンネル", "雲の上に建つ白亜の塔", "巨大なパラボラアンテナの森", "人工降雪機が動くスキーリゾート",
"溶岩をエネルギーに変える発電所", "宇宙ゴミの帯", "異星の古代遺跡と二重太陽", "巨大なモノリスが並ぶ海岸",
"紫色の草が生い茂る平原", "巨大な菌糸ネットワークの森", "常に夜の都市", "空中庭園の廃墟", "ロボットだけが住む街",
"巨大なエネルギーシールドに守られたドーム", "重金属の雨が降る惑星", "光ファイバーの森", "量子もつれの視覚化空間",
"崩壊したスペースコロニーの外壁", "無限回廊", "鏡の世界", "巨大な配管が密集する路地", "スチームが噴き出す工場地帯",
"アンドロイドの廃棄場", "巨大なクレーター湖", "六角形の柱状節理の惑星", "浮遊する水の球体がある空間",
"ソーラーセイル越しに見る星の海", "虹色のガスが漂う星雲","マイクラのようなブロックでできた町"

],
sf_obj: [
"火星の居住ドーム", "ダイソン球の巨大構造物", "ガス惑星の軌道プラットフォーム", "銀河を背景にした宇宙艦隊",
"巨大な恒星間移民船", "錆びついた巨大ロボットの残骸", "空を覆う巨大な宣伝飛行船", "宇宙を泳ぐ巨大生物",
"要塞化した移動都市", "砂漠に埋もれた宇宙船", "多層構造のハイウェイ", "エネルギー採掘リグ",
"未来のスポーツスタジアム", "軌道エレベーターのアンカー", "古代文明の黒いモノリス", "大気を浄化する巨大プラント",
"惑星を囲む巨大なリング", "墜落した軍事衛星", "成層圏まで伸びる世界樹", "シンギュラリティの塔", "巨大な螺旋構造物"
],
fantasy_bg: [
"古代遺跡が眠る密林", "クリスタルの洞窟", "妖精が住む光る森", "中世ヨーロッパ風の城下町",
"エルフの聖なる泉", "霧深い沼地", "空飛ぶ島々", "薄暗い地下ダンジョンの通路",
"グリフォンが飛び交う渓谷", "灼熱の溶岩地帯", "オーロラが輝く雪原", "巨大なキノコの森",
"精霊が集まる湖畔", "虹の架かる滝", "星降る夜の野営地", "ランタンの灯る石畳の道",
"呪われた森", "マナの奔流", "迷いの森", "オアシスの蜃気楼", "夜光虫の海", "神々の黄昏",
"彼岸花が咲き乱れる冥界の河原", "巨人の骨が散らばる荒野", "重力が歪んだ魔法地帯", "天まで届く大瀑布",
"雲海に浮かぶ天空の城郭", "朽ち果てた剣が無数に刺さる荒野(剣の墓場)", "サンゴでできた地上の森",
"水没した古代都市", "永遠に夜が続く吸血鬼の国", "ステンドグラスのような空の下", "巨大樹の枝の上に広がる村",
"見渡す限りのクリスタル砂漠", "竜巻が常に吹いている平原", "空中に浮遊する水の球体群", "黄金色に輝く麦畑と風車",
"紫色の霧に包まれた毒の沼", "ドワーフの巨大な地下採掘場", "本棚が地平線まで続く無限図書館", "鏡のような水面を持つ塩の湖",
"イバラに覆われた眠れる城の庭園", "火山灰が降り積もる灰色の世界", "星座が地上に描かれた魔法陣の広場",
"巨大な鎖で繋がれた浮遊大陸", "氷の華が咲き乱れる凍土", "七色の川が流れる谷", "ユニコーンが駆ける草原",
"月明かりに照らされた廃教会", "歯車と蒸気が動くドワーフの都", "精霊の光が舞う地下湖", "巨大な蓮の葉が浮く池",
"灼熱の砂漠にある氷のオアシス", "雷雲が渦巻くドラゴンの領域", "ガラスの花が咲く透明な草原", "時が止まったモノクロの世界",
"巨大な化石の中にある村", "虹の橋がかかる雲の上", "深海にある人魚の王国(広域)", "輝く鉱石が露出した峡谷",
"空から海へ水が落ちる世界の果て", "巨大なキノコの傘の上", "幽霊船が漂う霧の海", "五色の炎が燃える祭壇",
"古代文字が刻まれた石柱の森", "空飛ぶクジラの群れが見える空", "巨大な砂時計がある砂漠", "飴細工でできたお菓子の国",
"カボチャ畑が広がるハロウィンの里", "雪の中に温泉が湧く秘境", "大理石でできた白亜の迷宮", "巨大なランタンが浮く夜の祭",
"マンドラゴラが自生する奇妙な畑", "星の欠片が落ちている海岸", "巨大な本が開かれたような地形", "インクで描かれたような水墨画の世界",
"チェス盤のような白黒の大地", "トランプが舞う不思議の国", "煉獄の炎が燃える断崖", "天国へと続く長い階段",
"神殿の回廊", "地下深くに広がる発光苔の洞窟", "ペガサスの羽が舞う山頂", "巨大な水晶クラスターの谷",
"錬金術の廃液が流れる極彩色の川", "ルーン文字が光る黒い岩肌", "天空から鎖で吊るされた牢獄", "魔力が結晶化した森",
"ドライアドが住む新緑の森", "セイレーンが歌う岩礁地帯", "巨大な貝殻が点在する砂浜", "スライムが大量発生している草原",
"火の粉が舞う鍛冶の町", "風の精霊が通り抜ける風穴", "土の精霊が守る棚田", "光の精霊が住むプリズムの谷",
"闇の精霊が潜む影の谷", "異次元と繋がる亀裂のある空", "古代の戦争の跡が残るクレーター",
"神獣が眠る巨大な祠の前","マイクラのようなブロックでできた町",
"雲海の上に並ぶ大量の古代の塔"

],
fantasy_obj: [
"雲の上の神殿", "ドラゴンの巣がある火山", "ドワーフの地下要塞", "世界樹の根本", "満月の夜の古城",
"伝説の聖剣が刺さった岩", "召喚魔法陣と光の柱", "人魚が泳ぐサンゴ礁の宮殿", "氷の女王が住む水晶の城",
"砂漠に半分埋もれた巨像", "廃墟となった神殿", "雷雨の中の塔", "不動の巨大ゴーレム", "オークの砦",
"空飛ぶ帆船", "海賊船とクラーケン", "ツタの絡まる石橋", "ステンドグラスの大聖堂", "異界へのゲート",
"カボチャの馬車", "森の奥のお菓子の家", "雲を突き抜ける豆の木", "深海の沈没船", "天空の回廊",
"女神の彫像", "英雄の記念碑", "動く城", "バベルの塔のような未完の巨塔", "剣の墓場", "竜巻の中に建つ魔術師の塔"
],
nature_bg: [
"静かな湖畔のキャンプ場", "オーロラが見える雪原", "サンゴ礁が広がる海底", "桜が満開の並木道",
"紅葉が美しい日本庭園", "広大なサバンナ", "霧の立ち込める海岸", "険しい山岳地帯",
"エメラルドグリーンの透き通る海", "波打ち際の白い砂浜", "木漏れ日が差し込む深い森",
"満天の星空と天の川", "燃えるような夕焼け空", "入道雲が湧き上がる夏の空", "一面に広がるひまわり畑",
"ラベンダー畑の紫の絨毯", "雪を頂いた壮大な山脈", "熱帯雨林のジャングル", "苔むした岩と清流",
"砂丘に描かれた風紋", "マングローブの林", "竹林の小径", "雲海に浮かぶ山頂", "岩肌が露出した荒涼とした大地",
"新緑のブナ林", "風に揺れる黄金色の麦畑", "色とりどりの高山植物の群生", "薄明光線が降り注ぐ谷",
"月明かりに照らされた海", "紅葉で真っ赤に染まる山肌", "落ち葉の絨毯", "秋のすすき野原",
"野生の馬が走る草原", "ホタルが飛び交う小川", "イチョウ並木のトンネル", "ブドウ畑の丘",
"サボテンの生える砂漠", "霧氷のついた枝", "夜明け前の蒼い世界", "湿原の木道", "菜の花畑とローカル線",
"棚田の夕暮れ", "茶畑の幾何学模様", "塩湖の鏡張り", "バオバブの並木道", "入江の漁村", "嵐の海",
"雪解け水が流れる川", "水芭蕉の群生地", "ネモフィラの青い丘", "コスモス畑", "彼岸花が咲く畦道",
"樹氷の森", "流氷の海", "リアス式海岸", "干潟の夕景", "朝靄に包まれた牧場", "サトウキビ畑", "パイナップル畑",
"五色の石が転がる河原", "真っ白な石灰岩の棚田", "コバルトブルーの温泉地帯", "極夜の氷原",
"雲海の上に並ぶ大量の古代の塔","雲海"

],
nature_obj: [
"グランドキャニオンのような渓谷", "大迫力の巨大な滝", "水面に映る逆さ富士", "鍾乳洞の神秘的な空間",
"噴煙を上げる火山", "切り立ったフィヨルド", "カルスト台地の奇岩", "ダブルレインボー", "皆既日食の瞬間",
"流れ星が降り注ぐ夜", "紫色の雷光", "桜吹雪が舞う川沿い", "銀世界に佇む一本松", "凍りついた滝",
"渡り鳥の群れ", "クジラが泳ぐ大海原", "藤棚の下", "蜃気楼が見える地平線", "水平線に沈む太陽",
"青の洞窟", "孤島のリゾート", "氷河の崩落", "オアシスの泉", "間欠泉の噴出", "セコイアの巨木",
"断崖絶壁の灯台", "火口湖のエメラルドグリーン", "渓谷の吊り橋", "風車のある風景", "水車小屋と小川",
"蓮の花が咲く池", "テーブルマウンテンのような台地", "柱状節理の断崖", "巨大な一枚岩(モノリス)",
"ハートの形をした浮島", "古代杉の切り株", "竜の背のような岩礁"
],
time: [
"夜明け前", "早朝", "日の出", "朝", "午前", "正午", "真昼", "午後",
"夕方", "夕暮れ", "日没", "黄昏時", "マジックアワー", "ブルーアワー",
"夜", "真夜中", "深夜", "丑三つ時"
],
weather: [
"快晴", "青空", "曇り", "霧", "濃霧", "朝霧",
"雨", "小雨", "霧雨", "天気雨", "土砂降り", "豪雨", "雷雨", "嵐", "台風",
"雪", "吹雪", "雹", "ダイヤモンドダスト",
"虹", "天使の梯子", "蜃気楼", "花吹雪", "オーロラ",
"日食", "月食", "満月", "新月", "星空", "流星群", "天の川"
],
atm_bright: [
"神々しく幻想的な光", "活気に満ち溢れている", "夢の中にいるような感覚", "昭和レトロな暖かい雰囲気",
"80年代のポップな色彩", "ヴェイパーウェイヴのような幻想的な空間", "希望に満ちた明るい未来感",
"幸福感に包まれた優しい世界", "エネルギッシュで力強い躍動感", "清涼感のある爽やかな風",
"祝祭のような賑やかな喧騒", "魔法がかかったようなキラキラした空気", "アニメーションのような鮮やかな色彩",
"ロマンチックで甘い雰囲気", "夢心地のふわふわした感覚", "おとぎ話のようなメルヘンチックな世界",
"パステルカラーの可愛らしい空間", "洗練されたラグジュアリーな空間", "ボタニカルで癒やされる空間",
"近未来的なスタイリッシュさ", "壮大な冒険の予感", "勝利のファンファーレが聞こえる高揚感",
"トロピカルで開放的な空気", "コメディタッチの陽気な世界", "聖なる祝福に満ちたオーラ"
],
atm_dark: [
"ノスタルジックなセピア色の世界", "色あせた古い写真のような質感", "退廃的で不気味な空気感",
"孤独で寂しい雰囲気", "緊張感のある張り詰めた空気", "ゾクッとするような恐怖感",
"狂気に満ちたサイケデリックな空間", "ゴシックで重厚な空気", "絶望的な終末感", "闇に飲み込まれそうな気配",
"神秘的で厳かな静寂", "異次元のような浮遊感", "幽玄で儚い美しさ", "古代の神話のような荘厳さ",
"映画のワンシーンのようなドラマチックな構図", "孤独で静謐な時間", "世界から取り残されたような寂しさ",
"音のない静止した世界", "哀愁漂う夕暮れの空気", "冷たく無機質な質感", "ハードボイルドで渋い世界観",
"鉄と油の匂いがする無骨な雰囲気", "戦場のような張り詰めた緊張感", "スチームパンクな蒸気の煙る空気",
"ミニマルで整然とした美しさ", "カオスで雑多なエネルギー", "瞑想的な深い精神世界",
"記憶の断片のような曖昧なイメージ", "クリスタルのように透き通った空気", "嵐の前の不穏な静けさ",
"白昼夢のような非現実感", "インダストリアルな荒廃感", "監獄のような息苦しい閉塞感",
"呪われた不浄な空気", "深淵から見つめられている気配", "電子ドラッグのような陶酔感"
]
};

// ---------------------------------------------------------
// ■ 履歴管理機能 (Object保存版)
// ---------------------------------------------------------
const STORAGE_KEY = 'ai_image_full_history_v6';

window.onload = function() { loadHistory(); };

// 履歴には「指示(instruction)」と「詳細(details)」の両方を保存
function saveToHistory(inst, det) {
if (!inst && !det) return;

let history = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];

// 同じ組み合わせがあれば削除して先頭へ (重複回避)
const newItem = { instruction: inst, details: det };
const jsonItem = JSON.stringify(newItem);

// 既存の履歴から同一内容を探して削除(単純な文字比較)
history = history.filter(item => JSON.stringify(item) !== jsonItem);

// 先頭に追加
history.unshift(newItem);

// 最大20件まで
if (history.length > 20) history.pop();

localStorage.setItem(STORAGE_KEY, JSON.stringify(history));
loadHistory();
}

function loadHistory() {
const historySelect = document.getElementById('historySelect');
const history = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];

historySelect.innerHTML = '<option value="" disabled selected>履歴...</option>';

history.forEach((item, index) => {
const option = document.createElement('option');
// valueには配列のインデックスを入れる
option.value = index;

// 表示名は「指示 + 詳細」の一部を表示
let displayText = `[${item.instruction}] ${item.details.replace(/\n/g, ' ')}`;
if (displayText.length > 40) displayText = displayText.substring(0, 40) + "...";

option.text = displayText;
historySelect.appendChild(option);
});
}

function restoreHistory() {
const historySelect = document.getElementById('historySelect');
const index = historySelect.value;
const history = JSON.parse(localStorage.getItem(STORAGE_KEY)) || [];

if (history[index]) {
document.getElementById('instructionInput').value = history[index].instruction;
document.getElementById('detailArea').value = history[index].details;
}
}

function clearHistory() {
if(confirm("履歴をすべて削除しますか?")) {
localStorage.removeItem(STORAGE_KEY);
loadHistory();
}
}

// ---------------------------------------------------------
// ■ ランダム生成機能
// ---------------------------------------------------------
function generateRandomPrompt() {
const detailArea = document.getElementById('detailArea');
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];

let allParts = [];

// UIのIDとデータキーの紐付け
const config = [
{ id: 'n_sf_bg', data: promptData.sf_bg },
{ id: 'n_sf_obj', data: promptData.sf_obj },
{ id: 'n_fantasy_bg', data: promptData.fantasy_bg },
{ id: 'n_fantasy_obj',data: promptData.fantasy_obj },
{ id: 'n_nature_bg', data: promptData.nature_bg },
{ id: 'n_nature_obj', data: promptData.nature_obj },
{ id: 'n_time', data: promptData.time },
{ id: 'n_weather', data: promptData.weather },
{ id: 'n_atm_bright', data: promptData.atm_bright },
{ id: 'n_atm_dark', data: promptData.atm_dark }
];

config.forEach(item => {
const count = parseInt(document.getElementById(item.id).value) || 0;
for(let i = 0; i < count; i++) {
allParts.push(pick(item.data));
}
});

if (allParts.length === 0) {
detailArea.value = "(要素が選択されていません。数値を1以上に設定してください)";
return;
}

// テキストエリア上では見やすく改行を残す
detailArea.value = allParts.join("。\n") + "。";

// エフェクト
detailArea.style.borderColor = "#007bff";
setTimeout(() => detailArea.style.borderColor = "#ccc", 300);
}


// ---------------------------------------------------------
// ■ アクション処理 (1行化 & 保存 & コピー/起動)
// ---------------------------------------------------------
function handleAction(isLaunch) {
const instruction = document.getElementById('instructionInput').value.trim();
const details = document.getElementById('detailArea').value.trim();

if (!instruction || !details) {
alert("指示と詳細の両方が必要です。");
return;
}

// 1. 履歴に保存 (元の改行ありの状態で保存)
saveToHistory(instruction, details);

// 2. 1行化処理 (改行をスペースに変換)
// 指示 + スペース + 詳細 (詳細内の改行もスペース化)
const oneLineDetails = details.replace(/[\r\n]+/g, ' ');
const finalPrompt = `${instruction} ${oneLineDetails}`;

// 3. コピー or 起動
if (navigator.clipboard) {
navigator.clipboard.writeText(finalPrompt).then(() => {
if (!isLaunch) {
alert("1行にしてコピーしました!\n履歴にも保存されました。");
} else {
launchService(finalPrompt);
}
}).catch(err => {
alert("コピーに失敗しました。");
});
} else {
// クリップボードAPI非対応ブラウザ用
if (!isLaunch) {
alert("このブラウザではコピー機能が制限されています。");
} else {
launchService(finalPrompt);
}
}
}

function launchService(promptText) {
const aiService = document.getElementById('aiSelector').value;
const encodedPrompt = encodeURIComponent(promptText);
let url = "";

switch (aiService) {
case "bing": url = "https://www.bing.com/images/create"; break;
case "firefly": url = "https://firefly.adobe.com/upload/text_to_image"; break;
case "gemini": url = "https://gemini.google.com/app"; break;
case "chatgpt": url = "https://chatgpt.com/"; break;
case "claude": url = "https://claude.ai/new"; break;
case "perplexity": url = `https://www.perplexity.ai/search?q=${encodedPrompt}`; break;
}

if (aiService === 'perplexity') {
window.open(url, '_blank');
} else {
let serviceName = document.getElementById('aiSelector').options[document.getElementById('aiSelector').selectedIndex].text;
let msg = "プロンプトを1行にしてコピーしました!\n\n" + serviceName + "を別タブで開きますか?";

if(confirm(msg)) {
window.open(url, '_blank');
}
}
}
</script>

</body>
</html>


使用変数

aiService
allParts
borderColor
charset
class
clearHistory -------( Function )
config
count
detailArea
details
displayText
encodedPrompt
err
finalPrompt
for
generateRandomPrompt -------( Function )
handleAction -------( Function )
history
historySelect
i
id
index
innerHTML
instruction
item
jsonItem
lang
launchService -------( Function )
length
loadHistory -------( Function )
min
msg
newItem
onchange
onclick
oneLineDetails
onload
option
pick
placeholder
promptData
q
restoreHistory -------( Function )
saveToHistory -------( Function )
serviceName
STORAGE_KEY
text
type
url
value