@lvce-editor/chat-view 6.69.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';
@@ -6997,6 +7436,28 @@ const jsRules = [{
6997
7436
  className: TokenKeyword,
6998
7437
  regex: /\b(?:const|let|var|function|return|if|else|for|while|class|new|typeof|instanceof|import|export|from|default|async|await|try|catch|finally|throw|this|true|false|null|undefined)\b/
6999
7438
  }];
7439
+ const tsRules = [{
7440
+ className: TokenComment,
7441
+ regex: /\/\/[^\n]*/
7442
+ }, {
7443
+ className: TokenComment,
7444
+ regex: /\/\*[\s\S]*?\*\//
7445
+ }, {
7446
+ className: TokenString,
7447
+ regex: /"[^"\\]*(?:\\.[^"\\]*)*"/
7448
+ }, {
7449
+ className: TokenString,
7450
+ regex: /'[^'\\]*(?:\\.[^'\\]*)*'/
7451
+ }, {
7452
+ className: TokenString,
7453
+ regex: /`[^`\\]*(?:\\.[^`\\]*)*`/
7454
+ }, {
7455
+ className: TokenNumber,
7456
+ regex: /\b\d+\.?\d*\b/
7457
+ }, {
7458
+ className: TokenKeyword,
7459
+ regex: /\b(?:abstract|any|as|asserts|async|await|boolean|class|const|constructor|declare|default|enum|export|extends|false|finally|for|from|function|get|if|implements|import|in|infer|instanceof|interface|is|keyof|let|module|namespace|never|new|null|number|object|override|private|protected|public|readonly|return|satisfies|set|static|string|super|switch|symbol|this|throw|true|try|type|typeof|undefined|unknown|var|void|while)\b/
7460
+ }];
7000
7461
  const htmlRules = [{
7001
7462
  className: TokenComment,
7002
7463
  regex: /<!--[\s\S]*?-->/
@@ -7120,6 +7581,9 @@ const highlightCode = (code, language) => {
7120
7581
  if (normalized === 'js' || normalized === 'javascript') {
7121
7582
  return tokenize(code, jsRules);
7122
7583
  }
7584
+ if (normalized === 'ts' || normalized === 'typescript') {
7585
+ return tokenize(code, tsRules);
7586
+ }
7123
7587
  if (normalized === 'py' || normalized === 'python') {
7124
7588
  return tokenize(code, pythonRules);
7125
7589
  }
@@ -8263,6 +8727,12 @@ const parseAndStoreMessageContent = async (parsedMessages, message) => {
8263
8727
  const parsedContent = message.text === '' ? emptyMessageContent : await parseMessage(message.text);
8264
8728
  return setParsedMessageContent(parsedMessages, message.id, message.text, parsedContent);
8265
8729
  };
8730
+ const parseAndStoreMessageContentWithWorkerPreference = async (parsedMessages, message, useChatMessageParsingWorker) => {
8731
+ if (useChatMessageParsingWorker) {
8732
+ return parseAndStoreMessagesContentInWorker(parsedMessages, [message]);
8733
+ }
8734
+ return parseAndStoreMessageContent(parsedMessages, message);
8735
+ };
8266
8736
  const parseAndStoreMessagesContent = async (parsedMessages, messages) => {
8267
8737
  let nextParsedMessages = parsedMessages;
8268
8738
  for (const message of messages) {
@@ -8295,8 +8765,15 @@ const parseAndStoreMessagesContentWithWorkerPreference = async (parsedMessages,
8295
8765
  }
8296
8766
  return parseAndStoreMessagesContent(parsedMessages, messages);
8297
8767
  };
8298
- const getEmptyMessageContent = () => {
8299
- 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);
8300
8777
  };
8301
8778
 
8302
8779
  const setOpenApiApiKey = async (state, openApiApiKey, persist = true) => {
@@ -8713,10 +9190,6 @@ const getNextHandleTextChunkState = (latestState, parsedMessages, sessions) => {
8713
9190
  };
8714
9191
  };
8715
9192
 
8716
- const getSelectedSession = (sessions, selectedSessionId) => {
8717
- return sessions.find(session => session.id === selectedSessionId);
8718
- };
8719
-
8720
9193
  const setAndRerenderHandleTextChunkState = async (uid, previousState, nextState) => {
8721
9194
  set(uid, previousState, nextState);
8722
9195
  // @ts-ignore
@@ -8803,7 +9276,7 @@ const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls,
8803
9276
  };
8804
9277
  };
8805
9278
 
8806
- const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress) => {
9279
+ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, selectedSessionId, messageId, text, inProgress, useChatMessageParsingWorker) => {
8807
9280
  let updatedMessage;
8808
9281
  const updatedSessions = sessions.map(session => {
8809
9282
  if (session.id !== selectedSessionId) {
@@ -8826,7 +9299,7 @@ const updateMessageTextInSelectedSession = async (sessions, parsedMessages, sele
8826
9299
  });
8827
9300
  let nextParsedMessages = parsedMessages;
8828
9301
  if (updatedMessage) {
8829
- nextParsedMessages = await parseAndStoreMessageContent(parsedMessages, updatedMessage);
9302
+ nextParsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, updatedMessage, useChatMessageParsingWorker);
8830
9303
  }
8831
9304
  return {
8832
9305
  parsedMessages: nextParsedMessages,
@@ -8849,7 +9322,7 @@ const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTex
8849
9322
  };
8850
9323
  }
8851
9324
  const updatedText = assistantMessage.text + chunk;
8852
- 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);
8853
9326
  const nextState = {
8854
9327
  ...handleTextChunkState.latestState,
8855
9328
  ...(handleTextChunkState.latestState.messagesAutoScrollEnabled ? {
@@ -9015,6 +9488,11 @@ const withProvisionedBackgroundSession = async (state, session) => {
9015
9488
  };
9016
9489
  };
9017
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;
9018
9496
  const {
9019
9497
  agentMode,
9020
9498
  aiSessionTitleGenerationEnabled,
@@ -9043,27 +9521,28 @@ const handleSubmit = async state => {
9043
9521
  streamingEnabled,
9044
9522
  toolEnablement,
9045
9523
  useChatCoordinatorWorker,
9524
+ useChatMessageParsingWorker,
9046
9525
  useChatNetworkWorkerForRequests,
9047
9526
  useChatToolWorker,
9048
9527
  useMockApi,
9049
9528
  viewMode,
9050
9529
  webSearchEnabled
9051
- } = state;
9530
+ } = effectiveState;
9052
9531
  const userText = composerValue.trim();
9053
9532
  if (!userText) {
9054
- return state;
9533
+ return effectiveState;
9055
9534
  }
9056
9535
  const slashCommand = getSlashCommand(userText);
9057
9536
  if (slashCommand) {
9058
- return executeSlashCommand(state, slashCommand);
9537
+ return executeSlashCommand(effectiveState, slashCommand);
9059
9538
  }
9060
9539
  const userTime = new Date().toLocaleTimeString([], {
9061
9540
  hour: '2-digit',
9062
9541
  minute: '2-digit'
9063
9542
  });
9064
9543
  const userMessageId = crypto.randomUUID();
9065
- const composerAttachments = state.composerAttachments.length > 0 ? state.composerAttachments : await getComposerAttachments(state.selectedSessionId);
9066
- 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));
9067
9546
  const userMessage = {
9068
9547
  ...(composerAttachments.length > 0 ? {
9069
9548
  attachments: composerAttachments
@@ -9087,9 +9566,9 @@ const handleSubmit = async state => {
9087
9566
  };
9088
9567
  let {
9089
9568
  parsedMessages
9090
- } = state;
9091
- parsedMessages = await parseAndStoreMessageContent(parsedMessages, userMessage);
9092
- parsedMessages = await parseAndStoreMessageContent(parsedMessages, inProgressAssistantMessage);
9569
+ } = effectiveState;
9570
+ parsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, userMessage, useChatMessageParsingWorker);
9571
+ parsedMessages = await parseAndStoreMessageContentWithWorkerPreference(parsedMessages, inProgressAssistantMessage, useChatMessageParsingWorker);
9093
9572
  let workingSessions = sessions;
9094
9573
  if (viewMode === 'detail') {
9095
9574
  const loadedSession = await getChatSession(selectedSessionId);
@@ -9121,10 +9600,10 @@ const handleSubmit = async state => {
9121
9600
  const provisionedSession = await withProvisionedBackgroundSession(state, newSession);
9122
9601
  await saveChatSession(provisionedSession);
9123
9602
  optimisticState = withUpdatedMessageScrollTop(focusInput({
9124
- ...state,
9603
+ ...effectiveState,
9125
9604
  composerAttachments: [],
9126
9605
  composerAttachmentsHeight: 0,
9127
- composerHeight: getMinComposerHeightForState(state),
9606
+ composerHeight: getMinComposerHeightForState(effectiveState),
9128
9607
  composerSelectionEnd: 0,
9129
9608
  composerSelectionStart: 0,
9130
9609
  composerValue: '',
@@ -9160,10 +9639,10 @@ const handleSubmit = async state => {
9160
9639
  await saveChatSession(selectedSession);
9161
9640
  }
9162
9641
  optimisticState = withUpdatedMessageScrollTop(focusInput({
9163
- ...state,
9642
+ ...effectiveState,
9164
9643
  composerAttachments: [],
9165
9644
  composerAttachmentsHeight: 0,
9166
- composerHeight: getMinComposerHeightForState(state),
9645
+ composerHeight: getMinComposerHeightForState(effectiveState),
9167
9646
  composerSelectionEnd: 0,
9168
9647
  composerSelectionStart: 0,
9169
9648
  composerValue: '',
@@ -9175,7 +9654,7 @@ const handleSubmit = async state => {
9175
9654
  }));
9176
9655
  optimisticState = withUpdatedChatInputHistory(optimisticState, userText);
9177
9656
  }
9178
- set(state.uid, state, optimisticState);
9657
+ set(effectiveState.uid, effectiveState, optimisticState);
9179
9658
  // @ts-ignore
9180
9659
  await invoke('Chat.rerender');
9181
9660
  let handleTextChunkState = {
@@ -9264,11 +9743,11 @@ const handleSubmit = async state => {
9264
9743
  let finalParsedMessages = latestState.parsedMessages;
9265
9744
  let updatedSessions;
9266
9745
  if (streamingEnabled) {
9267
- 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);
9268
9747
  updatedSessions = updated.sessions;
9269
9748
  finalParsedMessages = updated.parsedMessages;
9270
9749
  } else {
9271
- finalParsedMessages = await parseAndStoreMessageContent(finalParsedMessages, assistantMessage);
9750
+ finalParsedMessages = await parseAndStoreMessageContentWithWorkerPreference(finalParsedMessages, assistantMessage, latestState.useChatMessageParsingWorker);
9272
9751
  updatedSessions = appendMessageToSelectedSession(latestState.sessions, latestState.selectedSessionId, assistantMessage);
9273
9752
  }
9274
9753
  if (aiSessionTitleGenerationEnabled && createsNewSession) {
@@ -9297,6 +9776,84 @@ const handleClickSend = async state => {
9297
9776
  return handleSubmit(state);
9298
9777
  };
9299
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
+
9300
9857
  const setReasoningEffort = (state, reasoningEffort) => {
9301
9858
  return {
9302
9859
  ...state,
@@ -9427,7 +9984,7 @@ const selectSession = async (state, id) => {
9427
9984
  }
9428
9985
  return loadedSession;
9429
9986
  });
9430
- return {
9987
+ return refreshGitBranchPickerVisibility({
9431
9988
  ...state,
9432
9989
  composerAttachments,
9433
9990
  composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, width),
@@ -9436,7 +9993,7 @@ const selectSession = async (state, id) => {
9436
9993
  selectedSessionId: id,
9437
9994
  sessions: hydratedSessions,
9438
9995
  viewMode: viewMode === 'chat-focus' ? 'chat-focus' : 'detail'
9439
- };
9996
+ });
9440
9997
  };
9441
9998
 
9442
9999
  const startRename = (state, id) => {
@@ -9466,15 +10023,17 @@ const toggleChatFocusMode = async state => {
9466
10023
  if (viewMode === 'chat-focus') {
9467
10024
  return {
9468
10025
  ...state,
10026
+ gitBranchPickerErrorMessage: '',
10027
+ gitBranchPickerOpen: false,
9469
10028
  viewMode: lastNormalViewMode
9470
10029
  };
9471
10030
  }
9472
10031
  if (viewMode === 'list' || viewMode === 'detail') {
9473
- return {
10032
+ return refreshGitBranchPickerVisibility({
9474
10033
  ...state,
9475
10034
  lastNormalViewMode: viewMode,
9476
10035
  viewMode: 'chat-focus'
9477
- };
10036
+ });
9478
10037
  }
9479
10038
  return state;
9480
10039
  };
@@ -9604,6 +10163,11 @@ const handleClick = async (state, name, id = '', eventX = 0, eventY = 0) => {
9604
10163
  visibleModels: state.models
9605
10164
  };
9606
10165
  }
10166
+ case isGitBranchPickerItemInputName(name):
10167
+ {
10168
+ const branchName = getGitBranchFromGitBranchPickerItemInputName(name);
10169
+ return handleGitBranchChange(state, branchName);
10170
+ }
9607
10171
  case isAgentModePickerItemInputName(name):
9608
10172
  {
9609
10173
  const agentMode = getAgentModeFromAgentModePickerItemInputName(name);
@@ -9728,6 +10292,71 @@ const handleClickFileName = async (state, uri) => {
9728
10292
  return state;
9729
10293
  };
9730
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
+
9731
10360
  const handleClickModelPickerList = async (state, eventY) => {
9732
10361
  const {
9733
10362
  height,
@@ -10590,19 +11219,6 @@ const getSavedSessions = savedState => {
10590
11219
  return sessions;
10591
11220
  };
10592
11221
 
10593
- const getSavedSystemPrompt = savedState => {
10594
- if (!isObject$1(savedState)) {
10595
- return undefined;
10596
- }
10597
- const {
10598
- systemPrompt
10599
- } = savedState;
10600
- if (typeof systemPrompt !== 'string') {
10601
- return undefined;
10602
- }
10603
- return systemPrompt;
10604
- };
10605
-
10606
11222
  const getSavedViewMode = savedState => {
10607
11223
  if (!isObject$1(savedState)) {
10608
11224
  return undefined;
@@ -10634,24 +11250,6 @@ const loadAuthEnabled = async () => {
10634
11250
  }
10635
11251
  };
10636
11252
 
10637
- const loadBackendAccessToken = async () => {
10638
- try {
10639
- const savedAccessToken = await get('secrets.chatBackendAccessToken');
10640
- return typeof savedAccessToken === 'string' ? savedAccessToken : '';
10641
- } catch {
10642
- return '';
10643
- }
10644
- };
10645
-
10646
- const loadBackendRefreshToken = async () => {
10647
- try {
10648
- const savedRefreshToken = await get('secrets.chatBackendRefreshToken');
10649
- return typeof savedRefreshToken === 'string' ? savedRefreshToken : '';
10650
- } catch {
10651
- return '';
10652
- }
10653
- };
10654
-
10655
11253
  const loadBackendUrl = async () => {
10656
11254
  try {
10657
11255
  const savedBackendUrl = await get('chat.backendUrl');
@@ -10832,12 +11430,10 @@ const loadVoiceDictationEnabled = async () => {
10832
11430
  };
10833
11431
 
10834
11432
  const loadPreferences = async () => {
10835
- 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()]);
10836
11434
  return {
10837
11435
  aiSessionTitleGenerationEnabled,
10838
- authAccessToken,
10839
11436
  authEnabled,
10840
- authRefreshToken,
10841
11437
  backendUrl,
10842
11438
  chatHistoryEnabled,
10843
11439
  composerDropEnabled,
@@ -10909,9 +11505,7 @@ const loadContent = async (state, savedState) => {
10909
11505
  const [composerSelectionStart, composerSelectionEnd] = savedComposerSelection ?? [state.composerSelectionStart, state.composerSelectionEnd];
10910
11506
  const {
10911
11507
  aiSessionTitleGenerationEnabled,
10912
- authAccessToken,
10913
11508
  authEnabled,
10914
- authRefreshToken,
10915
11509
  backendUrl,
10916
11510
  chatHistoryEnabled,
10917
11511
  composerDropEnabled,
@@ -10927,11 +11521,13 @@ const loadContent = async (state, savedState) => {
10927
11521
  toolEnablement,
10928
11522
  useChatCoordinatorWorker,
10929
11523
  useChatMathWorker,
10930
- useChatMessageParsingWorker,
11524
+ // useChatMessageParsingWorker,
10931
11525
  useChatNetworkWorkerForRequests,
10932
11526
  useChatToolWorker,
10933
11527
  voiceDictationEnabled
10934
11528
  } = await loadPreferences();
11529
+ const useChatMessageParsingWorker = true;
11530
+ const authState = authEnabled && backendUrl ? await syncBackendAuth(backendUrl) : getLoggedOutBackendAuthState();
10935
11531
  const legacySavedSessions = getSavedSessions(savedState);
10936
11532
  const storedSessions = await listChatSessions();
10937
11533
  let sessions = storedSessions;
@@ -10967,7 +11563,6 @@ const loadContent = async (state, savedState) => {
10967
11563
  const projectExpandedIds = (savedProjectExpandedIds || state.projectExpandedIds).filter(id => projects.some(project => project.id === id));
10968
11564
  const reasoningEffort = getSavedReasoningEffort(savedState) ?? state.reasoningEffort;
10969
11565
  const selectedModelId = state.models.some(model => model.id === preferredModelId) ? preferredModelId : state.models[0]?.id || '';
10970
- const systemPrompt = getSavedSystemPrompt(savedState) ?? state.systemPrompt;
10971
11566
  const visibleModels = getVisibleModels(state.models, '');
10972
11567
  const visibleSessions = getVisibleSessions(sessions, selectedProjectId);
10973
11568
  const selectedSessionId = visibleSessions.some(session => session.id === preferredSessionId) ? preferredSessionId : visibleSessions[0]?.id || '';
@@ -10983,15 +11578,14 @@ const loadContent = async (state, savedState) => {
10983
11578
  const savedLastNormalViewMode = getSavedLastNormalViewMode(savedState);
10984
11579
  const lastNormalViewMode = savedLastNormalViewMode || (preferredViewMode === 'detail' ? 'detail' : state.lastNormalViewMode);
10985
11580
  const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode;
10986
- return {
11581
+ const nextState = {
10987
11582
  ...state,
10988
11583
  agentMode,
10989
11584
  agentModePickerOpen: false,
10990
11585
  aiSessionTitleGenerationEnabled,
10991
- authAccessToken,
11586
+ authAccessToken: authState.authAccessToken,
10992
11587
  authEnabled,
10993
- authRefreshToken,
10994
- authStatus: authAccessToken ? 'signed-in' : 'signed-out',
11588
+ authErrorMessage: authState.authErrorMessage,
10995
11589
  backendUrl,
10996
11590
  chatHistoryEnabled,
10997
11591
  chatListScrollTop,
@@ -11032,7 +11626,6 @@ const loadContent = async (state, savedState) => {
11032
11626
  selectedSessionId,
11033
11627
  sessions,
11034
11628
  streamingEnabled,
11035
- systemPrompt,
11036
11629
  todoListToolEnabled,
11037
11630
  toolEnablement,
11038
11631
  useChatCoordinatorWorker,
@@ -11040,10 +11633,15 @@ const loadContent = async (state, savedState) => {
11040
11633
  useChatMessageParsingWorker,
11041
11634
  useChatNetworkWorkerForRequests,
11042
11635
  useChatToolWorker,
11636
+ userName: authState.userName,
11637
+ userState: authState.userState,
11638
+ userSubscriptionPlan: authState.userSubscriptionPlan,
11639
+ userUsedTokens: authState.userUsedTokens,
11043
11640
  viewMode,
11044
11641
  visibleModels,
11045
11642
  voiceDictationEnabled
11046
11643
  };
11644
+ return refreshGitBranchPickerVisibility(nextState);
11047
11645
  };
11048
11646
 
11049
11647
  const isObject = value => {
@@ -11133,7 +11731,24 @@ const openMockProject = async (state, projectId, projectName, projectUri) => {
11133
11731
  };
11134
11732
  };
11135
11733
 
11136
- 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) => {
11137
11752
  const {
11138
11753
  sessions: currentSessions
11139
11754
  } = state;
@@ -11146,20 +11761,20 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11146
11761
  if (session.id !== mockSessionId) {
11147
11762
  return session;
11148
11763
  }
11149
- return {
11764
+ return applySessionOptions({
11150
11765
  ...session,
11151
11766
  messages: mockChatMessages
11152
- };
11153
- }) : [...currentSessions, {
11767
+ }, options);
11768
+ }) : [...currentSessions, applySessionOptions({
11154
11769
  id: mockSessionId,
11155
11770
  messages: mockChatMessages,
11156
11771
  title: mockSessionId
11157
- }];
11772
+ }, options)];
11158
11773
  const selectedSession = sessions.find(session => session.id === mockSessionId);
11159
11774
  if (selectedSession) {
11160
11775
  await saveChatSession(selectedSession);
11161
11776
  }
11162
- return {
11777
+ return refreshGitBranchPickerVisibility({
11163
11778
  ...state,
11164
11779
  composerAttachments: [],
11165
11780
  composerAttachmentsHeight: 0,
@@ -11168,7 +11783,7 @@ const openMockSession = async (state, mockSessionId, mockChatMessages) => {
11168
11783
  selectedSessionId: mockSessionId,
11169
11784
  sessions,
11170
11785
  viewMode: 'detail'
11171
- };
11786
+ });
11172
11787
  };
11173
11788
 
11174
11789
  const pasteInput = async state => {
@@ -11281,6 +11896,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11281
11896
  color: var(--vscode-errorForeground, var(--vscode-foreground));
11282
11897
  }
11283
11898
 
11899
+ .InputInvalid{
11900
+ border-color: var(--vscode-inputValidation-errorBorder, var(--vscode-errorForeground));
11901
+ }
11902
+
11284
11903
  .ChatComposerAttachmentTextFile{
11285
11904
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11286
11905
  }
@@ -11335,6 +11954,23 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11335
11954
  border-color: var(--vscode-charts-green, var(--vscode-widget-border, var(--vscode-panel-border)));
11336
11955
  }
11337
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
+
11338
11974
  .Chat{
11339
11975
  position: relative;
11340
11976
  }
@@ -11378,6 +12014,14 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11378
12014
 
11379
12015
  }
11380
12016
 
12017
+ .ChatGitBranchPickerMessage{
12018
+ padding: 6px 8px;
12019
+ }
12020
+
12021
+ .ChatGitBranchPickerErrorMessage{
12022
+ color: var(--vscode-errorForeground, var(--vscode-foreground));
12023
+ }
12024
+
11381
12025
  .RunModePickerContainer{
11382
12026
  display: flex;
11383
12027
  justify-content: flex-end;
@@ -11482,13 +12126,10 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11482
12126
  .ChatFocus .ChatMessages > .Message > .ChatMessageContent{
11483
12127
  max-inline-size: 100%;
11484
12128
  }
11485
-
11486
-
11487
12129
  .Viewlet.Chat.ChatFocus{
11488
12130
  display: flex !important;
11489
12131
  min-width: 0;
11490
12132
  }
11491
-
11492
12133
  .ChatFocusMainArea{
11493
12134
  display: flex;
11494
12135
  flex: 1;
@@ -11500,7 +12141,6 @@ const getCss = (composerHeight, composerAttachmentsHeight, modelPickerHeight, li
11500
12141
  flex: 1;
11501
12142
  min-height: 0;
11502
12143
  }
11503
-
11504
12144
  `;
11505
12145
  return `${baseCss}
11506
12146
 
@@ -11645,7 +12285,8 @@ const HandlePointerUpModelPickerList = 55;
11645
12285
  const HandleClickReasoningEffortPickerToggle = 56;
11646
12286
  const HandleClickAgentModePickerToggle = 57;
11647
12287
  const HandleContextMenuChatImageAttachment = 58;
11648
- const HandleErrorComposerAttachmentPreviewOverlay = 59;
12288
+ const HandleClickGitBranchPickerToggle = 59;
12289
+ const HandleErrorComposerAttachmentPreviewOverlay = 60;
11649
12290
 
11650
12291
  const getAddContextButtonDom = () => {
11651
12292
  return [{
@@ -11660,10 +12301,12 @@ const getAddContextButtonDom = () => {
11660
12301
  }];
11661
12302
  };
11662
12303
 
11663
- const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = label) => {
12304
+ const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = label, ariaLabel = title) => {
11664
12305
  return [{
11665
12306
  'aria-expanded': open ? 'true' : 'false',
11666
12307
  'aria-haspopup': 'true',
12308
+ 'aria-label': ariaLabel,
12309
+ ariaLabel,
11667
12310
  childCount: 2,
11668
12311
  className: Select,
11669
12312
  inputType: 'button',
@@ -11686,12 +12329,12 @@ const getCustomSelectToggleVirtualDom = (label, name, open, onClick, title = lab
11686
12329
  }];
11687
12330
  };
11688
12331
 
11689
- const getCustomSelectPickerToggleVirtualDom = (label, name, open, onClick, title = label, containerChildCount = 1) => {
12332
+ const getCustomSelectPickerToggleVirtualDom = (label, name, open, onClick, title = label, ariaLabel = title, containerChildCount = 1) => {
11690
12333
  return [{
11691
12334
  childCount: containerChildCount,
11692
12335
  className: CustomSelectContainer,
11693
12336
  type: Div
11694
- }, ...getCustomSelectToggleVirtualDom(label, name, open, onClick, title)];
12337
+ }, ...getCustomSelectToggleVirtualDom(label, name, open, onClick, title, ariaLabel)];
11695
12338
  };
11696
12339
 
11697
12340
  const getAgentModePickerVirtualDom = (selectedAgentMode, agentModePickerOpen) => {
@@ -11701,7 +12344,8 @@ const getAgentModePickerVirtualDom = (selectedAgentMode, agentModePickerOpen) =>
11701
12344
  const getChatModelPickerToggleVirtualDom = (models, selectedModelId, modelPickerOpen) => {
11702
12345
  const selectedModel = models.find(model => model.id === selectedModelId);
11703
12346
  const selectedModelLabel = selectedModel ? selectedModel.name : selectedModelId;
11704
- return getCustomSelectPickerToggleVirtualDom(selectedModelLabel, ModelPickerToggle, modelPickerOpen, HandleClickModelPickerToggle);
12347
+ const pickModelLabel = pickModel(selectedModelLabel);
12348
+ return getCustomSelectPickerToggleVirtualDom(selectedModelLabel, ModelPickerToggle, modelPickerOpen, HandleClickModelPickerToggle, pickModelLabel, pickModelLabel);
11705
12349
  };
11706
12350
 
11707
12351
  const getCreatePullRequestButtonDom = () => {
@@ -11745,6 +12389,55 @@ const getCustomSelectOptionVirtualDom = (name, label, selected, detail = '') =>
11745
12389
  }, text(detail)] : [])];
11746
12390
  };
11747
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
+
11748
12441
  const reasoningEffortPickerHeight = reasoningEfforts.length * 28;
11749
12442
  const getReasoningEffortOptionsVirtualDom = selectedReasoningEffort => {
11750
12443
  return reasoningEfforts.flatMap(reasoningEffort => {
@@ -11754,7 +12447,7 @@ const getReasoningEffortOptionsVirtualDom = selectedReasoningEffort => {
11754
12447
  });
11755
12448
  };
11756
12449
  const getReasoningEffortPickerVirtualDom = (selectedReasoningEffort, reasoningEffortPickerOpen) => {
11757
- 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 ? [{
11758
12451
  childCount: 1,
11759
12452
  className: mergeClassNames(ChatModelPicker, CustomSelectPopOver),
11760
12453
  style: `height: ${reasoningEffortPickerHeight}px;`,
@@ -11921,6 +12614,7 @@ const getComposerAttachmentClassName = displayType => {
11921
12614
  };
11922
12615
  const getComposerAttachmentRemoveButtonDom = attachment => {
11923
12616
  return [{
12617
+ 'aria-label': removeAttachment(),
11924
12618
  buttonType: 'button',
11925
12619
  childCount: 1,
11926
12620
  className: ChatComposerAttachmentRemoveButton,
@@ -11992,12 +12686,13 @@ const getComposerTextAreaDom = () => {
11992
12686
  type: TextArea
11993
12687
  };
11994
12688
  };
11995
- 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) => {
11996
12690
  const isSendDisabled = composerValue.trim() === '';
11997
12691
  const showAgentModePicker = hasSpaceForAgentModePicker;
12692
+ const showGitBranchPicker = gitBranchPickerVisible || Boolean(fallbackBranchName);
11998
12693
  const showResponsiveRunModePicker = showRunMode && hasSpaceForRunModePicker;
11999
12694
  const showScrollDownButton = scrollDownButtonEnabled && !messagesAutoScrollEnabled;
12000
- 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);
12001
12696
  const primaryControlsCount = 1 + (showAgentModePicker ? 1 : 0) + (reasoningPickerEnabled ? 1 : 0) + (showResponsiveRunModePicker ? 1 : 0);
12002
12697
  const hasTodoList = todoListToolEnabled && todoListItems.length > 0;
12003
12698
  const hasComposerAttachments = composerAttachments.length > 0;
@@ -12021,7 +12716,62 @@ const getChatSendAreaDom = (composerValue, composerAttachments, agentMode, agent
12021
12716
  className: ChatSendAreaPrimaryControls,
12022
12717
  role: 'toolbar',
12023
12718
  type: Div
12024
- }, ...(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)];
12025
12775
  };
12026
12776
 
12027
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;';
@@ -12042,15 +12792,15 @@ const getFocusHeaderActionButtonDom = (label, name) => {
12042
12792
  type: Button$1
12043
12793
  }, text(label)];
12044
12794
  };
12045
- const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName) => {
12795
+ const getChatHeaderDomFocusMode = (selectedSessionTitle, selectedProjectName, authEnabled = false, userState = 'loggedOut', userName = '') => {
12046
12796
  const items = [[addAction(), FocusAddAction], [openInVsCode(), FocusOpenInVsCode], [commit(), FocusCommit], [openTerminal(), FocusOpenTerminal], [showDiff(), FocusShowDiff]];
12047
12797
  const hasProjectName = !!selectedProjectName;
12048
12798
  return [{
12049
- childCount: 2,
12799
+ childCount: 2 + (authEnabled ? 1 : 0),
12050
12800
  className: ChatFocusHeader,
12051
12801
  style: focusHeaderStyle,
12052
12802
  type: Header
12053
- }, {
12803
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
12054
12804
  childCount: hasProjectName ? 2 : 1,
12055
12805
  className: ChatName,
12056
12806
  style: focusHeaderMetaStyle,
@@ -12633,6 +13383,7 @@ const getMissingApiActionsDom = ({
12633
13383
  };
12634
13384
  const getMissingApiKeyDom = ({
12635
13385
  getApiKeyText,
13386
+ inputClassName,
12636
13387
  inputName,
12637
13388
  inputPattern,
12638
13389
  inputRequired = false,
@@ -12655,7 +13406,7 @@ const getMissingApiKeyDom = ({
12655
13406
  autocomplete: 'off',
12656
13407
  autocorrect: 'off',
12657
13408
  childCount: 0,
12658
- className: InputBox,
13409
+ className: inputClassName ? mergeClassNames(InputBox, inputClassName) : InputBox,
12659
13410
  name: inputName,
12660
13411
  onInput: HandleInput,
12661
13412
  ...(inputPattern ? {
@@ -12675,13 +13426,17 @@ const getMissingApiKeyDom = ({
12675
13426
  })];
12676
13427
  };
12677
13428
 
12678
- 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-.+') => {
12679
13430
  const isSaving = openApiApiKeyState === 'saving';
13431
+ const isInvalid = openApiApiKeyInput !== '' && !new RegExp(inputPattern).test(openApiApiKeyInput);
12680
13432
  return getMissingApiKeyDom({
12681
13433
  getApiKeyText: getOpenApiApiKey(),
13434
+ ...(isInvalid ? {
13435
+ inputClassName: InputInvalid
13436
+ } : {}),
12682
13437
  inputName: OpenApiApiKeyInput,
12683
13438
  inputPattern,
12684
- inputRequired: true,
13439
+ inputRequired: false,
12685
13440
  onSubmit: HandleMissingOpenAiApiKeyFormSubmit,
12686
13441
  openSettingsButtonName: OpenOpenApiApiKeyWebsite,
12687
13442
  openSettingsUrl,
@@ -13671,14 +14426,27 @@ const getChatAttachmentsDom = attachments => {
13671
14426
  }];
13672
14427
  })];
13673
14428
  };
13674
- 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) => {
13675
14442
  const roleClassName = message.role === 'user' ? MessageUser : MessageAssistant;
13676
14443
  const isOpenApiApiKeyMissingMessage = message.role === 'assistant' && message.text === openApiApiKeyRequiredMessage;
13677
14444
  const isOpenRouterApiKeyMissingMessage = message.role === 'assistant' && message.text === openRouterApiKeyRequiredMessage;
13678
14445
  const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
13679
14446
  const isOpenRouterTooManyRequestsMessage = message.role === 'assistant' && message.text.startsWith(openRouterTooManyRequestsMessage);
13680
- const messageDom = getMessageContentDom(parsedMessageContent, useChatMathWorker);
13681
- 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 ?? []) : [];
13682
14450
  const toolCallsDom = getToolCallsDom(message);
13683
14451
  const toolCallsChildCount = toolCallsDom.length > 0 ? 1 : 0;
13684
14452
  const messageDomChildCount = getTopLevelNodeCount(messageDom);
@@ -13690,9 +14458,9 @@ const getChatMessageDom = (message, parsedMessageContent, _openRouterApiKeyInput
13690
14458
  type: Div
13691
14459
  }, {
13692
14460
  childCount: extraChildCount,
13693
- className: ChatMessageContent,
14461
+ className: isStandaloneImageMessage ? mergeClassNames(ChatMessageContent, ChatImageMessageContent) : ChatMessageContent,
13694
14462
  type: Div
13695
- }, ...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() : [])];
13696
14464
  };
13697
14465
 
13698
14466
  const parentNode$1 = {
@@ -13708,6 +14476,22 @@ const getEmptyMessagesDom = () => {
13708
14476
  const hasMessageText = message => {
13709
14477
  return message.text.trim().length > 0;
13710
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
+ };
13711
14495
  const getDisplayMessages = (messages, parsedMessages) => {
13712
14496
  const displayMessages = [];
13713
14497
  for (const message of messages) {
@@ -13715,6 +14499,30 @@ const getDisplayMessages = (messages, parsedMessages) => {
13715
14499
  if (!parsedContent) {
13716
14500
  continue;
13717
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
+ }
13718
14526
  if (message.role !== 'assistant' || !message.toolCalls || message.toolCalls.length === 0) {
13719
14527
  displayMessages.push({
13720
14528
  message,
@@ -13770,7 +14578,7 @@ const getMessagesDom = (messages, parsedMessages, openRouterApiKeyInput, openApi
13770
14578
  role: 'log',
13771
14579
  scrollTop: messagesScrollTop,
13772
14580
  type: Div
13773
- }, ...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))];
13774
14582
  };
13775
14583
 
13776
14584
  const arrowLeft$1 = {
@@ -13884,7 +14692,6 @@ const getChatModeChatFocusVirtualDom = ({
13884
14692
  agentModePickerOpen = false,
13885
14693
  authEnabled = false,
13886
14694
  authErrorMessage = '',
13887
- authStatus = 'signed-out',
13888
14695
  composerAttachmentPreviewOverlayAttachmentId,
13889
14696
  composerAttachmentPreviewOverlayError = false,
13890
14697
  composerAttachments,
@@ -13895,6 +14702,10 @@ const getChatModeChatFocusVirtualDom = ({
13895
14702
  composerHeight = 28,
13896
14703
  composerLineHeight = 20,
13897
14704
  composerValue,
14705
+ gitBranches,
14706
+ gitBranchPickerErrorMessage,
14707
+ gitBranchPickerOpen,
14708
+ gitBranchPickerVisible,
13898
14709
  hasSpaceForAgentModePicker,
13899
14710
  hasSpaceForRunModePicker,
13900
14711
  messagesAutoScrollEnabled,
@@ -13929,14 +14740,15 @@ const getChatModeChatFocusVirtualDom = ({
13929
14740
  tokensUsed,
13930
14741
  usageOverviewEnabled,
13931
14742
  useChatMathWorker = false,
14743
+ userName = '',
14744
+ userState = 'loggedOut',
13932
14745
  visibleModels = models,
13933
14746
  voiceDictationEnabled = false
13934
14747
  }) => {
13935
14748
  const selectedSession = sessions.find(session => session.id === selectedSessionId);
13936
- const selectedProject = projects.find(project => project.id === selectedProjectId);
13937
- const messages = selectedSession ? selectedSession.messages : [];
13938
14749
  const selectedSessionTitle = selectedSession?.title || chatTitle();
13939
- const selectedProjectName = selectedProject?.name || '';
14750
+ const selectedProjectName = projects.find(project => project.id === selectedProjectId)?.name || '';
14751
+ const messages = selectedSession ? selectedSession.messages : [];
13940
14752
  const showCreatePullRequestButton = canCreatePullRequest(selectedSession);
13941
14753
  const isDropOverlayVisible = composerDropEnabled && composerDropActive;
13942
14754
  const isComposerAttachmentPreviewOverlayVisible = !!composerAttachmentPreviewOverlayAttachmentId;
@@ -13955,7 +14767,7 @@ const getChatModeChatFocusVirtualDom = ({
13955
14767
  childCount: 3,
13956
14768
  className: ChatFocusMainArea,
13957
14769
  type: Div
13958
- }, ...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({
13959
14771
  agentMode,
13960
14772
  agentModePickerVisible: isAgentModePickerVisible,
13961
14773
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14008,31 +14820,8 @@ const getHeaderActionVirtualDom = item => {
14008
14820
  }];
14009
14821
  };
14010
14822
 
14011
- const getAuthAction = (authEnabled, authStatus) => {
14012
- const isSigningIn = authStatus === 'signing-in';
14013
- if (!authEnabled) {
14014
- return undefined;
14015
- }
14016
- if (authStatus !== 'signed-in') {
14017
- return {
14018
- disabled: isSigningIn,
14019
- icon: mergeClassNames(MaskIcon, MaskIconAccount),
14020
- name: Login,
14021
- onClick: HandleClick,
14022
- title: isSigningIn ? loggingInToBackend() : loginToBackend()
14023
- };
14024
- }
14025
- return {
14026
- disabled: false,
14027
- icon: mergeClassNames(MaskIcon, MaskIconSignOut),
14028
- name: Logout,
14029
- onClick: HandleClick,
14030
- title: logoutFromBackend()
14031
- };
14032
- };
14033
- const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'signed-out', searchEnabled = false) => {
14823
+ const getChatHeaderActionsDom = (viewMode, searchEnabled = false) => {
14034
14824
  const toggleTitle = viewMode === 'chat-focus' ? normalChatMode() : chatFocusMode();
14035
- const authAction = getAuthAction(authEnabled, authStatus);
14036
14825
  const items = [{
14037
14826
  icon: mergeClassNames(MaskIcon, MaskIconLayoutPanelLeft),
14038
14827
  name: ToggleChatFocus,
@@ -14058,7 +14847,7 @@ const getChatHeaderActionsDom = (viewMode, authEnabled = false, authStatus = 'si
14058
14847
  name: Settings,
14059
14848
  onClick: HandleClickSettings,
14060
14849
  title: settings()
14061
- }, ...(authAction ? [authAction] : []), {
14850
+ }, {
14062
14851
  icon: mergeClassNames(MaskIcon, MaskIconClose),
14063
14852
  name: CloseChat,
14064
14853
  onClick: HandleClickClose,
@@ -14083,14 +14872,14 @@ const getAuthErrorDom = (hasAuthError, authErrorMessage) => {
14083
14872
  type: Span
14084
14873
  }, text(authErrorMessage)];
14085
14874
  };
14086
- const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, authStatus = 'signed-out', authErrorMessage = '') => {
14875
+ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, userState = 'loggedOut', userName = '', authErrorMessage = '') => {
14087
14876
  const hasAuthError = authEnabled && !!authErrorMessage;
14088
14877
  return [{
14089
- childCount: hasAuthError ? 3 : 2,
14878
+ childCount: 2 + (authEnabled ? 1 : 0) + (hasAuthError ? 1 : 0),
14090
14879
  className: ChatHeader,
14091
14880
  onContextMenu: HandleChatHeaderContextMenu,
14092
14881
  type: Header
14093
- }, {
14882
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
14094
14883
  childCount: 2,
14095
14884
  className: ChatName,
14096
14885
  type: Div
@@ -14098,7 +14887,7 @@ const getChatHeaderDomDetailMode = (selectedSessionTitle, authEnabled = false, a
14098
14887
  childCount: 1,
14099
14888
  className: ChatHeaderLabel,
14100
14889
  type: H2
14101
- }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail', authEnabled, authStatus), ...getAuthErrorDom(hasAuthError, authErrorMessage)];
14890
+ }, text(selectedSessionTitle), ...getChatHeaderActionsDom('detail'), ...getAuthErrorDom(hasAuthError, authErrorMessage)];
14102
14891
  };
14103
14892
 
14104
14893
  const getChatModeDetailVirtualDom = ({
@@ -14107,7 +14896,6 @@ const getChatModeDetailVirtualDom = ({
14107
14896
  agentModePickerOpen = false,
14108
14897
  authEnabled = false,
14109
14898
  authErrorMessage = '',
14110
- authStatus = 'signed-out',
14111
14899
  composerAttachmentPreviewOverlayAttachmentId,
14112
14900
  composerAttachmentPreviewOverlayError = false,
14113
14901
  composerAttachments,
@@ -14148,6 +14936,8 @@ const getChatModeDetailVirtualDom = ({
14148
14936
  tokensUsed,
14149
14937
  usageOverviewEnabled,
14150
14938
  useChatMathWorker = false,
14939
+ userName = '',
14940
+ userState = 'loggedOut',
14151
14941
  visibleModels = models,
14152
14942
  voiceDictationEnabled = false
14153
14943
  }) => {
@@ -14168,7 +14958,7 @@ const getChatModeDetailVirtualDom = ({
14168
14958
  onDragEnter: HandleDragEnterChatView,
14169
14959
  onDragOver: HandleDragOverChatView,
14170
14960
  type: Div
14171
- }, ...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({
14172
14962
  agentMode,
14173
14963
  agentModePickerVisible: isAgentModePickerVisible,
14174
14964
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14204,20 +14994,20 @@ const getChatSearchDom = (hasSearchField, searchValue) => {
14204
14994
  value: searchValue
14205
14995
  }];
14206
14996
  };
14207
- 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 = '') => {
14208
14998
  const hasAuthError = authEnabled && !!authErrorMessage;
14209
14999
  const hasSearchField = searchEnabled && searchFieldVisible;
14210
- const headerChildCount = 2 + (hasAuthError ? 1 : 0) + (hasSearchField ? 1 : 0);
15000
+ const headerChildCount = 2 + (authEnabled ? 1 : 0) + (hasAuthError ? 1 : 0) + (hasSearchField ? 1 : 0);
14211
15001
  return [{
14212
15002
  childCount: headerChildCount,
14213
15003
  className: ChatHeader,
14214
15004
  onContextMenu: HandleChatHeaderContextMenu,
14215
15005
  type: Header
14216
- }, {
15006
+ }, ...getChatHeaderAuthDom(authEnabled, userState, userName), {
14217
15007
  childCount: 1,
14218
15008
  className: ChatHeaderLabel,
14219
15009
  type: H2
14220
- }, text(chats()), ...getChatHeaderActionsDom('list', authEnabled, authStatus, searchEnabled), ...getChatSearchDom(hasSearchField, searchValue), ...(hasAuthError ? [{
15010
+ }, text(chats()), ...getChatHeaderActionsDom('list', searchEnabled), ...getChatSearchDom(hasSearchField, searchValue), ...(hasAuthError ? [{
14221
15011
  childCount: 1,
14222
15012
  className: ChatAuthError,
14223
15013
  type: Span
@@ -14317,7 +15107,6 @@ const getChatModeListVirtualDom = ({
14317
15107
  agentModePickerOpen = false,
14318
15108
  authEnabled = false,
14319
15109
  authErrorMessage = '',
14320
- authStatus = 'signed-out',
14321
15110
  chatListScrollTop = 0,
14322
15111
  composerAttachmentPreviewOverlayAttachmentId,
14323
15112
  composerAttachmentPreviewOverlayError = false,
@@ -14352,6 +15141,8 @@ const getChatModeListVirtualDom = ({
14352
15141
  tokensMax,
14353
15142
  tokensUsed,
14354
15143
  usageOverviewEnabled,
15144
+ userName = '',
15145
+ userState = 'loggedOut',
14355
15146
  visibleModels = models,
14356
15147
  voiceDictationEnabled = false
14357
15148
  }) => {
@@ -14370,7 +15161,7 @@ const getChatModeListVirtualDom = ({
14370
15161
  onDragEnter: HandleDragEnterChatView,
14371
15162
  onDragOver: HandleDragOverChatView,
14372
15163
  type: Div
14373
- }, ...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({
14374
15165
  agentMode,
14375
15166
  agentModePickerVisible: isAgentModePickerVisible,
14376
15167
  composerAttachmentPreviewOverlayAttachmentId,
@@ -14473,7 +15264,6 @@ const getChatVirtualDom = options => {
14473
15264
  agentModePickerOpen = false,
14474
15265
  authEnabled = false,
14475
15266
  authErrorMessage = '',
14476
- authStatus = 'signed-out',
14477
15267
  chatListScrollTop,
14478
15268
  composerAttachmentPreviewOverlayAttachmentId,
14479
15269
  composerAttachmentPreviewOverlayError = false,
@@ -14485,6 +15275,10 @@ const getChatVirtualDom = options => {
14485
15275
  composerHeight,
14486
15276
  composerLineHeight,
14487
15277
  composerValue,
15278
+ gitBranches = [],
15279
+ gitBranchPickerErrorMessage = '',
15280
+ gitBranchPickerOpen = false,
15281
+ gitBranchPickerVisible = false,
14488
15282
  hasSpaceForAgentModePicker,
14489
15283
  hasSpaceForRunModePicker,
14490
15284
  listFocusedIndex = -1,
@@ -14522,6 +15316,8 @@ const getChatVirtualDom = options => {
14522
15316
  tokensUsed,
14523
15317
  usageOverviewEnabled,
14524
15318
  useChatMathWorker = false,
15319
+ userName = '',
15320
+ userState = 'loggedOut',
14525
15321
  viewMode,
14526
15322
  visibleModels = models,
14527
15323
  voiceDictationEnabled = false
@@ -14536,7 +15332,6 @@ const getChatVirtualDom = options => {
14536
15332
  agentModePickerOpen,
14537
15333
  authEnabled,
14538
15334
  authErrorMessage,
14539
- authStatus,
14540
15335
  composerAttachmentPreviewOverlayAttachmentId,
14541
15336
  composerAttachmentPreviewOverlayError,
14542
15337
  composerAttachments,
@@ -14547,6 +15342,10 @@ const getChatVirtualDom = options => {
14547
15342
  composerHeight,
14548
15343
  composerLineHeight,
14549
15344
  composerValue,
15345
+ gitBranches,
15346
+ gitBranchPickerErrorMessage,
15347
+ gitBranchPickerOpen,
15348
+ gitBranchPickerVisible,
14550
15349
  hasSpaceForAgentModePicker,
14551
15350
  hasSpaceForRunModePicker,
14552
15351
  messagesAutoScrollEnabled,
@@ -14581,6 +15380,8 @@ const getChatVirtualDom = options => {
14581
15380
  tokensUsed,
14582
15381
  usageOverviewEnabled,
14583
15382
  useChatMathWorker,
15383
+ userName,
15384
+ userState,
14584
15385
  visibleModels,
14585
15386
  voiceDictationEnabled
14586
15387
  });
@@ -14591,7 +15392,6 @@ const getChatVirtualDom = options => {
14591
15392
  agentModePickerOpen,
14592
15393
  authEnabled,
14593
15394
  authErrorMessage,
14594
- authStatus,
14595
15395
  composerAttachmentPreviewOverlayAttachmentId,
14596
15396
  composerAttachmentPreviewOverlayError,
14597
15397
  composerAttachments,
@@ -14632,6 +15432,8 @@ const getChatVirtualDom = options => {
14632
15432
  tokensUsed,
14633
15433
  usageOverviewEnabled,
14634
15434
  useChatMathWorker,
15435
+ userName,
15436
+ userState,
14635
15437
  visibleModels,
14636
15438
  voiceDictationEnabled
14637
15439
  });
@@ -14642,7 +15444,6 @@ const getChatVirtualDom = options => {
14642
15444
  agentModePickerOpen,
14643
15445
  authEnabled,
14644
15446
  authErrorMessage,
14645
- authStatus,
14646
15447
  chatListScrollTop,
14647
15448
  composerAttachmentPreviewOverlayAttachmentId,
14648
15449
  composerAttachmentPreviewOverlayError,
@@ -14677,6 +15478,8 @@ const getChatVirtualDom = options => {
14677
15478
  tokensMax,
14678
15479
  tokensUsed,
14679
15480
  usageOverviewEnabled,
15481
+ userName,
15482
+ userState,
14680
15483
  visibleModels,
14681
15484
  voiceDictationEnabled
14682
15485
  });
@@ -14692,7 +15495,6 @@ const renderItems = (oldState, newState) => {
14692
15495
  agentModePickerOpen,
14693
15496
  authEnabled,
14694
15497
  authErrorMessage,
14695
- authStatus,
14696
15498
  chatListScrollTop,
14697
15499
  composerAttachmentPreviewOverlayAttachmentId,
14698
15500
  composerAttachmentPreviewOverlayError,
@@ -14704,6 +15506,10 @@ const renderItems = (oldState, newState) => {
14704
15506
  composerHeight,
14705
15507
  composerLineHeight,
14706
15508
  composerValue,
15509
+ gitBranches,
15510
+ gitBranchPickerErrorMessage,
15511
+ gitBranchPickerOpen,
15512
+ gitBranchPickerVisible,
14707
15513
  hasSpaceForAgentModePicker,
14708
15514
  hasSpaceForRunModePicker,
14709
15515
  initial,
@@ -14743,6 +15549,8 @@ const renderItems = (oldState, newState) => {
14743
15549
  uid,
14744
15550
  usageOverviewEnabled,
14745
15551
  useChatMathWorker,
15552
+ userName,
15553
+ userState,
14746
15554
  viewMode,
14747
15555
  visibleModels,
14748
15556
  voiceDictationEnabled
@@ -14756,7 +15564,6 @@ const renderItems = (oldState, newState) => {
14756
15564
  agentModePickerOpen,
14757
15565
  authEnabled,
14758
15566
  authErrorMessage,
14759
- authStatus,
14760
15567
  chatListScrollTop,
14761
15568
  composerAttachmentPreviewOverlayAttachmentId,
14762
15569
  composerAttachmentPreviewOverlayError,
@@ -14768,6 +15575,10 @@ const renderItems = (oldState, newState) => {
14768
15575
  composerHeight,
14769
15576
  composerLineHeight,
14770
15577
  composerValue,
15578
+ gitBranches,
15579
+ gitBranchPickerErrorMessage,
15580
+ gitBranchPickerOpen,
15581
+ gitBranchPickerVisible,
14771
15582
  hasSpaceForAgentModePicker,
14772
15583
  hasSpaceForRunModePicker,
14773
15584
  listFocusedIndex,
@@ -14805,6 +15616,8 @@ const renderItems = (oldState, newState) => {
14805
15616
  tokensUsed,
14806
15617
  usageOverviewEnabled,
14807
15618
  useChatMathWorker,
15619
+ userName,
15620
+ userState,
14808
15621
  viewMode,
14809
15622
  visibleModels,
14810
15623
  voiceDictationEnabled
@@ -14851,8 +15664,16 @@ const renderSelection = (oldState, newState) => {
14851
15664
  const renderValue = (oldState, newState) => {
14852
15665
  const {
14853
15666
  composerValue,
15667
+ openApiApiKeyInput,
15668
+ openRouterApiKeyInput,
14854
15669
  uid
14855
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
+ }
14856
15677
  return [SetValueByName, uid, Composer, composerValue];
14857
15678
  };
14858
15679
 
@@ -14946,6 +15767,9 @@ const renderEventListeners = () => {
14946
15767
  }, {
14947
15768
  name: HandleClickAgentModePickerToggle,
14948
15769
  params: ['openAgentModePicker']
15770
+ }, {
15771
+ name: HandleClickGitBranchPickerToggle,
15772
+ params: ['handleClickGitBranchPickerToggle']
14949
15773
  }, {
14950
15774
  name: HandleClickModelPickerOverlay,
14951
15775
  params: ['handleClickModelPickerOverlay', DefaultPrevented]
@@ -15104,6 +15928,10 @@ const reset = async state => {
15104
15928
  composerSelectionEnd: 0,
15105
15929
  composerSelectionStart: 0,
15106
15930
  composerValue: '',
15931
+ gitBranches: [],
15932
+ gitBranchPickerErrorMessage: '',
15933
+ gitBranchPickerOpen: false,
15934
+ gitBranchPickerVisible: false,
15107
15935
  mockAiResponseDelay: 0,
15108
15936
  modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, state.models.length),
15109
15937
  modelPickerListScrollTop: 0,
@@ -15183,7 +16011,6 @@ const saveState = state => {
15183
16011
  selectedModelId,
15184
16012
  selectedProjectId,
15185
16013
  selectedSessionId,
15186
- systemPrompt,
15187
16014
  viewMode
15188
16015
  } = state;
15189
16016
  return {
@@ -15206,7 +16033,6 @@ const saveState = state => {
15206
16033
  selectedModelId,
15207
16034
  selectedProjectId,
15208
16035
  selectedSessionId,
15209
- systemPrompt,
15210
16036
  viewMode
15211
16037
  };
15212
16038
  };
@@ -15221,7 +16047,13 @@ const setAddContextButtonEnabled = (state, addContextButtonEnabled) => {
15221
16047
  const setAuthEnabled = (state, authEnabled) => {
15222
16048
  return {
15223
16049
  ...state,
15224
- 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
15225
16057
  };
15226
16058
  };
15227
16059
 
@@ -15415,6 +16247,7 @@ const commandMap = {
15415
16247
  'Chat.chatListFocusNext': wrapCommand(chatListFocusNext),
15416
16248
  'Chat.chatListFocusPrevious': wrapCommand(chatListFocusPrevious),
15417
16249
  'Chat.clearInput': wrapCommand(clearInput),
16250
+ 'Chat.closeGitBranchPicker': wrapCommand(closeGitBranchPicker),
15418
16251
  'Chat.copyInput': wrapCommand(copyInput),
15419
16252
  'Chat.create': create,
15420
16253
  'Chat.cutInput': wrapCommand(cutInput),
@@ -15443,6 +16276,7 @@ const commandMap = {
15443
16276
  'Chat.handleClickDelete': wrapCommand(handleClickDelete),
15444
16277
  'Chat.handleClickDictationButton': wrapCommand(handleClickDictationButton),
15445
16278
  'Chat.handleClickFileName': wrapCommand(handleClickFileName),
16279
+ 'Chat.handleClickGitBranchPickerToggle': wrapCommand(handleClickGitBranchPickerToggle),
15446
16280
  'Chat.handleClickList': wrapCommand(handleClickList),
15447
16281
  'Chat.handleClickModelPickerList': wrapCommand(handleClickModelPickerList),
15448
16282
  'Chat.handleClickModelPickerListIndex': wrapCommand(handleClickModelPickerListIndex),
@@ -15494,6 +16328,7 @@ const commandMap = {
15494
16328
  'Chat.mockOpenApiStreamPushChunk': wrapCommand(mockOpenApiStreamPushChunk),
15495
16329
  'Chat.mockOpenApiStreamReset': wrapCommand(mockOpenApiStreamReset),
15496
16330
  'Chat.openAgentModePicker': wrapCommand(openAgentModePicker),
16331
+ 'Chat.openGitBranchPicker': wrapCommand(openGitBranchPicker),
15497
16332
  'Chat.openMockProject': wrapCommand(openMockProject),
15498
16333
  'Chat.openMockSession': wrapCommand(openMockSession),
15499
16334
  'Chat.openModelPicker': wrapCommand(openModelPicker),