FN-5779: restore Star on GitHub header button
Restore the Settings header GitHub star CTA while removing its configurability. - keep the Star on GitHub link in Settings header with refreshed split-pill styling and alignment - reintroduce GitHub star count fetching/caching and click-tracking helpers used by the header control - update Settings modal header-action test expectations to include the GitHub star link - revise the changeset note to reflect removing only the showGitHubStarButton setting/toggle Files changed: .changeset/FN-5777-removal.md | 4 +- packages/dashboard/app/components/SettingsModal.css | 51 ++++++++- packages/dashboard/app/components/SettingsModal.tsx | 118 ++++++++++++++++++++- packages/dashboard/app/components/__tests__/SettingsModal.test.tsx | 4 +- 4 files changed, 170 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-5779 Fusion-Task-Lineage: f485a062-6110-4a79-b42d-70710e3ce5da
This commit is contained in:
@@ -2,6 +2,6 @@
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Removed the Settings modal "Star on GitHub" header button and deleted its `showGitHubStarButton` setting.
|
||||
Removed the `showGitHubStarButton` setting and its Project General toggle from Settings.
|
||||
|
||||
This simplifies the Settings surface by removing a promotional control and all of its related client-side logic/styles (including star-count fetch/cache behavior).
|
||||
The Settings header "Star on GitHub" button remains available (always shown) while the dedicated visibility setting is no longer configurable.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Extracted from styles.css as part of the Sweep-3 CSS extraction effort.
|
||||
Imported by SettingsModal.tsx (and MemoryView.tsx for shared classes). */
|
||||
|
||||
/* === Settings Modal header action buttons === */
|
||||
/* === Settings Modal header action buttons (Star on GitHub, Help) === */
|
||||
.settings-header-actions {
|
||||
--settings-header-action-height: calc(var(--space-md) * 2 + var(--space-xs) / 2);
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
margin-right: var(--space-sm);
|
||||
}
|
||||
|
||||
/* Keep header action buttons at matching heights. */
|
||||
/* Keep the GitHub Star and Help buttons at matching heights regardless of
|
||||
their differing icon sizes (provider icon 16px vs. HelpCircle 13px). */
|
||||
.settings-header-actions > .settings-github-star-btn,
|
||||
.settings-header-actions > .settings-header-discord-btn,
|
||||
.settings-header-actions > .settings-header-help-btn {
|
||||
height: var(--settings-header-action-height);
|
||||
@@ -45,6 +47,51 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* GitHub star button — split-pill layout: [Star half | Count half] */
|
||||
.settings-github-star-btn {
|
||||
display: inline-flex;
|
||||
align-items: stretch;
|
||||
border: var(--btn-border-width) solid var(--border);
|
||||
border-radius: var(--radius-pill);
|
||||
background: var(--card);
|
||||
color: var(--text);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
transition:
|
||||
border-color var(--transition-fast),
|
||||
background var(--transition-fast);
|
||||
}
|
||||
|
||||
.settings-github-star-btn:hover {
|
||||
border-color: var(--text-muted);
|
||||
background: var(--card-hover);
|
||||
}
|
||||
|
||||
.settings-github-star-btn:focus-visible {
|
||||
outline: none;
|
||||
box-shadow: var(--focus-ring-strong);
|
||||
border-color: var(--todo);
|
||||
}
|
||||
|
||||
.settings-github-star-btn__action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-xs) calc(var(--space-sm) + var(--space-xs) / 2);
|
||||
}
|
||||
|
||||
.settings-github-star-btn__count {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: var(--space-xs) calc(var(--space-sm) + var(--space-xs) / 4);
|
||||
border-left: var(--btn-border-width) solid var(--border);
|
||||
background: color-mix(in srgb, var(--surface) 60%, var(--card));
|
||||
color: var(--text-muted);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* === Settings Modal: sizing + resizability === */
|
||||
.settings-modal {
|
||||
width: min(95vw, 1100px);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useState, useEffect, useCallback, useRef, lazy, Suspense, type CSSProperties, type MouseEvent } from "react";
|
||||
import { Globe, Folder, RefreshCw, HelpCircle, MessageCircle, Loader2, CheckCircle, AlertTriangle } from "lucide-react";
|
||||
import { Globe, Folder, RefreshCw, Star, HelpCircle, MessageCircle, Loader2, CheckCircle, AlertTriangle } from "lucide-react";
|
||||
import {
|
||||
AGENT_PERMISSION_POLICY_ACTION_CATEGORIES,
|
||||
THINKING_LEVELS,
|
||||
@@ -58,6 +58,13 @@ import { NodeHealthDot } from "./NodeHealthDot";
|
||||
import { TrackingRepoSelect, type TrackingRepoOption } from "./TrackingRepoSelect";
|
||||
import { filterVisibleOnboardingAndSettingsProviders } from "./providerVisibility";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GitHub star count — fetched once per session, cached in localStorage (1 h).
|
||||
// ---------------------------------------------------------------------------
|
||||
const GITHUB_STAR_CACHE_KEY = "fusion_github_star_count";
|
||||
const GITHUB_STAR_CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
||||
const GITHUB_STAR_CLICKED_KEY = "fusion:github-star-clicked";
|
||||
|
||||
function toCompleteAgentPermissionRules(rules?: Partial<AgentPermissionPolicyRules>): AgentPermissionPolicyRules {
|
||||
return AGENT_PERMISSION_POLICY_ACTION_CATEGORIES.reduce((acc, category) => {
|
||||
acc[category] = rules?.[category] ?? "allow";
|
||||
@@ -83,6 +90,90 @@ function toTrackingRepoOptions(remotes: GitRemote[]): TrackingRepoOption[] {
|
||||
return [...byValue.values()].sort((a, b) => a.value.localeCompare(b.value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the user already clicked the "Star on GitHub" button at any point in
|
||||
* the past? Used to permanently hide the button afterward — clicking opens
|
||||
* the repo where the actual star happens, so we treat that click as intent
|
||||
* to star and stop nagging.
|
||||
*/
|
||||
function useStarClickedFlag(): [boolean, () => void] {
|
||||
const [clicked, setClicked] = useState<boolean>(() => {
|
||||
try {
|
||||
return localStorage.getItem(GITHUB_STAR_CLICKED_KEY) === "true";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
const markClicked = useCallback(() => {
|
||||
setClicked(true);
|
||||
try {
|
||||
localStorage.setItem(GITHUB_STAR_CLICKED_KEY, "true");
|
||||
} catch {
|
||||
// quota / private mode — best-effort
|
||||
}
|
||||
}, []);
|
||||
return [clicked, markClicked];
|
||||
}
|
||||
|
||||
interface StarCache {
|
||||
count: number;
|
||||
fetchedAt: number;
|
||||
}
|
||||
|
||||
function useGitHubStarCount(): number | null {
|
||||
const [count, setCount] = useState<number | null>(() => {
|
||||
try {
|
||||
const raw = localStorage.getItem(GITHUB_STAR_CACHE_KEY);
|
||||
if (raw) {
|
||||
const parsed: StarCache = JSON.parse(raw) as StarCache;
|
||||
if (Date.now() - parsed.fetchedAt < GITHUB_STAR_CACHE_TTL_MS) {
|
||||
return parsed.count;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed cache
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
// If we already have a fresh count from the initial state, skip the fetch.
|
||||
try {
|
||||
const raw = localStorage.getItem(GITHUB_STAR_CACHE_KEY);
|
||||
if (raw) {
|
||||
const parsed: StarCache = JSON.parse(raw) as StarCache;
|
||||
if (Date.now() - parsed.fetchedAt < GITHUB_STAR_CACHE_TTL_MS) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
fetch("https://api.github.com/repos/Runfusion/Fusion")
|
||||
.then((res) => {
|
||||
if (!res.ok) return;
|
||||
return res.json() as Promise<{ stargazers_count?: number }>;
|
||||
})
|
||||
.then((data) => {
|
||||
if (data && typeof data.stargazers_count === "number") {
|
||||
const cache: StarCache = { count: data.stargazers_count, fetchedAt: Date.now() };
|
||||
try {
|
||||
localStorage.setItem(GITHUB_STAR_CACHE_KEY, JSON.stringify(cache));
|
||||
} catch {
|
||||
// quota exceeded — just skip
|
||||
}
|
||||
setCount(data.stargazers_count);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// Network failure — hide count gracefully, no update
|
||||
});
|
||||
}, []);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Settings sections configuration.
|
||||
*
|
||||
@@ -403,6 +494,8 @@ export function SettingsModal({
|
||||
const [appVersion, setAppVersion] = useState<string | null>(null);
|
||||
const [updateCheckLoading, setUpdateCheckLoading] = useState(false);
|
||||
const [updateCheckResult, setUpdateCheckResult] = useState<UpdateCheckResponse | null>(null);
|
||||
const gitHubStarCount = useGitHubStarCount();
|
||||
const [starClicked, markStarClicked] = useStarClickedFlag();
|
||||
const [prefixError, setPrefixError] = useState<string | null>(null);
|
||||
const [researchLimitError, setResearchLimitError] = useState<string | null>(null);
|
||||
const [overlapPathPickerIndex, setOverlapPathPickerIndex] = useState<number | null>(null);
|
||||
@@ -7335,6 +7428,29 @@ export function SettingsModal({
|
||||
<h3>Settings</h3>
|
||||
</div>
|
||||
<div className="settings-header-actions">
|
||||
<a
|
||||
href="https://github.com/Runfusion/Fusion"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="settings-github-star-btn"
|
||||
aria-label="Star Fusion on GitHub"
|
||||
title="Star Fusion on GitHub"
|
||||
onClick={markStarClicked}
|
||||
data-clicked={starClicked ? "true" : "false"}
|
||||
>
|
||||
<span className="settings-github-star-btn__action">
|
||||
<ProviderIcon provider="github" size="sm" />
|
||||
<Star size={11} aria-hidden="true" />
|
||||
Star
|
||||
</span>
|
||||
{gitHubStarCount !== null && (
|
||||
<span className="settings-github-star-btn__count" aria-label={`${gitHubStarCount.toLocaleString()} stars`}>
|
||||
{gitHubStarCount >= 1000
|
||||
? `${(gitHubStarCount / 1000).toFixed(1)}k`
|
||||
: gitHubStarCount.toLocaleString()}
|
||||
</span>
|
||||
)}
|
||||
</a>
|
||||
<a
|
||||
href="https://discord.gg/ksrfuy7WYR"
|
||||
target="_blank"
|
||||
|
||||
@@ -1529,14 +1529,14 @@ describe("SettingsModal", () => {
|
||||
});
|
||||
|
||||
describe("settings header actions", () => {
|
||||
it("renders Help and Discord controls, without GitHub star", async () => {
|
||||
it("renders Help, Discord, and GitHub star controls", async () => {
|
||||
renderModal();
|
||||
await waitForSettingsModalReady();
|
||||
|
||||
const headerActions = document.querySelector(".settings-header-actions");
|
||||
expect(headerActions).toBeInTheDocument();
|
||||
|
||||
expect(within(headerActions as HTMLElement).queryByRole("link", { name: "Star Fusion on GitHub" })).toBeNull();
|
||||
expect(within(headerActions as HTMLElement).getByRole("link", { name: "Star Fusion on GitHub" })).toBeInTheDocument();
|
||||
expect(within(headerActions as HTMLElement).getByRole("link", { name: "Join our Discord" })).toBeInTheDocument();
|
||||
expect(within(headerActions as HTMLElement).getByRole("link", { name: "Help and discussions" })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user