Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions scripts/check-github-action-pins.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -173,19 +173,6 @@ if (!/^ image: semgrep\/semgrep@sha256:[0-9a-f]{64}\s*$/m.test(semgrepGateJ
// the per-line validation above only covers workflows, a composite skew (e.g.
// setup-node v5 vs v7) was previously invisible. Assert each action name resolves
// to a single SHA everywhere it is used.
function discoverCompositeActionFiles(workflowRoot) {
const actionsRoot = path.join(workflowRoot, ".github", "actions");
if (!existsSync(actionsRoot)) return [];
const files = [];
for (const entry of readdirSync(actionsRoot, { withFileTypes: true })) {
if (!entry.isDirectory()) continue;
for (const name of ["action.yml", "action.yaml"]) {
const candidate = path.join(actionsRoot, entry.name, name);
if (existsSync(candidate)) files.push(candidate);
}
}
return files;
}

const actionPinPattern = /uses:\s*([^@\s]+)@([0-9a-f]{40})(?:\s*#\s*(\S+))?/;
const shasByAction = new Map();
Expand Down
64 changes: 62 additions & 2 deletions src/app/globals.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -104,13 +104,19 @@

--ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
--ease-spring: cubic-bezier(0.34, 1.3, 0.64, 1);
--ease-exit: cubic-bezier(0.4, 0, 1, 1);

--animate-fade-up: fade-up 200ms cubic-bezier(0.22, 1, 0.36, 1) both;
--animate-overlay-in: overlay-in 200ms cubic-bezier(0.22, 1, 0.36, 1) both;
--animate-overlay-out: overlay-out 140ms cubic-bezier(0.4, 0, 1, 1) both;
--animate-sheet-up: sheet-up 250ms cubic-bezier(0.22, 1, 0.36, 1) both;
--animate-sheet-down: sheet-down 140ms cubic-bezier(0.4, 0, 1, 1) both;
--animate-sheet-left: sheet-left 220ms cubic-bezier(0.22, 1, 0.36, 1) both;
--animate-pop-in: pop-in 180ms cubic-bezier(0.34, 1.3, 0.64, 1) both;
--animate-dialog-rise: dialog-rise 220ms cubic-bezier(0.22, 1, 0.36, 1) both;
--animate-dialog-fall: dialog-fall 140ms cubic-bezier(0.4, 0, 1, 1) both;
--animate-pop-out: pop-out 140ms cubic-bezier(0.4, 0, 1, 1) both;
--animate-sheet-left-out: sheet-left-out 140ms cubic-bezier(0.4, 0, 1, 1) both;
--animate-action-tray-in: action-tray-in 160ms cubic-bezier(0.22, 1, 0.36, 1) both;
--animate-shimmer: shimmer 1.4s linear infinite;
}
Expand DownExpand Up@@ -288,6 +294,7 @@
--duration-fast: 120ms;
--duration-base: 180ms;
--duration-slow: 240ms;
--duration-exit: 140ms;
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
--ease-emphasized: cubic-bezier(0.2, 0.8, 0.2, 1);
color-scheme: light;
Expand DownExpand Up@@ -1252,13 +1259,17 @@ summary::-webkit-details-marker {
}
}
@media (prefers-reduced-motion: no-preference) {
.mode-action-surface[data-placement="up"] {
.mode-action-surface[data-placement="up"]:not([data-state="exiting"]) {
animation: mode-action-fold-up 170ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.mode-action-surface[data-placement="down"] {
.mode-action-surface[data-placement="down"]:not([data-state="exiting"]) {
animation: mode-action-fold-down 170ms cubic-bezier(0.22, 1, 0.36, 1) both;
}

.mode-action-surface[data-state="exiting"] {
animation: pop-out 140ms cubic-bezier(0.4, 0, 1, 1) both;
}
}

.answer-footer-search-input {
Expand DownExpand Up@@ -2108,6 +2119,15 @@ html[data-motion="reduced"] .pwa-notice-card {
}
}

@keyframes overlay-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}

@keyframes sheet-up {
from {
transform: translateY(100%);
Expand All@@ -2117,6 +2137,15 @@ html[data-motion="reduced"] .pwa-notice-card {
}
}

@keyframes sheet-down {
from {
transform: translateY(0);
}
to {
transform: translateY(100%);
}
}

@keyframes sheet-left {
from {
transform: translateX(-100%);
Expand DownExpand Up@@ -2148,6 +2177,37 @@ html[data-motion="reduced"] .pwa-notice-card {
}
}

@keyframes dialog-fall {
from {
opacity: 1;
transform: translateY(0) scale(1);
}
to {
opacity: 0;
transform: translateY(12px) scale(0.98);
}
}

@keyframes pop-out {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.97);
}
}

@keyframes sheet-left-out {
from {
transform: translateX(0);
}
to {
transform: translateX(-100%);
}
}

@keyframes action-tray-in {
from {
opacity: 0;
Expand Down
4 changes: 2 additions & 2 deletions src/components/calculator-mockups/popup-sheet-mockup.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -102,9 +102,9 @@ export function CalculatorSheet({
aria-label="Close calculator"
tabIndex={-1}
onClick={onClose}
className="absolute inset-0 animate-overlay-in bg-[color:var(--neutral-950)]/55 backdrop-blur-[2px]"
className="absolute inset-0 motion-safe:animate-overlay-in bg-[color:var(--neutral-950)]/55 backdrop-blur-[2px] motion-reduce:transition-none"
/>
<div className="relative flex max-h-[92dvh] w-full animate-sheet-up flex-col overflow-hidden rounded-t-xl border border-[color:var(--border-strong)] bg-[color:var(--background)] shadow-[var(--shadow-lux)] sm:max-w-3xl sm:animate-dialog-rise sm:rounded-xl">
<div className="relative flex max-h-[92dvh] w-full motion-safe:animate-sheet-up flex-col overflow-hidden rounded-t-xl border border-[color:var(--border-strong)] bg-[color:var(--background)] shadow-[var(--shadow-lux)] sm:max-w-3xl sm:motion-safe:animate-dialog-rise sm:rounded-xl motion-reduce:transition-none">
<header className="grid shrink-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-3 border-b border-[color:var(--border)] bg-[color:var(--surface)] px-4 py-3">
<span className="grid size-9 shrink-0 place-items-center rounded-md border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]">
<Icon className="size-icon-md" aria-hidden="true" />
Expand Down
19 changes: 11 additions & 8 deletions src/components/clinical-dashboard/mode-action-popup.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -429,10 +429,12 @@ export function ModeActionPopup({
window.requestAnimationFrame(() => buttonRef.current?.focus());
}, [onOpenChange, setModeSelectorOpen]);

const popoverPresence = useOverlayPresence(open && !useSheet);

// The sheet owns its own focus trap, Escape, and backdrop dismissal; only the
// anchored popover needs the outside-click dismissable layer.
useDismissableLayer({
enabled: open && !useSheet,
enabled: popoverPresence.isMounted,
refs: [rootRef, surfaceRef, ...(dismissIgnoreRefs ?? [])],
restoreFocusRef: buttonRef,
onDismiss: () => {
Expand DownExpand Up@@ -622,22 +624,22 @@ export function ModeActionPopup({
}

useLayoutEffect(() => {
if (!open || useSheet) return;
if (!popoverPresence.isMounted) return;
updatePlacement();
}, [items.length, open, title, updatePlacement, useSheet]);
}, [items.length, popoverPresence.isMounted, title, updatePlacement]);

useEffect(() => {
if (open) return;
queueMicrotask(() => setIntegratedSurfaceLayout(null));
}, [open]);

useEffect(() => {
if (!open || useSheet) return;
if (!popoverPresence.isMounted) return;
onPlacementChange?.(placement);
}, [onPlacementChange, open, placement, useSheet]);
}, [onPlacementChange, popoverPresence.isMounted, placement]);

useEffect(() => {
if (!open || useSheet) return;
if (!popoverPresence.isMounted) return;

let placementFrame: number | null = null;
const schedulePlacementUpdate = () => {
Expand All@@ -661,7 +663,7 @@ export function ModeActionPopup({
window.visualViewport?.removeEventListener("resize", schedulePlacementUpdate);
window.visualViewport?.removeEventListener("scroll", schedulePlacementUpdate);
};
}, [open, updatePlacement, useSheet]);
}, [popoverPresence.isMounted, updatePlacement]);

const surfaceStyle = {
"--mode-action-max-height": surfaceMaxHeight ? `${surfaceMaxHeight}px` : undefined,
Expand DownExpand Up@@ -869,10 +871,11 @@ export function ModeActionPopup({
}

const actionSurface =
open && !useSheet ? (
popoverPresence.isMounted ? (
<div
ref={surfaceRef}
data-placement={placement}
data-state={popoverPresence.stage === "exiting" ? "exiting" : "open"}
style={surfaceStyle}
className={cn(
"mode-action-surface z-50 text-[color:var(--text)]",
Expand Down
18 changes: 11 additions & 7 deletions src/components/clinical-dashboard/source-preview-popover.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -13,6 +13,7 @@ import {
import { createPortal } from "react-dom";

import { useDismissableLayer } from "@/components/use-dismissable-layer";
import { useOverlayPresence } from "@/components/use-overlay-presence";
import { cn } from "@/components/ui-primitives";

type PopoverPlacement = "below" | "above";
Expand DownExpand Up@@ -67,6 +68,7 @@ export function SourcePreviewPopover({
children: ReactNode;
}) {
const surfaceRef = useRef<HTMLDivElement>(null);
const presence = useOverlayPresence(open);
const [layout, setLayout] = useState<{
placement: PopoverPlacement;
top: number;
Expand All@@ -77,12 +79,12 @@ export function SourcePreviewPopover({

const updateLayout = useCallback(() => {
const anchor = anchorRef.current;
if (!anchor) return;
if (!anchor || !presence.isMounted) return;
setLayout(computePopoverLayout(anchor));
}, [anchorRef]);
}, [anchorRef, presence.isMounted]);

useLayoutEffect(() => {
if (!open) return;
if (!presence.isMounted) return;
updateLayout();
}, [open, updateLayout]);

Expand DownExpand Up@@ -114,7 +116,7 @@ export function SourcePreviewPopover({
window.visualViewport?.removeEventListener("scroll", handleViewportChange);
window.removeEventListener("scroll", handleViewportChange, scrollOptions);
};
}, [open, updateLayout]);
}, [presence.isMounted, updateLayout]);

useEffect(() => {
if (!open) return undefined;
Expand DownExpand Up@@ -144,13 +146,13 @@ export function SourcePreviewPopover({
}, [open]);

useDismissableLayer({
enabled: open,
enabled: presence.isMounted,
refs: [anchorRef, surfaceRef],
restoreFocusRef: anchorRef,
onDismiss: () => onClose(),
});

if (!open || typeof document === "undefined") return null;
if (!presence.isMounted || typeof document === "undefined") return null;

const style: CSSProperties = layout
? {
Expand All@@ -168,11 +170,13 @@ export function SourcePreviewPopover({
role="dialog"
aria-modal="false"
aria-label={title}
aria-hidden={presence.stage === "exiting"}
data-testid="source-capsule-preview"
data-popover-placement={layout?.placement ?? "below"}
style={style}
className={cn(
"fixed z-[95] min-w-[min(100vw-1.5rem,20rem)] overflow-y-auto overscroll-contain rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-lux)] p-3 shadow-[var(--shadow-elevated)] motion-safe:animate-pop-in motion-reduce:animate-none",
"fixed z-[95] min-w-[min(100vw-1.5rem,20rem)] overflow-y-auto overscroll-contain rounded-lg border border-[color:var(--border)] bg-[color:var(--surface-lux)] p-3 shadow-[var(--shadow-elevated)] motion-reduce:animate-none",
presence.stage === "exiting" ? "motion-safe:animate-pop-out" : "motion-safe:animate-pop-in"
)}
>
{children}
Expand Down
Loading
Loading