mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-09 18:15:50 +00:00
Converge GitHub star banner on shared action primitives
This commit is contained in:
@@ -145,6 +145,7 @@ work extends shared components instead of creating new local variants.
|
||||
115. `frontend-modern/src/utils/upgradeNavigation.ts`
|
||||
116. `frontend-modern/src/components/DemoBanner.tsx`
|
||||
116a. `frontend-modern/src/components/CommercialMigrationBanner.tsx`
|
||||
116b. `frontend-modern/src/components/GitHubStarBanner.tsx`
|
||||
117. `frontend-modern/src/components/Login.tsx`
|
||||
118. `frontend-modern/src/stores/sessionCapabilities.ts`
|
||||
119. `frontend-modern/src/stores/sessionPresentationPolicy.ts`
|
||||
@@ -548,6 +549,11 @@ not a replacement status card, CTA band, or page-local nested card.
|
||||
disabled treatment, title fallback, and accessible name wiring must come
|
||||
from that shared primitive rather than page-local `<button>` plus inline SVG
|
||||
shells.
|
||||
Global app-shell prompts are part of the same action boundary.
|
||||
`frontend-modern/src/components/GitHubStarBanner.tsx` may own its display
|
||||
timing, product copy, and GitHub destination, but its primary, defer, and
|
||||
dismiss controls must compose `Button` and `ActionIconButton` instead of
|
||||
carrying local floating-prompt button shells.
|
||||
Settings selection helpers such as `ResourcePicker` must use the same
|
||||
`Button` primitive for select-all, clear, and chip remove actions instead of
|
||||
restoring footer-local action shells.
|
||||
|
||||
@@ -3124,6 +3124,7 @@
|
||||
"frontend-modern/scripts/shared-template-audit.mjs",
|
||||
"frontend-modern/scripts/shared-template-registry.json",
|
||||
"frontend-modern/src/components/CommercialMigrationBanner.tsx",
|
||||
"frontend-modern/src/components/GitHubStarBanner.tsx",
|
||||
"frontend-modern/src/components/Login.tsx",
|
||||
"frontend-modern/src/components/Settings/AgentIntegrationsPanel.tsx",
|
||||
"frontend-modern/src/components/Settings/AIChatMaintenanceSection.tsx",
|
||||
@@ -3261,6 +3262,21 @@
|
||||
"frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "global-github-star-banner",
|
||||
"label": "global GitHub star banner primitive proof",
|
||||
"match_prefixes": [],
|
||||
"match_files": [
|
||||
"frontend-modern/src/components/GitHubStarBanner.tsx"
|
||||
],
|
||||
"allow_same_subsystem_tests": false,
|
||||
"test_prefixes": [],
|
||||
"exact_files": [
|
||||
"frontend-modern/src/components/__tests__/GitHubStarBanner.test.tsx",
|
||||
"frontend-modern/src/components/shared/Button.test.tsx",
|
||||
"frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "page-controls-and-filters",
|
||||
"label": "page controls and filter proof",
|
||||
|
||||
@@ -574,6 +574,32 @@
|
||||
"scripts/shared-template-audit.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "github-star-banner-action-shell",
|
||||
"category": "action-button",
|
||||
"summary": "The GitHub star prompt must compose Button and ActionIconButton for its primary, defer, and dismiss actions instead of carrying local floating-prompt button shells.",
|
||||
"canonical": {
|
||||
"path": "src/components/shared/Button.tsx",
|
||||
"export": "Button"
|
||||
},
|
||||
"requiredConsumers": [{ "path": "src/components/GitHubStarBanner.tsx" }],
|
||||
"forbiddenPatterns": [
|
||||
{
|
||||
"path": "src/components/GitHubStarBanner.tsx",
|
||||
"patterns": [
|
||||
"inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted transition-colors hover:bg-surface-hover hover:text-base-content",
|
||||
"inline-flex min-h-9 items-center justify-center gap-2 rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700",
|
||||
"inline-flex min-h-9 items-center justify-center rounded-md px-3 py-2 text-sm text-muted transition-colors hover:bg-surface-hover hover:text-base-content"
|
||||
]
|
||||
}
|
||||
],
|
||||
"proof": [
|
||||
"src/components/shared/SharedPrimitives.guardrails.test.ts",
|
||||
"src/components/__tests__/GitHubStarBanner.test.tsx",
|
||||
"src/components/shared/Button.test.tsx",
|
||||
"scripts/shared-template-audit.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "status-dot-shell",
|
||||
"category": "status-indicator",
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
STORAGE_KEYS,
|
||||
} from '@/utils/localStorage';
|
||||
import { useWebSocket } from '@/contexts/appRuntime';
|
||||
import { ActionIconButton, Button } from '@/components/shared/Button';
|
||||
import GithubIcon from 'lucide-solid/icons/github';
|
||||
import StarIcon from 'lucide-solid/icons/star';
|
||||
import XIcon from 'lucide-solid/icons/x';
|
||||
@@ -137,36 +138,35 @@ export function GitHubStarBanner() {
|
||||
Enjoying Pulse?
|
||||
</h2>
|
||||
<p class="mt-1 text-xs leading-5 text-muted">
|
||||
Pulse is built and maintained by an independent developer. If it's been useful
|
||||
for monitoring your infrastructure, a GitHub star helps more than you'd think.
|
||||
Pulse is built and maintained by an independent developer. If it's been useful for
|
||||
monitoring your infrastructure, a GitHub star helps more than you'd think.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
<ActionIconButton
|
||||
onClick={handleDismiss}
|
||||
class="inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted transition-colors hover:bg-surface-hover hover:text-base-content"
|
||||
label="Close and don't show again"
|
||||
title="Don't show again"
|
||||
aria-label="Close and don't show again"
|
||||
tone="muted"
|
||||
size="sm"
|
||||
type="button"
|
||||
>
|
||||
<XIcon class="h-4 w-4" />
|
||||
</button>
|
||||
<XIcon class="h-4 w-4" aria-hidden="true" />
|
||||
</ActionIconButton>
|
||||
</div>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<button
|
||||
<Button
|
||||
onClick={handleStarClick}
|
||||
class="inline-flex min-h-9 items-center justify-center gap-2 rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700"
|
||||
variant="primary"
|
||||
size="mdCompact"
|
||||
class="gap-2"
|
||||
type="button"
|
||||
>
|
||||
<StarIcon class="h-4 w-4" />
|
||||
<StarIcon class="h-4 w-4" aria-hidden="true" />
|
||||
Star on GitHub
|
||||
</button>
|
||||
<button
|
||||
onClick={handleMaybeLater}
|
||||
class="inline-flex min-h-9 items-center justify-center rounded-md px-3 py-2 text-sm text-muted transition-colors hover:bg-surface-hover hover:text-base-content"
|
||||
type="button"
|
||||
>
|
||||
</Button>
|
||||
<Button onClick={handleMaybeLater} variant="ghost" size="mdCompact" type="button">
|
||||
Maybe later
|
||||
</button>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -30,6 +30,12 @@ vi.mock('@/utils/logger', () => ({
|
||||
const DISMISSED_KEY = 'pulse-github-star-dismissed';
|
||||
const FIRST_SEEN_KEY = 'pulse-github-star-first-seen';
|
||||
const SNOOZED_KEY = 'pulse-github-star-snoozed-until';
|
||||
const LOCAL_DISMISS_BUTTON_CLASS =
|
||||
'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted transition-colors hover:bg-surface-hover hover:text-base-content';
|
||||
const LOCAL_PRIMARY_BUTTON_CLASS =
|
||||
'inline-flex min-h-9 items-center justify-center gap-2 rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700';
|
||||
const LOCAL_DEFER_BUTTON_CLASS =
|
||||
'inline-flex min-h-9 items-center justify-center rounded-md px-3 py-2 text-sm text-muted transition-colors hover:bg-surface-hover hover:text-base-content';
|
||||
|
||||
async function renderBanner() {
|
||||
const mod = await import('../GitHubStarBanner');
|
||||
@@ -84,6 +90,15 @@ describe('GitHubStarBanner', () => {
|
||||
expect(gitHubStarBannerSource).toContain('z-30');
|
||||
});
|
||||
|
||||
it('routes prompt action chrome through shared Button primitives', () => {
|
||||
expect(gitHubStarBannerSource).toContain('@/components/shared/Button');
|
||||
expect(gitHubStarBannerSource).toContain('<ActionIconButton');
|
||||
expect(gitHubStarBannerSource).toContain('<Button');
|
||||
expect(gitHubStarBannerSource).not.toContain(LOCAL_DISMISS_BUTTON_CLASS);
|
||||
expect(gitHubStarBannerSource).not.toContain(LOCAL_PRIMARY_BUTTON_CLASS);
|
||||
expect(gitHubStarBannerSource).not.toContain(LOCAL_DEFER_BUTTON_CLASS);
|
||||
});
|
||||
|
||||
it('does not render on the first day infrastructure is seen (records first-seen date)', async () => {
|
||||
vi.setSystemTime(new Date('2026-03-01T12:00:00Z'));
|
||||
setResourceCount(3);
|
||||
|
||||
@@ -5,6 +5,7 @@ import calloutCardSource from '@/components/shared/CalloutCard.tsx?raw';
|
||||
import inlineNoticeSource from '@/components/shared/InlineNotice.tsx?raw';
|
||||
import demoBannerSource from '@/components/DemoBanner.tsx?raw';
|
||||
import commercialMigrationBannerSource from '@/components/CommercialMigrationBanner.tsx?raw';
|
||||
import gitHubStarBannerSource from '@/components/GitHubStarBanner.tsx?raw';
|
||||
import assistantCommandHelpDialogSource from '@/components/AI/Chat/AssistantCommandHelpDialog.tsx?raw';
|
||||
import chatMessagesSource from '@/components/AI/Chat/ChatMessages.tsx?raw';
|
||||
import aiChatSource from '@/components/AI/Chat/index.tsx?raw';
|
||||
@@ -4033,6 +4034,48 @@ describe('shared primitive guardrails', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('routes GitHub star prompt actions through Button primitives', () => {
|
||||
const registry = JSON.parse(sharedTemplateRegistrySource) as {
|
||||
rules?: Array<{
|
||||
id: string;
|
||||
canonical?: { path?: string; export?: string };
|
||||
requiredConsumers?: Array<{ path?: string }>;
|
||||
forbiddenPatterns?: Array<{ path?: string; patterns?: string[] }>;
|
||||
}>;
|
||||
};
|
||||
const registeredRule = registry.rules?.find(
|
||||
(rule) => rule.id === 'github-star-banner-action-shell',
|
||||
);
|
||||
|
||||
expect(registeredRule?.canonical?.path).toBe('src/components/shared/Button.tsx');
|
||||
expect(registeredRule?.canonical?.export).toBe('Button');
|
||||
expect(registeredRule?.requiredConsumers?.map((consumer) => consumer.path)).toEqual([
|
||||
'src/components/GitHubStarBanner.tsx',
|
||||
]);
|
||||
expect(registeredRule?.forbiddenPatterns).toEqual([
|
||||
{
|
||||
path: 'src/components/GitHubStarBanner.tsx',
|
||||
patterns: [
|
||||
'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted transition-colors hover:bg-surface-hover hover:text-base-content',
|
||||
'inline-flex min-h-9 items-center justify-center gap-2 rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700',
|
||||
'inline-flex min-h-9 items-center justify-center rounded-md px-3 py-2 text-sm text-muted transition-colors hover:bg-surface-hover hover:text-base-content',
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(gitHubStarBannerSource).toContain('@/components/shared/Button');
|
||||
expect(gitHubStarBannerSource).toContain('<ActionIconButton');
|
||||
expect(gitHubStarBannerSource).toContain('<Button');
|
||||
expect(gitHubStarBannerSource).not.toContain(
|
||||
'inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted transition-colors hover:bg-surface-hover hover:text-base-content',
|
||||
);
|
||||
expect(gitHubStarBannerSource).not.toContain(
|
||||
'inline-flex min-h-9 items-center justify-center gap-2 rounded-md bg-blue-600 px-3 py-2 text-sm font-medium text-white transition-colors hover:bg-blue-700',
|
||||
);
|
||||
expect(gitHubStarBannerSource).not.toContain(
|
||||
'inline-flex min-h-9 items-center justify-center rounded-md px-3 py-2 text-sm text-muted transition-colors hover:bg-surface-hover hover:text-base-content',
|
||||
);
|
||||
});
|
||||
|
||||
it('keeps TLS verification warnings in the shared primitive boundary', () => {
|
||||
expect(tlsVerificationWarningBannerSource).toContain('role="alert"');
|
||||
expect(tlsVerificationWarningBannerSource).toContain('TLS verification disabled.');
|
||||
|
||||
Reference in New Issue
Block a user