mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-10-04 02:52:55 +08:00
feat(desktop): add promotion marquee and proxy fix
This commit is contained in:
Generated
+10
@@ -23,6 +23,7 @@
|
||||
"keepalive-for-react-router": "^5.0.7",
|
||||
"monaco-editor": "0.56.0",
|
||||
"react": "^19.2.8",
|
||||
"react-css-marquee": "^1.0.1",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-router-dom": "^7.18.2",
|
||||
"sortablejs": "^1.15.7",
|
||||
@@ -2978,6 +2979,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-css-marquee": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-css-marquee/-/react-css-marquee-1.0.1.tgz",
|
||||
"integrity": "sha512-jPHydXILF3oIP2gOvabfbEY3PUdbkHtDVA4mqYQiYvEdLWk9oqNJkUwDBxHK1RtltvQY3zkyuxTaGnw+G8x48w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8 || ^17 || ^18 || ^19"
|
||||
}
|
||||
},
|
||||
"node_modules/react-dom": {
|
||||
"version": "19.2.8",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz",
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"keepalive-for-react-router": "^5.0.7",
|
||||
"monaco-editor": "0.56.0",
|
||||
"react": "^19.2.8",
|
||||
"react-css-marquee": "^1.0.1",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-router-dom": "^7.18.2",
|
||||
"sortablejs": "^1.15.7",
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"app": {
|
||||
"windows": [],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; img-src 'self' asset: http://asset.localhost data: https:; style-src 'self' 'unsafe-inline'; connect-src 'self' http://127.0.0.1:*",
|
||||
"csp": "default-src 'self'; img-src 'self' asset: http://asset.localhost data: https:; font-src 'self' data:; style-src 'self' 'unsafe-inline'; connect-src 'self' http://127.0.0.1:*",
|
||||
"dangerousDisableAssetCspModification": [
|
||||
"style-src"
|
||||
]
|
||||
|
||||
@@ -108,7 +108,7 @@ export function installDemoApi() {
|
||||
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
||||
const body = await readBody(input, init);
|
||||
|
||||
if (path === "/ads") return json({ slots: [] });
|
||||
if (path === "/promotions") return json({ slots: [] });
|
||||
if (path === "/models" && method === "GET") return json(models);
|
||||
if (path === "/models" && method === "POST") return json(models);
|
||||
if (path === "/models/order") return json(models);
|
||||
|
||||
@@ -57,7 +57,6 @@ export function HomePage() {
|
||||
const { locale } = useI18n();
|
||||
const [preset, setPreset] = useState<OverviewRangePreset>("month");
|
||||
const [quick, setQuick] = useState<QuickPreset | null>(null);
|
||||
const [fourHourBucket, setFourHourBucket] = useState<number | undefined>(undefined);
|
||||
const [customRange, setCustomRange] = useState<TimeRange | null>(null);
|
||||
const [customOpen, setCustomOpen] = useState(false);
|
||||
const [customStart, setCustomStart] = useState("");
|
||||
@@ -76,14 +75,13 @@ export function HomePage() {
|
||||
void api.overview({
|
||||
...selectedRange,
|
||||
modelHashes: appliedModels,
|
||||
bucketMs: fourHourBucket,
|
||||
}).then((next) => {
|
||||
if (active) setRangeOverview(next);
|
||||
}).finally(() => {
|
||||
if (active) setRangeBusy(false);
|
||||
});
|
||||
return () => { active = false; };
|
||||
}, [preset, customRange, overview, refreshVersion, appliedModels, fourHourBucket]);
|
||||
}, [preset, customRange, overview, refreshVersion, appliedModels]);
|
||||
|
||||
const filteredOverview = rangeOverview ?? overview;
|
||||
const dailyTokenUsage = filteredOverview.token_usage_series.map((bucket) => ({
|
||||
@@ -118,11 +116,10 @@ export function HomePage() {
|
||||
setCustomRange({ startMs, endMs });
|
||||
setAppliedModels(selectedModels);
|
||||
setQuick(null);
|
||||
setFourHourBucket(undefined);
|
||||
setPreset("custom");
|
||||
setCustomOpen(false);
|
||||
};
|
||||
const selectQuick = (durationMs: number, bucketMs?: number) => {
|
||||
const selectQuick = (durationMs: number) => {
|
||||
const end = new Date();
|
||||
const start = new Date(end.getTime() - durationMs);
|
||||
setCustomStart(formatTimeInput(start));
|
||||
@@ -130,13 +127,12 @@ export function HomePage() {
|
||||
setCustomRange({ startMs: start.getTime(), endMs: end.getTime() });
|
||||
setAppliedModels(selectedModels);
|
||||
setQuick(durationMs === 4 * 60 * 60_000 ? "four-hours" : "twenty-four-hours");
|
||||
setFourHourBucket(durationMs === 4 * 60 * 60_000 ? bucketMs : undefined);
|
||||
setPreset("custom");
|
||||
setCustomOpen(false);
|
||||
};
|
||||
const selectPreset = (value: Exclude<OverviewRangePreset, "custom">) => {
|
||||
setPreset(value);
|
||||
setQuick(null);
|
||||
setFourHourBucket(undefined);
|
||||
setCustomOpen(false);
|
||||
};
|
||||
const refresh = async () => {
|
||||
@@ -152,7 +148,7 @@ export function HomePage() {
|
||||
icon: iconFor(model.type),
|
||||
})),
|
||||
...plugins.flatMap((plugin) => plugin.providers.flatMap((provider) =>
|
||||
provider.configured ? provider.models.map((model) => ({
|
||||
provider.configured ? provider.models.filter((model) => model.enabled).map((model) => ({
|
||||
value: model.id,
|
||||
label: model.displayName,
|
||||
group: pluginText(provider.displayName, locale) || model.pluginName,
|
||||
@@ -187,7 +183,6 @@ export function HomePage() {
|
||||
<PageActions><OverviewTimeRangeFilter
|
||||
value={preset}
|
||||
quick={quick}
|
||||
fourHourBucket={fourHourBucket}
|
||||
customOpen={customOpen}
|
||||
customStart={customStart}
|
||||
customEnd={customEnd}
|
||||
|
||||
@@ -95,31 +95,3 @@
|
||||
&[aria-pressed="true"] { color: var(--vscode-list-activeSelectionForeground); background: var(--vscode-list-activeSelectionBackground); border-color: transparent; }
|
||||
}
|
||||
}
|
||||
|
||||
.granularityMenu {
|
||||
position: fixed;
|
||||
z-index: 15000;
|
||||
min-width: 96px;
|
||||
padding: 4px;
|
||||
background: var(--vscode-dropdown-background);
|
||||
border: 1px solid var(--vscode-dropdown-border);
|
||||
border-radius: 6px;
|
||||
box-shadow: var(--oa-dropdown-shadow);
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
min-height: 28px;
|
||||
display: block;
|
||||
padding: 4px 8px;
|
||||
color: var(--vscode-dropdown-foreground);
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
font-size: type.$font-size-xs;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover { background: var(--vscode-list-activeSelectionBackground); color: var(--vscode-list-activeSelectionForeground); }
|
||||
&[aria-pressed="true"] { background: var(--vscode-list-activeSelectionBackground); color: var(--vscode-list-activeSelectionForeground); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@ import styles from "./OverviewTimeRangeFilter.module.scss";
|
||||
export type OverviewRangePreset = "ten-minutes" | "hour" | "today" | "week" | "month" | "custom";
|
||||
export type QuickPreset = "four-hours" | "twenty-four-hours";
|
||||
|
||||
export function OverviewTimeRangeFilter({ value, quick, fourHourBucket, customOpen, customStart, customEnd, modelOptions, selectedModels, busy, onSelect, onQuickSelect, onCustomOpenChange, onCustomStartChange, onCustomEndChange, onSelectedModelsChange, onCustomApply, onRefresh }: {
|
||||
export function OverviewTimeRangeFilter({ value, quick, customOpen, customStart, customEnd, modelOptions, selectedModels, busy, onSelect, onQuickSelect, onCustomOpenChange, onCustomStartChange, onCustomEndChange, onSelectedModelsChange, onCustomApply, onRefresh }: {
|
||||
value: OverviewRangePreset;
|
||||
quick: QuickPreset | null;
|
||||
fourHourBucket: number | undefined;
|
||||
customOpen: boolean;
|
||||
customStart: string;
|
||||
customEnd: string;
|
||||
@@ -23,7 +22,7 @@ export function OverviewTimeRangeFilter({ value, quick, fourHourBucket, customOp
|
||||
selectedModels: string[];
|
||||
busy: boolean;
|
||||
onSelect: (value: Exclude<OverviewRangePreset, "custom">) => void;
|
||||
onQuickSelect: (durationMs: number, bucketMs?: number) => void;
|
||||
onQuickSelect: (durationMs: number) => void;
|
||||
onCustomOpenChange: (open: boolean) => void;
|
||||
onCustomStartChange: (value: string) => void;
|
||||
onCustomEndChange: (value: string) => void;
|
||||
@@ -42,21 +41,10 @@ export function OverviewTimeRangeFilter({ value, quick, fourHourBucket, customOp
|
||||
{ value: "four-hours", label: t("近4小时") },
|
||||
{ value: "twenty-four-hours", label: t("近24小时") },
|
||||
];
|
||||
const granularityOptions = [
|
||||
{ bucketMs: 60_000, label: t("1分钟") },
|
||||
{ bucketMs: 15 * 60_000, label: t("15分钟") },
|
||||
{ bucketMs: 30 * 60_000, label: t("30分钟") },
|
||||
{ bucketMs: 60 * 60_000, label: t("1小时") },
|
||||
];
|
||||
const customButton = useRef<HTMLButtonElement>(null);
|
||||
const popover = useRef<HTMLDivElement>(null);
|
||||
const fourHourButton = useRef<HTMLButtonElement>(null);
|
||||
const granularityMenu = useRef<HTMLDivElement>(null);
|
||||
const popoverId = useId();
|
||||
const granularityMenuId = useId();
|
||||
const [position, setPosition] = useState({ left: 0, top: 0, width: 300, maxHeight: 480 });
|
||||
const [menuPosition, setMenuPosition] = useState({ left: 0, top: 0 });
|
||||
const [granularityOpen, setGranularityOpen] = useState(false);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!customOpen || !customButton.current || !popover.current) return;
|
||||
@@ -72,28 +60,11 @@ export function OverviewTimeRangeFilter({ value, quick, fourHourBucket, customOp
|
||||
}).then(({ x, y }) => setPosition((current) => ({ ...current, left: x, top: y }))));
|
||||
}, [customOpen]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!granularityOpen || !fourHourButton.current || !granularityMenu.current) return;
|
||||
return autoUpdate(fourHourButton.current, granularityMenu.current, () =>
|
||||
void computePosition(fourHourButton.current!, granularityMenu.current!, {
|
||||
placement: "right-start",
|
||||
middleware: [offset(4), flip({ padding: 10 }), shift({ padding: 10 })],
|
||||
}).then(({ x, y }) => setMenuPosition({ left: x, top: y })));
|
||||
}, [granularityOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!customOpen) setGranularityOpen(false);
|
||||
}, [customOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!customOpen) return;
|
||||
const closeOutside = (event: PointerEvent) => {
|
||||
const target = event.target as Node;
|
||||
if (
|
||||
!customButton.current?.contains(target)
|
||||
&& !popover.current?.contains(target)
|
||||
&& !granularityMenu.current?.contains(target)
|
||||
) onCustomOpenChange(false);
|
||||
if (!customButton.current?.contains(target) && !popover.current?.contains(target)) onCustomOpenChange(false);
|
||||
};
|
||||
document.addEventListener("pointerdown", closeOutside);
|
||||
return () => document.removeEventListener("pointerdown", closeOutside);
|
||||
@@ -102,11 +73,6 @@ export function OverviewTimeRangeFilter({ value, quick, fourHourBucket, customOp
|
||||
const parsedStart = parseTimeInput(customStart);
|
||||
const parsedEnd = parseTimeInput(customEnd);
|
||||
const customValid = parsedStart !== null && parsedEnd !== null && parsedStart < parsedEnd;
|
||||
const closeGranularity = () => {
|
||||
setGranularityOpen(false);
|
||||
fourHourButton.current?.focus();
|
||||
};
|
||||
|
||||
return <div className={styles.root} aria-label={t("概览时间范围")}>
|
||||
<div className={styles.presets}>
|
||||
{presets.map((preset) => <button
|
||||
@@ -135,42 +101,22 @@ export function OverviewTimeRangeFilter({ value, quick, fourHourBucket, customOp
|
||||
role="dialog"
|
||||
aria-label={t("自定义概览筛选")}
|
||||
style={position}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.preventDefault();
|
||||
if (granularityOpen) {
|
||||
closeGranularity();
|
||||
return;
|
||||
}
|
||||
onCustomOpenChange(false);
|
||||
customButton.current?.focus();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className={styles.quickPresets} aria-label={t("快捷时间范围")}>
|
||||
{quickPresets.map((preset) => preset.value === "four-hours"
|
||||
? <button
|
||||
key={preset.value}
|
||||
ref={fourHourButton}
|
||||
type="button"
|
||||
aria-haspopup="menu"
|
||||
aria-controls={granularityOpen ? granularityMenuId : undefined}
|
||||
aria-expanded={granularityOpen}
|
||||
aria-pressed={quick === "four-hours"}
|
||||
onClick={() => {
|
||||
onQuickSelect(4 * 60 * 60_000, fourHourBucket);
|
||||
setGranularityOpen((current) => !current);
|
||||
}}
|
||||
>{preset.label}</button>
|
||||
: <button
|
||||
key={preset.value}
|
||||
type="button"
|
||||
aria-pressed={quick === "twenty-four-hours"}
|
||||
onClick={() => {
|
||||
setGranularityOpen(false);
|
||||
onQuickSelect(24 * 60 * 60_000);
|
||||
}}
|
||||
>{preset.label}</button>)}
|
||||
{quickPresets.map((preset) => <button
|
||||
key={preset.value}
|
||||
type="button"
|
||||
aria-pressed={quick === preset.value}
|
||||
onClick={() => onQuickSelect(preset.value === "four-hours" ? 4 * 60 * 60_000 : 24 * 60 * 60_000)}
|
||||
>{preset.label}</button>)}
|
||||
</div>
|
||||
<label><span>{t("开始时间")}</span><input type="text" placeholder={t("如:2026-08-23 09:00、1小时前")} value={customStart} onChange={(event) => onCustomStartChange(event.target.value)} /></label>
|
||||
<label><span>{t("结束时间")}</span><input type="text" placeholder={t("如:现在、2026-08-23 18:00")} value={customEnd} onChange={(event) => onCustomEndChange(event.target.value)} /></label>
|
||||
@@ -180,31 +126,5 @@ export function OverviewTimeRangeFilter({ value, quick, fourHourBucket, customOp
|
||||
<button type="button" className={controls.primary} disabled={!customValid} onClick={onCustomApply}>{t("应用")}</button>
|
||||
</div>
|
||||
</div>, document.body)}
|
||||
{granularityOpen && customOpen && createPortal(<div
|
||||
id={granularityMenuId}
|
||||
ref={granularityMenu}
|
||||
className={styles.granularityMenu}
|
||||
role="menu"
|
||||
aria-label={t("显示粒度")}
|
||||
style={menuPosition}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Escape") {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
closeGranularity();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{granularityOptions.map((option) => <button
|
||||
key={option.bucketMs}
|
||||
type="button"
|
||||
role="menuitem"
|
||||
aria-pressed={quick === "four-hours" && fourHourBucket === option.bucketMs}
|
||||
onClick={() => {
|
||||
setGranularityOpen(false);
|
||||
onQuickSelect(4 * 60 * 60_000, option.bucketMs);
|
||||
}}
|
||||
>{option.label}</button>)}
|
||||
</div>, document.body)}
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -78,6 +78,8 @@ export function PluginManagementPage() {
|
||||
? t("{name} 账号管理", { name: selectedPlugin?.name ?? "" })
|
||||
: t("添加 {name} 账号", { name: selectedPlugin?.name ?? "" })}
|
||||
onClose={() => setSelected(null)}
|
||||
onSubmit={() => setSelected(null)}
|
||||
submitLabel={t("确定")}
|
||||
>
|
||||
{selected?.mode === "add" && selectedPlugin && <PluginAddPanel plugin={selectedPlugin} onConfigured={() => setSelected(null)} />}
|
||||
{selected?.mode === "settings" && selectedPlugin && <PluginSettingsPanel plugin={selectedPlugin} />}
|
||||
|
||||
@@ -71,7 +71,7 @@ export function CommitSettingsCard() {
|
||||
for (const provider of plugin.providers) {
|
||||
if (!provider.configured) continue;
|
||||
const group = pluginText(provider.displayName, locale) || plugin.name;
|
||||
for (const model of provider.models) {
|
||||
for (const model of provider.models.filter((model) => model.enabled)) {
|
||||
seen.add(model.id);
|
||||
options.push({
|
||||
value: model.id,
|
||||
|
||||
@@ -409,7 +409,7 @@
|
||||
"f9aa11dbb15ce647": "Saturday",
|
||||
"f9b55ca75425161b": "Response content was not recorded. Enable detailed records and try again.",
|
||||
"fa5b4b8a751c7d1b": "The local proxy port used by Cursor. Enter 0 to select a random port at startup.",
|
||||
"fac2a67ad87807c4": "",
|
||||
"fac2a67ad87807c4": "Confirm",
|
||||
"fad86bf65f72c747": "Download progress",
|
||||
"fb11aa6f29827095": "Checking…",
|
||||
"fbe8778fa8b9bab5": "Initialize the local CA first",
|
||||
|
||||
@@ -303,7 +303,7 @@ export interface PluginImportResult {
|
||||
|
||||
export function configuredPluginModels(plugins: PluginDescriptor[]): PluginModelDescriptor[] {
|
||||
return plugins.flatMap((plugin) =>
|
||||
plugin.providers.flatMap((provider) => provider.configured ? provider.models : []));
|
||||
plugin.providers.flatMap((provider) => provider.configured ? provider.models.filter((model) => model.enabled) : []));
|
||||
}
|
||||
|
||||
export interface OverviewMetrics {
|
||||
@@ -437,14 +437,14 @@ async function request<T>(path: string, init?: RequestInit): Promise<T> {
|
||||
export const api = {
|
||||
ads: (disabledAdIds: Iterable<string>, locale: Locale) => {
|
||||
const value = [...disabledAdIds].join(",");
|
||||
return request<AdRuntime>("/ads", {
|
||||
return request<AdRuntime>("/promotions", {
|
||||
headers: {
|
||||
"accept-language": locale,
|
||||
...(value ? { "disable-ad-ids": value } : {}),
|
||||
},
|
||||
});
|
||||
},
|
||||
dismissAd: (id: string, reason: string) => request<void>(`/ads/${encodeURIComponent(id)}/dismissals`, { method: "POST", body: JSON.stringify({ reason }) }),
|
||||
dismissAd: (id: string, reason: string) => request<void>(`/promotions/${encodeURIComponent(id)}/dismissals`, { method: "POST", body: JSON.stringify({ reason }) }),
|
||||
models: () => request<Model[]>("/models"),
|
||||
createModels: (models: ModelInput[]) => request<Model[]>("/models", { method: "POST", body: JSON.stringify({ models }) }),
|
||||
reorderModels: (modelHashes: string[]) => request<Model[]>("/models/order", { method: "PUT", body: JSON.stringify({ model_hashes: modelHashes }) }),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { RefObject } from "react";
|
||||
import { useLayoutEffect, useRef, useState, type RefObject } from "react";
|
||||
import { Marquee } from "react-css-marquee";
|
||||
import { Icon } from "../../shared/ui/Icon";
|
||||
import { windowCloseIcon } from "../../shared/ui/icons";
|
||||
import type { AdSlot } from "./types";
|
||||
@@ -14,6 +15,32 @@ type AdMenuProps = {
|
||||
onDismiss: (ad: AdSlot) => void;
|
||||
};
|
||||
|
||||
type OverflowTextProps = {
|
||||
children: string;
|
||||
className: string;
|
||||
};
|
||||
|
||||
function OverflowText({ children, className }: OverflowTextProps) {
|
||||
const text = useRef<HTMLSpanElement>(null);
|
||||
const [overflowing, setOverflowing] = useState(false);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const element = text.current;
|
||||
if (!element) return;
|
||||
const update = () => setOverflowing(element.scrollWidth > element.clientWidth + 1);
|
||||
update();
|
||||
const observer = new ResizeObserver(update);
|
||||
observer.observe(element);
|
||||
if (element.parentElement) observer.observe(element.parentElement);
|
||||
return () => observer.disconnect();
|
||||
}, [children]);
|
||||
|
||||
if (!overflowing) return <span ref={text} className={className}>{children}</span>;
|
||||
return <Marquee className={styles.menuMarquee} repeat loop={0} speed={4} gap={24}>
|
||||
<span ref={text} className={className}>{children}</span>
|
||||
</Marquee>;
|
||||
}
|
||||
|
||||
export function AdMenu({ ads, activeAdId, dismissingAdId, readAdIds, triggerRefs, onOpen, onDismiss }: AdMenuProps) {
|
||||
return <div className={styles.menuSlots} aria-label={t("推荐内容")}>
|
||||
{ads.map((ad) => <div key={ad.id} className={styles.menuSlotContainer}>
|
||||
@@ -34,8 +61,8 @@ export function AdMenu({ ads, activeAdId, dismissingAdId, readAdIds, triggerRefs
|
||||
{!readAdIds.has(ad.id) && <span className={styles.unreadDot} aria-hidden="true" />}
|
||||
<img className={styles.menuImage} src={ad.target.imageUrl} alt="" />
|
||||
<span className={styles.menuCopy}>
|
||||
<span className={styles.menuTitle}>{ad.target.title}</span>
|
||||
<span className={styles.menuSubtitle}>{ad.target.description}</span>
|
||||
<OverflowText className={styles.menuTitle}>{ad.target.title}</OverflowText>
|
||||
<OverflowText className={styles.menuSubtitle}>{ad.target.description}</OverflowText>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
|
||||
@@ -25,18 +25,13 @@
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
background: color-mix(in srgb, var(--vscode-foreground) 4%, transparent);
|
||||
border: 1px solid var(--vscode-sideBar-border);
|
||||
border: 2px solid #be8d180d;
|
||||
border-radius: 6px;
|
||||
|
||||
&:hover,
|
||||
&[aria-expanded="true"] {
|
||||
background: color-mix(in srgb, var(--vscode-foreground) 9%, transparent);
|
||||
border-color: var(--vscode-sideBar-border);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 1px solid var(--vscode-focusBorder);
|
||||
outline-offset: 1px;
|
||||
border-color: #be8d180d;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +42,7 @@
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: color-mix(in srgb, var(--vscode-descriptionForeground) 42%, transparent);
|
||||
@@ -66,6 +61,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.menuSlotContainer:hover .menuDismiss {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.unreadDot {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
@@ -93,11 +92,24 @@
|
||||
|
||||
.menuTitle,
|
||||
.menuSubtitle {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.menuMarquee {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.menuMarquee .menuTitle,
|
||||
.menuMarquee .menuSubtitle {
|
||||
width: max-content;
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
.menuTitle {
|
||||
font-size: type.$font-size-xs;
|
||||
font-weight: 500;
|
||||
@@ -121,9 +133,9 @@
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
color: var(--vscode-foreground);
|
||||
background: var(--vscode-editorWidget-background);
|
||||
background: color-mix(in srgb, var(--vscode-editorWidget-background) 92%, var(--vscode-foreground) 8%);
|
||||
border-radius: 15px;
|
||||
border: none;
|
||||
border: 1px solid color-mix(in srgb, var(--vscode-foreground) 12%, transparent);
|
||||
box-shadow: 0 24px 20px rgb(0 0 0 / 28%);
|
||||
animation: ad-enter 180ms ease-out;
|
||||
|
||||
@@ -132,6 +144,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
:global(:root[data-theme="default-light"]) .floatingAd {
|
||||
background: var(--vscode-editorWidget-background);
|
||||
}
|
||||
|
||||
.hero {
|
||||
flex: 0 0 112px;
|
||||
position: relative;
|
||||
|
||||
Reference in New Issue
Block a user