-
{title}
- {sub &&
{sub}
}
+
+
+
+
-
- {cards.map((c, i) => (
-
-
-
-
-
- {c.title}
- {c.desc}
-
- {c.status && {c.status}}
-
-
- ))}
+
+
+ {entries.map((entry) => {
+ const Icon = entry.icon;
+ return (
+
+ );
+ })}
- {footer &&
{footer}
}
-
+
);
}
diff --git a/frontend/src/ui/CapabilityIcons.tsx b/frontend/src/ui/CapabilityIcons.tsx
index 61d363f57..94a0aed81 100644
--- a/frontend/src/ui/CapabilityIcons.tsx
+++ b/frontend/src/ui/CapabilityIcons.tsx
@@ -2,6 +2,130 @@ interface CapabilityIconProps {
className?: string;
}
+export type CanvasAgentType = "llm" | "sequential" | "parallel" | "loop" | "a2a";
+
+/** Figma `git-merge` mark used in the Agent canvas node identity strip. */
+export function AgentFlowCapabilityIcon({ className = "icon" }: CapabilityIconProps) {
+ return (
+
+ );
+}
+
+/** Type-specific identity mark used by Agent canvas cards and containers. */
+export function CanvasAgentTypeIcon({
+ type,
+ className = "icon",
+}: CapabilityIconProps & { type: CanvasAgentType }) {
+ if (type === "llm") {
+ return
;
+ }
+
+ const commonProps = {
+ className,
+ viewBox: "0 0 16 16",
+ fill: "none",
+ stroke: "currentColor",
+ strokeWidth: 1.45,
+ strokeLinecap: "round" as const,
+ strokeLinejoin: "round" as const,
+ "aria-hidden": true,
+ };
+
+ if (type === "sequential") {
+ return (
+
+ );
+ }
+
+ if (type === "parallel") {
+ return (
+
+ );
+ }
+
+ if (type === "loop") {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+}
+
+/** Open-book mark used for the compact skill count on Agent canvas cards. */
+export function AgentSkillCountIcon({ className = "icon" }: CapabilityIconProps) {
+ return (
+
+ );
+}
+
+/** Puzzle-piece mark used for the compact tool count on Agent canvas cards. */
+export function AgentToolCountIcon({ className = "icon" }: CapabilityIconProps) {
+ return (
+
+ );
+}
+
/** Three tuned control tracks: a compact mark for mounted tools. */
export function ToolCapabilityIcon({ className = "icon" }: CapabilityIconProps) {
return (
diff --git a/frontend/src/ui/CloudEnvironmentConfigurator.css b/frontend/src/ui/CloudEnvironmentConfigurator.css
index 7ffa90155..6adbe6871 100644
--- a/frontend/src/ui/CloudEnvironmentConfigurator.css
+++ b/frontend/src/ui/CloudEnvironmentConfigurator.css
@@ -1,10 +1,10 @@
.cloud-env-config {
- width: min(100%, 820px);
+ width: 100%;
min-height: 0;
display: flex;
flex-direction: column;
- gap: 22px;
- margin: 0 auto;
+ gap: 20px;
+ margin: 0;
padding: 0;
}
@@ -12,15 +12,18 @@
min-width: 0;
display: flex;
flex-direction: column;
- gap: 20px;
+ gap: 24px;
}
.cloud-env-category {
min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
}
.cloud-env-category h2 {
- margin: 0 0 6px 4px;
+ margin: 0;
color: hsl(var(--foreground));
font-size: 14px;
font-weight: 500;
@@ -30,8 +33,8 @@
.cloud-env-tool-list {
min-width: 0;
display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 2px 24px;
+ grid-template-columns: minmax(0, 1fr);
+ gap: 8px;
}
.cloud-env-tool {
@@ -40,7 +43,7 @@
display: flex;
align-items: center;
gap: 10px;
- padding: 4px;
+ padding: 4px 0;
border-radius: 8px;
transition:
background-color 150ms ease,
@@ -119,6 +122,7 @@
display: flex;
align-items: center;
gap: 10px;
+ margin: 20px 0 0;
padding: 7px 10px;
border: 1px solid hsl(var(--border));
border-radius: 10px;
diff --git a/frontend/src/ui/CloudEnvironmentConfigurator.tsx b/frontend/src/ui/CloudEnvironmentConfigurator.tsx
index 0c87f8900..9c0022744 100644
--- a/frontend/src/ui/CloudEnvironmentConfigurator.tsx
+++ b/frontend/src/ui/CloudEnvironmentConfigurator.tsx
@@ -164,6 +164,14 @@ export function CloudEnvironmentConfigurator({
: null;
document.body.style.overflow = "hidden";
closeButtonRef.current?.focus();
+ return () => {
+ document.body.style.overflow = previousOverflow;
+ if (previousFocus?.isConnected) previousFocus.focus();
+ };
+ }, [editorOpen]);
+
+ useEffect(() => {
+ if (!editorOpen) return;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "Escape" && !resetConfirmOpen) {
onEditorOpenChange(false);
@@ -171,9 +179,7 @@ export function CloudEnvironmentConfigurator({
};
window.addEventListener("keydown", handleKeyDown);
return () => {
- document.body.style.overflow = previousOverflow;
window.removeEventListener("keydown", handleKeyDown);
- if (previousFocus?.isConnected) previousFocus.focus();
};
}, [editorOpen, onEditorOpenChange, resetConfirmOpen]);
diff --git a/frontend/src/ui/ConversationActions.tsx b/frontend/src/ui/ConversationActions.tsx
new file mode 100644
index 000000000..8189209f9
--- /dev/null
+++ b/frontend/src/ui/ConversationActions.tsx
@@ -0,0 +1,74 @@
+import { useState } from "react";
+import { Check, Copy } from "lucide-react";
+import { FeedbackDownIcon, FeedbackUpIcon } from "./icons/FeedbackIcons";
+
+export type ConversationFeedbackRating = "good" | "bad" | null;
+
+export function ConversationCopyButton({ text }: { text: string }) {
+ const [copied, setCopied] = useState(false);
+
+ return (
+
+ );
+}
+
+export function ConversationFeedbackButtons({
+ rating,
+ pending = false,
+ onChange,
+}: {
+ rating: ConversationFeedbackRating;
+ pending?: boolean;
+ onChange: (rating: ConversationFeedbackRating) => void;
+}) {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/frontend/src/ui/CreateAgentHeader.css b/frontend/src/ui/CreateAgentHeader.css
new file mode 100644
index 000000000..b892bfce1
--- /dev/null
+++ b/frontend/src/ui/CreateAgentHeader.css
@@ -0,0 +1,202 @@
+.create-agent-header {
+ --create-agent-header-motion-duration: 220ms;
+ --create-agent-header-motion-easing: cubic-bezier(0.16, 1, 0.3, 1);
+
+ position: relative;
+ z-index: 12;
+ display: flex;
+ width: 100%;
+ height: 64px;
+ flex: 0 0 64px;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 24px;
+ background: transparent;
+ color: #0c0d0e;
+ transition:
+ width var(--create-agent-header-motion-duration) var(--create-agent-header-motion-easing),
+ height var(--create-agent-header-motion-duration) var(--create-agent-header-motion-easing),
+ flex-basis var(--create-agent-header-motion-duration) var(--create-agent-header-motion-easing),
+ margin-left var(--create-agent-header-motion-duration) var(--create-agent-header-motion-easing),
+ margin-right var(--create-agent-header-motion-duration) var(--create-agent-header-motion-easing);
+}
+
+.create-agent-header.is-debug-results {
+ width: calc(100% - 40px);
+ height: 60px;
+ flex-basis: 60px;
+ margin-inline: 20px;
+}
+
+.create-agent-header-start,
+.create-agent-header-actions {
+ display: flex;
+ align-items: center;
+}
+
+.create-agent-header-start {
+ width: 260px;
+ gap: 8px;
+ font-size: 18px;
+ font-weight: 500;
+ line-height: 26px;
+ letter-spacing: 0.003em;
+}
+
+.create-agent-header-back {
+ --button-size: 20px;
+ --button-icon-size: 15px;
+
+ position: relative;
+ flex: 0 0 20px;
+ border-radius: 4px;
+ color: #42464e;
+}
+
+.create-agent-header-back::before {
+ background: transparent !important;
+}
+
+.create-agent-header-back::after {
+ position: absolute;
+ inset: -4px;
+ content: "";
+}
+
+.create-agent-header-title {
+ white-space: nowrap;
+}
+
+.create-agent-header-actions {
+ position: relative;
+ width: 260px;
+ justify-content: flex-end;
+ gap: 12px;
+}
+
+.create-agent-header.is-debug-preview .create-agent-header-actions,
+.create-agent-header.is-debug-results .create-agent-header-actions {
+ gap: 16px;
+}
+
+.create-agent-header-action {
+ --button-size: 36px;
+ --button-gutter: 12px;
+ --button-icon-size: 16px;
+ --button-gap: 5px;
+
+ min-width: 80px;
+ border-radius: 8px;
+ color: #101013;
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.create-agent-header-action::before {
+ border: 0 !important;
+ border-radius: 8px !important;
+ box-shadow: 0 0 0 0.5px #c9cdd4 inset !important;
+}
+
+.create-agent-header-action.is-debug {
+ --button-icon-size: 10px;
+ --button-gap: 4px;
+
+ font-weight: 400;
+ line-height: 22px;
+}
+
+.create-agent-header-action.is-debug::before {
+ border: 0.5px solid #c9cdd4 !important;
+ background: #fff !important;
+ box-shadow: none !important;
+}
+
+.create-agent-header-action.is-code {
+ min-width: 72px;
+ color: #101013;
+}
+
+.create-agent-header-debug-action {
+ --button-size: 32px;
+ --button-gutter: 12px;
+ --button-icon-size: 16px;
+ --button-gap: 4px;
+
+ min-width: 100px;
+ border-radius: 8px;
+ color: #383740;
+ font-size: 14px;
+ font-weight: 400;
+}
+
+.create-agent-header-debug-action.is-add::before {
+ box-shadow: none !important;
+}
+
+.create-agent-header-debug-action:not(.is-add)::before {
+ border: 0 !important;
+ border-radius: 8px !important;
+ background: hsl(var(--background)) !important;
+ box-shadow: 0 0 0 0.5px #b8b7c3 inset !important;
+}
+
+.create-agent-header-action.is-code::before {
+ box-shadow: none !important;
+}
+
+.create-agent-header-action.is-deploy {
+ color: #fff;
+}
+
+.create-agent-header-action.is-deploy::before {
+ background: #101013 !important;
+ box-shadow: none !important;
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .create-agent-header {
+ transition: none;
+ }
+}
+
+@media (max-width: 700px) {
+ .create-agent-header {
+ padding-inline: 12px;
+ }
+
+ .create-agent-header.is-debug-results {
+ width: 100%;
+ margin-inline: 0;
+ }
+
+ .create-agent-header-start {
+ width: auto;
+ min-width: 0;
+ font-size: 16px;
+ }
+
+ .create-agent-header-action.is-code,
+ .create-agent-header-action:not(.is-deploy) {
+ display: none;
+ }
+
+ .create-agent-header-actions {
+ width: auto;
+ }
+
+ .create-agent-header.is-debug-preview .create-agent-header-actions,
+ .create-agent-header.is-debug-results .create-agent-header-actions {
+ gap: 4px;
+ }
+
+ .create-agent-header-debug-action {
+ --button-gutter: 8px;
+
+ min-width: 32px;
+ }
+
+ .create-agent-header-debug-action .create-agent-header-action-label {
+ display: none;
+ }
+}
diff --git a/frontend/src/ui/CreateAgentHeader.tsx b/frontend/src/ui/CreateAgentHeader.tsx
new file mode 100644
index 000000000..a3321ee08
--- /dev/null
+++ b/frontend/src/ui/CreateAgentHeader.tsx
@@ -0,0 +1,142 @@
+import { Button } from "@openai/apps-sdk-ui/components/Button";
+import {
+ CreateAddIcon,
+ CreateBackIcon,
+ CreateCloseIcon,
+ CreateDebugIcon,
+ CreateDeployIcon,
+ CreateDownloadIcon,
+} from "./icons/CreateAgentIcons";
+import "./CreateAgentHeader.css";
+
+export interface CreateAgentHeaderProps {
+ onBack: () => void;
+ onDebug?: () => void;
+ onDeploy?: () => void;
+ debugMode?: boolean;
+ showDebugPreview?: boolean;
+ comparisonDisabled?: boolean;
+ onAddComparison?: () => void;
+ onExitDebug?: () => void;
+}
+
+export function CreateAgentHeader({
+ onBack,
+ onDebug,
+ onDeploy,
+ debugMode = false,
+ showDebugPreview = true,
+ comparisonDisabled = false,
+ onAddComparison,
+ onExitDebug,
+}: CreateAgentHeaderProps) {
+ const workspace = Boolean(onDebug || onDeploy);
+ const stateClass = debugMode
+ ? showDebugPreview
+ ? "is-debug-preview"
+ : "is-debug-results"
+ : workspace
+ ? "is-build"
+ : "is-entry";
+
+ return (
+
+ );
+}
diff --git a/frontend/src/ui/DeploymentResources.css b/frontend/src/ui/DeploymentResources.css
index ef91833c1..fb1638f9c 100644
--- a/frontend/src/ui/DeploymentResources.css
+++ b/frontend/src/ui/DeploymentResources.css
@@ -8,23 +8,22 @@
}
.pp-resource-list {
- --pp-resource-mode-column: minmax(196px, 0.34fr);
-
display: flex;
flex-direction: column;
- gap: 12px;
+ gap: 0;
}
.pp-resource-item {
min-width: 0;
- padding: 12px;
- border: 1px solid hsl(var(--border) / 0.72);
- border-radius: 10px;
- background: hsl(var(--background));
+ padding: 16px 0;
+}
+
+.pp-resource-item + .pp-resource-item {
+ border-top: 1px dashed hsl(var(--border) / 0.8);
}
.pp-resource-name {
- margin-bottom: 10px;
+ margin-bottom: 12px;
color: hsl(var(--foreground));
font-size: 13px;
font-weight: 600;
@@ -33,59 +32,32 @@
.pp-resource-grid {
min-width: 0;
- display: grid;
- grid-template-columns: var(--pp-resource-mode-column) minmax(0, 0.66fr);
- align-items: start;
- gap: 12px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
}
.pp-resource-fields {
min-width: 0;
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 10px;
-}
-
-.pp-resource-fields-three {
- grid-template-columns: repeat(3, minmax(0, 1fr));
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
}
.pp-resource-field {
min-width: 0;
display: flex;
flex-direction: column;
- gap: 6px;
- color: hsl(var(--muted-foreground));
- font-size: 12px;
- line-height: 1.4;
+ gap: 8px;
+ color: hsl(var(--foreground));
+ font-size: 13px;
+ font-weight: 500;
+ line-height: 20px;
}
-.pp-resource-field > input {
+.pp-resource-field > :not(label) {
width: 100%;
min-width: 0;
- height: 36px;
- box-sizing: border-box;
- padding: 0 10px;
- border: 1px solid hsl(var(--border));
- border-radius: 6px;
- outline: none;
- background: hsl(var(--background));
- color: hsl(var(--foreground));
- font: inherit;
- font-size: 12.5px;
- transition:
- border-color 120ms ease,
- box-shadow 120ms ease;
-}
-
-.pp-resource-field > input:focus-visible {
- border-color: hsl(var(--ring) / 0.55);
- box-shadow: 0 0 0 2px hsl(var(--ring) / 0.08);
-}
-
-.pp-resource-field > input:disabled {
- cursor: not-allowed;
- opacity: 0.55;
}
.pp-resource-auto-names {
@@ -128,9 +100,11 @@
}
.pp-resource-auto-names dd {
- overflow-wrap: anywhere;
+ overflow: hidden;
color: hsl(var(--foreground));
font-size: 12.5px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
}
.pp-resource-auto-names > small {
@@ -172,18 +146,6 @@
.pp-resource-error button {
flex-shrink: 0;
- padding: 0;
- border: 0;
- background: transparent;
- color: inherit;
- cursor: pointer;
- font: inherit;
- font-weight: 600;
-}
-
-.pp-resource-error button:focus-visible {
- outline: 2px solid hsl(var(--ring) / 0.45);
- outline-offset: 2px;
}
.pp-resource-validation {
@@ -194,20 +156,8 @@
}
@media (max-width: 760px) {
- .pp-resource-grid,
- .pp-resource-fields,
- .pp-resource-fields-three {
- grid-template-columns: minmax(0, 1fr);
- }
-
.pp-resource-auto-names > dl > div {
grid-template-columns: minmax(0, 1fr);
gap: 2px;
}
}
-
-@media (prefers-reduced-motion: reduce) {
- .pp-resource-field > input {
- transition: none;
- }
-}
diff --git a/frontend/src/ui/DeploymentResources.tsx b/frontend/src/ui/DeploymentResources.tsx
index ab2ef53f7..e4f2eda0a 100644
--- a/frontend/src/ui/DeploymentResources.tsx
+++ b/frontend/src/ui/DeploymentResources.tsx
@@ -1,4 +1,10 @@
-import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
+import { Button } from "@openai/apps-sdk-ui/components/Button";
+import { Input } from "@openai/apps-sdk-ui/components/Input";
+import {
+ Select,
+ type Option as SelectOption,
+} from "@openai/apps-sdk-ui/components/Select";
import {
listDeploymentResources,
type DeploymentResource,
@@ -6,18 +12,13 @@ import {
type DeploymentResourceQuery,
type DeployResources,
} from "../adk/client";
-import {
- DeploymentSelect,
- type DeploymentSelectOption,
-} from "./DeploymentSelect";
import "./DeploymentResources.css";
-const RESOURCE_MODE_OPTIONS: DeploymentSelectOption[] = [
+const RESOURCE_MODE_OPTIONS: SelectOption[] = [
{
value: "auto",
label: "自动创建",
description: "部署时自动创建所需资源",
- badge: "推荐",
},
{
value: "create",
@@ -44,8 +45,6 @@ interface ResourceListState {
hasMore: boolean;
loading: boolean;
error: string | null;
- search: string;
- setSearch: (value: string) => void;
reload: () => void;
loadMore: () => void;
}
@@ -60,28 +59,12 @@ function useDeploymentResourceList(
const [hasMore, setHasMore] = useState(false);
const [loading, setLoading] = useState(false);
const [error, setError] = useState
(null);
- const [search, setSearch] = useState("");
- const [debouncedSearch, setDebouncedSearch] = useState("");
const [loadedQueryKey, setLoadedQueryKey] = useState("");
const [reloadKey, setReloadKey] = useState(0);
const loadingRef = useRef(false);
const requestRef = useRef(null);
const baseQueryKey = query ? JSON.stringify(query) : "";
- const queryKey = query
- ? JSON.stringify({ ...query, search: debouncedSearch })
- : "";
-
- useEffect(() => {
- const timer = window.setTimeout(() => {
- setDebouncedSearch(search.trim());
- }, 250);
- return () => window.clearTimeout(timer);
- }, [search]);
-
- useEffect(() => {
- setSearch("");
- setDebouncedSearch("");
- }, [baseQueryKey]);
+ const queryKey = baseQueryKey;
const loadPage = useCallback((nextPage: number, replace: boolean) => {
if (!queryKey) return;
@@ -147,9 +130,7 @@ function useDeploymentResourceList(
return () => requestRef.current?.abort();
}, [loadPage, queryKey, reloadKey]);
- const queryReady = Boolean(queryKey)
- && loadedQueryKey === queryKey
- && search.trim() === debouncedSearch;
+ const queryReady = Boolean(queryKey) && loadedQueryKey === queryKey;
const reload = useCallback(() => {
setLoadedQueryKey("");
setReloadKey((key) => key + 1);
@@ -166,8 +147,6 @@ function useDeploymentResourceList(
hasMore: queryReady ? hasMore : false,
loading: Boolean(queryKey) && (!queryReady || loading),
error,
- search,
- setSearch,
reload,
loadMore,
};
@@ -176,7 +155,7 @@ function useDeploymentResourceList(
function resourceOptions(
items: DeploymentResource[],
valueField: "id" | "name",
-): DeploymentSelectOption[] {
+): SelectOption[] {
return items.map((item) => ({
value: item[valueField],
label: item.name,
@@ -204,28 +183,55 @@ function ResourcePicker({
onChange: (resource: DeploymentResource) => void;
}) {
const options = useMemo(
- () => resourceOptions(state.items, valueField),
- [state.items, valueField],
+ () => {
+ const available = resourceOptions(state.items, valueField);
+ if (!value || available.some((option) => option.value === value)) {
+ return available;
+ }
+ return [
+ {
+ value,
+ label: valueLabel || value,
+ description: "当前选择",
+ },
+ ...available,
+ ];
+ },
+ [state.items, value, valueField, valueLabel],
);
+ const selectId = useId();
return (
-
{
+ searchEmptyMessage="未找到匹配资源"
+ actions={
+ state.hasMore
+ ? [
+ {
+ id: "load-more",
+ label: state.loading ? "正在加载…" : "加载更多",
+ onSelect: state.loadMore,
+ },
+ ]
+ : []
+ }
+ onChange={(option) => {
const resource = state.items.find(
- (item) => item[valueField] === selectedValue,
+ (item) => item[valueField] === option.value,
);
if (resource) onChange(resource);
}}
@@ -233,16 +239,19 @@ function ResourcePicker({
{state.error ? (
{state.error}
-
+
) : state.loading && state.items.length === 0 ? (
-
- {state.search.trim() ? "正在搜索云资源…" : "正在加载云资源…"}
-
+ 正在加载云资源…
) : state.items.length === 0 ? (
-
- {state.search.trim() ? "未找到匹配资源。" : "暂无可用资源。"}
-
+ 暂无可用资源。
) : state.serviceRegion ? (
实际服务区域:{state.serviceRegion} · 已加载 {state.items.length}
@@ -265,17 +274,24 @@ function ModeField({
onChange: (mode: DeploymentResourceMode) => void;
}) {
return (
-
+
);
}
@@ -293,16 +309,17 @@ function TextField({
onChange: (value: string) => void;
}) {
return (
-
- {label}
-
+ {label}
+ onChange(event.currentTarget.value)}
/>
-
+
);
}
diff --git a/frontend/src/ui/ProjectPreview.css b/frontend/src/ui/ProjectPreview.css
index 386eac4a2..ebcac0b2f 100644
--- a/frontend/src/ui/ProjectPreview.css
+++ b/frontend/src/ui/ProjectPreview.css
@@ -401,6 +401,116 @@
.pp-root.is-deploy.is-embedded {
--pp-publish-content-width: 100%;
+ overflow: hidden;
+}
+
+/* Figma 71:74878 — embedded Agent deployment keeps the canvas visible and
+ uses the 480px configuration drawer's spacing, typography and controls. */
+.pp-root.is-deploy.is-embedded .pp-body {
+ height: 100%;
+ min-height: 0;
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(420px, 480px);
+ grid-template-rows: minmax(0, 1fr);
+ overflow: hidden;
+}
+
+.pp-root.is-deploy.is-embedded .pp-release-overview {
+ min-height: 0;
+ overflow: hidden;
+ border-right: 1px solid hsl(var(--border) / 0.72);
+}
+
+.pp-root.is-deploy.is-embedded .pp-release-overview.is-embedded-canvas {
+ position: relative;
+ height: 100%;
+}
+
+.pp-root.is-deploy.is-embedded .pp-release-overview.is-embedded-canvas > .abc-root {
+ width: 100%;
+ height: 100%;
+ min-width: 0;
+ min-height: 0;
+ flex: 1 1 auto;
+ border: 0;
+}
+
+.pp-root.is-deploy.is-embedded .pp-release-overview.is-embedded-canvas > .abc-root .abc-canvas {
+ min-height: 0;
+}
+
+.pp-root.is-deploy.is-embedded .pp-config {
+ width: 100%;
+ height: 100%;
+ min-height: 0;
+ overflow: hidden;
+ background: hsl(var(--background));
+}
+
+.pp-root.is-deploy.is-embedded .pp-config-head {
+ width: 100%;
+ flex: 0 0 54px;
+ height: 54px;
+ margin: 0;
+ padding: 14px 24px;
+}
+
+.pp-root.is-deploy.is-embedded .pp-config-title {
+ font-size: 18px;
+ font-weight: 650;
+ line-height: 25px;
+}
+
+.pp-root.is-deploy.is-embedded .pp-config-scroll {
+ width: 100%;
+ flex: 1 1 auto;
+ margin: 0;
+ padding: 0 24px 32px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ overscroll-behavior: contain;
+}
+
+.pp-root.is-deploy.is-embedded .pp-config-section {
+ margin: 0 0 32px;
+ padding: 0;
+ overflow: visible;
+ border: 0;
+ border-radius: 0;
+ background: transparent;
+}
+
+.pp-root.is-deploy.is-embedded .pp-config-section:last-child {
+ margin-bottom: 0;
+}
+
+.pp-root.is-deploy.is-embedded .pp-config-label {
+ margin: 0 0 8px;
+ padding: 0;
+ border: 0;
+ background: transparent;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 22px;
+ letter-spacing: 0;
+}
+
+.pp-root.is-deploy.is-embedded .pp-env-head {
+ margin: 0 0 8px;
+ padding: 0;
+ border: 0;
+}
+
+.pp-root.is-deploy.is-embedded .pp-env-head .pp-config-label {
+ margin-bottom: 0;
+}
+
+.pp-root.is-deploy.is-embedded .pp-cloud-environment-section {
+ padding-bottom: 0;
+}
+
+.pp-root.is-deploy.is-embedded .pp-resource-grid {
+ grid-template-columns: minmax(0, 1fr);
}
.pp-root.is-deploy .pp-body {
@@ -422,23 +532,11 @@
background: transparent;
}
-.pp-root.is-deploy.has-primary-pane .pp-config-head,
-.pp-root.is-deploy.has-primary-pane .pp-config-actions {
+.pp-root.is-deploy.has-primary-pane .pp-config-head {
border: 0;
background: transparent;
}
-.pp-root.is-deploy.has-primary-pane .pp-config-actions {
- position: sticky;
- bottom: 0;
- width: var(--pp-publish-content-width);
- margin: 0 auto;
- justify-content: center;
- padding: 12px 0 18px;
- background: hsl(var(--background));
- transform: none;
-}
-
.pp-root.is-deploy.has-primary-pane .pp-deploy-hint {
position: absolute;
left: 18px;
@@ -466,11 +564,6 @@
padding: 8px 0 12px;
}
-.pp-release-preview.is-embedded {
- grid-template-columns: minmax(0, 1fr) 132px;
- align-items: stretch;
-}
-
.pp-flow-thumbnail {
position: relative;
height: 200px;
@@ -634,47 +727,6 @@
-webkit-line-clamp: 3;
}
-.pp-artifact-actions {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- margin-top: 18px;
- padding-top: 12px;
-}
-
-.pp-artifact-actions.is-rail {
- flex-direction: column;
- flex-wrap: nowrap;
- gap: 8px;
- margin: 0;
- padding: 0;
-}
-
-.pp-artifact-actions.is-rail .pp-secondary,
-.pp-artifact-actions.is-rail .code-browser-trigger {
- flex: 1 1 0;
- width: 100%;
- min-height: 36px;
- justify-content: center;
-}
-
-.pp-artifact-actions .pp-secondary,
-.pp-artifact-actions .code-browser-trigger {
- min-height: 34px;
- padding-inline: 12px;
- border: 0;
- border-radius: 7px;
- background: hsl(var(--secondary) / 0.58);
- box-shadow: none;
- color: hsl(var(--foreground));
- font-size: 13px;
-}
-
-.pp-artifact-actions .pp-secondary:hover,
-.pp-artifact-actions .code-browser-trigger:hover {
- background: hsl(var(--secondary));
-}
-
.pp-flow-backdrop {
--cw-workspace-ink: 222 24% 13%;
position: fixed;
@@ -800,25 +852,7 @@
display: block;
margin: 0 auto;
overflow: visible;
- padding: 0 0 88px;
-}
-
-.pp-config-actions {
- position: fixed;
- z-index: 40;
- left: calc((100vw + var(--pp-sidebar-width, 0px)) / 2);
- bottom: max(20px, env(safe-area-inset-bottom));
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 0;
- border: 0;
- background: transparent;
- transform: translateX(-50%);
-}
-
-.pp-config-actions.is-external {
- display: none;
+ padding: 0 0 32px;
}
.pp-config-section {
@@ -889,8 +923,7 @@
}
.pp-runtime-name-field {
- display: grid;
- gap: 6px;
+ display: flex;
}
.pp-runtime-name-field > .pp-config-note,
@@ -907,20 +940,58 @@
}
.pp-auth-fields {
- width: min(100%, 560px);
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- align-items: start;
- gap: 12px;
+ width: 100%;
}
-.pp-auth-fields > label {
+.pp-form-stack {
min-width: 0;
display: flex;
flex-direction: column;
- gap: 7px;
+ gap: 16px;
+}
+
+.pp-form-field {
+ min-width: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.pp-form-field > label,
+.pp-form-field > label:first-child {
+ color: hsl(var(--foreground));
+ font-size: 13px;
+ font-weight: 400;
+ line-height: 20px;
+}
+
+.pp-form-field > label small {
color: hsl(var(--muted-foreground));
- font-size: 12.5px;
+ font-size: 12px;
+ font-weight: 400;
+}
+
+.pp-form-field > :not(label):not(.pp-form-help):not(.pp-config-note):not(.pp-runtime-name-error) {
+ width: 100%;
+ min-width: 0;
+}
+
+.pp-config [data-variant="outline"][data-focused="true"] {
+ box-shadow:
+ 0 0 0 1px hsl(var(--ring) / 0.32) inset,
+ 0 0 0 3px hsl(var(--ring) / 0.08);
+}
+
+.pp-app-select-trigger {
+ width: 100%;
+ font-size: 13px;
+ font-weight: 400;
+}
+
+.pp-app-select-option,
+.pp-app-select-option[data-selected] {
+ font-size: 13px;
+ font-weight: 400;
}
.pp-github-cicd-title {
@@ -1244,18 +1315,6 @@
.pp-user-pool-error button {
flex-shrink: 0;
- padding: 0;
- border: 0;
- background: transparent;
- color: inherit;
- cursor: pointer;
- font: inherit;
- font-weight: 600;
-}
-
-.pp-user-pool-error button:focus-visible {
- outline: 2px solid hsl(var(--ring) / 0.45);
- outline-offset: 2px;
}
.pp-github-cicd-field:first-child {
@@ -1404,18 +1463,7 @@
}
.pp-instance-fields {
- display: grid;
- grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 12px;
-}
-
-.pp-instance-fields label {
- min-width: 0;
- display: flex;
- flex-direction: column;
- gap: 6px;
- color: hsl(var(--muted-foreground));
- font-size: 12.5px;
+ width: 100%;
}
.pp-instance-error {
@@ -1425,57 +1473,6 @@
line-height: 1.5;
}
-.pp-config-select,
-.pp-runtime-name-input,
-.pp-channel-fields input,
-.pp-github-cicd-field input,
-.pp-instance-fields input,
-.pp-network-fields input,
-.pp-env-row input,
-.pp-env-row textarea {
- width: 100%;
- min-width: 0;
- height: 34px;
- box-sizing: border-box;
- padding: 0 10px;
- border: 1px solid hsl(var(--border));
- border-radius: 5px;
- outline: none;
- background: hsl(var(--background));
- color: hsl(var(--foreground));
- font: inherit;
- font-size: 13px;
- transition:
- border-color 120ms ease,
- box-shadow 120ms ease;
-}
-
-.pp-channel-fields input {
- height: 30px;
- padding-inline: 8px;
- font-size: 11.5px;
-}
-
-.pp-config-select:focus,
-.pp-runtime-name-input:focus,
-.pp-channel-fields input:focus,
-.pp-github-cicd-field input:focus,
-.pp-instance-fields input:focus,
-.pp-network-fields input:focus,
-.pp-env-row input:focus,
-.pp-env-row textarea:focus {
- border-color: hsl(var(--ring) / 0.55);
- box-shadow: 0 0 0 2px hsl(var(--ring) / 0.08);
-}
-
-.pp-runtime-name-input[aria-invalid="true"] {
- border-color: hsl(var(--destructive) / 0.72);
-}
-
-.pp-runtime-name-input[aria-invalid="true"]:focus {
- box-shadow: 0 0 0 2px hsl(var(--destructive) / 0.1);
-}
-
.pp-runtime-name-error {
margin: -4px 0 10px;
color: hsl(var(--destructive));
@@ -1483,33 +1480,24 @@
line-height: 1.5;
}
-.pp-config-select:disabled,
-.pp-runtime-name-input:disabled,
-.pp-channel-fields input:disabled,
-.pp-github-cicd-field input:disabled,
-.pp-instance-fields input:disabled,
-.pp-network-fields input:disabled,
-.pp-env-row input:disabled,
-.pp-env-row textarea:disabled {
- opacity: 0.55;
-}
-
.pp-channel-card {
position: relative;
+ display: grid;
+ grid-template-rows: 112px;
width: clamp(154px, 33.333%, 236px);
max-width: 100%;
- height: 112px;
perspective: 1200px;
- transition: height 180ms ease;
+ transition: grid-template-rows 180ms ease;
}
.pp-channel-card.is-flipped {
- height: 176px;
+ grid-template-rows: 176px;
}
.pp-channel-card-inner {
width: 100%;
height: 100%;
+ min-height: 0;
position: relative;
transform-style: preserve-3d;
transition: transform 420ms cubic-bezier(0.22, 1, 0.36, 1);
@@ -1672,61 +1660,11 @@
}
.pp-network-layout {
- width: min(100%, 560px);
- display: grid;
- grid-template-columns: minmax(132px, 0.36fr) minmax(0, 0.64fr);
- align-items: start;
- gap: 24px;
-}
-
-.pp-network-region {
- position: relative;
- display: flex;
- flex-direction: column;
- gap: 7px;
- margin-bottom: 12px;
- color: hsl(var(--muted-foreground));
- font-size: 12.5px;
-}
-
-.pp-network-region.is-open {
- z-index: 80;
-}
-
-.pp-region-trigger {
width: 100%;
- height: 36px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 12px;
- padding: 0 11px;
- border: 1px solid hsl(var(--border));
- border-radius: 7px;
- background: hsl(var(--background));
- color: hsl(var(--foreground));
- font: inherit;
- font-size: 13px;
- cursor: pointer;
- transition: border-color 120ms ease, background-color 120ms ease;
}
-.pp-region-trigger:hover,
-.pp-region-trigger[aria-expanded="true"] {
- border-color: hsl(var(--foreground) / 0.22);
- background: hsl(var(--foreground) / 0.025);
-}
-
-.pp-region-trigger:focus-visible {
- outline: none;
- border-color: hsl(var(--ring));
- box-shadow: 0 0 0 3px hsl(var(--ring) / 0.12);
-}
-
-.pp-region-trigger:disabled {
- cursor: not-allowed;
- opacity: 0.58;
- background: hsl(var(--muted) / 0.32);
+.pp-network-region {
+ margin-bottom: 0;
}
.pp-region-help {
@@ -1735,128 +1673,8 @@
line-height: 1.5;
}
-.pp-region-chevron {
- width: 15px;
- height: 15px;
- color: hsl(var(--muted-foreground));
- transition: transform 150ms ease;
-}
-
-.pp-region-chevron.is-open {
- transform: rotate(180deg);
-}
-
-.pp-region-menu {
- position: absolute;
- top: calc(100% + 6px);
- right: 0;
- left: 0;
- z-index: 81;
- padding: 5px;
- border: 1px solid hsl(var(--border));
- border-radius: 10px;
- background: hsl(var(--panel));
- box-shadow: 0 12px 28px hsl(var(--foreground) / 0.12);
-}
-
-.pp-region-option {
- width: 100%;
- height: 36px;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 9px;
- border: 0;
- border-radius: 7px;
- background: transparent;
- color: hsl(var(--foreground));
- font: inherit;
- font-size: 13px;
- text-align: left;
- cursor: pointer;
-}
-
-.pp-region-option:hover,
-.pp-region-option:focus-visible,
-.pp-region-option.is-selected {
- outline: none;
- background: hsl(var(--foreground) / 0.055);
-}
-
-.pp-region-option.is-selected {
- font-weight: 600;
-}
-
-.pp-region-option svg {
- width: 15px;
- height: 15px;
- color: hsl(var(--primary));
-}
-
.pp-network-modes {
- display: flex;
- flex-direction: column;
- gap: 9px;
-}
-
-.pp-network-option {
- min-height: 28px;
- display: flex;
- align-items: center;
- gap: 9px;
- color: hsl(var(--muted-foreground));
- font-size: 12.5px;
- font-weight: 560;
- cursor: pointer;
-}
-
-.pp-network-option:has(input:checked) {
- color: hsl(var(--foreground));
-}
-
-.pp-network-option:has(input:disabled) {
- cursor: default;
- opacity: 0.58;
-}
-
-.pp-network-option input {
- width: 15px;
- height: 15px;
- flex: 0 0 15px;
- display: grid;
- place-items: center;
- margin: 0;
- appearance: none;
- border: 1px solid hsl(var(--border));
- border-radius: 50%;
- background: hsl(var(--background));
- cursor: inherit;
- transition: border-color 120ms ease, box-shadow 120ms ease;
-}
-
-.pp-network-option input::before {
- width: 7px;
- height: 7px;
- border-radius: 50%;
- background: hsl(var(--primary));
- content: "";
- transform: scale(0);
- transition: transform 120ms ease-out;
-}
-
-.pp-network-option input:checked {
- border-color: hsl(var(--primary));
-}
-
-.pp-network-option input:checked::before {
- transform: scale(1);
-}
-
-.pp-network-option input:focus-visible,
-.pp-network-check input:focus-visible,
-.pp-evaluation-set-option input:focus-visible {
- outline: 2px solid hsl(var(--ring) / 0.55);
- outline-offset: 2px;
+ width: 100%;
}
.pp-network-fields {
@@ -1866,26 +1684,15 @@
min-width: 0;
}
-.pp-network-fields label:not(.pp-network-check) {
- display: flex;
- flex-direction: column;
- gap: 6px;
- color: hsl(var(--muted-foreground));
- font-size: 12.5px;
-}
-
.pp-network-fields small {
font-size: 10.5px;
font-weight: 400;
}
.pp-network-check {
- display: flex;
- align-items: center;
- gap: 8px;
+ min-height: 32px;
color: hsl(var(--foreground));
- font-size: 12.5px;
- cursor: pointer;
+ font-size: 13px;
}
.pp-evaluation-set-option {
@@ -1915,48 +1722,6 @@
line-height: 1.5;
}
-.pp-network-check input,
-.pp-evaluation-set-option input {
- width: 16px;
- height: 16px;
- flex: 0 0 16px;
- display: grid;
- place-items: center;
- margin: 0;
- padding: 0;
- appearance: none;
- border: 1px solid hsl(var(--border));
- border-radius: 4px;
- background: hsl(var(--background));
- cursor: inherit;
- transition:
- border-color 120ms ease,
- background-color 120ms ease,
- box-shadow 120ms ease;
-}
-
-.pp-network-check input::before,
-.pp-evaluation-set-option input::before {
- width: 4px;
- height: 8px;
- border: solid hsl(var(--primary-foreground));
- border-width: 0 2px 2px 0;
- content: "";
- transform: translateY(-1px) rotate(45deg) scale(0);
- transition: transform 120ms ease-out;
-}
-
-.pp-network-check input:checked,
-.pp-evaluation-set-option input:checked {
- border-color: hsl(var(--primary));
- background: hsl(var(--primary));
-}
-
-.pp-network-check input:checked::before,
-.pp-evaluation-set-option input:checked::before {
- transform: translateY(-1px) rotate(45deg) scale(1);
-}
-
.pp-env-section {
width: 100%;
padding-bottom: 16px;
@@ -2519,12 +2284,16 @@
}
@media (prefers-reduced-motion: reduce) {
+ .pp-channel-card,
.pp-channel-card-inner,
.pp-channel-card-front,
- .pp-deployment-select-chevron,
- .pp-config-actions .pp-deploy {
+ .pp-deployment-select-chevron {
transition: none;
}
+
+ .spin {
+ animation: none;
+ }
}
@media (max-width: 1120px) {
@@ -2538,6 +2307,45 @@
}
}
+@media (max-width: 1000px) {
+ .pp-root.is-deploy.is-embedded {
+ overflow-x: hidden;
+ overflow-y: auto;
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-body {
+ height: auto;
+ display: flex;
+ flex-direction: column;
+ overflow: visible;
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-release-overview {
+ flex: 0 0 280px;
+ height: 280px;
+ border-right: 0;
+ border-bottom: 1px solid hsl(var(--border) / 0.72);
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-config {
+ width: min(100%, 480px);
+ height: auto;
+ min-height: 0;
+ align-self: center;
+ overflow: visible;
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-config-scroll {
+ overflow: visible;
+ padding: 0 20px 32px;
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-config-head {
+ padding-inline: 20px;
+ }
+
+}
+
@media (max-width: 860px) {
.layout {
--pp-sidebar-width: 204px;
@@ -2551,6 +2359,35 @@
--pp-publish-content-width: min(88%, calc(100% - 36px));
}
+ .pp-root.is-deploy.is-embedded {
+ overflow-x: hidden;
+ overflow-y: auto;
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-body {
+ height: auto;
+ display: flex;
+ overflow: visible;
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-release-overview {
+ flex: 0 0 280px;
+ height: 280px;
+ border-right: 0;
+ border-bottom: 1px solid hsl(var(--border) / 0.72);
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-config {
+ height: auto;
+ min-height: 0;
+ overflow: visible;
+ }
+
+ .pp-root.is-deploy.is-embedded .pp-config-scroll {
+ overflow: visible;
+ padding: 0 20px 32px;
+ }
+
.pp-toolbar {
align-items: flex-start;
flex-direction: column;
@@ -2600,11 +2437,7 @@
.pp-config-scroll {
padding-inline: 0;
- padding-bottom: 84px;
- }
-
- .pp-config-actions {
- bottom: max(14px, env(safe-area-inset-bottom));
+ padding-bottom: 32px;
}
.pp-flow-backdrop {
@@ -2613,22 +2446,6 @@
}
@media (max-width: 520px) {
- .pp-auth-fields,
- .pp-github-cicd-form,
- .pp-github-cicd-result-grid,
- .pp-github-cicd-error dl {
- grid-template-columns: minmax(0, 1fr);
- }
-
- .pp-github-cicd-submit {
- width: 100%;
- }
-
- .pp-network-layout {
- grid-template-columns: minmax(0, 1fr);
- gap: 16px;
- }
-
.pp-env-section {
width: 100%;
}
diff --git a/frontend/src/ui/ProjectPreview.tsx b/frontend/src/ui/ProjectPreview.tsx
index 893606fb7..a09b8ef72 100644
--- a/frontend/src/ui/ProjectPreview.tsx
+++ b/frontend/src/ui/ProjectPreview.tsx
@@ -11,16 +11,20 @@ import {
useState,
} from "react";
import { createPortal } from "react-dom";
+import { Button } from "@openai/apps-sdk-ui/components/Button";
+import { Checkbox } from "@openai/apps-sdk-ui/components/Checkbox";
+import { Input } from "@openai/apps-sdk-ui/components/Input";
+import {
+ Select,
+ type Option as SelectOption,
+} from "@openai/apps-sdk-ui/components/Select";
+import { Textarea } from "@openai/apps-sdk-ui/components/Textarea";
import {
AlertTriangle,
ArrowLeft,
- Check,
- ChevronDown,
ChevronRight,
- Download,
ExternalLink,
File,
- FileDown,
FilePlus,
Folder,
Loader2,
@@ -90,7 +94,6 @@ import {
} from "../adk/client";
import {
beginAgentDeploy,
- beginAgentSourceDownload,
classifyTelemetryError,
type AgentDeployFailedProps,
type AgentDeployStartedProps,
@@ -102,8 +105,6 @@ import {
type CloudProvider,
} from "../adk/cloudProvider";
import feishuLogo from "../assets/feishu-logo.svg";
-import { buildZip } from "./zip";
-import { ProjectCodeBrowser } from "./CodeBrowserDialog";
import { DeploymentErrorMessage } from "./DeploymentErrorMessage";
import {
DEFAULT_DEPLOY_RESOURCES,
@@ -111,10 +112,6 @@ import {
deploymentResourcesError,
} from "./DeploymentResources";
-import {
- DeploymentSelect,
- type DeploymentSelectOption,
-} from "./DeploymentSelect";
import { mergeDeployBuildLog } from "./deployBuildLog";
import {
GithubCicdPanel,
@@ -327,8 +324,9 @@ function IdentityUserPoolSelect({
.map((pool) => ({
value: pool.uid,
label: pool.name.trim() || "未命名用户池",
- description: pool.domain || pool.uid,
- badge: pool.isCurrent ? "当前用户池" : undefined,
+ description: pool.isCurrent
+ ? `当前用户池 · ${pool.domain || pool.uid}`
+ : pool.domain || pool.uid,
})),
[pools],
);
@@ -336,26 +334,37 @@ function IdentityUserPoolSelect({
return (