| author | |
| committer | |
| log | e420ef98333c97205f836201cfeb94682a41893c |
| tree | ca9dbc54a12fa1b5c91ce29892c3f81affdbb2ef |
| parent | 016cdb9622359967afe80a27c538056ef1e7b857 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>5 files changed, 231 insertions(+), 235 deletions(-)
apps/web/src/components/ChatMarkdown.tsx+25-24| ... | @@ -844,32 +844,33 @@ function MarkdownDetails({ | ... | @@ -844,32 +844,33 @@ function MarkdownDetails({ |
| 844 | const content = childNodes.filter((_, index) => index !== summaryIndex); | 844 | const content = childNodes.filter((_, index) => index !== summaryIndex); |
| 845 | 845 | ||
| 846 | return ( | 846 | return ( |
| 847 | <Collapsible | 847 | <div className="my-2 border-y border-border/60"> |
| 848 | defaultOpen={open} | 848 | <Collapsible |
| 849 | onOpenChange={setIsOpen} | 849 | defaultOpen={open} |
| 850 | className="chat-markdown-details my-2 border-y border-border/60" | 850 | onOpenChange={setIsOpen} |
| 851 | data-markdown-details="" | 851 | data-markdown-details="" |
| 852 | data-markdown-details-open={isOpen ? "true" : "false"} | 852 | data-markdown-details-open={isOpen ? "true" : "false"} |
| 853 | > | ||
| 854 | <CollapsibleTrigger | ||
| 855 | className="flex w-full items-center gap-2 py-2 text-left text-sm font-medium text-foreground data-panel-open:[&_svg]:rotate-90" | ||
| 856 | data-markdown-details-summary="" | ||
| 857 | > | 853 | > |
| 858 | <ChevronRightIcon | 854 | <CollapsibleTrigger |
| 859 | className="size-4 shrink-0 text-muted-foreground transition-transform" | 855 | className="flex w-full items-center gap-2 py-2 text-left text-sm font-medium text-foreground data-panel-open:[&_svg]:rotate-90" |
| 860 | aria-hidden | 856 | data-markdown-details-summary="" |
| 861 | /> | ||
| 862 | <span>{summary}</span> | ||
| 863 | </CollapsibleTrigger> | ||
| 864 | <CollapsiblePanel> | ||
| 865 | <div | ||
| 866 | className="pb-3 ps-6 text-foreground/[calc(80%+var(--appearance-contrast-boost)/5)]" | ||
| 867 | data-markdown-details-content="" | ||
| 868 | > | 857 | > |
| 869 | {content} | 858 | <ChevronRightIcon |
| 870 | </div> | 859 | className="size-4 shrink-0 text-muted-foreground transition-transform" |
| 871 | </CollapsiblePanel> | 860 | aria-hidden |
| 872 | </Collapsible> | 861 | /> |
| 862 | <span>{summary}</span> | ||
| 863 | </CollapsibleTrigger> | ||
| 864 | <CollapsiblePanel> | ||
| 865 | <div | ||
| 866 | className="pb-3 ps-6 text-foreground/[calc(80%+var(--appearance-contrast-boost)/5)]" | ||
| 867 | data-markdown-details-content="" | ||
| 868 | > | ||
| 869 | {content} | ||
| 870 | </div> | ||
| 871 | </CollapsiblePanel> | ||
| 872 | </Collapsible> | ||
| 873 | </div> | ||
| 873 | ); | 874 | ); |
| 874 | } | 875 | } |
| 875 | 876 |
apps/web/src/components/onboarding/WelcomeWizard.tsx+89-93| ... | @@ -350,40 +350,38 @@ function ConnectionStep({ | ... | @@ -350,40 +350,38 @@ function ConnectionStep({ |
| 350 | onToggleEnvironment={onToggleEnvironment} | 350 | onToggleEnvironment={onToggleEnvironment} |
| 351 | /> | 351 | /> |
| 352 | ) : null} | 352 | ) : null} |
| 353 | <Collapsible | 353 | <div className="rounded-lg border border-border bg-background"> |
| 354 | open={pairingOpen} | 354 | <Collapsible open={pairingOpen} onOpenChange={setPairingOpen}> |
| 355 | onOpenChange={setPairingOpen} | 355 | <CollapsibleTrigger |
| 356 | className="rounded-lg border border-border bg-background" | 356 | disabled={isPairing} |
| 357 | > | 357 | render={ |
| 358 | <CollapsibleTrigger | 358 | <Button |
| 359 | disabled={isPairing} | 359 | variant="ghost" |
| 360 | render={ | 360 | className="h-auto min-h-14 w-full justify-start gap-3 px-3 py-3 text-left whitespace-normal sm:h-auto" |
| 361 | <Button | 361 | /> |
| 362 | variant="ghost" | 362 | } |
| 363 | className="h-auto min-h-14 w-full justify-start gap-3 px-3 py-3 text-left whitespace-normal sm:h-auto" | 363 | > |
| 364 | /> | 364 | <LinkIcon className="size-4 text-muted-foreground" /> |
| 365 | } | 365 | <span className="flex-1">Add a computer</span> |
| 366 | > | 366 | <ChevronRightIcon |
| 367 | <LinkIcon className="size-4 text-muted-foreground" /> | 367 | className={cn("size-4 text-muted-foreground", pairingOpen && "rotate-90")} |
| 368 | <span className="flex-1">Add a computer</span> | ||
| 369 | <ChevronRightIcon | ||
| 370 | className={cn("size-4 text-muted-foreground", pairingOpen && "rotate-90")} | ||
| 371 | /> | ||
| 372 | </CollapsibleTrigger> | ||
| 373 | <CollapsiblePanel> | ||
| 374 | <div className="px-3 pb-3"> | ||
| 375 | <PairingForm | ||
| 376 | isPairing={isPairing} | ||
| 377 | setIsPairing={setIsPairing} | ||
| 378 | onPaired={(environmentId) => { | ||
| 379 | setPairingOpen(false); | ||
| 380 | onPaired(environmentId); | ||
| 381 | requestAnimationFrame(() => continueRef.current?.focus()); | ||
| 382 | }} | ||
| 383 | /> | 368 | /> |
| 384 | </div> | 369 | </CollapsibleTrigger> |
| 385 | </CollapsiblePanel> | 370 | <CollapsiblePanel> |
| 386 | </Collapsible> | 371 | <div className="px-3 pb-3"> |
| 372 | <PairingForm | ||
| 373 | isPairing={isPairing} | ||
| 374 | setIsPairing={setIsPairing} | ||
| 375 | onPaired={(environmentId) => { | ||
| 376 | setPairingOpen(false); | ||
| 377 | onPaired(environmentId); | ||
| 378 | requestAnimationFrame(() => continueRef.current?.focus()); | ||
| 379 | }} | ||
| 380 | /> | ||
| 381 | </div> | ||
| 382 | </CollapsiblePanel> | ||
| 383 | </Collapsible> | ||
| 384 | </div> | ||
| 387 | </div> | 385 | </div> |
| 388 | <div className="mt-6 flex items-center justify-end gap-3"> | 386 | <div className="mt-6 flex items-center justify-end gap-3"> |
| 389 | <Button | 387 | <Button |
| ... | @@ -419,69 +417,67 @@ function ConnectAccountOption({ | ... | @@ -419,69 +417,67 @@ function ConnectAccountOption({ |
| 419 | const onDiscoveryReady = useCallback(() => setDiscoveryReady(true), []); | 417 | const onDiscoveryReady = useCallback(() => setDiscoveryReady(true), []); |
| 420 | 418 | ||
| 421 | return ( | 419 | return ( |
| 422 | <Collapsible | 420 | <div className="rounded-lg border border-border bg-background"> |
| 423 | open={expanded && !!isSignedIn && discoveryReady} | 421 | <Collapsible open={expanded && !!isSignedIn && discoveryReady} onOpenChange={setExpanded}> |
| 424 | onOpenChange={setExpanded} | 422 | <CollapsibleTrigger |
| 425 | className="rounded-lg border border-border bg-background" | 423 | disabled={disabled || !isLoaded} |
| 426 | > | 424 | onClick={(event) => { |
| 427 | <CollapsibleTrigger | 425 | if (!isSignedIn) { |
| 428 | disabled={disabled || !isLoaded} | 426 | event.preventDefault(); |
| 429 | onClick={(event) => { | 427 | setExpanded(true); |
| 430 | if (!isSignedIn) { | 428 | openAuthPrompt(); |
| 431 | event.preventDefault(); | 429 | } |
| 432 | setExpanded(true); | 430 | }} |
| 433 | openAuthPrompt(); | 431 | render={ |
| 432 | <Button | ||
| 433 | variant="ghost" | ||
| 434 | className="h-auto min-h-14 w-full justify-start gap-3 px-3 py-3 text-left whitespace-normal sm:h-auto" | ||
| 435 | /> | ||
| 434 | } | 436 | } |
| 435 | }} | 437 | > |
| 436 | render={ | 438 | <CloudIcon className="size-4 text-muted-foreground" /> |
| 437 | <Button | 439 | <span className="flex-1">T3 Connect</span> |
| 438 | variant="ghost" | 440 | <span className="text-xs text-muted-foreground"> |
| 439 | className="h-auto min-h-14 w-full justify-start gap-3 px-3 py-3 text-left whitespace-normal sm:h-auto" | 441 | {!isLoaded |
| 442 | ? "Loading sign-in…" | ||
| 443 | : !isSignedIn | ||
| 444 | ? "Sign in" | ||
| 445 | : !discoveryReady | ||
| 446 | ? "Loading computers…" | ||
| 447 | : null} | ||
| 448 | </span> | ||
| 449 | <ChevronRightIcon | ||
| 450 | className={cn("size-4 text-muted-foreground", expanded && isSignedIn && "rotate-90")} | ||
| 440 | /> | 451 | /> |
| 441 | } | 452 | </CollapsibleTrigger> |
| 442 | > | 453 | <CollapsiblePanel keepMounted> |
| 443 | <CloudIcon className="size-4 text-muted-foreground" /> | 454 | <div className="px-3 pb-3"> |
| 444 | <span className="flex-1">T3 Connect</span> | 455 | <div className="mb-3 space-y-1.5"> |
| 445 | <span className="text-xs text-muted-foreground"> | 456 | {isSignedIn ? ( |
| 446 | {!isLoaded | 457 | <CloudEnvironmentConnectRows |
| 447 | ? "Loading sign-in…" | 458 | primaryEnvironmentId={null} |
| 448 | : !isSignedIn | 459 | savedEnvironments={environments} |
| 449 | ? "Sign in" | 460 | showSavedEnvironments |
| 450 | : !discoveryReady | 461 | onDiscoveryReady={onDiscoveryReady} |
| 451 | ? "Loading computers…" | 462 | selection={{ selectedIds, onChange: onToggleEnvironment, autoSelectedComputers }} |
| 452 | : null} | 463 | refreshWhileEmpty |
| 453 | </span> | 464 | empty={ |
| 454 | <ChevronRightIcon | 465 | <p className="py-3 text-sm text-muted-foreground">No computers linked yet.</p> |
| 455 | className={cn("size-4 text-muted-foreground", expanded && isSignedIn && "rotate-90")} | 466 | } |
| 456 | /> | 467 | /> |
| 457 | </CollapsibleTrigger> | 468 | ) : null} |
| 458 | <CollapsiblePanel keepMounted> | 469 | </div> |
| 459 | <div className="px-3 pb-3"> | 470 | <p className="text-sm text-muted-foreground"> |
| 460 | <div className="mb-3 space-y-1.5"> | 471 | Run this on each computer you want to connect. |
| 461 | {isSignedIn ? ( | 472 | </p> |
| 462 | <CloudEnvironmentConnectRows | 473 | <CommandBlock command="npx t3 connect" className="mt-3" /> |
| 463 | primaryEnvironmentId={null} | 474 | <p className="mt-3 text-xs text-muted-foreground"> |
| 464 | savedEnvironments={environments} | 475 | Keep T3 Code running. Select the computers you want to set up above. |
| 465 | showSavedEnvironments | 476 | </p> |
| 466 | onDiscoveryReady={onDiscoveryReady} | ||
| 467 | selection={{ selectedIds, onChange: onToggleEnvironment, autoSelectedComputers }} | ||
| 468 | refreshWhileEmpty | ||
| 469 | empty={ | ||
| 470 | <p className="py-3 text-sm text-muted-foreground">No computers linked yet.</p> | ||
| 471 | } | ||
| 472 | /> | ||
| 473 | ) : null} | ||
| 474 | </div> | 477 | </div> |
| 475 | <p className="text-sm text-muted-foreground"> | 478 | </CollapsiblePanel> |
| 476 | Run this on each computer you want to connect. | 479 | </Collapsible> |
| 477 | </p> | 480 | </div> |
| 478 | <CommandBlock command="npx t3 connect" className="mt-3" /> | ||
| 479 | <p className="mt-3 text-xs text-muted-foreground"> | ||
| 480 | Keep T3 Code running. Select the computers you want to set up above. | ||
| 481 | </p> | ||
| 482 | </div> | ||
| 483 | </CollapsiblePanel> | ||
| 484 | </Collapsible> | ||
| 485 | ); | 481 | ); |
| 486 | } | 482 | } |
| 487 | 483 |
apps/web/src/components/pullRequest/PullRequestCodeTab.tsx+53-53| ... | @@ -1406,63 +1406,63 @@ function PullRequestCodeTab({ | ... | @@ -1406,63 +1406,63 @@ function PullRequestCodeTab({ |
| 1406 | {/* Above the code, closed, and counted: these belong to the change rather than to any | 1406 | {/* Above the code, closed, and counted: these belong to the change rather than to any |
| 1407 | line of it, and in the stream they read as cards dropped into the patch. */} | 1407 | line of it, and in the stream they read as cards dropped into the patch. */} |
| 1408 | {orphanFiles.size > 0 ? ( | 1408 | {orphanFiles.size > 0 ? ( |
| 1409 | <Collapsible | 1409 | <div className="shrink-0 border-b border-border/60"> |
| 1410 | className="shrink-0 border-b border-border/60" | 1410 | <Collapsible open={orphansOpen} onOpenChange={setOrphansOpen}> |
| 1411 | open={orphansOpen} | 1411 | {/* Still a heading, so the section keeps its place in a screen reader's outline; |
| 1412 | onOpenChange={setOrphansOpen} | ||
| 1413 | > | ||
| 1414 | {/* Still a heading, so the section keeps its place in a screen reader's outline; | ||
| 1415 | the count is spelled out there rather than left as a bare number. */} | 1412 | the count is spelled out there rather than left as a bare number. */} |
| 1416 | <h2> | 1413 | <h2> |
| 1417 | <CollapsibleTrigger className="flex w-full items-center gap-1.5 px-4 py-2 text-left text-xs text-muted-foreground"> | 1414 | <CollapsibleTrigger className="flex w-full items-center gap-1.5 px-4 py-2 text-left text-xs text-muted-foreground"> |
| 1418 | {/* While slices are still arriving a conversation may simply belong to a file | 1415 | {/* While slices are still arriving a conversation may simply belong to a file |
| 1419 | that has not landed yet, which is not the same as being off the diff. */} | 1416 | that has not landed yet, which is not the same as being off the diff. */} |
| 1420 | <span> | 1417 | <span> |
| 1421 | {nextCursor === null | 1418 | {nextCursor === null |
| 1422 | ? "Conversations not on the current diff" | 1419 | ? "Conversations not on the current diff" |
| 1423 | : "Conversations not on the diff loaded so far"} | 1420 | : "Conversations not on the diff loaded so far"} |
| 1424 | </span> | 1421 | </span> |
| 1425 | <ChevronRightIcon | 1422 | <ChevronRightIcon |
| 1426 | aria-hidden | 1423 | aria-hidden |
| 1427 | className={cn("size-3.5 transition-transform", orphansOpen && "rotate-90")} | 1424 | className={cn("size-3.5 transition-transform", orphansOpen && "rotate-90")} |
| 1428 | /> | 1425 | /> |
| 1429 | <span aria-hidden className="tabular-nums"> | 1426 | <span aria-hidden className="tabular-nums"> |
| 1430 | {orphanThreads.length} | 1427 | {orphanThreads.length} |
| 1431 | </span> | 1428 | </span> |
| 1432 | <span className="sr-only"> | 1429 | <span className="sr-only"> |
| 1433 | {orphanThreads.length === 1 | 1430 | {orphanThreads.length === 1 |
| 1434 | ? "1 conversation" | 1431 | ? "1 conversation" |
| 1435 | : `${orphanThreads.length} conversations`} | 1432 | : `${orphanThreads.length} conversations`} |
| 1436 | </span> | 1433 | </span> |
| 1437 | </CollapsibleTrigger> | 1434 | </CollapsibleTrigger> |
| 1438 | </h2> | 1435 | </h2> |
| 1439 | <CollapsiblePanel> | 1436 | <CollapsiblePanel> |
| 1440 | {/* Capped: opened on a change with dozens of them, this would otherwise leave no | 1437 | {/* Capped: opened on a change with dozens of them, this would otherwise leave no |
| 1441 | room for the diff it sits above. */} | 1438 | room for the diff it sits above. */} |
| 1442 | <div className="max-h-64 space-y-3 overflow-auto px-4 pb-3"> | 1439 | <div className="max-h-64 space-y-3 overflow-auto px-4 pb-3"> |
| 1443 | {[...orphanFiles].map(([path, threads]) => ( | 1440 | {[...orphanFiles].map(([path, threads]) => ( |
| 1444 | <div key={path}> | 1441 | <div key={path}> |
| 1445 | <Tooltip> | 1442 | <Tooltip> |
| 1446 | <TooltipTrigger | 1443 | <TooltipTrigger |
| 1447 | render={<p className="truncate px-3 text-xs text-muted-foreground">{path}</p>} | 1444 | render={ |
| 1448 | /> | 1445 | <p className="truncate px-3 text-xs text-muted-foreground">{path}</p> |
| 1449 | <TooltipPopup side="top">{path}</TooltipPopup> | 1446 | } |
| 1450 | </Tooltip> | 1447 | /> |
| 1451 | <div className="mt-1 space-y-2"> | 1448 | <TooltipPopup side="top">{path}</TooltipPopup> |
| 1452 | {threads.map((thread) => ( | 1449 | </Tooltip> |
| 1453 | <div key={thread.id}> | 1450 | <div className="mt-1 space-y-2"> |
| 1454 | {thread.line === null ? null : ( | 1451 | {threads.map((thread) => ( |
| 1455 | <p className="px-3 text-xs text-muted-foreground">Line {thread.line}</p> | 1452 | <div key={thread.id}> |
| 1456 | )} | 1453 | {thread.line === null ? null : ( |
| 1457 | {renderThreadCard(thread)} | 1454 | <p className="px-3 text-xs text-muted-foreground">Line {thread.line}</p> |
| 1458 | </div> | 1455 | )} |
| 1459 | ))} | 1456 | {renderThreadCard(thread)} |
| 1457 | </div> | ||
| 1458 | ))} | ||
| 1459 | </div> | ||
| 1460 | </div> | 1460 | </div> |
| 1461 | </div> | 1461 | ))} |
| 1462 | ))} | 1462 | </div> |
| 1463 | </div> | 1463 | </CollapsiblePanel> |
| 1464 | </CollapsiblePanel> | 1464 | </Collapsible> |
| 1465 | </Collapsible> | 1465 | </div> |
| 1466 | ) : null} | 1466 | ) : null} |
| 1467 | <div className="flex min-h-0 flex-1 overflow-hidden"> | 1467 | <div className="flex min-h-0 flex-1 overflow-hidden"> |
| 1468 | {/* Relative wrapper so the review overlay floats over the diff rather than pushing it | 1468 | {/* Relative wrapper so the review overlay floats over the diff rather than pushing it |
apps/web/src/components/pullRequest/PullRequestSummaryTab.tsx+63-64| ... | @@ -378,74 +378,73 @@ function CommentGroup({ | ... | @@ -378,74 +378,73 @@ function CommentGroup({ |
| 378 | null, | 378 | null, |
| 379 | ); | 379 | ); |
| 380 | return ( | 380 | return ( |
| 381 | <Collapsible | 381 | <div className="overflow-hidden rounded-lg border border-border bg-background"> |
| 382 | className="overflow-hidden rounded-lg border border-border/70 bg-muted/20" | 382 | <Collapsible onOpenChange={onOpenChange}> |
| 383 | onOpenChange={onOpenChange} | 383 | <div className="flex items-center gap-3 pl-3"> |
| 384 | > | 384 | <div className="flex shrink-0 -space-x-1.5"> |
| 385 | <div className="flex items-center gap-3 pl-3"> | 385 | {authors.slice(0, 3).map((actor) => ( |
| 386 | <div className="flex shrink-0 -space-x-1.5"> | 386 | <PullRequestActorLabel |
| 387 | {authors.slice(0, 3).map((actor) => ( | 387 | key={actor?.login ?? "ghost"} |
| 388 | <PullRequestActorLabel | 388 | actor={actor} |
| 389 | key={actor?.login ?? "ghost"} | 389 | profileUrl={ |
| 390 | actor={actor} | 390 | detail.provider === "github" && actor |
| 391 | profileUrl={ | 391 | ? new URL( |
| 392 | detail.provider === "github" && actor | 392 | actor.isBot || actor.login.endsWith("[bot]") |
| 393 | ? new URL( | 393 | ? `/apps/${encodeURIComponent(actor.login.replace(/\[bot\]$/, ""))}` |
| 394 | actor.isBot || actor.login.endsWith("[bot]") | 394 | : `/${encodeURIComponent(actor.login)}`, |
| 395 | ? `/apps/${encodeURIComponent(actor.login.replace(/\[bot\]$/, ""))}` | 395 | detail.url, |
| 396 | : `/${encodeURIComponent(actor.login)}`, | 396 | ).toString() |
| 397 | detail.url, | 397 | : null |
| 398 | ).toString() | 398 | } |
| 399 | : null | 399 | labelClassName="sr-only" |
| 400 | } | 400 | className="relative rounded-full bg-background ring-2 ring-background hover:z-10 focus-visible:z-10 [&>img]:size-6 [&>span:first-child]:size-6" |
| 401 | labelClassName="sr-only" | 401 | /> |
| 402 | className="relative rounded-full bg-background ring-2 ring-background hover:z-10 focus-visible:z-10 [&>img]:size-6 [&>span:first-child]:size-6" | 402 | ))} |
| 403 | /> | 403 | {authors.length > 3 ? ( |
| 404 | ))} | 404 | <span className="relative flex size-6 items-center justify-center rounded-full bg-muted text-[10px] text-muted-foreground ring-2 ring-background"> |
| 405 | {authors.length > 3 ? ( | 405 | +{authors.length - 3} |
| 406 | <span className="relative flex size-6 items-center justify-center rounded-full bg-muted text-[10px] text-muted-foreground ring-2 ring-background"> | ||
| 407 | +{authors.length - 3} | ||
| 408 | </span> | ||
| 409 | ) : null} | ||
| 410 | </div> | ||
| 411 | <CollapsibleTrigger | ||
| 412 | aria-label={label} | ||
| 413 | className="group flex min-w-0 flex-1 items-center gap-3 rounded-md py-3 pr-3 text-left hover:bg-muted/30" | ||
| 414 | > | ||
| 415 | <span className="min-w-0 flex-1 space-y-1"> | ||
| 416 | <span className="block text-xs font-medium text-foreground/90">{label}</span> | ||
| 417 | <span className="flex flex-wrap gap-x-1.5 text-[11px] text-muted-foreground"> | ||
| 418 | <span> | ||
| 419 | {authors.length} {authors.length === 1 ? "author" : "authors"} | ||
| 420 | </span> | 406 | </span> |
| 421 | {fileCount > 0 ? ( | 407 | ) : null} |
| 422 | <span> | 408 | </div> |
| 423 | · {fileCount} {fileCount === 1 ? "file" : "files"} | 409 | <CollapsibleTrigger |
| 424 | </span> | 410 | aria-label={label} |
| 425 | ) : null} | 411 | className="group flex min-w-0 flex-1 items-center gap-3 rounded-md py-3 pr-3 text-left hover:bg-muted/30" |
| 426 | {latest ? ( | 412 | > |
| 413 | <span className="min-w-0 flex-1 space-y-1"> | ||
| 414 | <span className="block text-xs font-medium text-foreground/90">{label}</span> | ||
| 415 | <span className="flex flex-wrap gap-x-1.5 text-[11px] text-muted-foreground"> | ||
| 427 | <span> | 416 | <span> |
| 428 | · Latest{" "} | 417 | {authors.length} {authors.length === 1 ? "author" : "authors"} |
| 429 | <Tooltip> | ||
| 430 | <TooltipTrigger render={<time dateTime={latest} />}> | ||
| 431 | {formatRelativeTimeLabel(latest)} | ||
| 432 | </TooltipTrigger> | ||
| 433 | <TooltipPopup>{new Date(latest).toLocaleString()}</TooltipPopup> | ||
| 434 | </Tooltip> | ||
| 435 | </span> | 418 | </span> |
| 436 | ) : null} | 419 | {fileCount > 0 ? ( |
| 420 | <span> | ||
| 421 | · {fileCount} {fileCount === 1 ? "file" : "files"} | ||
| 422 | </span> | ||
| 423 | ) : null} | ||
| 424 | {latest ? ( | ||
| 425 | <span> | ||
| 426 | · Latest{" "} | ||
| 427 | <Tooltip> | ||
| 428 | <TooltipTrigger render={<time dateTime={latest} />}> | ||
| 429 | {formatRelativeTimeLabel(latest)} | ||
| 430 | </TooltipTrigger> | ||
| 431 | <TooltipPopup>{new Date(latest).toLocaleString()}</TooltipPopup> | ||
| 432 | </Tooltip> | ||
| 433 | </span> | ||
| 434 | ) : null} | ||
| 435 | </span> | ||
| 437 | </span> | 436 | </span> |
| 438 | </span> | 437 | <ChevronRightIcon |
| 439 | <ChevronRightIcon | 438 | aria-hidden |
| 440 | aria-hidden | 439 | className="size-3.5 shrink-0 text-muted-foreground transition-transform group-data-panel-open:rotate-90" |
| 441 | className="size-3.5 shrink-0 text-muted-foreground transition-transform group-data-panel-open:rotate-90" | 440 | /> |
| 442 | /> | 441 | </CollapsibleTrigger> |
| 443 | </CollapsibleTrigger> | 442 | </div> |
| 444 | </div> | 443 | <CollapsiblePanel keepMounted> |
| 445 | <CollapsiblePanel keepMounted> | 444 | <div className="border-t border-border/60 px-3 pb-3">{children}</div> |
| 446 | <div className="border-t border-border/60 px-3 pb-3">{children}</div> | 445 | </CollapsiblePanel> |
| 447 | </CollapsiblePanel> | 446 | </Collapsible> |
| 448 | </Collapsible> | 447 | </div> |
| 449 | ); | 448 | ); |
| 450 | } | 449 | } |
| 451 | 450 |
scripts/lint-restyle-ceiling.ts+1-1| ... | @@ -8,7 +8,7 @@ import * as NodeURL from "node:url"; | ... | @@ -8,7 +8,7 @@ import * as NodeURL from "node:url"; |
| 8 | // components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the | 8 | // components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the |
| 9 | // count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you | 9 | // count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you |
| 10 | // migrate a file, and delete this script when the rule becomes an error. | 10 | // migrate a file, and delete this script when the rule becomes an error. |
| 11 | export const RESTYLE_CEILING = 793; | 11 | export const RESTYLE_CEILING = 776; |
| 12 | 12 | ||
| 13 | const RULE = "shadcn(no-restyle)"; | 13 | const RULE = "shadcn(no-restyle)"; |
| 14 | 14 |