fix(web): make right sidebar header toggle work across mobile, projects, and groups
This commit is contained in:
parent
35b7291660
commit
996af168ef
@ -662,7 +662,7 @@ export function ShellPage() {
|
|||||||
);
|
);
|
||||||
const inProject = Boolean(activeSpace && !activeSpace.isDefault);
|
const inProject = Boolean(activeSpace && !activeSpace.isDefault);
|
||||||
const coordinatorBot: Bot | undefined = useMemo(() => {
|
const coordinatorBot: Bot | undefined = useMemo(() => {
|
||||||
if (!inProject || !activeSpace) return undefined;
|
if ((!inProject && !inGroup) || !activeSpace) return undefined;
|
||||||
const leadId =
|
const leadId =
|
||||||
snapshot?.leadBotId ??
|
snapshot?.leadBotId ??
|
||||||
activeGroup?.leadBotId ??
|
activeGroup?.leadBotId ??
|
||||||
@ -673,10 +673,12 @@ export function ShellPage() {
|
|||||||
bots.find((b) => b.kind === "main" && b.spaceId === activeSpace.id) ??
|
bots.find((b) => b.kind === "main" && b.spaceId === activeSpace.id) ??
|
||||||
bots.find((b) => b.spaceId === activeSpace.id);
|
bots.find((b) => b.spaceId === activeSpace.id);
|
||||||
if (!found) return undefined;
|
if (!found) return undefined;
|
||||||
const isLegacyName = found.name === "Chief" || found.name === "Projektkoordinator";
|
const isLegacyName =
|
||||||
|
inProject && (found.name === "Chief" || found.name === "Projektkoordinator");
|
||||||
return isLegacyName ? { ...found, name: "Projektmanager" } : found;
|
return isLegacyName ? { ...found, name: "Projektmanager" } : found;
|
||||||
}, [
|
}, [
|
||||||
inProject,
|
inProject,
|
||||||
|
inGroup,
|
||||||
activeSpace,
|
activeSpace,
|
||||||
snapshot?.leadBotId,
|
snapshot?.leadBotId,
|
||||||
activeGroup?.leadBotId,
|
activeGroup?.leadBotId,
|
||||||
@ -786,6 +788,8 @@ export function ShellPage() {
|
|||||||
routeGroupId.current = groupId;
|
routeGroupId.current = groupId;
|
||||||
const activeBotId = useRef<string | undefined>(inGroup ? undefined : active?.id);
|
const activeBotId = useRef<string | undefined>(inGroup ? undefined : active?.id);
|
||||||
activeBotId.current = inGroup ? undefined : active?.id;
|
activeBotId.current = inGroup ? undefined : active?.id;
|
||||||
|
const coordinatorBotIdRef = useRef<string | undefined>(coordinatorBot?.id);
|
||||||
|
coordinatorBotIdRef.current = coordinatorBot?.id;
|
||||||
const activeGroupId = useRef<string | undefined>(groupId);
|
const activeGroupId = useRef<string | undefined>(groupId);
|
||||||
activeGroupId.current = groupId;
|
activeGroupId.current = groupId;
|
||||||
const screenRequest = useRef(0);
|
const screenRequest = useRef(0);
|
||||||
@ -1053,7 +1057,9 @@ export function ShellPage() {
|
|||||||
return loadComputerScreen({
|
return loadComputerScreen({
|
||||||
load: () => rpc.computer.screenUrl({ botId: id }),
|
load: () => rpc.computer.screenUrl({ botId: id }),
|
||||||
isCurrent: () =>
|
isCurrent: () =>
|
||||||
request === screenRequest.current && activeBotId.current === id && computerVisible.current,
|
request === screenRequest.current &&
|
||||||
|
(activeBotId.current === id || coordinatorBotIdRef.current === id) &&
|
||||||
|
computerVisible.current,
|
||||||
commit: (screen) => {
|
commit: (screen) => {
|
||||||
setScreenUrl(screen.url);
|
setScreenUrl(screen.url);
|
||||||
setComputerError(screen.error);
|
setComputerError(screen.error);
|
||||||
@ -1480,17 +1486,21 @@ export function ShellPage() {
|
|||||||
void refreshThread(active.id).catch(() => undefined);
|
void refreshThread(active.id).catch(() => undefined);
|
||||||
} else if (isComputerStatusEvent(event)) {
|
} else if (isComputerStatusEvent(event)) {
|
||||||
void refreshComputerScreen(active.id).catch(() => undefined);
|
void refreshComputerScreen(active.id).catch(() => undefined);
|
||||||
} else if (event.type === "thread.computer") {
|
}
|
||||||
|
if (event.type === "thread.computer") {
|
||||||
agentPointerFeed.publish(event.botId, event.payload, event.createdAt);
|
agentPointerFeed.publish(event.botId, event.payload, event.createdAt);
|
||||||
const screenRun = liveScreenRunId(event);
|
}
|
||||||
if (screenRun) {
|
const screenRun = liveScreenRunId(event);
|
||||||
setScreenRunId(screenRun);
|
if (screenRun) {
|
||||||
setScreenActive(true);
|
setScreenRunId(screenRun);
|
||||||
window.clearTimeout(screenActiveTimer.current);
|
setScreenActive(true);
|
||||||
screenActiveTimer.current = window.setTimeout(
|
window.clearTimeout(screenActiveTimer.current);
|
||||||
() => setScreenActive(false),
|
screenActiveTimer.current = window.setTimeout(
|
||||||
SCREEN_ACTIVE_MS,
|
() => setScreenActive(false),
|
||||||
);
|
SCREEN_ACTIVE_MS,
|
||||||
|
);
|
||||||
|
if (computerRef.current?.state !== "running") {
|
||||||
|
void refreshComputerScreen(active.id).catch(() => undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1598,6 +1608,9 @@ export function ShellPage() {
|
|||||||
// waiting_input: reconcile ask cards if a stale post-send refresh raced SSE.
|
// waiting_input: reconcile ask cards if a stale post-send refresh raced SSE.
|
||||||
void refreshGroupThread(groupId).catch(() => undefined);
|
void refreshGroupThread(groupId).catch(() => undefined);
|
||||||
}
|
}
|
||||||
|
if (event.type === "thread.computer") {
|
||||||
|
agentPointerFeed.publish(event.botId, event.payload, event.createdAt);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return () => {
|
return () => {
|
||||||
@ -1910,10 +1923,11 @@ export function ShellPage() {
|
|||||||
const handleRunErrorPresented = useCallback((runId: string) => {
|
const handleRunErrorPresented = useCallback((runId: string) => {
|
||||||
rememberSeenRunErrorId(runId);
|
rememberSeenRunErrorId(runId);
|
||||||
}, []);
|
}, []);
|
||||||
const groupComputerBotId =
|
const groupComputerBotId = inGroup
|
||||||
inGroup && activeSnapshot?.leadBotId && bots.some((bot) => bot.id === activeSnapshot.leadBotId)
|
? (activeSnapshot?.leadBotId && bots.some((bot) => bot.id === activeSnapshot.leadBotId)
|
||||||
? activeSnapshot.leadBotId
|
? activeSnapshot.leadBotId
|
||||||
: undefined;
|
: coordinatorBot?.id)
|
||||||
|
: undefined;
|
||||||
// A bot message without a sender speaks for the conversation's own bot, never as an anonymous one.
|
// A bot message without a sender speaks for the conversation's own bot, never as an anonymous one.
|
||||||
const defaultSpeakerId = inGroup ? activeSnapshot?.leadBotId : active?.id;
|
const defaultSpeakerId = inGroup ? activeSnapshot?.leadBotId : active?.id;
|
||||||
const transcriptMessages = useMemo(() => {
|
const transcriptMessages = useMemo(() => {
|
||||||
@ -2031,6 +2045,7 @@ export function ShellPage() {
|
|||||||
const workspaceFocusRef = useRef<{ threadId: string; focus: WorkspaceFocus } | null>(null);
|
const workspaceFocusRef = useRef<{ threadId: string; focus: WorkspaceFocus } | null>(null);
|
||||||
const autoPanelRef = useRef(autoPanel);
|
const autoPanelRef = useRef(autoPanel);
|
||||||
autoPanelRef.current = autoPanel;
|
autoPanelRef.current = autoPanel;
|
||||||
|
const autoLayoutSplitRef = useRef(false);
|
||||||
const autoCloseTimer = useRef<number | undefined>(undefined);
|
const autoCloseTimer = useRef<number | undefined>(undefined);
|
||||||
useEffect(() => () => window.clearTimeout(autoCloseTimer.current), []);
|
useEffect(() => () => window.clearTimeout(autoCloseTimer.current), []);
|
||||||
// The pane follows the bot's work on focus edges only, so it never jumps while nothing changed.
|
// The pane follows the bot's work on focus edges only, so it never jumps while nothing changed.
|
||||||
@ -2049,7 +2064,7 @@ export function ShellPage() {
|
|||||||
if (!wideLayout) return;
|
if (!wideLayout) return;
|
||||||
const paneState: PaneState = {
|
const paneState: PaneState = {
|
||||||
open:
|
open:
|
||||||
panel === "computer"
|
panel === "computer" || panel === "browser"
|
||||||
? "screen"
|
? "screen"
|
||||||
: panel === "artifact"
|
: panel === "artifact"
|
||||||
? "artifact"
|
? "artifact"
|
||||||
@ -2066,13 +2081,22 @@ export function ShellPage() {
|
|||||||
if (closing === null || autoPanelRef.current !== closing) return;
|
if (closing === null || autoPanelRef.current !== closing) return;
|
||||||
setPanel(null);
|
setPanel(null);
|
||||||
setAutoPanel(null);
|
setAutoPanel(null);
|
||||||
|
if (autoLayoutSplitRef.current) {
|
||||||
|
autoLayoutSplitRef.current = false;
|
||||||
|
dualSurface.setLayoutMode("stream");
|
||||||
|
}
|
||||||
}, AUTO_SCREEN_CLOSE_DELAY_MS);
|
}, AUTO_SCREEN_CLOSE_DELAY_MS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (next.open === "artifact") setArtifactPane(workspaceFocus.artifact);
|
if (next.open === "artifact") setArtifactPane(workspaceFocus.artifact);
|
||||||
|
if (next.open === "screen") setCanvasTab("browser");
|
||||||
const nextPanel: Panel = next.open === "screen" ? "computer" : next.open;
|
const nextPanel: Panel = next.open === "screen" ? "computer" : next.open;
|
||||||
setPanel(nextPanel);
|
setPanel(nextPanel);
|
||||||
setAutoPanel(next.auto ? nextPanel : null);
|
setAutoPanel(next.auto ? nextPanel : null);
|
||||||
|
if (dualSurface.layoutMode === "stream") {
|
||||||
|
autoLayoutSplitRef.current = Boolean(next.auto);
|
||||||
|
dualSurface.setLayoutMode("split");
|
||||||
|
}
|
||||||
}, [workspaceFocus.key, activeSnapshot?.threadId, wideLayout]);
|
}, [workspaceFocus.key, activeSnapshot?.threadId, wideLayout]);
|
||||||
const learnScreenPane = useCallback(
|
const learnScreenPane = useCallback(
|
||||||
(choice: "opened" | "dismissed") => {
|
(choice: "opened" | "dismissed") => {
|
||||||
@ -2091,6 +2115,7 @@ export function ShellPage() {
|
|||||||
if (autoPanel === "computer" && panel === null && focus?.pane === "screen" && !focus.takeover) {
|
if (autoPanel === "computer" && panel === null && focus?.pane === "screen" && !focus.takeover) {
|
||||||
learnScreenPane("dismissed");
|
learnScreenPane("dismissed");
|
||||||
}
|
}
|
||||||
|
autoLayoutSplitRef.current = false;
|
||||||
setAutoPanel(null);
|
setAutoPanel(null);
|
||||||
}, [panel]);
|
}, [panel]);
|
||||||
const openArtifactBeside = useCallback((artifact: WorkspaceArtifact) => {
|
const openArtifactBeside = useCallback((artifact: WorkspaceArtifact) => {
|
||||||
@ -3886,6 +3911,7 @@ export function ShellPage() {
|
|||||||
panel &&
|
panel &&
|
||||||
(active ||
|
(active ||
|
||||||
activeGroup ||
|
activeGroup ||
|
||||||
|
coordinatorBot ||
|
||||||
panel === "create" ||
|
panel === "create" ||
|
||||||
panel === "create-group" ||
|
panel === "create-group" ||
|
||||||
panel === "browser")
|
panel === "browser")
|
||||||
@ -3898,6 +3924,7 @@ export function ShellPage() {
|
|||||||
panel &&
|
panel &&
|
||||||
(active ||
|
(active ||
|
||||||
activeGroup ||
|
activeGroup ||
|
||||||
|
coordinatorBot ||
|
||||||
panel === "create" ||
|
panel === "create" ||
|
||||||
panel === "create-group" ||
|
panel === "create-group" ||
|
||||||
panel === "browser"),
|
panel === "browser"),
|
||||||
@ -4116,56 +4143,46 @@ export function ShellPage() {
|
|||||||
layoutMode={showCanvasOnDesktop ? dualSurface.layoutMode : "stream"}
|
layoutMode={showCanvasOnDesktop ? dualSurface.layoutMode : "stream"}
|
||||||
orientation={dualSurface.orientation}
|
orientation={dualSurface.orientation}
|
||||||
onSelectMode={handleSelectLayoutMode}
|
onSelectMode={handleSelectLayoutMode}
|
||||||
onToggleOrientation={dualSurface.toggleOrientation}
|
onToggleOrientation={() => {
|
||||||
|
dualSurface.toggleOrientation();
|
||||||
|
if (!showCanvasOnDesktop) {
|
||||||
|
handleSelectLayoutMode("split");
|
||||||
|
}
|
||||||
|
}}
|
||||||
disabled={dualSurface.isMobile}
|
disabled={dualSurface.isMobile}
|
||||||
className="hidden md:flex"
|
className="hidden md:flex"
|
||||||
/>
|
/>
|
||||||
{active || groupComputerBotId ? (
|
{active || activeGroup || inProject || groupComputerBotId ? (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
title={t`Agent computer`}
|
title={t`Agent computer`}
|
||||||
aria-label={t`Agent computer`}
|
aria-label={t`Agent computer`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (inProject && !active) {
|
if (isPanelVisible) {
|
||||||
if (showCanvasOnDesktop) {
|
|
||||||
setPanel(null);
|
|
||||||
dualSurface.setLayoutMode("stream");
|
|
||||||
} else {
|
|
||||||
dualSurface.setLayoutMode("split");
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// The computer belongs to a bot; from a group, open its lead's computer.
|
|
||||||
if (!active && groupComputerBotId) {
|
|
||||||
navigate(`/app/${groupComputerBotId}`);
|
|
||||||
setCanvasTab("browser");
|
|
||||||
setPanel("computer");
|
|
||||||
dualSurface.setLayoutMode("split");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isCurrentlyOpen =
|
|
||||||
(panel === "computer" || panel === "browser") &&
|
|
||||||
dualSurface.layoutMode !== "stream";
|
|
||||||
if (isCurrentlyOpen) {
|
|
||||||
setPanel(null);
|
setPanel(null);
|
||||||
dualSurface.setLayoutMode("stream");
|
dualSurface.setLayoutMode("stream");
|
||||||
} else {
|
return;
|
||||||
if (transcriptRunning) learnScreenPane("opened");
|
}
|
||||||
setCanvasTab("browser");
|
if (inProject && !active) {
|
||||||
setPanel("computer");
|
if (dualSurface.isMobile) {
|
||||||
dualSurface.setLayoutMode("split");
|
setCanvasTab("overview");
|
||||||
if (active) {
|
setPanel("computer");
|
||||||
// Refresh run/computer so Take control isn't stuck on a stale busyBotName.
|
|
||||||
void refreshThread(active.id).catch(() => undefined);
|
|
||||||
}
|
}
|
||||||
|
dualSurface.setLayoutMode("split");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (transcriptRunning) learnScreenPane("opened");
|
||||||
|
setCanvasTab(inProject ? "overview" : "browser");
|
||||||
|
setPanel("computer");
|
||||||
|
dualSurface.setLayoutMode("split");
|
||||||
|
if (active) {
|
||||||
|
// Refresh run/computer so Take control isn't stuck on a stale busyBotName.
|
||||||
|
void refreshThread(active.id).catch(() => undefined);
|
||||||
|
} else if (groupComputerBotId) {
|
||||||
|
void refreshComputerScreen(groupComputerBotId).catch(() => undefined);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
data-active={
|
data-active={isPanelVisible ? "" : undefined}
|
||||||
(panel === "computer" || panel === "browser" || (inProject && !active)) &&
|
|
||||||
showCanvasOnDesktop
|
|
||||||
? ""
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
className="app-no-drag rk-touch-target grid size-control-sm min-h-control-sm min-w-control-sm place-items-center rounded-lg hover:bg-accent data-active:bg-accent"
|
className="app-no-drag rk-touch-target grid size-control-sm min-h-control-sm min-w-control-sm place-items-center rounded-lg hover:bg-accent data-active:bg-accent"
|
||||||
>
|
>
|
||||||
<PanelRight strokeWidth={1.7} className="text-foreground/75 size-icon-sm" />
|
<PanelRight strokeWidth={1.7} className="text-foreground/75 size-icon-sm" />
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user