@lvce-editor/chat-view 6.70.0 → 7.0.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.
@@ -1775,6 +1775,17 @@ const clearInput = async state => {
1775
1775
  };
1776
1776
  };
1777
1777
 
1778
+ const closeGitBranchPicker = state => {
1779
+ if (!state.gitBranchPickerOpen && !state.gitBranchPickerErrorMessage) {
1780
+ return state;
1781
+ }
1782
+ return {
1783
+ ...state,
1784
+ gitBranchPickerErrorMessage: '',
1785
+ gitBranchPickerOpen: false
1786
+ };
1787
+ };
1788
+
1778
1789
  const clampSelectionIndex = (value, max) => {
1779
1790
  if (!Number.isFinite(value)) {
1780
1791
  return 0;
@@ -1852,6 +1863,7 @@ const BackToChatList = 'Back to chat list';
1852
1863
  const Search$1 = 'Search';
1853
1864
  const ScrollDown$1 = 'Scroll down';
1854
1865
  const SearchModels = 'Search models';
1866
+ const PickModel = 'Pick Model';
1855
1867
  const SearchChats = 'Search chats';
1856
1868
  const Paste = 'Paste';
1857
1869
  const Rename = 'Rename';
@@ -1859,9 +1871,13 @@ const Archive = 'Archive';
1859
1871
  const AddProjectChat = 'Add Project';
1860
1872
  const RemoveProject = 'Remove Project';
1861
1873
  const Settings$1 = 'Settings';
1874
+ const Login$1 = 'Login';
1875
+ const Logout$1 = 'Logout';
1862
1876
  const LoginToBackend = 'Login to backend';
1863
1877
  const LogoutFromBackend = 'Logout from backend';
1864
1878
  const LoggingInToBackend = 'Logging in to backend';
1879
+ const LoggingOutFromBackend = 'Logging out from backend';
1880
+ const SignedIn = 'Signed in';
1865
1881
  const SwitchToChatFocusMode = 'Switch to chat focus mode';
1866
1882
  const SwitchToNormalChatMode = 'Switch to normal chat mode';
1867
1883
  const CloseChat$1 = 'Close Chat';
@@ -1941,18 +1957,33 @@ const scrollDown = () => {
1941
1957
  const searchModels = () => {
1942
1958
  return i18nString(SearchModels);
1943
1959
  };
1960
+ const pickModel = modelName => {
1961
+ return `${i18nString(PickModel)}, ${modelName}`;
1962
+ };
1944
1963
  const searchChats = () => {
1945
1964
  return i18nString(SearchChats);
1946
1965
  };
1966
+ const login = () => {
1967
+ return i18nString(Login$1);
1968
+ };
1969
+ const logout = () => {
1970
+ return i18nString(Logout$1);
1971
+ };
1947
1972
  const loginToBackend = () => {
1948
1973
  return i18nString(LoginToBackend);
1949
1974
  };
1950
- const logoutFromBackend = () => {
1975
+ const logoutFromBackend$1 = () => {
1951
1976
  return i18nString(LogoutFromBackend);
1952
1977
  };
1953
1978
  const loggingInToBackend = () => {
1954
1979
  return i18nString(LoggingInToBackend);
1955
1980
  };
1981
+ const loggingOutFromBackend = () => {
1982
+ return i18nString(LoggingOutFromBackend);
1983
+ };
1984
+ const signedIn = () => {
1985
+ return i18nString(SignedIn);
1986
+ };
1956
1987
  const chatFocusMode = () => {
1957
1988
  return i18nString(SwitchToChatFocusMode);
1958
1989
  };
@@ -2324,8 +2355,6 @@ const createDefaultState = () => {
2324
2355
  authAccessToken: '',
2325
2356
  authEnabled: false,
2326
2357
  authErrorMessage: '',
2327
- authRefreshToken: '',
2328
- authStatus: 'signed-out',
2329
2358
  backendUrl: '',
2330
2359
  chatFocusContentMaxWidth: 700,
2331
2360
  chatHistoryEnabled: true,
@@ -2357,6 +2386,10 @@ const createDefaultState = () => {
2357
2386
  errorCount: 0,
2358
2387
  focus: 'composer',
2359
2388
  focused: false,
2389
+ gitBranches: [],
2390
+ gitBranchPickerErrorMessage: '',
2391
+ gitBranchPickerOpen: false,
2392
+ gitBranchPickerVisible: false,
2360
2393
  headerHeight: 50,
2361
2394
  height: 0,
2362
2395
  ...responsivePickerState,
@@ -2443,6 +2476,7 @@ const createDefaultState = () => {
2443
2476
  useChatToolWorker: true,
2444
2477
  useMockApi: false,
2445
2478
  userName: '',
2479
+ userState: 'loggedOut',
2446
2480
  userSubscriptionPlan: '',
2447
2481
  userUsedTokens: 0,
2448
2482
  viewMode: 'list',
@@ -2829,7 +2863,24 @@ const isEqualComposerAttachments = (a, b) => {
2829
2863
  }
2830
2864
  return true;
2831
2865
  };
2866
+ const isEqualGitBranches = (a, b) => {
2867
+ if (a === b) {
2868
+ return true;
2869
+ }
2870
+ if (a.length !== b.length) {
2871
+ return false;
2872
+ }
2873
+ for (let i = 0; i < a.length; i++) {
2874
+ if (a[i].current !== b[i].current || a[i].name !== b[i].name) {
2875
+ return false;
2876
+ }
2877
+ }
2878
+ return true;
2879
+ };
2832
2880
  const isEqualProjectExpandedIds = (a, b) => {
2881
+ if (a === b) {
2882
+ return true;
2883
+ }
2833
2884
  if (a.length !== b.length) {
2834
2885
  return false;
2835
2886
  }
@@ -2840,6 +2891,20 @@ const isEqualProjectExpandedIds = (a, b) => {
2840
2891
  }
2841
2892
  return true;
2842
2893
  };
2894
+ const isEqualProjects = (a, b) => {
2895
+ if (a === b) {
2896
+ return true;
2897
+ }
2898
+ if (a.length !== b.length) {
2899
+ return false;
2900
+ }
2901
+ for (let i = 0; i < a.length; i++) {
2902
+ if (a[i].id !== b[i].id || a[i].name !== b[i].name || a[i].uri !== b[i].uri) {
2903
+ return false;
2904
+ }
2905
+ }
2906
+ return true;
2907
+ };
2843
2908
  const isEqualVisibleModels = (a, b) => {
2844
2909
  if (a === b) {
2845
2910
  return true;
@@ -2856,7 +2921,7 @@ const isEqualVisibleModels = (a, b) => {
2856
2921
  };
2857
2922
 
2858
2923
  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;
2924
+ 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
2925
  };
2861
2926
 
2862
2927
  const diffScrollTop = (oldState, newState) => {
@@ -2877,6 +2942,12 @@ const RenderIncremental = 11;
2877
2942
  const RenderScrollTop = 12;
2878
2943
 
2879
2944
  const diffValue = (oldState, newState) => {
2945
+ if (oldState.openApiApiKeyInput !== newState.openApiApiKeyInput) {
2946
+ return false;
2947
+ }
2948
+ if (oldState.openRouterApiKeyInput !== newState.openRouterApiKeyInput) {
2949
+ return false;
2950
+ }
2880
2951
  return oldState.composerValue === newState.composerValue || newState.inputSource !== 'script';
2881
2952
  };
2882
2953
 
@@ -2907,10 +2978,9 @@ const getAuthState = state => {
2907
2978
  authAccessToken,
2908
2979
  authEnabled,
2909
2980
  authErrorMessage,
2910
- authRefreshToken,
2911
- authStatus,
2912
2981
  backendUrl,
2913
2982
  userName,
2983
+ userState,
2914
2984
  userSubscriptionPlan,
2915
2985
  userUsedTokens
2916
2986
  } = state;
@@ -2918,10 +2988,9 @@ const getAuthState = state => {
2918
2988
  authAccessToken,
2919
2989
  authEnabled,
2920
2990
  authErrorMessage,
2921
- authRefreshToken,
2922
- authStatus,
2923
2991
  backendUrl,
2924
2992
  userName,
2993
+ userState,
2925
2994
  userSubscriptionPlan,
2926
2995
  userUsedTokens
2927
2996
  };
@@ -3387,9 +3456,11 @@ const ComposerAttachmentPreviewOverlay = 'composer-attachment-preview-overlay';
3387
3456
  const AddContext = 'add-context';
3388
3457
  const Dictate = 'dictate';
3389
3458
  const CreatePullRequest$1 = 'create-pull-request';
3459
+ const GitBranchPickerToggle = 'git-branch-picker-toggle';
3460
+ const GitBranchPickerItemPrefix = 'git-branch-picker-item:';
3390
3461
  const FocusAddAction = 'focus-add-action';
3391
- const FocusOpenInVsCode = 'focus-open-in-vscode';
3392
3462
  const FocusCommit = 'focus-commit';
3463
+ const FocusOpenInVsCode = 'focus-open-in-vscode';
3393
3464
  const FocusOpenTerminal = 'focus-open-terminal';
3394
3465
  const FocusShowDiff = 'focus-show-diff';
3395
3466
  const Send = 'send';
@@ -3458,6 +3529,15 @@ const getRenameIdFromInputName = name => {
3458
3529
  const getModelPickerItemInputName = modelId => {
3459
3530
  return `${ModelPickerItemPrefix}${modelId}`;
3460
3531
  };
3532
+ const getGitBranchPickerItemInputName = branchName => {
3533
+ return `${GitBranchPickerItemPrefix}${branchName}`;
3534
+ };
3535
+ const isGitBranchPickerItemInputName = name => {
3536
+ return name.startsWith(GitBranchPickerItemPrefix);
3537
+ };
3538
+ const getGitBranchFromGitBranchPickerItemInputName = name => {
3539
+ return name.slice(GitBranchPickerItemPrefix.length);
3540
+ };
3461
3541
  const getComposerAttachmentInputName = attachmentId => {
3462
3542
  return `${ComposerAttachmentPrefix}${attachmentId}`;
3463
3543
  };
@@ -3818,6 +3898,236 @@ const openFolder = async () => {
3818
3898
  }
3819
3899
  };
3820
3900
 
3901
+ // cspell:ignore gitdir worktrees
3902
+ const FileTypeFile = 1;
3903
+ const FileTypeDirectory = 2;
3904
+ const slashAtEndRegex = /\/$/;
3905
+ const gitDirPointerRegex = /^gitdir:\s*(.+)$/m;
3906
+ const headRefRegex = /^ref:\s+refs\/heads\/(.+)$/m;
3907
+ const toGitUri = (baseUri, ...segments) => {
3908
+ const url = new URL(baseUri.endsWith('/') ? baseUri : `${baseUri}/`);
3909
+ for (const segment of segments) {
3910
+ url.pathname = `${url.pathname.replace(slashAtEndRegex, '')}/${segment.split('/').map(part => encodeURIComponent(part)).join('/')}`;
3911
+ }
3912
+ return url.toString();
3913
+ };
3914
+ const decodeFileContent = content => {
3915
+ if (typeof content === 'string') {
3916
+ return content;
3917
+ }
3918
+ if (content instanceof Uint8Array) {
3919
+ return new TextDecoder().decode(content);
3920
+ }
3921
+ if (Array.isArray(content)) {
3922
+ return new TextDecoder().decode(new Uint8Array(content));
3923
+ }
3924
+ return '';
3925
+ };
3926
+ const toFileSystemPath = uri => {
3927
+ if (!uri.startsWith('file://')) {
3928
+ return uri;
3929
+ }
3930
+ return decodeURIComponent(new URL(uri).pathname);
3931
+ };
3932
+ const getRelativePath = (fromPath, toPath) => {
3933
+ if (!fromPath.startsWith('/') || !toPath.startsWith('/')) {
3934
+ return toPath;
3935
+ }
3936
+ const fromParts = fromPath.split('/').filter(Boolean);
3937
+ const toParts = toPath.split('/').filter(Boolean);
3938
+ let commonPrefixLength = 0;
3939
+ while (commonPrefixLength < fromParts.length && commonPrefixLength < toParts.length && fromParts[commonPrefixLength] === toParts[commonPrefixLength]) {
3940
+ commonPrefixLength++;
3941
+ }
3942
+ const parentSegments = fromParts.slice(commonPrefixLength).map(() => '..');
3943
+ const childSegments = toParts.slice(commonPrefixLength);
3944
+ return [...parentSegments, ...childSegments].join('/') || '.';
3945
+ };
3946
+ const toFileSystemTarget = (workspaceUri, uri) => {
3947
+ const workspacePath = toFileSystemPath(workspaceUri);
3948
+ const fileSystemPath = toFileSystemPath(uri);
3949
+ if (!workspaceUri.startsWith('file://') || !uri.startsWith('file://')) {
3950
+ return fileSystemPath;
3951
+ }
3952
+ return getRelativePath(workspacePath, fileSystemPath);
3953
+ };
3954
+ const parseEntries = value => {
3955
+ if (!Array.isArray(value)) {
3956
+ return [];
3957
+ }
3958
+ return value.map(entry => {
3959
+ if (Array.isArray(entry) && typeof entry[0] === 'string' && typeof entry[1] === 'number') {
3960
+ return {
3961
+ name: entry[0],
3962
+ type: entry[1]
3963
+ };
3964
+ }
3965
+ if (entry && typeof entry === 'object' && typeof Reflect.get(entry, 'name') === 'string' && typeof Reflect.get(entry, 'type') === 'number') {
3966
+ return {
3967
+ name: Reflect.get(entry, 'name'),
3968
+ type: Reflect.get(entry, 'type')
3969
+ };
3970
+ }
3971
+ return undefined;
3972
+ }).filter(entry => !!entry);
3973
+ };
3974
+ const readDir = async (workspaceUri, uri) => {
3975
+ const result = await invoke('FileSystem.readDirWithFileTypes', toFileSystemTarget(workspaceUri, uri));
3976
+ return parseEntries(result);
3977
+ };
3978
+ const readTextFile = async (workspaceUri, uri) => {
3979
+ const result = await invoke('FileSystem.readFile', toFileSystemTarget(workspaceUri, uri));
3980
+ return decodeFileContent(result);
3981
+ };
3982
+ const getGitDirUri = async workspaceUri => {
3983
+ const gitUri = toGitUri(workspaceUri, '.git');
3984
+ try {
3985
+ await readTextFile(workspaceUri, toGitUri(gitUri, 'HEAD'));
3986
+ return gitUri;
3987
+ } catch {
3988
+ // Fall through to support worktrees/submodules where .git is a pointer file.
3989
+ }
3990
+ const gitPointer = await readTextFile(workspaceUri, gitUri);
3991
+ const match = gitDirPointerRegex.exec(gitPointer);
3992
+ if (!match) {
3993
+ return '';
3994
+ }
3995
+ return new URL(match[1].trim(), workspaceUri.endsWith('/') ? workspaceUri : `${workspaceUri}/`).toString();
3996
+ };
3997
+ const collectBranchNames = async (workspaceUri, refsHeadsUri, prefix, branches) => {
3998
+ const entries = await readDir(workspaceUri, refsHeadsUri);
3999
+ for (const entry of entries) {
4000
+ if (entry.type === FileTypeDirectory) {
4001
+ await collectBranchNames(workspaceUri, toGitUri(refsHeadsUri, entry.name), `${prefix}${entry.name}/`, branches);
4002
+ continue;
4003
+ }
4004
+ if (entry.type === FileTypeFile) {
4005
+ branches.add(`${prefix}${entry.name}`);
4006
+ }
4007
+ }
4008
+ };
4009
+ const parseCurrentBranch = headContent => {
4010
+ const match = headRefRegex.exec(headContent.trim());
4011
+ if (match) {
4012
+ return match[1].trim();
4013
+ }
4014
+ return '';
4015
+ };
4016
+ const hasGitRepository = async workspaceUri => {
4017
+ try {
4018
+ return Boolean(await getGitDirUri(workspaceUri));
4019
+ } catch {
4020
+ return false;
4021
+ }
4022
+ };
4023
+ const getGitBranches = async workspaceUri => {
4024
+ const gitDirUri = await getGitDirUri(workspaceUri);
4025
+ if (!gitDirUri) {
4026
+ // eslint-disable-next-line @typescript-eslint/only-throw-error
4027
+ throw new globalThis.Error('Git repository not found.');
4028
+ }
4029
+ const branches = new Set();
4030
+ let currentBranch = '';
4031
+ try {
4032
+ const headContent = await readTextFile(workspaceUri, toGitUri(gitDirUri, 'HEAD'));
4033
+ currentBranch = parseCurrentBranch(headContent);
4034
+ if (currentBranch) {
4035
+ branches.add(currentBranch);
4036
+ }
4037
+ } catch {
4038
+ // Keep trying to discover branches from refs even if HEAD cannot be read.
4039
+ }
4040
+ try {
4041
+ await collectBranchNames(workspaceUri, toGitUri(gitDirUri, 'refs', 'heads'), '', branches);
4042
+ } catch {
4043
+ // Repositories without local refs should still open and surface any current branch we found.
4044
+ }
4045
+ if (branches.size === 0) {
4046
+ // eslint-disable-next-line @typescript-eslint/only-throw-error
4047
+ throw new globalThis.Error('No local git branches found.');
4048
+ }
4049
+ return [...branches].toSorted((a, b) => a.localeCompare(b)).map(name => ({
4050
+ current: name === currentBranch,
4051
+ name
4052
+ }));
4053
+ };
4054
+
4055
+ const getSelectedSession = (sessions, selectedSessionId) => {
4056
+ return sessions.find(session => session.id === selectedSessionId);
4057
+ };
4058
+
4059
+ const getProjectUri$1 = (state, projectId) => {
4060
+ return state.projects.find(project => project.id === projectId)?.uri || '';
4061
+ };
4062
+ const getWorkspaceUri$1 = (state, session) => {
4063
+ if (session?.workspaceUri) {
4064
+ return session.workspaceUri;
4065
+ }
4066
+ return getProjectUri$1(state, session?.projectId || state.selectedProjectId);
4067
+ };
4068
+
4069
+ const withHiddenBranchPicker = state => {
4070
+ return {
4071
+ ...state,
4072
+ gitBranches: [],
4073
+ gitBranchPickerErrorMessage: '',
4074
+ gitBranchPickerOpen: false,
4075
+ gitBranchPickerVisible: false
4076
+ };
4077
+ };
4078
+ const refreshGitBranchPickerVisibility = async state => {
4079
+ if (state.viewMode !== 'chat-focus') {
4080
+ return {
4081
+ ...state,
4082
+ gitBranchPickerErrorMessage: '',
4083
+ gitBranchPickerOpen: false
4084
+ };
4085
+ }
4086
+ const selectedSession = getSelectedSession(state.sessions, state.selectedSessionId);
4087
+ const fallbackBranchName = selectedSession?.branchName || '';
4088
+ const workspaceUri = getWorkspaceUri$1(state, selectedSession);
4089
+ if (!workspaceUri) {
4090
+ return withHiddenBranchPicker(state);
4091
+ }
4092
+ const visible = await hasGitRepository(workspaceUri);
4093
+ if (!visible) {
4094
+ if (fallbackBranchName) {
4095
+ return {
4096
+ ...state,
4097
+ gitBranches: [{
4098
+ current: true,
4099
+ name: fallbackBranchName
4100
+ }],
4101
+ gitBranchPickerVisible: true
4102
+ };
4103
+ }
4104
+ return withHiddenBranchPicker(state);
4105
+ }
4106
+ try {
4107
+ const gitBranches = await getGitBranches(workspaceUri);
4108
+ return {
4109
+ ...state,
4110
+ gitBranches,
4111
+ gitBranchPickerVisible: true
4112
+ };
4113
+ } catch {
4114
+ if (fallbackBranchName) {
4115
+ return {
4116
+ ...state,
4117
+ gitBranches: [{
4118
+ current: true,
4119
+ name: fallbackBranchName
4120
+ }],
4121
+ gitBranchPickerVisible: true
4122
+ };
4123
+ }
4124
+ return {
4125
+ ...state,
4126
+ gitBranchPickerVisible: true
4127
+ };
4128
+ }
4129
+ };
4130
+
3821
4131
  const selectProject = async (state, projectId) => {
3822
4132
  const {
3823
4133
  selectedProjectId,
@@ -3831,14 +4141,14 @@ const selectProject = async (state, projectId) => {
3831
4141
  }
3832
4142
  const visibleSessions = getVisibleSessions(sessions, projectId);
3833
4143
  if (visibleSessions.length === 0) {
3834
- return {
4144
+ return refreshGitBranchPickerVisibility({
3835
4145
  ...state,
3836
4146
  composerAttachments: [],
3837
4147
  composerAttachmentsHeight: 0,
3838
4148
  selectedProjectId: projectId,
3839
4149
  selectedSessionId: '',
3840
4150
  viewMode: viewMode === 'chat-focus' ? 'chat-focus' : 'list'
3841
- };
4151
+ });
3842
4152
  }
3843
4153
  const currentSessionVisible = visibleSessions.some(session => session.id === selectedSessionId);
3844
4154
  const nextSelectedSessionId = currentSessionVisible ? selectedSessionId : visibleSessions[0].id;
@@ -3850,7 +4160,7 @@ const selectProject = async (state, projectId) => {
3850
4160
  }
3851
4161
  return loadedSession;
3852
4162
  });
3853
- return {
4163
+ return refreshGitBranchPickerVisibility({
3854
4164
  ...state,
3855
4165
  composerAttachments,
3856
4166
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, width),
@@ -3858,7 +4168,7 @@ const selectProject = async (state, projectId) => {
3858
4168
  selectedSessionId: nextSelectedSessionId,
3859
4169
  sessions: hydratedSessions,
3860
4170
  viewMode: viewMode === 'chat-focus' ? 'chat-focus' : 'detail'
3861
- };
4171
+ });
3862
4172
  };
3863
4173
 
3864
4174
  const fileSchemeRegex = /^file:\/\//;
@@ -3993,6 +4303,148 @@ const handleClickCreatePullRequest = async state => {
3993
4303
  };
3994
4304
  };
3995
4305
 
4306
+ const trailingSlashesRegex$3 = /\/+$/;
4307
+ const trimTrailingSlashes = value => {
4308
+ return value.replace(trailingSlashesRegex$3, '');
4309
+ };
4310
+
4311
+ const getBackendAuthUrl = (backendUrl, path) => {
4312
+ return `${trimTrailingSlashes(backendUrl)}${path}`;
4313
+ };
4314
+
4315
+ const getBackendLoginUrl = backendUrl => {
4316
+ return getBackendAuthUrl(backendUrl, '/auth/login');
4317
+ };
4318
+
4319
+ const getLoggedOutBackendAuthState = (authErrorMessage = '') => {
4320
+ return {
4321
+ authAccessToken: '',
4322
+ authErrorMessage,
4323
+ userName: '',
4324
+ userState: 'loggedOut',
4325
+ userSubscriptionPlan: '',
4326
+ userUsedTokens: 0
4327
+ };
4328
+ };
4329
+
4330
+ const getBackendLogoutUrl = backendUrl => {
4331
+ return getBackendAuthUrl(backendUrl, '/auth/logout');
4332
+ };
4333
+
4334
+ const logoutFromBackend = async backendUrl => {
4335
+ if (!backendUrl) {
4336
+ return;
4337
+ }
4338
+ try {
4339
+ await fetch(getBackendLogoutUrl(backendUrl), {
4340
+ credentials: 'include',
4341
+ headers: {
4342
+ Accept: 'application/json'
4343
+ },
4344
+ method: 'POST'
4345
+ });
4346
+ } catch {
4347
+ // Ignore logout failures and still clear local auth state.
4348
+ }
4349
+ };
4350
+
4351
+ const getBackendRefreshUrl = backendUrl => {
4352
+ return getBackendAuthUrl(backendUrl, '/auth/refresh');
4353
+ };
4354
+
4355
+ const isObject$2 = value => {
4356
+ return !!value && typeof value === 'object';
4357
+ };
4358
+
4359
+ const isBackendAuthResponse = value => {
4360
+ return isObject$2(value);
4361
+ };
4362
+
4363
+ const getNumber = (value, fallback = 0) => {
4364
+ return typeof value === 'number' ? value : fallback;
4365
+ };
4366
+
4367
+ const getString = (value, fallback = '') => {
4368
+ return typeof value === 'string' ? value : fallback;
4369
+ };
4370
+
4371
+ const toBackendAuthState = value => {
4372
+ return {
4373
+ authAccessToken: getString(value.accessToken),
4374
+ authErrorMessage: getString(value.error),
4375
+ userName: getString(value.userName),
4376
+ userState: value.accessToken ? 'loggedIn' : 'loggedOut',
4377
+ userSubscriptionPlan: getString(value.subscriptionPlan),
4378
+ userUsedTokens: getNumber(value.usedTokens)
4379
+ };
4380
+ };
4381
+
4382
+ const parseBackendAuthResponse = value => {
4383
+ if (!isBackendAuthResponse(value)) {
4384
+ return getLoggedOutBackendAuthState('Backend returned an invalid authentication response.');
4385
+ }
4386
+ return toBackendAuthState(value);
4387
+ };
4388
+
4389
+ const syncBackendAuth = async backendUrl => {
4390
+ if (!backendUrl) {
4391
+ return getLoggedOutBackendAuthState('Backend URL is missing.');
4392
+ }
4393
+ try {
4394
+ const response = await fetch(getBackendRefreshUrl(backendUrl), {
4395
+ credentials: 'include',
4396
+ headers: {
4397
+ Accept: 'application/json'
4398
+ },
4399
+ method: 'POST'
4400
+ });
4401
+ if (response.status === 401 || response.status === 403) {
4402
+ return getLoggedOutBackendAuthState();
4403
+ }
4404
+ let payload = undefined;
4405
+ try {
4406
+ payload = await response.json();
4407
+ } catch {
4408
+ payload = undefined;
4409
+ }
4410
+ if (!response.ok) {
4411
+ const parsed = parseBackendAuthResponse(payload);
4412
+ return getLoggedOutBackendAuthState(parsed.authErrorMessage || 'Backend authentication failed.');
4413
+ }
4414
+ const parsed = parseBackendAuthResponse(payload);
4415
+ if (parsed.authErrorMessage) {
4416
+ return getLoggedOutBackendAuthState(parsed.authErrorMessage);
4417
+ }
4418
+ if (!parsed.authAccessToken) {
4419
+ return getLoggedOutBackendAuthState();
4420
+ }
4421
+ return parsed;
4422
+ } catch (error) {
4423
+ const authErrorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
4424
+ return getLoggedOutBackendAuthState(authErrorMessage);
4425
+ }
4426
+ };
4427
+
4428
+ const delay = async ms => {
4429
+ await new Promise(resolve => setTimeout(resolve, ms));
4430
+ };
4431
+
4432
+ const waitForBackendLogin = async (backendUrl, timeoutMs = 30_000, pollIntervalMs = 1000) => {
4433
+ const deadline = Date.now() + timeoutMs;
4434
+ let lastErrorMessage = '';
4435
+ while (Date.now() < deadline) {
4436
+ const authState = await syncBackendAuth(backendUrl);
4437
+ if (authState.userState === 'loggedIn') {
4438
+ return authState;
4439
+ }
4440
+ if (authState.authErrorMessage) {
4441
+ lastErrorMessage = authState.authErrorMessage;
4442
+ }
4443
+ await delay(pollIntervalMs);
4444
+ }
4445
+ return getLoggedOutBackendAuthState(lastErrorMessage);
4446
+ };
4447
+
3996
4448
  let nextLoginResponse;
3997
4449
  const setNextLoginResponse = response => {
3998
4450
  nextLoginResponse = response;
@@ -4016,35 +4468,36 @@ const consumeNextLoginResponse = async () => {
4016
4468
  return response.response;
4017
4469
  };
4018
4470
 
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
4471
  const isLoginResponse = value => {
4028
4472
  if (!value || typeof value !== 'object') {
4029
4473
  return false;
4030
4474
  }
4031
4475
  return true;
4032
4476
  };
4033
- const trimTrailingSlashes = value => {
4034
- return value.replace(trailingSlashesRegex$3, '');
4477
+ const getLoggedInState = (state, response) => {
4478
+ const accessToken = typeof response.accessToken === 'string' ? response.accessToken : '';
4479
+ return {
4480
+ ...state,
4481
+ authAccessToken: accessToken,
4482
+ authErrorMessage: '',
4483
+ userName: typeof response.userName === 'string' ? response.userName : state.userName,
4484
+ userState: accessToken ? 'loggedIn' : 'loggedOut',
4485
+ userSubscriptionPlan: typeof response.subscriptionPlan === 'string' ? response.subscriptionPlan : state.userSubscriptionPlan,
4486
+ userUsedTokens: typeof response.usedTokens === 'number' ? response.usedTokens : state.userUsedTokens
4487
+ };
4035
4488
  };
4036
4489
  const handleClickLogin = async state => {
4037
4490
  if (!state.backendUrl) {
4038
4491
  return {
4039
4492
  ...state,
4040
4493
  authErrorMessage: 'Backend URL is missing.',
4041
- authStatus: 'signed-out'
4494
+ userState: 'loggedOut'
4042
4495
  };
4043
4496
  }
4044
4497
  const signingInState = {
4045
4498
  ...state,
4046
4499
  authErrorMessage: '',
4047
- authStatus: 'signing-in'
4500
+ userState: 'loggingIn'
4048
4501
  };
4049
4502
  if (state.uid) {
4050
4503
  set(state.uid, state, signingInState);
@@ -4053,69 +4506,53 @@ const handleClickLogin = async state => {
4053
4506
  let usedMockResponse = false;
4054
4507
  try {
4055
4508
  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
- };
4509
+ if (usedMockResponse) {
4510
+ const response = await consumeNextLoginResponse();
4511
+ if (!isLoginResponse(response)) {
4512
+ return {
4513
+ ...signingInState,
4514
+ authErrorMessage: 'Backend returned an invalid login response.',
4515
+ userState: 'loggedOut'
4516
+ };
4517
+ }
4518
+ if (typeof response.error === 'string' && response.error) {
4519
+ return {
4520
+ ...signingInState,
4521
+ authErrorMessage: response.error,
4522
+ userState: 'loggedOut'
4523
+ };
4524
+ }
4525
+ return getLoggedInState(signingInState, response);
4070
4526
  }
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
- });
4527
+ await invoke('Main.openUri', getBackendLoginUrl(state.backendUrl));
4528
+ const authState = await waitForBackendLogin(state.backendUrl);
4077
4529
  return {
4078
4530
  ...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
4531
+ ...authState
4086
4532
  };
4087
4533
  } catch (error) {
4088
4534
  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
4535
  return {
4093
4536
  ...signingInState,
4094
- authErrorMessage: errorMessage,
4095
- authStatus: 'signed-out'
4537
+ ...getLoggedOutBackendAuthState(errorMessage)
4096
4538
  };
4097
4539
  }
4098
4540
  };
4099
4541
 
4100
4542
  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 {
4543
+ const loggingOutState = {
4111
4544
  ...state,
4112
- authAccessToken: '',
4113
4545
  authErrorMessage: '',
4114
- authRefreshToken: '',
4115
- authStatus: 'signed-out',
4116
- userName: '',
4117
- userSubscriptionPlan: '',
4118
- userUsedTokens: 0
4546
+ userState: 'loggingOut'
4547
+ };
4548
+ if (state.uid) {
4549
+ set(state.uid, state, loggingOutState);
4550
+ await invoke('Chat.rerender');
4551
+ }
4552
+ await logoutFromBackend(state.backendUrl);
4553
+ return {
4554
+ ...loggingOutState,
4555
+ ...getLoggedOutBackendAuthState()
4119
4556
  };
4120
4557
  };
4121
4558
 
@@ -4440,10 +4877,6 @@ const getClientRequestIdHeader = () => {
4440
4877
  };
4441
4878
  };
4442
4879
 
4443
- const delay = async ms => {
4444
- await new Promise(resolve => setTimeout(resolve, ms));
4445
- };
4446
-
4447
4880
  const getMockAiResponse = async (userMessage, delayInMs) => {
4448
4881
  await delay(delayInMs);
4449
4882
  return `Mock AI response: I received "${userMessage}".`;
@@ -6830,7 +7263,12 @@ const ChatFocusActions = 'ChatFocusActions';
6830
7263
  const ChatFocusHeader = 'ChatFocusHeader';
6831
7264
  const ChatFocusMainArea = 'ChatFocusMainArea';
6832
7265
  const ChatFocusProject = 'ChatFocusProject';
7266
+ const ChatGitBranchPicker = 'ChatGitBranchPicker';
7267
+ const ChatGitBranchPickerErrorMessage = 'ChatGitBranchPickerErrorMessage';
7268
+ const ChatGitBranchPickerMessage = 'ChatGitBranchPickerMessage';
6833
7269
  const ChatHeader = 'ChatHeader';
7270
+ const ChatHeaderAuth = 'ChatHeaderAuth';
7271
+ const ChatHeaderAuthName = 'ChatHeaderAuthName';
6834
7272
  const ChatHeaderLabel = 'ChatHeaderLabel';
6835
7273
  const ChatInputBox = 'ChatInputBox';
6836
7274
  const ChatList = 'ChatList';
@@ -6844,6 +7282,7 @@ const ChatListItemStatusInProgress = 'ChatListItemStatusInProgress';
6844
7282
  const ChatListItemStatusRow = 'ChatListItemStatusRow';
6845
7283
  const ChatListItemStatusStopped = 'ChatListItemStatusStopped';
6846
7284
  const ChatMessageContent = 'ChatMessageContent';
7285
+ const ChatImageMessageContent = 'ChatImageMessageContent';
6847
7286
  const ChatMessageLink = 'ChatMessageLink';
6848
7287
  const ChatModelPicker = 'ChatModelPicker';
6849
7288
  const ChatModelPickerContainer = 'ChatModelPickerContainer';
@@ -6878,6 +7317,7 @@ const ChatAttachmentLabel = 'ChatAttachmentLabel';
6878
7317
  const ChatAttachmentPreview = 'ChatAttachmentPreview';
6879
7318
  const ChatAttachments = 'ChatAttachments';
6880
7319
  const ChatAttachmentTextFile = 'ChatAttachmentTextFile';
7320
+ const ChatMessageImage = 'ChatMessageImage';
6881
7321
  const ChatSendArea = 'ChatSendArea';
6882
7322
  const ChatSendAreaBottom = 'ChatSendAreaBottom';
6883
7323
  const ChatSendAreaContent = 'ChatSendAreaContent';
@@ -6915,6 +7355,7 @@ const IconButtonDisabled = 'IconButtonDisabled';
6915
7355
  const ImageElement = 'ImageElement';
6916
7356
  const ImageErrorMessage = 'ImageErrorMessage';
6917
7357
  const InputBox = 'InputBox';
7358
+ const InputInvalid = 'InputInvalid';
6918
7359
  const Insertion = 'Insertion';
6919
7360
  const Label = 'Label';
6920
7361
  const LabelDetail = 'LabelDetail';
@@ -6923,7 +7364,6 @@ const MarkdownMathBlock = 'MarkdownMathBlock';
6923
7364
  const MarkdownQuote = 'MarkdownQuote';
6924
7365
  const MarkdownTable = 'MarkdownTable';
6925
7366
  const MaskIcon = 'MaskIcon';
6926
- const MaskIconAccount = 'MaskIconAccount';
6927
7367
  const MaskIconAdd = 'MaskIconAdd';
6928
7368
  const MaskIconArrowLeft = 'MaskIconArrowLeft';
6929
7369
  const MaskIconChevronDown = 'MaskIconChevronDown';
@@ -6934,7 +7374,6 @@ const MaskIconDebugPause = 'MaskIconDebugPause';
6934
7374
  const MaskIconLayoutPanelLeft = 'MaskIconLayoutPanelLeft';
6935
7375
  const MaskIconSearch = 'MaskIconSearch';
6936
7376
  const MaskIconSettingsGear = 'MaskIconSettingsGear';
6937
- const MaskIconSignOut = 'MaskIconSignOut';
6938
7377
  const Message = 'Message';
6939
7378
  const MessageAssistant = 'MessageAssistant';
6940
7379
  const MessageUser = 'MessageUser';
@@ -8288,6 +8727,12 @@ const parseAndStoreMessageContent = async (parsedMessages, message) => {
8288
8727
  const parsedContent = message.text === '' ? emptyMessageContent : await parseMessage(message.text);
8289
8728
  return setParsedMessageContent(parsedMessages, message.id, message.text, parsedContent);
8290
8729
  };
8730
+ const parseAndStoreMessageContentWithWorkerPreference = async (parsedMessages, message, useChatMessageParsingWorker) => {
8731
+ if (useChatMessageParsingWorker) {
8732
+ return parseAndStoreMessagesContentInWorker(parsedMessages, [message]);
8733
+ }
8734
+ return parseAndStoreMessageContent(parsedMessages, message);
8735
+ };
8291
8736
  const parseAndStoreMessagesContent = async (parsedMessages, messages) => {
8292
8737
  let nextParsedMessages = parsedMessages;
8293
8738
  for (const message of messages) {
@@ -8320,8 +8765,15 @@ const parseAndStoreMessagesContentWithWorkerPreference = async (parsedMessages,
8320
8765
  }
8321
8766
  return parseAndStoreMessagesContent(parsedMessages, messages);
8322
8767
  };
8323
- const getEmptyMessageContent = () => {
8324
- return emptyMessageContent;
8768
+ const getEmptyMessageContent = () => {
8769
+ return emptyMessageContent;
8770
+ };
8771
+
8772
+ const get = async key => {
8773
+ return getPreference(key);
8774
+ };
8775
+ const update = async settings => {
8776
+ await invoke('Preferences.update', settings);
8325
8777
  };
8326
8778
 
8327
8779
  const setOpenApiApiKey = async (state, openApiApiKey, persist = true) => {
@@ -8738,10 +9190,6 @@ const getNextHandleTextChunkState = (latestState, parsedMessages, sessions) => {
8738
9190
  };
8739
9191
  };
8740
9192
 
8741
- const getSelectedSession = (sessions, selectedSessionId) => {
8742
- return sessions.find(session => session.id === selectedSessionId);
8743
- };
8744
-
8745
9193
  const setAndRerenderHandleTextChunkState = async (uid, previousState, nextState) => {
8746
9194
  set(uid, previousState, nextState);
8747
9195
  // @ts-ignore
@@ -8828,7 +9276,7 @@ const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls,
8828
9276
  };
8829
9277
  };
8830
9278
 
8831
- const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress) => {
9279
+ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress, useChatMessageParsingWorker) => {
8832
9280
  let updatedMessage;
8833
9281
  const updatedSessions = sessions.map(session => {
8834
9282
  if (session.id !== selectedSessionId) {
@@ -8851,7 +9299,7 @@ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, sele
8851
9299
  });
8852
9300
  let nextParsedMessages = parsedMessages;
8853
9301
  if (updatedMessage) {
8854
- nextParsedMessages = await parseAndStoreMessageContent(parsedMessages, updatedMessage);
9302
+ nextParsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, updatedMessage, useChatMessageParsingWorker);
8855
9303
  }
8856
9304
  return {
8857
9305
  parsedMessages: nextParsedMessages,
@@ -8874,7 +9322,7 @@ const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTex
8874
9322
  };
8875
9323
  }
8876
9324
  const updatedText = assistantMessage.text + chunk;
8877
- const updated = await updateMessageTextInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, updatedText, true);
9325
+ const updated = await updateMessageTextInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.parsedMessages, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, updatedText, true, handleTextChunkState.latestState.useChatMessageParsingWorker);
8878
9326
  const nextState = {
8879
9327
  ...handleTextChunkState.latestState,
8880
9328
  ...(handleTextChunkState.latestState.messagesAutoScrollEnabled ? {
@@ -9040,6 +9488,11 @@ const withProvisionedBackgroundSession = async (state, session) => {
9040
9488
  };
9041
9489
  };
9042
9490
  const handleSubmit = async state => {
9491
+ const authState = state.authEnabled && state.backendUrl ? await syncBackendAuth(state.backendUrl) : undefined;
9492
+ const effectiveState = authState ? {
9493
+ ...state,
9494
+ ...authState
9495
+ } : state;
9043
9496
  const {
9044
9497
  agentMode,
9045
9498
  aiSessionTitleGenerationEnabled,
@@ -9068,27 +9521,28 @@ const handleSubmit = async state => {
9068
9521
  streamingEnabled,
9069
9522
  toolEnablement,
9070
9523
  useChatCoordinatorWorker,
9524
+ useChatMessageParsingWorker,
9071
9525
  useChatNetworkWorkerForRequests,
9072
9526
  useChatToolWorker,
9073
9527
  useMockApi,
9074
9528
  viewMode,
9075
9529
  webSearchEnabled
9076
- } = state;
9530
+ } = effectiveState;
9077
9531
  const userText = composerValue.trim();
9078
9532
  if (!userText) {
9079
- return state;
9533
+ return effectiveState;
9080
9534
  }
9081
9535
  const slashCommand = getSlashCommand(userText);
9082
9536
  if (slashCommand) {
9083
- return executeSlashCommand(state, slashCommand);
9537
+ return executeSlashCommand(effectiveState, slashCommand);
9084
9538
  }
9085
9539
  const userTime = new Date().toLocaleTimeString([], {
9086
9540
  hour: '2-digit',
9087
9541
  minute: '2-digit'
9088
9542
  });
9089
9543
  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));
9544
+ const composerAttachments = effectiveState.composerAttachments.length > 0 ? effectiveState.composerAttachments : await getComposerAttachments(effectiveState.selectedSessionId);
9545
+ await clearComposerAttachments(effectiveState.selectedSessionId, composerAttachments.map(attachment => attachment.attachmentId));
9092
9546
  const userMessage = {
9093
9547
  ...(composerAttachments.length > 0 ? {
9094
9548
  attachments: composerAttachments
@@ -9112,9 +9566,9 @@ const handleSubmit = async state => {
9112
9566
  };
9113
9567
  let {
9114
9568
  parsedMessages
9115
- } = state;
9116
- parsedMessages = await parseAndStoreMessageContent(parsedMessages, userMessage);
9117
- parsedMessages = await parseAndStoreMessageContent(parsedMessages, inProgressAssistantMessage);
9569
+ } = effectiveState;
9570
+ parsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, userMessage, useChatMessageParsingWorker);
9571
+ parsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, inProgressAssistantMessage, useChatMessageParsingWorker);
9118
9572
  let workingSessions = sessions;
9119
9573
  if (viewMode === 'detail') {
9120
9574
  const loadedSession = await getChatSession(selectedSessionId);
@@ -9146,10 +9600,10 @@ const handleSubmit = async state => {
9146
9600
  const provisionedSession = await withProvisionedBackgroundSession(state, newSession);
9147
9601
  await saveChatSession(provisionedSession);
9148
9602
  optimisticState = withUpdatedMessageScrollTop(focusInput({
9149
- ...state,
9603
+ ...effectiveState,
9150
9604
  composerAttachments: [],
9151
9605
  composerAttachmentsHeight: 0,
9152
- composerHeight: getMinComposerHeightForState(state),
9606
+ composerHeight: getMinComposerHeightForState(effectiveState),
9153
9607
  composerSelectionEnd: 0,
9154
9608
  composerSelectionStart: 0,
9155
9609
  composerValue: '',
@@ -9185,10 +9639,10 @@ const handleSubmit = async state => {
9185
9639
  await saveChatSession(selectedSession);
9186
9640
  }
9187
9641
  optimisticState = withUpdatedMessageScrollTop(focusInput({
9188
- ...state,
9642
+ ...effectiveState,
9189
9643
  composerAttachments: [],
9190
9644
  composerAttachmentsHeight: 0,
9191
- composerHeight: getMinComposerHeightForState(state),
9645
+ composerHeight: getMinComposerHeightForState(effectiveState),
9192
9646
  composerSelectionEnd: 0,
9193
9647
  composerSelectionStart: 0,
9194
9648
  composerValue: '',
@@ -9200,7 +9654,7 @@ const handleSubmit = async state => {
9200
9654
  }));
9201
9655
  optimisticState = withUpdatedChatInputHistory(optimisticState, userText);
9202
9656
  }
9203
- set(state.uid, state, optimisticState);
9657
+ set(effectiveState.uid, effectiveState, optimisticState);
9204
9658
  // @ts-ignore
9205
9659
  await invoke('Chat.rerender');
9206
9660
  let handleTextChunkState = {
@@ -9289,11 +9743,11 @@ const handleSubmit = async state => {
9289
9743
  let finalParsedMessages = latestState.parsedMessages;
9290
9744
  let updatedSessions;
9291
9745
  if (streamingEnabled) {
9292
- const updated = await updateMessageTextInSelectedSession(latestState.sessions, finalParsedMessages, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false);
9746
+ const updated = await updateMessageTextInSelectedSession(latestState.sessions, finalParsedMessages, latestState.selectedSessionId, assistantMessageId, assistantMessage.text, false, latestState.useChatMessageParsingWorker);
9293
9747
  updatedSessions = updated.sessions;
9294
9748
  finalParsedMessages = updated.parsedMessages;
9295
9749
  } else {
9296
- finalParsedMessages = await parseAndStoreMessageContent(finalParsedMessages, assistantMessage);
9750
+ finalParsedMessages = await parseAndStoreMessageContentWithWorkerPreference(finalParsedMessages, assistantMessage, latestState.useChatMessageParsingWorker);
9297
9751
  updatedSessions = appendMessageToSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessage);
9298
9752
  }
9299
9753
  if (aiSessionTitleGenerationEnabled && createsNewSession) {
@@ -9322,6 +9776,84 @@ const handleClickSend = async state => {
9322
9776
  return handleSubmit(state);
9323
9777
  };
9324
9778
 
9779
+ const SwitchGitBranch = 'Chat.switchGitBranch';
9780
+ const switchGitBranch = async ({
9781
+ assetDir,
9782
+ branchName,
9783
+ platform,
9784
+ workspaceUri
9785
+ }) => {
9786
+ await executeProvider({
9787
+ assetDir,
9788
+ event: `onCommand:${SwitchGitBranch}`,
9789
+ method: CommandExecute,
9790
+ noProviderFoundMessage: 'No git branch switch command found',
9791
+ params: [SwitchGitBranch, {
9792
+ branchName,
9793
+ workspaceUri
9794
+ }],
9795
+ platform
9796
+ });
9797
+ };
9798
+
9799
+ const getGitBranchSwitchErrorMessage = (branchName, error) => {
9800
+ if (error instanceof Error && error.message) {
9801
+ return `Failed to switch to branch "${branchName}". ${error.message}`;
9802
+ }
9803
+ return `Failed to switch to branch "${branchName}".`;
9804
+ };
9805
+ const handleGitBranchChange = async (state, branchName) => {
9806
+ if (!branchName) {
9807
+ return state;
9808
+ }
9809
+ const selectedSession = getSelectedSession(state.sessions, state.selectedSessionId);
9810
+ const workspaceUri = getWorkspaceUri$1(state, selectedSession);
9811
+ if (!workspaceUri) {
9812
+ return {
9813
+ ...state,
9814
+ gitBranchPickerErrorMessage: 'No workspace is selected.',
9815
+ gitBranchPickerOpen: true
9816
+ };
9817
+ }
9818
+ try {
9819
+ await switchGitBranch({
9820
+ assetDir: state.assetDir,
9821
+ branchName,
9822
+ platform: state.platform,
9823
+ workspaceUri
9824
+ });
9825
+ const updatedSessions = state.sessions.map(session => {
9826
+ if (session.id !== state.selectedSessionId) {
9827
+ return session;
9828
+ }
9829
+ return {
9830
+ ...session,
9831
+ branchName
9832
+ };
9833
+ });
9834
+ const updatedSelectedSession = updatedSessions.find(session => session.id === state.selectedSessionId);
9835
+ if (updatedSelectedSession) {
9836
+ await saveChatSession(updatedSelectedSession);
9837
+ }
9838
+ return {
9839
+ ...state,
9840
+ gitBranches: state.gitBranches.map(branch => ({
9841
+ ...branch,
9842
+ current: branch.name === branchName
9843
+ })),
9844
+ gitBranchPickerErrorMessage: '',
9845
+ gitBranchPickerOpen: false,
9846
+ sessions: updatedSessions
9847
+ };
9848
+ } catch (error) {
9849
+ return {
9850
+ ...state,
9851
+ gitBranchPickerErrorMessage: getGitBranchSwitchErrorMessage(branchName, error),
9852
+ gitBranchPickerOpen: true
9853
+ };
9854
+ }
9855
+ };
9856
+
9325
9857
  const setReasoningEffort = (state, reasoningEffort) => {
9326
9858
  return {
9327
9859
  ...state,
@@ -9452,7 +9984,7 @@ const selectSession = async (state, id) => {
9452
9984
  }
9453
9985
  return loadedSession;
9454
9986
  });
9455
- return {
9987
+ return refreshGitBranchPickerVisibility({
9456
9988
  ...state,
9457
9989
  composerAttachments,
9458
9990
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, width),
@@ -9461,7 +9993,7 @@ const selectSession = async (state, id) => {
9461
9993
  selectedSessionId: id,
9462
9994
  sessions: hydratedSessions,
9463
9995
  viewMode: viewMode === 'chat-focus' ? 'chat-focus' : 'detail'
9464
- };
9996
+ });
9465
9997
  };
9466
9998
 
9467
9999
  const startRename = (state, id) => {
@@ -9491,15 +10023,17 @@ const toggleChatFocusMode = async state => {
9491
10023
  if (viewMode === 'chat-focus') {
9492
10024
  return {
9493
10025
  ...state,
10026
+ gitBranchPickerErrorMessage: '',
10027
+ gitBranchPickerOpen: false,
9494
10028
  viewMode: lastNormalViewMode
9495
10029
  };
9496
10030
  }
9497
10031
  if (viewMode === 'list' || viewMode === 'detail') {
9498
- return {
10032
+ return refreshGitBranchPickerVisibility({
9499
10033
  ...state,
9500
10034
  lastNormalViewMode: viewMode,
9501
10035
  viewMode: 'chat-focus'
9502
- };
10036
+ });
9503
10037
  }
9504
10038
  return state;
9505
10039
  };
@@ -9629,6 +10163,11 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
9629
10163
  visibleModels: state.models
9630
10164
  };
9631
10165
  }
10166
+ case isGitBranchPickerItemInputName(name):
10167
+ {
10168
+ const branchName = getGitBranchFromGitBranchPickerItemInputName(name);
10169
+ return handleGitBranchChange(state, branchName);
10170
+ }
9632
10171
  case isAgentModePickerItemInputName(name):
9633
10172
  {
9634
10173
  const agentMode = getAgentModeFromAgentModePickerItemInputName(name);
@@ -9753,6 +10292,71 @@ const handleClickFileName = async (state, uri) => {
9753
10292
  return state;
9754
10293
  };
9755
10294
 
10295
+ const getBranchPickerErrorMessage = error => {
10296
+ if (error instanceof Error && error.message) {
10297
+ return error.message;
10298
+ }
10299
+ return 'Failed to load git branches.';
10300
+ };
10301
+ const openGitBranchPicker = async state => {
10302
+ const visibleState = await refreshGitBranchPickerVisibility(state);
10303
+ const selectedSession = getSelectedSession(visibleState.sessions, visibleState.selectedSessionId);
10304
+ const fallbackBranchName = selectedSession?.branchName || '';
10305
+ if (visibleState.viewMode !== 'chat-focus' || !visibleState.gitBranchPickerVisible && !fallbackBranchName) {
10306
+ return {
10307
+ ...visibleState,
10308
+ gitBranchPickerOpen: false
10309
+ };
10310
+ }
10311
+ const workspaceUri = getWorkspaceUri$1(visibleState, selectedSession);
10312
+ if (!workspaceUri) {
10313
+ return {
10314
+ ...visibleState,
10315
+ gitBranches: [],
10316
+ gitBranchPickerErrorMessage: 'No workspace is selected.',
10317
+ gitBranchPickerOpen: true
10318
+ };
10319
+ }
10320
+ try {
10321
+ const gitBranches = await getGitBranches(workspaceUri);
10322
+ return {
10323
+ ...visibleState,
10324
+ agentModePickerOpen: false,
10325
+ gitBranches,
10326
+ gitBranchPickerErrorMessage: '',
10327
+ gitBranchPickerOpen: true,
10328
+ modelPickerOpen: false,
10329
+ modelPickerSearchValue: '',
10330
+ reasoningEffortPickerOpen: false,
10331
+ runModePickerOpen: false,
10332
+ visibleModels: visibleState.models
10333
+ };
10334
+ } catch (error) {
10335
+ return {
10336
+ ...visibleState,
10337
+ agentModePickerOpen: false,
10338
+ gitBranches: fallbackBranchName ? [{
10339
+ current: true,
10340
+ name: fallbackBranchName
10341
+ }] : [],
10342
+ gitBranchPickerErrorMessage: getBranchPickerErrorMessage(error),
10343
+ gitBranchPickerOpen: true,
10344
+ modelPickerOpen: false,
10345
+ modelPickerSearchValue: '',
10346
+ reasoningEffortPickerOpen: false,
10347
+ runModePickerOpen: false,
10348
+ visibleModels: visibleState.models
10349
+ };
10350
+ }
10351
+ };
10352
+
10353
+ const handleClickGitBranchPickerToggle = async state => {
10354
+ if (state.gitBranchPickerOpen) {
10355
+ return closeGitBranchPicker(state);
10356
+ }
10357
+ return openGitBranchPicker(state);
10358
+ };
10359
+
9756
10360
  const handleClickModelPickerList = async (state, eventY) => {
9757
10361
  const {
9758
10362
  height,
@@ -10615,19 +11219,6 @@ const getSavedSessions = savedState => {
10615
11219
  return sessions;
10616
11220
  };
10617
11221
 
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
11222
  const getSavedViewMode = savedState => {
10632
11223
  if (!isObject$1(savedState)) {
10633
11224
  return undefined;
@@ -10659,24 +11250,6 @@ const loadAuthEnabled = async () => {
10659
11250
  }
10660
11251
  };
10661
11252
 
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
11253
  const loadBackendUrl = async () => {
10681
11254
  try {
10682
11255
  const savedBackendUrl = await get('chat.backendUrl');
@@ -10857,12 +11430,10 @@ const loadVoiceDictationEnabled = async () => {
10857
11430
  };
10858
11431
 
10859
11432
  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()]);
11433
+ 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
11434
  return {
10862
11435
  aiSessionTitleGenerationEnabled,
10863
- authAccessToken,
10864
11436
  authEnabled,
10865
- authRefreshToken,
10866
11437
  backendUrl,
10867
11438
  chatHistoryEnabled,
10868
11439
  composerDropEnabled,
@@ -10934,9 +11505,7 @@ const loadContent = async (state, savedState) => {
10934
11505
  const [composerSelectionStart, composerSelectionEnd] = savedComposerSelection ?? [state.composerSelectionStart, state.composerSelectionEnd];
10935
11506
  const {
10936
11507
  aiSessionTitleGenerationEnabled,
10937
- authAccessToken,
10938
11508
  authEnabled,
10939
- authRefreshToken,
10940
11509
  backendUrl,
10941
11510
  chatHistoryEnabled,
10942
11511
  composerDropEnabled,
@@ -10952,11 +11521,13 @@ const loadContent = async (state, savedState) => {
10952
11521
  toolEnablement,
10953
11522
  useChatCoordinatorWorker,
10954
11523
  useChatMathWorker,
10955
- useChatMessageParsingWorker,
11524
+ // useChatMessageParsingWorker,
10956
11525
  useChatNetworkWorkerForRequests,
10957
11526
  useChatToolWorker,
10958
11527
  voiceDictationEnabled
10959
11528
  } = await loadPreferences();
11529
+ const useChatMessageParsingWorker = true;
11530
+ const authState = authEnabled && backendUrl ? await syncBackendAuth(backendUrl) : getLoggedOutBackendAuthState();
10960
11531
  const legacySavedSessions = getSavedSessions(savedState);
10961
11532
  const storedSessions = await listChatSessions();
10962
11533
  let sessions = storedSessions;
@@ -10992,7 +11563,6 @@ const loadContent = async (state, savedState) => {
10992
11563
  const projectExpandedIds = (savedProjectExpandedIds || state.projectExpandedIds).filter(id => projects.some(project => project.id === id));
10993
11564
  const reasoningEffort = getSavedReasoningEffort(savedState) ?? state.reasoningEffort;
10994
11565
  const selectedModelId = state.models.some(model => model.id === preferredModelId) ? preferredModelId : state.models[0]?.id || '';
10995
- const systemPrompt = getSavedSystemPrompt(savedState) ?? state.systemPrompt;
10996
11566
  const visibleModels = getVisibleModels(state.models, '');
10997
11567
  const visibleSessions = getVisibleSessions(sessions, selectedProjectId);
10998
11568
  const selectedSessionId = visibleSessions.some(session => session.id === preferredSessionId) ? preferredSessionId : visibleSessions[0]?.id || '';
@@ -11008,15 +11578,14 @@ const loadContent = async (state, savedState) => {
11008
11578
  const savedLastNormalViewMode = getSavedLastNormalViewMode(savedState);
11009
11579
  const lastNormalViewMode = savedLastNormalViewMode || (preferredViewMode === 'detail' ? 'detail' : state.lastNormalViewMode);
11010
11580
  const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode;
11011
- return {
11581
+ const nextState = {
11012
11582
  ...state,
11013
11583
  agentMode,
11014
11584
  agentModePickerOpen: false,
11015
11585
  aiSessionTitleGenerationEnabled,
11016
- authAccessToken,
11586
+ authAccessToken: authState.authAccessToken,
11017
11587
  authEnabled,
11018
- authRefreshToken,
11019
- authStatus: authAccessToken ? 'signed-in' : 'signed-out',
11588
+ authErrorMessage: authState.authErrorMessage,
11020
11589
  backendUrl,
11021
11590
  chatHistoryEnabled,
11022
11591
  chatListScrollTop,
@@ -11057,7 +11626,6 @@ const loadContent = async (state, savedState) => {
11057
11626
  selectedSessionId,
11058
11627
  sessions,
11059
11628
  streamingEnabled,
11060
- systemPrompt,
11061
11629
  todoListToolEnabled,
11062
11630
  toolEnablement,
11063
11631
  useChatCoordinatorWorker,
@@ -11065,10 +11633,15 @@ const loadContent = async (state, savedState) => {
11065
11633
  useChatMessageParsingWorker,
11066
11634
  useChatNetworkWorkerForRequests,
11067
11635
  useChatToolWorker,
11636
+ userName: authState.userName,
11637
+ userState: authState.userState,
11638
+ userSubscriptionPlan: authState.userSubscriptionPlan,
11639
+ userUsedTokens: authState.userUsedTokens,
11068
11640
  viewMode,
11069
11641
  visibleModels,
11070
11642
  voiceDictationEnabled
11071
11643
  };
11644
+ return refreshGitBranchPickerVisibility(nextState);
11072
11645
  };
11073
11646
 
11074
11647
  const isObject = value => {
@@ -11158,7 +11731,24 @@ const openMockProject = async (state, projectId, projectName, projectUri) => {
11158
11731
  };
11159
11732
  };
11160
11733
 
11161
- const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11734
+ const applySessionOptions = (session, options) => {
11735
+ if (!options) {
11736
+ return session;
11737
+ }
11738
+ return {
11739
+ ...session,
11740
+ ...(options.branchName ? {
11741
+ branchName: options.branchName
11742
+ } : {}),
11743
+ ...(options.projectId ? {
11744
+ projectId: options.projectId
11745
+ } : {}),
11746
+ ...(options.workspaceUri ? {
11747
+ workspaceUri: options.workspaceUri
11748
+ } : {})
11749
+ };
11750
+ };
11751
+ const openMockSession = async (state, mockSessionId, mockChatMessages, options) => {
11162
11752
  const {
11163
11753
  sessions: currentSessions
11164
11754
  } = state;
@@ -11171,20 +11761,20 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11171
11761
  if (session.id !== mockSessionId) {
11172
11762
  return session;
11173
11763
  }
11174
- return {
11764
+ return applySessionOptions({
11175
11765
  ...session,
11176
11766
  messages: mockChatMessages
11177
- };
11178
- }) : [...currentSessions, {
11767
+ }, options);
11768
+ }) : [...currentSessions, applySessionOptions({
11179
11769
  id: mockSessionId,
11180
11770
  messages: mockChatMessages,
11181
11771
  title: mockSessionId
11182
- }];
11772
+ }, options)];
11183
11773
  const selectedSession = sessions.find(session => session.id === mockSessionId);
11184
11774
  if (selectedSession) {
11185
11775
  await saveChatSession(selectedSession);
11186
11776
  }
11187
- return {
11777
+ return refreshGitBranchPickerVisibility({
11188
11778
  ...state,
11189
11779
  composerAttachments: [],
11190
11780
  composerAttachmentsHeight: 0,
@@ -11193,7 +11783,7 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11193
11783
  selectedSessionId: mockSessionId,
11194
11784
  sessions,
11195
11785
  viewMode: 'detail'
11196
- };
11786
+ });
11197
11787
  };
11198
11788
 
11199
11789
  const pasteInput = async state => {
@@ -11306,6 +11896,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11306
11896
  color: var(--vscode-errorForeground, var(--vscode-foreground));
11307
11897
  }
11308
11898
 
11899
+ .InputInvalid{
11900
+ border-color: var(--vscode-inputValidation-errorBorder, var(--vscode-errorForeground));
11901
+ }
11902
+
11309
11903
  .ChatComposerAttachmentTextFile{
11310
11904
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11311
11905
  }
@@ -11360,6 +11954,23 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11360
11954
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11361
11955
  }
11362
11956
 
11957
+ .ChatImageMessageContent{
11958
+ padding: 6px;
11959
+ border: 1px solid var(--vscode-widget-border, var(--vscode-panel-border));
11960
+ border-radius: 12px;
11961
+ background: var(--vscode-editorWidget-background, var(--vscode-editor-background));
11962
+ overflow: hidden;
11963
+ }
11964
+
11965
+ .ChatMessageImage{
11966
+ display: block;
11967
+ max-width: min(320px, 100%);
11968
+ max-height: min(320px, calc(100vh - 220px));
11969
+ border-radius: 8px;
11970
+ object-fit: contain;
11971
+ background: color-mix(in srgb, var(--vscode-editor-background) 88%, black);
11972
+ }
11973
+
11363
11974
  .Chat{
11364
11975
  position: relative;
11365
11976
  }
@@ -11403,6 +12014,14 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11403
12014
 
11404
12015
  }
11405
12016
 
12017
+ .ChatGitBranchPickerMessage{
12018
+ padding: 6px 8px;
12019
+ }
12020
+
12021
+ .ChatGitBranchPickerErrorMessage{
12022
+ color: var(--vscode-errorForeground, var(--vscode-foreground));
12023
+ }
12024
+
11406
12025
  .RunModePickerContainer{
11407
12026
  display: flex;
11408
12027
  justify-content: flex-end;
@@ -11507,13 +12126,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11507
12126
  .ChatFocus .ChatMessages > .Message > .ChatMessageContent{
11508
12127
  max-inline-size: 100%;
11509
12128
  }
11510
-
11511
-
11512
12129
  .Viewlet.Chat.ChatFocus{
11513
12130
  display: flex !important;
11514
12131
  min-width: 0;
11515
12132
  }
11516
-
11517
12133
  .ChatFocusMainArea{
11518
12134
  display: flex;
11519
12135
  flex: 1;
@@ -11525,7 +12141,6 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11525
12141
  flex: 1;
11526
12142
  min-height: 0;
11527
12143
  }
11528
-
11529
12144
  `;
11530
12145
  return `${baseCss}
11531
12146
 
@@ -11670,7 +12285,8 @@ const HandlePointerUpModelPickerList = 55;
11670
12285
  const HandleClickReasoningEffortPickerToggle = 56;
11671
12286
  const HandleClickAgentModePickerToggle = 57;
11672
12287
  const HandleContextMenuChatImageAttachment = 58;
11673
- const HandleErrorComposerAttachmentPreviewOverlay = 59;
12288
+ const HandleClickGitBranchPickerToggle = 59;
12289
+ const HandleErrorComposerAttachmentPreviewOverlay = 60;
11674
12290
 
11675
12291
  const getAddContextButtonDom = () => {
11676
12292
  return [{
@@ -11685,10 +12301,12 @@ const getAddContextButtonDom = () => {
11685
12301
  }];
11686
12302
  };
11687
12303
 
11688
- const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = label) => {
12304
+ const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = label, ariaLabel = title) => {
11689
12305
  return [{
11690
12306
  'aria-expanded': open ? 'true' : 'false',
11691
12307
  'aria-haspopup': 'true',
12308
+ 'aria-label': ariaLabel,
12309
+ ariaLabel,
11692
12310
  childCount: 2,
11693
12311
  className: Select,
11694
12312
  inputType: 'button',
@@ -11711,12 +12329,12 @@ const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = lab
11711
12329
  }];
11712
12330
  };
11713
12331
 
11714
- const getCustomSelectPickerToggleVirtualDom = (label, name, open, onClick, title = label, containerChildCount = 1) => {
12332
+ const getCustomSelectPickerToggleVirtualDom = (label, name, open, onClick, title = label, ariaLabel = title, containerChildCount = 1) => {
11715
12333
  return [{
11716
12334
  childCount: containerChildCount,
11717
12335
  className: CustomSelectContainer,
11718
12336
  type: Div
11719
- }, ...getCustomSelectToggleVirtualDom(label, name, open, onClick, title)];
12337
+ }, ...getCustomSelectToggleVirtualDom(label, name, open, onClick, title, ariaLabel)];
11720
12338
  };
11721
12339
 
11722
12340
  const getAgentModePickerVirtualDom = (selectedAgentMode, agentModePickerOpen) => {
@@ -11726,7 +12344,8 @@ const getAgentModePickerVirtualDom = (selectedAgentMode, agentModePickerOpen) =>
11726
12344
  const getChatModelPickerToggleVirtualDom = (models, selectedModelId, modelPickerOpen) => {
11727
12345
  const selectedModel = models.find(model => model.id === selectedModelId);
11728
12346
  const selectedModelLabel = selectedModel ? selectedModel.name : selectedModelId;
11729
- return getCustomSelectPickerToggleVirtualDom(selectedModelLabel, ModelPickerToggle, modelPickerOpen, HandleClickModelPickerToggle);
12347
+ const pickModelLabel = pickModel(selectedModelLabel);
12348
+ return getCustomSelectPickerToggleVirtualDom(selectedModelLabel, ModelPickerToggle, modelPickerOpen, HandleClickModelPickerToggle, pickModelLabel, pickModelLabel);
11730
12349
  };
11731
12350
 
11732
12351
  const getCreatePullRequestButtonDom = () => {
@@ -11770,6 +12389,55 @@ const getCustomSelectOptionVirtualDom = (name, label, selected, detail = '') =>
11770
12389
  }, text(detail)] : [])];
11771
12390
  };
11772
12391
 
12392
+ const itemHeight = 28;
12393
+ const messageHeight = 32;
12394
+ const getCurrentBranchLabel = (gitBranches, fallbackBranchName) => {
12395
+ const currentBranch = gitBranches.find(branch => branch.current);
12396
+ if (currentBranch) {
12397
+ return currentBranch.name;
12398
+ }
12399
+ if (fallbackBranchName) {
12400
+ return fallbackBranchName;
12401
+ }
12402
+ return 'Branch';
12403
+ };
12404
+ const getBranchOptionsVirtualDom = gitBranches => {
12405
+ return gitBranches.flatMap(branch => {
12406
+ return getCustomSelectOptionVirtualDom(getGitBranchPickerItemInputName(branch.name), branch.name, branch.current, branch.current ? 'current' : '');
12407
+ });
12408
+ };
12409
+ const getBranchPickerMessageDom = (gitBranches, errorMessage) => {
12410
+ const message = errorMessage || (gitBranches.length === 0 ? 'No local git branches found.' : '');
12411
+ if (!message) {
12412
+ return [];
12413
+ }
12414
+ return [{
12415
+ childCount: 1,
12416
+ className: mergeClassNames(ChatGitBranchPickerMessage, errorMessage ? ChatGitBranchPickerErrorMessage : Empty),
12417
+ type: Div
12418
+ }, {
12419
+ text: message,
12420
+ type: Text
12421
+ }];
12422
+ };
12423
+ const getGitBranchPickerVirtualDom = (gitBranches, gitBranchPickerOpen, gitBranchPickerErrorMessage, fallbackBranchName) => {
12424
+ const label = getCurrentBranchLabel(gitBranches, fallbackBranchName);
12425
+ const branchOptions = getBranchOptionsVirtualDom(gitBranches);
12426
+ const messageDom = getBranchPickerMessageDom(gitBranches, gitBranchPickerErrorMessage);
12427
+ const showMessage = messageDom.length > 0;
12428
+ const popOverHeight = gitBranches.length * itemHeight + (showMessage ? messageHeight : 0);
12429
+ return [...getCustomSelectPickerToggleVirtualDom(label, GitBranchPickerToggle, gitBranchPickerOpen, HandleClickGitBranchPickerToggle, 'Switch branch', 'Switch branch', gitBranchPickerOpen ? 2 : 1), ...(gitBranchPickerOpen ? [{
12430
+ childCount: (showMessage ? 1 : 0) + 1,
12431
+ className: mergeClassNames(ChatModelPicker, CustomSelectPopOver, ChatGitBranchPicker),
12432
+ style: `height: ${popOverHeight}px;`,
12433
+ type: Div
12434
+ }, ...messageDom, {
12435
+ childCount: branchOptions.length / 4,
12436
+ className: ChatModelPickerList,
12437
+ type: Ul
12438
+ }, ...branchOptions] : [])];
12439
+ };
12440
+
11773
12441
  const reasoningEffortPickerHeight = reasoningEfforts.length * 28;
11774
12442
  const getReasoningEffortOptionsVirtualDom = selectedReasoningEffort => {
11775
12443
  return reasoningEfforts.flatMap(reasoningEffort => {
@@ -11779,7 +12447,7 @@ const getReasoningEffortOptionsVirtualDom = selectedReasoningEffort => {
11779
12447
  });
11780
12448
  };
11781
12449
  const getReasoningEffortPickerVirtualDom = (selectedReasoningEffort, reasoningEffortPickerOpen) => {
11782
- return [...getCustomSelectPickerToggleVirtualDom(getReasoningEffortLabel(selectedReasoningEffort), ReasoningEffortPickerToggle, reasoningEffortPickerOpen, HandleClickReasoningEffortPickerToggle, 'Reasoning', reasoningEffortPickerOpen ? 2 : 1), ...(reasoningEffortPickerOpen ? [{
12450
+ return [...getCustomSelectPickerToggleVirtualDom(getReasoningEffortLabel(selectedReasoningEffort), ReasoningEffortPickerToggle, reasoningEffortPickerOpen, HandleClickReasoningEffortPickerToggle, 'Reasoning', 'Reasoning', reasoningEffortPickerOpen ? 2 : 1), ...(reasoningEffortPickerOpen ? [{
11783
12451
  childCount: 1,
11784
12452
  className: mergeClassNames(ChatModelPicker, CustomSelectPopOver),
11785
12453
  style: `height: ${reasoningEffortPickerHeight}px;`,
@@ -11946,6 +12614,7 @@ const getComposerAttachmentClassName = displayType => {
11946
12614
  };
11947
12615
  const getComposerAttachmentRemoveButtonDom = attachment => {
11948
12616
  return [{
12617
+ 'aria-label': removeAttachment(),
11949
12618
  buttonType: 'button',
11950
12619
  childCount: 1,
11951
12620
  className: ChatComposerAttachmentRemoveButton,
@@ -12017,12 +12686,13 @@ const getComposerTextAreaDom = () => {
12017
12686
  type: TextArea
12018
12687
  };
12019
12688
  };
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) => {
12689
+ 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
12690
  const isSendDisabled = composerValue.trim() === '';
12022
12691
  const showAgentModePicker = hasSpaceForAgentModePicker;
12692
+ const showGitBranchPicker = gitBranchPickerVisible || Boolean(fallbackBranchName);
12023
12693
  const showResponsiveRunModePicker = showRunMode && hasSpaceForRunModePicker;
12024
12694
  const showScrollDownButton = scrollDownButtonEnabled && !messagesAutoScrollEnabled;
12025
- const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (voiceDictationEnabled ? 1 : 0) + (showScrollDownButton ? 1 : 0);
12695
+ const bottomControlsCount = 2 + (usageOverviewEnabled ? 1 : 0) + (addContextButtonEnabled ? 1 : 0) + (showCreatePullRequestButton ? 1 : 0) + (showGitBranchPicker ? 1 : 0) + (voiceDictationEnabled ? 1 : 0) + (showScrollDownButton ? 1 : 0);
12026
12696
  const primaryControlsCount = 1 + (showAgentModePicker ? 1 : 0) + (reasoningPickerEnabled ? 1 : 0) + (showResponsiveRunModePicker ? 1 : 0);
12027
12697
  const hasTodoList = todoListToolEnabled && todoListItems.length > 0;
12028
12698
  const hasComposerAttachments = composerAttachments.length > 0;
@@ -12046,7 +12716,62 @@ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agent
12046
12716
  className: ChatSendAreaPrimaryControls,
12047
12717
  role: 'toolbar',
12048
12718
  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)];
12719
+ }, ...(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)];
12720
+ };
12721
+
12722
+ const authContainerStyle = 'align-items:center;display:flex;gap:8px;min-width:0;';
12723
+ const authNameStyle = 'max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;';
12724
+ const authButtonStyle = 'white-space:nowrap;';
12725
+ const getButtonLabel = (userState, isAuthenticated) => {
12726
+ if (userState === 'loggingIn') {
12727
+ return loggingInToBackend();
12728
+ }
12729
+ if (userState === 'loggingOut') {
12730
+ return loggingOutFromBackend();
12731
+ }
12732
+ return isAuthenticated ? logout() : login();
12733
+ };
12734
+ const getButtonTitle = (userState, isAuthenticated) => {
12735
+ if (userState === 'loggingIn') {
12736
+ return loggingInToBackend();
12737
+ }
12738
+ if (userState === 'loggingOut') {
12739
+ return loggingOutFromBackend();
12740
+ }
12741
+ return isAuthenticated ? logoutFromBackend$1() : loginToBackend();
12742
+ };
12743
+ const getChatHeaderAuthDom = (authEnabled = false, userState = 'loggedOut', userName = '') => {
12744
+ if (!authEnabled) {
12745
+ return [];
12746
+ }
12747
+ const isAuthenticated = userState === 'loggedIn' || userState === 'loggingOut';
12748
+ const buttonName = isAuthenticated ? Logout : Login;
12749
+ const buttonLabel = getButtonLabel(userState, isAuthenticated);
12750
+ const buttonTitle = getButtonTitle(userState, isAuthenticated);
12751
+ const isPending = userState === 'loggingIn' || userState === 'loggingOut';
12752
+ const displayName = userName || signedIn();
12753
+ return [{
12754
+ childCount: isAuthenticated ? 2 : 1,
12755
+ className: ChatHeaderAuth,
12756
+ style: authContainerStyle,
12757
+ type: Div
12758
+ }, ...(isAuthenticated ? [{
12759
+ childCount: 1,
12760
+ className: ChatHeaderAuthName,
12761
+ style: authNameStyle,
12762
+ title: displayName,
12763
+ type: Span
12764
+ }, text(displayName)] : []), {
12765
+ childCount: 1,
12766
+ className: mergeClassNames(Button, ButtonSecondary),
12767
+ disabled: isPending,
12768
+ inputType: 'button',
12769
+ name: buttonName,
12770
+ onClick: HandleClick,
12771
+ style: authButtonStyle,
12772
+ title: buttonTitle,
12773
+ type: Button$1
12774
+ }, text(buttonLabel)];
12050
12775
  };
12051
12776
 
12052
12777
  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 +12792,15 @@ const getFocusHeaderActionButtonDom = (label, name) => {
12067
12792
  type: Button$1
12068
12793
  }, text(label)];
12069
12794
  };
12070
- const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName) => {
12795
+ const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName, authEnabled = false, userState = 'loggedOut', userName = '') => {
12071
12796
  const items = [[addAction(), FocusAddAction], [openInVsCode(), FocusOpenInVsCode], [commit(), FocusCommit], [openTerminal(), FocusOpenTerminal], [showDiff(), FocusShowDiff]];
12072
12797
  const hasProjectName = !!selectedProjectName;
12073
12798
  return [{
12074
- childCount: 2,
12799
+ childCount: 2 + (authEnabled ? 1 : 0),
12075
12800
  className: ChatFocusHeader,
12076
12801
  style: focusHeaderStyle,
12077
12802
  type: Header
12078
- }, {
12803
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
12079
12804
  childCount: hasProjectName ? 2 : 1,
12080
12805
  className: ChatName,
12081
12806
  style: focusHeaderMetaStyle,
@@ -12658,6 +13383,7 @@ const getMissingApiActionsDom = ({
12658
13383
  };
12659
13384
  const getMissingApiKeyDom = ({
12660
13385
  getApiKeyText,
13386
+ inputClassName,
12661
13387
  inputName,
12662
13388
  inputPattern,
12663
13389
  inputRequired = false,
@@ -12680,7 +13406,7 @@ const getMissingApiKeyDom = ({
12680
13406
  autocomplete: 'off',
12681
13407
  autocorrect: 'off',
12682
13408
  childCount: 0,
12683
- className: InputBox,
13409
+ className: inputClassName ? mergeClassNames(InputBox, inputClassName) : InputBox,
12684
13410
  name: inputName,
12685
13411
  onInput: HandleInput,
12686
13412
  ...(inputPattern ? {
@@ -12700,13 +13426,17 @@ const getMissingApiKeyDom = ({
12700
13426
  })];
12701
13427
  };
12702
13428
 
12703
- const getMissingOpenApiApiKeyDom = (openApiApiKeyState = 'idle', openSettingsUrl = 'https://platform.openai.com/api-keys', inputPattern = '^sk-.+') => {
13429
+ const getMissingOpenApiApiKeyDom = (openApiApiKeyInput = '', openApiApiKeyState = 'idle', openSettingsUrl = 'https://platform.openai.com/api-keys', inputPattern = '^sk-.+') => {
12704
13430
  const isSaving = openApiApiKeyState === 'saving';
13431
+ const isInvalid = openApiApiKeyInput !== '' && !new RegExp(inputPattern).test(openApiApiKeyInput);
12705
13432
  return getMissingApiKeyDom({
12706
13433
  getApiKeyText: getOpenApiApiKey(),
13434
+ ...(isInvalid ? {
13435
+ inputClassName: InputInvalid
13436
+ } : {}),
12707
13437
  inputName: OpenApiApiKeyInput,
12708
13438
  inputPattern,
12709
- inputRequired: true,
13439
+ inputRequired: false,
12710
13440
  onSubmit: HandleMissingOpenAiApiKeyFormSubmit,
12711
13441
  openSettingsButtonName: OpenOpenApiApiKeyWebsite,
12712
13442
  openSettingsUrl,
@@ -13696,14 +14426,27 @@ const getChatAttachmentsDom = attachments => {
13696
14426
  }];
13697
14427
  })];
13698
14428
  };
13699
- const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput, _openApiApiKeyInput = '', openApiApiKeyState = 'idle', openApiApiKeysSettingsUrl = 'https://platform.openai.com/api-keys', openApiApiKeyInputPattern = '^sk-.+', openRouterApiKeyState = 'idle', useChatMathWorker = false) => {
14429
+ const getStandaloneImageAttachmentDom = attachment => {
14430
+ if (!attachment.previewSrc) {
14431
+ return getChatAttachmentsDom([attachment]);
14432
+ }
14433
+ return [{
14434
+ alt: attachment.name,
14435
+ childCount: 0,
14436
+ className: ChatMessageImage,
14437
+ src: attachment.previewSrc,
14438
+ type: Img
14439
+ }];
14440
+ };
14441
+ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput, openApiApiKeyInput = '', openApiApiKeyState = 'idle', openApiApiKeysSettingsUrl = 'https://platform.openai.com/api-keys', openApiApiKeyInputPattern = '^sk-.+', openRouterApiKeyState = 'idle', useChatMathWorker = false, standaloneImageAttachment) => {
13700
14442
  const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
13701
14443
  const isOpenApiApiKeyMissingMessage = message.role === 'assistant' && message.text === openApiApiKeyRequiredMessage;
13702
14444
  const isOpenRouterApiKeyMissingMessage = message.role === 'assistant' && message.text === openRouterApiKeyRequiredMessage;
13703
14445
  const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
13704
14446
  const isOpenRouterTooManyRequestsMessage = message.role === 'assistant' && message.text.startsWith(openRouterTooManyRequestsMessage);
13705
- const messageDom = getMessageContentDom(parsedMessageContent, useChatMathWorker);
13706
- const attachmentsDom = message.role === 'user' ? getChatAttachmentsDom(message.attachments ?? []) : [];
14447
+ const isStandaloneImageMessage = !!standaloneImageAttachment;
14448
+ const messageDom = isStandaloneImageMessage ? getStandaloneImageAttachmentDom(standaloneImageAttachment) : getMessageContentDom(parsedMessageContent, useChatMathWorker);
14449
+ const attachmentsDom = !isStandaloneImageMessage && message.role === 'user' ? getChatAttachmentsDom(message.attachments ?? []) : [];
13707
14450
  const toolCallsDom = getToolCallsDom(message);
13708
14451
  const toolCallsChildCount = toolCallsDom.length > 0 ? 1 : 0;
13709
14452
  const messageDomChildCount = getTopLevelNodeCount(messageDom);
@@ -13715,9 +14458,9 @@ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput
13715
14458
  type: Div
13716
14459
  }, {
13717
14460
  childCount: extraChildCount,
13718
- className: ChatMessageContent,
14461
+ className: isStandaloneImageMessage ? mergeClassNames(ChatMessageContent, ChatImageMessageContent) : ChatMessageContent,
13719
14462
  type: Div
13720
- }, ...toolCallsDom, ...messageDom, ...attachmentsDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
14463
+ }, ...toolCallsDom, ...messageDom, ...attachmentsDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
13721
14464
  };
13722
14465
 
13723
14466
  const parentNode$1 = {
@@ -13733,6 +14476,22 @@ const getEmptyMessagesDom = () => {
13733
14476
  const hasMessageText = message => {
13734
14477
  return message.text.trim().length > 0;
13735
14478
  };
14479
+ const isImageAttachment = attachment => {
14480
+ return attachment.displayType === 'image';
14481
+ };
14482
+ const withAttachments = (message, attachments) => {
14483
+ const {
14484
+ attachments: _attachments,
14485
+ ...messageWithoutAttachments
14486
+ } = message;
14487
+ if (attachments.length === 0) {
14488
+ return messageWithoutAttachments;
14489
+ }
14490
+ return {
14491
+ ...messageWithoutAttachments,
14492
+ attachments
14493
+ };
14494
+ };
13736
14495
  const getDisplayMessages = (messages, parsedMessages) => {
13737
14496
  const displayMessages = [];
13738
14497
  for (const message of messages) {
@@ -13740,6 +14499,30 @@ const getDisplayMessages = (messages, parsedMessages) => {
13740
14499
  if (!parsedContent) {
13741
14500
  continue;
13742
14501
  }
14502
+ if (message.role === 'user') {
14503
+ const attachments = message.attachments ?? [];
14504
+ const imageAttachments = attachments.filter(isImageAttachment);
14505
+ if (imageAttachments.length > 0) {
14506
+ const nonImageAttachments = attachments.filter(attachment => !isImageAttachment(attachment));
14507
+ if (hasMessageText(message) || nonImageAttachments.length > 0) {
14508
+ displayMessages.push({
14509
+ message: withAttachments(message, nonImageAttachments),
14510
+ parsedContent: hasMessageText(message) ? parsedContent : getEmptyMessageContent()
14511
+ });
14512
+ }
14513
+ for (const attachment of imageAttachments) {
14514
+ displayMessages.push({
14515
+ message: {
14516
+ ...withAttachments(message, [attachment]),
14517
+ text: ''
14518
+ },
14519
+ parsedContent: getEmptyMessageContent(),
14520
+ standaloneImageAttachment: attachment
14521
+ });
14522
+ }
14523
+ continue;
14524
+ }
14525
+ }
13743
14526
  if (message.role !== 'assistant' || !message.toolCalls || message.toolCalls.length === 0) {
13744
14527
  displayMessages.push({
13745
14528
  message,
@@ -13795,7 +14578,7 @@ const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApi
13795
14578
  role: 'log',
13796
14579
  scrollTop: messagesScrollTop,
13797
14580
  type: Div
13798
- }, ...displayMessages.flatMap(item => getChatMessageDom(item.message, item.parsedContent, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, useChatMathWorker))];
14581
+ }, ...displayMessages.flatMap(item => getChatMessageDom(item.message, item.parsedContent, openRouterApiKeyInput, openApiApiKeyInput, openApiApiKeyState, openApiApiKeysSettingsUrl, openApiApiKeyInputPattern, openRouterApiKeyState, useChatMathWorker, item.standaloneImageAttachment))];
13799
14582
  };
13800
14583
 
13801
14584
  const arrowLeft$1 = {
@@ -13909,7 +14692,6 @@ const getChatModeChatFocusVirtualDom = ({
13909
14692
  agentModePickerOpen = false,
13910
14693
  authEnabled = false,
13911
14694
  authErrorMessage = '',
13912
- authStatus = 'signed-out',
13913
14695
  composerAttachmentPreviewOverlayAttachmentId,
13914
14696
  composerAttachmentPreviewOverlayError = false,
13915
14697
  composerAttachments,
@@ -13920,6 +14702,10 @@ const getChatModeChatFocusVirtualDom = ({
13920
14702
  composerHeight = 28,
13921
14703
  composerLineHeight = 20,
13922
14704
  composerValue,
14705
+ gitBranches,
14706
+ gitBranchPickerErrorMessage,
14707
+ gitBranchPickerOpen,
14708
+ gitBranchPickerVisible,
13923
14709
  hasSpaceForAgentModePicker,
13924
14710
  hasSpaceForRunModePicker,
13925
14711
  messagesAutoScrollEnabled,
@@ -13954,14 +14740,15 @@ const getChatModeChatFocusVirtualDom = ({
13954
14740
  tokensUsed,
13955
14741
  usageOverviewEnabled,
13956
14742
  useChatMathWorker = false,
14743
+ userName = '',
14744
+ userState = 'loggedOut',
13957
14745
  visibleModels = models,
13958
14746
  voiceDictationEnabled = false
13959
14747
  }) => {
13960
14748
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
13961
- const selectedProject = projects.find(project => project.id === selectedProjectId);
13962
- const messages = selectedSession ? selectedSession.messages : [];
13963
14749
  const selectedSessionTitle = selectedSession?.title || chatTitle();
13964
- const selectedProjectName = selectedProject?.name || '';
14750
+ const selectedProjectName = projects.find(project => project.id === selectedProjectId)?.name || '';
14751
+ const messages = selectedSession ? selectedSession.messages : [];
13965
14752
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13966
14753
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13967
14754
  const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
@@ -13980,7 +14767,7 @@ const getChatModeChatFocusVirtualDom = ({
13980
14767
  childCount: 3,
13981
14768
  className: ChatFocusMainArea,
13982
14769
  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({
14770
+ }, ...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
14771
  agentMode,
13985
14772
  agentModePickerVisible: isAgentModePickerVisible,
13986
14773
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14033,31 +14820,8 @@ const getHeaderActionVirtualDom = item => {
14033
14820
  }];
14034
14821
  };
14035
14822
 
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) => {
14823
+ const getChatHeaderActionsDom = (viewMode, searchEnabled = false) => {
14059
14824
  const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
14060
- const authAction = getAuthAction(authEnabled, authStatus);
14061
14825
  const items = [{
14062
14826
  icon: mergeClassNames(MaskIcon, MaskIconLayoutPanelLeft),
14063
14827
  name: ToggleChatFocus,
@@ -14083,7 +14847,7 @@ const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'si
14083
14847
  name: Settings,
14084
14848
  onClick: HandleClickSettings,
14085
14849
  title: settings()
14086
- }, ...(authAction ? [authAction] : []), {
14850
+ }, {
14087
14851
  icon: mergeClassNames(MaskIcon, MaskIconClose),
14088
14852
  name: CloseChat,
14089
14853
  onClick: HandleClickClose,
@@ -14108,14 +14872,14 @@ const getAuthErrorDom = (hasAuthError, authErrorMessage) => {
14108
14872
  type: Span
14109
14873
  }, text(authErrorMessage)];
14110
14874
  };
14111
- const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
14875
+ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, userState = 'loggedOut', userName = '', authErrorMessage = '') => {
14112
14876
  const hasAuthError = authEnabled && !!authErrorMessage;
14113
14877
  return [{
14114
- childCount: hasAuthError ? 3 : 2,
14878
+ childCount: 2 + (authEnabled ? 1 : 0) + (hasAuthError ? 1 : 0),
14115
14879
  className: ChatHeader,
14116
14880
  onContextMenu: HandleChatHeaderContextMenu,
14117
14881
  type: Header
14118
- }, {
14882
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
14119
14883
  childCount: 2,
14120
14884
  className: ChatName,
14121
14885
  type: Div
@@ -14123,7 +14887,7 @@ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, a
14123
14887
  childCount: 1,
14124
14888
  className: ChatHeaderLabel,
14125
14889
  type: H2
14126
- }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail', authEnabled, authStatus), ...getAuthErrorDom(hasAuthError, authErrorMessage)];
14890
+ }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail'), ...getAuthErrorDom(hasAuthError, authErrorMessage)];
14127
14891
  };
14128
14892
 
14129
14893
  const getChatModeDetailVirtualDom = ({
@@ -14132,7 +14896,6 @@ const getChatModeDetailVirtualDom = ({
14132
14896
  agentModePickerOpen = false,
14133
14897
  authEnabled = false,
14134
14898
  authErrorMessage = '',
14135
- authStatus = 'signed-out',
14136
14899
  composerAttachmentPreviewOverlayAttachmentId,
14137
14900
  composerAttachmentPreviewOverlayError = false,
14138
14901
  composerAttachments,
@@ -14173,6 +14936,8 @@ const getChatModeDetailVirtualDom = ({
14173
14936
  tokensUsed,
14174
14937
  usageOverviewEnabled,
14175
14938
  useChatMathWorker = false,
14939
+ userName = '',
14940
+ userState = 'loggedOut',
14176
14941
  visibleModels = models,
14177
14942
  voiceDictationEnabled = false
14178
14943
  }) => {
@@ -14193,7 +14958,7 @@ const getChatModeDetailVirtualDom = ({
14193
14958
  onDragEnter: HandleDragEnterChatView,
14194
14959
  onDragOver: HandleDragOverChatView,
14195
14960
  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({
14961
+ }, ...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
14962
  agentMode,
14198
14963
  agentModePickerVisible: isAgentModePickerVisible,
14199
14964
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14229,20 +14994,20 @@ const getChatSearchDom = (hasSearchField, searchValue) => {
14229
14994
  value: searchValue
14230
14995
  }];
14231
14996
  };
14232
- const getChatHeaderListModeDom = (authEnabled = false, authStatus = 'signed-out', authErrorMessage = '', searchEnabled = false, searchFieldVisible = false, searchValue = '') => {
14997
+ const getChatHeaderListModeDom = (authEnabled = false, userState = 'loggedOut', userName = '', authErrorMessage = '', searchEnabled = false, searchFieldVisible = false, searchValue = '') => {
14233
14998
  const hasAuthError = authEnabled && !!authErrorMessage;
14234
14999
  const hasSearchField = searchEnabled && searchFieldVisible;
14235
- const headerChildCount = 2 + (hasAuthError ? 1 : 0) + (hasSearchField ? 1 : 0);
15000
+ const headerChildCount = 2 + (authEnabled ? 1 : 0) + (hasAuthError ? 1 : 0) + (hasSearchField ? 1 : 0);
14236
15001
  return [{
14237
15002
  childCount: headerChildCount,
14238
15003
  className: ChatHeader,
14239
15004
  onContextMenu: HandleChatHeaderContextMenu,
14240
15005
  type: Header
14241
- }, {
15006
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
14242
15007
  childCount: 1,
14243
15008
  className: ChatHeaderLabel,
14244
15009
  type: H2
14245
- }, text(chats()), ...getChatHeaderActionsDom('list', authEnabled, authStatus, searchEnabled), ...getChatSearchDom(hasSearchField, searchValue), ...(hasAuthError ? [{
15010
+ }, text(chats()), ...getChatHeaderActionsDom('list', searchEnabled), ...getChatSearchDom(hasSearchField, searchValue), ...(hasAuthError ? [{
14246
15011
  childCount: 1,
14247
15012
  className: ChatAuthError,
14248
15013
  type: Span
@@ -14342,7 +15107,6 @@ const getChatModeListVirtualDom = ({
14342
15107
  agentModePickerOpen = false,
14343
15108
  authEnabled = false,
14344
15109
  authErrorMessage = '',
14345
- authStatus = 'signed-out',
14346
15110
  chatListScrollTop = 0,
14347
15111
  composerAttachmentPreviewOverlayAttachmentId,
14348
15112
  composerAttachmentPreviewOverlayError = false,
@@ -14377,6 +15141,8 @@ const getChatModeListVirtualDom = ({
14377
15141
  tokensMax,
14378
15142
  tokensUsed,
14379
15143
  usageOverviewEnabled,
15144
+ userName = '',
15145
+ userState = 'loggedOut',
14380
15146
  visibleModels = models,
14381
15147
  voiceDictationEnabled = false
14382
15148
  }) => {
@@ -14395,7 +15161,7 @@ const getChatModeListVirtualDom = ({
14395
15161
  onDragEnter: HandleDragEnterChatView,
14396
15162
  onDragOver: HandleDragOverChatView,
14397
15163
  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({
15164
+ }, ...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
15165
  agentMode,
14400
15166
  agentModePickerVisible: isAgentModePickerVisible,
14401
15167
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14498,7 +15264,6 @@ const getChatVirtualDom = options => {
14498
15264
  agentModePickerOpen = false,
14499
15265
  authEnabled = false,
14500
15266
  authErrorMessage = '',
14501
- authStatus = 'signed-out',
14502
15267
  chatListScrollTop,
14503
15268
  composerAttachmentPreviewOverlayAttachmentId,
14504
15269
  composerAttachmentPreviewOverlayError = false,
@@ -14510,6 +15275,10 @@ const getChatVirtualDom = options => {
14510
15275
  composerHeight,
14511
15276
  composerLineHeight,
14512
15277
  composerValue,
15278
+ gitBranches = [],
15279
+ gitBranchPickerErrorMessage = '',
15280
+ gitBranchPickerOpen = false,
15281
+ gitBranchPickerVisible = false,
14513
15282
  hasSpaceForAgentModePicker,
14514
15283
  hasSpaceForRunModePicker,
14515
15284
  listFocusedIndex = -1,
@@ -14547,6 +15316,8 @@ const getChatVirtualDom = options => {
14547
15316
  tokensUsed,
14548
15317
  usageOverviewEnabled,
14549
15318
  useChatMathWorker = false,
15319
+ userName = '',
15320
+ userState = 'loggedOut',
14550
15321
  viewMode,
14551
15322
  visibleModels = models,
14552
15323
  voiceDictationEnabled = false
@@ -14561,7 +15332,6 @@ const getChatVirtualDom = options => {
14561
15332
  agentModePickerOpen,
14562
15333
  authEnabled,
14563
15334
  authErrorMessage,
14564
- authStatus,
14565
15335
  composerAttachmentPreviewOverlayAttachmentId,
14566
15336
  composerAttachmentPreviewOverlayError,
14567
15337
  composerAttachments,
@@ -14572,6 +15342,10 @@ const getChatVirtualDom = options => {
14572
15342
  composerHeight,
14573
15343
  composerLineHeight,
14574
15344
  composerValue,
15345
+ gitBranches,
15346
+ gitBranchPickerErrorMessage,
15347
+ gitBranchPickerOpen,
15348
+ gitBranchPickerVisible,
14575
15349
  hasSpaceForAgentModePicker,
14576
15350
  hasSpaceForRunModePicker,
14577
15351
  messagesAutoScrollEnabled,
@@ -14606,6 +15380,8 @@ const getChatVirtualDom = options => {
14606
15380
  tokensUsed,
14607
15381
  usageOverviewEnabled,
14608
15382
  useChatMathWorker,
15383
+ userName,
15384
+ userState,
14609
15385
  visibleModels,
14610
15386
  voiceDictationEnabled
14611
15387
  });
@@ -14616,7 +15392,6 @@ const getChatVirtualDom = options => {
14616
15392
  agentModePickerOpen,
14617
15393
  authEnabled,
14618
15394
  authErrorMessage,
14619
- authStatus,
14620
15395
  composerAttachmentPreviewOverlayAttachmentId,
14621
15396
  composerAttachmentPreviewOverlayError,
14622
15397
  composerAttachments,
@@ -14657,6 +15432,8 @@ const getChatVirtualDom = options => {
14657
15432
  tokensUsed,
14658
15433
  usageOverviewEnabled,
14659
15434
  useChatMathWorker,
15435
+ userName,
15436
+ userState,
14660
15437
  visibleModels,
14661
15438
  voiceDictationEnabled
14662
15439
  });
@@ -14667,7 +15444,6 @@ const getChatVirtualDom = options => {
14667
15444
  agentModePickerOpen,
14668
15445
  authEnabled,
14669
15446
  authErrorMessage,
14670
- authStatus,
14671
15447
  chatListScrollTop,
14672
15448
  composerAttachmentPreviewOverlayAttachmentId,
14673
15449
  composerAttachmentPreviewOverlayError,
@@ -14702,6 +15478,8 @@ const getChatVirtualDom = options => {
14702
15478
  tokensMax,
14703
15479
  tokensUsed,
14704
15480
  usageOverviewEnabled,
15481
+ userName,
15482
+ userState,
14705
15483
  visibleModels,
14706
15484
  voiceDictationEnabled
14707
15485
  });
@@ -14717,7 +15495,6 @@ const renderItems = (oldState, newState) => {
14717
15495
  agentModePickerOpen,
14718
15496
  authEnabled,
14719
15497
  authErrorMessage,
14720
- authStatus,
14721
15498
  chatListScrollTop,
14722
15499
  composerAttachmentPreviewOverlayAttachmentId,
14723
15500
  composerAttachmentPreviewOverlayError,
@@ -14729,6 +15506,10 @@ const renderItems = (oldState, newState) => {
14729
15506
  composerHeight,
14730
15507
  composerLineHeight,
14731
15508
  composerValue,
15509
+ gitBranches,
15510
+ gitBranchPickerErrorMessage,
15511
+ gitBranchPickerOpen,
15512
+ gitBranchPickerVisible,
14732
15513
  hasSpaceForAgentModePicker,
14733
15514
  hasSpaceForRunModePicker,
14734
15515
  initial,
@@ -14768,6 +15549,8 @@ const renderItems = (oldState, newState) => {
14768
15549
  uid,
14769
15550
  usageOverviewEnabled,
14770
15551
  useChatMathWorker,
15552
+ userName,
15553
+ userState,
14771
15554
  viewMode,
14772
15555
  visibleModels,
14773
15556
  voiceDictationEnabled
@@ -14781,7 +15564,6 @@ const renderItems = (oldState, newState) => {
14781
15564
  agentModePickerOpen,
14782
15565
  authEnabled,
14783
15566
  authErrorMessage,
14784
- authStatus,
14785
15567
  chatListScrollTop,
14786
15568
  composerAttachmentPreviewOverlayAttachmentId,
14787
15569
  composerAttachmentPreviewOverlayError,
@@ -14793,6 +15575,10 @@ const renderItems = (oldState, newState) => {
14793
15575
  composerHeight,
14794
15576
  composerLineHeight,
14795
15577
  composerValue,
15578
+ gitBranches,
15579
+ gitBranchPickerErrorMessage,
15580
+ gitBranchPickerOpen,
15581
+ gitBranchPickerVisible,
14796
15582
  hasSpaceForAgentModePicker,
14797
15583
  hasSpaceForRunModePicker,
14798
15584
  listFocusedIndex,
@@ -14830,6 +15616,8 @@ const renderItems = (oldState, newState) => {
14830
15616
  tokensUsed,
14831
15617
  usageOverviewEnabled,
14832
15618
  useChatMathWorker,
15619
+ userName,
15620
+ userState,
14833
15621
  viewMode,
14834
15622
  visibleModels,
14835
15623
  voiceDictationEnabled
@@ -14876,8 +15664,16 @@ const renderSelection = (oldState, newState) => {
14876
15664
  const renderValue = (oldState, newState) => {
14877
15665
  const {
14878
15666
  composerValue,
15667
+ openApiApiKeyInput,
15668
+ openRouterApiKeyInput,
14879
15669
  uid
14880
15670
  } = newState;
15671
+ if (oldState.openApiApiKeyInput !== openApiApiKeyInput) {
15672
+ return [SetValueByName, uid, OpenApiApiKeyInput, openApiApiKeyInput];
15673
+ }
15674
+ if (oldState.openRouterApiKeyInput !== openRouterApiKeyInput) {
15675
+ return [SetValueByName, uid, OpenRouterApiKeyInput, openRouterApiKeyInput];
15676
+ }
14881
15677
  return [SetValueByName, uid, Composer, composerValue];
14882
15678
  };
14883
15679
 
@@ -14971,6 +15767,9 @@ const renderEventListeners = () => {
14971
15767
  }, {
14972
15768
  name: HandleClickAgentModePickerToggle,
14973
15769
  params: ['openAgentModePicker']
15770
+ }, {
15771
+ name: HandleClickGitBranchPickerToggle,
15772
+ params: ['handleClickGitBranchPickerToggle']
14974
15773
  }, {
14975
15774
  name: HandleClickModelPickerOverlay,
14976
15775
  params: ['handleClickModelPickerOverlay', DefaultPrevented]
@@ -15129,6 +15928,10 @@ const reset = async state => {
15129
15928
  composerSelectionEnd: 0,
15130
15929
  composerSelectionStart: 0,
15131
15930
  composerValue: '',
15931
+ gitBranches: [],
15932
+ gitBranchPickerErrorMessage: '',
15933
+ gitBranchPickerOpen: false,
15934
+ gitBranchPickerVisible: false,
15132
15935
  mockAiResponseDelay: 0,
15133
15936
  modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, state.models.length),
15134
15937
  modelPickerListScrollTop: 0,
@@ -15208,7 +16011,6 @@ const saveState = state => {
15208
16011
  selectedModelId,
15209
16012
  selectedProjectId,
15210
16013
  selectedSessionId,
15211
- systemPrompt,
15212
16014
  viewMode
15213
16015
  } = state;
15214
16016
  return {
@@ -15231,7 +16033,6 @@ const saveState = state => {
15231
16033
  selectedModelId,
15232
16034
  selectedProjectId,
15233
16035
  selectedSessionId,
15234
- systemPrompt,
15235
16036
  viewMode
15236
16037
  };
15237
16038
  };
@@ -15246,7 +16047,13 @@ const setAddContextButtonEnabled = (state, addContextButtonEnabled) => {
15246
16047
  const setAuthEnabled = (state, authEnabled) => {
15247
16048
  return {
15248
16049
  ...state,
15249
- authEnabled
16050
+ authAccessToken: authEnabled ? state.authAccessToken : '',
16051
+ authEnabled,
16052
+ authErrorMessage: authEnabled ? state.authErrorMessage : '',
16053
+ userName: authEnabled ? state.userName : '',
16054
+ userState: authEnabled ? state.userState : 'loggedOut',
16055
+ userSubscriptionPlan: authEnabled ? state.userSubscriptionPlan : '',
16056
+ userUsedTokens: authEnabled ? state.userUsedTokens : 0
15250
16057
  };
15251
16058
  };
15252
16059
 
@@ -15440,6 +16247,7 @@ const commandMap = {
15440
16247
  'Chat.chatListFocusNext': wrapCommand(chatListFocusNext),
15441
16248
  'Chat.chatListFocusPrevious': wrapCommand(chatListFocusPrevious),
15442
16249
  'Chat.clearInput': wrapCommand(clearInput),
16250
+ 'Chat.closeGitBranchPicker': wrapCommand(closeGitBranchPicker),
15443
16251
  'Chat.copyInput': wrapCommand(copyInput),
15444
16252
  'Chat.create': create,
15445
16253
  'Chat.cutInput': wrapCommand(cutInput),
@@ -15468,6 +16276,7 @@ const commandMap = {
15468
16276
  'Chat.handleClickDelete': wrapCommand(handleClickDelete),
15469
16277
  'Chat.handleClickDictationButton': wrapCommand(handleClickDictationButton),
15470
16278
  'Chat.handleClickFileName': wrapCommand(handleClickFileName),
16279
+ 'Chat.handleClickGitBranchPickerToggle': wrapCommand(handleClickGitBranchPickerToggle),
15471
16280
  'Chat.handleClickList': wrapCommand(handleClickList),
15472
16281
  'Chat.handleClickModelPickerList': wrapCommand(handleClickModelPickerList),
15473
16282
  'Chat.handleClickModelPickerListIndex': wrapCommand(handleClickModelPickerListIndex),
@@ -15519,6 +16328,7 @@ const commandMap = {
15519
16328
  'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
15520
16329
  'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
15521
16330
  'Chat.openAgentModePicker': wrapCommand(openAgentModePicker),
16331
+ 'Chat.openGitBranchPicker': wrapCommand(openGitBranchPicker),
15522
16332
  'Chat.openMockProject': wrapCommand(openMockProject),
15523
16333
  'Chat.openMockSession': wrapCommand(openMockSession),
15524
16334
  'Chat.openModelPicker': wrapCommand(openModelPicker),