feat(repo-tabs): add context menu for managing repository tabs
This update introduces a context menu for repository tabs, allowing users to easily close individual tabs, close all other tabs, or close tabs to the right. Additionally, several visual elements related to loading animations have been removed to streamline the user interface. - Implement context menu actions for repository tab management - Remove unnecessary loading animations from the splash screen and overlays - Enhance user experience with improved tab management functionality
This commit is contained in:
@@ -66,15 +66,6 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.splash::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
background: linear-gradient(110deg, transparent 0%, rgba(160, 124, 255, 0.12) 48%, transparent 54%);
|
|
||||||
transform: translateX(-100%);
|
|
||||||
animation: card-scan 2.8s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mark {
|
.mark {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -145,18 +136,6 @@
|
|||||||
animation: icon-float 2.4s ease-in-out infinite;
|
animation: icon-float 2.4s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sheen {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
width: 112px;
|
|
||||||
height: 172px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(90deg, transparent 8%, rgba(255, 255, 255, 0.18), transparent 72%);
|
|
||||||
mix-blend-mode: screen;
|
|
||||||
transform: translateX(-98px) rotate(16deg);
|
|
||||||
animation: icon-sheen 2.8s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copy {
|
.copy {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
@@ -202,22 +181,11 @@
|
|||||||
to { transform: translate3d(42px, 42px, 0); }
|
to { transform: translate3d(42px, 42px, 0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes card-scan {
|
|
||||||
0%, 34% { transform: translateX(-100%); }
|
|
||||||
72%, 100% { transform: translateX(100%); }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes icon-float {
|
@keyframes icon-float {
|
||||||
0%, 100% { transform: translateY(0) scale(1); }
|
0%, 100% { transform: translateY(0) scale(1); }
|
||||||
50% { transform: translateY(-5px) scale(1.015); }
|
50% { transform: translateY(-5px) scale(1.015); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes icon-sheen {
|
|
||||||
0%, 18% { transform: translateX(-98px) rotate(16deg); opacity: 0; }
|
|
||||||
44% { opacity: 0.9; }
|
|
||||||
68%, 100% { transform: translateX(98px) rotate(16deg); opacity: 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes halo-breathe {
|
@keyframes halo-breathe {
|
||||||
0%, 100% { opacity: 0.35; transform: rotate(45deg) scale(0.95); }
|
0%, 100% { opacity: 0.35; transform: rotate(45deg) scale(0.95); }
|
||||||
50% { opacity: 0.8; transform: rotate(45deg) scale(1.04); }
|
50% { opacity: 0.8; transform: rotate(45deg) scale(1.04); }
|
||||||
@@ -236,11 +204,9 @@
|
|||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.grid,
|
.grid,
|
||||||
.splash::before,
|
|
||||||
.halo,
|
.halo,
|
||||||
.trace,
|
.trace,
|
||||||
.icon,
|
.icon,
|
||||||
.sheen,
|
|
||||||
.bar span {
|
.bar span {
|
||||||
animation: none;
|
animation: none;
|
||||||
}
|
}
|
||||||
@@ -264,7 +230,6 @@
|
|||||||
<path class="trace base" d="M46 180 L174 180" />
|
<path class="trace base" d="M46 180 L174 180" />
|
||||||
</svg>
|
</svg>
|
||||||
<img class="icon" src="/splash-icon.png" alt="" />
|
<img class="icon" src="/splash-icon.png" alt="" />
|
||||||
<span class="sheen"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="copy">
|
<div class="copy">
|
||||||
<strong>GitLite</strong>
|
<strong>GitLite</strong>
|
||||||
|
|||||||
+112
-3
@@ -145,6 +145,12 @@
|
|||||||
lastOpened: number;
|
lastOpened: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface RepoTabContextMenu {
|
||||||
|
path: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
interface CloneRequest {
|
interface CloneRequest {
|
||||||
remoteUrl: string;
|
remoteUrl: string;
|
||||||
parentPath: string;
|
parentPath: string;
|
||||||
@@ -173,6 +179,7 @@
|
|||||||
let activeRepoPath = "";
|
let activeRepoPath = "";
|
||||||
let activeView: AppView = "management";
|
let activeView: AppView = "management";
|
||||||
let repoTabs: RepoTab[] = [];
|
let repoTabs: RepoTab[] = [];
|
||||||
|
let repoTabContextMenu: RepoTabContextMenu | null = null;
|
||||||
let recentRepoPaths: string[] = [];
|
let recentRepoPaths: string[] = [];
|
||||||
let favoriteRepoPaths: string[] = [];
|
let favoriteRepoPaths: string[] = [];
|
||||||
// Last-seen branch/ahead/behind/changed for repos that are known (recent/favorites)
|
// Last-seen branch/ahead/behind/changed for repos that are known (recent/favorites)
|
||||||
@@ -1427,15 +1434,41 @@
|
|||||||
async function selectRepoTab(path: string) {
|
async function selectRepoTab(path: string) {
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
if (activeView === "repository" && sameRepoPath(activeRepoPath, path)) return;
|
if (activeView === "repository" && sameRepoPath(activeRepoPath, path)) return;
|
||||||
|
closeRepoTabContextMenu();
|
||||||
trackEvent("repository_tab_selected", {
|
trackEvent("repository_tab_selected", {
|
||||||
open_repositories: repoTabs.length,
|
open_repositories: repoTabs.length,
|
||||||
});
|
});
|
||||||
await openRepo(path);
|
await openRepo(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function repoTabIndex(path: string): number {
|
||||||
|
return repoTabs.findIndex((tab) => sameRepoPath(tab.path, path));
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeRepoTabContextMenu() {
|
||||||
|
repoTabContextMenu = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRepoTabContextMenu(path: string, event: MouseEvent) {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (isBusy) return;
|
||||||
|
repoTabContextMenu = {
|
||||||
|
path,
|
||||||
|
x: Math.max(8, Math.min(event.clientX, window.innerWidth - 210)),
|
||||||
|
y: Math.max(8, Math.min(event.clientY, window.innerHeight - 128)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeRepoTabFromContext(path: string) {
|
||||||
|
closeRepoTabContextMenu();
|
||||||
|
await closeRepoTab(path);
|
||||||
|
}
|
||||||
|
|
||||||
async function closeRepoTab(path: string, event?: MouseEvent) {
|
async function closeRepoTab(path: string, event?: MouseEvent) {
|
||||||
event?.stopPropagation();
|
event?.stopPropagation();
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
|
closeRepoTabContextMenu();
|
||||||
|
|
||||||
const index = repoTabs.findIndex((tab) => sameRepoPath(tab.path, path));
|
const index = repoTabs.findIndex((tab) => sameRepoPath(tab.path, path));
|
||||||
const remaining = repoTabs.filter((tab) => !sameRepoPath(tab.path, path));
|
const remaining = repoTabs.filter((tab) => !sameRepoPath(tab.path, path));
|
||||||
@@ -1457,6 +1490,45 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function closeOtherRepoTabs(path: string) {
|
||||||
|
if (isBusy) return;
|
||||||
|
closeRepoTabContextMenu();
|
||||||
|
const target = repoTabs.find((tab) => sameRepoPath(tab.path, path));
|
||||||
|
if (!target || repoTabs.length <= 1) return;
|
||||||
|
|
||||||
|
const closedCount = repoTabs.length - 1;
|
||||||
|
const wasActive = sameRepoPath(activeRepoPath, path);
|
||||||
|
repoTabs = [target];
|
||||||
|
persistRepoLists();
|
||||||
|
trackEvent("repository_tabs_closed", {
|
||||||
|
mode: "others",
|
||||||
|
closed_tabs: closedCount,
|
||||||
|
open_repositories: repoTabs.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!wasActive) await openRepo(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeRepoTabsToRight(path: string) {
|
||||||
|
if (isBusy) return;
|
||||||
|
closeRepoTabContextMenu();
|
||||||
|
const index = repoTabIndex(path);
|
||||||
|
if (index < 0 || index >= repoTabs.length - 1) return;
|
||||||
|
|
||||||
|
const remaining = repoTabs.slice(0, index + 1);
|
||||||
|
const closedCount = repoTabs.length - remaining.length;
|
||||||
|
const activeStillOpen = remaining.some((tab) => sameRepoPath(tab.path, activeRepoPath));
|
||||||
|
repoTabs = remaining;
|
||||||
|
persistRepoLists();
|
||||||
|
trackEvent("repository_tabs_closed", {
|
||||||
|
mode: "right",
|
||||||
|
closed_tabs: closedCount,
|
||||||
|
open_repositories: repoTabs.length,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!activeStillOpen) await openRepo(path);
|
||||||
|
}
|
||||||
|
|
||||||
async function removeRepoFromManagement(path: string, event?: MouseEvent) {
|
async function removeRepoFromManagement(path: string, event?: MouseEvent) {
|
||||||
event?.stopPropagation();
|
event?.stopPropagation();
|
||||||
if (isBusy) return;
|
if (isBusy) return;
|
||||||
@@ -2717,7 +2789,8 @@
|
|||||||
// ── Event handlers ─────────────────────────────────────────────────────────
|
// ── Event handlers ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
function handleWindowKeydown(event: KeyboardEvent) {
|
function handleWindowKeydown(event: KeyboardEvent) {
|
||||||
if (event.key === "Escape" && pendingDiscard && !isBusy) closeDiscardConfirm();
|
if (event.key === "Escape" && repoTabContextMenu) closeRepoTabContextMenu();
|
||||||
|
else if (event.key === "Escape" && pendingDiscard && !isBusy) closeDiscardConfirm();
|
||||||
else if (event.key === "Escape" && compareDialogOpen) closeCompareDialog();
|
else if (event.key === "Escape" && compareDialogOpen) closeCompareDialog();
|
||||||
else if (event.key === "Escape" && newBranchCommit) newBranchCommit = null;
|
else if (event.key === "Escape" && newBranchCommit) newBranchCommit = null;
|
||||||
else if (event.key === "Escape" && renameBranchTarget) renameBranchTarget = null;
|
else if (event.key === "Escape" && renameBranchTarget) renameBranchTarget = null;
|
||||||
@@ -2729,13 +2802,17 @@
|
|||||||
function handleWindowContextMenu(event: MouseEvent) {
|
function handleWindowContextMenu(event: MouseEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleWindowClick() {
|
||||||
|
if (repoTabContextMenu) closeRepoTabContextMenu();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
<title>GitLite</title>
|
<title>GitLite</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<svelte:window on:keydown={handleWindowKeydown} on:contextmenu={handleWindowContextMenu} />
|
<svelte:window on:click={handleWindowClick} on:keydown={handleWindowKeydown} on:contextmenu={handleWindowContextMenu} />
|
||||||
|
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
<TitleBar
|
<TitleBar
|
||||||
@@ -2776,7 +2853,12 @@
|
|||||||
|
|
||||||
<div class="repo-tabs-scroll">
|
<div class="repo-tabs-scroll">
|
||||||
{#each repoTabs as repo (repo.path)}
|
{#each repoTabs as repo (repo.path)}
|
||||||
<div class="repo-tab-wrap" class:active={activeView === "repository" && sameRepoPath(activeRepoPath, repo.path)}>
|
<div
|
||||||
|
class="repo-tab-wrap"
|
||||||
|
class:active={activeView === "repository" && sameRepoPath(activeRepoPath, repo.path)}
|
||||||
|
role="presentation"
|
||||||
|
oncontextmenu={(event) => openRepoTabContextMenu(repo.path, event)}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
class="repo-tab"
|
class="repo-tab"
|
||||||
type="button"
|
type="button"
|
||||||
@@ -2816,6 +2898,33 @@
|
|||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{#if repoTabContextMenu}
|
||||||
|
{@const menu = repoTabContextMenu}
|
||||||
|
{@const contextTab = repoTabs.find((tab) => sameRepoPath(tab.path, menu.path))}
|
||||||
|
{@const contextIndex = repoTabIndex(menu.path)}
|
||||||
|
{#if contextTab}
|
||||||
|
<div
|
||||||
|
class="repo-tab-context-menu"
|
||||||
|
style={`left: ${menu.x}px; top: ${menu.y}px;`}
|
||||||
|
role="menu"
|
||||||
|
tabindex="-1"
|
||||||
|
aria-label={`Tab actions for ${contextTab.name}`}
|
||||||
|
oncontextmenu={(event) => { event.preventDefault(); event.stopPropagation(); }}
|
||||||
|
>
|
||||||
|
<button type="button" role="menuitem" onclick={() => closeRepoTabFromContext(contextTab.path)} disabled={isBusy}>
|
||||||
|
Close tab
|
||||||
|
</button>
|
||||||
|
<div class="menu-separator" aria-hidden="true"></div>
|
||||||
|
<button type="button" role="menuitem" onclick={() => closeOtherRepoTabs(contextTab.path)} disabled={isBusy || repoTabs.length <= 1}>
|
||||||
|
Close other tabs
|
||||||
|
</button>
|
||||||
|
<button type="button" role="menuitem" onclick={() => closeRepoTabsToRight(contextTab.path)} disabled={isBusy || contextIndex < 0 || contextIndex >= repoTabs.length - 1}>
|
||||||
|
Close tabs to the right
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{/if}
|
||||||
|
|
||||||
<!-- Status notices -->
|
<!-- Status notices -->
|
||||||
{#if errorMessage}
|
{#if errorMessage}
|
||||||
<section class="notice error" role="alert">
|
<section class="notice error" role="alert">
|
||||||
|
|||||||
+12
-24
@@ -1671,7 +1671,8 @@
|
|||||||
|
|
||||||
.branch-context-menu,
|
.branch-context-menu,
|
||||||
.history-context-menu,
|
.history-context-menu,
|
||||||
.explorer-context-menu {
|
.explorer-context-menu,
|
||||||
|
.repo-tab-context-menu {
|
||||||
z-index: 120;
|
z-index: 120;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
@@ -1685,11 +1686,13 @@
|
|||||||
|
|
||||||
.branch-context-menu,
|
.branch-context-menu,
|
||||||
.history-context-menu { position: absolute; }
|
.history-context-menu { position: absolute; }
|
||||||
.explorer-context-menu { position: fixed; }
|
.explorer-context-menu,
|
||||||
|
.repo-tab-context-menu { position: fixed; }
|
||||||
|
|
||||||
.branch-context-menu button,
|
.branch-context-menu button,
|
||||||
.history-context-menu button,
|
.history-context-menu button,
|
||||||
.explorer-context-menu button {
|
.explorer-context-menu button,
|
||||||
|
.repo-tab-context-menu button {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
@@ -1708,14 +1711,16 @@
|
|||||||
|
|
||||||
.branch-context-menu button:hover:not(:disabled),
|
.branch-context-menu button:hover:not(:disabled),
|
||||||
.history-context-menu button:hover:not(:disabled),
|
.history-context-menu button:hover:not(:disabled),
|
||||||
.explorer-context-menu button:hover:not(:disabled) {
|
.explorer-context-menu button:hover:not(:disabled),
|
||||||
|
.repo-tab-context-menu button:hover:not(:disabled) {
|
||||||
border-color: var(--color-border-subtle);
|
border-color: var(--color-border-subtle);
|
||||||
background: rgba(255,255,255,0.06);
|
background: rgba(255,255,255,0.06);
|
||||||
color: var(--color-ink);
|
color: var(--color-ink);
|
||||||
}
|
}
|
||||||
|
|
||||||
.branch-context-menu .menu-separator,
|
.branch-context-menu .menu-separator,
|
||||||
.history-context-menu .menu-separator {
|
.history-context-menu .menu-separator,
|
||||||
|
.repo-tab-context-menu .menu-separator {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
margin: 4px 3px;
|
margin: 4px 3px;
|
||||||
background: var(--color-border-subtle);
|
background: var(--color-border-subtle);
|
||||||
@@ -1733,7 +1738,8 @@
|
|||||||
|
|
||||||
.branch-context-menu button:disabled,
|
.branch-context-menu button:disabled,
|
||||||
.history-context-menu button:disabled,
|
.history-context-menu button:disabled,
|
||||||
.explorer-context-menu button:disabled {
|
.explorer-context-menu button:disabled,
|
||||||
|
.repo-tab-context-menu button:disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
opacity: 0.48;
|
opacity: 0.48;
|
||||||
}
|
}
|
||||||
@@ -2223,18 +2229,6 @@
|
|||||||
animation: fhl-icon-float 2.4s ease-in-out infinite;
|
animation: fhl-icon-float 2.4s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.file-history-loading-sheen {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
width: 54px;
|
|
||||||
height: 82px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(90deg, transparent 8%, rgba(255, 255, 255, 0.16), transparent 72%);
|
|
||||||
mix-blend-mode: screen;
|
|
||||||
transform: translateX(-48px) rotate(16deg);
|
|
||||||
animation: fhl-icon-sheen 2.8s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.file-history-loading-label {
|
.file-history-loading-label {
|
||||||
color: var(--color-ink-faint);
|
color: var(--color-ink-faint);
|
||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
@@ -2267,11 +2261,6 @@
|
|||||||
0%, 100% { transform: translateY(0) scale(1); }
|
0%, 100% { transform: translateY(0) scale(1); }
|
||||||
50% { transform: translateY(-3px) scale(1.02); }
|
50% { transform: translateY(-3px) scale(1.02); }
|
||||||
}
|
}
|
||||||
@keyframes fhl-icon-sheen {
|
|
||||||
0%, 18% { transform: translateX(-48px) rotate(16deg); opacity: 0; }
|
|
||||||
44% { opacity: 0.8; }
|
|
||||||
68%, 100% { transform: translateX(50px) rotate(16deg); opacity: 0; }
|
|
||||||
}
|
|
||||||
@keyframes fhl-trace-draw {
|
@keyframes fhl-trace-draw {
|
||||||
0% { stroke-dashoffset: 92; opacity: 0; }
|
0% { stroke-dashoffset: 92; opacity: 0; }
|
||||||
36% { opacity: 1; }
|
36% { opacity: 1; }
|
||||||
@@ -2286,7 +2275,6 @@
|
|||||||
.file-history-loading-halo,
|
.file-history-loading-halo,
|
||||||
.file-history-loading-traces .fhl-trace,
|
.file-history-loading-traces .fhl-trace,
|
||||||
.file-history-loading-icon,
|
.file-history-loading-icon,
|
||||||
.file-history-loading-sheen,
|
|
||||||
.file-history-loading-bar span { animation: none; }
|
.file-history-loading-bar span { animation: none; }
|
||||||
.file-history-loading-traces .fhl-trace { stroke-dashoffset: 0; opacity: 0.72; }
|
.file-history-loading-traces .fhl-trace { stroke-dashoffset: 0; opacity: 0.72; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,6 @@
|
|||||||
<path class="fhl-trace fhl-trace-branch" d="M28 36 C52 48, 70 70, 91 94" />
|
<path class="fhl-trace fhl-trace-branch" d="M28 36 C52 48, 70 70, 91 94" />
|
||||||
</svg>
|
</svg>
|
||||||
<img src={iconUrl} alt="" class="file-history-loading-icon" />
|
<img src={iconUrl} alt="" class="file-history-loading-icon" />
|
||||||
<span class="file-history-loading-sheen"></span>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="file-history-loading-label">Loading history…</span>
|
<span class="file-history-loading-label">Loading history…</span>
|
||||||
<span class="file-history-loading-bar" aria-hidden="true"><span></span></span>
|
<span class="file-history-loading-bar" aria-hidden="true"><span></span></span>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
<path class="trace trace-cut" d="M46 180 L174 180" />
|
<path class="trace trace-cut" d="M46 180 L174 180" />
|
||||||
</svg>
|
</svg>
|
||||||
<img src={iconUrl} alt="" class="repo-loading-icon" />
|
<img src={iconUrl} alt="" class="repo-loading-icon" />
|
||||||
<span class="repo-loading-sheen"></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="repo-loading-text">
|
<div class="repo-loading-text">
|
||||||
@@ -78,17 +77,6 @@
|
|||||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
.repo-loading-card::before {
|
|
||||||
content: "";
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
border-radius: inherit;
|
|
||||||
pointer-events: none;
|
|
||||||
background: linear-gradient(110deg, transparent 0%, rgba(160, 124, 255, 0.1) 48%, transparent 54%);
|
|
||||||
transform: translateX(-100%);
|
|
||||||
animation: card-scan 2.8s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-loading-mark {
|
.repo-loading-mark {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 168px;
|
width: 168px;
|
||||||
@@ -157,18 +145,6 @@
|
|||||||
animation: icon-float 2.4s ease-in-out infinite;
|
animation: icon-float 2.4s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.repo-loading-sheen {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
width: 108px;
|
|
||||||
height: 168px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: linear-gradient(90deg, transparent 8%, rgba(255, 255, 255, 0.18), transparent 72%);
|
|
||||||
transform: translateX(-96px) rotate(16deg);
|
|
||||||
mix-blend-mode: screen;
|
|
||||||
animation: icon-sheen 2.8s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.repo-loading-text {
|
.repo-loading-text {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -213,19 +189,10 @@
|
|||||||
|
|
||||||
@keyframes overlay-in { from { opacity: 0; } to { opacity: 1; } }
|
@keyframes overlay-in { from { opacity: 0; } to { opacity: 1; } }
|
||||||
@keyframes grid-drift { to { transform: translate3d(42px, 42px, 0); } }
|
@keyframes grid-drift { to { transform: translate3d(42px, 42px, 0); } }
|
||||||
@keyframes card-scan {
|
|
||||||
0%, 34% { transform: translateX(-100%); }
|
|
||||||
72%, 100% { transform: translateX(100%); }
|
|
||||||
}
|
|
||||||
@keyframes icon-float {
|
@keyframes icon-float {
|
||||||
0%, 100% { transform: translateY(0) scale(1); }
|
0%, 100% { transform: translateY(0) scale(1); }
|
||||||
50% { transform: translateY(-5px) scale(1.015); }
|
50% { transform: translateY(-5px) scale(1.015); }
|
||||||
}
|
}
|
||||||
@keyframes icon-sheen {
|
|
||||||
0%, 18% { transform: translateX(-96px) rotate(16deg); opacity: 0; }
|
|
||||||
44% { opacity: 0.9; }
|
|
||||||
68%, 100% { transform: translateX(98px) rotate(16deg); opacity: 0; }
|
|
||||||
}
|
|
||||||
@keyframes halo-breathe {
|
@keyframes halo-breathe {
|
||||||
0%, 100% { opacity: 0.35; transform: rotate(45deg) scale(0.95); }
|
0%, 100% { opacity: 0.35; transform: rotate(45deg) scale(0.95); }
|
||||||
50% { opacity: 0.8; transform: rotate(45deg) scale(1.04); }
|
50% { opacity: 0.8; transform: rotate(45deg) scale(1.04); }
|
||||||
@@ -242,11 +209,9 @@
|
|||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.repo-loading-grid,
|
.repo-loading-grid,
|
||||||
.repo-loading-card::before,
|
|
||||||
.repo-loading-halo,
|
.repo-loading-halo,
|
||||||
.repo-loading-traces .trace,
|
.repo-loading-traces .trace,
|
||||||
.repo-loading-icon,
|
.repo-loading-icon,
|
||||||
.repo-loading-sheen,
|
|
||||||
.repo-loading-bar span { animation: none; }
|
.repo-loading-bar span { animation: none; }
|
||||||
.repo-loading-traces .trace { stroke-dashoffset: 0; opacity: 0.72; }
|
.repo-loading-traces .trace { stroke-dashoffset: 0; opacity: 0.72; }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user