mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-07-07 22:22:38 +08:00
fix: resolve source control and chat UX bugs
- make staging real in the git panel: new /stage and /unstage endpoints, /status now reports the actual index via porcelain -z (handles renames, conflicts, and paths with spaces), and Stage/Unstage All run real git commands instead of toggling client-side state - add branch search to the header dropdown and Branches tab - persist the chosen permission mode per provider so a brand-new chat keeps it once the session id arrives, instead of reverting to default - replace cmdk's fuzzy model search with strict token matching so "chatgpt" no longer surfaces unrelated models - unit tests for the git status parser
This commit is contained in:
@@ -451,17 +451,19 @@ export function useChatProviderState({ selectedSession, selectedProject: _select
|
||||
}, [providerEfforts, providerModels, reconcileStoredEffort]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedSession?.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const savedMode = localStorage.getItem(`permissionMode-${selectedSession.id}`) as PermissionMode | null;
|
||||
const validModes = getPermissionModesForProvider(provider);
|
||||
setPermissionMode(
|
||||
savedMode && validModes.includes(savedMode)
|
||||
? savedMode
|
||||
: getDefaultPermissionModeForProvider(provider),
|
||||
const sessionSavedMode = selectedSession?.id
|
||||
? (localStorage.getItem(`permissionMode-${selectedSession.id}`) as PermissionMode | null)
|
||||
: null;
|
||||
// Fall back to the last mode picked for this provider: a brand-new chat
|
||||
// only receives its session id after the first send, so without this the
|
||||
// mode chosen beforehand would snap back to the default as soon as the
|
||||
// session id appears.
|
||||
const providerSavedMode = localStorage.getItem(`permissionMode-last-${provider}`) as PermissionMode | null;
|
||||
const savedMode = [sessionSavedMode, providerSavedMode].find(
|
||||
(mode): mode is PermissionMode => Boolean(mode && validModes.includes(mode)),
|
||||
);
|
||||
setPermissionMode(savedMode ?? getDefaultPermissionModeForProvider(provider));
|
||||
}, [selectedSession?.id, provider, getDefaultPermissionModeForProvider, getPermissionModesForProvider]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -511,6 +513,10 @@ export function useChatProviderState({ selectedSession, selectedProject: _select
|
||||
const nextMode = modes[nextIndex];
|
||||
setPermissionMode(nextMode);
|
||||
|
||||
// Persist per provider as well as per session: a brand-new chat has no
|
||||
// session id yet, and the per-provider key keeps the choice sticky when
|
||||
// the real id arrives (and for future sessions of this provider).
|
||||
localStorage.setItem(`permissionMode-last-${provider}`, nextMode);
|
||||
if (selectedSession?.id) {
|
||||
localStorage.setItem(`permissionMode-${selectedSession.id}`, nextMode);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,17 @@ const PROVIDER_META: { id: LLMProvider; name: string }[] = [
|
||||
const MOD_KEY =
|
||||
typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform) ? "⌘" : "Ctrl";
|
||||
|
||||
// cmdk's default filter is fuzzy (loose character-subsequence scoring), which
|
||||
// surfaces unrelated models — e.g. searching "chatgpt" also matched "Fable".
|
||||
// Require every whitespace-separated search token to appear as a literal
|
||||
// substring instead, so "claude 4.5" still matches "Anthropic Claude Haiku 4.5"
|
||||
// but "chatgpt" only matches models that actually contain it.
|
||||
function modelSearchFilter(value: string, search: string): number {
|
||||
const haystack = value.toLowerCase();
|
||||
const tokens = search.toLowerCase().split(/\s+/).filter(Boolean);
|
||||
return tokens.every((token) => haystack.includes(token)) ? 1 : 0;
|
||||
}
|
||||
|
||||
type ProviderSelectionEmptyStateProps = {
|
||||
selectedSession: ProjectSession | null;
|
||||
currentSessionId: string | null;
|
||||
@@ -234,7 +245,7 @@ export default function ProviderSelectionEmptyState({
|
||||
<div className="border-b border-border/60 bg-muted/20 px-4 py-3">
|
||||
<p className="text-sm font-semibold text-foreground">Choose a model</p>
|
||||
</div>
|
||||
<Command>
|
||||
<Command filter={modelSearchFilter}>
|
||||
<CommandInput
|
||||
placeholder={t("providerSelection.searchModels", {
|
||||
defaultValue: "Search models...",
|
||||
|
||||
Reference in New Issue
Block a user