1437 lines
30 KiB
CSS
1437 lines
30 KiB
CSS
/* Plugin manager page: a centred column with its header, notices, and the package cards. */
|
|
|
|
.page {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 32px;
|
|
box-sizing: border-box;
|
|
height: 100%;
|
|
overflow: auto;
|
|
/* Inline and bottom insets only: the 28px top inset belongs to the head row
|
|
(`.pageHead`, `.detailTop`). macOS window dragging is owned by a row's own box,
|
|
so an inset above that box leaves a top strip no drag region covers. */
|
|
padding: 0 clamp(24px, 4vw, 48px) 48px;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.page > * {
|
|
width: 100%;
|
|
max-width: 960px;
|
|
}
|
|
|
|
.pageHead {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
justify-content: space-between;
|
|
gap: 16px;
|
|
box-sizing: border-box;
|
|
/* The page's former top inset: the row starts at the window's top edge, so the whole
|
|
strip above the page content drags the window. */
|
|
padding-top: 28px;
|
|
}
|
|
|
|
/* macOS hiddenInset titlebar: the frame's top clearance keeps the head below
|
|
the window's top strip in both sidebar states so it never jumps, and the head
|
|
owns the window drag over its own box — clearance included — so this page's
|
|
top strip drags without a frame band. */
|
|
:global([data-platform='darwin']) .pageHead {
|
|
padding-top: calc(28px + var(--dsh-frame-top-clearance, 0px));
|
|
}
|
|
|
|
.pageTitle {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
font-weight: 500;
|
|
line-height: 28px;
|
|
}
|
|
|
|
.pageIntro {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
margin: 4px 0 0;
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.infoButton {
|
|
flex: none;
|
|
width: 20px;
|
|
height: 20px;
|
|
padding: 0;
|
|
color: var(--dsw-alias-label-caption);
|
|
}
|
|
|
|
.toolbar {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: flex-end;
|
|
gap: 16px;
|
|
}
|
|
|
|
.addMenu {
|
|
width: 320px;
|
|
max-width: calc(100vw - 48px);
|
|
}
|
|
|
|
.addMenu [role='menuitem'] {
|
|
height: 72px;
|
|
box-sizing: border-box;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 6px 12px;
|
|
}
|
|
|
|
/* Keep both action icons centred beside the complete text block. */
|
|
.addMenu [role='menuitem'] > span:first-child:has(> svg) {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
.addMenu [role='menuitem'] > span:first-child:has(> svg) > svg {
|
|
width: 16px;
|
|
height: 16px;
|
|
}
|
|
|
|
.addMenuItem {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
white-space: normal;
|
|
}
|
|
|
|
.addMenuDescription {
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.status,
|
|
.failure p,
|
|
.empty {
|
|
margin: 0;
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
}
|
|
|
|
.failure {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
color: var(--dsw-alias-state-error-primary);
|
|
}
|
|
|
|
.statusWithDot {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
}
|
|
|
|
.groupHead {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 8px;
|
|
}
|
|
|
|
.groupTitle {
|
|
margin: 0;
|
|
font-size: 14px;
|
|
line-height: 22px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.count {
|
|
font-size: 14px;
|
|
color: var(--dsw-alias-label-caption);
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
/* The group's explanation, folded behind a hover/focus tooltip. */
|
|
.groupInfo {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
align-self: center;
|
|
color: var(--dsw-alias-label-caption);
|
|
}
|
|
|
|
.groupInfo:hover,
|
|
.groupInfo:focus-visible {
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
/* Center the status label geometrically: beside larger titles the capsule's line-box offset shows. */
|
|
.statusTag {
|
|
height: 18px;
|
|
padding: 0 7px;
|
|
font-size: 10px;
|
|
line-height: 1;
|
|
}
|
|
|
|
/* The package an install just enabled: a ring that fades while the list scrolls to it. */
|
|
.card[data-plugin-highlight] {
|
|
animation: dsh-plugin-highlight 2400ms ease-out;
|
|
}
|
|
|
|
@keyframes dsh-plugin-highlight {
|
|
0%,
|
|
55% {
|
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary) 40%, transparent);
|
|
}
|
|
|
|
100% {
|
|
box-shadow: 0 0 0 0 transparent;
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.card[data-plugin-highlight] {
|
|
animation: none;
|
|
box-shadow: 0 0 0 2px color-mix(in srgb, var(--dsw-alias-state-business-primary) 40%, transparent);
|
|
}
|
|
}
|
|
|
|
.iconButton:focus-visible {
|
|
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
.cards {
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.cards {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
}
|
|
|
|
.card {
|
|
min-width: 0;
|
|
/* The hover surface reaches 8px past the content on both sides while the
|
|
row's content stays aligned with the page header. */
|
|
margin: 0 -8px;
|
|
border-radius: var(--dsw-radius-xl);
|
|
/* The page itself scrolls on the base surface. Anything that scrolls inside
|
|
a card draws the l2 thumb (ui-theme styles/scrollbar.css rebinding contract). */
|
|
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
--dsh-scrollbar-thumb-hover: var(--dsw-alias-scrollbar-hover-l2);
|
|
}
|
|
|
|
.cardHead {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
padding: 8px;
|
|
}
|
|
|
|
.cardIcon {
|
|
display: inline-flex;
|
|
flex: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 48px;
|
|
height: 48px;
|
|
/* The same hairline frame as the component rows below. */
|
|
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
border-radius: var(--dsw-radius-lg);
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.packageImage {
|
|
object-fit: contain;
|
|
}
|
|
|
|
.cardMain {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
}
|
|
|
|
.titleRow {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.cardTitle {
|
|
font-size: 14px;
|
|
line-height: 20px;
|
|
font-weight: 500;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/*
|
|
* A package card opens its page: the title button stretches over the whole
|
|
* card, and the controls at the card's end sit above that stretch, so the
|
|
* switch and the Add to… menu keep their own clicks.
|
|
*/
|
|
.cardLink {
|
|
position: relative;
|
|
}
|
|
|
|
.cardLink:hover {
|
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
}
|
|
|
|
.cardOpen {
|
|
max-width: 100%;
|
|
padding: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
color: inherit;
|
|
/* font: inherit resets the size, so the card title's own size follows. */
|
|
font: inherit;
|
|
font-size: 14px;
|
|
line-height: 20px;
|
|
font-weight: 500;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.cardOpen::after {
|
|
content: '';
|
|
position: absolute;
|
|
inset: 0;
|
|
border-radius: var(--dsw-radius-xl);
|
|
}
|
|
|
|
.cardOpen:focus-visible {
|
|
outline: none;
|
|
}
|
|
|
|
.cardOpen:focus-visible::after {
|
|
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.cardDesc {
|
|
font-size: 13px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
display: -webkit-box;
|
|
-webkit-box-orient: vertical;
|
|
-webkit-line-clamp: 1;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* Match Session search's pulse without coupling the two feature packages. */
|
|
.skeletonFill {
|
|
background: var(--dsw-alias-bg-skeleton);
|
|
animation: dsh-plugin-skeleton 2s cubic-bezier(0.36, 0, 0.64, 1) infinite;
|
|
}
|
|
|
|
/* Keep each real text line's height; center the shorter painted bar within it. */
|
|
.skeletonText {
|
|
display: flex;
|
|
align-items: center;
|
|
height: 1lh;
|
|
}
|
|
|
|
.skeletonBar {
|
|
width: 100%;
|
|
height: 12px;
|
|
border-radius: var(--dsw-radius-xs);
|
|
}
|
|
|
|
.skeletonHeading { width: 48px; }
|
|
.skeletonTitle { width: min(144px, 60%); }
|
|
.skeletonDescription { width: min(280px, 85%); }
|
|
.skeletonIcon { border-color: transparent; }
|
|
|
|
/* Keep text-column metrics while leaving the action area blank. */
|
|
.skeletonActions {
|
|
width: 36px;
|
|
height: 20px;
|
|
}
|
|
|
|
@keyframes dsh-plugin-skeleton {
|
|
0% { opacity: 1; }
|
|
40% { opacity: 0.6; }
|
|
80%, 100% { opacity: 1; }
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.skeletonFill { animation: none; }
|
|
}
|
|
|
|
.cardEnd {
|
|
position: relative;
|
|
z-index: 1;
|
|
display: inline-flex;
|
|
flex: none;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* The toolbar's icon action, round like the message-row actions. */
|
|
.iconButton {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 28px;
|
|
height: 28px;
|
|
border: 0;
|
|
border-radius: var(--dsw-radius-sm);
|
|
background: transparent;
|
|
color: var(--dsw-alias-label-caption);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.iconButton:hover:not(:disabled) {
|
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.iconButton:disabled {
|
|
opacity: 0.5;
|
|
cursor: default;
|
|
}
|
|
|
|
/* The toolbar's Add plugin action, one step up from the 28px sm capsule. */
|
|
.addButton,
|
|
.addPrimary,
|
|
.addMore {
|
|
height: 32px;
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
padding: 0 12px;
|
|
border-radius: var(--dsw-radius-md);
|
|
}
|
|
|
|
.addGroup {
|
|
display: inline-flex;
|
|
flex: none;
|
|
align-items: stretch;
|
|
}
|
|
|
|
.addPrimary {
|
|
border-top-right-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
}
|
|
|
|
.addMore {
|
|
width: 32px;
|
|
padding: 0;
|
|
border-left: 0.5px solid color-mix(in srgb, var(--dsw-alias-label-primary-foreground) 30%, transparent);
|
|
border-top-left-radius: 0;
|
|
border-bottom-left-radius: 0;
|
|
}
|
|
|
|
.iconWrap {
|
|
display: inline-flex;
|
|
}
|
|
|
|
/* The destructive text action in a card body: red ink inside a 30% red frame,
|
|
and the outline hover rebinds to a red wash instead of the gray one. */
|
|
.danger {
|
|
color: var(--dsw-alias-state-error-primary);
|
|
border-color: color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent);
|
|
--dsw-alias-interactive-bg-hover: color-mix(in srgb, var(--dsw-alias-state-error-primary) 8%, transparent);
|
|
}
|
|
|
|
/* Retry, uninstall, and the switch at the end of the page's head. */
|
|
.detailActions {
|
|
display: flex;
|
|
flex: none;
|
|
align-items: center;
|
|
gap: 16px;
|
|
}
|
|
|
|
.actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
}
|
|
|
|
/* The delete of a row the person added: red on its own, not only on hover. */
|
|
.deleteButton {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 28px;
|
|
height: 28px;
|
|
border: 0;
|
|
border-radius: var(--dsw-radius-sm);
|
|
background: transparent;
|
|
color: var(--dsw-alias-state-error-primary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.deleteButton:hover:not(:disabled) {
|
|
background: var(--dsw-alias-interactive-bg-hover-danger, var(--dsw-alias-interactive-bg-hover));
|
|
}
|
|
|
|
.deleteButton:disabled {
|
|
opacity: 0.5;
|
|
cursor: default;
|
|
}
|
|
|
|
.deleteButton:focus-visible {
|
|
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
|
|
.reason {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-state-error-primary);
|
|
overflow-wrap: anywhere;
|
|
/* The Host's captured tail, when no output streamed, keeps its lines. */
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.partsHead .subLabel {
|
|
margin: 0;
|
|
}
|
|
|
|
.partsFilter {
|
|
width: 200px;
|
|
}
|
|
|
|
.partsFilter:focus-visible {
|
|
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
outline-offset: 1px;
|
|
}
|
|
|
|
/* Wider than the default dialog: pnpm's lines are long, and the terminal keeps them unwrapped. The card never
|
|
outgrows the Modal's padding box and shares the settings panel's 800px height cap; the running screens
|
|
scroll inside it so the head's controls stay reachable. */
|
|
.installDialog {
|
|
width: min(560px, 100%);
|
|
max-height: min(800px, 100%);
|
|
}
|
|
|
|
/* The spec screen's content scrolls inside the card when the guide is open on a short viewport; the footer stays put. */
|
|
.installContent {
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.installBody {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.installFooter {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
gap: 20px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.templateHint {
|
|
margin: -4px 0 0;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
/* Where pnpm installs: the profile directory the first run names. */
|
|
.installLocation {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.installField {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.installField input[type='text'] {
|
|
height: 40px;
|
|
padding: 0 14px;
|
|
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
border-radius: var(--dsw-radius-md);
|
|
outline: none;
|
|
background: var(--dsw-alias-bg-layer-3);
|
|
font: inherit;
|
|
font-size: 13px;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.installField input[type='text']:focus,
|
|
.registryCustomField:focus {
|
|
border-color: var(--dsw-alias-state-business-primary);
|
|
box-shadow: inset 0 0 0 0.5px var(--dsw-alias-state-business-primary);
|
|
}
|
|
|
|
.installField input[type='text'][aria-invalid='true'],
|
|
.registryCustomField[aria-invalid='true'] {
|
|
border-color: var(--dsw-alias-state-error-primary);
|
|
box-shadow: none;
|
|
}
|
|
|
|
.installField input[type='text'][aria-invalid='true']:focus,
|
|
.registryCustomField[aria-invalid='true']:focus {
|
|
box-shadow: inset 0 0 0 0.5px var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
}
|
|
|
|
/* The guide under the field: opened by a text button, it says what an ID is and shows the three forms with examples. */
|
|
.guideToggle {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
align-self: flex-start;
|
|
gap: 4px;
|
|
margin-top: 4px;
|
|
padding: 0;
|
|
border: 0;
|
|
background: none;
|
|
font: inherit;
|
|
font-size: 12.5px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.guideToggle:hover {
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.guideChevron {
|
|
transition: transform 160ms ease;
|
|
}
|
|
|
|
.guideToggle[aria-expanded='true'] .guideChevron {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.guide {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 10px;
|
|
padding: 8px 14px 14px;
|
|
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
border-radius: var(--dsw-radius-lg);
|
|
background: var(--dsw-alias-bg-layer-1);
|
|
}
|
|
|
|
.guideHint {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
/* Trust guidance stays beside the install action, including while examples are collapsed. */
|
|
.installSafety {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 8px;
|
|
margin: 0;
|
|
padding: 10px 12px;
|
|
border-radius: var(--dsw-radius-md);
|
|
background: var(--dsw-alias-state-warn-tertiary);
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.installSafety > svg {
|
|
flex: none;
|
|
margin-top: 2px;
|
|
color: var(--dsw-alias-state-warn-primary);
|
|
}
|
|
|
|
.guideList {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.guideItem {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
padding: 8px 0;
|
|
}
|
|
|
|
.guideItem > button {
|
|
align-self: center;
|
|
}
|
|
|
|
.guideIndex {
|
|
display: inline-flex;
|
|
flex: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 20px;
|
|
height: 20px;
|
|
border-radius: 999px;
|
|
corner-shape: round;
|
|
background: var(--dsw-alias-bg-module-platform);
|
|
font-size: 11px;
|
|
font-weight: 500;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.guideMain {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
gap: 0;
|
|
min-width: 0;
|
|
}
|
|
|
|
.guideTitle {
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.guideExample {
|
|
font-size: 12px;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.guideExample code {
|
|
font-family: var(--ds-font-family-code);
|
|
}
|
|
|
|
.guideExampleLabel {
|
|
color: var(--dsw-alias-label-tertiary);
|
|
}
|
|
|
|
/* The row under the field: the guide's toggle on the left, the registry's on the right, both folded by default. */
|
|
.optionsRow {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 8px 12px;
|
|
}
|
|
|
|
/* The registry toggle names the registry the install asks first, so the choice reads without unfolding. */
|
|
.registryToggle {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
height: 28px;
|
|
padding: 0 8px 0 10px;
|
|
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
border-radius: var(--dsw-radius-md);
|
|
background: var(--dsw-alias-bg-layer-2);
|
|
font: inherit;
|
|
font-size: 12.5px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.registryToggle:hover:not(:disabled) {
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.registryToggle:disabled {
|
|
cursor: default;
|
|
opacity: 0.6;
|
|
}
|
|
|
|
.registryToggle[aria-expanded='true'] {
|
|
border-color: var(--dsw-alias-border-l3);
|
|
background: var(--dsw-alias-bg-layer-3);
|
|
}
|
|
|
|
.registryToggle[aria-expanded='true'] .guideChevron {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.registryChosen {
|
|
font-weight: 500;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
/* The registry options float from their toggle over the dialog: pnpm's own, the configured mirrors, and a typed
|
|
address, with the order asked under them. Portaled and fixed, so the dialog keeps its height. */
|
|
.registry {
|
|
position: fixed;
|
|
z-index: 1100;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
box-sizing: border-box;
|
|
width: min(440px, calc(100vw - 24px));
|
|
margin: 0;
|
|
padding: 8px;
|
|
border: 0;
|
|
/* An opaque layer keeps the registry form controls readable over the dialog. */
|
|
background: var(--dsw-alias-bg-layer-2);
|
|
border-radius: var(--dsw-radius-lg);
|
|
--dsw-elevation-stroke-color: var(--dsw-alias-border-l1);
|
|
box-shadow: var(--dsw-elevation-prominent);
|
|
min-width: 0;
|
|
}
|
|
|
|
.registryOption {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
padding: 8px 10px;
|
|
border: 0;
|
|
border-radius: var(--dsw-radius-md);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.registryOption:hover {
|
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
}
|
|
|
|
/* An offered registry is one row: the radio beside its title and the line under it. */
|
|
label.registryOption {
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
}
|
|
|
|
.registryOption input[type='radio'] {
|
|
flex: none;
|
|
width: 16px;
|
|
height: 16px;
|
|
margin: 2px 0 0;
|
|
accent-color: var(--dsw-alias-brand-primary);
|
|
}
|
|
|
|
.registryTitle {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 8px;
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.registryHint {
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.registryCustomPick {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.registryCustomField {
|
|
outline: none;
|
|
height: 32px;
|
|
margin-left: 26px;
|
|
padding: 0 10px;
|
|
border: 0.5px solid var(--dsw-alias-border-l4);
|
|
border-radius: var(--dsw-radius-md);
|
|
background: var(--dsw-alias-bg-layer-3);
|
|
font: inherit;
|
|
font-size: 13px;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
/* The typed address's field, refusal, and line sit under its title, past the radio and its gap. */
|
|
.registryOption > .inputError,
|
|
.registryOption > .registryHint {
|
|
margin-left: 26px;
|
|
}
|
|
|
|
/* What the check refused, under the field. */
|
|
.inputError {
|
|
margin: -4px 0 0;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-state-error-primary);
|
|
}
|
|
|
|
/* The footer's only action spans the dialog, as the primary action of a short form does. */
|
|
.wide {
|
|
width: 100%;
|
|
height: 40px;
|
|
justify-content: center;
|
|
border-radius: var(--dsw-radius-md);
|
|
}
|
|
|
|
/* The installing, installed, and failed screens: a head row, the centred
|
|
state, the subject card, then the details toggle beside the screen's action. */
|
|
.wizard {
|
|
display: flex;
|
|
flex: 1 1 auto;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
min-width: 0;
|
|
min-height: 0;
|
|
padding: 16px 20px 0;
|
|
}
|
|
|
|
/* Everything under the head: it scrolls when the runs' output makes the screen taller than the card. */
|
|
.wizardScroll {
|
|
display: flex;
|
|
flex: 1 1 auto;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
min-height: 0;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.wizardHead {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
min-height: 24px;
|
|
}
|
|
|
|
.wizardBack {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 0;
|
|
border: 0;
|
|
background: none;
|
|
font: inherit;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
color: var(--dsw-alias-label-primary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.wizardClose {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 24px;
|
|
height: 24px;
|
|
padding: 0;
|
|
border: 0;
|
|
border-radius: var(--dsw-radius-sm);
|
|
background: none;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.wizardClose:hover {
|
|
background: var(--dsw-alias-bg-layer-3);
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.wizardHero {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 8px 0 4px;
|
|
text-align: center;
|
|
}
|
|
|
|
.wizardIcon {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 44px;
|
|
height: 44px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.wizardIcon[data-state='done'] {
|
|
color: var(--dsw-alias-state-success-secondary);
|
|
}
|
|
|
|
.wizardIcon[data-state='error'] {
|
|
color: var(--dsw-alias-state-warn-label);
|
|
}
|
|
|
|
.wizardTitle {
|
|
margin: 0;
|
|
font-size: 18px;
|
|
line-height: 26px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.wizardSub {
|
|
margin: 0;
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.subject {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 16px;
|
|
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
border-radius: var(--dsw-radius-lg);
|
|
text-align: center;
|
|
}
|
|
|
|
.subjectName {
|
|
margin: 0;
|
|
font-size: 15px;
|
|
line-height: 22px;
|
|
font-weight: 600;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.subjectDesc,
|
|
.subjectMeta {
|
|
margin: 0;
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.wizardFoot {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
}
|
|
|
|
.footAction {
|
|
height: 32px;
|
|
border-radius: var(--dsw-radius-md);
|
|
}
|
|
|
|
.detailsToggle {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 4px;
|
|
padding: 0;
|
|
border: 0;
|
|
background: none;
|
|
font: inherit;
|
|
font-size: 13px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.detailsChevron {
|
|
transition: transform 160ms ease;
|
|
}
|
|
|
|
.detailsToggle[aria-expanded='true'] .detailsChevron {
|
|
transform: rotate(180deg);
|
|
}
|
|
|
|
.detailsBody {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
min-width: 0;
|
|
}
|
|
|
|
/* The failed screen's actions beside the details toggle: change the registry, retry. */
|
|
.wizardActions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* One pnpm run of the install; once several registries were asked, a badge names each run's. */
|
|
.run {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 4px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.attemptBadge {
|
|
align-self: flex-start;
|
|
margin: 0;
|
|
padding: 0 6px;
|
|
border-radius: var(--dsw-radius-sm);
|
|
background: var(--dsw-alias-bg-layer-3);
|
|
font-size: 11px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.detailsChevron {
|
|
transition: none;
|
|
}
|
|
}
|
|
|
|
.result,
|
|
.resultWarn {
|
|
margin: 0;
|
|
padding: 8px 12px;
|
|
border-radius: var(--dsw-radius-md);
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.resultWarn {
|
|
background: var(--dsw-alias-state-warn-tertiary);
|
|
}
|
|
|
|
/* The approval a blocked run asks for: the packages whose install scripts pnpm did not run, and the one action. */
|
|
.approval {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
padding: 12px 14px;
|
|
border: 0.5px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);
|
|
border-radius: var(--dsw-radius-lg);
|
|
background: var(--dsw-alias-state-warn-tertiary);
|
|
}
|
|
|
|
.approvalTitle {
|
|
margin: 0;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.approvalText {
|
|
margin: 0;
|
|
font-size: 12.5px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.approvalCaution {
|
|
margin: 0;
|
|
font-size: 12.5px;
|
|
line-height: 18px;
|
|
font-weight: 500;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.approvalList {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6px;
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.approvalList code {
|
|
display: inline-block;
|
|
padding: 2px 8px;
|
|
border-radius: var(--dsw-radius-sm);
|
|
background: var(--dsw-alias-bg-layer-3);
|
|
font-family: var(--ds-font-family-code);
|
|
font-size: 12px;
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
/* The install terminal: the shared TerminalBlock at the bash card's smaller
|
|
size, its output capped so an expanded log scrolls inside the dialog. */
|
|
.terminal {
|
|
--dsl-terminal-font: var(--dsw-font-markdown-code-block-small);
|
|
--dsl-terminal-line-height: 18px;
|
|
--dsl-terminal-output-max-height: 240px;
|
|
margin: 4px 0 0;
|
|
border: 0.5px solid var(--dsw-alias-border-l1);
|
|
}
|
|
|
|
.dependents {
|
|
margin: 8px 0 0;
|
|
padding-left: 18px;
|
|
font-size: 13px;
|
|
line-height: 20px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.dangerButton {
|
|
--dsw-alias-button-primary-fill: var(--dsw-alias-state-error-primary);
|
|
--dsw-alias-button-primary-hover: var(--dsw-alias-state-error-primary);
|
|
}
|
|
|
|
.detail {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
/* The head row every detail view shares: the crumb over the icon/actions row.
|
|
It carries the same top clearance as .pageHead — the crumb is the detail
|
|
views' leading content and otherwise lands under the traffic lights — and the
|
|
same drag ownership: it marks itself `data-window-drag`, so this row's own box
|
|
is the window's draggable geometry. The detail container below stays
|
|
content: marking it would drag the window over its text and form labels. */
|
|
.detailTop {
|
|
display: flex;
|
|
flex-direction: column;
|
|
/* The same 28px top inset `.pageHead` carries, so the row starts at the window's
|
|
top edge and the strip above the detail content drags the window. */
|
|
padding-top: 28px;
|
|
}
|
|
|
|
:global([data-platform='darwin']) .detailTop {
|
|
padding-top: calc(28px + var(--dsh-frame-top-clearance, 0px));
|
|
}
|
|
|
|
.crumb {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
border: 0;
|
|
padding: 0;
|
|
background: none;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
font: inherit;
|
|
font-size: 12.5px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.crumb:hover {
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.crumb:focus-visible {
|
|
outline: var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color, var(--dsw-alias-state-business-primary));
|
|
outline-offset: 2px;
|
|
}
|
|
|
|
.crumbIcon {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
/* Head row: the pack's icon at one edge, its actions at the other; the name
|
|
block sits under the icon (figma detail handoff). */
|
|
.detailHead {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
margin: 32px 0 0;
|
|
}
|
|
|
|
.detailMain {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 8px;
|
|
min-width: 0;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.detailTitle {
|
|
margin: 0;
|
|
font-size: 20px;
|
|
line-height: 28px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.versionTag {
|
|
flex: none;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
.detailDesc {
|
|
margin: 0;
|
|
color: var(--dsw-alias-label-secondary);
|
|
font-size: 14px;
|
|
line-height: 22px;
|
|
}
|
|
|
|
/* The package or row identity under the title: the exact spec that installs the
|
|
package elsewhere, where a title of its own or a shortened name hides it. */
|
|
.detailName {
|
|
margin: 0;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.detailName code {
|
|
font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
}
|
|
|
|
/* The unknown-package note under the name block. */
|
|
.detail > .detailDesc {
|
|
margin-top: 12px;
|
|
}
|
|
|
|
.detailSections {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 32px;
|
|
margin-top: 32px;
|
|
}
|
|
|
|
.detailSection {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.sectionHead {
|
|
display: flex;
|
|
align-items: baseline;
|
|
gap: 10px;
|
|
}
|
|
|
|
.sectionTitle {
|
|
margin: 0;
|
|
font-size: 14px;
|
|
line-height: 20px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.sectionCount {
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
/* The way into a package's page. */
|
|
.cardArrow {
|
|
flex: none;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
}
|
|
|
|
/* Rows sit on the page ground, one hairline apart, so they read on a white page too. */
|
|
.rows {
|
|
display: flex;
|
|
flex-direction: column;
|
|
margin: 0;
|
|
padding: 0;
|
|
list-style: none;
|
|
}
|
|
|
|
.row {
|
|
padding: 12px 2px;
|
|
border-bottom: 0.5px solid var(--dsw-alias-border-l2);
|
|
}
|
|
|
|
.row:last-child {
|
|
border-bottom: 0;
|
|
}
|
|
|
|
.rowLine {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.rowIcon {
|
|
display: inline-flex;
|
|
flex: none;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 0.5px solid var(--dsw-alias-border-l3);
|
|
border-radius: var(--dsw-radius-md);
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.rowMain {
|
|
display: flex;
|
|
flex: 1;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
min-width: 0;
|
|
}
|
|
|
|
.rowId {
|
|
font-size: 13.5px;
|
|
line-height: 20px;
|
|
font-weight: 500;
|
|
color: var(--dsw-alias-label-primary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
.row[data-state='off'] .rowId {
|
|
color: var(--dsw-alias-label-secondary);
|
|
}
|
|
|
|
.rowModule {
|
|
font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
|
|
font-size: 11.5px;
|
|
line-height: 16px;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
overflow-wrap: anywhere;
|
|
}
|
|
|
|
/* A row with a configuration page: its name opens the page, the arrow says so. */
|
|
.rowOpen {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 2px;
|
|
padding: 0;
|
|
border: 0;
|
|
background: transparent;
|
|
color: inherit;
|
|
font: inherit;
|
|
text-align: left;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.rowOpen:hover .rowId {
|
|
text-decoration: underline;
|
|
text-underline-offset: 3px;
|
|
}
|
|
|
|
.rowOpenIcon {
|
|
flex: none;
|
|
color: var(--dsw-alias-label-tertiary);
|
|
}
|
|
|
|
.rowOpen:hover .rowOpenIcon {
|
|
color: var(--dsw-alias-label-primary);
|
|
}
|
|
|
|
.rowState {
|
|
display: inline-flex;
|
|
flex: none;
|
|
align-items: center;
|
|
gap: 6px;
|
|
font-size: 12.5px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-label-secondary);
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.row[data-state='failed'] .rowState {
|
|
color: var(--dsw-alias-state-error-primary);
|
|
}
|
|
|
|
|
|
|
|
.rowFailure {
|
|
margin: 4px 0 0 56px;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
color: var(--dsw-alias-state-error-primary);
|
|
overflow-wrap: anywhere;
|
|
}
|