mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
feat(app): import-agent pill above draft composer (#833)
Surfaces the existing Import-agent sheet directly from the new-agent draft composer, so users don't have to dig into the workspace header menu. The pill renders only on draft tabs (gated by the prop being provided) and reuses the existing WorkspaceImportSheet.
This commit is contained in:
@@ -18,8 +18,15 @@ function useDraftPanelDescriptor() {
|
||||
}
|
||||
|
||||
function DraftPanel() {
|
||||
const { serverId, workspaceId, tabId, target, openFileInWorkspace, retargetCurrentTab } =
|
||||
usePaneContext();
|
||||
const {
|
||||
serverId,
|
||||
workspaceId,
|
||||
tabId,
|
||||
target,
|
||||
openFileInWorkspace,
|
||||
openImportSheet,
|
||||
retargetCurrentTab,
|
||||
} = usePaneContext();
|
||||
const { isInteractive } = usePaneFocus();
|
||||
invariant(target.kind === "draft", "DraftPanel requires draft target");
|
||||
|
||||
@@ -52,6 +59,7 @@ function DraftPanel() {
|
||||
isPaneFocused={isInteractive}
|
||||
onOpenWorkspaceFile={handleOpenWorkspaceFile}
|
||||
onCreated={handleCreated}
|
||||
onOpenImportSheet={openImportSheet}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ export interface PaneContextValue {
|
||||
closeCurrentTab: () => void;
|
||||
retargetCurrentTab: (target: WorkspaceTabTarget) => void;
|
||||
openFileInWorkspace: (filePath: string) => void;
|
||||
openImportSheet: () => void;
|
||||
}
|
||||
|
||||
export interface PaneFocusContextValue {
|
||||
|
||||
63
packages/app/src/screens/workspace/composer-import-pill.tsx
Normal file
63
packages/app/src/screens/workspace/composer-import-pill.tsx
Normal file
@@ -0,0 +1,63 @@
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { Pressable, Text, View } from "react-native";
|
||||
import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
||||
import { Import as ImportIcon } from "lucide-react-native";
|
||||
import type { Theme } from "@/styles/theme";
|
||||
|
||||
const ThemedImportIcon = withUnistyles(ImportIcon);
|
||||
const iconColorMapping = (theme: Theme) => ({ color: theme.colors.foregroundMuted });
|
||||
|
||||
interface ComposerImportPillProps {
|
||||
onPress: () => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function ComposerImportPill({ onPress, disabled = false }: ComposerImportPillProps) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const handleHoverIn = useCallback(() => setIsHovered(true), []);
|
||||
const handleHoverOut = useCallback(() => setIsHovered(false), []);
|
||||
const bodyStyle = useMemo(() => [styles.body, isHovered && styles.bodyHovered], [isHovered]);
|
||||
return (
|
||||
<View style={styles.row}>
|
||||
<Pressable
|
||||
testID="composer-import-agent-pill"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Import agent"
|
||||
onPress={onPress}
|
||||
disabled={disabled}
|
||||
onHoverIn={handleHoverIn}
|
||||
onHoverOut={handleHoverOut}
|
||||
style={bodyStyle}
|
||||
>
|
||||
<ThemedImportIcon size={14} uniProps={iconColorMapping} />
|
||||
<Text style={styles.label} numberOfLines={1}>
|
||||
Import agent
|
||||
</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create((theme) => ({
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
},
|
||||
body: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing[2],
|
||||
paddingHorizontal: theme.spacing[3],
|
||||
paddingVertical: theme.spacing[2],
|
||||
borderRadius: theme.borderRadius.md,
|
||||
borderWidth: theme.borderWidth[1],
|
||||
borderColor: theme.colors.borderAccent,
|
||||
backgroundColor: theme.colors.surface1,
|
||||
},
|
||||
bodyHovered: {
|
||||
backgroundColor: theme.colors.surface2,
|
||||
},
|
||||
label: {
|
||||
color: theme.colors.foreground,
|
||||
fontSize: theme.fontSize.sm,
|
||||
},
|
||||
}));
|
||||
@@ -4,6 +4,7 @@ import { StyleSheet } from "react-native-unistyles";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import invariant from "tiny-invariant";
|
||||
import { Composer } from "@/components/composer";
|
||||
import { ComposerImportPill } from "@/screens/workspace/composer-import-pill";
|
||||
import { FileDropZone } from "@/components/file-drop-zone";
|
||||
import { AgentStreamView } from "@/components/agent-stream-view";
|
||||
import { composerWorkspaceAttachment } from "@/attachments/composer-workspace-attachments";
|
||||
@@ -28,7 +29,7 @@ import {
|
||||
useWorkspaceAttachmentScopeKey,
|
||||
} from "@/attachments/workspace-attachments-store";
|
||||
import type { UserMessageImageAttachment } from "@/types/stream";
|
||||
import { useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { MAX_CONTENT_WIDTH, useIsCompactFormFactor } from "@/constants/layout";
|
||||
import { isWeb } from "@/constants/platform";
|
||||
|
||||
const EMPTY_PENDING_PERMISSIONS = new Map();
|
||||
@@ -278,6 +279,7 @@ interface WorkspaceDraftAgentTabProps {
|
||||
isPaneFocused: boolean;
|
||||
onCreated: (snapshot: AgentSnapshotPayload) => void;
|
||||
onOpenWorkspaceFile: (input: { filePath: string }) => void;
|
||||
onOpenImportSheet?: () => void;
|
||||
}
|
||||
|
||||
export function WorkspaceDraftAgentTab({
|
||||
@@ -288,6 +290,7 @@ export function WorkspaceDraftAgentTab({
|
||||
isPaneFocused,
|
||||
onCreated,
|
||||
onOpenWorkspaceFile,
|
||||
onOpenImportSheet,
|
||||
}: WorkspaceDraftAgentTabProps) {
|
||||
const insets = useSafeAreaInsets();
|
||||
const client = useHostRuntimeClient(serverId);
|
||||
@@ -590,6 +593,13 @@ export function WorkspaceDraftAgentTab({
|
||||
</View>
|
||||
|
||||
<View style={inputAreaWrapperStyle}>
|
||||
{onOpenImportSheet ? (
|
||||
<View style={styles.importPillRow}>
|
||||
<View style={styles.importPillContent}>
|
||||
<ComposerImportPill onPress={onOpenImportSheet} disabled={isSubmitting} />
|
||||
</View>
|
||||
</View>
|
||||
) : null}
|
||||
<Composer
|
||||
agentId={tabId}
|
||||
serverId={serverId}
|
||||
@@ -644,6 +654,18 @@ const styles = StyleSheet.create((theme) => ({
|
||||
width: "100%",
|
||||
backgroundColor: theme.colors.surface0,
|
||||
},
|
||||
importPillRow: {
|
||||
width: "100%",
|
||||
paddingHorizontal: theme.spacing[4],
|
||||
paddingTop: theme.spacing[3],
|
||||
paddingBottom: theme.spacing[3],
|
||||
alignItems: "center",
|
||||
},
|
||||
importPillContent: {
|
||||
width: "100%",
|
||||
maxWidth: MAX_CONTENT_WIDTH,
|
||||
flexDirection: "row",
|
||||
},
|
||||
errorContainer: {
|
||||
marginTop: theme.spacing[2],
|
||||
paddingHorizontal: theme.spacing[3],
|
||||
|
||||
@@ -64,6 +64,7 @@ function buildContent() {
|
||||
onCloseCurrentTab: vi.fn(),
|
||||
onRetargetCurrentTab: vi.fn(),
|
||||
onOpenWorkspaceFile: vi.fn(),
|
||||
onOpenImportSheet: vi.fn(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface BuildWorkspacePaneContentModelInput {
|
||||
onCloseCurrentTab: () => void;
|
||||
onRetargetCurrentTab: (target: WorkspaceTabDescriptor["target"]) => void;
|
||||
onOpenWorkspaceFile: (filePath: string) => void;
|
||||
onOpenImportSheet: () => void;
|
||||
}
|
||||
|
||||
export function buildWorkspacePaneContentModel({
|
||||
@@ -34,6 +35,7 @@ export function buildWorkspacePaneContentModel({
|
||||
onCloseCurrentTab,
|
||||
onRetargetCurrentTab,
|
||||
onOpenWorkspaceFile,
|
||||
onOpenImportSheet,
|
||||
}: BuildWorkspacePaneContentModelInput): WorkspacePaneContentModel {
|
||||
ensurePanelsRegistered();
|
||||
const registration = getPanelRegistration(tab.kind);
|
||||
@@ -50,6 +52,7 @@ export function buildWorkspacePaneContentModel({
|
||||
closeCurrentTab: onCloseCurrentTab,
|
||||
retargetCurrentTab: onRetargetCurrentTab,
|
||||
openFileInWorkspace: onOpenWorkspaceFile,
|
||||
openImportSheet: onOpenImportSheet,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2833,6 +2833,7 @@ function WorkspaceScreenContent({
|
||||
}
|
||||
handleOpenFileFromChat({ filePath });
|
||||
},
|
||||
onOpenImportSheet: openImportSheet,
|
||||
}),
|
||||
[
|
||||
handleCloseTabById,
|
||||
@@ -2841,6 +2842,7 @@ function WorkspaceScreenContent({
|
||||
navigateToTabId,
|
||||
normalizedServerId,
|
||||
normalizedWorkspaceId,
|
||||
openImportSheet,
|
||||
openWorkspaceTabFocused,
|
||||
persistenceKey,
|
||||
convertWorkspaceDraftToAgent,
|
||||
|
||||
Reference in New Issue
Block a user