WIP: AGC 资源画布与替换改造 V3.0 #316
+46
-29
@@ -1,8 +1,7 @@
|
||||
import { ArrowUp, Loader2 } from 'lucide-react';
|
||||
import { ArrowUp, AtSign, Loader2 } from 'lucide-react';
|
||||
import type {
|
||||
ComponentProps,
|
||||
FormEventHandler,
|
||||
Ref,
|
||||
RefObject,
|
||||
UIEventHandler,
|
||||
} from 'react';
|
||||
@@ -70,7 +69,7 @@ type ProjectSupervisorViewProps = RuntimePanelProps & {
|
||||
chatInput: string;
|
||||
chatReferences: ChatReference[];
|
||||
chatProjectAssets: import('../../../../../packages/shared/src/contracts/gameCreationApp').GameCreationAppAssetManifestEntry[];
|
||||
composerRef?: Ref<ResourceReferenceInputHandle>;
|
||||
composerRef?: RefObject<ResourceReferenceInputHandle | null>;
|
||||
directCodex?: boolean;
|
||||
directStatus?: GameCreatorDirectTurnUpdateStatus | null;
|
||||
directProcessDetail?: string;
|
||||
@@ -162,6 +161,24 @@ export function ProjectSupervisorView({
|
||||
const [modelValidating, setModelValidating] = useState(false);
|
||||
const modelSelectRef = useRef<ConversationModelSelectHandle>(null);
|
||||
const modelValidateInFlightRef = useRef(false);
|
||||
const submitButton = (
|
||||
<button
|
||||
type="submit"
|
||||
aria-label={submitLabel}
|
||||
title={submitLabel}
|
||||
disabled={
|
||||
runtimePanelProps.controlBusy ||
|
||||
needsUserInput ||
|
||||
(directCodex && (!modelReady || modelValidating))
|
||||
}
|
||||
>
|
||||
{submitting ? (
|
||||
<Loader2 size={16} aria-hidden="true" className="animate-spin" />
|
||||
) : (
|
||||
<ArrowUp size={16} aria-hidden="true" />
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
return (
|
||||
<section
|
||||
className={`project-supervisor-surface${directCodex ? ' is-direct-codex' : ''}`}
|
||||
@@ -336,7 +353,9 @@ export function ProjectSupervisorView({
|
||||
</div>
|
||||
) : null}
|
||||
<form
|
||||
className="project-supervisor-composer"
|
||||
className={`project-supervisor-composer${
|
||||
directCodex ? ' is-direct-codex' : ''
|
||||
}`}
|
||||
onSubmit={(event) => {
|
||||
if (!directCodex) {
|
||||
onSubmit(event);
|
||||
@@ -371,6 +390,7 @@ export function ProjectSupervisorView({
|
||||
rows={3}
|
||||
value={chatInput}
|
||||
references={chatReferences}
|
||||
showTriggerButton={!directCodex}
|
||||
placeholder={
|
||||
directCodex
|
||||
? '告诉陶泥儿接下来要做什么,或输入 @ 选择资源'
|
||||
@@ -379,31 +399,28 @@ export function ProjectSupervisorView({
|
||||
onChange={onChatInputChange}
|
||||
/>
|
||||
{directCodex ? (
|
||||
<ConversationModelSelect
|
||||
ref={modelSelectRef}
|
||||
// 允许在对话进行中切换模型:写回的是客户端配置,只影响后续轮次,
|
||||
// 当前回合不受影响;发送按钮仍由 controlBusy / modelReady 把关。
|
||||
disabled={needsUserInput}
|
||||
onReady={setModelReady}
|
||||
projectPath={projectPath}
|
||||
/>
|
||||
) : null}
|
||||
<button
|
||||
type="submit"
|
||||
aria-label={submitLabel}
|
||||
title={submitLabel}
|
||||
disabled={
|
||||
runtimePanelProps.controlBusy ||
|
||||
needsUserInput ||
|
||||
(directCodex && (!modelReady || modelValidating))
|
||||
}
|
||||
>
|
||||
{submitting ? (
|
||||
<Loader2 size={16} aria-hidden="true" className="animate-spin" />
|
||||
) : (
|
||||
<ArrowUp size={16} aria-hidden="true" />
|
||||
)}
|
||||
</button>
|
||||
<div className="project-supervisor-composer-controls">
|
||||
<ConversationModelSelect
|
||||
ref={modelSelectRef}
|
||||
disabled={runtimePanelProps.controlBusy || needsUserInput}
|
||||
onReady={setModelReady}
|
||||
projectPath={projectPath}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="project-supervisor-reference-trigger"
|
||||
aria-label="插入素材引用"
|
||||
title="插入素材引用"
|
||||
disabled={runtimePanelProps.controlBusy || needsUserInput}
|
||||
onClick={() => composerRef?.current?.openPicker()}
|
||||
>
|
||||
<AtSign size={15} aria-hidden="true" />
|
||||
</button>
|
||||
{submitButton}
|
||||
</div>
|
||||
) : (
|
||||
submitButton
|
||||
)}
|
||||
</form>
|
||||
<small className="project-supervisor-workspace-status">
|
||||
{projectWorkspaceStatusForDisplay(workspaceStatus)}
|
||||
|
||||
+27
-19
@@ -84,11 +84,13 @@ type ResourceReferenceInputProps = {
|
||||
ariaLabel: string;
|
||||
multiline?: boolean;
|
||||
rows?: number;
|
||||
showTriggerButton?: boolean;
|
||||
inputRef?: RefObject<HTMLDivElement | null>;
|
||||
};
|
||||
|
||||
export type ResourceReferenceInputHandle = {
|
||||
insertReferences: (references: ChatReference[]) => void;
|
||||
openPicker: () => void;
|
||||
focus: () => void;
|
||||
};
|
||||
|
||||
@@ -184,6 +186,7 @@ function ResourceReferenceEditor({
|
||||
ariaLabel,
|
||||
multiline,
|
||||
rows,
|
||||
showTriggerButton = true,
|
||||
inputRef,
|
||||
composerRef,
|
||||
}: ResourceReferenceInputProps & {
|
||||
@@ -254,13 +257,21 @@ function ResourceReferenceEditor({
|
||||
[editor],
|
||||
);
|
||||
|
||||
const openPicker = useCallback(() => {
|
||||
setPickerQuery('');
|
||||
setPickerFilter('all');
|
||||
setSelectedResourceIds([]);
|
||||
setPickerOpen(true);
|
||||
}, []);
|
||||
|
||||
useImperativeHandle(
|
||||
composerRef,
|
||||
() => ({
|
||||
insertReferences,
|
||||
openPicker,
|
||||
focus: () => editor.focus(),
|
||||
}),
|
||||
[editor, insertReferences],
|
||||
[editor, insertReferences, openPicker],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -459,24 +470,21 @@ function ResourceReferenceEditor({
|
||||
}
|
||||
ErrorBoundary={LexicalErrorBoundary}
|
||||
/>
|
||||
<div className="resource-reference-input-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="resource-reference-input-at"
|
||||
aria-label="插入素材引用"
|
||||
title="插入素材引用"
|
||||
disabled={disabled}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={() => {
|
||||
setPickerQuery('');
|
||||
setPickerFilter('all');
|
||||
setSelectedResourceIds([]);
|
||||
setPickerOpen(true);
|
||||
}}
|
||||
>
|
||||
<AtSign size={15} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
{showTriggerButton ? (
|
||||
<div className="resource-reference-input-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="resource-reference-input-at"
|
||||
aria-label="插入素材引用"
|
||||
title="插入素材引用"
|
||||
disabled={disabled}
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
onClick={openPicker}
|
||||
>
|
||||
<AtSign size={15} aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
<LexicalTypeaheadMenuPlugin<ResourceMentionOption>
|
||||
options={mentionOptions}
|
||||
triggerFn={triggerFn}
|
||||
|
||||
@@ -9032,6 +9032,80 @@ iframe.preview-frame {
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
|
||||
/* Direct Codex composer: keep the editor and its actions in one vertical
|
||||
structure instead of positioning each control independently. */
|
||||
.game-workbench-chat
|
||||
.project-supervisor-surface.is-direct-codex
|
||||
.project-supervisor-composer.is-direct-codex {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
grid-template-rows: auto auto;
|
||||
gap: 8px;
|
||||
align-items: stretch;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 5;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.game-workbench-chat
|
||||
.project-supervisor-surface.is-direct-codex
|
||||
.project-supervisor-composer.is-direct-codex
|
||||
.resource-reference-input {
|
||||
width: 100%;
|
||||
min-height: 72px;
|
||||
padding: 12px 15px 10px;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.game-workbench-chat
|
||||
.project-supervisor-surface.is-direct-codex
|
||||
.project-supervisor-composer.is-direct-codex
|
||||
.project-supervisor-composer-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.game-workbench-chat
|
||||
.project-supervisor-surface.is-direct-codex
|
||||
.project-supervisor-composer.is-direct-codex
|
||||
.project-supervisor-composer-controls
|
||||
.conversation-model-select {
|
||||
position: static !important;
|
||||
right: auto !important;
|
||||
bottom: auto !important;
|
||||
max-width: min(220px, 40%);
|
||||
}
|
||||
|
||||
.game-workbench-chat
|
||||
.project-supervisor-surface.is-direct-codex
|
||||
.project-supervisor-composer.is-direct-codex
|
||||
.project-supervisor-composer-controls
|
||||
> button {
|
||||
position: static !important;
|
||||
right: auto !important;
|
||||
bottom: auto !important;
|
||||
display: grid;
|
||||
width: 30px;
|
||||
min-width: 30px;
|
||||
height: 30px;
|
||||
min-height: 30px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 9px;
|
||||
background: var(--platform-button-primary-fill);
|
||||
color: var(--platform-button-primary-text);
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.project-supervisor-composer .resource-reference-input {
|
||||
min-height: 72px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user