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/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..de3d50e 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
@@ -50,8 +53,8 @@ const DEFAULT_GLOBALS = {
skipSquare: false,
skipParens: false,
skipCurly: false,
- skipCitations: true,
- skipSuperscriptCitations: false,
+ skipCitations: true,
+ skipSuperscriptCitations: true,
visualIndicator: true
},
customPronunciations: [],
@@ -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 */}
+
+

+
+
+ );
+};
+
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 && (
+
+ )}
{/* 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