fix: resolve e2e test failures for terminal creation and new workspace

Terminal tests (launcher-tab, terminal-performance):
- Terminal creation no longer silently no-ops when workspace metadata
  hasn't hydrated. Early clicks are queued and flushed once the workspace
  directory is ready, with toast feedback.
- Terminal button is disabled while waiting on workspace readiness.
- Removed isFocused gate from ?open= intent consumption in workspace
  layout so deep-linked terminals open reliably on cold startup.

New workspace test:
- New workspace screen now shows a visible Create button even when the
  composer is empty, via new allowEmptySubmit and
  submitButtonAccessibilityLabel props threaded through Composer and
  MessageInput.
This commit is contained in:
Mohamed Boudra
2026-04-15 11:42:18 +07:00
parent 751a1df682
commit 43bec954ea
6 changed files with 105 additions and 20 deletions
+8
View File
@@ -67,6 +67,10 @@ interface ComposerProps {
onSubmitMessage?: (payload: MessagePayload) => Promise<void>;
/** When true, the submit button is enabled even without text or images (e.g. external attachment selected). */
hasExternalContent?: boolean;
/** When true, the composer can submit even with no text or attachments. */
allowEmptySubmit?: boolean;
/** Optional accessibility label for the primary submit button. */
submitButtonAccessibilityLabel?: string;
/** Externally controlled loading state. When true, disables the submit button. */
isSubmitLoading?: boolean;
/** When true, blurs the input immediately when submitting. */
@@ -105,6 +109,8 @@ export function Composer({
isInputActive,
onSubmitMessage,
hasExternalContent = false,
allowEmptySubmit = false,
submitButtonAccessibilityLabel,
isSubmitLoading = false,
blurOnSubmit = false,
value,
@@ -745,6 +751,8 @@ export function Composer({
onChangeText={setUserInput}
onSubmit={handleSubmit}
hasExternalContent={hasExternalContent}
allowEmptySubmit={allowEmptySubmit}
submitButtonAccessibilityLabel={submitButtonAccessibilityLabel}
isSubmitDisabled={isProcessing || isSubmitLoading}
isSubmitLoading={isProcessing || isSubmitLoading}
images={selectedImages}
+23 -5
View File
@@ -64,6 +64,10 @@ export interface MessageInputProps {
onSubmit: (payload: MessagePayload) => void;
/** When true, the submit button is enabled even without text or images (e.g. external attachment selected). */
hasExternalContent?: boolean;
/** When true, the submit button stays visible and can submit even with no content. */
allowEmptySubmit?: boolean;
/** Optional accessibility label for the primary submit button. */
submitButtonAccessibilityLabel?: string;
isSubmitDisabled?: boolean;
isSubmitLoading?: boolean;
images?: ImageAttachment[];
@@ -201,6 +205,8 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
onChangeText,
onSubmit,
hasExternalContent = false,
allowEmptySubmit = false,
submitButtonAccessibilityLabel,
isSubmitDisabled = false,
isSubmitLoading = false,
images = [],
@@ -571,7 +577,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
const handleSendMessage = useCallback(() => {
const trimmed = value.trim();
if (!trimmed && images.length === 0 && !hasExternalContent) return;
if (!trimmed && images.length === 0 && !hasExternalContent && !allowEmptySubmit) return;
const payload = {
text: trimmed,
images: images.length > 0 ? images : undefined,
@@ -581,7 +587,15 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
inputHeightRef.current = MIN_INPUT_HEIGHT;
setInputHeight(MIN_INPUT_HEIGHT);
onHeightChange?.(MIN_INPUT_HEIGHT);
}, [value, images, onSubmit, isAgentRunning, onHeightChange, hasExternalContent]);
}, [
allowEmptySubmit,
value,
images,
onSubmit,
isAgentRunning,
onHeightChange,
hasExternalContent,
]);
const handleQueueMessage = useCallback(() => {
if (!onQueue) return;
@@ -934,18 +948,20 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
const hasImages = images.length > 0;
const hasRealContent = value.trim().length > 0 || hasImages;
const hasSendableContent = hasRealContent || hasExternalContent;
const shouldShowSendButton = hasSendableContent || isSubmitLoading;
const shouldShowSendButton = hasSendableContent || allowEmptySubmit || isSubmitLoading;
const canPressLoadingButton = isSubmitLoading && typeof onSubmitLoadingPress === "function";
const isSendButtonDisabled =
disabled || (!canPressLoadingButton && (isSubmitDisabled || isSubmitLoading));
const defaultActionQueues = defaultSendBehavior === "queue" && isAgentRunning;
const submitAccessibilityLabel = canPressLoadingButton
const defaultSubmitAccessibilityLabel = canPressLoadingButton
? "Interrupt agent"
: defaultActionQueues
? "Queue message"
: isAgentRunning
? "Send and interrupt"
: "Send message";
const submitAccessibilityLabel =
submitButtonAccessibilityLabel ?? defaultSubmitAccessibilityLabel;
const handleInputChange = useCallback(
(nextValue: string) => {
@@ -1178,7 +1194,9 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(funct
</TooltipTrigger>
<TooltipContent side="top" align="center" offset={8}>
<View style={styles.tooltipRow}>
<Text style={styles.tooltipText}>{defaultActionQueues ? "Queue" : "Send"}</Text>
<Text style={styles.tooltipText}>
{submitButtonAccessibilityLabel ?? (defaultActionQueues ? "Queue" : "Send")}
</Text>
{sendKeys ? <Shortcut chord={sendKeys} style={styles.tooltipShortcut} /> : null}
</View>
</TooltipContent>