@lvce-editor/chat-view 6.70.0 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1214,7 +1214,6 @@ const Home = 12;
1214
1214
  const UpArrow = 14;
1215
1215
  const DownArrow = 16;
1216
1216
  const KeyN = 42;
1217
- const KeyV = 50;
1218
1217
 
1219
1218
  const CtrlCmd = 1 << 11 >>> 0;
1220
1219
  const Shift = 1 << 10 >>> 0;
@@ -1775,6 +1774,17 @@ const clearInput = async state => {
1775
1774
  };
1776
1775
  };
1777
1776
 
1777
+ const closeGitBranchPicker = state => {
1778
+ if (!state.gitBranchPickerOpen && !state.gitBranchPickerErrorMessage) {
1779
+ return state;
1780
+ }
1781
+ return {
1782
+ ...state,
1783
+ gitBranchPickerErrorMessage: '',
1784
+ gitBranchPickerOpen: false
1785
+ };
1786
+ };
1787
+
1778
1788
  const clampSelectionIndex = (value, max) => {
1779
1789
  if (!Number.isFinite(value)) {
1780
1790
  return 0;
@@ -1852,6 +1862,7 @@ const BackToChatList = 'Back to chat list';
1852
1862
  const Search$1 = 'Search';
1853
1863
  const ScrollDown$1 = 'Scroll down';
1854
1864
  const SearchModels = 'Search models';
1865
+ const PickModel = 'Pick Model';
1855
1866
  const SearchChats = 'Search chats';
1856
1867
  const Paste = 'Paste';
1857
1868
  const Rename = 'Rename';
@@ -1859,9 +1870,13 @@ const Archive = 'Archive';
1859
1870
  const AddProjectChat = 'Add Project';
1860
1871
  const RemoveProject = 'Remove Project';
1861
1872
  const Settings$1 = 'Settings';
1873
+ const Login$1 = 'Login';
1874
+ const Logout$1 = 'Logout';
1862
1875
  const LoginToBackend = 'Login to backend';
1863
1876
  const LogoutFromBackend = 'Logout from backend';
1864
1877
  const LoggingInToBackend = 'Logging in to backend';
1878
+ const LoggingOutFromBackend = 'Logging out from backend';
1879
+ const SignedIn = 'Signed in';
1865
1880
  const SwitchToChatFocusMode = 'Switch to chat focus mode';
1866
1881
  const SwitchToNormalChatMode = 'Switch to normal chat mode';
1867
1882
  const CloseChat$1 = 'Close Chat';
@@ -1941,18 +1956,33 @@ const scrollDown = () => {
1941
1956
  const searchModels = () => {
1942
1957
  return i18nString(SearchModels);
1943
1958
  };
1959
+ const pickModel = modelName => {
1960
+ return `${i18nString(PickModel)}, ${modelName}`;
1961
+ };
1944
1962
  const searchChats = () => {
1945
1963
  return i18nString(SearchChats);
1946
1964
  };
1965
+ const login = () => {
1966
+ return i18nString(Login$1);
1967
+ };
1968
+ const logout = () => {
1969
+ return i18nString(Logout$1);
1970
+ };
1947
1971
  const loginToBackend = () => {
1948
1972
  return i18nString(LoginToBackend);
1949
1973
  };
1950
- const logoutFromBackend = () => {
1974
+ const logoutFromBackend$1 = () => {
1951
1975
  return i18nString(LogoutFromBackend);
1952
1976
  };
1953
1977
  const loggingInToBackend = () => {
1954
1978
  return i18nString(LoggingInToBackend);
1955
1979
  };
1980
+ const loggingOutFromBackend = () => {
1981
+ return i18nString(LoggingOutFromBackend);
1982
+ };
1983
+ const signedIn = () => {
1984
+ return i18nString(SignedIn);
1985
+ };
1956
1986
  const chatFocusMode = () => {
1957
1987
  return i18nString(SwitchToChatFocusMode);
1958
1988
  };
@@ -2324,8 +2354,6 @@ const createDefaultState = () => {
2324
2354
  authAccessToken: '',
2325
2355
  authEnabled: false,
2326
2356
  authErrorMessage: '',
2327
- authRefreshToken: '',
2328
- authStatus: 'signed-out',
2329
2357
  backendUrl: '',
2330
2358
  chatFocusContentMaxWidth: 700,
2331
2359
  chatHistoryEnabled: true,
@@ -2357,6 +2385,10 @@ const createDefaultState = () => {
2357
2385
  errorCount: 0,
2358
2386
  focus: 'composer',
2359
2387
  focused: false,
2388
+ gitBranches: [],
2389
+ gitBranchPickerErrorMessage: '',
2390
+ gitBranchPickerOpen: false,
2391
+ gitBranchPickerVisible: false,
2360
2392
  headerHeight: 50,
2361
2393
  height: 0,
2362
2394
  ...responsivePickerState,
@@ -2443,6 +2475,7 @@ const createDefaultState = () => {
2443
2475
  useChatToolWorker: true,
2444
2476
  useMockApi: false,
2445
2477
  userName: '',
2478
+ userState: 'loggedOut',
2446
2479
  userSubscriptionPlan: '',
2447
2480
  userUsedTokens: 0,
2448
2481
  viewMode: 'list',
@@ -2829,7 +2862,24 @@ const isEqualComposerAttachments = (a, b) => {
2829
2862
  }
2830
2863
  return true;
2831
2864
  };
2865
+ const isEqualGitBranches = (a, b) => {
2866
+ if (a === b) {
2867
+ return true;
2868
+ }
2869
+ if (a.length !== b.length) {
2870
+ return false;
2871
+ }
2872
+ for (let i = 0; i < a.length; i++) {
2873
+ if (a[i].current !== b[i].current || a[i].name !== b[i].name) {
2874
+ return false;
2875
+ }
2876
+ }
2877
+ return true;
2878
+ };
2832
2879
  const isEqualProjectExpandedIds = (a, b) => {
2880
+ if (a === b) {
2881
+ return true;
2882
+ }
2833
2883
  if (a.length !== b.length) {
2834
2884
  return false;
2835
2885
  }
@@ -2840,6 +2890,20 @@ const isEqualProjectExpandedIds = (a, b) => {
2840
2890
  }
2841
2891
  return true;
2842
2892
  };
2893
+ const isEqualProjects = (a, b) => {
2894
+ if (a === b) {
2895
+ return true;
2896
+ }
2897
+ if (a.length !== b.length) {
2898
+ return false;
2899
+ }
2900
+ for (let i = 0; i < a.length; i++) {
2901
+ if (a[i].id !== b[i].id || a[i].name !== b[i].name || a[i].uri !== b[i].uri) {
2902
+ return false;
2903
+ }
2904
+ }
2905
+ return true;
2906
+ };
2843
2907
  const isEqualVisibleModels = (a, b) => {
2844
2908
  if (a === b) {
2845
2909
  return true;
@@ -2856,7 +2920,7 @@ const isEqualVisibleModels = (a, b) => {
2856
2920
  };
2857
2921
 
2858
2922
  const isEqual = (oldState, newState) => {
2859
- return oldState.addContextButtonEnabled === newState.addContextButtonEnabled && oldState.agentMode === newState.agentMode && oldState.agentModePickerOpen === newState.agentModePickerOpen && oldState.authEnabled === newState.authEnabled && oldState.authErrorMessage === newState.authErrorMessage && oldState.authStatus === newState.authStatus && isEqualComposerAttachments(oldState.composerAttachments, newState.composerAttachments) && oldState.composerDropActive === newState.composerDropActive && oldState.composerDropEnabled === newState.composerDropEnabled && oldState.composerValue === newState.composerValue && oldState.hasSpaceForAgentModePicker === newState.hasSpaceForAgentModePicker && oldState.hasSpaceForRunModePicker === newState.hasSpaceForRunModePicker && oldState.initial === newState.initial && oldState.modelPickerOpen === newState.modelPickerOpen && oldState.modelPickerSearchValue === newState.modelPickerSearchValue && isEqualVisibleModels(oldState.visibleModels, newState.visibleModels) && oldState.listFocusedIndex === newState.listFocusedIndex && isEqualProjectExpandedIds(oldState.projectExpandedIds, newState.projectExpandedIds) && oldState.projectListScrollTop === newState.projectListScrollTop && oldState.reasoningEffort === newState.reasoningEffort && oldState.reasoningEffortPickerOpen === newState.reasoningEffortPickerOpen && oldState.reasoningPickerEnabled === newState.reasoningPickerEnabled && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedModelId === newState.selectedModelId && oldState.selectedProjectId === newState.selectedProjectId && oldState.selectedSessionId === newState.selectedSessionId && oldState.showRunMode === newState.showRunMode && oldState.runMode === newState.runMode && oldState.runModePickerOpen === newState.runModePickerOpen && oldState.sessions === newState.sessions && oldState.tokensMax === newState.tokensMax && oldState.tokensUsed === newState.tokensUsed && oldState.usageOverviewEnabled === newState.usageOverviewEnabled && oldState.useChatMathWorker === newState.useChatMathWorker && oldState.viewMode === newState.viewMode && oldState.voiceDictationEnabled === newState.voiceDictationEnabled;
2923
+ return oldState.addContextButtonEnabled === newState.addContextButtonEnabled && oldState.agentMode === newState.agentMode && oldState.agentModePickerOpen === newState.agentModePickerOpen && oldState.authEnabled === newState.authEnabled && oldState.authErrorMessage === newState.authErrorMessage && isEqualComposerAttachments(oldState.composerAttachments, newState.composerAttachments) && oldState.composerDropActive === newState.composerDropActive && oldState.composerDropEnabled === newState.composerDropEnabled && oldState.composerValue === newState.composerValue && oldState.gitBranchPickerErrorMessage === newState.gitBranchPickerErrorMessage && oldState.gitBranchPickerOpen === newState.gitBranchPickerOpen && oldState.gitBranchPickerVisible === newState.gitBranchPickerVisible && isEqualGitBranches(oldState.gitBranches, newState.gitBranches) && oldState.hasSpaceForAgentModePicker === newState.hasSpaceForAgentModePicker && oldState.hasSpaceForRunModePicker === newState.hasSpaceForRunModePicker && oldState.initial === newState.initial && oldState.modelPickerOpen === newState.modelPickerOpen && oldState.modelPickerSearchValue === newState.modelPickerSearchValue && isEqualVisibleModels(oldState.visibleModels, newState.visibleModels) && oldState.listFocusedIndex === newState.listFocusedIndex && oldState.openApiApiKeyInput === newState.openApiApiKeyInput && oldState.openRouterApiKeyInput === newState.openRouterApiKeyInput && isEqualProjectExpandedIds(oldState.projectExpandedIds, newState.projectExpandedIds) && isEqualProjects(oldState.projects, newState.projects) && oldState.reasoningEffort === newState.reasoningEffort && oldState.reasoningEffortPickerOpen === newState.reasoningEffortPickerOpen && oldState.reasoningPickerEnabled === newState.reasoningPickerEnabled && oldState.renamingSessionId === newState.renamingSessionId && oldState.selectedModelId === newState.selectedModelId && oldState.selectedProjectId === newState.selectedProjectId && oldState.selectedSessionId === newState.selectedSessionId && oldState.searchEnabled === newState.searchEnabled && oldState.searchFieldVisible === newState.searchFieldVisible && oldState.searchValue === newState.searchValue && oldState.showRunMode === newState.showRunMode && oldState.runMode === newState.runMode && oldState.runModePickerOpen === newState.runModePickerOpen && oldState.sessions === newState.sessions && oldState.tokensMax === newState.tokensMax && oldState.tokensUsed === newState.tokensUsed && oldState.usageOverviewEnabled === newState.usageOverviewEnabled && oldState.useChatMathWorker === newState.useChatMathWorker && oldState.userName === newState.userName && oldState.userState === newState.userState && oldState.viewMode === newState.viewMode && oldState.voiceDictationEnabled === newState.voiceDictationEnabled;
2860
2924
  };
2861
2925
 
2862
2926
  const diffScrollTop = (oldState, newState) => {
@@ -2877,6 +2941,12 @@ const RenderIncremental = 11;
2877
2941
  const RenderScrollTop = 12;
2878
2942
 
2879
2943
  const diffValue = (oldState, newState) => {
2944
+ if (oldState.openApiApiKeyInput !== newState.openApiApiKeyInput) {
2945
+ return false;
2946
+ }
2947
+ if (oldState.openRouterApiKeyInput !== newState.openRouterApiKeyInput) {
2948
+ return false;
2949
+ }
2880
2950
  return oldState.composerValue === newState.composerValue || newState.inputSource !== 'script';
2881
2951
  };
2882
2952
 
@@ -2907,10 +2977,9 @@ const getAuthState = state => {
2907
2977
  authAccessToken,
2908
2978
  authEnabled,
2909
2979
  authErrorMessage,
2910
- authRefreshToken,
2911
- authStatus,
2912
2980
  backendUrl,
2913
2981
  userName,
2982
+ userState,
2914
2983
  userSubscriptionPlan,
2915
2984
  userUsedTokens
2916
2985
  } = state;
@@ -2918,10 +2987,9 @@ const getAuthState = state => {
2918
2987
  authAccessToken,
2919
2988
  authEnabled,
2920
2989
  authErrorMessage,
2921
- authRefreshToken,
2922
- authStatus,
2923
2990
  backendUrl,
2924
2991
  userName,
2992
+ userState,
2925
2993
  userSubscriptionPlan,
2926
2994
  userUsedTokens
2927
2995
  };
@@ -3257,10 +3325,6 @@ const getKeyBindings = () => {
3257
3325
  command: 'Chat.chatInputHistoryDown',
3258
3326
  key: DownArrow,
3259
3327
  when: FocusChatInput
3260
- }, {
3261
- command: 'Chat.pasteInput',
3262
- key: CtrlCmd | KeyV,
3263
- when: FocusChatInput
3264
3328
  }, {
3265
3329
  command: 'Chat.chatInputHistoryUp',
3266
3330
  key: UpArrow,
@@ -3387,9 +3451,11 @@ const ComposerAttachmentPreviewOverlay = 'composer-attachment-preview-overlay';
3387
3451
  const AddContext = 'add-context';
3388
3452
  const Dictate = 'dictate';
3389
3453
  const CreatePullRequest$1 = 'create-pull-request';
3454
+ const GitBranchPickerToggle = 'git-branch-picker-toggle';
3455
+ const GitBranchPickerItemPrefix = 'git-branch-picker-item:';
3390
3456
  const FocusAddAction = 'focus-add-action';
3391
- const FocusOpenInVsCode = 'focus-open-in-vscode';
3392
3457
  const FocusCommit = 'focus-commit';
3458
+ const FocusOpenInVsCode = 'focus-open-in-vscode';
3393
3459
  const FocusOpenTerminal = 'focus-open-terminal';
3394
3460
  const FocusShowDiff = 'focus-show-diff';
3395
3461
  const Send = 'send';
@@ -3458,6 +3524,15 @@ const getRenameIdFromInputName = name => {
3458
3524
  const getModelPickerItemInputName = modelId => {
3459
3525
  return `${ModelPickerItemPrefix}${modelId}`;
3460
3526
  };
3527
+ const getGitBranchPickerItemInputName = branchName => {
3528
+ return `${GitBranchPickerItemPrefix}${branchName}`;
3529
+ };
3530
+ const isGitBranchPickerItemInputName = name => {
3531
+ return name.startsWith(GitBranchPickerItemPrefix);
3532
+ };
3533
+ const getGitBranchFromGitBranchPickerItemInputName = name => {
3534
+ return name.slice(GitBranchPickerItemPrefix.length);
3535
+ };
3461
3536
  const getComposerAttachmentInputName = attachmentId => {
3462
3537
  return `${ComposerAttachmentPrefix}${attachmentId}`;
3463
3538
  };
@@ -3818,6 +3893,236 @@ const openFolder = async () => {
3818
3893
  }
3819
3894
  };
3820
3895
 
3896
+ // cspell:ignore gitdir worktrees
3897
+ const FileTypeFile = 1;
3898
+ const FileTypeDirectory = 2;
3899
+ const slashAtEndRegex = /\/$/;
3900
+ const gitDirPointerRegex = /^gitdir:\s*(.+)$/m;
3901
+ const headRefRegex = /^ref:\s+refs\/heads\/(.+)$/m;
3902
+ const toGitUri = (baseUri, ...segments) => {
3903
+ const url = new URL(baseUri.endsWith('/') ? baseUri : `${baseUri}/`);
3904
+ for (const segment of segments) {
3905
+ url.pathname = `${url.pathname.replace(slashAtEndRegex, '')}/${segment.split('/').map(part => encodeURIComponent(part)).join('/')}`;
3906
+ }
3907
+ return url.toString();
3908
+ };
3909
+ const decodeFileContent = content => {
3910
+ if (typeof content === 'string') {
3911
+ return content;
3912
+ }
3913
+ if (content instanceof Uint8Array) {
3914
+ return new TextDecoder().decode(content);
3915
+ }
3916
+ if (Array.isArray(content)) {
3917
+ return new TextDecoder().decode(new Uint8Array(content));
3918
+ }
3919
+ return '';
3920
+ };
3921
+ const toFileSystemPath = uri => {
3922
+ if (!uri.startsWith('file://')) {
3923
+ return uri;
3924
+ }
3925
+ return decodeURIComponent(new URL(uri).pathname);
3926
+ };
3927
+ const getRelativePath = (fromPath, toPath) => {
3928
+ if (!fromPath.startsWith('/') || !toPath.startsWith('/')) {
3929
+ return toPath;
3930
+ }
3931
+ const fromParts = fromPath.split('/').filter(Boolean);
3932
+ const toParts = toPath.split('/').filter(Boolean);
3933
+ let commonPrefixLength = 0;
3934
+ while (commonPrefixLength < fromParts.length && commonPrefixLength < toParts.length && fromParts[commonPrefixLength] === toParts[commonPrefixLength]) {
3935
+ commonPrefixLength++;
3936
+ }
3937
+ const parentSegments = fromParts.slice(commonPrefixLength).map(() => '..');
3938
+ const childSegments = toParts.slice(commonPrefixLength);
3939
+ return [...parentSegments, ...childSegments].join('/') || '.';
3940
+ };
3941
+ const toFileSystemTarget = (workspaceUri, uri) => {
3942
+ const workspacePath = toFileSystemPath(workspaceUri);
3943
+ const fileSystemPath = toFileSystemPath(uri);
3944
+ if (!workspaceUri.startsWith('file://') || !uri.startsWith('file://')) {
3945
+ return fileSystemPath;
3946
+ }
3947
+ return getRelativePath(workspacePath, fileSystemPath);
3948
+ };
3949
+ const parseEntries = value => {
3950
+ if (!Array.isArray(value)) {
3951
+ return [];
3952
+ }
3953
+ return value.map(entry => {
3954
+ if (Array.isArray(entry) && typeof entry[0] === 'string' && typeof entry[1] === 'number') {
3955
+ return {
3956
+ name: entry[0],
3957
+ type: entry[1]
3958
+ };
3959
+ }
3960
+ if (entry && typeof entry === 'object' && typeof Reflect.get(entry, 'name') === 'string' && typeof Reflect.get(entry, 'type') === 'number') {
3961
+ return {
3962
+ name: Reflect.get(entry, 'name'),
3963
+ type: Reflect.get(entry, 'type')
3964
+ };
3965
+ }
3966
+ return undefined;
3967
+ }).filter(entry => !!entry);
3968
+ };
3969
+ const readDir = async (workspaceUri, uri) => {
3970
+ const result = await invoke('FileSystem.readDirWithFileTypes', toFileSystemTarget(workspaceUri, uri));
3971
+ return parseEntries(result);
3972
+ };
3973
+ const readTextFile = async (workspaceUri, uri) => {
3974
+ const result = await invoke('FileSystem.readFile', toFileSystemTarget(workspaceUri, uri));
3975
+ return decodeFileContent(result);
3976
+ };
3977
+ const getGitDirUri = async workspaceUri => {
3978
+ const gitUri = toGitUri(workspaceUri, '.git');
3979
+ try {
3980
+ await readTextFile(workspaceUri, toGitUri(gitUri, 'HEAD'));
3981
+ return gitUri;
3982
+ } catch {
3983
+ // Fall through to support worktrees/submodules where .git is a pointer file.
3984
+ }
3985
+ const gitPointer = await readTextFile(workspaceUri, gitUri);
3986
+ const match = gitDirPointerRegex.exec(gitPointer);
3987
+ if (!match) {
3988
+ return '';
3989
+ }
3990
+ return new URL(match[1].trim(), workspaceUri.endsWith('/') ? workspaceUri : `${workspaceUri}/`).toString();
3991
+ };
3992
+ const collectBranchNames = async (workspaceUri, refsHeadsUri, prefix, branches) => {
3993
+ const entries = await readDir(workspaceUri, refsHeadsUri);
3994
+ for (const entry of entries) {
3995
+ if (entry.type === FileTypeDirectory) {
3996
+ await collectBranchNames(workspaceUri, toGitUri(refsHeadsUri, entry.name), `${prefix}${entry.name}/`, branches);
3997
+ continue;
3998
+ }
3999
+ if (entry.type === FileTypeFile) {
4000
+ branches.add(`${prefix}${entry.name}`);
4001
+ }
4002
+ }
4003
+ };
4004
+ const parseCurrentBranch = headContent => {
4005
+ const match = headRefRegex.exec(headContent.trim());
4006
+ if (match) {
4007
+ return match[1].trim();
4008
+ }
4009
+ return '';
4010
+ };
4011
+ const hasGitRepository = async workspaceUri => {
4012
+ try {
4013
+ return Boolean(await getGitDirUri(workspaceUri));
4014
+ } catch {
4015
+ return false;
4016
+ }
4017
+ };
4018
+ const getGitBranches = async workspaceUri => {
4019
+ const gitDirUri = await getGitDirUri(workspaceUri);
4020
+ if (!gitDirUri) {
4021
+ // eslint-disable-next-line @typescript-eslint/only-throw-error
4022
+ throw new globalThis.Error('Git repository not found.');
4023
+ }
4024
+ const branches = new Set();
4025
+ let currentBranch = '';
4026
+ try {
4027
+ const headContent = await readTextFile(workspaceUri, toGitUri(gitDirUri, 'HEAD'));
4028
+ currentBranch = parseCurrentBranch(headContent);
4029
+ if (currentBranch) {
4030
+ branches.add(currentBranch);
4031
+ }
4032
+ } catch {
4033
+ // Keep trying to discover branches from refs even if HEAD cannot be read.
4034
+ }
4035
+ try {
4036
+ await collectBranchNames(workspaceUri, toGitUri(gitDirUri, 'refs', 'heads'), '', branches);
4037
+ } catch {
4038
+ // Repositories without local refs should still open and surface any current branch we found.
4039
+ }
4040
+ if (branches.size === 0) {
4041
+ // eslint-disable-next-line @typescript-eslint/only-throw-error
4042
+ throw new globalThis.Error('No local git branches found.');
4043
+ }
4044
+ return [...branches].toSorted((a, b) => a.localeCompare(b)).map(name => ({
4045
+ current: name === currentBranch,
4046
+ name
4047
+ }));
4048
+ };
4049
+
4050
+ const getSelectedSession = (sessions, selectedSessionId) => {
4051
+ return sessions.find(session => session.id === selectedSessionId);
4052
+ };
4053
+
4054
+ const getProjectUri$1 = (state, projectId) => {
4055
+ return state.projects.find(project => project.id === projectId)?.uri || '';
4056
+ };
4057
+ const getWorkspaceUri$1 = (state, session) => {
4058
+ if (session?.workspaceUri) {
4059
+ return session.workspaceUri;
4060
+ }
4061
+ return getProjectUri$1(state, session?.projectId || state.selectedProjectId);
4062
+ };
4063
+
4064
+ const withHiddenBranchPicker = state => {
4065
+ return {
4066
+ ...state,
4067
+ gitBranches: [],
4068
+ gitBranchPickerErrorMessage: '',
4069
+ gitBranchPickerOpen: false,
4070
+ gitBranchPickerVisible: false
4071
+ };
4072
+ };
4073
+ const refreshGitBranchPickerVisibility = async state => {
4074
+ if (state.viewMode !== 'chat-focus') {
4075
+ return {
4076
+ ...state,
4077
+ gitBranchPickerErrorMessage: '',
4078
+ gitBranchPickerOpen: false
4079
+ };
4080
+ }
4081
+ const selectedSession = getSelectedSession(state.sessions, state.selectedSessionId);
4082
+ const fallbackBranchName = selectedSession?.branchName || '';
4083
+ const workspaceUri = getWorkspaceUri$1(state, selectedSession);
4084
+ if (!workspaceUri) {
4085
+ return withHiddenBranchPicker(state);
4086
+ }
4087
+ const visible = await hasGitRepository(workspaceUri);
4088
+ if (!visible) {
4089
+ if (fallbackBranchName) {
4090
+ return {
4091
+ ...state,
4092
+ gitBranches: [{
4093
+ current: true,
4094
+ name: fallbackBranchName
4095
+ }],
4096
+ gitBranchPickerVisible: true
4097
+ };
4098
+ }
4099
+ return withHiddenBranchPicker(state);
4100
+ }
4101
+ try {
4102
+ const gitBranches = await getGitBranches(workspaceUri);
4103
+ return {
4104
+ ...state,
4105
+ gitBranches,
4106
+ gitBranchPickerVisible: true
4107
+ };
4108
+ } catch {
4109
+ if (fallbackBranchName) {
4110
+ return {
4111
+ ...state,
4112
+ gitBranches: [{
4113
+ current: true,
4114
+ name: fallbackBranchName
4115
+ }],
4116
+ gitBranchPickerVisible: true
4117
+ };
4118
+ }
4119
+ return {
4120
+ ...state,
4121
+ gitBranchPickerVisible: true
4122
+ };
4123
+ }
4124
+ };
4125
+
3821
4126
  const selectProject = async (state, projectId) => {
3822
4127
  const {
3823
4128
  selectedProjectId,
@@ -3831,14 +4136,14 @@ const selectProject = async (state, projectId) => {
3831
4136
  }
3832
4137
  const visibleSessions = getVisibleSessions(sessions, projectId);
3833
4138
  if (visibleSessions.length === 0) {
3834
- return {
4139
+ return refreshGitBranchPickerVisibility({
3835
4140
  ...state,
3836
4141
  composerAttachments: [],
3837
4142
  composerAttachmentsHeight: 0,
3838
4143
  selectedProjectId: projectId,
3839
4144
  selectedSessionId: '',
3840
4145
  viewMode: viewMode === 'chat-focus' ? 'chat-focus' : 'list'
3841
- };
4146
+ });
3842
4147
  }
3843
4148
  const currentSessionVisible = visibleSessions.some(session => session.id === selectedSessionId);
3844
4149
  const nextSelectedSessionId = currentSessionVisible ? selectedSessionId : visibleSessions[0].id;
@@ -3850,7 +4155,7 @@ const selectProject = async (state, projectId) => {
3850
4155
  }
3851
4156
  return loadedSession;
3852
4157
  });
3853
- return {
4158
+ return refreshGitBranchPickerVisibility({
3854
4159
  ...state,
3855
4160
  composerAttachments,
3856
4161
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, width),
@@ -3858,7 +4163,7 @@ const selectProject = async (state, projectId) => {
3858
4163
  selectedSessionId: nextSelectedSessionId,
3859
4164
  sessions: hydratedSessions,
3860
4165
  viewMode: viewMode === 'chat-focus' ? 'chat-focus' : 'detail'
3861
- };
4166
+ });
3862
4167
  };
3863
4168
 
3864
4169
  const fileSchemeRegex = /^file:\/\//;
@@ -3993,6 +4298,148 @@ const handleClickCreatePullRequest = async state => {
3993
4298
  };
3994
4299
  };
3995
4300
 
4301
+ const trailingSlashesRegex$3 = /\/+$/;
4302
+ const trimTrailingSlashes = value => {
4303
+ return value.replace(trailingSlashesRegex$3, '');
4304
+ };
4305
+
4306
+ const getBackendAuthUrl = (backendUrl, path) => {
4307
+ return `${trimTrailingSlashes(backendUrl)}${path}`;
4308
+ };
4309
+
4310
+ const getBackendLoginUrl = backendUrl => {
4311
+ return getBackendAuthUrl(backendUrl, '/auth/login');
4312
+ };
4313
+
4314
+ const getLoggedOutBackendAuthState = (authErrorMessage = '') => {
4315
+ return {
4316
+ authAccessToken: '',
4317
+ authErrorMessage,
4318
+ userName: '',
4319
+ userState: 'loggedOut',
4320
+ userSubscriptionPlan: '',
4321
+ userUsedTokens: 0
4322
+ };
4323
+ };
4324
+
4325
+ const getBackendLogoutUrl = backendUrl => {
4326
+ return getBackendAuthUrl(backendUrl, '/auth/logout');
4327
+ };
4328
+
4329
+ const logoutFromBackend = async backendUrl => {
4330
+ if (!backendUrl) {
4331
+ return;
4332
+ }
4333
+ try {
4334
+ await fetch(getBackendLogoutUrl(backendUrl), {
4335
+ credentials: 'include',
4336
+ headers: {
4337
+ Accept: 'application/json'
4338
+ },
4339
+ method: 'POST'
4340
+ });
4341
+ } catch {
4342
+ // Ignore logout failures and still clear local auth state.
4343
+ }
4344
+ };
4345
+
4346
+ const getBackendRefreshUrl = backendUrl => {
4347
+ return getBackendAuthUrl(backendUrl, '/auth/refresh');
4348
+ };
4349
+
4350
+ const isObject$2 = value => {
4351
+ return !!value && typeof value === 'object';
4352
+ };
4353
+
4354
+ const isBackendAuthResponse = value => {
4355
+ return isObject$2(value);
4356
+ };
4357
+
4358
+ const getNumber = (value, fallback = 0) => {
4359
+ return typeof value === 'number' ? value : fallback;
4360
+ };
4361
+
4362
+ const getString = (value, fallback = '') => {
4363
+ return typeof value === 'string' ? value : fallback;
4364
+ };
4365
+
4366
+ const toBackendAuthState = value => {
4367
+ return {
4368
+ authAccessToken: getString(value.accessToken),
4369
+ authErrorMessage: getString(value.error),
4370
+ userName: getString(value.userName),
4371
+ userState: value.accessToken ? 'loggedIn' : 'loggedOut',
4372
+ userSubscriptionPlan: getString(value.subscriptionPlan),
4373
+ userUsedTokens: getNumber(value.usedTokens)
4374
+ };
4375
+ };
4376
+
4377
+ const parseBackendAuthResponse = value => {
4378
+ if (!isBackendAuthResponse(value)) {
4379
+ return getLoggedOutBackendAuthState('Backend returned an invalid authentication response.');
4380
+ }
4381
+ return toBackendAuthState(value);
4382
+ };
4383
+
4384
+ const syncBackendAuth = async backendUrl => {
4385
+ if (!backendUrl) {
4386
+ return getLoggedOutBackendAuthState('Backend URL is missing.');
4387
+ }
4388
+ try {
4389
+ const response = await fetch(getBackendRefreshUrl(backendUrl), {
4390
+ credentials: 'include',
4391
+ headers: {
4392
+ Accept: 'application/json'
4393
+ },
4394
+ method: 'POST'
4395
+ });
4396
+ if (response.status === 401 || response.status === 403) {
4397
+ return getLoggedOutBackendAuthState();
4398
+ }
4399
+ let payload = undefined;
4400
+ try {
4401
+ payload = await response.json();
4402
+ } catch {
4403
+ payload = undefined;
4404
+ }
4405
+ if (!response.ok) {
4406
+ const parsed = parseBackendAuthResponse(payload);
4407
+ return getLoggedOutBackendAuthState(parsed.authErrorMessage || 'Backend authentication failed.');
4408
+ }
4409
+ const parsed = parseBackendAuthResponse(payload);
4410
+ if (parsed.authErrorMessage) {
4411
+ return getLoggedOutBackendAuthState(parsed.authErrorMessage);
4412
+ }
4413
+ if (!parsed.authAccessToken) {
4414
+ return getLoggedOutBackendAuthState();
4415
+ }
4416
+ return parsed;
4417
+ } catch (error) {
4418
+ const authErrorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
4419
+ return getLoggedOutBackendAuthState(authErrorMessage);
4420
+ }
4421
+ };
4422
+
4423
+ const delay = async ms => {
4424
+ await new Promise(resolve => setTimeout(resolve, ms));
4425
+ };
4426
+
4427
+ const waitForBackendLogin = async (backendUrl, timeoutMs = 30_000, pollIntervalMs = 1000) => {
4428
+ const deadline = Date.now() + timeoutMs;
4429
+ let lastErrorMessage = '';
4430
+ while (Date.now() < deadline) {
4431
+ const authState = await syncBackendAuth(backendUrl);
4432
+ if (authState.userState === 'loggedIn') {
4433
+ return authState;
4434
+ }
4435
+ if (authState.authErrorMessage) {
4436
+ lastErrorMessage = authState.authErrorMessage;
4437
+ }
4438
+ await delay(pollIntervalMs);
4439
+ }
4440
+ return getLoggedOutBackendAuthState(lastErrorMessage);
4441
+ };
4442
+
3996
4443
  let nextLoginResponse;
3997
4444
  const setNextLoginResponse = response => {
3998
4445
  nextLoginResponse = response;
@@ -4016,35 +4463,36 @@ const consumeNextLoginResponse = async () => {
4016
4463
  return response.response;
4017
4464
  };
4018
4465
 
4019
- const get = async key => {
4020
- return getPreference(key);
4021
- };
4022
- const update = async settings => {
4023
- await invoke('Preferences.update', settings);
4024
- };
4025
-
4026
- const trailingSlashesRegex$3 = /\/+$/;
4027
4466
  const isLoginResponse = value => {
4028
4467
  if (!value || typeof value !== 'object') {
4029
4468
  return false;
4030
4469
  }
4031
4470
  return true;
4032
4471
  };
4033
- const trimTrailingSlashes = value => {
4034
- return value.replace(trailingSlashesRegex$3, '');
4472
+ const getLoggedInState = (state, response) => {
4473
+ const accessToken = typeof response.accessToken === 'string' ? response.accessToken : '';
4474
+ return {
4475
+ ...state,
4476
+ authAccessToken: accessToken,
4477
+ authErrorMessage: '',
4478
+ userName: typeof response.userName === 'string' ? response.userName : state.userName,
4479
+ userState: accessToken ? 'loggedIn' : 'loggedOut',
4480
+ userSubscriptionPlan: typeof response.subscriptionPlan === 'string' ? response.subscriptionPlan : state.userSubscriptionPlan,
4481
+ userUsedTokens: typeof response.usedTokens === 'number' ? response.usedTokens : state.userUsedTokens
4482
+ };
4035
4483
  };
4036
4484
  const handleClickLogin = async state => {
4037
4485
  if (!state.backendUrl) {
4038
4486
  return {
4039
4487
  ...state,
4040
4488
  authErrorMessage: 'Backend URL is missing.',
4041
- authStatus: 'signed-out'
4489
+ userState: 'loggedOut'
4042
4490
  };
4043
4491
  }
4044
4492
  const signingInState = {
4045
4493
  ...state,
4046
4494
  authErrorMessage: '',
4047
- authStatus: 'signing-in'
4495
+ userState: 'loggingIn'
4048
4496
  };
4049
4497
  if (state.uid) {
4050
4498
  set(state.uid, state, signingInState);
@@ -4053,69 +4501,53 @@ const handleClickLogin = async state => {
4053
4501
  let usedMockResponse = false;
4054
4502
  try {
4055
4503
  usedMockResponse = hasPendingMockLoginResponse();
4056
- const response = usedMockResponse ? await consumeNextLoginResponse() : await invoke('Auth.login', state.backendUrl);
4057
- if (!isLoginResponse(response)) {
4058
- return {
4059
- ...signingInState,
4060
- authErrorMessage: 'Backend returned an invalid login response.',
4061
- authStatus: 'signed-out'
4062
- };
4063
- }
4064
- if (typeof response.error === 'string' && response.error) {
4065
- return {
4066
- ...signingInState,
4067
- authErrorMessage: response.error,
4068
- authStatus: 'signed-out'
4069
- };
4504
+ if (usedMockResponse) {
4505
+ const response = await consumeNextLoginResponse();
4506
+ if (!isLoginResponse(response)) {
4507
+ return {
4508
+ ...signingInState,
4509
+ authErrorMessage: 'Backend returned an invalid login response.',
4510
+ userState: 'loggedOut'
4511
+ };
4512
+ }
4513
+ if (typeof response.error === 'string' && response.error) {
4514
+ return {
4515
+ ...signingInState,
4516
+ authErrorMessage: response.error,
4517
+ userState: 'loggedOut'
4518
+ };
4519
+ }
4520
+ return getLoggedInState(signingInState, response);
4070
4521
  }
4071
- const accessToken = typeof response.accessToken === 'string' ? response.accessToken : '';
4072
- const refreshToken = typeof response.refreshToken === 'string' ? response.refreshToken : '';
4073
- await update({
4074
- 'secrets.chatBackendAccessToken': accessToken,
4075
- 'secrets.chatBackendRefreshToken': refreshToken
4076
- });
4522
+ await invoke('Main.openUri', getBackendLoginUrl(state.backendUrl));
4523
+ const authState = await waitForBackendLogin(state.backendUrl);
4077
4524
  return {
4078
4525
  ...signingInState,
4079
- authAccessToken: accessToken,
4080
- authErrorMessage: '',
4081
- authRefreshToken: refreshToken,
4082
- authStatus: accessToken ? 'signed-in' : 'signed-out',
4083
- userName: typeof response.userName === 'string' ? response.userName : state.userName,
4084
- userSubscriptionPlan: typeof response.subscriptionPlan === 'string' ? response.subscriptionPlan : state.userSubscriptionPlan,
4085
- userUsedTokens: typeof response.usedTokens === 'number' ? response.usedTokens : state.userUsedTokens
4526
+ ...authState
4086
4527
  };
4087
4528
  } catch (error) {
4088
4529
  const errorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
4089
- if (!usedMockResponse) {
4090
- await invoke('Main.openUri', `${trimTrailingSlashes(state.backendUrl)}/auth/login`);
4091
- }
4092
4530
  return {
4093
4531
  ...signingInState,
4094
- authErrorMessage: errorMessage,
4095
- authStatus: 'signed-out'
4532
+ ...getLoggedOutBackendAuthState(errorMessage)
4096
4533
  };
4097
4534
  }
4098
4535
  };
4099
4536
 
4100
4537
  const handleClickLogout = async state => {
4101
- try {
4102
- await invoke('Auth.logout', state.backendUrl);
4103
- } catch {
4104
- // Ignore logout bridge errors and still clear local auth state.
4105
- }
4106
- await update({
4107
- 'secrets.chatBackendAccessToken': '',
4108
- 'secrets.chatBackendRefreshToken': ''
4109
- });
4110
- return {
4538
+ const loggingOutState = {
4111
4539
  ...state,
4112
- authAccessToken: '',
4113
4540
  authErrorMessage: '',
4114
- authRefreshToken: '',
4115
- authStatus: 'signed-out',
4116
- userName: '',
4117
- userSubscriptionPlan: '',
4118
- userUsedTokens: 0
4541
+ userState: 'loggingOut'
4542
+ };
4543
+ if (state.uid) {
4544
+ set(state.uid, state, loggingOutState);
4545
+ await invoke('Chat.rerender');
4546
+ }
4547
+ await logoutFromBackend(state.backendUrl);
4548
+ return {
4549
+ ...loggingOutState,
4550
+ ...getLoggedOutBackendAuthState()
4119
4551
  };
4120
4552
  };
4121
4553
 
@@ -4440,10 +4872,6 @@ const getClientRequestIdHeader = () => {
4440
4872
  };
4441
4873
  };
4442
4874
 
4443
- const delay = async ms => {
4444
- await new Promise(resolve => setTimeout(resolve, ms));
4445
- };
4446
-
4447
4875
  const getMockAiResponse = async (userMessage, delayInMs) => {
4448
4876
  await delay(delayInMs);
4449
4877
  return `Mock AI response: I received "${userMessage}".`;
@@ -6830,7 +7258,12 @@ const ChatFocusActions = 'ChatFocusActions';
6830
7258
  const ChatFocusHeader = 'ChatFocusHeader';
6831
7259
  const ChatFocusMainArea = 'ChatFocusMainArea';
6832
7260
  const ChatFocusProject = 'ChatFocusProject';
7261
+ const ChatGitBranchPicker = 'ChatGitBranchPicker';
7262
+ const ChatGitBranchPickerErrorMessage = 'ChatGitBranchPickerErrorMessage';
7263
+ const ChatGitBranchPickerMessage = 'ChatGitBranchPickerMessage';
6833
7264
  const ChatHeader = 'ChatHeader';
7265
+ const ChatHeaderAuth = 'ChatHeaderAuth';
7266
+ const ChatHeaderAuthName = 'ChatHeaderAuthName';
6834
7267
  const ChatHeaderLabel = 'ChatHeaderLabel';
6835
7268
  const ChatInputBox = 'ChatInputBox';
6836
7269
  const ChatList = 'ChatList';
@@ -6844,6 +7277,7 @@ const ChatListItemStatusInProgress = 'ChatListItemStatusInProgress';
6844
7277
  const ChatListItemStatusRow = 'ChatListItemStatusRow';
6845
7278
  const ChatListItemStatusStopped = 'ChatListItemStatusStopped';
6846
7279
  const ChatMessageContent = 'ChatMessageContent';
7280
+ const ChatImageMessageContent = 'ChatImageMessageContent';
6847
7281
  const ChatMessageLink = 'ChatMessageLink';
6848
7282
  const ChatModelPicker = 'ChatModelPicker';
6849
7283
  const ChatModelPickerContainer = 'ChatModelPickerContainer';
@@ -6878,6 +7312,7 @@ const ChatAttachmentLabel = 'ChatAttachmentLabel';
6878
7312
  const ChatAttachmentPreview = 'ChatAttachmentPreview';
6879
7313
  const ChatAttachments = 'ChatAttachments';
6880
7314
  const ChatAttachmentTextFile = 'ChatAttachmentTextFile';
7315
+ const ChatMessageImage = 'ChatMessageImage';
6881
7316
  const ChatSendArea = 'ChatSendArea';
6882
7317
  const ChatSendAreaBottom = 'ChatSendAreaBottom';
6883
7318
  const ChatSendAreaContent = 'ChatSendAreaContent';
@@ -6915,6 +7350,7 @@ const IconButtonDisabled = 'IconButtonDisabled';
6915
7350
  const ImageElement = 'ImageElement';
6916
7351
  const ImageErrorMessage = 'ImageErrorMessage';
6917
7352
  const InputBox = 'InputBox';
7353
+ const InputInvalid = 'InputInvalid';
6918
7354
  const Insertion = 'Insertion';
6919
7355
  const Label = 'Label';
6920
7356
  const LabelDetail = 'LabelDetail';
@@ -6923,7 +7359,6 @@ const MarkdownMathBlock = 'MarkdownMathBlock';
6923
7359
  const MarkdownQuote = 'MarkdownQuote';
6924
7360
  const MarkdownTable = 'MarkdownTable';
6925
7361
  const MaskIcon = 'MaskIcon';
6926
- const MaskIconAccount = 'MaskIconAccount';
6927
7362
  const MaskIconAdd = 'MaskIconAdd';
6928
7363
  const MaskIconArrowLeft = 'MaskIconArrowLeft';
6929
7364
  const MaskIconChevronDown = 'MaskIconChevronDown';
@@ -6934,7 +7369,6 @@ const MaskIconDebugPause = 'MaskIconDebugPause';
6934
7369
  const MaskIconLayoutPanelLeft = 'MaskIconLayoutPanelLeft';
6935
7370
  const MaskIconSearch = 'MaskIconSearch';
6936
7371
  const MaskIconSettingsGear = 'MaskIconSettingsGear';
6937
- const MaskIconSignOut = 'MaskIconSignOut';
6938
7372
  const Message = 'Message';
6939
7373
  const MessageAssistant = 'MessageAssistant';
6940
7374
  const MessageUser = 'MessageUser';
@@ -8288,6 +8722,12 @@ const parseAndStoreMessageContent = async (parsedMessages, message) => {
8288
8722
  const parsedContent = message.text === '' ? emptyMessageContent : await parseMessage(message.text);
8289
8723
  return setParsedMessageContent(parsedMessages, message.id, message.text, parsedContent);
8290
8724
  };
8725
+ const parseAndStoreMessageContentWithWorkerPreference = async (parsedMessages, message, useChatMessageParsingWorker) => {
8726
+ if (useChatMessageParsingWorker) {
8727
+ return parseAndStoreMessagesContentInWorker(parsedMessages, [message]);
8728
+ }
8729
+ return parseAndStoreMessageContent(parsedMessages, message);
8730
+ };
8291
8731
  const parseAndStoreMessagesContent = async (parsedMessages, messages) => {
8292
8732
  let nextParsedMessages = parsedMessages;
8293
8733
  for (const message of messages) {
@@ -8324,6 +8764,13 @@ const getEmptyMessageContent = () => {
8324
8764
  return emptyMessageContent;
8325
8765
  };
8326
8766
 
8767
+ const get = async key => {
8768
+ return getPreference(key);
8769
+ };
8770
+ const update = async settings => {
8771
+ await invoke('Preferences.update', settings);
8772
+ };
8773
+
8327
8774
  const setOpenApiApiKey = async (state, openApiApiKey, persist = true) => {
8328
8775
  if (persist) {
8329
8776
  await update({
@@ -8738,10 +9185,6 @@ const getNextHandleTextChunkState = (latestState, parsedMessages, sessions) => {
8738
9185
  };
8739
9186
  };
8740
9187
 
8741
- const getSelectedSession = (sessions, selectedSessionId) => {
8742
- return sessions.find(session => session.id === selectedSessionId);
8743
- };
8744
-
8745
9188
  const setAndRerenderHandleTextChunkState = async (uid, previousState, nextState) => {
8746
9189
  set(uid, previousState, nextState);
8747
9190
  // @ts-ignore
@@ -8828,7 +9271,7 @@ const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls,
8828
9271
  };
8829
9272
  };
8830
9273
 
8831
- const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress) => {
9274
+ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress, useChatMessageParsingWorker) => {
8832
9275
  let updatedMessage;
8833
9276
  const updatedSessions = sessions.map(session => {
8834
9277
  if (session.id !== selectedSessionId) {
@@ -8851,7 +9294,7 @@ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, sele
8851
9294
  });
8852
9295
  let nextParsedMessages = parsedMessages;
8853
9296
  if (updatedMessage) {
8854
- nextParsedMessages = await parseAndStoreMessageContent(parsedMessages, updatedMessage);
9297
+ nextParsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, updatedMessage, useChatMessageParsingWorker);
8855
9298
  }
8856
9299
  return {
8857
9300
  parsedMessages: nextParsedMessages,
@@ -8874,7 +9317,7 @@ const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTex
8874
9317
  };
8875
9318
  }
8876
9319
  const updatedText = assistantMessage.text + chunk;
8877
- const updated = await updateMessageTextInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, updatedText, true);
9320
+ const updated = await updateMessageTextInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, updatedText, true, handleTextChunkState.latestState.useChatMessageParsingWorker);
8878
9321
  const nextState = {
8879
9322
  ...handleTextChunkState.latestState,
8880
9323
  ...(handleTextChunkState.latestState.messagesAutoScrollEnabled ? {
@@ -9040,6 +9483,11 @@ const withProvisionedBackgroundSession = async (state, session) => {
9040
9483
  };
9041
9484
  };
9042
9485
  const handleSubmit = async state => {
9486
+ const authState = state.authEnabled && state.backendUrl ? await syncBackendAuth(state.backendUrl) : undefined;
9487
+ const effectiveState = authState ? {
9488
+ ...state,
9489
+ ...authState
9490
+ } : state;
9043
9491
  const {
9044
9492
  agentMode,
9045
9493
  aiSessionTitleGenerationEnabled,
@@ -9068,27 +9516,28 @@ const handleSubmit = async state => {
9068
9516
  streamingEnabled,
9069
9517
  toolEnablement,
9070
9518
  useChatCoordinatorWorker,
9519
+ useChatMessageParsingWorker,
9071
9520
  useChatNetworkWorkerForRequests,
9072
9521
  useChatToolWorker,
9073
9522
  useMockApi,
9074
9523
  viewMode,
9075
9524
  webSearchEnabled
9076
- } = state;
9525
+ } = effectiveState;
9077
9526
  const userText = composerValue.trim();
9078
9527
  if (!userText) {
9079
- return state;
9528
+ return effectiveState;
9080
9529
  }
9081
9530
  const slashCommand = getSlashCommand(userText);
9082
9531
  if (slashCommand) {
9083
- return executeSlashCommand(state, slashCommand);
9532
+ return executeSlashCommand(effectiveState, slashCommand);
9084
9533
  }
9085
9534
  const userTime = new Date().toLocaleTimeString([], {
9086
9535
  hour: '2-digit',
9087
9536
  minute: '2-digit'
9088
9537
  });
9089
9538
  const userMessageId = crypto.randomUUID();
9090
- const composerAttachments = state.composerAttachments.length > 0 ? state.composerAttachments : await getComposerAttachments(state.selectedSessionId);
9091
- await clearComposerAttachments(state.selectedSessionId, composerAttachments.map(attachment => attachment.attachmentId));
9539
+ const composerAttachments = effectiveState.composerAttachments.length > 0 ? effectiveState.composerAttachments : await getComposerAttachments(effectiveState.selectedSessionId);
9540
+ await clearComposerAttachments(effectiveState.selectedSessionId, composerAttachments.map(attachment => attachment.attachmentId));
9092
9541
  const userMessage = {
9093
9542
  ...(composerAttachments.length > 0 ? {
9094
9543
  attachments: composerAttachments
@@ -9112,9 +9561,9 @@ const handleSubmit = async state => {
9112
9561
  };
9113
9562
  let {
9114
9563
  parsedMessages
9115
- } = state;
9116
- parsedMessages = await parseAndStoreMessageContent(parsedMessages, userMessage);
9117
- parsedMessages = await parseAndStoreMessageContent(parsedMessages, inProgressAssistantMessage);
9564
+ } = effectiveState;
9565
+ parsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, userMessage, useChatMessageParsingWorker);
9566
+ parsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, inProgressAssistantMessage, useChatMessageParsingWorker);
9118
9567
  let workingSessions = sessions;
9119
9568
  if (viewMode === 'detail') {
9120
9569
  const loadedSession = await getChatSession(selectedSessionId);
@@ -9146,10 +9595,10 @@ const handleSubmit = async state => {
9146
9595
  const provisionedSession = await withProvisionedBackgroundSession(state, newSession);
9147
9596
  await saveChatSession(provisionedSession);
9148
9597
  optimisticState = withUpdatedMessageScrollTop(focusInput({
9149
- ...state,
9598
+ ...effectiveState,
9150
9599
  composerAttachments: [],
9151
9600
  composerAttachmentsHeight: 0,
9152
- composerHeight: getMinComposerHeightForState(state),
9601
+ composerHeight: getMinComposerHeightForState(effectiveState),
9153
9602
  composerSelectionEnd: 0,
9154
9603
  composerSelectionStart: 0,
9155
9604
  composerValue: '',
@@ -9185,10 +9634,10 @@ const handleSubmit = async state => {
9185
9634
  await saveChatSession(selectedSession);
9186
9635
  }
9187
9636
  optimisticState = withUpdatedMessageScrollTop(focusInput({
9188
- ...state,
9637
+ ...effectiveState,
9189
9638
  composerAttachments: [],
9190
9639
  composerAttachmentsHeight: 0,
9191
- composerHeight: getMinComposerHeightForState(state),
9640
+ composerHeight: getMinComposerHeightForState(effectiveState),
9192
9641
  composerSelectionEnd: 0,
9193
9642
  composerSelectionStart: 0,
9194
9643
  composerValue: '',
@@ -9200,7 +9649,7 @@ const handleSubmit = async state => {
9200
9649
  }));
9201
9650
  optimisticState = withUpdatedChatInputHistory(optimisticState, userText);
9202
9651
  }
9203
- set(state.uid, state, optimisticState);
9652
+ set(effectiveState.uid, effectiveState, optimisticState);
9204
9653
  // @ts-ignore
9205
9654
  await invoke('Chat.rerender');
9206
9655
  let handleTextChunkState = {
@@ -9289,11 +9738,11 @@ const handleSubmit = async state => {
9289
9738
  let finalParsedMessages = latestState.parsedMessages;
9290
9739
  let updatedSessions;
9291
9740
  if (streamingEnabled) {
9292
- const updated = await updateMessageTextInSelectedSession(latestState.sessions, finalParsedMessages, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false);
9741
+ const updated = await updateMessageTextInSelectedSession(latestState.sessions, finalParsedMessages, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false, latestState.useChatMessageParsingWorker);
9293
9742
  updatedSessions = updated.sessions;
9294
9743
  finalParsedMessages = updated.parsedMessages;
9295
9744
  } else {
9296
- finalParsedMessages = await parseAndStoreMessageContent(finalParsedMessages, assistantMessage);
9745
+ finalParsedMessages = await parseAndStoreMessageContentWithWorkerPreference(finalParsedMessages, assistantMessage, latestState.useChatMessageParsingWorker);
9297
9746
  updatedSessions = appendMessageToSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessage);
9298
9747
  }
9299
9748
  if (aiSessionTitleGenerationEnabled && createsNewSession) {
@@ -9322,6 +9771,84 @@ const handleClickSend = async state => {
9322
9771
  return handleSubmit(state);
9323
9772
  };
9324
9773
 
9774
+ const SwitchGitBranch = 'Chat.switchGitBranch';
9775
+ const switchGitBranch = async ({
9776
+ assetDir,
9777
+ branchName,
9778
+ platform,
9779
+ workspaceUri
9780
+ }) => {
9781
+ await executeProvider({
9782
+ assetDir,
9783
+ event: `onCommand:${SwitchGitBranch}`,
9784
+ method: CommandExecute,
9785
+ noProviderFoundMessage: 'No git branch switch command found',
9786
+ params: [SwitchGitBranch, {
9787
+ branchName,
9788
+ workspaceUri
9789
+ }],
9790
+ platform
9791
+ });
9792
+ };
9793
+
9794
+ const getGitBranchSwitchErrorMessage = (branchName, error) => {
9795
+ if (error instanceof Error && error.message) {
9796
+ return `Failed to switch to branch "${branchName}". ${error.message}`;
9797
+ }
9798
+ return `Failed to switch to branch "${branchName}".`;
9799
+ };
9800
+ const handleGitBranchChange = async (state, branchName) => {
9801
+ if (!branchName) {
9802
+ return state;
9803
+ }
9804
+ const selectedSession = getSelectedSession(state.sessions, state.selectedSessionId);
9805
+ const workspaceUri = getWorkspaceUri$1(state, selectedSession);
9806
+ if (!workspaceUri) {
9807
+ return {
9808
+ ...state,
9809
+ gitBranchPickerErrorMessage: 'No workspace is selected.',
9810
+ gitBranchPickerOpen: true
9811
+ };
9812
+ }
9813
+ try {
9814
+ await switchGitBranch({
9815
+ assetDir: state.assetDir,
9816
+ branchName,
9817
+ platform: state.platform,
9818
+ workspaceUri
9819
+ });
9820
+ const updatedSessions = state.sessions.map(session => {
9821
+ if (session.id !== state.selectedSessionId) {
9822
+ return session;
9823
+ }
9824
+ return {
9825
+ ...session,
9826
+ branchName
9827
+ };
9828
+ });
9829
+ const updatedSelectedSession = updatedSessions.find(session => session.id === state.selectedSessionId);
9830
+ if (updatedSelectedSession) {
9831
+ await saveChatSession(updatedSelectedSession);
9832
+ }
9833
+ return {
9834
+ ...state,
9835
+ gitBranches: state.gitBranches.map(branch => ({
9836
+ ...branch,
9837
+ current: branch.name === branchName
9838
+ })),
9839
+ gitBranchPickerErrorMessage: '',
9840
+ gitBranchPickerOpen: false,
9841
+ sessions: updatedSessions
9842
+ };
9843
+ } catch (error) {
9844
+ return {
9845
+ ...state,
9846
+ gitBranchPickerErrorMessage: getGitBranchSwitchErrorMessage(branchName, error),
9847
+ gitBranchPickerOpen: true
9848
+ };
9849
+ }
9850
+ };
9851
+
9325
9852
  const setReasoningEffort = (state, reasoningEffort) => {
9326
9853
  return {
9327
9854
  ...state,
@@ -9452,7 +9979,7 @@ const selectSession = async (state, id) => {
9452
9979
  }
9453
9980
  return loadedSession;
9454
9981
  });
9455
- return {
9982
+ return refreshGitBranchPickerVisibility({
9456
9983
  ...state,
9457
9984
  composerAttachments,
9458
9985
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, width),
@@ -9461,7 +9988,7 @@ const selectSession = async (state, id) => {
9461
9988
  selectedSessionId: id,
9462
9989
  sessions: hydratedSessions,
9463
9990
  viewMode: viewMode === 'chat-focus' ? 'chat-focus' : 'detail'
9464
- };
9991
+ });
9465
9992
  };
9466
9993
 
9467
9994
  const startRename = (state, id) => {
@@ -9491,15 +10018,17 @@ const toggleChatFocusMode = async state => {
9491
10018
  if (viewMode === 'chat-focus') {
9492
10019
  return {
9493
10020
  ...state,
10021
+ gitBranchPickerErrorMessage: '',
10022
+ gitBranchPickerOpen: false,
9494
10023
  viewMode: lastNormalViewMode
9495
10024
  };
9496
10025
  }
9497
10026
  if (viewMode === 'list' || viewMode === 'detail') {
9498
- return {
10027
+ return refreshGitBranchPickerVisibility({
9499
10028
  ...state,
9500
10029
  lastNormalViewMode: viewMode,
9501
10030
  viewMode: 'chat-focus'
9502
- };
10031
+ });
9503
10032
  }
9504
10033
  return state;
9505
10034
  };
@@ -9629,6 +10158,11 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
9629
10158
  visibleModels: state.models
9630
10159
  };
9631
10160
  }
10161
+ case isGitBranchPickerItemInputName(name):
10162
+ {
10163
+ const branchName = getGitBranchFromGitBranchPickerItemInputName(name);
10164
+ return handleGitBranchChange(state, branchName);
10165
+ }
9632
10166
  case isAgentModePickerItemInputName(name):
9633
10167
  {
9634
10168
  const agentMode = getAgentModeFromAgentModePickerItemInputName(name);
@@ -9753,6 +10287,71 @@ const handleClickFileName = async (state, uri) => {
9753
10287
  return state;
9754
10288
  };
9755
10289
 
10290
+ const getBranchPickerErrorMessage = error => {
10291
+ if (error instanceof Error && error.message) {
10292
+ return error.message;
10293
+ }
10294
+ return 'Failed to load git branches.';
10295
+ };
10296
+ const openGitBranchPicker = async state => {
10297
+ const visibleState = await refreshGitBranchPickerVisibility(state);
10298
+ const selectedSession = getSelectedSession(visibleState.sessions, visibleState.selectedSessionId);
10299
+ const fallbackBranchName = selectedSession?.branchName || '';
10300
+ if (visibleState.viewMode !== 'chat-focus' || !visibleState.gitBranchPickerVisible && !fallbackBranchName) {
10301
+ return {
10302
+ ...visibleState,
10303
+ gitBranchPickerOpen: false
10304
+ };
10305
+ }
10306
+ const workspaceUri = getWorkspaceUri$1(visibleState, selectedSession);
10307
+ if (!workspaceUri) {
10308
+ return {
10309
+ ...visibleState,
10310
+ gitBranches: [],
10311
+ gitBranchPickerErrorMessage: 'No workspace is selected.',
10312
+ gitBranchPickerOpen: true
10313
+ };
10314
+ }
10315
+ try {
10316
+ const gitBranches = await getGitBranches(workspaceUri);
10317
+ return {
10318
+ ...visibleState,
10319
+ agentModePickerOpen: false,
10320
+ gitBranches,
10321
+ gitBranchPickerErrorMessage: '',
10322
+ gitBranchPickerOpen: true,
10323
+ modelPickerOpen: false,
10324
+ modelPickerSearchValue: '',
10325
+ reasoningEffortPickerOpen: false,
10326
+ runModePickerOpen: false,
10327
+ visibleModels: visibleState.models
10328
+ };
10329
+ } catch (error) {
10330
+ return {
10331
+ ...visibleState,
10332
+ agentModePickerOpen: false,
10333
+ gitBranches: fallbackBranchName ? [{
10334
+ current: true,
10335
+ name: fallbackBranchName
10336
+ }] : [],
10337
+ gitBranchPickerErrorMessage: getBranchPickerErrorMessage(error),
10338
+ gitBranchPickerOpen: true,
10339
+ modelPickerOpen: false,
10340
+ modelPickerSearchValue: '',
10341
+ reasoningEffortPickerOpen: false,
10342
+ runModePickerOpen: false,
10343
+ visibleModels: visibleState.models
10344
+ };
10345
+ }
10346
+ };
10347
+
10348
+ const handleClickGitBranchPickerToggle = async state => {
10349
+ if (state.gitBranchPickerOpen) {
10350
+ return closeGitBranchPicker(state);
10351
+ }
10352
+ return openGitBranchPicker(state);
10353
+ };
10354
+
9756
10355
  const handleClickModelPickerList = async (state, eventY) => {
9757
10356
  const {
9758
10357
  height,
@@ -10615,19 +11214,6 @@ const getSavedSessions = savedState => {
10615
11214
  return sessions;
10616
11215
  };
10617
11216
 
10618
- const getSavedSystemPrompt = savedState => {
10619
- if (!isObject$1(savedState)) {
10620
- return undefined;
10621
- }
10622
- const {
10623
- systemPrompt
10624
- } = savedState;
10625
- if (typeof systemPrompt !== 'string') {
10626
- return undefined;
10627
- }
10628
- return systemPrompt;
10629
- };
10630
-
10631
11217
  const getSavedViewMode = savedState => {
10632
11218
  if (!isObject$1(savedState)) {
10633
11219
  return undefined;
@@ -10659,24 +11245,6 @@ const loadAuthEnabled = async () => {
10659
11245
  }
10660
11246
  };
10661
11247
 
10662
- const loadBackendAccessToken = async () => {
10663
- try {
10664
- const savedAccessToken = await get('secrets.chatBackendAccessToken');
10665
- return typeof savedAccessToken === 'string' ? savedAccessToken : '';
10666
- } catch {
10667
- return '';
10668
- }
10669
- };
10670
-
10671
- const loadBackendRefreshToken = async () => {
10672
- try {
10673
- const savedRefreshToken = await get('secrets.chatBackendRefreshToken');
10674
- return typeof savedRefreshToken === 'string' ? savedRefreshToken : '';
10675
- } catch {
10676
- return '';
10677
- }
10678
- };
10679
-
10680
11248
  const loadBackendUrl = async () => {
10681
11249
  try {
10682
11250
  const savedBackendUrl = await get('chat.backendUrl');
@@ -10857,12 +11425,10 @@ const loadVoiceDictationEnabled = async () => {
10857
11425
  };
10858
11426
 
10859
11427
  const loadPreferences = async () => {
10860
- const [aiSessionTitleGenerationEnabled, authAccessToken, authEnabled, authRefreshToken, backendUrl, chatHistoryEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, reasoningPickerEnabled, scrollDownButtonEnabled, searchEnabled, streamingEnabled, todoListToolEnabled, toolEnablement, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatMessageParsingWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadBackendAccessToken(), loadAuthEnabled(), loadBackendRefreshToken(), loadBackendUrl(), loadChatHistoryEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadReasoningPickerEnabled(), loadScrollDownButtonEnabled(), loadSearchEnabled(), loadStreamingEnabled(), loadTodoListToolEnabled(), loadToolEnablement(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatMessageParsingWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
11428
+ const [aiSessionTitleGenerationEnabled, authEnabled, backendUrl, chatHistoryEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, reasoningPickerEnabled, scrollDownButtonEnabled, searchEnabled, streamingEnabled, todoListToolEnabled, toolEnablement, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatMessageParsingWorker, useChatNetworkWorkerForRequests, useChatToolWorker, voiceDictationEnabled] = await Promise.all([loadAiSessionTitleGenerationEnabled(), loadAuthEnabled(), loadBackendUrl(), loadChatHistoryEnabled(), loadComposerDropEnabled(), loadOpenApiApiKey(), loadOpenRouterApiKey(), loadEmitStreamingFunctionCallEvents(), loadReasoningPickerEnabled(), loadScrollDownButtonEnabled(), loadSearchEnabled(), loadStreamingEnabled(), loadTodoListToolEnabled(), loadToolEnablement(), loadPassIncludeObfuscation(), loadUseChatCoordinatorWorker(), loadUseChatMathWorker(), loadUseChatMessageParsingWorker(), loadUseChatNetworkWorkerForRequests(), loadUseChatToolWorker(), loadVoiceDictationEnabled()]);
10861
11429
  return {
10862
11430
  aiSessionTitleGenerationEnabled,
10863
- authAccessToken,
10864
11431
  authEnabled,
10865
- authRefreshToken,
10866
11432
  backendUrl,
10867
11433
  chatHistoryEnabled,
10868
11434
  composerDropEnabled,
@@ -10934,9 +11500,7 @@ const loadContent = async (state, savedState) => {
10934
11500
  const [composerSelectionStart, composerSelectionEnd] = savedComposerSelection ?? [state.composerSelectionStart, state.composerSelectionEnd];
10935
11501
  const {
10936
11502
  aiSessionTitleGenerationEnabled,
10937
- authAccessToken,
10938
11503
  authEnabled,
10939
- authRefreshToken,
10940
11504
  backendUrl,
10941
11505
  chatHistoryEnabled,
10942
11506
  composerDropEnabled,
@@ -10952,11 +11516,13 @@ const loadContent = async (state, savedState) => {
10952
11516
  toolEnablement,
10953
11517
  useChatCoordinatorWorker,
10954
11518
  useChatMathWorker,
10955
- useChatMessageParsingWorker,
11519
+ // useChatMessageParsingWorker,
10956
11520
  useChatNetworkWorkerForRequests,
10957
11521
  useChatToolWorker,
10958
11522
  voiceDictationEnabled
10959
11523
  } = await loadPreferences();
11524
+ const useChatMessageParsingWorker = true;
11525
+ const authState = authEnabled && backendUrl ? await syncBackendAuth(backendUrl) : getLoggedOutBackendAuthState();
10960
11526
  const legacySavedSessions = getSavedSessions(savedState);
10961
11527
  const storedSessions = await listChatSessions();
10962
11528
  let sessions = storedSessions;
@@ -10992,7 +11558,6 @@ const loadContent = async (state, savedState) => {
10992
11558
  const projectExpandedIds = (savedProjectExpandedIds || state.projectExpandedIds).filter(id => projects.some(project => project.id === id));
10993
11559
  const reasoningEffort = getSavedReasoningEffort(savedState) ?? state.reasoningEffort;
10994
11560
  const selectedModelId = state.models.some(model => model.id === preferredModelId) ? preferredModelId : state.models[0]?.id || '';
10995
- const systemPrompt = getSavedSystemPrompt(savedState) ?? state.systemPrompt;
10996
11561
  const visibleModels = getVisibleModels(state.models, '');
10997
11562
  const visibleSessions = getVisibleSessions(sessions, selectedProjectId);
10998
11563
  const selectedSessionId = visibleSessions.some(session => session.id === preferredSessionId) ? preferredSessionId : visibleSessions[0]?.id || '';
@@ -11008,15 +11573,14 @@ const loadContent = async (state, savedState) => {
11008
11573
  const savedLastNormalViewMode = getSavedLastNormalViewMode(savedState);
11009
11574
  const lastNormalViewMode = savedLastNormalViewMode || (preferredViewMode === 'detail' ? 'detail' : state.lastNormalViewMode);
11010
11575
  const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode;
11011
- return {
11576
+ const nextState = {
11012
11577
  ...state,
11013
11578
  agentMode,
11014
11579
  agentModePickerOpen: false,
11015
11580
  aiSessionTitleGenerationEnabled,
11016
- authAccessToken,
11581
+ authAccessToken: authState.authAccessToken,
11017
11582
  authEnabled,
11018
- authRefreshToken,
11019
- authStatus: authAccessToken ? 'signed-in' : 'signed-out',
11583
+ authErrorMessage: authState.authErrorMessage,
11020
11584
  backendUrl,
11021
11585
  chatHistoryEnabled,
11022
11586
  chatListScrollTop,
@@ -11057,7 +11621,6 @@ const loadContent = async (state, savedState) => {
11057
11621
  selectedSessionId,
11058
11622
  sessions,
11059
11623
  streamingEnabled,
11060
- systemPrompt,
11061
11624
  todoListToolEnabled,
11062
11625
  toolEnablement,
11063
11626
  useChatCoordinatorWorker,
@@ -11065,10 +11628,15 @@ const loadContent = async (state, savedState) => {
11065
11628
  useChatMessageParsingWorker,
11066
11629
  useChatNetworkWorkerForRequests,
11067
11630
  useChatToolWorker,
11631
+ userName: authState.userName,
11632
+ userState: authState.userState,
11633
+ userSubscriptionPlan: authState.userSubscriptionPlan,
11634
+ userUsedTokens: authState.userUsedTokens,
11068
11635
  viewMode,
11069
11636
  visibleModels,
11070
11637
  voiceDictationEnabled
11071
11638
  };
11639
+ return refreshGitBranchPickerVisibility(nextState);
11072
11640
  };
11073
11641
 
11074
11642
  const isObject = value => {
@@ -11158,7 +11726,24 @@ const openMockProject = async (state, projectId, projectName, projectUri) => {
11158
11726
  };
11159
11727
  };
11160
11728
 
11161
- const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11729
+ const applySessionOptions = (session, options) => {
11730
+ if (!options) {
11731
+ return session;
11732
+ }
11733
+ return {
11734
+ ...session,
11735
+ ...(options.branchName ? {
11736
+ branchName: options.branchName
11737
+ } : {}),
11738
+ ...(options.projectId ? {
11739
+ projectId: options.projectId
11740
+ } : {}),
11741
+ ...(options.workspaceUri ? {
11742
+ workspaceUri: options.workspaceUri
11743
+ } : {})
11744
+ };
11745
+ };
11746
+ const openMockSession = async (state, mockSessionId, mockChatMessages, options) => {
11162
11747
  const {
11163
11748
  sessions: currentSessions
11164
11749
  } = state;
@@ -11171,20 +11756,20 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11171
11756
  if (session.id !== mockSessionId) {
11172
11757
  return session;
11173
11758
  }
11174
- return {
11759
+ return applySessionOptions({
11175
11760
  ...session,
11176
11761
  messages: mockChatMessages
11177
- };
11178
- }) : [...currentSessions, {
11762
+ }, options);
11763
+ }) : [...currentSessions, applySessionOptions({
11179
11764
  id: mockSessionId,
11180
11765
  messages: mockChatMessages,
11181
11766
  title: mockSessionId
11182
- }];
11767
+ }, options)];
11183
11768
  const selectedSession = sessions.find(session => session.id === mockSessionId);
11184
11769
  if (selectedSession) {
11185
11770
  await saveChatSession(selectedSession);
11186
11771
  }
11187
- return {
11772
+ return refreshGitBranchPickerVisibility({
11188
11773
  ...state,
11189
11774
  composerAttachments: [],
11190
11775
  composerAttachmentsHeight: 0,
@@ -11193,7 +11778,7 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11193
11778
  selectedSessionId: mockSessionId,
11194
11779
  sessions,
11195
11780
  viewMode: 'detail'
11196
- };
11781
+ });
11197
11782
  };
11198
11783
 
11199
11784
  const pasteInput = async state => {
@@ -11306,6 +11891,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11306
11891
  color: var(--vscode-errorForeground, var(--vscode-foreground));
11307
11892
  }
11308
11893
 
11894
+ .InputInvalid{
11895
+ border-color: var(--InputValidationErrorBorder, red);
11896
+ }
11897
+
11309
11898
  .ChatComposerAttachmentTextFile{
11310
11899
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11311
11900
  }
@@ -11360,6 +11949,23 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11360
11949
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11361
11950
  }
11362
11951
 
11952
+ .ChatImageMessageContent{
11953
+ padding: 6px;
11954
+ border: 1px solid var(--vscode-widget-border, var(--vscode-panel-border));
11955
+ border-radius: 12px;
11956
+ background: var(--vscode-editorWidget-background, var(--vscode-editor-background));
11957
+ overflow: hidden;
11958
+ }
11959
+
11960
+ .ChatMessageImage{
11961
+ display: block;
11962
+ max-width: min(320px, 100%);
11963
+ max-height: min(320px, calc(100vh - 220px));
11964
+ border-radius: 8px;
11965
+ object-fit: contain;
11966
+ background: color-mix(in srgb, var(--vscode-editor-background) 88%, black);
11967
+ }
11968
+
11363
11969
  .Chat{
11364
11970
  position: relative;
11365
11971
  }
@@ -11403,6 +12009,14 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11403
12009
 
11404
12010
  }
11405
12011
 
12012
+ .ChatGitBranchPickerMessage{
12013
+ padding: 6px 8px;
12014
+ }
12015
+
12016
+ .ChatGitBranchPickerErrorMessage{
12017
+ color: var(--vscode-errorForeground, var(--vscode-foreground));
12018
+ }
12019
+
11406
12020
  .RunModePickerContainer{
11407
12021
  display: flex;
11408
12022
  justify-content: flex-end;
@@ -11507,13 +12121,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11507
12121
  .ChatFocus .ChatMessages > .Message > .ChatMessageContent{
11508
12122
  max-inline-size: 100%;
11509
12123
  }
11510
-
11511
-
11512
12124
  .Viewlet.Chat.ChatFocus{
11513
12125
  display: flex !important;
11514
12126
  min-width: 0;
11515
12127
  }
11516
-
11517
12128
  .ChatFocusMainArea{
11518
12129
  display: flex;
11519
12130
  flex: 1;
@@ -11525,7 +12136,6 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11525
12136
  flex: 1;
11526
12137
  min-height: 0;
11527
12138
  }
11528
-
11529
12139
  `;
11530
12140
  return `${baseCss}
11531
12141
 
@@ -11670,7 +12280,8 @@ const HandlePointerUpModelPickerList = 55;
11670
12280
  const HandleClickReasoningEffortPickerToggle = 56;
11671
12281
  const HandleClickAgentModePickerToggle = 57;
11672
12282
  const HandleContextMenuChatImageAttachment = 58;
11673
- const HandleErrorComposerAttachmentPreviewOverlay = 59;
12283
+ const HandleClickGitBranchPickerToggle = 59;
12284
+ const HandleErrorComposerAttachmentPreviewOverlay = 60;
11674
12285
 
11675
12286
  const getAddContextButtonDom = () => {
11676
12287
  return [{
@@ -11685,10 +12296,12 @@ const getAddContextButtonDom = () => {
11685
12296
  }];
11686
12297
  };
11687
12298
 
11688
- const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = label) => {
12299
+ const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = label, ariaLabel = title) => {
11689
12300
  return [{
11690
12301
  'aria-expanded': open ? 'true' : 'false',
11691
12302
  'aria-haspopup': 'true',
12303
+ 'aria-label': ariaLabel,
12304
+ ariaLabel,
11692
12305
  childCount: 2,
11693
12306
  className: Select,
11694
12307
  inputType: 'button',
@@ -11711,12 +12324,12 @@ const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = lab
11711
12324
  }];
11712
12325
  };
11713
12326
 
11714
- const getCustomSelectPickerToggleVirtualDom = (label, name, open, onClick, title = label, containerChildCount = 1) => {
12327
+ const getCustomSelectPickerToggleVirtualDom = (label, name, open, onClick, title = label, ariaLabel = title, containerChildCount = 1) => {
11715
12328
  return [{
11716
12329
  childCount: containerChildCount,
11717
12330
  className: CustomSelectContainer,
11718
12331
  type: Div
11719
- }, ...getCustomSelectToggleVirtualDom(label, name, open, onClick, title)];
12332
+ }, ...getCustomSelectToggleVirtualDom(label, name, open, onClick, title, ariaLabel)];
11720
12333
  };
11721
12334
 
11722
12335
  const getAgentModePickerVirtualDom = (selectedAgentMode, agentModePickerOpen) => {
@@ -11726,7 +12339,8 @@ const getAgentModePickerVirtualDom = (selectedAgentMode, agentModePickerOpen) =>
11726
12339
  const getChatModelPickerToggleVirtualDom = (models, selectedModelId, modelPickerOpen) => {
11727
12340
  const selectedModel = models.find(model => model.id === selectedModelId);
11728
12341
  const selectedModelLabel = selectedModel ? selectedModel.name : selectedModelId;
11729
- return getCustomSelectPickerToggleVirtualDom(selectedModelLabel, ModelPickerToggle, modelPickerOpen, HandleClickModelPickerToggle);
12342
+ const pickModelLabel = pickModel(selectedModelLabel);
12343
+ return getCustomSelectPickerToggleVirtualDom(selectedModelLabel, ModelPickerToggle, modelPickerOpen, HandleClickModelPickerToggle, pickModelLabel, pickModelLabel);
11730
12344
  };
11731
12345
 
11732
12346
  const getCreatePullRequestButtonDom = () => {
@@ -11770,6 +12384,55 @@ const getCustomSelectOptionVirtualDom = (name, label, selected, detail = '') =>
11770
12384
  }, text(detail)] : [])];
11771
12385
  };
11772
12386
 
12387
+ const itemHeight = 28;
12388
+ const messageHeight = 32;
12389
+ const getCurrentBranchLabel = (gitBranches, fallbackBranchName) => {
12390
+ const currentBranch = gitBranches.find(branch => branch.current);
12391
+ if (currentBranch) {
12392
+ return currentBranch.name;
12393
+ }
12394
+ if (fallbackBranchName) {
12395
+ return fallbackBranchName;
12396
+ }
12397
+ return 'Branch';
12398
+ };
12399
+ const getBranchOptionsVirtualDom = gitBranches => {
12400
+ return gitBranches.flatMap(branch => {
12401
+ return getCustomSelectOptionVirtualDom(getGitBranchPickerItemInputName(branch.name), branch.name, branch.current, branch.current ? 'current' : '');
12402
+ });
12403
+ };
12404
+ const getBranchPickerMessageDom = (gitBranches, errorMessage) => {
12405
+ const message = errorMessage || (gitBranches.length === 0 ? 'No local git branches found.' : '');
12406
+ if (!message) {
12407
+ return [];
12408
+ }
12409
+ return [{
12410
+ childCount: 1,
12411
+ className: mergeClassNames(ChatGitBranchPickerMessage, errorMessage ? ChatGitBranchPickerErrorMessage : Empty),
12412
+ type: Div
12413
+ }, {
12414
+ text: message,
12415
+ type: Text
12416
+ }];
12417
+ };
12418
+ const getGitBranchPickerVirtualDom = (gitBranches, gitBranchPickerOpen, gitBranchPickerErrorMessage, fallbackBranchName) => {
12419
+ const label = getCurrentBranchLabel(gitBranches, fallbackBranchName);
12420
+ const branchOptions = getBranchOptionsVirtualDom(gitBranches);
12421
+ const messageDom = getBranchPickerMessageDom(gitBranches, gitBranchPickerErrorMessage);
12422
+ const showMessage = messageDom.length > 0;
12423
+ const popOverHeight = gitBranches.length * itemHeight + (showMessage ? messageHeight : 0);
12424
+ return [...getCustomSelectPickerToggleVirtualDom(label, GitBranchPickerToggle, gitBranchPickerOpen, HandleClickGitBranchPickerToggle, 'Switch branch', 'Switch branch', gitBranchPickerOpen ? 2 : 1), ...(gitBranchPickerOpen ? [{
12425
+ childCount: (showMessage ? 1 : 0) + 1,
12426
+ className: mergeClassNames(ChatModelPicker, CustomSelectPopOver, ChatGitBranchPicker),
12427
+ style: `height: ${popOverHeight}px;`,
12428
+ type: Div
12429
+ }, ...messageDom, {
12430
+ childCount: branchOptions.length / 4,
12431
+ className: ChatModelPickerList,
12432
+ type: Ul
12433
+ }, ...branchOptions] : [])];
12434
+ };
12435
+
11773
12436
  const reasoningEffortPickerHeight = reasoningEfforts.length * 28;
11774
12437
  const getReasoningEffortOptionsVirtualDom = selectedReasoningEffort => {
11775
12438
  return reasoningEfforts.flatMap(reasoningEffort => {
@@ -11779,7 +12442,7 @@ const getReasoningEffortOptionsVirtualDom = selectedReasoningEffort => {
11779
12442
  });
11780
12443
  };
11781
12444
  const getReasoningEffortPickerVirtualDom = (selectedReasoningEffort, reasoningEffortPickerOpen) => {
11782
- return [...getCustomSelectPickerToggleVirtualDom(getReasoningEffortLabel(selectedReasoningEffort), ReasoningEffortPickerToggle, reasoningEffortPickerOpen, HandleClickReasoningEffortPickerToggle, 'Reasoning', reasoningEffortPickerOpen ? 2 : 1), ...(reasoningEffortPickerOpen ? [{
12445
+ return [...getCustomSelectPickerToggleVirtualDom(getReasoningEffortLabel(selectedReasoningEffort), ReasoningEffortPickerToggle, reasoningEffortPickerOpen, HandleClickReasoningEffortPickerToggle, 'Reasoning', 'Reasoning', reasoningEffortPickerOpen ? 2 : 1), ...(reasoningEffortPickerOpen ? [{
11783
12446
  childCount: 1,
11784
12447
  className: mergeClassNames(ChatModelPicker, CustomSelectPopOver),
11785
12448
  style: `height: ${reasoningEffortPickerHeight}px;`,
@@ -11946,6 +12609,7 @@ const getComposerAttachmentClassName = displayType => {
11946
12609
  };
11947
12610
  const getComposerAttachmentRemoveButtonDom = attachment => {
11948
12611
  return [{
12612
+ 'aria-label': removeAttachment(),
11949
12613
  buttonType: 'button',
11950
12614
  childCount: 1,
11951
12615
  className: ChatComposerAttachmentRemoveButton,
@@ -12017,12 +12681,13 @@ const getComposerTextAreaDom = () => {
12017
12681
  type: TextArea
12018
12682
  };
12019
12683
  };
12020
- const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton = false, voiceDictationEnabled = false, scrollDownButtonEnabled = false, messagesAutoScrollEnabled = true) => {
12684
+ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agentModePickerOpen, gitBranchPickerVisible, gitBranchPickerOpen, gitBranchPickerErrorMessage, gitBranches, fallbackBranchName, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton = false, voiceDictationEnabled = false, scrollDownButtonEnabled = false, messagesAutoScrollEnabled = true) => {
12021
12685
  const isSendDisabled = composerValue.trim() === '';
12022
12686
  const showAgentModePicker = hasSpaceForAgentModePicker;
12687
+ const showGitBranchPicker = gitBranchPickerVisible || Boolean(fallbackBranchName);
12023
12688
  const showResponsiveRunModePicker = showRunMode && hasSpaceForRunModePicker;
12024
12689
  const showScrollDownButton = scrollDownButtonEnabled && !messagesAutoScrollEnabled;
12025
- const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (voiceDictationEnabled ? 1 : 0) + (showScrollDownButton ? 1 : 0);
12690
+ const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (showGitBranchPicker ? 1 : 0) + (voiceDictationEnabled ? 1 : 0) + (showScrollDownButton ? 1 : 0);
12026
12691
  const primaryControlsCount = 1 + (showAgentModePicker ? 1 : 0) + (reasoningPickerEnabled ? 1 : 0) + (showResponsiveRunModePicker ? 1 : 0);
12027
12692
  const hasTodoList = todoListToolEnabled && todoListItems.length > 0;
12028
12693
  const hasComposerAttachments = composerAttachments.length > 0;
@@ -12046,7 +12711,62 @@ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agent
12046
12711
  className: ChatSendAreaPrimaryControls,
12047
12712
  role: 'toolbar',
12048
12713
  type: Div
12049
- }, ...(showAgentModePicker ? getAgentModePickerVirtualDom(agentMode, agentModePickerOpen) : []), ...getChatModelPickerToggleVirtualDom(models, selectedModelId, modelPickerOpen), ...(reasoningPickerEnabled ? getReasoningEffortPickerVirtualDom(reasoningEffort, reasoningEffortPickerOpen) : []), ...(showResponsiveRunModePicker ? getRunModePickerVirtualDom(runMode, runModePickerOpen) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...(addContextButtonEnabled ? getAddContextButtonDom() : []), ...(showCreatePullRequestButton ? getCreatePullRequestButtonDom() : []), ...(showScrollDownButton ? getScrollDownButtonDom() : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
12714
+ }, ...(showAgentModePicker ? getAgentModePickerVirtualDom(agentMode, agentModePickerOpen) : []), ...getChatModelPickerToggleVirtualDom(models, selectedModelId, modelPickerOpen), ...(reasoningPickerEnabled ? getReasoningEffortPickerVirtualDom(reasoningEffort, reasoningEffortPickerOpen) : []), ...(showResponsiveRunModePicker ? getRunModePickerVirtualDom(runMode, runModePickerOpen) : []), ...(usageOverviewEnabled ? getUsageOverviewDom(tokensUsed, tokensMax) : []), ...(addContextButtonEnabled ? getAddContextButtonDom() : []), ...(showCreatePullRequestButton ? getCreatePullRequestButtonDom() : []), ...(showGitBranchPicker ? getGitBranchPickerVirtualDom(gitBranches, gitBranchPickerOpen, gitBranchPickerErrorMessage, fallbackBranchName) : []), ...(showScrollDownButton ? getScrollDownButtonDom() : []), ...getSendButtonDom(isSendDisabled, voiceDictationEnabled)];
12715
+ };
12716
+
12717
+ const authContainerStyle = 'align-items:center;display:flex;gap:8px;min-width:0;';
12718
+ const authNameStyle = 'max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
12719
+ const authButtonStyle = 'white-space:nowrap;';
12720
+ const getButtonLabel = (userState, isAuthenticated) => {
12721
+ if (userState === 'loggingIn') {
12722
+ return loggingInToBackend();
12723
+ }
12724
+ if (userState === 'loggingOut') {
12725
+ return loggingOutFromBackend();
12726
+ }
12727
+ return isAuthenticated ? logout() : login();
12728
+ };
12729
+ const getButtonTitle = (userState, isAuthenticated) => {
12730
+ if (userState === 'loggingIn') {
12731
+ return loggingInToBackend();
12732
+ }
12733
+ if (userState === 'loggingOut') {
12734
+ return loggingOutFromBackend();
12735
+ }
12736
+ return isAuthenticated ? logoutFromBackend$1() : loginToBackend();
12737
+ };
12738
+ const getChatHeaderAuthDom = (authEnabled = false, userState = 'loggedOut', userName = '') => {
12739
+ if (!authEnabled) {
12740
+ return [];
12741
+ }
12742
+ const isAuthenticated = userState === 'loggedIn' || userState === 'loggingOut';
12743
+ const buttonName = isAuthenticated ? Logout : Login;
12744
+ const buttonLabel = getButtonLabel(userState, isAuthenticated);
12745
+ const buttonTitle = getButtonTitle(userState, isAuthenticated);
12746
+ const isPending = userState === 'loggingIn' || userState === 'loggingOut';
12747
+ const displayName = userName || signedIn();
12748
+ return [{
12749
+ childCount: isAuthenticated ? 2 : 1,
12750
+ className: ChatHeaderAuth,
12751
+ style: authContainerStyle,
12752
+ type: Div
12753
+ }, ...(isAuthenticated ? [{
12754
+ childCount: 1,
12755
+ className: ChatHeaderAuthName,
12756
+ style: authNameStyle,
12757
+ title: displayName,
12758
+ type: Span
12759
+ }, text(displayName)] : []), {
12760
+ childCount: 1,
12761
+ className: mergeClassNames(Button, ButtonSecondary),
12762
+ disabled: isPending,
12763
+ inputType: 'button',
12764
+ name: buttonName,
12765
+ onClick: HandleClick,
12766
+ style: authButtonStyle,
12767
+ title: buttonTitle,
12768
+ type: Button$1
12769
+ }, text(buttonLabel)];
12050
12770
  };
12051
12771
 
12052
12772
  const focusHeaderStyle = 'align-items:center;border-bottom:1px solid var(--vscode-panel-border, transparent);display:flex;gap:12px;justify-content:space-between;padding:8px 12px;';
@@ -12067,15 +12787,15 @@ const getFocusHeaderActionButtonDom = (label, name) => {
12067
12787
  type: Button$1
12068
12788
  }, text(label)];
12069
12789
  };
12070
- const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName) => {
12790
+ const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName, authEnabled = false, userState = 'loggedOut', userName = '') => {
12071
12791
  const items = [[addAction(), FocusAddAction], [openInVsCode(), FocusOpenInVsCode], [commit(), FocusCommit], [openTerminal(), FocusOpenTerminal], [showDiff(), FocusShowDiff]];
12072
12792
  const hasProjectName = !!selectedProjectName;
12073
12793
  return [{
12074
- childCount: 2,
12794
+ childCount: 2 + (authEnabled ? 1 : 0),
12075
12795
  className: ChatFocusHeader,
12076
12796
  style: focusHeaderStyle,
12077
12797
  type: Header
12078
- }, {
12798
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
12079
12799
  childCount: hasProjectName ? 2 : 1,
12080
12800
  className: ChatName,
12081
12801
  style: focusHeaderMetaStyle,
@@ -12658,6 +13378,7 @@ const getMissingApiActionsDom = ({
12658
13378
  };
12659
13379
  const getMissingApiKeyDom = ({
12660
13380
  getApiKeyText,
13381
+ inputClassName,
12661
13382
  inputName,
12662
13383
  inputPattern,
12663
13384
  inputRequired = false,
@@ -12680,7 +13401,7 @@ const getMissingApiKeyDom = ({
12680
13401
  autocomplete: 'off',
12681
13402
  autocorrect: 'off',
12682
13403
  childCount: 0,
12683
- className: InputBox,
13404
+ className: inputClassName ? mergeClassNames(InputBox, inputClassName) : InputBox,
12684
13405
  name: inputName,
12685
13406
  onInput: HandleInput,
12686
13407
  ...(inputPattern ? {
@@ -12700,13 +13421,17 @@ const getMissingApiKeyDom = ({
12700
13421
  })];
12701
13422
  };
12702
13423
 
12703
- const getMissingOpenApiApiKeyDom = (openApiApiKeyState = 'idle', openSettingsUrl = 'https://platform.openai.com/api-keys', inputPattern = '^sk-.+') => {
13424
+ const getMissingOpenApiApiKeyDom = (openApiApiKeyInput = '', openApiApiKeyState = 'idle', openSettingsUrl = 'https://platform.openai.com/api-keys', inputPattern = '^sk-.+') => {
12704
13425
  const isSaving = openApiApiKeyState === 'saving';
13426
+ const isInvalid = openApiApiKeyInput !== '' && !new RegExp(inputPattern).test(openApiApiKeyInput);
12705
13427
  return getMissingApiKeyDom({
12706
13428
  getApiKeyText: getOpenApiApiKey(),
13429
+ ...(isInvalid ? {
13430
+ inputClassName: InputInvalid
13431
+ } : {}),
12707
13432
  inputName: OpenApiApiKeyInput,
12708
13433
  inputPattern,
12709
- inputRequired: true,
13434
+ inputRequired: false,
12710
13435
  onSubmit: HandleMissingOpenAiApiKeyFormSubmit,
12711
13436
  openSettingsButtonName: OpenOpenApiApiKeyWebsite,
12712
13437
  openSettingsUrl,
@@ -13696,14 +14421,27 @@ const getChatAttachmentsDom = attachments => {
13696
14421
  }];
13697
14422
  })];
13698
14423
  };
13699
- const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput, _openApiApiKeyInput = '', openApiApiKeyState = 'idle', openApiApiKeysSettingsUrl = 'https://platform.openai.com/api-keys', openApiApiKeyInputPattern = '^sk-.+', openRouterApiKeyState = 'idle', useChatMathWorker = false) => {
14424
+ const getStandaloneImageAttachmentDom = attachment => {
14425
+ if (!attachment.previewSrc) {
14426
+ return getChatAttachmentsDom([attachment]);
14427
+ }
14428
+ return [{
14429
+ alt: attachment.name,
14430
+ childCount: 0,
14431
+ className: ChatMessageImage,
14432
+ src: attachment.previewSrc,
14433
+ type: Img
14434
+ }];
14435
+ };
14436
+ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput, openApiApiKeyInput = '', openApiApiKeyState = 'idle', openApiApiKeysSettingsUrl = 'https://platform.openai.com/api-keys', openApiApiKeyInputPattern = '^sk-.+', openRouterApiKeyState = 'idle', useChatMathWorker = false, standaloneImageAttachment) => {
13700
14437
  const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
13701
14438
  const isOpenApiApiKeyMissingMessage = message.role === 'assistant' && message.text === openApiApiKeyRequiredMessage;
13702
14439
  const isOpenRouterApiKeyMissingMessage = message.role === 'assistant' && message.text === openRouterApiKeyRequiredMessage;
13703
14440
  const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
13704
14441
  const isOpenRouterTooManyRequestsMessage = message.role === 'assistant' && message.text.startsWith(openRouterTooManyRequestsMessage);
13705
- const messageDom = getMessageContentDom(parsedMessageContent, useChatMathWorker);
13706
- const attachmentsDom = message.role === 'user' ? getChatAttachmentsDom(message.attachments ?? []) : [];
14442
+ const isStandaloneImageMessage = !!standaloneImageAttachment;
14443
+ const messageDom = isStandaloneImageMessage ? getStandaloneImageAttachmentDom(standaloneImageAttachment) : getMessageContentDom(parsedMessageContent, useChatMathWorker);
14444
+ const attachmentsDom = !isStandaloneImageMessage && message.role === 'user' ? getChatAttachmentsDom(message.attachments ?? []) : [];
13707
14445
  const toolCallsDom = getToolCallsDom(message);
13708
14446
  const toolCallsChildCount = toolCallsDom.length > 0 ? 1 : 0;
13709
14447
  const messageDomChildCount = getTopLevelNodeCount(messageDom);
@@ -13715,9 +14453,9 @@ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput
13715
14453
  type: Div
13716
14454
  }, {
13717
14455
  childCount: extraChildCount,
13718
- className: ChatMessageContent,
14456
+ className: isStandaloneImageMessage ? mergeClassNames(ChatMessageContent, ChatImageMessageContent) : ChatMessageContent,
13719
14457
  type: Div
13720
- }, ...toolCallsDom, ...messageDom, ...attachmentsDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
14458
+ }, ...toolCallsDom, ...messageDom, ...attachmentsDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
13721
14459
  };
13722
14460
 
13723
14461
  const parentNode$1 = {
@@ -13733,6 +14471,22 @@ const getEmptyMessagesDom = () => {
13733
14471
  const hasMessageText = message => {
13734
14472
  return message.text.trim().length > 0;
13735
14473
  };
14474
+ const isImageAttachment = attachment => {
14475
+ return attachment.displayType === 'image';
14476
+ };
14477
+ const withAttachments = (message, attachments) => {
14478
+ const {
14479
+ attachments: _attachments,
14480
+ ...messageWithoutAttachments
14481
+ } = message;
14482
+ if (attachments.length === 0) {
14483
+ return messageWithoutAttachments;
14484
+ }
14485
+ return {
14486
+ ...messageWithoutAttachments,
14487
+ attachments
14488
+ };
14489
+ };
13736
14490
  const getDisplayMessages = (messages, parsedMessages) => {
13737
14491
  const displayMessages = [];
13738
14492
  for (const message of messages) {
@@ -13740,6 +14494,30 @@ const getDisplayMessages = (messages, parsedMessages) => {
13740
14494
  if (!parsedContent) {
13741
14495
  continue;
13742
14496
  }
14497
+ if (message.role === 'user') {
14498
+ const attachments = message.attachments ?? [];
14499
+ const imageAttachments = attachments.filter(isImageAttachment);
14500
+ if (imageAttachments.length > 0) {
14501
+ const nonImageAttachments = attachments.filter(attachment => !isImageAttachment(attachment));
14502
+ if (hasMessageText(message) || nonImageAttachments.length > 0) {
14503
+ displayMessages.push({
14504
+ message: withAttachments(message, nonImageAttachments),
14505
+ parsedContent: hasMessageText(message) ? parsedContent : getEmptyMessageContent()
14506
+ });
14507
+ }
14508
+ for (const attachment of imageAttachments) {
14509
+ displayMessages.push({
14510
+ message: {
14511
+ ...withAttachments(message, [attachment]),
14512
+ text: ''
14513
+ },
14514
+ parsedContent: getEmptyMessageContent(),
14515
+ standaloneImageAttachment: attachment
14516
+ });
14517
+ }
14518
+ continue;
14519
+ }
14520
+ }
13743
14521
  if (message.role !== 'assistant' || !message.toolCalls || message.toolCalls.length === 0) {
13744
14522
  displayMessages.push({
13745
14523
  message,
@@ -13795,7 +14573,7 @@ const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApi
13795
14573
  role: 'log',
13796
14574
  scrollTop: messagesScrollTop,
13797
14575
  type: Div
13798
- }, ...displayMessages.flatMap(item => getChatMessageDom(item.message, item.parsedContent, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, useChatMathWorker))];
14576
+ }, ...displayMessages.flatMap(item => getChatMessageDom(item.message, item.parsedContent, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, useChatMathWorker, item.standaloneImageAttachment))];
13799
14577
  };
13800
14578
 
13801
14579
  const arrowLeft$1 = {
@@ -13909,7 +14687,6 @@ const getChatModeChatFocusVirtualDom = ({
13909
14687
  agentModePickerOpen = false,
13910
14688
  authEnabled = false,
13911
14689
  authErrorMessage = '',
13912
- authStatus = 'signed-out',
13913
14690
  composerAttachmentPreviewOverlayAttachmentId,
13914
14691
  composerAttachmentPreviewOverlayError = false,
13915
14692
  composerAttachments,
@@ -13920,6 +14697,10 @@ const getChatModeChatFocusVirtualDom = ({
13920
14697
  composerHeight = 28,
13921
14698
  composerLineHeight = 20,
13922
14699
  composerValue,
14700
+ gitBranches,
14701
+ gitBranchPickerErrorMessage,
14702
+ gitBranchPickerOpen,
14703
+ gitBranchPickerVisible,
13923
14704
  hasSpaceForAgentModePicker,
13924
14705
  hasSpaceForRunModePicker,
13925
14706
  messagesAutoScrollEnabled,
@@ -13954,14 +14735,15 @@ const getChatModeChatFocusVirtualDom = ({
13954
14735
  tokensUsed,
13955
14736
  usageOverviewEnabled,
13956
14737
  useChatMathWorker = false,
14738
+ userName = '',
14739
+ userState = 'loggedOut',
13957
14740
  visibleModels = models,
13958
14741
  voiceDictationEnabled = false
13959
14742
  }) => {
13960
14743
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
13961
- const selectedProject = projects.find(project => project.id === selectedProjectId);
13962
- const messages = selectedSession ? selectedSession.messages : [];
13963
14744
  const selectedSessionTitle = selectedSession?.title || chatTitle();
13964
- const selectedProjectName = selectedProject?.name || '';
14745
+ const selectedProjectName = projects.find(project => project.id === selectedProjectId)?.name || '';
14746
+ const messages = selectedSession ? selectedSession.messages : [];
13965
14747
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13966
14748
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13967
14749
  const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
@@ -13980,7 +14762,7 @@ const getChatModeChatFocusVirtualDom = ({
13980
14762
  childCount: 3,
13981
14763
  className: ChatFocusMainArea,
13982
14764
  type: Div
13983
- }, ...getChatHeaderDomFocusMode(selectedSessionTitle, selectedProjectName), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
14765
+ }, ...getChatHeaderDomFocusMode(selectedSessionTitle, selectedProjectName, authEnabled, userState, userName), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker, true), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, gitBranchPickerVisible, gitBranchPickerOpen, gitBranchPickerErrorMessage, gitBranches, selectedSession?.branchName || '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
13984
14766
  agentMode,
13985
14767
  agentModePickerVisible: isAgentModePickerVisible,
13986
14768
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14033,31 +14815,8 @@ const getHeaderActionVirtualDom = item => {
14033
14815
  }];
14034
14816
  };
14035
14817
 
14036
- const getAuthAction = (authEnabled, authStatus) => {
14037
- const isSigningIn = authStatus === 'signing-in';
14038
- if (!authEnabled) {
14039
- return undefined;
14040
- }
14041
- if (authStatus !== 'signed-in') {
14042
- return {
14043
- disabled: isSigningIn,
14044
- icon: mergeClassNames(MaskIcon, MaskIconAccount),
14045
- name: Login,
14046
- onClick: HandleClick,
14047
- title: isSigningIn ? loggingInToBackend() : loginToBackend()
14048
- };
14049
- }
14050
- return {
14051
- disabled: false,
14052
- icon: mergeClassNames(MaskIcon, MaskIconSignOut),
14053
- name: Logout,
14054
- onClick: HandleClick,
14055
- title: logoutFromBackend()
14056
- };
14057
- };
14058
- const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'signed-out', searchEnabled = false) => {
14818
+ const getChatHeaderActionsDom = (viewMode, searchEnabled = false) => {
14059
14819
  const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
14060
- const authAction = getAuthAction(authEnabled, authStatus);
14061
14820
  const items = [{
14062
14821
  icon: mergeClassNames(MaskIcon, MaskIconLayoutPanelLeft),
14063
14822
  name: ToggleChatFocus,
@@ -14083,7 +14842,7 @@ const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'si
14083
14842
  name: Settings,
14084
14843
  onClick: HandleClickSettings,
14085
14844
  title: settings()
14086
- }, ...(authAction ? [authAction] : []), {
14845
+ }, {
14087
14846
  icon: mergeClassNames(MaskIcon, MaskIconClose),
14088
14847
  name: CloseChat,
14089
14848
  onClick: HandleClickClose,
@@ -14108,14 +14867,14 @@ const getAuthErrorDom = (hasAuthError, authErrorMessage) => {
14108
14867
  type: Span
14109
14868
  }, text(authErrorMessage)];
14110
14869
  };
14111
- const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
14870
+ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, userState = 'loggedOut', userName = '', authErrorMessage = '') => {
14112
14871
  const hasAuthError = authEnabled && !!authErrorMessage;
14113
14872
  return [{
14114
- childCount: hasAuthError ? 3 : 2,
14873
+ childCount: 2 + (authEnabled ? 1 : 0) + (hasAuthError ? 1 : 0),
14115
14874
  className: ChatHeader,
14116
14875
  onContextMenu: HandleChatHeaderContextMenu,
14117
14876
  type: Header
14118
- }, {
14877
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
14119
14878
  childCount: 2,
14120
14879
  className: ChatName,
14121
14880
  type: Div
@@ -14123,7 +14882,7 @@ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, a
14123
14882
  childCount: 1,
14124
14883
  className: ChatHeaderLabel,
14125
14884
  type: H2
14126
- }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail', authEnabled, authStatus), ...getAuthErrorDom(hasAuthError, authErrorMessage)];
14885
+ }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail'), ...getAuthErrorDom(hasAuthError, authErrorMessage)];
14127
14886
  };
14128
14887
 
14129
14888
  const getChatModeDetailVirtualDom = ({
@@ -14132,7 +14891,6 @@ const getChatModeDetailVirtualDom = ({
14132
14891
  agentModePickerOpen = false,
14133
14892
  authEnabled = false,
14134
14893
  authErrorMessage = '',
14135
- authStatus = 'signed-out',
14136
14894
  composerAttachmentPreviewOverlayAttachmentId,
14137
14895
  composerAttachmentPreviewOverlayError = false,
14138
14896
  composerAttachments,
@@ -14173,6 +14931,8 @@ const getChatModeDetailVirtualDom = ({
14173
14931
  tokensUsed,
14174
14932
  usageOverviewEnabled,
14175
14933
  useChatMathWorker = false,
14934
+ userName = '',
14935
+ userState = 'loggedOut',
14176
14936
  visibleModels = models,
14177
14937
  voiceDictationEnabled = false
14178
14938
  }) => {
@@ -14193,7 +14953,7 @@ const getChatModeDetailVirtualDom = ({
14193
14953
  onDragEnter: HandleDragEnterChatView,
14194
14954
  onDragOver: HandleDragOverChatView,
14195
14955
  type: Div
14196
- }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, authStatus, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
14956
+ }, ...getChatHeaderDomDetailMode(selectedSessionTitle, authEnabled, userState, userName, authErrorMessage), ...getMessagesDom(messages, parsedMessages, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, messagesScrollTop, useChatMathWorker), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, false, false, '', [], '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, showCreatePullRequestButton, voiceDictationEnabled, scrollDownButtonEnabled, messagesAutoScrollEnabled), ...getChatOverlaysVirtualDom({
14197
14957
  agentMode,
14198
14958
  agentModePickerVisible: isAgentModePickerVisible,
14199
14959
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14229,20 +14989,20 @@ const getChatSearchDom = (hasSearchField, searchValue) => {
14229
14989
  value: searchValue
14230
14990
  }];
14231
14991
  };
14232
- const getChatHeaderListModeDom = (authEnabled = false, authStatus = 'signed-out', authErrorMessage = '', searchEnabled = false, searchFieldVisible = false, searchValue = '') => {
14992
+ const getChatHeaderListModeDom = (authEnabled = false, userState = 'loggedOut', userName = '', authErrorMessage = '', searchEnabled = false, searchFieldVisible = false, searchValue = '') => {
14233
14993
  const hasAuthError = authEnabled && !!authErrorMessage;
14234
14994
  const hasSearchField = searchEnabled && searchFieldVisible;
14235
- const headerChildCount = 2 + (hasAuthError ? 1 : 0) + (hasSearchField ? 1 : 0);
14995
+ const headerChildCount = 2 + (authEnabled ? 1 : 0) + (hasAuthError ? 1 : 0) + (hasSearchField ? 1 : 0);
14236
14996
  return [{
14237
14997
  childCount: headerChildCount,
14238
14998
  className: ChatHeader,
14239
14999
  onContextMenu: HandleChatHeaderContextMenu,
14240
15000
  type: Header
14241
- }, {
15001
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
14242
15002
  childCount: 1,
14243
15003
  className: ChatHeaderLabel,
14244
15004
  type: H2
14245
- }, text(chats()), ...getChatHeaderActionsDom('list', authEnabled, authStatus, searchEnabled), ...getChatSearchDom(hasSearchField, searchValue), ...(hasAuthError ? [{
15005
+ }, text(chats()), ...getChatHeaderActionsDom('list', searchEnabled), ...getChatSearchDom(hasSearchField, searchValue), ...(hasAuthError ? [{
14246
15006
  childCount: 1,
14247
15007
  className: ChatAuthError,
14248
15008
  type: Span
@@ -14342,7 +15102,6 @@ const getChatModeListVirtualDom = ({
14342
15102
  agentModePickerOpen = false,
14343
15103
  authEnabled = false,
14344
15104
  authErrorMessage = '',
14345
- authStatus = 'signed-out',
14346
15105
  chatListScrollTop = 0,
14347
15106
  composerAttachmentPreviewOverlayAttachmentId,
14348
15107
  composerAttachmentPreviewOverlayError = false,
@@ -14377,6 +15136,8 @@ const getChatModeListVirtualDom = ({
14377
15136
  tokensMax,
14378
15137
  tokensUsed,
14379
15138
  usageOverviewEnabled,
15139
+ userName = '',
15140
+ userState = 'loggedOut',
14380
15141
  visibleModels = models,
14381
15142
  voiceDictationEnabled = false
14382
15143
  }) => {
@@ -14395,7 +15156,7 @@ const getChatModeListVirtualDom = ({
14395
15156
  onDragEnter: HandleDragEnterChatView,
14396
15157
  onDragOver: HandleDragOverChatView,
14397
15158
  type: Div
14398
- }, ...getChatHeaderListModeDom(authEnabled, authStatus, authErrorMessage, searchEnabled, searchFieldVisible, searchValue), ...getChatListDom(visibleSessions, selectedSessionId, listFocusedIndex, chatListScrollTop), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, false, voiceDictationEnabled), ...getChatOverlaysVirtualDom({
15159
+ }, ...getChatHeaderListModeDom(authEnabled, userState, userName, authErrorMessage, searchEnabled, searchFieldVisible, searchValue), ...getChatListDom(visibleSessions, selectedSessionId, listFocusedIndex, chatListScrollTop), ...getChatSendAreaDom(composerValue, composerAttachments, agentMode, agentModePickerOpen, false, false, '', [], '', hasSpaceForAgentModePicker, modelPickerOpen, models, selectedModelId, reasoningPickerEnabled, reasoningEffort, reasoningEffortPickerOpen, usageOverviewEnabled, tokensUsed, tokensMax, addContextButtonEnabled, showRunMode, hasSpaceForRunModePicker, runMode, runModePickerOpen, todoListToolEnabled, todoListItems, false, voiceDictationEnabled), ...getChatOverlaysVirtualDom({
14399
15160
  agentMode,
14400
15161
  agentModePickerVisible: isAgentModePickerVisible,
14401
15162
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14498,7 +15259,6 @@ const getChatVirtualDom = options => {
14498
15259
  agentModePickerOpen = false,
14499
15260
  authEnabled = false,
14500
15261
  authErrorMessage = '',
14501
- authStatus = 'signed-out',
14502
15262
  chatListScrollTop,
14503
15263
  composerAttachmentPreviewOverlayAttachmentId,
14504
15264
  composerAttachmentPreviewOverlayError = false,
@@ -14510,6 +15270,10 @@ const getChatVirtualDom = options => {
14510
15270
  composerHeight,
14511
15271
  composerLineHeight,
14512
15272
  composerValue,
15273
+ gitBranches = [],
15274
+ gitBranchPickerErrorMessage = '',
15275
+ gitBranchPickerOpen = false,
15276
+ gitBranchPickerVisible = false,
14513
15277
  hasSpaceForAgentModePicker,
14514
15278
  hasSpaceForRunModePicker,
14515
15279
  listFocusedIndex = -1,
@@ -14547,6 +15311,8 @@ const getChatVirtualDom = options => {
14547
15311
  tokensUsed,
14548
15312
  usageOverviewEnabled,
14549
15313
  useChatMathWorker = false,
15314
+ userName = '',
15315
+ userState = 'loggedOut',
14550
15316
  viewMode,
14551
15317
  visibleModels = models,
14552
15318
  voiceDictationEnabled = false
@@ -14561,7 +15327,6 @@ const getChatVirtualDom = options => {
14561
15327
  agentModePickerOpen,
14562
15328
  authEnabled,
14563
15329
  authErrorMessage,
14564
- authStatus,
14565
15330
  composerAttachmentPreviewOverlayAttachmentId,
14566
15331
  composerAttachmentPreviewOverlayError,
14567
15332
  composerAttachments,
@@ -14572,6 +15337,10 @@ const getChatVirtualDom = options => {
14572
15337
  composerHeight,
14573
15338
  composerLineHeight,
14574
15339
  composerValue,
15340
+ gitBranches,
15341
+ gitBranchPickerErrorMessage,
15342
+ gitBranchPickerOpen,
15343
+ gitBranchPickerVisible,
14575
15344
  hasSpaceForAgentModePicker,
14576
15345
  hasSpaceForRunModePicker,
14577
15346
  messagesAutoScrollEnabled,
@@ -14606,6 +15375,8 @@ const getChatVirtualDom = options => {
14606
15375
  tokensUsed,
14607
15376
  usageOverviewEnabled,
14608
15377
  useChatMathWorker,
15378
+ userName,
15379
+ userState,
14609
15380
  visibleModels,
14610
15381
  voiceDictationEnabled
14611
15382
  });
@@ -14616,7 +15387,6 @@ const getChatVirtualDom = options => {
14616
15387
  agentModePickerOpen,
14617
15388
  authEnabled,
14618
15389
  authErrorMessage,
14619
- authStatus,
14620
15390
  composerAttachmentPreviewOverlayAttachmentId,
14621
15391
  composerAttachmentPreviewOverlayError,
14622
15392
  composerAttachments,
@@ -14657,6 +15427,8 @@ const getChatVirtualDom = options => {
14657
15427
  tokensUsed,
14658
15428
  usageOverviewEnabled,
14659
15429
  useChatMathWorker,
15430
+ userName,
15431
+ userState,
14660
15432
  visibleModels,
14661
15433
  voiceDictationEnabled
14662
15434
  });
@@ -14667,7 +15439,6 @@ const getChatVirtualDom = options => {
14667
15439
  agentModePickerOpen,
14668
15440
  authEnabled,
14669
15441
  authErrorMessage,
14670
- authStatus,
14671
15442
  chatListScrollTop,
14672
15443
  composerAttachmentPreviewOverlayAttachmentId,
14673
15444
  composerAttachmentPreviewOverlayError,
@@ -14702,6 +15473,8 @@ const getChatVirtualDom = options => {
14702
15473
  tokensMax,
14703
15474
  tokensUsed,
14704
15475
  usageOverviewEnabled,
15476
+ userName,
15477
+ userState,
14705
15478
  visibleModels,
14706
15479
  voiceDictationEnabled
14707
15480
  });
@@ -14717,7 +15490,6 @@ const renderItems = (oldState, newState) => {
14717
15490
  agentModePickerOpen,
14718
15491
  authEnabled,
14719
15492
  authErrorMessage,
14720
- authStatus,
14721
15493
  chatListScrollTop,
14722
15494
  composerAttachmentPreviewOverlayAttachmentId,
14723
15495
  composerAttachmentPreviewOverlayError,
@@ -14729,6 +15501,10 @@ const renderItems = (oldState, newState) => {
14729
15501
  composerHeight,
14730
15502
  composerLineHeight,
14731
15503
  composerValue,
15504
+ gitBranches,
15505
+ gitBranchPickerErrorMessage,
15506
+ gitBranchPickerOpen,
15507
+ gitBranchPickerVisible,
14732
15508
  hasSpaceForAgentModePicker,
14733
15509
  hasSpaceForRunModePicker,
14734
15510
  initial,
@@ -14768,6 +15544,8 @@ const renderItems = (oldState, newState) => {
14768
15544
  uid,
14769
15545
  usageOverviewEnabled,
14770
15546
  useChatMathWorker,
15547
+ userName,
15548
+ userState,
14771
15549
  viewMode,
14772
15550
  visibleModels,
14773
15551
  voiceDictationEnabled
@@ -14781,7 +15559,6 @@ const renderItems = (oldState, newState) => {
14781
15559
  agentModePickerOpen,
14782
15560
  authEnabled,
14783
15561
  authErrorMessage,
14784
- authStatus,
14785
15562
  chatListScrollTop,
14786
15563
  composerAttachmentPreviewOverlayAttachmentId,
14787
15564
  composerAttachmentPreviewOverlayError,
@@ -14793,6 +15570,10 @@ const renderItems = (oldState, newState) => {
14793
15570
  composerHeight,
14794
15571
  composerLineHeight,
14795
15572
  composerValue,
15573
+ gitBranches,
15574
+ gitBranchPickerErrorMessage,
15575
+ gitBranchPickerOpen,
15576
+ gitBranchPickerVisible,
14796
15577
  hasSpaceForAgentModePicker,
14797
15578
  hasSpaceForRunModePicker,
14798
15579
  listFocusedIndex,
@@ -14830,6 +15611,8 @@ const renderItems = (oldState, newState) => {
14830
15611
  tokensUsed,
14831
15612
  usageOverviewEnabled,
14832
15613
  useChatMathWorker,
15614
+ userName,
15615
+ userState,
14833
15616
  viewMode,
14834
15617
  visibleModels,
14835
15618
  voiceDictationEnabled
@@ -14876,8 +15659,16 @@ const renderSelection = (oldState, newState) => {
14876
15659
  const renderValue = (oldState, newState) => {
14877
15660
  const {
14878
15661
  composerValue,
15662
+ openApiApiKeyInput,
15663
+ openRouterApiKeyInput,
14879
15664
  uid
14880
15665
  } = newState;
15666
+ if (oldState.openApiApiKeyInput !== openApiApiKeyInput) {
15667
+ return [SetValueByName, uid, OpenApiApiKeyInput, openApiApiKeyInput];
15668
+ }
15669
+ if (oldState.openRouterApiKeyInput !== openRouterApiKeyInput) {
15670
+ return [SetValueByName, uid, OpenRouterApiKeyInput, openRouterApiKeyInput];
15671
+ }
14881
15672
  return [SetValueByName, uid, Composer, composerValue];
14882
15673
  };
14883
15674
 
@@ -14971,6 +15762,9 @@ const renderEventListeners = () => {
14971
15762
  }, {
14972
15763
  name: HandleClickAgentModePickerToggle,
14973
15764
  params: ['openAgentModePicker']
15765
+ }, {
15766
+ name: HandleClickGitBranchPickerToggle,
15767
+ params: ['handleClickGitBranchPickerToggle']
14974
15768
  }, {
14975
15769
  name: HandleClickModelPickerOverlay,
14976
15770
  params: ['handleClickModelPickerOverlay', DefaultPrevented]
@@ -15129,6 +15923,10 @@ const reset = async state => {
15129
15923
  composerSelectionEnd: 0,
15130
15924
  composerSelectionStart: 0,
15131
15925
  composerValue: '',
15926
+ gitBranches: [],
15927
+ gitBranchPickerErrorMessage: '',
15928
+ gitBranchPickerOpen: false,
15929
+ gitBranchPickerVisible: false,
15132
15930
  mockAiResponseDelay: 0,
15133
15931
  modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, state.models.length),
15134
15932
  modelPickerListScrollTop: 0,
@@ -15208,7 +16006,6 @@ const saveState = state => {
15208
16006
  selectedModelId,
15209
16007
  selectedProjectId,
15210
16008
  selectedSessionId,
15211
- systemPrompt,
15212
16009
  viewMode
15213
16010
  } = state;
15214
16011
  return {
@@ -15231,7 +16028,6 @@ const saveState = state => {
15231
16028
  selectedModelId,
15232
16029
  selectedProjectId,
15233
16030
  selectedSessionId,
15234
- systemPrompt,
15235
16031
  viewMode
15236
16032
  };
15237
16033
  };
@@ -15246,7 +16042,13 @@ const setAddContextButtonEnabled = (state, addContextButtonEnabled) => {
15246
16042
  const setAuthEnabled = (state, authEnabled) => {
15247
16043
  return {
15248
16044
  ...state,
15249
- authEnabled
16045
+ authAccessToken: authEnabled ? state.authAccessToken : '',
16046
+ authEnabled,
16047
+ authErrorMessage: authEnabled ? state.authErrorMessage : '',
16048
+ userName: authEnabled ? state.userName : '',
16049
+ userState: authEnabled ? state.userState : 'loggedOut',
16050
+ userSubscriptionPlan: authEnabled ? state.userSubscriptionPlan : '',
16051
+ userUsedTokens: authEnabled ? state.userUsedTokens : 0
15250
16052
  };
15251
16053
  };
15252
16054
 
@@ -15440,6 +16242,7 @@ const commandMap = {
15440
16242
  'Chat.chatListFocusNext': wrapCommand(chatListFocusNext),
15441
16243
  'Chat.chatListFocusPrevious': wrapCommand(chatListFocusPrevious),
15442
16244
  'Chat.clearInput': wrapCommand(clearInput),
16245
+ 'Chat.closeGitBranchPicker': wrapCommand(closeGitBranchPicker),
15443
16246
  'Chat.copyInput': wrapCommand(copyInput),
15444
16247
  'Chat.create': create,
15445
16248
  'Chat.cutInput': wrapCommand(cutInput),
@@ -15468,6 +16271,7 @@ const commandMap = {
15468
16271
  'Chat.handleClickDelete': wrapCommand(handleClickDelete),
15469
16272
  'Chat.handleClickDictationButton': wrapCommand(handleClickDictationButton),
15470
16273
  'Chat.handleClickFileName': wrapCommand(handleClickFileName),
16274
+ 'Chat.handleClickGitBranchPickerToggle': wrapCommand(handleClickGitBranchPickerToggle),
15471
16275
  'Chat.handleClickList': wrapCommand(handleClickList),
15472
16276
  'Chat.handleClickModelPickerList': wrapCommand(handleClickModelPickerList),
15473
16277
  'Chat.handleClickModelPickerListIndex': wrapCommand(handleClickModelPickerListIndex),
@@ -15519,6 +16323,7 @@ const commandMap = {
15519
16323
  'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
15520
16324
  'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
15521
16325
  'Chat.openAgentModePicker': wrapCommand(openAgentModePicker),
16326
+ 'Chat.openGitBranchPicker': wrapCommand(openGitBranchPicker),
15522
16327
  'Chat.openMockProject': wrapCommand(openMockProject),
15523
16328
  'Chat.openMockSession': wrapCommand(openMockSession),
15524
16329
  'Chat.openModelPicker': wrapCommand(openModelPicker),