File size: 26,358 Bytes
a73fa4e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
"""
チュートリアルコンポーネント
初回ユーザー向けのガイド機能を提供する
"""
import streamlit as st
import logging
from typing import Dict, List, Optional

logger = logging.getLogger(__name__)

class TutorialManager:
    """チュートリアル管理クラス"""
    
    def __init__(self):
        """初期化"""
        self.tutorial_steps = {
            1: {
                "title": "最初の一言を送ってみよう",
                "description": "画面下部の入力欄に「こんにちは」などの一言を入力して、麻理に話しかけてみましょう。",
                "icon": "💬",
                "target": "chat_input",
                "completed_key": "tutorial_step1_completed"
            },
            2: {
                "title": "本音を見てみよう(ポチ機能)",
                "description": "画面右下の犬アイコン「ポチ🐕」をクリックすると、麻理の本音が見えるようになります。",
                "icon": "🐕",
                "target": "dog_assistant",
                "completed_key": "tutorial_step2_completed"
            },
            3: {
                "title": "セーフティ機能を切り替えてみよう",
                "description": "左サイドバー上部の🔒ボタンをクリックすると、麻理の表現がより大胆になります。",
                "icon": "🔓",
                "target": "safety_button",
                "completed_key": "tutorial_step3_completed"
            },
            4: {
                "title": "手紙をリクエストしよう",
                "description": "「手紙を受け取る」タブから、麻理からの特別な手紙をリクエストできます。チュートリアル中は即座に短縮版の手紙が生成されます。",
                "icon": "✉️",
                "target": "letter_tab",
                "completed_key": "tutorial_step4_completed"
            },
            5: {
                "title": "麻理との関係性を育てよう",
                "description": "会話を重ねることで好感度が上がり、関係性のステージが進展します。",
                "icon": "💖",
                "target": "affection_display",
                "completed_key": "tutorial_step5_completed"
            },
            6: {
                "title": "風景が変わる会話をしてみよう",
                "description": "「カフェ」「神社」「美術館」などのキーワードを話すと、背景が動的に変わります。",
                "icon": "🎨",
                "target": "scene_change",
                "completed_key": "tutorial_step6_completed"
            }
        }
    
    def is_first_visit(self) -> bool:
        """初回訪問かどうかを判定"""
        return not st.session_state.get('tutorial_shown', False)
    
    def should_show_tutorial(self) -> bool:
        """チュートリアルを表示すべきかどうか"""
        # 初回訪問または明示的にチュートリアルが要求された場合
        return (self.is_first_visit() or 
                st.session_state.get('show_tutorial_requested', False))
    
    def mark_tutorial_shown(self):
        """チュートリアル表示済みとしてマーク"""
        st.session_state.tutorial_shown = True
        st.session_state.show_tutorial_requested = False
    
    def request_tutorial(self):
        """チュートリアル表示を要求"""
        st.session_state.show_tutorial_requested = True
    
    def get_current_step(self) -> int:
        """現在のチュートリアルステップを取得"""
        for step_num in range(1, 7):
            if not st.session_state.get(self.tutorial_steps[step_num]['completed_key'], False):
                return step_num
        return 7  # 全ステップ完了
    
    def complete_step(self, step_num: int):
        """ステップを完了としてマーク"""
        if step_num in self.tutorial_steps:
            st.session_state[self.tutorial_steps[step_num]['completed_key']] = True
            logger.info(f"チュートリアルステップ{step_num}が完了しました")
    
    def is_step_completed(self, step_num: int) -> bool:
        """ステップが完了しているかチェック"""
        if step_num in self.tutorial_steps:
            return st.session_state.get(self.tutorial_steps[step_num]['completed_key'], False)
        return False
    
    def render_welcome_dialog(self):
        """初回訪問時のウェルカムダイアログ"""
        if not self.is_first_visit():
            return
        
        # ウェルカムダイアログのスタイル
        welcome_css = """
        <style>
        .welcome-container {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
            padding: 40px;
            border-radius: 20px;
            text-align: center;
            box-shadow: 0 20px 40px rgba(0,0,0,0.3);
            margin: 20px 0;
            animation: welcomeSlideIn 0.8s ease-out;
        }
        
        .welcome-title {
            font-size: 28px;
            font-weight: bold;
            margin-bottom: 20px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }
        
        .welcome-description {
            font-size: 16px;
            line-height: 1.6;
            margin-bottom: 30px;
            opacity: 0.9;
        }
        
        @keyframes welcomeSlideIn {
            from {
                opacity: 0;
                transform: translateY(-20px) scale(0.95);
            }
            to {
                opacity: 1;
                transform: translateY(0) scale(1);
            }
        }
        
        @media (max-width: 768px) {
            .welcome-container {
                padding: 30px 20px;
            }
            
            .welcome-title {
                font-size: 24px;
            }
        }
        </style>
        """
        
        st.markdown(welcome_css, unsafe_allow_html=True)
        
        # ウェルカムメッセージ
        welcome_html = """
        <div class="welcome-container">
            <div class="welcome-title">🐕 麻理チャットへようこそ!</div>
            <div class="welcome-description">
                感情豊かなアンドロイド「麻理」と対話しながら、<br>
                本音や関係性の変化を楽しめる新感覚のAIチャット体験です。<br><br>
                最初の数分で、麻理との距離が少しだけ縮まります。
            </div>
        </div>
        """
        
        st.markdown(welcome_html, unsafe_allow_html=True)
        
        # ボタンを2列で配置
        col1, col2 = st.columns(2)
        
        with col1:
            if st.button("📘 チュートリアルを始める", type="primary", use_container_width=True, key="start_tutorial"):
                # 初期メッセージを即座に保護
                if 'chat' in st.session_state and 'messages' in st.session_state.chat:
                    messages = st.session_state.chat['messages']
                    if not any(msg.get('is_initial', False) for msg in messages):
                        initial_message = {"role": "assistant", "content": "何の用?遊びに来たの?", "is_initial": True}
                        st.session_state.chat['messages'].insert(0, initial_message)
                        logger.info("チュートリアル開始ボタン押下時に初期メッセージを即座に復元")
                
                # チュートリアル開始フラグを設定
                st.session_state.tutorial_start_requested = True
                st.session_state.tutorial_shown = True
                st.session_state.preserve_initial_message = True
                logger.info("チュートリアル開始 - 初期メッセージ保護フラグ設定")
        
        with col2:
            if st.button("⏭️ スキップして始める", type="secondary", use_container_width=True, key="skip_tutorial"):
                # 初期メッセージを即座に保護
                if 'chat' in st.session_state and 'messages' in st.session_state.chat:
                    messages = st.session_state.chat['messages']
                    if not any(msg.get('is_initial', False) for msg in messages):
                        initial_message = {"role": "assistant", "content": "何の用?遊びに来たの?", "is_initial": True}
                        st.session_state.chat['messages'].insert(0, initial_message)
                        logger.info("チュートリアルスキップボタン押下時に初期メッセージを即座に復元")
                
                # チュートリアルをスキップして全ステップを完了扱いにする
                for step_num in range(1, 7):
                    if step_num in self.tutorial_steps:
                        st.session_state[self.tutorial_steps[step_num]['completed_key']] = True
                
                st.session_state.tutorial_shown = True
                st.session_state.tutorial_skip_requested = True
                st.session_state.preserve_initial_message = True
                logger.info("チュートリアルスキップ - 初期メッセージ保護フラグ設定")
    
    def render_tutorial_sidebar(self):
        """サイドバーのチュートリアル案内(簡素版)"""
        with st.sidebar:
            st.markdown("---")
            
            # チュートリアル進行状況
            current_step = self.get_current_step()
            total_steps = len(self.tutorial_steps)
            
            if current_step <= total_steps:
                progress = (current_step - 1) / total_steps
                st.markdown("### 📘 チュートリアル進行")
                st.progress(progress)
                st.caption(f"ステップ {current_step - 1}/{total_steps} 完了")
            else:
                st.success("🎉 チュートリアル完了!")
                st.caption("麻理との会話を楽しんでください")
            
            # チュートリアル再表示ボタン
            if st.button("📘 チュートリアルを見る", use_container_width=True):
                self.request_tutorial()
                # st.rerun()を削除 - 状態変更により自動的に再描画される
    
    def render_chat_tutorial_guide(self):
        """チャットタブでのチュートリアル案内"""
        current_step = self.get_current_step()
        total_steps = len(self.tutorial_steps)
        
        # チュートリアル完了済みの場合は何も表示しない
        if current_step > total_steps:
            return
        
        # ステップ4が完了済みの場合(手紙タブに遷移済み)は表示しない
        if current_step == 4 and self.is_step_completed(4):
            return
        
        step_info = self.tutorial_steps[current_step]
        
        # ステップごとの案内スタイル
        guide_css = """
        <style>
        .tutorial-guide {
            background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%);
            border: 2px solid #2196f3;
            border-radius: 15px;
            padding: 20px;
            margin: 15px 0;
            box-shadow: 0 4px 12px rgba(33, 150, 243, 0.2);
            animation: tutorialGlow 3s ease-in-out infinite;
            position: relative;
        }
        
        .tutorial-guide::before {
            content: '📘';
            position: absolute;
            top: -10px;
            left: 20px;
            background: white;
            padding: 5px 10px;
            border-radius: 50%;
            font-size: 18px;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        
        .tutorial-step-number {
            color: #1976d2;
            font-weight: bold;
            font-size: 14px;
            margin-bottom: 8px;
        }
        
        .tutorial-step-title {
            color: #1565c0;
            font-size: 18px;
            font-weight: bold;
            margin-bottom: 10px;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        
        .tutorial-step-description {
            color: #424242;
            font-size: 14px;
            line-height: 1.6;
            margin-bottom: 15px;
        }
        
        .tutorial-step-action {
            background: rgba(33, 150, 243, 0.1);
            border-left: 4px solid #2196f3;
            padding: 12px 15px;
            border-radius: 0 8px 8px 0;
            font-size: 14px;
            color: #1565c0;
            font-weight: 500;
        }
        
        .tutorial-dismiss {
            position: absolute;
            top: 10px;
            right: 15px;
            background: none;
            border: none;
            color: #666;
            cursor: pointer;
            font-size: 18px;
            opacity: 0.7;
            transition: opacity 0.3s ease;
        }
        
        .tutorial-dismiss:hover {
            opacity: 1;
        }
        
        @keyframes tutorialGlow {
            0%, 100% { box-shadow: 0 4px 12px rgba(33, 150, 243, 0.2); }
            50% { box-shadow: 0 6px 20px rgba(33, 150, 243, 0.4); }
        }
        
        @media (max-width: 768px) {
            .tutorial-guide {
                padding: 15px;
                margin: 10px 0;
            }
            
            .tutorial-step-title {
                font-size: 16px;
            }
        }
        </style>
        """
        
        st.markdown(guide_css, unsafe_allow_html=True)
        
        # ステップごとの具体的な案内
        action_text = self._get_step_action_text(current_step)
        
        guide_html = f"""
        <div class="tutorial-guide">
            <div class="tutorial-step-number">チュートリアル ステップ {current_step}/{total_steps}</div>
            <div class="tutorial-step-title">
                <span>{step_info['icon']}</span>
                <span>{step_info['title']}</span>
            </div>
            <div class="tutorial-step-description">
                {step_info['description']}
            </div>
            <div class="tutorial-step-action">
                💡 {action_text}
            </div>
        </div>
        """
        
        st.markdown(guide_html, unsafe_allow_html=True)
    
    def _get_step_action_text(self, step_num: int) -> str:
        """ステップごとの具体的なアクション案内テキストを取得"""
        action_texts = {
            1: "下のチャット入力欄に「こんにちは」と入力して送信してみてください。",
            2: "画面右下に表示される犬のアイコン「ポチ🐕」をクリックしてみてください。",
            3: "左サイドバーの一番上にある🔒ボタンをクリックして、セーフティ機能を切り替えてみてください。",
            4: "画面上部の光っている「✉️ 手紙を受け取る」タブをクリックして、手紙をリクエストしてみてください。矢印が案内しています!",
            5: "麻理ともっと会話して、左サイドバーの好感度の変化を確認してみてください。",
            6: "「カフェに行きたい」「神社でお参りしたい」「美術館を見に行こう」などと話しかけて、背景の変化を楽しんでください。"
        }
        return action_texts.get(step_num, "次のステップに進んでください。")
    
    def render_step_highlight(self, step_num: int, target_element: str):
        """特定のステップのハイライト表示"""
        if self.get_current_step() != step_num:
            return
        
        step_info = self.tutorial_steps[step_num]
        
        highlight_css = f"""
        <style>
        .tutorial-highlight-{step_num} {{
            position: relative;
            animation: tutorialPulse 2s ease-in-out infinite;
        }}
        
        .tutorial-highlight-{step_num}::after {{
            content: '';
            position: absolute;
            top: -5px;
            left: -5px;
            right: -5px;
            bottom: -5px;
            border: 3px solid #ff6b6b;
            border-radius: 10px;
            pointer-events: none;
            animation: tutorialGlow 2s ease-in-out infinite;
        }}
        
        .tutorial-tooltip-{step_num} {{
            position: absolute;
            background: #ff6b6b;
            color: white;
            padding: 10px 15px;
            border-radius: 10px;
            font-size: 14px;
            z-index: 1000;
            box-shadow: 0 4px 12px rgba(0,0,0,0.3);
            animation: tutorialTooltip 0.5s ease-out;
        }}
        
        @keyframes tutorialPulse {{
            0%, 100% {{ transform: scale(1); }}
            50% {{ transform: scale(1.02); }}
        }}
        
        @keyframes tutorialGlow {{
            0%, 100% {{ opacity: 0.7; }}
            50% {{ opacity: 1; }}
        }}
        
        @keyframes tutorialTooltip {{
            from {{ opacity: 0; transform: translateY(10px); }}
            to {{ opacity: 1; transform: translateY(0); }}
        }}
        </style>
        """
        
        st.markdown(highlight_css, unsafe_allow_html=True)
    
    def render_tutorial_tab(self):
        """チュートリアル専用タブの内容"""
        st.markdown("# 📘 麻理チャット チュートリアル")
        
        st.markdown("""
        **ようこそ、麻理チャットへ!**
        
        感情豊かなアンドロイド「麻理」と対話しながら、本音や関係性の変化を楽しめる新感覚のAIチャット体験です。
        このチュートリアルで、主要機能を順番に体験してみましょう。
        """)
        
        # 進行状況表示
        current_step = self.get_current_step()
        total_steps = len(self.tutorial_steps)
        
        col1, col2, col3 = st.columns([1, 2, 1])
        with col2:
            progress = min((current_step - 1) / total_steps, 1.0)
            st.progress(progress)
            st.caption(f"進行状況: {min(current_step - 1, total_steps)}/{total_steps} ステップ完了")
        
        st.markdown("---")
        
        # 各ステップの表示
        for step_num, step_info in self.tutorial_steps.items():
            is_completed = self.is_step_completed(step_num)
            is_current = (current_step == step_num)
            
            # ステップのスタイル決定
            if is_completed:
                status_icon = "✅"
                status_color = "#28a745"
                card_style = "background: rgba(40, 167, 69, 0.1); border-left: 4px solid #28a745;"
            elif is_current:
                status_icon = "👉"
                status_color = "#ff6b6b"
                card_style = "background: rgba(255, 107, 107, 0.1); border-left: 4px solid #ff6b6b;"
            else:
                status_icon = "⏳"
                status_color = "#6c757d"
                card_style = "background: rgba(108, 117, 125, 0.1); border-left: 4px solid #6c757d;"
            
            # ステップカード
            st.markdown(f"""
            <div style="padding: 20px; margin: 15px 0; border-radius: 10px; {card_style}">
                <h3 style="color: {status_color}; margin-bottom: 10px;">
                    {status_icon} ステップ {step_num}: {step_info['icon']} {step_info['title']}
                </h3>
                <p style="margin-bottom: 0; line-height: 1.6;">
                    {step_info['description']}
                </p>
            </div>
            """, unsafe_allow_html=True)
            
            # 現在のステップの場合、追加のガイダンス
            if is_current:
                if step_num == 1:
                    st.info("💡 **ヒント**: 「麻理と話す」タブに移動して、画面下部の入力欄にメッセージを入力してみてください。")
                elif step_num == 2:
                    st.info("💡 **ヒント**: 画面右下に表示される犬のアイコン「ポチ🐕」をクリックしてみてください。")
                elif step_num == 3:
                    st.info("💡 **ヒント**: 左サイドバーの一番上にある🔒ボタンをクリックしてみてください。")
                elif step_num == 4:
                    st.info("💡 **ヒント**: 画面上部の「手紙を受け取る」タブをクリックして、手紙をリクエストしてみてください。")
                elif step_num == 5:
                    st.info("💡 **ヒント**: 左サイドバーの「ステータス」で好感度の変化を確認できます。")
                elif step_num == 6:
                    st.info("💡 **ヒント**: 「カフェに行きたい」「神社でお参りしたい」などと話しかけてみてください。")
        
        # 完了時のメッセージ
        if current_step > total_steps:
            st.balloons()
            st.success("""
            🎉 **チュートリアル完了おめでとうございます!**
            
            これで麻理チャットの主要機能をすべて体験しました。
            これからは自由に麻理との会話を楽しんでください。
            
            何か分からないことがあれば、いつでもこのチュートリアルに戻ってきてくださいね。
            """)
    
    def check_step_completion(self, step_num: int, condition_met: bool):
        """ステップ完了条件をチェック(順序制御付き)"""
        # 順序制御:現在のステップまたは次のステップのみ完了可能
        current_step = self.get_current_step()
        
        # 現在のステップより先のステップは完了できない
        if step_num > current_step + 1:
            logger.debug(f"ステップ{step_num}は順序違反のためスキップ(現在ステップ: {current_step})")
            return
        
        # 既に完了済みのステップは再完了しない
        if self.is_step_completed(step_num):
            logger.debug(f"ステップ{step_num}は既に完了済み")
            return
        
        if condition_met:
            self.complete_step(step_num)
            logger.info(f"✅ チュートリアルステップ{step_num}完了!現在ステップ: {current_step}")
            
            # 完了通知(控えめに)
            step_info = self.tutorial_steps[step_num]
            
            # 次のステップの案内
            next_step = step_num + 1
            if next_step in self.tutorial_steps:
                next_info = self.tutorial_steps[next_step]
                st.success(f"✅ ステップ{step_num}完了!次は「{next_info['title']}」です。")
            else:
                # 全ステップ完了
                st.balloons()
                st.success("🎉 チュートリアル完了!麻理との会話を存分にお楽しみください!")
            
            # ステップ4完了時に強調表示を解除するためのページ再読み込み
            # st.rerun()を削除 - 状態変更により自動的に再描画される
    
    def auto_check_completions(self):
        """自動的にステップ完了をチェック(順序制御強化版)"""
        current_step = self.get_current_step()
        
        # 現在のステップのみをチェック(先のステップは無視)
        if current_step == 1:
            # ステップ1: メッセージ送信
            messages = st.session_state.get('chat', {}).get('messages', [])
            non_initial_messages = [msg for msg in messages if not msg.get('is_initial', False)]
            if len(non_initial_messages) > 0:  # ユーザーが1回でもメッセージを送信した
                self.check_step_completion(1, True)
        
        elif current_step == 2:
            # ステップ2: ポチ機能使用
            if st.session_state.get('show_all_hidden', False):
                self.check_step_completion(2, True)
        
        elif current_step == 3:
            # ステップ3: セーフティ機能使用
            if st.session_state.get('chat', {}).get('ura_mode', False):
                self.check_step_completion(3, True)
        
        elif current_step == 4:
            # ステップ4: 手紙タブに到達(手紙タブでのみ完了判定)
            # auto_check_completionsでは判定しない(手紙タブで明示的に完了)
            pass
        
        elif current_step == 5:
            # ステップ5: 好感度変化(ステップ4完了後のみ)
            initial_affection = 30
            current_affection = st.session_state.get('chat', {}).get('affection', initial_affection)
            if current_affection != initial_affection:
                self.check_step_completion(5, True)
        
        elif current_step == 6:
            # ステップ6: シーン変更(ステップ5完了後のみ)
            current_theme = st.session_state.get('chat', {}).get('scene_params', {}).get('theme', 'default')
            if current_theme != 'default':
                self.check_step_completion(6, True)
    
    def get_tutorial_status(self) -> Dict:
        """チュートリアルの状態情報を取得"""
        current_step = self.get_current_step()
        total_steps = len(self.tutorial_steps)
        completed_steps = sum(1 for i in range(1, total_steps + 1) if self.is_step_completed(i))
        
        return {
            'is_first_visit': self.is_first_visit(),
            'current_step': current_step,
            'total_steps': total_steps,
            'completed_steps': completed_steps,
            'progress_percentage': (completed_steps / total_steps) * 100,
            'is_completed': current_step > total_steps
        }