feat(forge): pluggable forge abstraction + GitLab and Gitea/Forgejo/Codeberg (#1913)
* refactor(forge): forge-neutral foundation (GitHub-only) Decouple git-hosting from GitHub behind a neutral abstraction (issue #1616), GitHub-only for now; existing GitHub behaviour is unchanged. - Forge manifest, neutral ForgeService contract, forge registry + resolver, and a client forge-module registry. - GitHub code renamed to the neutral shape; PR/Issue attachment wording preserved. - forge.search.response enums parse tolerantly (unknown kind/auth state degrade instead of breaking the client). - createPullRequest reports typed CLI/auth errors instead of a generic message. - forge-resolver host/remote caches are LRU-bounded. - Forge host trust is explicit: only a known cloud host or a CLI-authenticated host is ever talked to; an unauthenticated GitHub Enterprise host fails resolution instead of routing to github.com. - Docs: forge-providers guide, glossary and i18n forge-copy conventions, architecture and rpc-namespacing terminology. - Vitest React Native mocks (unistyles, svg, linking, lucide) consolidated into shared aliased test-stubs. * feat(forge): GitLab adapter, forge-aware UI, pipelines and approvals GitLab adapter over the glab CLI on the neutral contracts: MR status, forge-aware UI, pipeline tree, and N-of-M approvals. - threadIsResolved is part of the neutral timeline item. - Pipeline load failures show an error instead of an empty section. - Manual pipeline jobs render as pending. - Fork/detached MR head pipelines are fetched by MR iid (glab ci get --merge-request). * feat(forge): Gitea family adapter (Gitea, Forgejo, Codeberg) One adapter over the tea CLI serving Gitea, Forgejo, and Codeberg on the neutral contracts. - CI status aggregates commit statuses and Actions runs together. - Gitea's terminal "warning" state maps to failure on server and client. - Gitea Actions check details are reachable from the PR pane by workflowRunId. * refactor(forge): localize compatibility handling * test(forge): expect normalized GitLab facts --------- Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
This commit is contained in:
co-authored by
Mohamed Boudra
parent
04d1ebdce0
commit
a8ebd390fa
@@ -347,6 +347,7 @@ function ExplorerSidebarContent({
|
||||
testID="explorer-tab-pr"
|
||||
>
|
||||
<PullRequestTabIcon
|
||||
forge={prPane.forge}
|
||||
size={13}
|
||||
color={
|
||||
resolvedTab === "pr" ? theme.colors.foreground : theme.colors.foregroundMuted
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SvgPathIcon, type SvgPathIconProps } from "./svg-path-icon";
|
||||
|
||||
const CODEBERG_ICON_PATH =
|
||||
"M11.999.747A11.974 11.974 0 0 0 0 12.75c0 2.254.635 4.465 1.833 6.376L11.837 6.19c.072-.092.251-.092.323 0l4.178 5.402h-2.992l.065.239h3.113l.882 1.138h-3.674l.103.374h3.86l.777 1.003h-4.358l.135.483h4.593l.695.894h-5.038l.165.589h5.326l.609.785h-5.717l.182.65h6.038l.562.727h-6.397l.183.65h6.717A12.003 12.003 0 0 0 24 12.75 11.977 11.977 0 0 0 11.999.747zm3.654 19.104.182.65h5.326c.173-.204.353-.433.513-.65zm.385 1.377.18.65h3.563c.233-.198.485-.428.712-.65zm.383 1.377.182.648h1.203c.356-.204.685-.412 1.042-.648z";
|
||||
|
||||
export function CodebergIcon(props: SvgPathIconProps) {
|
||||
return <SvgPathIcon {...props} viewBox="0 0 24 24" path={CODEBERG_ICON_PATH} />;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SvgPathIcon, type SvgPathIconProps } from "./svg-path-icon";
|
||||
|
||||
const FORGEJO_ICON_PATH =
|
||||
"M16.7773 0c1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.1175a7.0759 7.0759 0 0 1 4.148-1.4205l.1176-.001 1.3385.0002c.4973-.8827 1.4434-1.4788 2.5288-1.4788 1.6018 0 2.9004 1.2986 2.9004 2.9005s-1.2986 2.9004-2.9004 2.9004c-1.0854 0-2.0315-.596-2.5288-1.4787H12.91c-2.3322 0-4.2272 1.8718-4.2649 4.195l-.0007 2.319c.8827.4973 1.4788 1.4434 1.4788 2.5287 0 1.602-1.2986 2.9005-2.9005 2.9005-1.6018 0-2.9004-1.2986-2.9004-2.9005 0-1.0853.596-2.0314 1.4788-2.5287l-.0002-9.9831c0-3.887 3.1195-7.0453 6.9915-7.108l.1176-.001h1.3385C14.7458.5962 15.692 0 16.7773 0ZM7.2227 19.9052c-.6596 0-1.1943.5347-1.1943 1.1943s.5347 1.1943 1.1943 1.1943 1.1944-.5347 1.1944-1.1943-.5348-1.1943-1.1944-1.1943Zm9.5546-10.4644c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Zm0-7.7346c-.6596 0-1.1944.5347-1.1944 1.1943s.5348 1.1943 1.1944 1.1943c.6596 0 1.1943-.5347 1.1943-1.1943s-.5347-1.1943-1.1943-1.1943Z";
|
||||
|
||||
export function ForgejoIcon(props: SvgPathIconProps) {
|
||||
return <SvgPathIcon {...props} viewBox="0 0 24 24" path={FORGEJO_ICON_PATH} />;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SvgPathIcon, type SvgPathIconProps } from "./svg-path-icon";
|
||||
|
||||
const GITEA_ICON_PATH =
|
||||
"M4.209 4.603c-.247 0-.525.02-.84.088-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768 1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367 2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068 0 0 2.107-4.471 2.107-8.823-.042-1.318-.367-1.55-.443-1.627-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422-.529 1.075a.686.686 0 0 0-.614.359.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527.687.687 0 0 0 .347.763.686.686 0 0 0 .867-.206.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02.657.657 0 0 0 .271-.137 8.826 8.826 0 0 1 1.016.512.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431.19-.397.515-1.16.515-1.16.035-.066.218-.394.103-.814-.095-.435-.48-.638-.48-.638-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062 2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z";
|
||||
|
||||
export function GiteaIcon(props: SvgPathIconProps) {
|
||||
return <SvgPathIcon {...props} viewBox="0 0 24 24" path={GITEA_ICON_PATH} />;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { SvgPathIcon, type SvgPathIconProps } from "./svg-path-icon";
|
||||
|
||||
const GITLAB_ICON_PATH =
|
||||
"M23.6004 9.5927l-.0337-.0862L20.3 .9814a.851.851 0 0 0-.3362-.405.8748.8748 0 0 0-.9997.0539.8748.8748 0 0 0-.29.4399l-2.2055 6.748H7.5375l-2.2057-6.748a.8573.8573 0 0 0-.29-.4412.8748.8748 0 0 0-.9997-.0537.8585.8585 0 0 0-.3362.4049L.4332 9.5015l-.0325.0862a6.0657 6.0657 0 0 0 2.0119 7.0105l.0113.0087.03.0213 4.976 3.7264 2.462 1.8633 1.4995 1.1321a1.0085 1.0085 0 0 0 1.2197 0l1.4995-1.1321 2.462-1.8633 5.006-3.7489.0125-.01a6.0682 6.0682 0 0 0 2.0094-7.003z";
|
||||
|
||||
export function GitLabIcon(props: SvgPathIconProps) {
|
||||
return <SvgPathIcon {...props} viewBox="0 0 24 24" path={GITLAB_ICON_PATH} />;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import Svg, { Path } from "react-native-svg";
|
||||
|
||||
export interface SvgPathIconProps {
|
||||
size?: number;
|
||||
color?: string;
|
||||
}
|
||||
|
||||
interface SvgPathIconInput extends SvgPathIconProps {
|
||||
path: string;
|
||||
viewBox: string;
|
||||
}
|
||||
|
||||
export function SvgPathIcon({
|
||||
size = 16,
|
||||
color = "currentColor",
|
||||
path,
|
||||
viewBox,
|
||||
}: SvgPathIconInput) {
|
||||
return (
|
||||
<Svg width={size} height={size} viewBox={viewBox} fill={color}>
|
||||
<Path d={path} />
|
||||
</Svg>
|
||||
);
|
||||
}
|
||||
@@ -84,6 +84,7 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { SyncedLoader } from "@/components/synced-loader";
|
||||
import { useToast } from "@/contexts/toast-context";
|
||||
import { getForgePresentation, normalizeForge } from "@/git/forge";
|
||||
import { toWorktreeArchiveRisk } from "@/git/worktree-archive-warning";
|
||||
import { hasVisibleOrderChanged, mergeWithRemainder } from "@/utils/sidebar-reorder";
|
||||
import { decideLongPressMove } from "@/utils/sidebar-gesture-arbitration";
|
||||
@@ -319,12 +320,14 @@ export function PrBadge({ hint }: { hint: PrHint }) {
|
||||
|
||||
const textStyle = isHovered ? prBadgeTextHoveredCombined : prBadgeStyles.text;
|
||||
const iconUniProps = isHovered ? foregroundColorMapping : getPrIconUniMapping(hint.state);
|
||||
const presentation = getForgePresentation(normalizeForge(hint.forge));
|
||||
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="link"
|
||||
accessibilityLabel={t("workspace.git.pr.accessibility.pullRequest", {
|
||||
number: hint.number,
|
||||
context: presentation.changeRequestContext,
|
||||
})}
|
||||
hitSlop={4}
|
||||
onPressIn={handlePressIn}
|
||||
@@ -339,6 +342,7 @@ export function PrBadge({ hint }: { hint: PrHint }) {
|
||||
<ThemedGitPullRequest size={12} uniProps={iconUniProps} />
|
||||
)}
|
||||
<Text style={textStyle} numberOfLines={1}>
|
||||
{presentation.numberPrefix}
|
||||
{hint.number}
|
||||
</Text>
|
||||
</Pressable>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { memo, useCallback, useMemo, useState, type ReactNode } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ActivityIndicator,
|
||||
Pressable,
|
||||
@@ -18,13 +19,14 @@ import {
|
||||
Monitor,
|
||||
SquareTerminal,
|
||||
} from "lucide-react-native";
|
||||
import { GitHubIcon } from "@/components/icons/github-icon";
|
||||
import { WorkspaceHoverCard } from "@/components/workspace-hover-card";
|
||||
import { SyncedLoader } from "@/components/synced-loader";
|
||||
import type { SidebarWorkspaceEntry } from "@/hooks/use-sidebar-workspaces-list";
|
||||
import { useAppSettings } from "@/hooks/use-settings";
|
||||
import type { Theme } from "@/styles/theme";
|
||||
import type { PrHint } from "@/git/use-pr-status-query";
|
||||
import { getForgePresentation, normalizeForge } from "@/git/forge";
|
||||
import { ForgeBrandIcon } from "@/git/forge-icon";
|
||||
import type { SidebarStateBucket } from "@/utils/sidebar-agent-state";
|
||||
import { isEmphasizedStatusDotBucket } from "@/utils/status-dot-color";
|
||||
import { shouldRenderSyncedStatusLoader } from "@/utils/status-loader";
|
||||
@@ -52,7 +54,6 @@ const purpleColorMapping = (theme: Theme) => ({ color: theme.colors.palette.purp
|
||||
|
||||
const ThemedExternalLink = withUnistyles(ExternalLink);
|
||||
const ThemedGitPullRequest = withUnistyles(GitPullRequest);
|
||||
const ThemedGitHubIcon = withUnistyles(GitHubIcon);
|
||||
const ThemedActivityIndicator = withUnistyles(ActivityIndicator);
|
||||
const ThemedCircleAlert = withUnistyles(CircleAlert);
|
||||
const ThemedSyncedLoader = withUnistyles(SyncedLoader);
|
||||
@@ -62,6 +63,10 @@ const ThemedFolderGit2 = withUnistyles(FolderGit2);
|
||||
const ThemedGlobe = withUnistyles(Globe);
|
||||
const ThemedSquareTerminal = withUnistyles(SquareTerminal);
|
||||
|
||||
function renderChecksBadgeForgeIcon(icon: string) {
|
||||
return <ForgeBrandIcon iconKind={icon} size={10} uniProps={redColorMapping} />;
|
||||
}
|
||||
|
||||
type SidebarWorkspaceScriptIconKind = "service" | "command";
|
||||
|
||||
export function SidebarWorkspaceRowFrame({
|
||||
@@ -156,7 +161,7 @@ export const SidebarWorkspaceRowContent = memo(function SidebarWorkspaceRowConte
|
||||
{workspace.prHint ? (
|
||||
<View style={styles.workspacePrBadgeRow}>
|
||||
<PrBadge hint={workspace.prHint} />
|
||||
<ChecksBadge checks={workspace.prHint.checks} />
|
||||
<ChecksBadge checks={workspace.prHint.checks} forge={workspace.prHint.forge} />
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
@@ -290,6 +295,7 @@ function StatusDotOverlay({
|
||||
}
|
||||
|
||||
function PrBadge({ hint }: { hint: PrHint }) {
|
||||
const { t } = useTranslation();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const handlePress = useCallback(
|
||||
(event: GestureResponderEvent) => {
|
||||
@@ -303,6 +309,7 @@ function PrBadge({ hint }: { hint: PrHint }) {
|
||||
[isHovered],
|
||||
);
|
||||
const iconUniProps = isHovered ? foregroundColorMapping : getPrIconUniMapping(hint.state);
|
||||
const presentation = getForgePresentation(normalizeForge(hint.forge));
|
||||
|
||||
const handlePressIn = useCallback((event: GestureResponderEvent) => event.stopPropagation(), []);
|
||||
const handleHoverIn = useCallback(() => setIsHovered(true), []);
|
||||
@@ -316,7 +323,10 @@ function PrBadge({ hint }: { hint: PrHint }) {
|
||||
return (
|
||||
<Pressable
|
||||
accessibilityRole="link"
|
||||
accessibilityLabel={`Pull request #${hint.number}`}
|
||||
accessibilityLabel={t("workspace.git.pr.accessibility.pullRequest", {
|
||||
number: hint.number,
|
||||
context: presentation.changeRequestContext,
|
||||
})}
|
||||
hitSlop={4}
|
||||
onPressIn={handlePressIn}
|
||||
onPress={handlePress}
|
||||
@@ -330,19 +340,21 @@ function PrBadge({ hint }: { hint: PrHint }) {
|
||||
<ThemedGitPullRequest size={12} uniProps={iconUniProps} />
|
||||
)}
|
||||
<Text style={textStyle} numberOfLines={1}>
|
||||
{presentation.numberPrefix}
|
||||
{hint.number}
|
||||
</Text>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
|
||||
function ChecksBadge({ checks }: { checks: PrHint["checks"] }) {
|
||||
function ChecksBadge({ checks, forge }: { checks: PrHint["checks"]; forge: PrHint["forge"] }) {
|
||||
if (!checks || checks.length === 0) return null;
|
||||
const failed = checks.filter((check) => check.status === "failure").length;
|
||||
if (failed === 0) return null;
|
||||
const icon = getForgePresentation(normalizeForge(forge)).icon;
|
||||
return (
|
||||
<View style={checksBadgeStyles.badge}>
|
||||
<ThemedGitHubIcon size={10} uniProps={redColorMapping} />
|
||||
{renderChecksBadgeForgeIcon(icon)}
|
||||
<Text style={checksBadgeStyles.text}>{failed} failed</Text>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,8 @@ import {
|
||||
GitBranch,
|
||||
Server,
|
||||
} from "lucide-react-native";
|
||||
import { GitHubIcon } from "@/components/icons/github-icon";
|
||||
import { getForgePresentation, normalizeForge } from "@/git/forge";
|
||||
import { ForgeBrandIcon } from "@/git/forge-icon";
|
||||
import type { Theme } from "@/styles/theme";
|
||||
import { DiffStat } from "@/components/diff-stat";
|
||||
import { Pressable } from "react-native";
|
||||
@@ -319,7 +320,11 @@ function WorkspaceHoverCardContent({
|
||||
{prHint?.checks && prHint.checks.length > 0 ? (
|
||||
<>
|
||||
<View style={styles.separator} />
|
||||
<ChecksSummaryPressable checks={prHint.checks} url={prHint.url} />
|
||||
<ChecksSummaryPressable
|
||||
checks={prHint.checks}
|
||||
url={prHint.url}
|
||||
forge={prHint.forge}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
</FloatingSurface>
|
||||
@@ -343,7 +348,6 @@ function HostRow({ serverId }: { serverId: string }): ReactElement | null {
|
||||
}
|
||||
|
||||
const ThemedExternalLink = withUnistyles(ExternalLink);
|
||||
const ThemedGitHubIcon = withUnistyles(GitHubIcon);
|
||||
const ThemedCircleCheck = withUnistyles(CircleCheck);
|
||||
const ThemedCircleDot = withUnistyles(CircleDot);
|
||||
const ThemedCircleX = withUnistyles(CircleX);
|
||||
@@ -375,6 +379,10 @@ function InfoRow({
|
||||
);
|
||||
}
|
||||
|
||||
function renderChecksSummaryForgeIcon(icon: string, iconUniProps: typeof foregroundColorMapping) {
|
||||
return <ForgeBrandIcon iconKind={icon} size={12} uniProps={iconUniProps} />;
|
||||
}
|
||||
|
||||
function CopyableInfoRow({
|
||||
icon: Icon,
|
||||
value,
|
||||
@@ -488,9 +496,11 @@ function ChecksSummaryPill({
|
||||
|
||||
function ChecksSummaryContent({
|
||||
checks,
|
||||
forge,
|
||||
hovered,
|
||||
}: {
|
||||
checks: NonNullable<PrHint["checks"]>;
|
||||
forge: PrHint["forge"];
|
||||
hovered: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
@@ -498,13 +508,14 @@ function ChecksSummaryContent({
|
||||
|
||||
const labelStyle = hovered ? checksSummaryLabelHoveredCombined : styles.checksSummaryLabel;
|
||||
const iconUniProps = hovered ? foregroundColorMapping : foregroundMutedColorMapping;
|
||||
const icon = getForgePresentation(normalizeForge(forge)).icon;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hovered ? (
|
||||
<ThemedExternalLink size={12} uniProps={iconUniProps} />
|
||||
) : (
|
||||
<ThemedGitHubIcon size={12} uniProps={iconUniProps} />
|
||||
renderChecksSummaryForgeIcon(icon, iconUniProps)
|
||||
)}
|
||||
<Text style={labelStyle}>{t("workspace.git.pr.sections.checks")}</Text>
|
||||
<View style={styles.checksSummaryCounts}>
|
||||
@@ -518,9 +529,11 @@ function ChecksSummaryContent({
|
||||
|
||||
function ChecksSummaryPressable({
|
||||
checks,
|
||||
forge,
|
||||
url,
|
||||
}: {
|
||||
checks: NonNullable<PrHint["checks"]>;
|
||||
forge: PrHint["forge"];
|
||||
url: string;
|
||||
}) {
|
||||
const handlePress = useCallback(() => {
|
||||
@@ -529,9 +542,9 @@ function ChecksSummaryPressable({
|
||||
|
||||
const renderChildren = useCallback(
|
||||
({ hovered }: { pressed: boolean; hovered?: boolean }) => (
|
||||
<ChecksSummaryContent checks={checks} hovered={Boolean(hovered)} />
|
||||
<ChecksSummaryContent checks={checks} forge={forge} hovered={Boolean(hovered)} />
|
||||
),
|
||||
[checks],
|
||||
[checks, forge],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,7 +18,10 @@ import { normalizeAgentSnapshot } from "@/utils/agent-snapshots";
|
||||
import { applyLegacyDaemonWorkspaceOwnership } from "@/workspace/legacy-daemon-workspaces";
|
||||
import { encodeImages } from "@/utils/encode-images";
|
||||
import { toErrorMessage } from "@/utils/error-messages";
|
||||
import { splitComposerAttachmentsForSubmit } from "@/composer/attachments/submit";
|
||||
import {
|
||||
resolveComposerAttachmentSubmitFormat,
|
||||
splitComposerAttachmentsForSubmit,
|
||||
} from "@/composer/attachments/submit";
|
||||
import type {
|
||||
CreateAgentRequestOptions,
|
||||
DaemonClient,
|
||||
@@ -171,6 +174,9 @@ export function WorkspaceSetupDialog() {
|
||||
const [pendingAction, setPendingAction] = useState<"chat" | null>(null);
|
||||
|
||||
const serverId = pendingWorkspaceSetup?.serverId ?? "";
|
||||
const supportsForgeSearch = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.serverInfo?.features?.forgeSearch === true,
|
||||
);
|
||||
const sourceDirectory = pendingWorkspaceSetup?.sourceDirectory ?? "";
|
||||
const displayName = pendingWorkspaceSetup?.displayName?.trim() ?? "";
|
||||
const workspace = createdWorkspace;
|
||||
@@ -309,7 +315,11 @@ export function WorkspaceSetupDialog() {
|
||||
throw new Error(t("workspaceSetup.errors.selectModel"));
|
||||
}
|
||||
|
||||
const wirePayload = splitComposerAttachmentsForSubmit(attachments);
|
||||
const wirePayload = splitComposerAttachmentsForSubmit(attachments, {
|
||||
format: resolveComposerAttachmentSubmitFormat({
|
||||
supportsForgeAttachments: supportsForgeSearch,
|
||||
}),
|
||||
});
|
||||
const encodedImages = await encodeImages(wirePayload.images);
|
||||
const workspaceDirectory = requireWorkspaceDirectory({
|
||||
workspaceId: ensuredWorkspace.id,
|
||||
@@ -363,6 +373,7 @@ export function WorkspaceSetupDialog() {
|
||||
t,
|
||||
toast,
|
||||
withConnectedClient,
|
||||
supportsForgeSearch,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user