Spaces:
Runtime error
Runtime error
Upload 3 files
Browse files- async_storage_manager.py +54 -9
- components_chat_interface.py +15 -1
- main_app.py +58 -27
async_storage_manager.py
CHANGED
|
@@ -35,6 +35,10 @@ class AsyncStorageManager:
|
|
| 35 |
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
| 36 |
self.backup_dir.mkdir(parents=True, exist_ok=True)
|
| 37 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
# 初期データ構造
|
| 39 |
self.default_data = {
|
| 40 |
"users": {},
|
|
@@ -45,20 +49,30 @@ class AsyncStorageManager:
|
|
| 45 |
}
|
| 46 |
}
|
| 47 |
|
| 48 |
-
async def load_data(self) -> Dict[str, Any]:
|
| 49 |
-
"""
|
| 50 |
async with self.lock:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
try:
|
| 52 |
if not self.file_path.exists():
|
| 53 |
-
|
|
|
|
| 54 |
await self._save_data_unsafe(self.default_data)
|
| 55 |
-
|
|
|
|
|
|
|
| 56 |
|
| 57 |
# ファイルサイズチェック
|
| 58 |
if self.file_path.stat().st_size == 0:
|
| 59 |
-
|
|
|
|
| 60 |
await self._save_data_unsafe(self.default_data)
|
| 61 |
-
|
|
|
|
|
|
|
| 62 |
|
| 63 |
# JSONファイルの読み込み
|
| 64 |
with open(self.file_path, 'r', encoding='utf-8') as f:
|
|
@@ -67,7 +81,14 @@ class AsyncStorageManager:
|
|
| 67 |
# データ構造の検証と修復
|
| 68 |
data = self._validate_and_repair_data(data)
|
| 69 |
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
return data
|
| 72 |
|
| 73 |
except json.JSONDecodeError as e:
|
|
@@ -83,6 +104,9 @@ class AsyncStorageManager:
|
|
| 83 |
"""データファイルに保存"""
|
| 84 |
async with self.lock:
|
| 85 |
await self._save_data_unsafe(data)
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
async def _save_data_unsafe(self, data: Dict[str, Any]) -> None:
|
| 88 |
"""ロックなしでデータを保存(内部使用)"""
|
|
@@ -99,7 +123,11 @@ class AsyncStorageManager:
|
|
| 99 |
# アトミックな移動
|
| 100 |
shutil.move(str(temp_path), str(self.file_path))
|
| 101 |
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
|
| 104 |
except Exception as e:
|
| 105 |
logger.error(f"データ保存エラー: {e}")
|
|
@@ -199,13 +227,20 @@ class AsyncStorageManager:
|
|
| 199 |
# 復旧したデータを保存
|
| 200 |
await self._save_data_unsafe(data)
|
| 201 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 202 |
return data
|
| 203 |
|
| 204 |
except Exception as e:
|
| 205 |
logger.error(f"バックアップからの復旧に失敗: {e}")
|
| 206 |
logger.info("初期データを使用します")
|
| 207 |
await self._save_data_unsafe(self.default_data)
|
| 208 |
-
|
|
|
|
|
|
|
|
|
|
| 209 |
|
| 210 |
async def _cleanup_old_backups(self, days: int = 7) -> None:
|
| 211 |
"""古いバックアップファイルを削除"""
|
|
@@ -362,6 +397,16 @@ class AsyncStorageManager:
|
|
| 362 |
except Exception as e:
|
| 363 |
logger.error(f"統計情報の取得エラー: {e}")
|
| 364 |
return {}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
|
| 366 |
|
| 367 |
# テスト用の関数
|
|
|
|
| 35 |
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
| 36 |
self.backup_dir.mkdir(parents=True, exist_ok=True)
|
| 37 |
|
| 38 |
+
# データキャッシュ(重複読み込み防止)
|
| 39 |
+
self._cached_data = None
|
| 40 |
+
self._data_loaded = False
|
| 41 |
+
|
| 42 |
# 初期データ構造
|
| 43 |
self.default_data = {
|
| 44 |
"users": {},
|
|
|
|
| 49 |
}
|
| 50 |
}
|
| 51 |
|
| 52 |
+
async def load_data(self, force_reload: bool = False) -> Dict[str, Any]:
|
| 53 |
+
"""データファイルを読み込み(キャッシュ機能付き)"""
|
| 54 |
async with self.lock:
|
| 55 |
+
# キャッシュされたデータがあり、強制リロードでない場合はキャッシュを返す
|
| 56 |
+
if self._data_loaded and self._cached_data is not None and not force_reload:
|
| 57 |
+
return self._cached_data.copy()
|
| 58 |
+
|
| 59 |
try:
|
| 60 |
if not self.file_path.exists():
|
| 61 |
+
if not self._data_loaded: # 初回のみログ出力
|
| 62 |
+
logger.debug("データファイルが存在しないため、初期データを作成します")
|
| 63 |
await self._save_data_unsafe(self.default_data)
|
| 64 |
+
self._cached_data = self.default_data.copy()
|
| 65 |
+
self._data_loaded = True
|
| 66 |
+
return self._cached_data.copy()
|
| 67 |
|
| 68 |
# ファイルサイズチェック
|
| 69 |
if self.file_path.stat().st_size == 0:
|
| 70 |
+
if not self._data_loaded: # 初回のみログ出力
|
| 71 |
+
logger.warning("データファイルが空のため、初期データを作成します")
|
| 72 |
await self._save_data_unsafe(self.default_data)
|
| 73 |
+
self._cached_data = self.default_data.copy()
|
| 74 |
+
self._data_loaded = True
|
| 75 |
+
return self._cached_data.copy()
|
| 76 |
|
| 77 |
# JSONファイルの読み込み
|
| 78 |
with open(self.file_path, 'r', encoding='utf-8') as f:
|
|
|
|
| 81 |
# データ構造の検証と修復
|
| 82 |
data = self._validate_and_repair_data(data)
|
| 83 |
|
| 84 |
+
# キャッシュを更新
|
| 85 |
+
self._cached_data = data.copy()
|
| 86 |
+
|
| 87 |
+
# 初回のみログ出力
|
| 88 |
+
if not self._data_loaded:
|
| 89 |
+
logger.debug(f"データファイルを正常に読み込みました: {self.file_path}")
|
| 90 |
+
self._data_loaded = True
|
| 91 |
+
|
| 92 |
return data
|
| 93 |
|
| 94 |
except json.JSONDecodeError as e:
|
|
|
|
| 104 |
"""データファイルに保存"""
|
| 105 |
async with self.lock:
|
| 106 |
await self._save_data_unsafe(data)
|
| 107 |
+
# キャッシュを更新
|
| 108 |
+
self._cached_data = data.copy()
|
| 109 |
+
self._data_loaded = True
|
| 110 |
|
| 111 |
async def _save_data_unsafe(self, data: Dict[str, Any]) -> None:
|
| 112 |
"""ロックなしでデータを保存(内部使用)"""
|
|
|
|
| 123 |
# アトミックな移動
|
| 124 |
shutil.move(str(temp_path), str(self.file_path))
|
| 125 |
|
| 126 |
+
# キャッシュを更新
|
| 127 |
+
self._cached_data = validated_data.copy()
|
| 128 |
+
self._data_loaded = True
|
| 129 |
+
|
| 130 |
+
logger.debug(f"データを正常に保存しました: {self.file_path}")
|
| 131 |
|
| 132 |
except Exception as e:
|
| 133 |
logger.error(f"データ保存エラー: {e}")
|
|
|
|
| 227 |
# 復旧したデータを保存
|
| 228 |
await self._save_data_unsafe(data)
|
| 229 |
|
| 230 |
+
# キャッシュを更新
|
| 231 |
+
self._cached_data = data.copy()
|
| 232 |
+
self._data_loaded = True
|
| 233 |
+
|
| 234 |
return data
|
| 235 |
|
| 236 |
except Exception as e:
|
| 237 |
logger.error(f"バックアップからの復旧に失敗: {e}")
|
| 238 |
logger.info("初期データを使用します")
|
| 239 |
await self._save_data_unsafe(self.default_data)
|
| 240 |
+
# キャッシュを更新
|
| 241 |
+
self._cached_data = self.default_data.copy()
|
| 242 |
+
self._data_loaded = True
|
| 243 |
+
return self._cached_data.copy()
|
| 244 |
|
| 245 |
async def _cleanup_old_backups(self, days: int = 7) -> None:
|
| 246 |
"""古いバックアップファイルを削除"""
|
|
|
|
| 397 |
except Exception as e:
|
| 398 |
logger.error(f"統計情報の取得エラー: {e}")
|
| 399 |
return {}
|
| 400 |
+
|
| 401 |
+
def invalidate_cache(self) -> None:
|
| 402 |
+
"""キャッシュを無効化(外部からファイルが変更された場合など)"""
|
| 403 |
+
self._cached_data = None
|
| 404 |
+
self._data_loaded = False
|
| 405 |
+
logger.debug("データキャッシュを無効化しました")
|
| 406 |
+
|
| 407 |
+
async def reload_data(self) -> Dict[str, Any]:
|
| 408 |
+
"""データを強制的に再読み込み"""
|
| 409 |
+
return await self.load_data(force_reload=True)
|
| 410 |
|
| 411 |
|
| 412 |
# テスト用の関数
|
components_chat_interface.py
CHANGED
|
@@ -792,7 +792,21 @@ class ChatInterface:
|
|
| 792 |
st.chat_input(placeholder, disabled=True)
|
| 793 |
return None
|
| 794 |
|
| 795 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 796 |
user_input = st.chat_input(placeholder)
|
| 797 |
|
| 798 |
if user_input:
|
|
|
|
| 792 |
st.chat_input(placeholder, disabled=True)
|
| 793 |
return None
|
| 794 |
|
| 795 |
+
# 通常の入力フィールド(autocomplete属性付き)
|
| 796 |
+
# Streamlitのchat_inputにautocomplete属性を追加するためのCSS
|
| 797 |
+
chat_input_css = """
|
| 798 |
+
<style>
|
| 799 |
+
/* チャット入力フィールドのautocomplete属性を有効化 */
|
| 800 |
+
.stChatInput input[type="text"] {
|
| 801 |
+
autocomplete: on !important;
|
| 802 |
+
}
|
| 803 |
+
.stChatInput textarea {
|
| 804 |
+
autocomplete: on !important;
|
| 805 |
+
}
|
| 806 |
+
</style>
|
| 807 |
+
"""
|
| 808 |
+
st.markdown(chat_input_css, unsafe_allow_html=True)
|
| 809 |
+
|
| 810 |
user_input = st.chat_input(placeholder)
|
| 811 |
|
| 812 |
if user_input:
|
main_app.py
CHANGED
|
@@ -181,21 +181,11 @@ def run_async(coro):
|
|
| 181 |
return asyncio.run(coro)
|
| 182 |
|
| 183 |
def update_background(scene_manager: SceneManager, theme: str):
|
| 184 |
-
"""現在のテーマに基づいて背景画像を動的に設定するCSS
|
| 185 |
-
|
| 186 |
-
current_background_theme = st.session_state.get('current_background_theme', None)
|
| 187 |
-
if current_background_theme == theme:
|
| 188 |
-
logger.debug(f"背景更新スキップ - 既に同じテーマ適用済み: {theme}")
|
| 189 |
-
return
|
| 190 |
-
|
| 191 |
-
logger.info(f"背景更新開始 - テーマ: {theme}")
|
| 192 |
-
|
| 193 |
-
# 背景更新中フラグを設定(重複呼び出し防止)
|
| 194 |
-
if st.session_state.get('background_updating', False):
|
| 195 |
-
logger.debug(f"背景更新中 - スキップ: {theme}")
|
| 196 |
-
return
|
| 197 |
|
| 198 |
-
|
|
|
|
| 199 |
|
| 200 |
try:
|
| 201 |
logger.info(f"背景更新を開始します - テーマ: {theme}")
|
|
@@ -366,8 +356,9 @@ def update_background(scene_manager: SceneManager, theme: str):
|
|
| 366 |
forceApplyBackground();
|
| 367 |
}}
|
| 368 |
|
| 369 |
-
// Streamlit
|
| 370 |
-
setTimeout(forceApplyBackground,
|
|
|
|
| 371 |
setTimeout(forceApplyBackground, 500);
|
| 372 |
setTimeout(forceApplyBackground, 1000);
|
| 373 |
|
|
@@ -384,7 +375,7 @@ def update_background(scene_manager: SceneManager, theme: str):
|
|
| 384 |
|
| 385 |
observer.observe(document.body, {{ childList: true, subtree: true }});
|
| 386 |
|
| 387 |
-
//
|
| 388 |
setInterval(() => {{
|
| 389 |
const target = document.querySelector('[data-testid="stAppViewContainer"]');
|
| 390 |
if (target) {{
|
|
@@ -393,7 +384,7 @@ def update_background(scene_manager: SceneManager, theme: str):
|
|
| 393 |
forceApplyBackground();
|
| 394 |
}}
|
| 395 |
}}
|
| 396 |
-
}},
|
| 397 |
</script>
|
| 398 |
"""
|
| 399 |
st.markdown(background_js, unsafe_allow_html=True)
|
|
@@ -402,11 +393,11 @@ def update_background(scene_manager: SceneManager, theme: str):
|
|
| 402 |
if st.session_state.get("debug_mode", False) and image_url:
|
| 403 |
st.success(f"🖼️ 背景更新: {theme}")
|
| 404 |
|
| 405 |
-
#
|
| 406 |
st.session_state.current_background_theme = theme
|
| 407 |
st.session_state.last_background_theme = theme
|
| 408 |
|
| 409 |
-
logger.info(f"背景更新完了 - テーマ: {theme}")
|
| 410 |
|
| 411 |
except Exception as e:
|
| 412 |
logger.error(f"背景更新エラー: {e}")
|
|
@@ -425,11 +416,9 @@ def update_background(scene_manager: SceneManager, theme: str):
|
|
| 425 |
</style>
|
| 426 |
"""
|
| 427 |
st.markdown(fallback_css, unsafe_allow_html=True)
|
|
|
|
| 428 |
st.session_state.current_background_theme = theme
|
| 429 |
st.session_state.last_background_theme = theme
|
| 430 |
-
finally:
|
| 431 |
-
# 背景更新中フラグをクリア
|
| 432 |
-
st.session_state.background_updating = False
|
| 433 |
|
| 434 |
# --- ▼▼▼ 1. 初期化処理の一元管理 ▼▼▼ ---
|
| 435 |
|
|
@@ -824,6 +813,28 @@ def inject_custom_css(file_path="streamlit_styles.css"):
|
|
| 824 |
except Exception as e:
|
| 825 |
logger.error(f"CSS読み込みエラー: {e}")
|
| 826 |
apply_fallback_css()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 827 |
|
| 828 |
def apply_fallback_css():
|
| 829 |
"""フォールバック用の基本CSSを適用"""
|
|
@@ -1201,7 +1212,7 @@ def render_chat_tab(managers):
|
|
| 1201 |
# 背景を更新
|
| 1202 |
try:
|
| 1203 |
current_theme = st.session_state.chat['scene_params'].get("theme", "default")
|
| 1204 |
-
logger.
|
| 1205 |
update_background(managers['scene_manager'], current_theme)
|
| 1206 |
except Exception as e:
|
| 1207 |
logger.error(f"チャットタブ背景更新でエラーが発生: {e}")
|
|
@@ -2261,7 +2272,7 @@ Streamlit情報:
|
|
| 2261 |
|
| 2262 |
# シーン変更時に背景を更新
|
| 2263 |
try:
|
| 2264 |
-
logger.
|
| 2265 |
update_background(managers['scene_manager'], new_theme)
|
| 2266 |
except Exception as e:
|
| 2267 |
logger.error(f"シーン変更時の背景更新でエラーが発生: {e}")
|
|
@@ -2716,6 +2727,16 @@ def render_letter_tab(managers):
|
|
| 2716 |
with st.form("instant_letter_form"):
|
| 2717 |
st.write("💌 **初回手紙生成**")
|
| 2718 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2719 |
theme = st.text_input(
|
| 2720 |
"手紙に書いてほしいテーマ",
|
| 2721 |
placeholder="例: 今日見た美しい夕日について",
|
|
@@ -2741,6 +2762,16 @@ def render_letter_tab(managers):
|
|
| 2741 |
with st.form("scheduled_letter_form"):
|
| 2742 |
st.write("📮 **手紙のリクエスト**")
|
| 2743 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2744 |
theme = st.text_input(
|
| 2745 |
"手紙に書いてほしいテーマ",
|
| 2746 |
placeholder="例: 今日見た美しい夕日について",
|
|
@@ -3015,9 +3046,9 @@ def main():
|
|
| 3015 |
# CSS読み込み完了後に背景を更新(初期設定)
|
| 3016 |
try:
|
| 3017 |
initial_theme = st.session_state.chat['scene_params'].get('theme', 'default')
|
| 3018 |
-
logger.
|
| 3019 |
|
| 3020 |
-
#
|
| 3021 |
update_background(managers['scene_manager'], initial_theme)
|
| 3022 |
|
| 3023 |
except Exception as e:
|
|
|
|
| 181 |
return asyncio.run(coro)
|
| 182 |
|
| 183 |
def update_background(scene_manager: SceneManager, theme: str):
|
| 184 |
+
"""現在のテーマに基づいて背景画像を動的に設定するCSSを注入する(重複更新許容版)"""
|
| 185 |
+
logger.info(f"背景更新開始 - テーマ: {theme} (重複更新許容)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
+
# 重複更新を許容: 毎回テーマ更新を実行
|
| 188 |
+
# rerun時でも確実に背景が適用されるようにする
|
| 189 |
|
| 190 |
try:
|
| 191 |
logger.info(f"背景更新を開始します - テーマ: {theme}")
|
|
|
|
| 356 |
forceApplyBackground();
|
| 357 |
}}
|
| 358 |
|
| 359 |
+
// Streamlitの再レンダリング対策(重複更新許容)
|
| 360 |
+
setTimeout(forceApplyBackground, 50);
|
| 361 |
+
setTimeout(forceApplyBackground, 200);
|
| 362 |
setTimeout(forceApplyBackground, 500);
|
| 363 |
setTimeout(forceApplyBackground, 1000);
|
| 364 |
|
|
|
|
| 375 |
|
| 376 |
observer.observe(document.body, {{ childList: true, subtree: true }});
|
| 377 |
|
| 378 |
+
// 定期的な背景チェック(重複更新許容・高頻度)
|
| 379 |
setInterval(() => {{
|
| 380 |
const target = document.querySelector('[data-testid="stAppViewContainer"]');
|
| 381 |
if (target) {{
|
|
|
|
| 384 |
forceApplyBackground();
|
| 385 |
}}
|
| 386 |
}}
|
| 387 |
+
}}, 1000); // 1秒ごとにチェック(重複更新許容)
|
| 388 |
</script>
|
| 389 |
"""
|
| 390 |
st.markdown(background_js, unsafe_allow_html=True)
|
|
|
|
| 393 |
if st.session_state.get("debug_mode", False) and image_url:
|
| 394 |
st.success(f"🖼️ 背景更新: {theme}")
|
| 395 |
|
| 396 |
+
# 現在のテーマを記録(参考用)
|
| 397 |
st.session_state.current_background_theme = theme
|
| 398 |
st.session_state.last_background_theme = theme
|
| 399 |
|
| 400 |
+
logger.info(f"背景更新完了 - テーマ: {theme} (重複更新許容)")
|
| 401 |
|
| 402 |
except Exception as e:
|
| 403 |
logger.error(f"背景更新エラー: {e}")
|
|
|
|
| 416 |
</style>
|
| 417 |
"""
|
| 418 |
st.markdown(fallback_css, unsafe_allow_html=True)
|
| 419 |
+
# フォールバック時もテーマを記録
|
| 420 |
st.session_state.current_background_theme = theme
|
| 421 |
st.session_state.last_background_theme = theme
|
|
|
|
|
|
|
|
|
|
| 422 |
|
| 423 |
# --- ▼▼▼ 1. 初期化処理の一元管理 ▼▼▼ ---
|
| 424 |
|
|
|
|
| 813 |
except Exception as e:
|
| 814 |
logger.error(f"CSS読み込みエラー: {e}")
|
| 815 |
apply_fallback_css()
|
| 816 |
+
|
| 817 |
+
# 全体的なautocomplete設定を追加
|
| 818 |
+
autocomplete_css = """
|
| 819 |
+
<style>
|
| 820 |
+
/* 全ての入力フィールドでautocompleteを有効化 */
|
| 821 |
+
input[type="text"],
|
| 822 |
+
input[type="email"],
|
| 823 |
+
input[type="password"],
|
| 824 |
+
textarea {
|
| 825 |
+
autocomplete: on !important;
|
| 826 |
+
}
|
| 827 |
+
|
| 828 |
+
/* Streamlit固有の入力フィールド */
|
| 829 |
+
.stTextInput input,
|
| 830 |
+
.stTextArea textarea,
|
| 831 |
+
.stChatInput input,
|
| 832 |
+
.stChatInput textarea {
|
| 833 |
+
autocomplete: on !important;
|
| 834 |
+
}
|
| 835 |
+
</style>
|
| 836 |
+
"""
|
| 837 |
+
st.markdown(autocomplete_css, unsafe_allow_html=True)
|
| 838 |
|
| 839 |
def apply_fallback_css():
|
| 840 |
"""フォールバック用の基本CSSを適用"""
|
|
|
|
| 1212 |
# 背景を更新
|
| 1213 |
try:
|
| 1214 |
current_theme = st.session_state.chat['scene_params'].get("theme", "default")
|
| 1215 |
+
logger.info(f"チャットタブ背景更新: テーマ '{current_theme}' (重複更新許容)")
|
| 1216 |
update_background(managers['scene_manager'], current_theme)
|
| 1217 |
except Exception as e:
|
| 1218 |
logger.error(f"チャットタブ背景更新でエラーが発生: {e}")
|
|
|
|
| 2272 |
|
| 2273 |
# シーン変更時に背景を更新
|
| 2274 |
try:
|
| 2275 |
+
logger.info(f"シーン変更時の背景更新: テーマ '{new_theme}' (重複更新許容)")
|
| 2276 |
update_background(managers['scene_manager'], new_theme)
|
| 2277 |
except Exception as e:
|
| 2278 |
logger.error(f"シーン変更時の背景更新でエラーが発生: {e}")
|
|
|
|
| 2727 |
with st.form("instant_letter_form"):
|
| 2728 |
st.write("💌 **初回手紙生成**")
|
| 2729 |
|
| 2730 |
+
# テーマ入力フィールドのautocomplete属性を有効化
|
| 2731 |
+
theme_input_css = """
|
| 2732 |
+
<style>
|
| 2733 |
+
.stTextInput input[type="text"] {
|
| 2734 |
+
autocomplete: on !important;
|
| 2735 |
+
}
|
| 2736 |
+
</style>
|
| 2737 |
+
"""
|
| 2738 |
+
st.markdown(theme_input_css, unsafe_allow_html=True)
|
| 2739 |
+
|
| 2740 |
theme = st.text_input(
|
| 2741 |
"手紙に書いてほしいテーマ",
|
| 2742 |
placeholder="例: 今日見た美しい夕日について",
|
|
|
|
| 2762 |
with st.form("scheduled_letter_form"):
|
| 2763 |
st.write("📮 **手紙のリクエスト**")
|
| 2764 |
|
| 2765 |
+
# テーマ入力フィールドのautocomplete属性を有効化
|
| 2766 |
+
theme_input_css = """
|
| 2767 |
+
<style>
|
| 2768 |
+
.stTextInput input[type="text"] {
|
| 2769 |
+
autocomplete: on !important;
|
| 2770 |
+
}
|
| 2771 |
+
</style>
|
| 2772 |
+
"""
|
| 2773 |
+
st.markdown(theme_input_css, unsafe_allow_html=True)
|
| 2774 |
+
|
| 2775 |
theme = st.text_input(
|
| 2776 |
"手紙に書いてほしいテーマ",
|
| 2777 |
placeholder="例: 今日見た美しい夕日について",
|
|
|
|
| 3046 |
# CSS読み込み完了後に背景を更新(初期設定)
|
| 3047 |
try:
|
| 3048 |
initial_theme = st.session_state.chat['scene_params'].get('theme', 'default')
|
| 3049 |
+
logger.info(f"初期背景設定: テーマ '{initial_theme}' (重複更新許容)")
|
| 3050 |
|
| 3051 |
+
# 常に背景を更新(重複更新許容)
|
| 3052 |
update_background(managers['scene_manager'], initial_theme)
|
| 3053 |
|
| 3054 |
except Exception as e:
|