diff --git a/README.md b/README.md
index bd96ed9..9c5c26c 100644
--- a/README.md
+++ b/README.md
@@ -81,6 +81,26 @@ The application uses a local Express proxy server (`proxy-server.js`) to communi
- Keeps the API key secure from browser developer tools
- Provides a single point for API request/response logging
+**The proxy server is automatically started when you run `npm start`** and runs on port 3001 alongside the Vite dev server (port 5173).
+
+#### Troubleshooting the Proxy Server
+
+If you encounter errors like "Failed to fetch" or "Network request failed" when generating briefings:
+
+1. **Check if the proxy server is running**: Look for console output showing `Proxy server listening on port 3001`
+2. **Restart the application**: The easiest way to restart both servers is to:
+ - Stop the current process (Ctrl+C in terminal)
+ - Run `npm start` again
+
+If you need to run the servers separately for debugging:
+```bash
+# Terminal 1: Run proxy server only
+npm run proxy
+
+# Terminal 2: Run Vite dev server only
+npm run dev
+```
+
### Source Parser
The `sourceParser.js` service intelligently handles various data formats:
diff --git a/claudash/src/components/Dashboard.jsx b/claudash/src/components/Dashboard.jsx
index dd69fb0..3e0ba02 100644
--- a/claudash/src/components/Dashboard.jsx
+++ b/claudash/src/components/Dashboard.jsx
@@ -17,6 +17,7 @@ function Dashboard({ themeToggle, isDark }) {
const [error, setError] = useState(null);
const [refreshing, setRefreshing] = useState(false);
const [tokenUsage, setTokenUsage] = useState(null);
+ const [cacheInfo, setCacheInfo] = useState(null);
useEffect(() => {
loadDashboard();
@@ -34,6 +35,18 @@ function Dashboard({ themeToggle, isDark }) {
const todaysBriefing = getTodaysBriefing();
setBriefing(todaysBriefing.briefing);
setSourceData(todaysBriefing.sources || {});
+
+ // Calculate cache age
+ const cacheAge = Date.now() - todaysBriefing.timestamp;
+ const cacheAgeMinutes = Math.floor(cacheAge / (1000 * 60));
+
+ // Set cache info to display message
+ setCacheInfo({
+ age: cacheAgeMinutes,
+ timestamp: todaysBriefing.timestamp
+ });
+ setTokenUsage(null); // Clear token usage when using cache
+
setLoading(false);
return;
}
@@ -83,10 +96,12 @@ function Dashboard({ themeToggle, isDark }) {
if (result && typeof result === 'object' && result.text) {
setBriefing(result.text);
setTokenUsage(result.usage);
+ setCacheInfo(null); // Clear cache info when generating fresh briefing
console.log('Token usage:', result.usage);
} else if (typeof result === 'string') {
// Fallback for string responses
setBriefing(result);
+ setCacheInfo(null);
}
// Save to localStorage
@@ -186,6 +201,18 @@ function Dashboard({ themeToggle, isDark }) {
)}
+ {cacheInfo && (
+
+
+ 💾 Using cached results from {cacheInfo.age < 1 ? 'less than a minute' : cacheInfo.age === 1 ? '1 minute' : `${cacheInfo.age} minutes`} ago — no API costs incurred
+ {' '}
+
+ (Click refresh to generate a new briefing)
+
+
+
+ )}
+
{/* Widget Grid - easily rearrangeable */}