animation tweaks and mobile desktop fixes
This commit is contained in:
parent
8a379ec4b5
commit
ee11b4f71b
4 changed files with 107 additions and 16 deletions
|
|
@ -1,8 +1,21 @@
|
|||
const USERNAME = "dvorakmortonxd";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const res = await fetch("https://git.dvorakmortonxd.dev/api/v1/users/dvorakmortonxd/activities/feeds?limit=20");
|
||||
// The public user feed also contains activity from repositories the user
|
||||
// watches (e.g. commits pushed by other people to an org repo), so pull a
|
||||
// larger page and keep only actions the user actually performed.
|
||||
const res = await fetch(
|
||||
`https://git.dvorakmortonxd.dev/api/v1/users/${USERNAME}/activities/feeds?limit=50`
|
||||
);
|
||||
const data = await res.json();
|
||||
return Response.json(data);
|
||||
if (!Array.isArray(data)) {
|
||||
return Response.json(data);
|
||||
}
|
||||
const mine = data.filter(
|
||||
(act) => act?.act_user?.login === USERNAME
|
||||
);
|
||||
return Response.json(mine);
|
||||
} catch {
|
||||
return Response.json({ error: "failed to load" }, { status: 500 });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,12 +27,11 @@ export default function ServiceButton({ url, checkUrl, className, icon, name, de
|
|||
const statusColor = effectiveStatus === 'up' ? '#4ade80' : effectiveStatus === 'down' ? '#ef4444' : '#9ca3af';
|
||||
const statusGlow = effectiveStatus === 'up' ? 'rgba(74, 222, 128, 0.9)' : effectiveStatus === 'down' ? 'rgba(239, 68, 68, 0.9)' : 'transparent';
|
||||
const statusLabel = effectiveStatus === 'up' ? 'Active' : effectiveStatus === 'down' ? 'Dead' : 'Checking...';
|
||||
const isFriend = className === 'friend';
|
||||
|
||||
const content = (
|
||||
<>
|
||||
<span className="service-icon-wrap">
|
||||
{isFriend && <span className="service-status-dot" />}
|
||||
<span className="service-status-dot" />
|
||||
<span className="service-icon">{icon}</span>
|
||||
</span>
|
||||
<span className="service-text">
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ export default function WindowsDesktop() {
|
|||
]);
|
||||
const [activeId, setActiveId] = useState<number | null>(1);
|
||||
const [startOpen, setStartOpen] = useState(false);
|
||||
const [startClosing, setStartClosing] = useState(false);
|
||||
const [selectedIcon, setSelectedIcon] = useState<string | null>(null);
|
||||
const [closingIds, setClosingIds] = useState<number[]>([]);
|
||||
|
||||
|
|
@ -78,7 +79,7 @@ export default function WindowsDesktop() {
|
|||
};
|
||||
|
||||
const openApp = (appId: string) => {
|
||||
setStartOpen(false);
|
||||
if (startOpen) setStartClosing(true);
|
||||
const existing = wins.find((w) => w.appId === appId);
|
||||
if (existing) {
|
||||
setWins((prev) =>
|
||||
|
|
@ -117,6 +118,21 @@ export default function WindowsDesktop() {
|
|||
setActiveId(id);
|
||||
};
|
||||
|
||||
const closeStart = () => {
|
||||
if (startOpen && !startClosing) setStartClosing(true);
|
||||
};
|
||||
|
||||
const toggleStart = () => {
|
||||
if (!startOpen) {
|
||||
setStartClosing(false);
|
||||
setStartOpen(true);
|
||||
} else if (startClosing) {
|
||||
setStartClosing(false);
|
||||
} else {
|
||||
setStartClosing(true);
|
||||
}
|
||||
};
|
||||
|
||||
// Animate the window out first, then drop it from state.
|
||||
const closeWin = (id: number) =>
|
||||
setClosingIds((prev) => (prev.includes(id) ? prev : [...prev, id]));
|
||||
|
|
@ -168,7 +184,7 @@ export default function WindowsDesktop() {
|
|||
style={{ backgroundImage: wallpaper }}
|
||||
onPointerDown={(e) => {
|
||||
if (e.target === e.currentTarget) {
|
||||
setStartOpen(false);
|
||||
closeStart();
|
||||
setSelectedIcon(null);
|
||||
}
|
||||
}}
|
||||
|
|
@ -264,7 +280,17 @@ export default function WindowsDesktop() {
|
|||
|
||||
{/* Application launcher */}
|
||||
{startOpen && (
|
||||
<div className="kickoff" onPointerDown={(e) => e.stopPropagation()}>
|
||||
<div
|
||||
className={"kickoff" + (startClosing ? " closing" : "")}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
onAnimationEnd={(e) => {
|
||||
if (e.target !== e.currentTarget) return;
|
||||
if (startClosing) {
|
||||
setStartOpen(false);
|
||||
setStartClosing(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="kickoff-content">
|
||||
<div className="kickoff-section-title">Applications</div>
|
||||
<div className="kickoff-list">
|
||||
|
|
@ -333,7 +359,7 @@ export default function WindowsDesktop() {
|
|||
<button
|
||||
type="button"
|
||||
className={"win-start" + (startOpen ? " open" : "")}
|
||||
onClick={() => setStartOpen((v) => !v)}
|
||||
onClick={() => toggleStart()}
|
||||
aria-label="Application Launcher"
|
||||
title="Application Launcher"
|
||||
>
|
||||
|
|
@ -352,7 +378,8 @@ export default function WindowsDesktop() {
|
|||
className={
|
||||
"win-tb-btn" +
|
||||
(activeId === w.id && !w.minimized ? " active" : "") +
|
||||
(w.minimized ? " minimized" : "")
|
||||
(w.minimized ? " minimized" : "") +
|
||||
(closingIds.includes(w.id) ? " closing" : "")
|
||||
}
|
||||
onClick={() => taskbarClick(w)}
|
||||
title={app.title}
|
||||
|
|
|
|||
|
|
@ -92,17 +92,13 @@ html[data-device="phone"] .skeuo-mobile-root { display: block; }
|
|||
color: #ffffff;
|
||||
cursor: default;
|
||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.95);
|
||||
transition: transform 0.12s ease, background-color 0.12s ease,
|
||||
border-color 0.12s ease;
|
||||
transition: background-color 0.12s ease, border-color 0.12s ease;
|
||||
}
|
||||
.win-icon:hover,
|
||||
.win-icon.selected {
|
||||
background: rgba(61, 174, 233, 0.16);
|
||||
border-color: rgba(61, 174, 233, 0.4);
|
||||
}
|
||||
.win-icon:active {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
.win-icon-glyph {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
|
@ -128,8 +124,11 @@ a.win-icon {
|
|||
right: 10px;
|
||||
left: auto;
|
||||
align-items: flex-end;
|
||||
flex-wrap: nowrap;
|
||||
max-height: none;
|
||||
/* Wrap into extra columns on short screens so the service shortcuts never
|
||||
collide with the friends stack anchored to the bottom-right. */
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
max-height: calc(100% - 350px);
|
||||
}
|
||||
.win-icons-br {
|
||||
top: auto;
|
||||
|
|
@ -396,6 +395,17 @@ a.win-icon {
|
|||
.app-surface .service-button::before {
|
||||
content: none;
|
||||
}
|
||||
/* Emoji glyphs get a square drop-shadow, so the status glow is drawn with the
|
||||
status dot behind the icon instead of a filter on the glyph. */
|
||||
.app-surface .service-button:not(.friend) .service-icon {
|
||||
filter: none;
|
||||
}
|
||||
.app-surface .service-button:not(.friend) .service-status-dot {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
background: radial-gradient(circle, var(--status-glow) 0%, rgba(0, 0, 0, 0) 70%);
|
||||
filter: blur(1px);
|
||||
}
|
||||
.app-surface .service-button:hover {
|
||||
background: linear-gradient(to bottom, #f2f9ff, #cfe4f8);
|
||||
border-color: #7db9e8;
|
||||
|
|
@ -912,6 +922,31 @@ a.win-icon {
|
|||
background: transparent;
|
||||
color: #eff0f1;
|
||||
cursor: pointer;
|
||||
animation: tbBtnIn 0.16s ease-out;
|
||||
}
|
||||
.win-tb-btn.closing {
|
||||
animation: tbBtnOut 0.12s ease-in forwards;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes tbBtnIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(6px) scale(0.92);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
||||
@keyframes tbBtnOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(6px) scale(0.92);
|
||||
}
|
||||
}
|
||||
.win-tb-btn::after {
|
||||
content: "";
|
||||
|
|
@ -1031,6 +1066,20 @@ a.win-icon {
|
|||
overflow: auto;
|
||||
padding: 10px 12px;
|
||||
}
|
||||
.kickoff.closing {
|
||||
animation: kickoffOut 0.16s ease-in forwards;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes kickoffOut {
|
||||
from {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
transform: translateY(10px) scale(0.98);
|
||||
}
|
||||
}
|
||||
.kickoff-section-title {
|
||||
margin: 4px 2px 8px;
|
||||
font-size: 0.7rem;
|
||||
|
|
@ -1098,6 +1147,9 @@ a.win-icon {
|
|||
color: #ffffff;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", Roboto,
|
||||
"Helvetica Neue", Arial, sans-serif;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
.mobile-home::before {
|
||||
content: "";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue