diff --git a/public/demo.html b/public/demo.html new file mode 100644 index 0000000..46c5b27 --- /dev/null +++ b/public/demo.html @@ -0,0 +1,29 @@ + + + + + + Redirecting... + + + + + + + + + +

Redirecting.

+

If your browser does not redirect you automatically, click here to + proceed.

+ + + \ No newline at end of file diff --git a/public/demo.pdf b/public/demo.pdf index fd012be..dc25ec3 100644 Binary files a/public/demo.pdf and b/public/demo.pdf differ diff --git a/src/App.css b/src/App.css index 2574628..2e205b7 100644 --- a/src/App.css +++ b/src/App.css @@ -1010,4 +1010,29 @@ kbd { padding: 2px 6px; white-space: nowrap; font-family: monospace; +} + +.demo-banner { + position: fixed; + top: 0; + left: 50%; + transform: translateX(-50%); + background: linear-gradient(90deg, #6366f1 0%, #a855f7 100%); + color: white; + text-align: center; + padding: 6px 16px; + font-size: 12px; + font-weight: 500; + z-index: 10000; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.3); + border-bottom-left-radius: 8px; + border-bottom-right-radius: 8px; + white-space: nowrap; +} + +.demo-banner a { + color: white; + text-decoration: underline; + font-weight: 600; + margin-left: 4px; } \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx index 2d75e42..b8f0425 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -24,11 +24,14 @@ import { groupTokensIntoSentences } from './parsing'; import SpeechCustomizationPanel from './SpeechCustomizationPanel'; import { getVoiceSettings, calculateActualRate, PRIORITY_VOICES } from './voiceSpeedConfig'; // IMPORT VOICE CONFIG import BugReport from './components/BugReport/BugReport'; -import { initDemoFile } from './services/demoService'; +import { initDemoFile, DEMO_DEFAULTS } from './services/demoService'; import './App.css'; pdfjsLib.GlobalWorkerOptions.workerSrc = pdfWorker; +// --- Demo Mode Flag --- +const IS_DEMO_MODE = true; + // --- Local Storage Keys --- const LS_GLOBALS = 'pdf_reader_globals'; const LS_AI_CONFIG = 'pdf_reader_ai_config'; // New Key @@ -42,7 +45,7 @@ const DEFAULT_GLOBALS = { highlightColor: '#ffeb3b', highlightOpacity: 0.4, autoHide: false, - autoScroll: true, + autoScroll: false, layoutMode: 'grid', speechCustomization: { skipUrls: true, @@ -74,9 +77,94 @@ const DEFAULT_STORAGE_SETTINGS = { autoMetadataOnly: false }; +const isWeChat = /MicroMessenger/i.test(navigator.userAgent); + +const WeChatBlock = () => { + const [imageIndex, setImageIndex] = useState(1); + + useEffect(() => { + const interval = setInterval(() => { + setImageIndex(prev => (prev === 1 ? 2 : 1)); + }, 500); + return () => clearInterval(interval); + }, []); + + return ( +
+
+ VIVIDpdf +
+
+ 请用电脑/iPad浏览器打开 +
+ uoft.me/vividpdf +
+
+ + {/* Flickering Images at Bottom */} +
+ flicker +
+
+ ); +}; + const App = () => { + if (isWeChat) { + return ; + } + + // --- Global Settings (Init from LocalStorage) --- const [globalSettings, setGlobalSettings] = useState(() => { + if (IS_DEMO_MODE) return { ...DEFAULT_GLOBALS, ...DEMO_DEFAULTS }; try { const saved = localStorage.getItem(LS_GLOBALS); return saved ? { ...DEFAULT_GLOBALS, ...JSON.parse(saved) } : DEFAULT_GLOBALS; @@ -87,6 +175,7 @@ const App = () => { // --- AI Config State --- const [aiConfig, setAiConfig] = useState(() => { + if (IS_DEMO_MODE) return { ...DEFAULT_AI_CONFIG, ...DEMO_DEFAULTS.aiConfig }; try { const saved = localStorage.getItem(LS_AI_CONFIG); return saved ? { ...DEFAULT_AI_CONFIG, ...JSON.parse(saved) } : DEFAULT_AI_CONFIG; @@ -122,8 +211,9 @@ const App = () => { // Save AI Config on change useEffect(() => { - localStorage.setItem(LS_AI_CONFIG, JSON.stringify(aiConfig)); aiConfigRef.current = aiConfig; + if (IS_DEMO_MODE) return; + localStorage.setItem(LS_AI_CONFIG, JSON.stringify(aiConfig)); }, [aiConfig]); const aiConfigRef = useRef(aiConfig); @@ -415,6 +505,7 @@ const App = () => { // 1. Save Global Settings to LocalStorage on change useEffect(() => { + if (IS_DEMO_MODE) return; const settings = { voiceURI: selectedVoiceURI, readingMode, @@ -449,15 +540,30 @@ const App = () => { // 2. Load Recent Files on Mount useEffect(() => { - const checkDemoRoute = async () => { - if (window.location.pathname === '/demo') { - console.log("[Demo] Detected /demo route, initializing demo.pdf"); - await initDemoFile(); - loadRecentFilesList(); + const initDemo = async () => { + const fid = await initDemoFile(); + if (fid) { + const record = await getFileRecord(fid); + if (record && record.blob) { + loadFromBlob(record.blob, record); + } } + loadRecentFilesList(); }; - checkDemoRoute(); - loadRecentFilesList(); + + if (IS_DEMO_MODE) { + initDemo(); + } else { + const checkDemoRoute = async () => { + if (window.location.pathname === '/demo') { + console.log("[Demo] Detected /demo route, initializing demo.pdf"); + await initDemoFile(); + loadRecentFilesList(); + } + }; + checkDemoRoute(); + loadRecentFilesList(); + } }, []); useEffect(() => { @@ -906,7 +1012,7 @@ const App = () => { } catch (error) { console.error("Error loading PDF:", error); - alert("Failed to load PDF. Please ensure it is a valid file."); + alert("Failed to load PDF. Please ensure it is a valid file. 请使用电脑/平板打开"); } finally { setIsLoading(false); } @@ -2146,6 +2252,11 @@ const App = () => { return (
+ {IS_DEMO_MODE && ( +
+ This is a demo page. To use our app, go to vividpdf.pages.dev +
+ )} {/* Hidden File Input with Ref */} { scale: 1.5, rotation: 0, darkMode: false, - skipZones: [] + ...DEMO_DEFAULTS }); console.log(`[Demo] demo.pdf initialized with ID: ${fid}`); + //import.meta.env.VITE_GEMINI_DEMO_KEY + console.log(`[API Keys] Gemini: ${import.meta.env.VITE_GEMINI_DEMO_KEY ? 'Provided' : 'Not Provided'}`); + console.log(import.meta.env.VITE_GEMINI_DEMO_KEY); return fid; } catch (error) { console.error('[Demo] Error initializing demo file:', error); diff --git a/src/services/trash.py b/src/services/trash.py new file mode 100644 index 0000000..c0d9fa8 --- /dev/null +++ b/src/services/trash.py @@ -0,0 +1,19 @@ +import base64 + +def prepare_for_atob(input_string): + # 1. Convert the string to bytes (UTF-8 is standard) + message_bytes = input_string.encode('utf-8') + + # 2. Encode bytes to Base64 bytes + base64_bytes = base64.b64encode(message_bytes) + + # 3. Decode back to a string for transport/output + base64_message = base64_bytes.decode('utf-8') + + return base64_message + +# Example Usage +encoded_output = prepare_for_atob(original_text) + +print(f"Original: {original_text}") +print(f"For atob(): {encoded_output}") \ No newline at end of file