Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import streamlit.components.v1 as components | |
| # 頁面配置 | |
| st.set_page_config(layout="wide", page_title="AlterEgo LinkedIn Lab") | |
| # 1. CSS 樣式 (絕對靠左,防止誤判) | |
| css_styles = """ | |
| <style> | |
| /* --- 全局設定 --- */ | |
| html, body, .stApp { | |
| height: 100vh; | |
| overflow: hidden !important; | |
| margin: 0; | |
| } | |
| .stApp { | |
| background: | |
| radial-gradient(circle at 20% 30%, rgba(70, 130, 255, 0.3) 0%, transparent 50%), | |
| radial-gradient(circle at 80% 70%, rgba(255, 20, 147, 0.2) 0%, transparent 50%), | |
| radial-gradient(circle at center, #4b1a8a 0%, #1a0b2e 60%, #000000 100%); | |
| } | |
| .block-container { padding: 0 !important; max-width: 100% !important; } | |
| header, footer, .stDeployButton { display: none !important; } | |
| /* --- 佈局外殼 --- */ | |
| .shell { | |
| position: fixed; | |
| top: 0; left: 0; | |
| width: 100vw; height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* --- Header & Footer --- */ | |
| .header, .footer { | |
| flex: 0 0 70px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 30px; | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(15px); | |
| z-index: 100; | |
| } | |
| .header { border-bottom: 1px solid rgba(255, 255, 255, 0.15); } | |
| .footer { | |
| border-top: 1px solid rgba(255, 255, 255, 0.15); | |
| position: fixed; bottom: 0; left: 0; width: 100%; | |
| } | |
| .header-title { color: white; font-weight: bold; font-size: 1.1rem; } | |
| .step-indicator { font-size: 0.7rem; color: #45B0FF; margin-top: 2px; } | |
| /* --- Main Content --- */ | |
| .main-content { | |
| flex: 1; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| padding-bottom: 70px; | |
| } | |
| .linkedin-box { | |
| width: 800px; | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(20px); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| border-radius: 16px; | |
| padding: 30px; | |
| box-shadow: 0 0 60px rgba(0, 0, 0, 0.6); | |
| position: relative; | |
| } | |
| /* User Info */ | |
| .user-info { display: flex; align-items: center; margin-bottom: 20px; } | |
| .avatar { | |
| width: 64px; height: 64px; | |
| border-radius: 50%; | |
| background: #555; | |
| border: 3px solid #FF69B4; | |
| box-shadow: 0 0 15px #FF69B4; | |
| margin-right: 18px; | |
| } | |
| .name-text { color: white; font-weight: 600; font-size: 1.3rem; } | |
| .anyone-badge { | |
| background: rgba(255,255,255,0.1); | |
| border: 1px solid rgba(255,255,255,0.3); | |
| border-radius: 20px; | |
| padding: 4px 12px; | |
| font-size: 0.85rem; | |
| color: #ccc; | |
| display: inline-block; | |
| margin-top: 6px; | |
| } | |
| /* Magic Button */ | |
| .alterego-magic-btn { | |
| position: absolute; | |
| top: 55%; left: 50%; | |
| transform: translate(-50%, -50%); | |
| background: linear-gradient(135deg, #45B0FF, #FF69B4); | |
| border: none; | |
| border-radius: 35px; | |
| padding: 15px 35px; | |
| color: white; | |
| font-weight: 900; | |
| font-size: 1.2rem; | |
| box-shadow: 0 0 25px rgba(69, 176, 255, 0.8); | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| cursor: pointer; | |
| z-index: 10; | |
| white-space: nowrap; | |
| animation: pulse 3s infinite ease-in-out; | |
| } | |
| @keyframes pulse { | |
| 0% { box-shadow: 0 0 20px rgba(69, 176, 255, 0.8); } | |
| 50% { box-shadow: 0 0 40px rgba(255, 105, 180, 0.8); transform: translate(-50%, -50%) scale(1.05); } | |
| 100% { box-shadow: 0 0 20px rgba(69, 176, 255, 0.8); } | |
| } | |
| /* Text Area */ | |
| .input-placeholder { | |
| height: 180px; | |
| color: rgba(255, 255, 255, 0.5); | |
| font-size: 1.5rem; | |
| padding-top: 10px; | |
| } | |
| /* Smiley Icon */ | |
| .smiley-icon { | |
| font-size: 1.8rem; | |
| color: #45B0FF; | |
| margin-bottom: 15px; | |
| cursor: pointer; | |
| width: fit-content; | |
| } | |
| /* Bottom Icons Row */ | |
| .bottom-row { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| border-top: 1px solid rgba(255,255,255,0.1); | |
| padding-top: 20px; | |
| } | |
| .icon-group { | |
| display: flex; | |
| gap: 30px; | |
| font-size: 1.6rem; | |
| color: #45B0FF; | |
| } | |
| /* Post Actions (Clock + Button) */ | |
| .post-actions { | |
| display: flex; | |
| align-items: center; | |
| gap: 15px; /* 時鐘與按鈕的距離 */ | |
| } | |
| .schedule-icon { | |
| font-size: 1.5rem; | |
| color: rgba(255, 255, 255, 0.6); | |
| cursor: pointer; | |
| } | |
| /* Hide native buttons */ | |
| div[data-testid="stButton"] { display: none !important; } | |
| .shell div[data-testid="stButton"] { display: inline-block !important; } | |
| </style> | |
| """ | |
| st.markdown(css_styles, unsafe_allow_html=True) | |
| # 2. HTML 骨架 (絕對靠左) | |
| html_structure = f""" | |
| <div class="shell"> | |
| <div class="header"> | |
| <div id="slot-back"></div> | |
| <div style="text-align: center;"> | |
| <div class="header-title">新 LinkedIn 貼文</div> | |
| <div class="step-indicator">STEP 2/3 : 專業賦能</div> | |
| </div> | |
| <div id="slot-next"></div> | |
| </div> | |
| <div class="main-content"> | |
| <div class="linkedin-box"> | |
| <div class="user-info"> | |
| <div class="avatar"></div> | |
| <div> | |
| <div class="name-text">Judy Wang</div> | |
| <div class="anyone-badge">🌐 Anyone</div> | |
| </div> | |
| </div> | |
| <div class="input-placeholder">分享您的意見想法......</div> | |
| <div class="alterego-magic-btn"> | |
| <span>🤖</span> ✨ AlterEgo AI 智能潤飾 | |
| </div> | |
| <div class="smiley-icon">🙂</div> | |
| <div class="bottom-row"> | |
| <div class="icon-group"> | |
| <span>🖼️</span> <span>🗓️</span> <span>🏵️</span> <span>➕</span> | |
| </div> | |
| <div class="post-actions"> | |
| <span class="schedule-icon">🕒</span> | |
| <div id="slot-post"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="footer"></div> | |
| </div> | |
| """ | |
| st.markdown(html_structure, unsafe_allow_html=True) | |
| # 3. Streamlit 按鈕 | |
| st.button("⬅ 返回上一步", key="btn_back") | |
| st.button("下一步", key="btn_next") | |
| # 注意:這裡的按鈕文字改成了 "發表" | |
| st.button("發表", key="btn_post") | |
| # 4. JS 搬運工 | |
| components.html(""" | |
| <script> | |
| function move(btnText, targetId) { | |
| const buttons = Array.from(window.parent.document.querySelectorAll('button')); | |
| const targetBtn = buttons.find(b => b.innerText.includes(btnText)); | |
| const targetSlot = window.parent.document.getElementById(targetId); | |
| if (targetBtn && targetSlot) { | |
| const wrapper = targetBtn.closest('div[data-testid="stButton"]'); | |
| if (wrapper) targetSlot.appendChild(wrapper); | |
| } | |
| } | |
| setTimeout(() => { | |
| move("返回上一步", "slot-back"); | |
| move("下一步", "slot-next"); | |
| move("發表", "slot-post"); // JS 也要改成抓取 "發表" | |
| }, 150); | |
| </script> | |
| """, height=0) |