From 801bc6f9ac3f25564943d4fad37b7287e8aeac44 Mon Sep 17 00:00:00 2001
From: Pedram Amini
Date: Sat, 13 Dec 2025 02:14:29 -0600
Subject: [PATCH] OAuth enabled but no valid token found. Starting
authentication... Found expired OAuth token, attempting refresh... Token
refresh successful # CHANGES
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Removed macOS quarantine label clearing requirement from README 🎉
- Added smart caching for GitHub user profile fetching 🚀
- Enhanced leaderboard with real-time ranking notifications and improvements 📊
- Added personal best tracking for longest run achievements 🏆
- Improved markdown image rendering with better display properties 🖼️
- Added privacy note for email addresses in registration modal 🔒
- Fixed remote image toggle behavior in markdown preview 🔧
- Added quick action shortcut to open Maestro website 🌐
- Enhanced leaderboard API with ranking position feedback system 📈
- Improved GitHub analytics script performance with user caching ⚡
---
README.md | 2 -
scripts/github-community-analytics.js | 50 ++++++++++++++-----
src/main/index.ts | 30 +++++++++++
src/renderer/App.tsx | 33 ++++++++++++
src/renderer/components/FilePreview.tsx | 14 +++++-
.../LeaderboardRegistrationModal.tsx | 2 +-
src/renderer/components/QuickActionsModal.tsx | 1 +
src/renderer/types/index.ts | 12 +++++
8 files changed, 126 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
index 64c2aa62..765aef62 100644
--- a/README.md
+++ b/README.md
@@ -36,8 +36,6 @@ Download the latest release for your platform from the [Releases](https://github
- **Linux**: `.AppImage`, `.deb`, or `.rpm`
- **Upgrading**: Simply replace the old binary with the new one. All your data (sessions, settings, playbooks, history) persists in your [config directory](#configuration).
-NOTE: On macOS you may need to clear the quarantine label to successfully launch: `xattr -dr com.apple.quarantine Maestro.app`
-
### Requirements
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed and authenticated
diff --git a/scripts/github-community-analytics.js b/scripts/github-community-analytics.js
index 0b7b0553..d53ae2d0 100755
--- a/scripts/github-community-analytics.js
+++ b/scripts/github-community-analytics.js
@@ -586,24 +586,48 @@ async function main() {
uniqueUsers.join('\n')
);
- // Optionally fetch user details
+ // Optionally fetch user details (with caching)
let userDetails = null;
if (fetchDetails) {
- console.log(`\nFetching details for ${uniqueUsers.length} users (this may take a while)...`);
- userDetails = {};
- for (let i = 0; i < uniqueUsers.length; i++) {
- const username = uniqueUsers[i];
- process.stdout.write(` [${i + 1}/${uniqueUsers.length}] ${username}...`);
- userDetails[username] = fetchUserDetails(username);
- console.log(' done');
-
- // Rate limiting - GitHub allows 5000 requests/hour for authenticated users
- if (i > 0 && i % 50 === 0) {
- console.log(' Pausing for rate limiting...');
- await new Promise(r => setTimeout(r, 2000));
+ // Load existing cached user details
+ const cacheFile = path.join(OUTPUT_DIR, 'user_details.json');
+ let cachedDetails = {};
+ if (fs.existsSync(cacheFile)) {
+ try {
+ cachedDetails = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
+ console.log(`\nLoaded ${Object.keys(cachedDetails).length} cached user profiles`);
+ } catch (e) {
+ console.log('\nCould not load cache, starting fresh');
}
}
+ // Determine which users need fetching
+ const usersToFetch = uniqueUsers.filter(u => !cachedDetails[u]);
+ const cachedUsers = uniqueUsers.filter(u => cachedDetails[u]);
+
+ console.log(` ${cachedUsers.length} users already cached`);
+ console.log(` ${usersToFetch.length} users need fetching`);
+
+ userDetails = { ...cachedDetails };
+
+ if (usersToFetch.length > 0) {
+ console.log(`\nFetching details for ${usersToFetch.length} new users...`);
+ for (let i = 0; i < usersToFetch.length; i++) {
+ const username = usersToFetch[i];
+ process.stdout.write(` [${i + 1}/${usersToFetch.length}] ${username}...`);
+ userDetails[username] = fetchUserDetails(username);
+ console.log(' done');
+
+ // Rate limiting - GitHub allows 5000 requests/hour for authenticated users
+ if (i > 0 && i % 50 === 0) {
+ console.log(' Pausing for rate limiting...');
+ await new Promise(r => setTimeout(r, 2000));
+ }
+ }
+ } else {
+ console.log('\nAll user details already cached, no API calls needed');
+ }
+
fs.writeFileSync(
path.join(OUTPUT_DIR, 'user_details.json'),
JSON.stringify(userDetails, null, 2)
diff --git a/src/main/index.ts b/src/main/index.ts
index 3cb08134..f72594f9 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -5022,6 +5022,20 @@ function setupIpcHandlers() {
requiresConfirmation?: boolean;
confirmationUrl?: string;
error?: string;
+ ranking?: {
+ cumulative: {
+ rank: number;
+ total: number;
+ previousRank: number | null;
+ improved: boolean;
+ };
+ longestRun: {
+ rank: number;
+ total: number;
+ previousRank: number | null;
+ improved: boolean;
+ } | null;
+ };
}> => {
try {
logger.info('Submitting leaderboard entry', 'Leaderboard', {
@@ -5045,17 +5059,33 @@ function setupIpcHandlers() {
requiresConfirmation?: boolean;
confirmationUrl?: string;
error?: string;
+ ranking?: {
+ cumulative: {
+ rank: number;
+ total: number;
+ previousRank: number | null;
+ improved: boolean;
+ };
+ longestRun: {
+ rank: number;
+ total: number;
+ previousRank: number | null;
+ improved: boolean;
+ } | null;
+ };
};
if (response.ok) {
logger.info('Leaderboard submission successful', 'Leaderboard', {
requiresConfirmation: result.requiresConfirmation,
+ ranking: result.ranking,
});
return {
success: true,
message: result.message || 'Submission received',
requiresConfirmation: result.requiresConfirmation,
confirmationUrl: result.confirmationUrl,
+ ranking: result.ranking,
};
} else {
logger.warn('Leaderboard submission failed', 'Leaderboard', {
diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx
index 29d7a8b5..3ff515bd 100644
--- a/src/renderer/App.tsx
+++ b/src/renderer/App.tsx
@@ -2743,6 +2743,39 @@ export default function MaestroConsole() {
lastSubmissionAt: Date.now(),
emailConfirmed: !result.requiresConfirmation,
});
+
+ // Show ranking notification if available
+ if (result.ranking) {
+ const { cumulative, longestRun } = result.ranking;
+ let message = '';
+
+ // Build cumulative ranking message
+ if (cumulative.previousRank === null) {
+ // New entry
+ message = `You're ranked #${cumulative.rank} of ${cumulative.total}!`;
+ } else if (cumulative.improved) {
+ // Moved up
+ const spotsUp = cumulative.previousRank - cumulative.rank;
+ message = `You moved up ${spotsUp} spot${spotsUp > 1 ? 's' : ''}! Now #${cumulative.rank} (was #${cumulative.previousRank})`;
+ } else if (cumulative.rank === cumulative.previousRank) {
+ // Holding steady
+ message = `You're holding steady at #${cumulative.rank}`;
+ } else {
+ // Dropped (shouldn't happen often, but handle it)
+ message = `You're now #${cumulative.rank} of ${cumulative.total}`;
+ }
+
+ // Add longest run info if it's a new record or improved
+ if (longestRun && isNewRecord) {
+ message += ` | New personal best! #${longestRun.rank} on longest runs!`;
+ }
+
+ addToastRef.current({
+ type: 'success',
+ title: 'Leaderboard Updated',
+ message,
+ });
+ }
}
// Silent failure - don't bother the user if submission fails
}).catch(() => {
diff --git a/src/renderer/components/FilePreview.tsx b/src/renderer/components/FilePreview.tsx
index 87dbc81f..446207cf 100644
--- a/src/renderer/components/FilePreview.tsx
+++ b/src/renderer/components/FilePreview.tsx
@@ -164,7 +164,11 @@ function MarkdownImage({
const isRemoteUrl = src?.startsWith('http://') || src?.startsWith('https://');
useEffect(() => {
+ // Reset state when src or showRemoteImages changes
+ setError(null);
+
if (!src) {
+ setDataUrl(null);
setLoading(false);
return;
}
@@ -181,12 +185,16 @@ function MarkdownImage({
if (showRemoteImages) {
setDataUrl(src);
} else {
+ // Explicitly clear the dataUrl when hiding remote images
setDataUrl(null);
}
setLoading(false);
return;
}
+ // For local files, we need to load them
+ setLoading(true);
+
// Resolve the path relative to the markdown file
const resolvedPath = resolveImagePath(src, markdownFilePath);
@@ -253,7 +261,7 @@ function MarkdownImage({
);
@@ -1053,6 +1061,7 @@ export function FilePreview({ file, onClose, theme, markdownEditMode, setMarkdow
.prose th { background-color: ${theme.colors.bgActivity}; font-weight: bold; }
.prose strong { font-weight: bold; }
.prose em { font-style: italic; }
+ .prose img { display: block; max-width: 100%; height: auto; }
`}
{hoveredLink.url}
diff --git a/src/renderer/components/LeaderboardRegistrationModal.tsx b/src/renderer/components/LeaderboardRegistrationModal.tsx
index 044b4871..42df72ce 100644
--- a/src/renderer/components/LeaderboardRegistrationModal.tsx
+++ b/src/renderer/components/LeaderboardRegistrationModal.tsx
@@ -286,7 +286,7 @@ export function LeaderboardRegistrationModal({
)}
- You'll receive a confirmation email to verify your registration
+ Your email is kept private and will not be displayed on the leaderboard
diff --git a/src/renderer/components/QuickActionsModal.tsx b/src/renderer/components/QuickActionsModal.tsx
index cf149567..48e18de4 100644
--- a/src/renderer/components/QuickActionsModal.tsx
+++ b/src/renderer/components/QuickActionsModal.tsx
@@ -257,6 +257,7 @@ export function QuickActionsModal(props: QuickActionsModalProps) {
} }] : []),
{ id: 'devtools', label: 'Toggle JavaScript Console', action: () => { window.maestro.devtools.toggle(); setQuickActionOpen(false); } },
{ id: 'about', label: 'About Maestro', action: () => { setAboutModalOpen(true); setQuickActionOpen(false); } },
+ { id: 'website', label: 'Maestro Website', subtext: 'Open the Maestro website', action: () => { window.maestro.shell.openExternal('https://runmaestro.ai/'); setQuickActionOpen(false); } },
{ id: 'discord', label: 'Join Discord', subtext: 'Join the Maestro community', action: () => { window.maestro.shell.openExternal('https://discord.gg/86crXbGb'); setQuickActionOpen(false); } },
...(setUpdateCheckModalOpen ? [{ id: 'updateCheck', label: 'Check for Updates', action: () => { setUpdateCheckModalOpen(true); setQuickActionOpen(false); } }] : []),
{ id: 'goToFiles', label: 'Go to Files Tab', action: () => { setRightPanelOpen(true); setActiveRightTab('files'); setQuickActionOpen(false); } },
diff --git a/src/renderer/types/index.ts b/src/renderer/types/index.ts
index 66f86d14..3ffb179e 100644
--- a/src/renderer/types/index.ts
+++ b/src/renderer/types/index.ts
@@ -466,6 +466,14 @@ export interface LeaderboardRegistration {
lastSubmissionAt?: number; // Last successful submission timestamp
}
+// Ranking info for a single leaderboard category
+export interface LeaderboardRankingInfo {
+ rank: number; // User's position (1 = first place)
+ total: number; // Total entries on leaderboard
+ previousRank: number | null; // Previous position (null if new entry)
+ improved: boolean; // Did they move up?
+}
+
// Response from leaderboard submission API
export interface LeaderboardSubmitResponse {
success: boolean;
@@ -473,5 +481,9 @@ export interface LeaderboardSubmitResponse {
requiresConfirmation?: boolean;
confirmationUrl?: string;
error?: string;
+ ranking?: {
+ cumulative: LeaderboardRankingInfo;
+ longestRun: LeaderboardRankingInfo | null; // null if no longestRunMs submitted
+ };
}