Merge remote-tracking branch 'origin/master' into codex/generation-task-reliability
This commit is contained in:
+7
-1
@@ -374,6 +374,7 @@ function App() {
|
|||||||
})));
|
})));
|
||||||
|
|
||||||
const [ecommerceEverMounted, setEcommerceEverMounted] = useState(false);
|
const [ecommerceEverMounted, setEcommerceEverMounted] = useState(false);
|
||||||
|
const [workbenchResetToken, setWorkbenchResetToken] = useState(0);
|
||||||
const isEcommerceActive = activeView === "ecommerce" || activeView === "ecommerceHub";
|
const isEcommerceActive = activeView === "ecommerce" || activeView === "ecommerceHub";
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isEcommerceActive && !ecommerceEverMounted) setEcommerceEverMounted(true);
|
if (isEcommerceActive && !ecommerceEverMounted) setEcommerceEverMounted(true);
|
||||||
@@ -459,6 +460,9 @@ function App() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleSetView = useCallback((view: WebViewKey) => {
|
const handleSetView = useCallback((view: WebViewKey) => {
|
||||||
|
if (view === "workbench" && Boolean(session)) {
|
||||||
|
setWorkbenchResetToken((token) => token + 1);
|
||||||
|
}
|
||||||
window.location.hash = `/${view}`;
|
window.location.hash = `/${view}`;
|
||||||
setView(view);
|
setView(view);
|
||||||
if (view !== "login") {
|
if (view !== "login") {
|
||||||
@@ -467,7 +471,7 @@ function App() {
|
|||||||
if (isWorkspaceView(view)) {
|
if (isWorkspaceView(view)) {
|
||||||
setWorkspaceExpanded(true);
|
setWorkspaceExpanded(true);
|
||||||
}
|
}
|
||||||
}, [setView, setWorkspaceExpanded]);
|
}, [session, setView, setWorkspaceExpanded]);
|
||||||
|
|
||||||
const clearAuthenticatedState = useCallback((options?: { resetView?: boolean }) => {
|
const clearAuthenticatedState = useCallback((options?: { resetView?: boolean }) => {
|
||||||
clearAllUserStorage();
|
clearAllUserStorage();
|
||||||
@@ -1313,11 +1317,13 @@ function App() {
|
|||||||
case "workbench":
|
case "workbench":
|
||||||
return (
|
return (
|
||||||
<WorkbenchPage
|
<WorkbenchPage
|
||||||
|
key={`workbench-${workbenchResetToken}`}
|
||||||
isAuthenticated={Boolean(session)}
|
isAuthenticated={Boolean(session)}
|
||||||
session={session}
|
session={session}
|
||||||
onRequireLogin={handleRequireTaskLogin}
|
onRequireLogin={handleRequireTaskLogin}
|
||||||
onOpenResultInCanvas={handleOpenResultInCanvas}
|
onOpenResultInCanvas={handleOpenResultInCanvas}
|
||||||
onRefreshUsage={refreshUsage}
|
onRefreshUsage={refreshUsage}
|
||||||
|
resetToken={workbenchResetToken}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "home":
|
case "home":
|
||||||
|
|||||||
@@ -200,6 +200,7 @@ interface WorkbenchPageProps {
|
|||||||
onRequireLogin: (input: CreatePreviewTaskInput) => void;
|
onRequireLogin: (input: CreatePreviewTaskInput) => void;
|
||||||
onOpenResultInCanvas?: (payload: import("./workbenchConstants").WorkbenchResultActionPayload) => void;
|
onOpenResultInCanvas?: (payload: import("./workbenchConstants").WorkbenchResultActionPayload) => void;
|
||||||
onRefreshUsage?: () => void;
|
onRefreshUsage?: () => void;
|
||||||
|
resetToken?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Component ───────────────────────────────────────────────────────────
|
// ─── Component ───────────────────────────────────────────────────────────
|
||||||
@@ -231,6 +232,7 @@ function WorkbenchPage({
|
|||||||
onRequireLogin,
|
onRequireLogin,
|
||||||
onOpenResultInCanvas,
|
onOpenResultInCanvas,
|
||||||
onRefreshUsage,
|
onRefreshUsage,
|
||||||
|
resetToken,
|
||||||
}: WorkbenchPageProps) {
|
}: WorkbenchPageProps) {
|
||||||
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
||||||
const referenceInputRef = useRef<HTMLInputElement | null>(null);
|
const referenceInputRef = useRef<HTMLInputElement | null>(null);
|
||||||
@@ -249,10 +251,11 @@ function WorkbenchPage({
|
|||||||
const activeConversationIdRef = useRef<number | null>(null);
|
const activeConversationIdRef = useRef<number | null>(null);
|
||||||
const messagesRef = useRef<ChatMessage[]>([]);
|
const messagesRef = useRef<ChatMessage[]>([]);
|
||||||
const conversationMessagesCacheRef = useRef<Map<number, ChatMessage[]>>(new Map());
|
const conversationMessagesCacheRef = useRef<Map<number, ChatMessage[]>>(new Map());
|
||||||
const skipConversationAutoSelectRef = useRef(false);
|
const skipConversationAutoSelectRef = useRef(Boolean(resetToken));
|
||||||
const keepaliveTasksRef = useRef<Record<string, WorkbenchKeepaliveTask>>(readStoredKeepaliveTasks());
|
const keepaliveTasksRef = useRef<Record<string, WorkbenchKeepaliveTask>>(readStoredKeepaliveTasks());
|
||||||
const taskAbortControllersRef = useRef<Map<string, AbortController>>(new Map());
|
const taskAbortControllersRef = useRef<Map<string, AbortController>>(new Map());
|
||||||
const lastScrollTopRef = useRef(0);
|
const lastScrollTopRef = useRef(0);
|
||||||
|
const scrollActionHintTimerRef = useRef<number | null>(null);
|
||||||
const shouldFollowNewMessagesRef = useRef(true);
|
const shouldFollowNewMessagesRef = useRef(true);
|
||||||
const pendingScrollToLatestRef = useRef(true);
|
const pendingScrollToLatestRef = useRef(true);
|
||||||
const genTracker = useGenerationTasks({ sourceView: "workbench" });
|
const genTracker = useGenerationTasks({ sourceView: "workbench" });
|
||||||
@@ -261,7 +264,7 @@ function WorkbenchPage({
|
|||||||
|
|
||||||
const [activeMode, setActiveMode] = useState<WorkbenchMode>("video");
|
const [activeMode, setActiveMode] = useState<WorkbenchMode>("video");
|
||||||
const [inputValue, setInputValue] = useState("");
|
const [inputValue, setInputValue] = useState("");
|
||||||
const [messages, setMessages] = useState<ChatMessage[]>(() => readStoredMessages());
|
const [messages, setMessages] = useState<ChatMessage[]>(() => (resetToken ? [] : readStoredMessages()));
|
||||||
const [promptHistory, setPromptHistory] = useState<string[]>(() => readStoredPromptHistory());
|
const [promptHistory, setPromptHistory] = useState<string[]>(() => readStoredPromptHistory());
|
||||||
const [toolbarMenuId, setToolbarMenuId] = useState<ToolbarMenuId>(null);
|
const [toolbarMenuId, setToolbarMenuId] = useState<ToolbarMenuId>(null);
|
||||||
const [referenceItems, setReferenceItems] = useState<ReferenceItem[]>([]);
|
const [referenceItems, setReferenceItems] = useState<ReferenceItem[]>([]);
|
||||||
@@ -284,7 +287,7 @@ function WorkbenchPage({
|
|||||||
const [projectError, setProjectError] = useState<string | null>(null);
|
const [projectError, setProjectError] = useState<string | null>(null);
|
||||||
const [conversations, setConversations] = useState<ConversationSummary[]>([]);
|
const [conversations, setConversations] = useState<ConversationSummary[]>([]);
|
||||||
const [activeConversationId, setActiveConversationId] = useState<number | null>(() =>
|
const [activeConversationId, setActiveConversationId] = useState<number | null>(() =>
|
||||||
readStoredActiveConversationId(readStoredMessages()),
|
resetToken ? null : readStoredActiveConversationId(readStoredMessages()),
|
||||||
);
|
);
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
|
||||||
const [deleteDialog, setDeleteDialog] = useState<DeleteDialogState | null>(null);
|
const [deleteDialog, setDeleteDialog] = useState<DeleteDialogState | null>(null);
|
||||||
@@ -294,7 +297,9 @@ function WorkbenchPage({
|
|||||||
const [promptSelectionRange, setPromptSelectionRange] = useState({ start: 0, end: 0 });
|
const [promptSelectionRange, setPromptSelectionRange] = useState({ start: 0, end: 0 });
|
||||||
const [mentionActiveIndex, setMentionActiveIndex] = useState(0);
|
const [mentionActiveIndex, setMentionActiveIndex] = useState(0);
|
||||||
const [composerHidden, setComposerHidden] = useState(false);
|
const [composerHidden, setComposerHidden] = useState(false);
|
||||||
|
const [scrollActionHint, setScrollActionHint] = useState<"top" | "bottom" | null>(null);
|
||||||
const [workspaceStarted, setWorkspaceStarted] = useState(false);
|
const [workspaceStarted, setWorkspaceStarted] = useState(false);
|
||||||
|
const lastResetTokenRef = useRef(resetToken);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
activeConversationIdRef.current = activeConversationId;
|
activeConversationIdRef.current = activeConversationId;
|
||||||
@@ -420,7 +425,6 @@ function WorkbenchPage({
|
|||||||
const toolTheme = MODE_META[activeMode];
|
const toolTheme = MODE_META[activeMode];
|
||||||
const workbenchAccent = "#00ff88";
|
const workbenchAccent = "#00ff88";
|
||||||
const hasConversationRecords = activeConversationId !== null || messages.length > 0;
|
const hasConversationRecords = activeConversationId !== null || messages.length > 0;
|
||||||
const hasActivatedWorkspace = workspaceStarted || isGenerating || hasConversationRecords;
|
|
||||||
const referenceCount = referenceItems.length;
|
const referenceCount = referenceItems.length;
|
||||||
const activeVideoModelValue = toHappyHorseDisplayModel(videoModel);
|
const activeVideoModelValue = toHappyHorseDisplayModel(videoModel);
|
||||||
const activeModelValue =
|
const activeModelValue =
|
||||||
@@ -448,6 +452,7 @@ function WorkbenchPage({
|
|||||||
[conversations],
|
[conversations],
|
||||||
);
|
);
|
||||||
const hasSidebarRecords = conversationRecords.length > 0;
|
const hasSidebarRecords = conversationRecords.length > 0;
|
||||||
|
const hasActivatedWorkspace = workspaceStarted || isGenerating || hasConversationRecords;
|
||||||
|
|
||||||
const activeConversationTitle = useMemo(() => {
|
const activeConversationTitle = useMemo(() => {
|
||||||
if (!activeConversationId) return "";
|
if (!activeConversationId) return "";
|
||||||
@@ -543,6 +548,31 @@ function WorkbenchPage({
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const hideScrollActionHint = useCallback(() => {
|
||||||
|
if (scrollActionHintTimerRef.current !== null) {
|
||||||
|
window.clearTimeout(scrollActionHintTimerRef.current);
|
||||||
|
scrollActionHintTimerRef.current = null;
|
||||||
|
}
|
||||||
|
setScrollActionHint(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const showScrollActionHint = useCallback((direction: "top" | "bottom") => {
|
||||||
|
if (scrollActionHintTimerRef.current !== null) {
|
||||||
|
window.clearTimeout(scrollActionHintTimerRef.current);
|
||||||
|
}
|
||||||
|
setScrollActionHint(direction);
|
||||||
|
scrollActionHintTimerRef.current = window.setTimeout(() => {
|
||||||
|
setScrollActionHint(null);
|
||||||
|
scrollActionHintTimerRef.current = null;
|
||||||
|
}, 1400);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => () => {
|
||||||
|
if (scrollActionHintTimerRef.current !== null) {
|
||||||
|
window.clearTimeout(scrollActionHintTimerRef.current);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
const imageSettingGroups = useMemo<WorkbenchFieldGroup[]>(
|
const imageSettingGroups = useMemo<WorkbenchFieldGroup[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@@ -1313,6 +1343,12 @@ function WorkbenchPage({
|
|||||||
activeConversationIdRef.current = null;
|
activeConversationIdRef.current = null;
|
||||||
}, [syncActiveGenerationUi]);
|
}, [syncActiveGenerationUi]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (resetToken === undefined || lastResetTokenRef.current === resetToken) return;
|
||||||
|
lastResetTokenRef.current = resetToken;
|
||||||
|
handleNewConversation();
|
||||||
|
}, [handleNewConversation, resetToken]);
|
||||||
|
|
||||||
const handleSelectProject = useCallback((id: string) => {
|
const handleSelectProject = useCallback((id: string) => {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
handleNewConversation();
|
handleNewConversation();
|
||||||
@@ -1467,6 +1503,7 @@ function WorkbenchPage({
|
|||||||
const atBottom = top + surface.clientHeight >= surface.scrollHeight - edgeThreshold;
|
const atBottom = top + surface.clientHeight >= surface.scrollHeight - edgeThreshold;
|
||||||
shouldFollowNewMessagesRef.current = atBottom;
|
shouldFollowNewMessagesRef.current = atBottom;
|
||||||
setComposerHidden(!(atTop || atBottom));
|
setComposerHidden(!(atTop || atBottom));
|
||||||
|
hideScrollActionHint();
|
||||||
lastScrollTopRef.current = top;
|
lastScrollTopRef.current = top;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1481,24 +1518,27 @@ function WorkbenchPage({
|
|||||||
shouldFollowNewMessagesRef.current = atBottom;
|
shouldFollowNewMessagesRef.current = atBottom;
|
||||||
if (atTop || atBottom) {
|
if (atTop || atBottom) {
|
||||||
setComposerHidden(false);
|
setComposerHidden(false);
|
||||||
|
hideScrollActionHint();
|
||||||
} else if (Math.abs(delta) > scrollDeltaThreshold) {
|
} else if (Math.abs(delta) > scrollDeltaThreshold) {
|
||||||
setComposerHidden(true);
|
setComposerHidden(true);
|
||||||
|
showScrollActionHint(delta < 0 ? "top" : "bottom");
|
||||||
}
|
}
|
||||||
lastScrollTopRef.current = top;
|
lastScrollTopRef.current = top;
|
||||||
};
|
};
|
||||||
|
|
||||||
surface.addEventListener("scroll", handleScroll, { passive: true });
|
surface.addEventListener("scroll", handleScroll, { passive: true });
|
||||||
return () => surface.removeEventListener("scroll", handleScroll);
|
return () => surface.removeEventListener("scroll", handleScroll);
|
||||||
}, [hasActivatedWorkspace]);
|
}, [hasActivatedWorkspace, hideScrollActionHint, showScrollActionHint]);
|
||||||
|
|
||||||
const scrollMessagesSurface = useCallback((direction: "top" | "bottom") => {
|
const scrollMessagesSurface = useCallback((direction: "top" | "bottom") => {
|
||||||
const surface = messagesSurfaceRef.current;
|
const surface = messagesSurfaceRef.current;
|
||||||
if (!surface) return;
|
if (!surface) return;
|
||||||
|
|
||||||
const top = direction === "top" ? 0 : surface.scrollHeight;
|
const top = direction === "top" ? 0 : surface.scrollHeight;
|
||||||
|
hideScrollActionHint();
|
||||||
setComposerHidden(false);
|
setComposerHidden(false);
|
||||||
surface.scrollTo({ top, behavior: "smooth" });
|
surface.scrollTo({ top, behavior: "smooth" });
|
||||||
}, []);
|
}, [hideScrollActionHint]);
|
||||||
|
|
||||||
const closeToolbarMenus = () => setToolbarMenuId(null);
|
const closeToolbarMenus = () => setToolbarMenuId(null);
|
||||||
const toggleToolbarMenu = (menuId: Exclude<ToolbarMenuId, null>) => {
|
const toggleToolbarMenu = (menuId: Exclude<ToolbarMenuId, null>) => {
|
||||||
@@ -2993,9 +3033,29 @@ function WorkbenchPage({
|
|||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const renderPromptCaseOverlay = () =>
|
const renderPromptCaseOverlay = () => {
|
||||||
selectedPromptCase ? (
|
if (!selectedPromptCase) return null;
|
||||||
<div className="wb-prompt-case-modal" role="dialog" aria-modal="true" aria-labelledby="wb-prompt-case-title">
|
|
||||||
|
const measuredRatio = promptCaseMeasuredRatios[selectedPromptCase.id];
|
||||||
|
const ratioParts = selectedPromptCase.ratio.replace(/\s+/g, "").split(":").map(Number);
|
||||||
|
const declaredRatio =
|
||||||
|
ratioParts.length === 2 && ratioParts[0] > 0 && ratioParts[1] > 0
|
||||||
|
? ratioParts[0] / ratioParts[1]
|
||||||
|
: null;
|
||||||
|
const caseRatio =
|
||||||
|
typeof measuredRatio === "number" && Number.isFinite(measuredRatio) && measuredRatio > 0
|
||||||
|
? measuredRatio
|
||||||
|
: declaredRatio;
|
||||||
|
const copyLength = `${selectedPromptCase.summary} ${selectedPromptCase.prompt}`.length;
|
||||||
|
const modalClassName = [
|
||||||
|
"wb-prompt-case-modal",
|
||||||
|
caseRatio && caseRatio < 0.72 ? "is-tall-media" : "",
|
||||||
|
caseRatio && caseRatio >= 0.72 && caseRatio < 1 ? "is-portrait-media" : "",
|
||||||
|
copyLength > 260 ? "is-long-copy" : "",
|
||||||
|
].filter(Boolean).join(" ");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={modalClassName} role="dialog" aria-modal="true" aria-labelledby="wb-prompt-case-title">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="wb-prompt-case-modal__backdrop"
|
className="wb-prompt-case-modal__backdrop"
|
||||||
@@ -3004,7 +3064,11 @@ function WorkbenchPage({
|
|||||||
/>
|
/>
|
||||||
<section className="wb-prompt-case-modal__panel">
|
<section className="wb-prompt-case-modal__panel">
|
||||||
<div className="wb-prompt-case-modal__media">
|
<div className="wb-prompt-case-modal__media">
|
||||||
<img src={selectedPromptCase.imageUrl} alt={selectedPromptCase.title} />
|
<img
|
||||||
|
src={selectedPromptCase.imageUrl}
|
||||||
|
alt={selectedPromptCase.title}
|
||||||
|
onLoad={(event) => handlePromptCaseImageLoad(selectedPromptCase.id, event)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<aside className="wb-prompt-case-modal__sidebar">
|
<aside className="wb-prompt-case-modal__sidebar">
|
||||||
<button
|
<button
|
||||||
@@ -3044,7 +3108,8 @@ function WorkbenchPage({
|
|||||||
</aside>
|
</aside>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if (!hasActivatedWorkspace) {
|
if (!hasActivatedWorkspace) {
|
||||||
return (
|
return (
|
||||||
@@ -3139,8 +3204,8 @@ function WorkbenchPage({
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{renderConversationSidebar()}
|
||||||
</div>
|
</div>
|
||||||
{renderConversationSidebar()}
|
|
||||||
{renderMessagePreviewOverlay()}
|
{renderMessagePreviewOverlay()}
|
||||||
{renderPromptCaseOverlay()}
|
{renderPromptCaseOverlay()}
|
||||||
{renderDeleteDialog()}
|
{renderDeleteDialog()}
|
||||||
@@ -3282,10 +3347,10 @@ function WorkbenchPage({
|
|||||||
{renderComposerToolbar(false, isGenerating)}
|
{renderComposerToolbar(false, isGenerating)}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<div className="wb-chat-scroll-actions" aria-label="聊天滚动">
|
<div className={`wb-chat-scroll-actions${scrollActionHint ? ` is-showing-${scrollActionHint}` : ""}`} aria-label="聊天滚动">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="wb-chat-scroll-actions__button"
|
className="wb-chat-scroll-actions__button wb-chat-scroll-actions__button--top"
|
||||||
title="返回聊天顶部"
|
title="返回聊天顶部"
|
||||||
aria-label="返回聊天顶部"
|
aria-label="返回聊天顶部"
|
||||||
onClick={() => scrollMessagesSurface("top")}
|
onClick={() => scrollMessagesSurface("top")}
|
||||||
@@ -3294,7 +3359,7 @@ function WorkbenchPage({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="wb-chat-scroll-actions__button"
|
className="wb-chat-scroll-actions__button wb-chat-scroll-actions__button--bottom"
|
||||||
title="到达聊天底部"
|
title="到达聊天底部"
|
||||||
aria-label="到达聊天底部"
|
aria-label="到达聊天底部"
|
||||||
onClick={() => scrollMessagesSurface("bottom")}
|
onClick={() => scrollMessagesSurface("bottom")}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -10845,8 +10845,44 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
|
.web-shell[data-ui-theme="dark-green"] {
|
||||||
|
--dg-mobile-nav-height: 58px;
|
||||||
|
--dg-mobile-nav-gap: 12px;
|
||||||
|
--dg-mobile-nav-space: calc(var(--dg-mobile-nav-height) + var(--dg-mobile-nav-gap));
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-shell[data-ui-theme="dark-green"] .web-topbar {
|
||||||
|
z-index: 72;
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-shell[data-ui-theme="dark-green"] .web-shell__content,
|
||||||
|
.web-shell[data-ui-theme="dark-green"] .web-shell__page {
|
||||||
|
min-height: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-shell[data-ui-theme="dark-green"] .web-shell__page {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-shell[data-ui-theme="dark-green"]:not([data-view="home"]):not([data-view="login"]):not([data-view="workbench"]):not([data-view="agent"]):not([data-view="avatarConsole"]) .web-shell__page {
|
||||||
|
padding-top: var(--dg-mobile-nav-space);
|
||||||
|
}
|
||||||
|
|
||||||
|
.web-shell[data-ui-theme="dark-green"] .profile-popover {
|
||||||
|
position: fixed;
|
||||||
|
top: calc(56px + var(--dg-mobile-nav-space) + env(safe-area-inset-top, 0px));
|
||||||
|
right: 12px;
|
||||||
|
z-index: 120;
|
||||||
|
width: min(288px, calc(100vw - 24px));
|
||||||
|
max-height: calc(100svh - 56px - var(--dg-mobile-nav-space) - 24px);
|
||||||
|
overflow-y: auto;
|
||||||
|
transform-origin: top right;
|
||||||
|
}
|
||||||
|
|
||||||
.web-shell[data-ui-theme="dark-green"] .floating-nav {
|
.web-shell[data-ui-theme="dark-green"] .floating-nav {
|
||||||
top: calc(56px + env(safe-area-inset-top, 0px));
|
top: calc(56px + env(safe-area-inset-top, 0px));
|
||||||
|
z-index: 50;
|
||||||
right: 12px;
|
right: 12px;
|
||||||
bottom: auto;
|
bottom: auto;
|
||||||
left: 12px;
|
left: 12px;
|
||||||
|
|||||||
Reference in New Issue
Block a user