fix(ui): align button states and action roles with UIKit #6250
Triggered via pull request
September 8, 2026 12:03
Status
Cancelled
Total duration
26m 3s
Artifacts
–
ci.yml
on: pull_request
Build Impact
24s
Shell Deploy Scripts
12s
Matrix: cli-test
Matrix: Impact-selected Linux / Relay Contract / build
Waiting for pending jobs
Matrix: rust-build-check
DSH Profile Packaging (windows-latest)
0s
Frontend Build
8m 14s
Rust / CLI Validation
6s
Annotations
11 errors and 11 warnings
|
Frontend Build
Process completed with exit code 1.
|
|
src/app/scenes/profile/views/AssistantCard.test.tsx > AssistantCard actions > disables session actions while a new session is starting:
src/web-ui/src/app/scenes/profile/views/AssistantCard.test.tsx#L160
TypeError: Cannot read properties of null (reading 'disabled')
❯ src/app/scenes/profile/views/AssistantCard.test.tsx:160:23
|
|
src/app/scenes/profile/views/AssistantCard.test.tsx > AssistantCard actions > disables card actions while setting the primary assistant:
src/web-ui/src/app/scenes/profile/views/AssistantCard.test.tsx#L137
TypeError: Cannot read properties of null (reading 'disabled')
❯ src/app/scenes/profile/views/AssistantCard.test.tsx:137:23
|
|
src/app/scenes/profile/views/AssistantCard.test.tsx > AssistantCard actions > keeps configuration and new-session actions distinct:
src/web-ui/src/app/scenes/profile/views/AssistantCard.test.tsx#L101
TypeError: Cannot read properties of null (reading 'click')
❯ src/app/scenes/profile/views/AssistantCard.test.tsx:101:26
❯ Proxy.act ../../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.development.js:2512:16
❯ src/app/scenes/profile/views/AssistantCard.test.tsx:101:5
|
|
src/infrastructure/config/components/VoiceInputConfig.presentation.test.ts > VoiceInputConfig status presentation > renders setup and ready states as compact body copy:
src/web-ui/src/infrastructure/config/components/VoiceInputConfig.presentation.test.ts#L29
AssertionError: expected 'import { Button, Input, Select, type …' to contain 'variant={status === \'setup\' ? \'fil…'
- Expected
+ Received
- variant={status === 'setup' ? 'fill' : 'outline'}
+ import { Button, Input, Select, type SelectOption, StatusPill, type StatusPillTone, Switch } from '@openbitfun/ui';
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+ import { Trans, useTranslation } from 'react-i18next';
+ import { CloudOff, HardDrive } from 'lucide-react';
+ import {
+ LOCAL_SENSEVOICE_SMALL_INT8_MODEL_ID,
+ speechAPI,
+ type SpeechModelStatus,
+ type SpeechRealtimeConfig,
+ } from '@/infrastructure/api';
+ import { isTauriRuntime } from '@/infrastructure/runtime';
+ import { notificationService } from '@/shared/notification-system';
+ import { createLogger } from '@/shared/utils/logger';
+ import { useAIExperienceSettings } from '../hooks';
+ import { aiExperienceConfigService } from '../services/AIExperienceConfigService';
+ import type { VoiceInputSettings } from '../types';
+ import LocalVoiceModelsConfig from './LocalVoiceModelsConfig';
+ import { VoiceInputDiagnostics } from './VoiceInputDiagnostics';
+ import {
+ ConfigPageContent,
+ ConfigPageHeader,
+ ConfigPageLayout,
+ ConfigLoadingState,
+ ConfigMessage,
+ ConfigPageRow,
+ ConfigPageSection,
+ ConfigRetryState,
+ } from './common';
+ import './VoiceInputConfig.scss';
+
+ const log = createLogger('VoiceInputConfig');
+ const DEFAULT_LOCAL_VOICE_MODEL_ID = LOCAL_SENSEVOICE_SMALL_INT8_MODEL_ID;
+
+ function formatBytes(bytes: number): string {
+ if (!Number.isFinite(bytes) || bytes <= 0) return '0 B';
+ const units = ['B', 'KiB', 'MiB', 'GiB'];
+ let value = bytes;
+ let unitIndex = 0;
+ while (value >= 1024 && unitIndex < units.length - 1) {
+ value /= 1024;
+ unitIndex += 1;
+ }
+ const digits = value >= 10 || unitIndex === 0 ? 0 : 1;
+ return `${value.toFixed(digits)} ${units[unitIndex]}`;
+ }
+
+ type VoiceInputStatus =
+ | 'ready'
+ | 'setup'
+ | 'downloading'
+ | 'verifying'
+ | 'deleting'
+ | 'unavailable'
+ | 'error';
+
+ function statusBadgeVariant(status: VoiceInputStatus): StatusPillTone {
+ switch (status) {
+ case 'ready':
+ return 'success';
+ case 'downloading':
+ case 'verifying':
+ return 'info';
+ case 'unavailable':
+ case 'error':
+ return 'danger';
+ default:
+ return 'neutral';
+ }
+ }
+
+ function statusActionKey(status: VoiceInputStatus): string {
+ switch (status) {
+ case 'setup':
+ return 'status.downloadModel';
+ case 'downloading':
+ case 'verifying':
+ case 'deleting':
+ return 'status.viewDetails';
+ case 'error':
+ return 'status.repair';
+ default:
+ return 'status.manageModels';
+ }
+ }
+
+ const VoiceInputConfig: React.FC = () => {
+ const { t } = useTranslation('settings/voice-input');
+ const speechRuntimeSupported = isTauriRuntime();
+ const {
+ settings,
+ isLoading: settingsLoading,
+ error: settingsError,
+ reload: reloadSettings,
+ } = useAIExperienceSettings();
+ const [models, setModels] = useState<SpeechModelStatus[]>([]);
+ const [modelsLoading, setModelsLoading] = useState(speechRuntimeSupported);
+ const [modelsLoadFailed, setModelsLoadFailed] = useState(false);
+ const [voiceInputSaving, setVoiceInputSaving] = useState(false);
+ const [busyAction, setBusyAction] = useState<string | null>(null);
+ const [localModelsOpen, setLocalModelsOpen] = useState(false);
+ const [voiceCallDraft, setVoiceCallDraft] = useState<SpeechRealtimeConfig | null>(null);
+ const voiceCallConfigRef = useRef<SpeechRealtimeConfig | null>(null);
+ const persistedVoiceCallConfigRef = useRef<SpeechRealtimeConfig | null>(null);
+ const [voiceCallLoading, setVoiceCallLoading] = useState(speechRuntimeSupported);
+ const [voiceCallLoadFailed, setVoiceCallLoadFailed] = useState(false);
+ const [voiceCallSaveError, setVoiceCallSaveError] = useState<string | null>(null);
+ const voiceCallR
|
|
src/infrastructure/config/components/ModelSettingsDialog.presentation.test.ts > ModelSettingsPage dialog presentation > keeps model configuration editors at a moderate responsive size:
src/web-ui/src/infrastructure/config/components/ModelSettingsDialog.presentation.test.ts#L33
AssertionError: expected 'open={isEditing && !!editingConfig}\n…' to contain '<Button variant="outline" size="sm" o…'
- Expected
+ Received
- <Button variant="outline" size="sm" onClick={requestCloseEditingModal} disabled={isEditorSaving}>
+ open={isEditing && !!editingConfig}
+ onOpenChange={(nextOpen) => {
+ if (!nextOpen && !isEditorSaving) {
+ if (reasoningPanelDraft) finishReasoningPanel();
+ else requestCloseEditingModal();
+ }
+ }}
+ className="openbitfun-model-settings__editor-dialog"
+ size="xl"
+ >
+ <DialogHeader>
+ <DialogHeading>
+ <DialogTitle>{reasoningPanelDraft
+ ? t('reasoningPresets.dialogTitle', {
+ provider: editingConfig?.name?.trim()
+ || currentTemplate?.name
+ || editingConfig?.provider
+ || '',
+ model: reasoningPanelDraft.modelName,
+ })
+ : editingConfig?.id
+ ? t('editModel')
+ : (getProviderInstanceId(editingConfig)
+ ? t('editProvider')
+ : (currentTemplate ? `${t('newProvider')} - ${currentTemplate.name}` : t('newProvider')))}</DialogTitle>
+ </DialogHeading>
+ <DialogClose disabled={isEditorSaving} />
+ </DialogHeader>
+ <DialogBody
+ inset="none"
+ aria-busy={isEditorSaving}
+ {...(isEditorSaving ? { inert: '' } : {})}
+ >
+ {reasoningPanelDraft ? (
+ <ReasoningConfigPanel
+ key={reasoningPanelDraft.key}
+ value={reasoningPanelDraft.reasoning}
+ generatedProjection={reasoningPanelProjection}
+ modelsDevReasoningCatalog={modelCatalog?.models_dev_reasoning_catalog}
+ projectionRequest={reasoningPanelProjectionRequest}
+ requestFormatLabel={reasoningPanelProjectionRequest
+ ? requestFormatLabelMap[reasoningPanelProjectionRequest.provider]
+ || reasoningPanelProjectionRequest.provider
+ : undefined}
+ onCancel={cancelReasoningPanel}
+ onDraftChange={updateReasoningPanelDraft}
+ onApply={(result: ReasoningConfigApplyResult) => {
+ updateReasoningPanelDraft(result);
+ finishReasoningPanel();
+ }}
+ />
+ ) : renderEditingForm()}
+ </DialogBody>
+ {!reasoningPanelDraft && (
+ <DialogFooter>
+ <Button variant="fill" size="sm" onClick={requestCloseEditingModal} disabled={isEditorSaving}>
+ {t('actions.cancel')}
+ </Button>
+ <Button
+ data-testid="settings-model-save-btn"
+ variant="primary"
+ size="sm"
+ onClick={() => void handleSave()}
+ loading={isEditorSaving}
+ >
+ {t('actions.save')}
+ </Button>
+ </DialogFooter>
+ )}
+ </Dialog>
+
❯ src/infrastructure/config/components/ModelSettingsDialog.presentation.test.ts:33:26
|
|
src/tools/generative-widget/appearancePayload.test.ts > generated widget appearance payload contract > derives its complete host payload allowlist from the canonical theme package:
src/web-ui/src/tools/generative-widget/appearancePayload.test.ts#L67
AssertionError: expected { count: 140, …(3) } to deeply equal { count: 127, …(3) }
- Expected
+ Received
{
- "count": 127,
+ "count": 140,
"first": "--openbitfun-color-accent-border",
- "hash": "e80ebcdf9452a0b5f8ab26d9b41cb81c4e74d75451e4402f99afc68af3c13f42",
+ "hash": "b9e8ed7d5fdc8a39768649aaf3643f7a6bef1d79c08b3f969f1798016f2d44ae",
"last": "--openbitfun-shadow-xs",
}
❯ src/tools/generative-widget/appearancePayload.test.ts:67:8
|
|
Rust Build Check (windows-latest)
Canceling since a higher priority waiting request for ci-refs/pull/2901/merge exists
|
|
Rust Build Check (windows-latest)
The operation was canceled.
|
|
Rust / CLI Validation
Process completed with exit code 1.
|
|
CI
Canceling since a higher priority waiting request for ci-refs/pull/2901/merge exists
|
|
Frontend Build:
src/web-ui/src/shared/announcement-system/components/ReleaseLetterModal.tsx#L22
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/flow_chat/tool-cards/AgentWaitToolCard.tsx#L17
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/flow_chat/components/voice/RealtimeVoiceCallContext.tsx#L32
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/flow_chat/components/voice/RealtimeVoiceCallContext.tsx#L23
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/flow_chat/components/modern/MessageReferenceCapsule.tsx#L29
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/flow_chat/components/modern/MessageReferenceCapsule.tsx#L7
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/flow_chat/components/ReasoningPresetSelector.tsx#L112
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/flow_chat/components/ReasoningPresetSelector.tsx#L36
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/app/components/SceneTopBar/SceneChrome.tsx#L121
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Frontend Build:
src/web-ui/src/app/components/NavPanel/components/DeviceArtwork.tsx#L6
Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
|
|
Tauri Windows test-loader limitation
The desktop test harness hit upstream Tauri issue #13419 before any Rust test ran. Compilation and the cross-platform product-control contracts remain blocking.
|