Add CI and Release workflows; enhance loading skeletons and splash screen
- Introduced CI workflow for automated testing and validation on push and pull request events. - Added Release workflow for packaging and distributing the application on version tag pushes. - Enhanced loading skeletons in ChatHistory and ProjectsGrid components for improved visual feedback. - Updated splash screen with new animations and styles for better user experience. - Refined CSS variables for skeleton loading states to ensure consistency across themes.
This commit is contained in:
parent
5a0e4c474f
commit
e8f65cf492
7 changed files with 286 additions and 73 deletions
45
.github/workflows/ci.yml
vendored
Normal file
45
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
validate:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.25.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
- name: Typecheck
|
||||
run: pnpm typecheck
|
||||
|
||||
- name: Lint
|
||||
run: pnpm lint
|
||||
|
||||
- name: Test
|
||||
run: pnpm test
|
||||
|
||||
- name: Build
|
||||
run: pnpm build
|
||||
48
.github/workflows/release.yml
vendored
Normal file
48
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
package:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4
|
||||
with:
|
||||
version: 10.25.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: pnpm
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
|
||||
- name: Build app
|
||||
run: pnpm build
|
||||
|
||||
- name: Package macOS
|
||||
env:
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
run: pnpm dist:mac
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-macos
|
||||
path: release/**
|
||||
if-no-files-found: error
|
||||
|
|
@ -1,20 +1,40 @@
|
|||
/**
|
||||
* Loading skeleton for ChatHistory while conversation is loading.
|
||||
* Industrial shimmer with organic line widths — no generic pulse.
|
||||
*/
|
||||
export const ChatHistoryLoadingState = (): JSX.Element => {
|
||||
const rows = [
|
||||
{ user: ['85%', '60%'], ai: ['92%', '70%', '82%', '45%'] },
|
||||
{ user: ['75%', '92%', '40%'], ai: ['88%', '65%', '78%'] },
|
||||
{ user: ['95%', '55%'], ai: ['72%', '85%', '60%', '92%', '35%'] },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 items-center justify-center overflow-hidden bg-[#141416]">
|
||||
<div className="flex flex-1 items-center justify-center overflow-hidden bg-surface">
|
||||
<div className="w-full max-w-5xl space-y-8 px-6">
|
||||
{/* Loading skeleton */}
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div key={i} className="animate-pulse space-y-6">
|
||||
{/* User message skeleton - right aligned */}
|
||||
{rows.map((row, i) => (
|
||||
<div key={i} className="space-y-6">
|
||||
{/* User message skeleton — right aligned */}
|
||||
<div className="flex justify-end">
|
||||
<div className="h-16 w-2/3 rounded-2xl rounded-br-sm border border-white/5 bg-[#27272A]/50" />
|
||||
<div className="w-2/3 space-y-2">
|
||||
{row.user.map((width, j) => (
|
||||
<div
|
||||
key={j}
|
||||
className="skeleton-shimmer ml-auto h-3 rounded-sm"
|
||||
style={{ width, backgroundColor: 'var(--skeleton-base)' }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* AI response skeleton - left aligned with border accent */}
|
||||
<div className="border-l-2 border-white/5 pl-3">
|
||||
<div className="h-24 w-full rounded-lg bg-[#27272A]/30" />
|
||||
{/* AI response skeleton — left aligned with border accent */}
|
||||
<div className="space-y-2.5 border-l-2 border-border pl-3">
|
||||
{row.ai.map((width, j) => (
|
||||
<div
|
||||
key={j}
|
||||
className="skeleton-shimmer h-3 rounded-sm"
|
||||
style={{ width, backgroundColor: 'var(--skeleton-base)' }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const CommandSearch = ({ value, onChange }: Readonly<CommandSearchProps>): React
|
|||
<div className="relative mx-auto w-full max-w-xl">
|
||||
{/* Search container with glow effect on focus */}
|
||||
<div
|
||||
className={`relative flex items-center gap-3 rounded-lg border bg-surface-raised px-4 py-3 transition-all duration-200 ${
|
||||
className={`relative flex items-center gap-3 rounded-sm border bg-surface-raised px-4 py-3 transition-all duration-200 ${
|
||||
isFocused
|
||||
? 'border-zinc-500 shadow-[0_0_20px_rgba(255,255,255,0.04)] ring-1 ring-zinc-600/30'
|
||||
: 'border-border hover:border-zinc-600'
|
||||
|
|
@ -154,14 +154,14 @@ const RepositoryCard = ({
|
|||
return (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`group relative flex min-h-[120px] flex-col overflow-hidden rounded-xl border p-4 text-left transition-all duration-300 ${
|
||||
className={`group relative flex min-h-[120px] flex-col overflow-hidden rounded-sm border p-4 text-left transition-all duration-300 ${
|
||||
isHighlighted
|
||||
? 'border-border-emphasis bg-surface-raised'
|
||||
: 'bg-surface/50 border-border hover:border-border-emphasis hover:bg-surface-raised'
|
||||
} `}
|
||||
>
|
||||
{/* Icon with subtle border */}
|
||||
<div className="mb-3 flex size-8 items-center justify-center rounded-lg border border-border bg-surface-overlay transition-colors duration-300 group-hover:border-border-emphasis">
|
||||
<div className="mb-3 flex size-8 items-center justify-center rounded-sm border border-border bg-surface-overlay transition-colors duration-300 group-hover:border-border-emphasis">
|
||||
<FolderGit2 className="size-4 text-text-secondary transition-colors group-hover:text-text" />
|
||||
</div>
|
||||
|
||||
|
|
@ -232,11 +232,11 @@ const NewProjectCard = (): React.JSX.Element => {
|
|||
|
||||
return (
|
||||
<button
|
||||
className="hover:bg-surface/30 group relative flex min-h-[120px] flex-col items-center justify-center rounded-xl border border-dashed border-border bg-transparent p-4 transition-all duration-300 hover:border-border-emphasis"
|
||||
className="hover:bg-surface/30 group relative flex min-h-[120px] flex-col items-center justify-center rounded-sm border border-dashed border-border bg-transparent p-4 transition-all duration-300 hover:border-border-emphasis"
|
||||
onClick={handleClick}
|
||||
title="Select a project folder"
|
||||
>
|
||||
<div className="mb-2 flex size-8 items-center justify-center rounded-lg border border-dashed border-border transition-colors duration-300 group-hover:border-border-emphasis">
|
||||
<div className="mb-2 flex size-8 items-center justify-center rounded-sm border border-dashed border-border transition-colors duration-300 group-hover:border-border-emphasis">
|
||||
<FolderOpen className="size-4 text-text-muted transition-colors group-hover:text-text-secondary" />
|
||||
</div>
|
||||
<span className="text-xs text-text-muted transition-colors group-hover:text-text-secondary">
|
||||
|
|
@ -295,24 +295,52 @@ const ProjectsGrid = ({
|
|||
}, [repositoryGroups, searchQuery, maxProjects]);
|
||||
|
||||
if (repositoryGroupsLoading) {
|
||||
// Organic widths per card — no repeating stamp
|
||||
const titleWidths = [60, 66, 50, 55, 75, 45, 40, 65];
|
||||
const pathWidths = [80, 75, 85, 66, 70, 80, 60, 72];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-3 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{Array.from({ length: 8 }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="skeleton-card bg-surface/50 flex min-h-[120px] flex-col rounded-xl border border-border p-4"
|
||||
style={{ animationDelay: `${i * 80}ms` }}
|
||||
className="skeleton-card flex min-h-[120px] flex-col rounded-sm border border-border p-4"
|
||||
style={{
|
||||
animationDelay: `${i * 80}ms`,
|
||||
backgroundColor: 'var(--skeleton-base)',
|
||||
}}
|
||||
>
|
||||
{/* Icon placeholder */}
|
||||
<div className="mb-3 size-8 rounded-lg bg-surface-raised" />
|
||||
<div
|
||||
className="mb-3 size-8 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--skeleton-base-light)' }}
|
||||
/>
|
||||
{/* Title placeholder */}
|
||||
<div className="mb-2 h-3.5 w-3/5 rounded bg-surface-raised" />
|
||||
<div
|
||||
className="mb-2 h-3.5 rounded-sm"
|
||||
style={{
|
||||
width: `${titleWidths[i]}%`,
|
||||
backgroundColor: 'var(--skeleton-base-light)',
|
||||
}}
|
||||
/>
|
||||
{/* Path placeholder */}
|
||||
<div className="bg-surface-raised/60 mb-auto h-2.5 w-4/5 rounded" />
|
||||
<div
|
||||
className="mb-auto h-2.5 rounded-sm"
|
||||
style={{
|
||||
width: `${pathWidths[i]}%`,
|
||||
backgroundColor: 'var(--skeleton-base-dim)',
|
||||
}}
|
||||
/>
|
||||
{/* Meta row placeholder */}
|
||||
<div className="mt-3 flex gap-2">
|
||||
<div className="bg-surface-raised/40 h-2.5 w-16 rounded" />
|
||||
<div className="bg-surface-raised/40 h-2.5 w-12 rounded" />
|
||||
<div
|
||||
className="h-2.5 w-16 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--skeleton-base-dim)' }}
|
||||
/>
|
||||
<div
|
||||
className="h-2.5 w-12 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--skeleton-base-dim)' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -322,8 +350,8 @@ const ProjectsGrid = ({
|
|||
|
||||
if (filteredRepos.length === 0 && searchQuery.trim()) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-border px-8 py-16">
|
||||
<div className="mb-4 flex size-12 items-center justify-center rounded-xl border border-border bg-surface-raised">
|
||||
<div className="flex flex-col items-center justify-center rounded-sm border border-dashed border-border px-8 py-16">
|
||||
<div className="mb-4 flex size-12 items-center justify-center rounded-sm border border-border bg-surface-raised">
|
||||
<Search className="size-6 text-text-muted" />
|
||||
</div>
|
||||
<p className="mb-1 text-sm text-text-secondary">No projects found</p>
|
||||
|
|
@ -334,8 +362,8 @@ const ProjectsGrid = ({
|
|||
|
||||
if (repositoryGroups.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-border px-8 py-16">
|
||||
<div className="mb-4 flex size-12 items-center justify-center rounded-xl border border-border bg-surface-raised">
|
||||
<div className="flex flex-col items-center justify-center rounded-sm border border-dashed border-border px-8 py-16">
|
||||
<div className="mb-4 flex size-12 items-center justify-center rounded-sm border border-border bg-surface-raised">
|
||||
<FolderGit2 className="size-6 text-text-muted" />
|
||||
</div>
|
||||
<p className="mb-1 text-sm text-text-secondary">No projects found</p>
|
||||
|
|
|
|||
|
|
@ -203,22 +203,28 @@ export const DateGroupedSessions = (): React.JSX.Element => {
|
|||
}
|
||||
|
||||
if (sessionsLoading && sessions.length === 0) {
|
||||
const widths = [
|
||||
{ header: '30%', title: '75%', sub: '90%' },
|
||||
{ header: '22%', title: '60%', sub: '80%' },
|
||||
{ header: '26%', title: '85%', sub: '65%' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="space-y-3">
|
||||
{[...Array<undefined>(3)].map((_, i) => (
|
||||
<div key={i} className="animate-pulse">
|
||||
{widths.map((w, i) => (
|
||||
<div key={i} className="space-y-2">
|
||||
<div
|
||||
className="mb-3 h-3 w-1/4 rounded"
|
||||
style={{ backgroundColor: 'var(--color-surface-raised)' }}
|
||||
className="skeleton-shimmer h-3 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--skeleton-base-dim)', width: w.header }}
|
||||
/>
|
||||
<div
|
||||
className="mb-2 h-4 w-2/3 rounded"
|
||||
style={{ backgroundColor: 'var(--color-surface-raised)' }}
|
||||
className="skeleton-shimmer h-4 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--skeleton-base)', width: w.title }}
|
||||
/>
|
||||
<div
|
||||
className="h-3 w-full rounded"
|
||||
style={{ backgroundColor: 'var(--color-surface-raised)', opacity: 0.5 }}
|
||||
className="skeleton-shimmer h-3 rounded-sm"
|
||||
style={{ backgroundColor: 'var(--skeleton-base-dim)', width: w.sub }}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -186,6 +186,11 @@
|
|||
--context-btn-bg-hover: rgba(255, 255, 255, 0.14);
|
||||
--context-btn-active-bg: rgba(99, 102, 241, 0.45);
|
||||
--context-btn-active-text: #e0e7ff;
|
||||
|
||||
/* Skeleton — tinted deep charcoal (2% cool shift) */
|
||||
--skeleton-base: #24262c;
|
||||
--skeleton-base-light: #2c2e35;
|
||||
--skeleton-base-dim: rgba(36, 38, 44, 0.6);
|
||||
}
|
||||
|
||||
/* Light theme overrides - Warm neutral palette for eye comfort */
|
||||
|
|
@ -371,6 +376,11 @@
|
|||
--context-btn-bg-hover: rgba(0, 0, 0, 0.1);
|
||||
--context-btn-active-bg: rgba(99, 102, 241, 0.35);
|
||||
--context-btn-active-text: #3730a3;
|
||||
|
||||
/* Skeleton — tinted cool gray */
|
||||
--skeleton-base: #d6d8de;
|
||||
--skeleton-base-light: #cdd0d7;
|
||||
--skeleton-base-dim: rgba(205, 208, 215, 0.6);
|
||||
}
|
||||
|
||||
* {
|
||||
|
|
@ -485,21 +495,55 @@ body {
|
|||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.04) 40%,
|
||||
rgba(255, 255, 255, 0.06) 50%,
|
||||
rgba(255, 255, 255, 0.04) 60%,
|
||||
rgba(255, 255, 255, 0.06) 40%,
|
||||
rgba(255, 255, 255, 0.1) 50%,
|
||||
rgba(255, 255, 255, 0.06) 60%,
|
||||
transparent 100%
|
||||
);
|
||||
animation: shimmer 1.8s ease-in-out infinite;
|
||||
animation: shimmer 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
:root.light .skeleton-card::after {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 0, 0, 0.03) 40%,
|
||||
rgba(0, 0, 0, 0.05) 50%,
|
||||
rgba(0, 0, 0, 0.03) 60%,
|
||||
rgba(0, 0, 0, 0.04) 40%,
|
||||
rgba(0, 0, 0, 0.07) 50%,
|
||||
rgba(0, 0, 0, 0.04) 60%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
||||
/* Per-element shimmer — fast, high-contrast sweep */
|
||||
.skeleton-shimmer {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.skeleton-shimmer::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
transform: translateX(-100%);
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(255, 255, 255, 0.06) 40%,
|
||||
rgba(255, 255, 255, 0.1) 50%,
|
||||
rgba(255, 255, 255, 0.06) 60%,
|
||||
transparent 100%
|
||||
);
|
||||
animation: shimmer 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
:root.light .skeleton-shimmer::after {
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 0, 0, 0.04) 40%,
|
||||
rgba(0, 0, 0, 0.07) 50%,
|
||||
rgba(0, 0, 0, 0.04) 60%,
|
||||
transparent 100%
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,18 +6,43 @@
|
|||
<link rel="icon" type="image/png" href="./favicon.png" />
|
||||
<title>Claude Code Context</title>
|
||||
<style>
|
||||
@keyframes splash-slide {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(350%); }
|
||||
/* Splash: spotlight gradient + noise overlay */
|
||||
#splash {
|
||||
position: fixed; inset: 0; z-index: 9999;
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
background: radial-gradient(ellipse 60% 50% at 50% 45%, #1c1c20 0%, #0e0e10 100%);
|
||||
transition: opacity 0.3s ease-out;
|
||||
}
|
||||
#splash-bar {
|
||||
animation: splash-slide 1.2s ease-in-out infinite;
|
||||
#splash-noise {
|
||||
position: absolute; inset: 0; width: 100%; height: 100%;
|
||||
opacity: 0.03; pointer-events: none;
|
||||
}
|
||||
#splash-logo { margin-bottom: 18px; }
|
||||
#splash-text {
|
||||
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', monospace;
|
||||
font-size: 15px; font-weight: 500; letter-spacing: 0.05em;
|
||||
color: #a1a1aa;
|
||||
}
|
||||
|
||||
/* Logo quadrant breathing — clockwise: TL → TR → BR → BL */
|
||||
@keyframes splash-q {
|
||||
0%, 100% { opacity: 0.12; }
|
||||
10%, 25% { opacity: 0.95; }
|
||||
40% { opacity: 0.12; }
|
||||
}
|
||||
.splash-q {
|
||||
animation: splash-q 3.2s cubic-bezier(0.4, 0, 0.2, 1) infinite both;
|
||||
}
|
||||
|
||||
/* Light theme splash overrides */
|
||||
:root.light #splash { background: #f4f4f5; }
|
||||
:root.light #splash-text { color: #18181b; }
|
||||
:root.light #splash-track { background: rgba(0,0,0,0.06); }
|
||||
:root.light #splash-bar { background: rgba(0,0,0,0.15); }
|
||||
:root.light #splash {
|
||||
background: radial-gradient(ellipse 60% 50% at 50% 45%, #fafafa 0%, #e4e4e7 100%);
|
||||
}
|
||||
:root.light #splash-text { color: #52525b; }
|
||||
:root.light #splash-noise { opacity: 0.02; }
|
||||
:root.light .splash-logo-bg { fill: #e4e4e7; }
|
||||
:root.light .splash-logo-shape { fill: #52525b; }
|
||||
</style>
|
||||
<script>
|
||||
// Flash prevention: Apply cached theme before React loads
|
||||
|
|
@ -32,30 +57,27 @@
|
|||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="splash" style="
|
||||
position: fixed; inset: 0; z-index: 9999;
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
background: #141416;
|
||||
transition: opacity 0.3s ease-out;
|
||||
">
|
||||
<img src="./favicon.png" width="56" height="56" alt=""
|
||||
style="border-radius: 14px; margin-bottom: 18px;" />
|
||||
<div id="splash-text" style="
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
font-size: 17px; font-weight: 500; letter-spacing: 0.02em;
|
||||
color: #fafafa;
|
||||
">Claude Code Context</div>
|
||||
<div id="splash-track" style="
|
||||
margin-top: 24px; width: 120px; height: 2px;
|
||||
background: rgba(255,255,255,0.08); border-radius: 1px;
|
||||
overflow: hidden;
|
||||
">
|
||||
<div id="splash-bar" style="
|
||||
width: 40%; height: 100%;
|
||||
background: rgba(255,255,255,0.25); border-radius: 1px;
|
||||
"></div>
|
||||
</div>
|
||||
<div id="splash">
|
||||
<!-- SVG noise texture — matte paper grain -->
|
||||
<svg id="splash-noise" xmlns="http://www.w3.org/2000/svg">
|
||||
<filter id="noiseFilter">
|
||||
<feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/>
|
||||
</filter>
|
||||
<rect width="100%" height="100%" filter="url(#noiseFilter)"/>
|
||||
</svg>
|
||||
<!-- Logo with animated quadrants -->
|
||||
<svg id="splash-logo" viewBox="0 0 56 56" width="56" height="56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect class="splash-logo-bg" width="56" height="56" rx="14" fill="#1c1c20"/>
|
||||
<!-- TL: small circle -->
|
||||
<circle class="splash-q splash-logo-shape" style="animation-delay:0s" cx="19" cy="19" r="5" fill="#d4d4d8"/>
|
||||
<!-- TR: right-facing semicircle -->
|
||||
<path class="splash-q splash-logo-shape" style="animation-delay:0.8s" d="M34,12 A7,7 0 0,1 34,26 Z" fill="#d4d4d8"/>
|
||||
<!-- BR: large circle -->
|
||||
<circle class="splash-q splash-logo-shape" style="animation-delay:1.6s" cx="37" cy="37" r="6.5" fill="#d4d4d8"/>
|
||||
<!-- BL: down-facing semicircle -->
|
||||
<path class="splash-q splash-logo-shape" style="animation-delay:2.4s" d="M12,34 A7,7 0 0,0 26,34 Z" fill="#d4d4d8"/>
|
||||
</svg>
|
||||
<div id="splash-text">Claude Code Context</div>
|
||||
</div>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="./main.tsx"></script>
|
||||
|
|
|
|||
Loading…
Reference in a new issue