Refine playground composer and settings layout
This commit is contained in:
parent
a4f731af88
commit
bdc9be63d5
@ -1,7 +1,7 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import type { GatewayApiKey, GatewayTask, PlatformModel } from '@easyai-ai-gateway/contracts';
|
||||
import { ChevronDown, MessageSquarePlus, Send, Sparkles } from 'lucide-react';
|
||||
import { Badge, Button, Select, Textarea } from '../components/ui';
|
||||
import { ArrowUp, ChevronDown, MessageSquarePlus, Settings2, Sparkles } from 'lucide-react';
|
||||
import { Badge, Button, FormDialog, Select, Textarea } from '../components/ui';
|
||||
import { createImageEditTask, createImageGenerationTask, createVideoGenerationTask, pollTaskUntilSettled, resolveApiAssetUrl, taskIsPending } from '../api';
|
||||
import type { PlaygroundMode } from '../types';
|
||||
import {
|
||||
@ -48,7 +48,6 @@ import {
|
||||
modeOptions,
|
||||
modelOptionLabel,
|
||||
placeholderByMode,
|
||||
quickPrompts,
|
||||
resolveSelectedApiKeyId,
|
||||
videoModeOptions,
|
||||
type ModelOption,
|
||||
@ -96,6 +95,7 @@ export function PlaygroundPage(props: {
|
||||
const [mediaUploadMessage, setMediaUploadMessage] = useState('');
|
||||
const [mediaUploads, setMediaUploads] = useState<PlaygroundUpload[]>([]);
|
||||
const [mediaUploading, setMediaUploading] = useState(false);
|
||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||
const isMountedRef = useRef(false);
|
||||
const pendingMediaModelRef = useRef('');
|
||||
const resumedTaskIdsRef = useRef(new Set<string>());
|
||||
@ -288,6 +288,12 @@ export function PlaygroundPage(props: {
|
||||
|
||||
setMediaRuns((current) => [...current, run]);
|
||||
setMediaMessage('');
|
||||
if (!overrides) {
|
||||
setPrompt('');
|
||||
setMediaUploads([]);
|
||||
setMediaUploadMessage('');
|
||||
setImageHasReference(false);
|
||||
}
|
||||
try {
|
||||
const requestPrompt = replacePlaygroundResourceTokens(trimmedPrompt, runUploads, runMode);
|
||||
let response: { task: GatewayTask; next: Record<string, string> };
|
||||
@ -310,11 +316,6 @@ export function PlaygroundPage(props: {
|
||||
: await createImageGenerationTask(credential, requestPayload);
|
||||
}
|
||||
setMediaRuns((current) => updateMediaRun(current, localId, { status: response.task.status, task: response.task }));
|
||||
if (!overrides) {
|
||||
setMediaUploads([]);
|
||||
setMediaUploadMessage('');
|
||||
setImageHasReference(false);
|
||||
}
|
||||
void pollMediaRunUntilSettled(credential, localId, response.task);
|
||||
} catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : '生成任务提交失败';
|
||||
@ -397,12 +398,9 @@ export function PlaygroundPage(props: {
|
||||
|
||||
const mediaComposer = props.mode === 'chat' ? null : (
|
||||
<Composer
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
mode={props.mode}
|
||||
modelOptions={modelOptions}
|
||||
prompt={prompt}
|
||||
selectedApiKeyId={activeApiKeyId}
|
||||
selectedModel={selectedModel}
|
||||
imageHasReference={effectiveImageHasReference}
|
||||
mediaSettings={mediaSettings}
|
||||
@ -412,8 +410,6 @@ export function PlaygroundPage(props: {
|
||||
uploads={mediaUploads}
|
||||
uploading={mediaUploading}
|
||||
videoMode={videoMode}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
onCreateApiKey={props.onCreateApiKey}
|
||||
onImageReferenceChange={setImageHasReference}
|
||||
onMediaSettingsChange={setMediaSettings}
|
||||
onModeChange={props.onModeChange}
|
||||
@ -445,13 +441,19 @@ export function PlaygroundPage(props: {
|
||||
<strong>开启创作</strong>
|
||||
<Badge variant="secondary">Test</Badge>
|
||||
</div>
|
||||
<button type="button" className="playgroundSideItem active" onClick={startNewThread}>
|
||||
<MessageSquarePlus size={15} />
|
||||
新对话
|
||||
</button>
|
||||
<button type="button" className="playgroundSideItem">
|
||||
<Sparkles size={15} />
|
||||
默认创作
|
||||
<div className="playgroundSidebarNav">
|
||||
<button type="button" className="playgroundSideItem active" onClick={startNewThread}>
|
||||
<MessageSquarePlus size={15} />
|
||||
新对话
|
||||
</button>
|
||||
<button type="button" className="playgroundSideItem">
|
||||
<Sparkles size={15} />
|
||||
默认创作
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" className="playgroundSideItem playgroundSettingsButton" onClick={() => setSettingsOpen(true)}>
|
||||
<Settings2 size={15} />
|
||||
设置
|
||||
</button>
|
||||
</aside>
|
||||
|
||||
@ -490,10 +492,70 @@ export function PlaygroundPage(props: {
|
||||
)}
|
||||
</section>
|
||||
</main>
|
||||
<PlaygroundSettingsDialog
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
open={settingsOpen}
|
||||
selectedApiKeyId={activeApiKeyId}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
onClose={() => setSettingsOpen(false)}
|
||||
onCreateApiKey={() => {
|
||||
setSettingsOpen(false);
|
||||
props.onCreateApiKey();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function PlaygroundSettingsDialog(props: {
|
||||
apiKeySecretsById: Record<string, string>;
|
||||
apiKeys: GatewayApiKey[];
|
||||
open: boolean;
|
||||
selectedApiKeyId: string;
|
||||
onApiKeyChange: (apiKeyId: string) => void;
|
||||
onClose: () => void;
|
||||
onCreateApiKey: () => void;
|
||||
}) {
|
||||
const notice = apiKeyNoticeText(props.apiKeys, props.apiKeySecretsById);
|
||||
return (
|
||||
<FormDialog
|
||||
bodyClassName="playgroundSettingsDialogBody"
|
||||
className="playgroundSettingsDialog"
|
||||
eyebrow="在线测试"
|
||||
footer={<Button type="submit">完成</Button>}
|
||||
open={props.open}
|
||||
title="设置"
|
||||
onClose={props.onClose}
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
props.onClose();
|
||||
}}
|
||||
>
|
||||
<label className="playgroundSettingsField">
|
||||
<span className="playgroundSettingsFieldCopy">
|
||||
<strong>API Key</strong>
|
||||
<small>在线测试调用凭证</small>
|
||||
</span>
|
||||
<ApiKeySelect
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
selectedApiKeyId={props.selectedApiKeyId}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
/>
|
||||
</label>
|
||||
{notice && (
|
||||
<div className="playgroundSettingsNotice">
|
||||
<span>{notice}</span>
|
||||
<Button type="button" size="sm" variant="secondary" onClick={props.onCreateApiKey}>
|
||||
创建 API Key
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</FormDialog>
|
||||
);
|
||||
}
|
||||
|
||||
export function PlaygroundEntry(props: {
|
||||
onModeChange: (mode: PlaygroundMode) => void;
|
||||
}) {
|
||||
@ -566,8 +628,6 @@ export function PublicWorksGallery() {
|
||||
}
|
||||
|
||||
function Composer(props: {
|
||||
apiKeySecretsById?: Record<string, string>;
|
||||
apiKeys?: GatewayApiKey[];
|
||||
compact?: boolean;
|
||||
imageHasReference?: boolean;
|
||||
mediaCapabilities?: MediaModelCapabilities;
|
||||
@ -575,15 +635,12 @@ function Composer(props: {
|
||||
mode: PlaygroundMode;
|
||||
modelOptions: ModelOption[];
|
||||
prompt: string;
|
||||
selectedApiKeyId?: string;
|
||||
selectedModel?: string;
|
||||
uploadAccept?: string;
|
||||
uploadMessage?: string;
|
||||
uploads?: PlaygroundUpload[];
|
||||
uploading?: boolean;
|
||||
videoMode?: VideoCreateMode;
|
||||
onApiKeyChange?: (apiKeyId: string) => void;
|
||||
onCreateApiKey?: () => void;
|
||||
onImageReferenceChange?: (value: boolean) => void;
|
||||
onMediaSettingsChange?: (settings: MediaGenerationSettings) => void;
|
||||
onModeChange: (mode: PlaygroundMode) => void;
|
||||
@ -595,8 +652,6 @@ function Composer(props: {
|
||||
onUploadFiles?: (files: File[], targetRole?: PlaygroundUploadRole) => void;
|
||||
onVideoModeChange?: (value: VideoCreateMode) => void;
|
||||
}) {
|
||||
const quickItems = quickPrompts[props.mode];
|
||||
const apiKeyNotice = props.apiKeys && props.apiKeySecretsById ? apiKeyNoticeText(props.apiKeys, props.apiKeySecretsById) : '';
|
||||
const hasMediaReferencePicker = props.mode !== 'chat' && Boolean(props.onUploadFiles);
|
||||
const mediaReferenceMessage = hasMediaReferencePicker
|
||||
? props.uploadMessage || sharedMediaUploadSummaryMessage(props.uploads ?? [], props.mode, props.videoMode ?? 'text_to_video')
|
||||
@ -672,24 +727,12 @@ function Composer(props: {
|
||||
onChange={props.onMediaSettingsChange}
|
||||
/>
|
||||
)}
|
||||
{props.apiKeys && props.apiKeySecretsById && props.onApiKeyChange && (
|
||||
<ApiKeySelect
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
selectedApiKeyId={props.selectedApiKeyId ?? ''}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
/>
|
||||
)}
|
||||
{apiKeyNotice && props.onCreateApiKey && (
|
||||
<Button type="button" size="sm" variant="secondary" onClick={props.onCreateApiKey}>
|
||||
创建 API Key
|
||||
</Button>
|
||||
)}
|
||||
<div className="composerQuickPrompts">
|
||||
{quickItems.map((item) => <button type="button" key={item} onClick={() => props.onPromptChange(item)}>{item}</button>)}
|
||||
</div>
|
||||
<Button type="button" size="icon" aria-label="发送测试" onClick={props.onSubmit}>
|
||||
<Send size={15} />
|
||||
<span className="composerEstimatedCharge" aria-label="预计扣费 1 / 张">
|
||||
<Sparkles size={14} />
|
||||
<span>1 / 张</span>
|
||||
</span>
|
||||
<Button type="button" size="icon" className="composerMediaSendButton" aria-label="发送测试" onClick={props.onSubmit}>
|
||||
<ArrowUp size={24} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -33,7 +33,6 @@ import {
|
||||
type PlaygroundUpload,
|
||||
} from './playground-upload';
|
||||
import {
|
||||
ApiKeySelect,
|
||||
ModeSwitch,
|
||||
PlaygroundGreeting,
|
||||
apiKeyNoticeText,
|
||||
@ -175,12 +174,8 @@ export function AssistantChatPlayground(props: {
|
||||
<ThreadPrimitive.Empty>
|
||||
<div className="assistantEmptyStage">
|
||||
<AssistantEmptyState
|
||||
apiKeyNotice={apiKeyNotice}
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
canRun={canRun}
|
||||
modelOptions={props.modelOptions}
|
||||
selectedApiKeyId={activeApiKeyId}
|
||||
selectedModel={props.selectedModel}
|
||||
token={props.token}
|
||||
activeApiKeySecret={activeApiKeySecret}
|
||||
@ -188,8 +183,6 @@ export function AssistantChatPlayground(props: {
|
||||
uploadMessage={chatUploadMessage}
|
||||
uploads={chatUploads}
|
||||
uploading={chatUploading}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
onCreateApiKey={props.onCreateApiKey}
|
||||
onModeChange={props.onModeChange}
|
||||
onModelChange={props.onModelChange}
|
||||
onRemoveUpload={(id) => setChatUploads((current) => current.filter((item) => item.id !== id))}
|
||||
@ -219,21 +212,15 @@ export function AssistantChatPlayground(props: {
|
||||
</div>
|
||||
<ThreadPrimitive.ViewportFooter className="assistantComposerDock">
|
||||
<AssistantChatComposer
|
||||
apiKeyNotice={apiKeyNotice}
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
canRun={canRun}
|
||||
docked
|
||||
modelOptions={props.modelOptions}
|
||||
placeholder={assistantPlaceholder(props.token, props.selectedModel, activeApiKeySecret)}
|
||||
selectedApiKeyId={activeApiKeyId}
|
||||
selectedModel={props.selectedModel}
|
||||
uploadAccept={sharedChatUploadAccept}
|
||||
uploadMessage={chatUploadMessage}
|
||||
uploads={chatUploads}
|
||||
uploading={chatUploading}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
onCreateApiKey={props.onCreateApiKey}
|
||||
onModeChange={props.onModeChange}
|
||||
onModelChange={props.onModelChange}
|
||||
onRemoveUpload={(id) => setChatUploads((current) => current.filter((item) => item.id !== id))}
|
||||
@ -265,20 +252,14 @@ function AssistantChatPersistenceBridge(props: { storedMessagesById: StoredOpenA
|
||||
|
||||
function AssistantEmptyState(props: {
|
||||
activeApiKeySecret: string;
|
||||
apiKeyNotice: string;
|
||||
apiKeySecretsById: Record<string, string>;
|
||||
apiKeys: GatewayApiKey[];
|
||||
canRun: boolean;
|
||||
modelOptions: ModelOption[];
|
||||
selectedApiKeyId: string;
|
||||
selectedModel: string;
|
||||
token: string;
|
||||
uploadAccept: string;
|
||||
uploadMessage: string;
|
||||
uploads: PlaygroundUpload[];
|
||||
uploading: boolean;
|
||||
onApiKeyChange: (apiKeyId: string) => void;
|
||||
onCreateApiKey: () => void;
|
||||
onModeChange: (mode: PlaygroundMode) => void;
|
||||
onModelChange: (value: string) => void;
|
||||
onRemoveUpload: (id: string) => void;
|
||||
@ -292,20 +273,14 @@ function AssistantEmptyState(props: {
|
||||
<ModeSwitch activeMode="chat" onModeChange={props.onModeChange} />
|
||||
<PlaygroundGreeting activeMode={activeMode} />
|
||||
<AssistantChatComposer
|
||||
apiKeyNotice={props.apiKeyNotice}
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
canRun={props.canRun}
|
||||
modelOptions={props.modelOptions}
|
||||
placeholder={placeholder}
|
||||
selectedApiKeyId={props.selectedApiKeyId}
|
||||
selectedModel={props.selectedModel}
|
||||
uploadAccept={props.uploadAccept}
|
||||
uploadMessage={props.uploadMessage}
|
||||
uploads={props.uploads}
|
||||
uploading={props.uploading}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
onCreateApiKey={props.onCreateApiKey}
|
||||
onModeChange={props.onModeChange}
|
||||
onModelChange={props.onModelChange}
|
||||
onRemoveUpload={props.onRemoveUpload}
|
||||
@ -316,21 +291,15 @@ function AssistantEmptyState(props: {
|
||||
}
|
||||
|
||||
function AssistantChatComposer(props: {
|
||||
apiKeyNotice: string;
|
||||
apiKeySecretsById: Record<string, string>;
|
||||
apiKeys: GatewayApiKey[];
|
||||
canRun: boolean;
|
||||
docked?: boolean;
|
||||
modelOptions: ModelOption[];
|
||||
placeholder: string;
|
||||
selectedApiKeyId: string;
|
||||
selectedModel: string;
|
||||
uploadAccept?: string;
|
||||
uploadMessage?: string;
|
||||
uploads?: PlaygroundUpload[];
|
||||
uploading?: boolean;
|
||||
onApiKeyChange: (apiKeyId: string) => void;
|
||||
onCreateApiKey: () => void;
|
||||
onModeChange: (mode: PlaygroundMode) => void;
|
||||
onModelChange: (value: string) => void;
|
||||
onRemoveUpload?: (id: string) => void;
|
||||
@ -375,17 +344,6 @@ function AssistantChatComposer(props: {
|
||||
<option value={item.value} key={item.value}>{modelOptionLabel(item)}</option>
|
||||
)) : <option value="">没有可用模型</option>}
|
||||
</Select>
|
||||
<ApiKeySelect
|
||||
apiKeySecretsById={props.apiKeySecretsById}
|
||||
apiKeys={props.apiKeys}
|
||||
selectedApiKeyId={props.selectedApiKeyId}
|
||||
onApiKeyChange={props.onApiKeyChange}
|
||||
/>
|
||||
{props.apiKeyNotice && (
|
||||
<Button type="button" size="sm" variant="secondary" onClick={props.onCreateApiKey}>
|
||||
创建 API Key
|
||||
</Button>
|
||||
)}
|
||||
<ComposerPrimitive.Send className="composerSendButton" disabled={!props.canRun} aria-label="发送消息">
|
||||
<Send size={18} />
|
||||
</ComposerPrimitive.Send>
|
||||
|
||||
@ -613,23 +613,42 @@
|
||||
width: min(260px, 26vw);
|
||||
}
|
||||
|
||||
.composerQuickPrompts {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
.composerEstimatedCharge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
overflow: hidden;
|
||||
margin-left: auto;
|
||||
color: #526170;
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-medium);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.composerQuickPrompts button {
|
||||
min-height: 32px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid var(--border);
|
||||
.composerEstimatedCharge svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
color: #526170;
|
||||
}
|
||||
|
||||
.composerMediaSendButton.shButton {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
background: var(--surface);
|
||||
color: var(--text-soft);
|
||||
font-size: var(--font-size-xs);
|
||||
white-space: nowrap;
|
||||
background: #0d0f14;
|
||||
color: #fff;
|
||||
box-shadow: 0 10px 28px rgba(13, 15, 20, 0.18);
|
||||
}
|
||||
|
||||
.composerMediaSendButton.shButton:hover {
|
||||
background: #1b1f27;
|
||||
}
|
||||
|
||||
.composerMediaSendButton.shButton svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
stroke-width: 2.5;
|
||||
}
|
||||
|
||||
.playgroundModeCards {
|
||||
@ -801,8 +820,8 @@
|
||||
}
|
||||
|
||||
.playgroundSidebar {
|
||||
display: grid;
|
||||
align-content: start;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
padding: 22px 16px;
|
||||
border-right: 1px solid var(--border);
|
||||
@ -816,6 +835,11 @@
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.playgroundSidebarNav {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.playgroundSideItem {
|
||||
display: flex;
|
||||
min-height: 38px;
|
||||
@ -834,6 +858,75 @@
|
||||
background: var(--surface-muted);
|
||||
}
|
||||
|
||||
.playgroundSettingsButton {
|
||||
margin-top: auto;
|
||||
border-top: 1px solid var(--border);
|
||||
border-radius: 0;
|
||||
padding-top: 12px;
|
||||
}
|
||||
|
||||
.playgroundSettingsButton:hover {
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.playgroundSettingsDialog {
|
||||
width: min(640px, calc(100vw - 32px));
|
||||
}
|
||||
|
||||
.playgroundSettingsDialogBody {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 14px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.playgroundSettingsField {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(260px, 360px);
|
||||
gap: 24px;
|
||||
align-items: center;
|
||||
padding: 18px 24px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.playgroundSettingsFieldCopy {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.playgroundSettingsFieldCopy strong {
|
||||
color: var(--text-strong);
|
||||
font-size: var(--font-size-base);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.playgroundSettingsFieldCopy small {
|
||||
color: var(--muted-foreground);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: var(--line-height-normal);
|
||||
}
|
||||
|
||||
.playgroundSettingsField .playgroundApiKeySelect {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.playgroundSettingsNotice {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
margin: 16px 24px;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-md);
|
||||
background: var(--surface-muted);
|
||||
color: var(--text-soft);
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: var(--line-height-relaxed);
|
||||
}
|
||||
|
||||
.playgroundSettingsNotice .shButton {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.playgroundStage {
|
||||
display: grid;
|
||||
align-items: stretch;
|
||||
@ -1424,7 +1517,7 @@
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
margin: auto auto 0;
|
||||
padding: 18px 0 24px;
|
||||
padding: 12px 0 14px;
|
||||
background: linear-gradient(180deg, rgba(250, 250, 250, 0), var(--surface-subtle) 40%);
|
||||
}
|
||||
|
||||
@ -1824,6 +1917,8 @@
|
||||
}
|
||||
|
||||
.mediaTaskPage {
|
||||
--media-task-max-width: 1120px;
|
||||
--media-composer-max-width: 960px;
|
||||
display: grid;
|
||||
grid-template-rows: minmax(0, 1fr) auto;
|
||||
width: 100%;
|
||||
@ -1843,18 +1938,18 @@
|
||||
}
|
||||
|
||||
.mediaTaskTimeline h1 {
|
||||
width: min(1240px, 100%);
|
||||
width: min(var(--media-task-max-width), 100%);
|
||||
font-size: 1.875rem;
|
||||
}
|
||||
|
||||
.mediaTaskTimeline > .playgroundError {
|
||||
width: min(1240px, 100%);
|
||||
width: min(var(--media-task-max-width), 100%);
|
||||
}
|
||||
|
||||
.mediaTaskItem {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
width: min(1240px, 100%);
|
||||
width: min(var(--media-task-max-width), 100%);
|
||||
}
|
||||
|
||||
.mediaTaskHeader {
|
||||
@ -1988,12 +2083,11 @@
|
||||
height: var(--task-reference-height);
|
||||
margin: 2px 2px 2px 0;
|
||||
vertical-align: middle;
|
||||
transition: width 180ms ease;
|
||||
transition: z-index 120ms ease;
|
||||
}
|
||||
|
||||
.mediaTaskReferenceStack:hover {
|
||||
z-index: 14;
|
||||
width: min(calc(var(--reference-count) * 58px), calc(100vw - 96px));
|
||||
}
|
||||
|
||||
.mediaTaskReferenceCard {
|
||||
@ -2285,12 +2379,12 @@
|
||||
}
|
||||
|
||||
.mediaComposerDock {
|
||||
padding: 16px 40px 24px;
|
||||
padding: 10px 40px 14px;
|
||||
background: linear-gradient(180deg, rgba(250, 250, 250, 0), var(--surface-subtle) 26%);
|
||||
}
|
||||
|
||||
.mediaComposerDock .playgroundComposer {
|
||||
width: min(1240px, 100%);
|
||||
width: min(var(--media-composer-max-width), 100%);
|
||||
min-height: 190px;
|
||||
margin: 0 auto;
|
||||
border-radius: 26px;
|
||||
@ -2450,15 +2544,11 @@
|
||||
}
|
||||
|
||||
.mediaComposerDock {
|
||||
padding: 12px 18px 20px;
|
||||
padding: 10px 18px 14px;
|
||||
}
|
||||
|
||||
.assistantComposerDock {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.composerQuickPrompts {
|
||||
flex-wrap: wrap;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
|
||||
.publicWorksMasonry {
|
||||
@ -2467,6 +2557,16 @@
|
||||
}
|
||||
|
||||
@media (max-width: 560px) {
|
||||
.playgroundSettingsField {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.playgroundSettingsNotice {
|
||||
margin: 14px 16px;
|
||||
}
|
||||
|
||||
.mediaAspectGrid,
|
||||
.mediaCountGrid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user