gitxelectron 1.1.7 → 1.1.8

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.
@@ -13819,14 +13819,19 @@ function ContributorsModal({ onClose, remoteUrl }) {
13819
13819
  const [removingId, setRemovingId] = reactExports.useState(null);
13820
13820
  const [suggestions, setSuggestions] = reactExports.useState([]);
13821
13821
  const [showSuggestions, setShowSuggestions] = reactExports.useState(false);
13822
- const [debounceTimeout, setDebounceTimeout] = reactExports.useState(null);
13822
+ const debounceTimeoutRef = reactExports.useRef(null);
13823
13823
  const fetchData = async () => {
13824
+ if (!remoteUrl) {
13825
+ setLoading(false);
13826
+ setErrorMsg("No remote GitHub repository URL detected for this folder. Please push or add a remote origin first to invite collaborators.");
13827
+ return;
13828
+ }
13824
13829
  try {
13825
13830
  setLoading(true);
13826
13831
  setErrorMsg("");
13827
13832
  const data = await window.api.getCollaborators(remoteUrl);
13828
- setCollaborators(data.collaborators);
13829
- setInvitations(data.invitations);
13833
+ setCollaborators(data.collaborators || []);
13834
+ setInvitations(data.invitations || []);
13830
13835
  setContributors(data.contributors || []);
13831
13836
  } catch (e) {
13832
13837
  setErrorMsg(e.message || "Failed to load collaborators");
@@ -13836,24 +13841,50 @@ function ContributorsModal({ onClose, remoteUrl }) {
13836
13841
  };
13837
13842
  reactExports.useEffect(() => {
13838
13843
  fetchData();
13844
+ return () => {
13845
+ if (debounceTimeoutRef.current) clearTimeout(debounceTimeoutRef.current);
13846
+ };
13839
13847
  }, []);
13848
+ const handleSelectUser = async (user) => {
13849
+ setNewUsername(user.login);
13850
+ setShowSuggestions(false);
13851
+ setIsSearching(true);
13852
+ setErrorMsg("");
13853
+ try {
13854
+ const fullUser = await window.api.searchGitHubUser(user.login);
13855
+ const targetUser = fullUser || user;
13856
+ const isExisting = collaborators.some((c) => c.login.toLowerCase() === targetUser.login.toLowerCase()) || invitations.some((i) => i.invitee && i.invitee.login.toLowerCase() === targetUser.login.toLowerCase());
13857
+ if (isExisting) {
13858
+ setErrorMsg(`${targetUser.login} is already a collaborator or has a pending invitation.`);
13859
+ setSearchResult(null);
13860
+ } else {
13861
+ setSearchResult(targetUser);
13862
+ }
13863
+ } catch (e) {
13864
+ setErrorMsg(e.message || "Error verifying GitHub account.");
13865
+ } finally {
13866
+ setIsSearching(false);
13867
+ }
13868
+ };
13840
13869
  const handleSearch = async (e) => {
13841
13870
  e.preventDefault();
13842
- if (!newUsername.trim()) return;
13871
+ const query = newUsername.trim();
13872
+ if (!query) return;
13843
13873
  try {
13844
13874
  setIsSearching(true);
13845
13875
  setErrorMsg("");
13846
13876
  setSearchResult(null);
13847
- const user = await window.api.searchGitHubUser(newUsername.trim());
13877
+ setShowSuggestions(false);
13878
+ const user = await window.api.searchGitHubUser(query);
13848
13879
  if (user) {
13849
- const isExisting = collaborators.some((c) => c.login.toLowerCase() === user.login.toLowerCase()) || invitations.some((i) => i.invitee.login.toLowerCase() === user.login.toLowerCase());
13880
+ const isExisting = collaborators.some((c) => c.login.toLowerCase() === user.login.toLowerCase()) || invitations.some((i) => i.invitee && i.invitee.login.toLowerCase() === user.login.toLowerCase());
13850
13881
  if (isExisting) {
13851
13882
  setErrorMsg(`${user.login} is already a collaborator or has a pending invitation.`);
13852
13883
  } else {
13853
13884
  setSearchResult(user);
13854
13885
  }
13855
13886
  } else {
13856
- setErrorMsg("User not found on GitHub.");
13887
+ setErrorMsg(`User '${query}' was not found in GitHub's database.`);
13857
13888
  }
13858
13889
  } catch (e2) {
13859
13890
  setErrorMsg(e2.message || "Error searching user.");
@@ -13861,6 +13892,37 @@ function ContributorsModal({ onClose, remoteUrl }) {
13861
13892
  setIsSearching(false);
13862
13893
  }
13863
13894
  };
13895
+ const handleInputChange = (val) => {
13896
+ setNewUsername(val);
13897
+ setSearchResult(null);
13898
+ setErrorMsg("");
13899
+ if (debounceTimeoutRef.current) {
13900
+ clearTimeout(debounceTimeoutRef.current);
13901
+ }
13902
+ const trimmed = val.trim();
13903
+ if (trimmed) {
13904
+ setShowSuggestions(true);
13905
+ setIsSearching(true);
13906
+ debounceTimeoutRef.current = setTimeout(async () => {
13907
+ try {
13908
+ const users = await window.api.searchGitHubUsers(trimmed);
13909
+ const existingLogins = /* @__PURE__ */ new Set([
13910
+ ...collaborators.map((c) => c.login.toLowerCase()),
13911
+ ...invitations.map((i) => i.invitee ? i.invitee.login.toLowerCase() : "")
13912
+ ]);
13913
+ setSuggestions(users.filter((u) => !existingLogins.has(u.login.toLowerCase())));
13914
+ } catch (err) {
13915
+ console.error("Failed to query GitHub users", err);
13916
+ } finally {
13917
+ setIsSearching(false);
13918
+ }
13919
+ }, 300);
13920
+ } else {
13921
+ setSuggestions([]);
13922
+ setIsSearching(false);
13923
+ setShowSuggestions(false);
13924
+ }
13925
+ };
13864
13926
  const handleAdd = async () => {
13865
13927
  if (!searchResult) return;
13866
13928
  try {
@@ -13913,7 +13975,7 @@ function ContributorsModal({ onClose, remoteUrl }) {
13913
13975
  )
13914
13976
  }
13915
13977
  ),
13916
- "Manage Collaborators"
13978
+ "Collaboration & Contributors"
13917
13979
  ] }),
13918
13980
  /* @__PURE__ */ jsxRuntimeExports.jsx(
13919
13981
  "button",
@@ -13955,7 +14017,10 @@ function ContributorsModal({ onClose, remoteUrl }) {
13955
14017
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: errorMsg })
13956
14018
  ] }),
13957
14019
  /* @__PURE__ */ jsxRuntimeExports.jsxs("form", { onSubmit: handleSearch, className: "mb-4", children: [
13958
- /* @__PURE__ */ jsxRuntimeExports.jsx("label", { className: "block text-sm font-medium text-secondary mb-2", children: "Invite Collaborator" }),
14020
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("label", { className: "block text-sm font-medium text-secondary mb-2 flex items-center justify-between", children: [
14021
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Invite Collaborator" }),
14022
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[11px] text-brand/80 font-normal", children: "Searches worldwide GitHub users" })
14023
+ ] }),
13959
14024
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2", children: [
13960
14025
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative flex-1", children: [
13961
14026
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -13981,62 +14046,53 @@ function ContributorsModal({ onClose, remoteUrl }) {
13981
14046
  {
13982
14047
  type: "text",
13983
14048
  value: newUsername,
13984
- onChange: (e) => {
13985
- const val = e.target.value;
13986
- setNewUsername(val);
13987
- setSearchResult(null);
13988
- setShowSuggestions(true);
13989
- if (debounceTimeout) clearTimeout(debounceTimeout);
13990
- if (val.trim()) {
13991
- setIsSearching(true);
13992
- setDebounceTimeout(
13993
- setTimeout(async () => {
13994
- try {
13995
- setErrorMsg("");
13996
- const users = await window.api.searchGitHubUsers(val.trim());
13997
- const existingLogins = /* @__PURE__ */ new Set([
13998
- ...collaborators.map((c) => c.login.toLowerCase()),
13999
- ...invitations.map((i) => i.invitee.login.toLowerCase())
14000
- ]);
14001
- setSuggestions(users.filter((u) => !existingLogins.has(u.login.toLowerCase())));
14002
- } catch (err) {
14003
- console.error(err);
14004
- } finally {
14005
- setIsSearching(false);
14006
- }
14007
- }, 400)
14008
- );
14009
- } else {
14010
- setSuggestions([]);
14011
- setIsSearching(false);
14012
- setShowSuggestions(false);
14013
- }
14014
- },
14015
- onBlur: () => setTimeout(() => setShowSuggestions(false), 200),
14049
+ onChange: (e) => handleInputChange(e.target.value),
14050
+ onBlur: () => setTimeout(() => setShowSuggestions(false), 250),
14016
14051
  onFocus: () => {
14017
14052
  if (suggestions.length > 0) setShowSuggestions(true);
14018
14053
  },
14019
- placeholder: "GitHub Username",
14054
+ placeholder: "Type GitHub username...",
14020
14055
  className: "w-full bg-surface border border-border rounded-lg pl-9 pr-3 py-2 text-sm text-primary placeholder-secondary focus:outline-none focus:border-brand focus:ring-1 focus:ring-brand transition-all",
14021
14056
  disabled: adding || loading
14022
14057
  }
14023
14058
  ),
14024
- showSuggestions && suggestions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute top-full left-0 right-0 mt-1 bg-surface border border-border rounded-lg shadow-xl overflow-hidden z-10 max-h-60 overflow-y-auto", children: suggestions.map((user) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
14025
- "div",
14026
- {
14027
- onClick: () => {
14028
- setNewUsername(user.login);
14029
- setSearchResult(user);
14030
- setShowSuggestions(false);
14059
+ showSuggestions && suggestions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute top-full left-0 right-0 mt-1 bg-surface border border-border rounded-lg shadow-2xl overflow-hidden z-20 max-h-64 overflow-y-auto custom-scrollbar", children: [
14060
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-3 py-1.5 bg-canvas/80 text-[10px] uppercase font-bold text-secondary tracking-wider border-b border-border flex items-center justify-between", children: [
14061
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Worldwide GitHub Accounts" }),
14062
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
14063
+ suggestions.length,
14064
+ " found"
14065
+ ] })
14066
+ ] }),
14067
+ suggestions.map((user) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
14068
+ "div",
14069
+ {
14070
+ onMouseDown: (e) => {
14071
+ e.preventDefault();
14072
+ handleSelectUser(user);
14073
+ },
14074
+ className: "px-3 py-2.5 flex items-center justify-between hover:bg-brand/10 cursor-pointer transition-colors border-b border-border/50 last:border-b-0 group",
14075
+ children: [
14076
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2.5", children: [
14077
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
14078
+ "img",
14079
+ {
14080
+ src: user.avatar_url,
14081
+ alt: user.login,
14082
+ className: "w-7 h-7 rounded-full bg-canvas object-cover border border-border"
14083
+ }
14084
+ ),
14085
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col", children: [
14086
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-primary font-medium group-hover:text-brand transition-colors", children: user.login }),
14087
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-secondary", children: "Real GitHub Account" })
14088
+ ] })
14089
+ ] }),
14090
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-brand opacity-0 group-hover:opacity-100 transition-opacity font-medium", children: "Select →" })
14091
+ ]
14031
14092
  },
14032
- className: "px-3 py-2 flex items-center gap-3 hover:bg-surface-hover cursor-pointer transition-colors border-b border-border last:border-b-0",
14033
- children: [
14034
- /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: user.avatar_url, alt: user.login, className: "w-6 h-6 rounded-full" }),
14035
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm text-primary font-medium", children: user.login })
14036
- ]
14037
- },
14038
- user.id
14039
- )) })
14093
+ user.id
14094
+ ))
14095
+ ] })
14040
14096
  ] }),
14041
14097
  /* @__PURE__ */ jsxRuntimeExports.jsx(
14042
14098
  "button",
@@ -14044,7 +14100,7 @@ function ContributorsModal({ onClose, remoteUrl }) {
14044
14100
  type: "submit",
14045
14101
  disabled: !newUsername.trim() || adding || loading || isSearching,
14046
14102
  className: "bg-surface-hover hover:brightness-110 text-primary border border-border px-4 py-2 rounded-lg text-sm font-medium transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2",
14047
- children: isSearching ? /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { className: "w-4 h-4 animate-spin", fill: "none", viewBox: "0 0 24 24", children: [
14103
+ children: isSearching ? /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { className: "w-4 h-4 animate-spin text-brand", fill: "none", viewBox: "0 0 24 24", children: [
14048
14104
  /* @__PURE__ */ jsxRuntimeExports.jsx(
14049
14105
  "circle",
14050
14106
  {
@@ -14069,48 +14125,42 @@ function ContributorsModal({ onClose, remoteUrl }) {
14069
14125
  )
14070
14126
  ] })
14071
14127
  ] }),
14072
- searchResult && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6 p-4 bg-surface border border-brand/30 rounded-lg flex items-center justify-between shadow-sm animate-in fade-in zoom-in-95 duration-200", children: [
14128
+ searchResult && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-6 p-4 bg-surface/90 border border-brand/40 rounded-xl flex items-center justify-between shadow-lg animate-in fade-in zoom-in-95 duration-200", children: [
14073
14129
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
14074
14130
  /* @__PURE__ */ jsxRuntimeExports.jsx(
14075
14131
  "img",
14076
14132
  {
14077
14133
  src: searchResult.avatar_url,
14078
14134
  alt: searchResult.login,
14079
- className: "w-10 h-10 rounded-full bg-canvas object-cover"
14135
+ className: "w-12 h-12 rounded-full bg-canvas object-cover border-2 border-brand/40"
14080
14136
  }
14081
14137
  ),
14082
14138
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
14083
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-bold text-primary", children: searchResult.login }),
14084
- searchResult.name && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-secondary", children: searchResult.name })
14139
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
14140
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm font-bold text-primary", children: searchResult.login }),
14141
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-emerald-500/20 text-emerald-400 border border-emerald-500/30 px-1.5 py-0.5 rounded font-medium flex items-center gap-1", children: "✓ Real GitHub User" })
14142
+ ] }),
14143
+ searchResult.name && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-secondary font-medium", children: searchResult.name }),
14144
+ searchResult.bio && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-[11px] text-secondary/80 line-clamp-1 max-w-xs", children: searchResult.bio })
14085
14145
  ] })
14086
14146
  ] }),
14087
14147
  /* @__PURE__ */ jsxRuntimeExports.jsx(
14088
14148
  "button",
14089
14149
  {
14150
+ type: "button",
14090
14151
  onClick: handleAdd,
14091
14152
  disabled: adding,
14092
- className: "bg-brand hover:bg-brand-hover text-white px-4 py-2 rounded-lg text-sm font-medium transition-all disabled:opacity-50 flex items-center gap-2 shadow-sm",
14093
- children: adding ? /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { className: "w-4 h-4 animate-spin", fill: "none", viewBox: "0 0 24 24", children: [
14094
- /* @__PURE__ */ jsxRuntimeExports.jsx(
14095
- "circle",
14096
- {
14097
- className: "opacity-25",
14098
- cx: "12",
14099
- cy: "12",
14100
- r: "10",
14101
- stroke: "currentColor",
14102
- strokeWidth: "4"
14103
- }
14104
- ),
14105
- /* @__PURE__ */ jsxRuntimeExports.jsx(
14106
- "path",
14107
- {
14108
- className: "opacity-75",
14109
- fill: "currentColor",
14110
- d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
14111
- }
14112
- )
14113
- ] }) : "Invite User"
14153
+ className: "bg-brand hover:bg-brand-hover text-white px-4 py-2 rounded-lg text-sm font-semibold transition-all disabled:opacity-50 flex items-center gap-2 shadow-md hover:shadow-brand/20",
14154
+ children: adding ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
14155
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { className: "w-4 h-4 animate-spin", fill: "none", viewBox: "0 0 24 24", children: [
14156
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
14157
+ /* @__PURE__ */ jsxRuntimeExports.jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
14158
+ ] }),
14159
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Inviting..." })
14160
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
14161
+ /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M18 9v3m0 0v3m0-3h3m-3 0h-3m-2-5a4 4 0 11-8 0 4 4 0 018 0zM3 20a6 6 0 0112 0v1H3v-1z" }) }),
14162
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Invite" })
14163
+ ] })
14114
14164
  }
14115
14165
  )
14116
14166
  ] }),
@@ -23900,6 +23950,9 @@ const THEMES = [
23900
23950
  ];
23901
23951
  function App() {
23902
23952
  const [repoPath, setRepoPath] = reactExports.useState(null);
23953
+ const [collaboratedRepos, setCollaboratedRepos] = reactExports.useState([]);
23954
+ const [showCollabActionModal, setShowCollabActionModal] = reactExports.useState(null);
23955
+ const [isCloning, setIsCloning] = reactExports.useState(false);
23903
23956
  reactExports.useEffect(() => {
23904
23957
  const playSusSound = () => {
23905
23958
  const AudioContextClass = window.AudioContext || window.webkitAudioContext;
@@ -24063,6 +24116,16 @@ function App() {
24063
24116
  };
24064
24117
  const [theme, setTheme] = reactExports.useState(localStorage.getItem("gitelectron-theme") || "github-dark");
24065
24118
  const [changesTab, setChangesTab] = reactExports.useState("changes");
24119
+ const [collabSearch, setCollabSearch] = reactExports.useState("");
24120
+ const [collabMappings, setCollabMappings] = reactExports.useState(() => {
24121
+ try {
24122
+ return JSON.parse(localStorage.getItem("gitelectron_collab_mappings") || "{}");
24123
+ } catch {
24124
+ return {};
24125
+ }
24126
+ });
24127
+ const [pathExistenceMap, setPathExistenceMap] = reactExports.useState({});
24128
+ const [isUploading, setIsUploading] = reactExports.useState(false);
24066
24129
  const [showSetupWizard, setShowSetupWizard] = reactExports.useState(false);
24067
24130
  const [pushAllBranches, setPushAllBranches] = reactExports.useState(false);
24068
24131
  const [showUniversalWizard, setShowUniversalWizard] = reactExports.useState(false);
@@ -24088,7 +24151,41 @@ function App() {
24088
24151
  setToasts((prev) => prev.filter((t2) => t2.id !== id2));
24089
24152
  }, 4e3);
24090
24153
  };
24091
- const loadRepo = (targetPath) => {
24154
+ const loadRepo = async (targetPath) => {
24155
+ try {
24156
+ const exists = await window.api.checkPathExists(targetPath);
24157
+ if (!exists) {
24158
+ console.warn("Target path does not exist on disk:", targetPath);
24159
+ const currentMappings = JSON.parse(localStorage.getItem("gitelectron_collab_mappings") || "{}");
24160
+ const matching = Object.entries(currentMappings).find(([_, p]) => p === targetPath);
24161
+ if (matching) {
24162
+ const [fullName] = matching;
24163
+ let repo = collaboratedRepos.find((r) => r.full_name === fullName);
24164
+ if (!repo) {
24165
+ try {
24166
+ const allCollabs = await window.api.getCollaboratedRepos();
24167
+ setCollaboratedRepos(allCollabs);
24168
+ repo = allCollabs.find((r) => r.full_name === fullName);
24169
+ } catch (e) {
24170
+ }
24171
+ }
24172
+ if (repo) {
24173
+ showToast(`The local folder for ${fullName} was deleted. You can re-clone it now.`, "warning");
24174
+ setShowCollabActionModal({
24175
+ repo,
24176
+ url: repo.clone_url,
24177
+ name: repo.name,
24178
+ fullName: repo.full_name,
24179
+ missingPreviousPath: targetPath
24180
+ });
24181
+ setChangesTab("collaboration-repo");
24182
+ return;
24183
+ }
24184
+ }
24185
+ }
24186
+ } catch (e) {
24187
+ console.error("Error checking target path existence:", e);
24188
+ }
24092
24189
  window.api.setRepoPath(targetPath).then(async (result) => {
24093
24190
  if (result) {
24094
24191
  setRepoPath(result.path);
@@ -24103,6 +24200,33 @@ function App() {
24103
24200
  }
24104
24201
  }).catch((err) => console.error("Failed to load repo", err));
24105
24202
  };
24203
+ reactExports.useEffect(() => {
24204
+ const checkAllPaths = async () => {
24205
+ const results = {};
24206
+ for (const [fullName, path] of Object.entries(collabMappings)) {
24207
+ if (path) {
24208
+ try {
24209
+ results[fullName] = await window.api.checkPathExists(path);
24210
+ } catch {
24211
+ results[fullName] = false;
24212
+ }
24213
+ }
24214
+ }
24215
+ setPathExistenceMap(results);
24216
+ };
24217
+ if (changesTab === "collaboration-repo" || collaboratedRepos.length > 0) {
24218
+ checkAllPaths();
24219
+ }
24220
+ }, [collabMappings, changesTab, collaboratedRepos.length]);
24221
+ reactExports.useEffect(() => {
24222
+ if (authStatus.status === "Verified") {
24223
+ window.api.getCollaboratedRepos().then((repos) => {
24224
+ setCollaboratedRepos(repos);
24225
+ }).catch((err) => console.error(err));
24226
+ } else {
24227
+ setCollaboratedRepos([]);
24228
+ }
24229
+ }, [authStatus.status]);
24106
24230
  reactExports.useEffect(() => {
24107
24231
  window.api.getAuthStatus().then((auth) => {
24108
24232
  setAuthStatus(auth);
@@ -24212,6 +24336,106 @@ function App() {
24212
24336
  setTheme(newTheme);
24213
24337
  localStorage.setItem("gitelectron-theme", newTheme);
24214
24338
  };
24339
+ const saveMapping = (repoFullName, localPath) => {
24340
+ setCollabMappings((prev) => {
24341
+ const updated = { ...prev, [repoFullName]: localPath };
24342
+ localStorage.setItem("gitelectron_collab_mappings", JSON.stringify(updated));
24343
+ return updated;
24344
+ });
24345
+ };
24346
+ const openCollabRepoPath = async (targetPath) => {
24347
+ localStorage.setItem("gitelectron-last-repo", targetPath);
24348
+ setRepoPath(targetPath);
24349
+ setIsRepo(true);
24350
+ await window.api.setRepoPath(targetPath);
24351
+ window.api.toggleAutoSync(targetPath, true);
24352
+ await refreshRepoState();
24353
+ setChangesTab("changes");
24354
+ };
24355
+ const handleRepoCardClick = async (repo) => {
24356
+ const savedPath = collabMappings[repo.full_name];
24357
+ if (savedPath) {
24358
+ const exists = await window.api.checkPathExists(savedPath);
24359
+ if (exists) {
24360
+ await openCollabRepoPath(savedPath);
24361
+ return;
24362
+ } else {
24363
+ setShowCollabActionModal({
24364
+ repo,
24365
+ url: repo.clone_url,
24366
+ name: repo.name,
24367
+ fullName: repo.full_name,
24368
+ missingPreviousPath: savedPath
24369
+ });
24370
+ return;
24371
+ }
24372
+ }
24373
+ setShowCollabActionModal({
24374
+ repo,
24375
+ url: repo.clone_url,
24376
+ name: repo.name,
24377
+ fullName: repo.full_name
24378
+ });
24379
+ };
24380
+ const handleCollabMap = async () => {
24381
+ if (!showCollabActionModal) return;
24382
+ const { fullName } = showCollabActionModal;
24383
+ setShowCollabActionModal(null);
24384
+ const result = await window.api.selectDirectory();
24385
+ if (result) {
24386
+ if (fullName) {
24387
+ saveMapping(fullName, result.path);
24388
+ }
24389
+ localStorage.setItem("gitelectron-last-repo", result.path);
24390
+ setRepoPath(result.path);
24391
+ setIsRepo(result.isRepo);
24392
+ if (result.isRepo) {
24393
+ await window.api.setRepoPath(result.path);
24394
+ await refreshRepoState();
24395
+ window.api.toggleAutoSync(result.path, true);
24396
+ }
24397
+ setChangesTab("changes");
24398
+ showToast(`Linked repository to ${result.path}`, "success");
24399
+ }
24400
+ };
24401
+ const handleCollabClone = async () => {
24402
+ if (!showCollabActionModal) return;
24403
+ const { url, name, fullName } = showCollabActionModal;
24404
+ setShowCollabActionModal(null);
24405
+ try {
24406
+ const result = await window.api.selectDirectory();
24407
+ if (!result) return;
24408
+ const selectedBasename = result.path.replace(/\\/g, "/").split("/").pop();
24409
+ const destPath = selectedBasename?.toLowerCase() === name.toLowerCase() ? result.path : `${result.path}\\${name}`;
24410
+ setIsCloning(true);
24411
+ const clonedPath = await window.api.cloneRepository(url, destPath);
24412
+ if (fullName) {
24413
+ saveMapping(fullName, clonedPath);
24414
+ }
24415
+ await openCollabRepoPath(clonedPath);
24416
+ showToast(`Repository successfully cloned into ${clonedPath}!`, "success");
24417
+ } catch (err) {
24418
+ console.error(err);
24419
+ showToast(`Failed to clone repository: ${err.message || err}`, "error");
24420
+ } finally {
24421
+ setIsCloning(false);
24422
+ }
24423
+ };
24424
+ const handleUploadFiles = async () => {
24425
+ if (!repoPath) return;
24426
+ try {
24427
+ setIsUploading(true);
24428
+ const res = await window.api.uploadFilesToRepo(repoPath);
24429
+ if (res && res.success && res.count > 0) {
24430
+ await refreshRepoState();
24431
+ }
24432
+ } catch (err) {
24433
+ console.error("Failed to upload files", err);
24434
+ alert(`Failed to upload files: ${err.message || err}`);
24435
+ } finally {
24436
+ setIsUploading(false);
24437
+ }
24438
+ };
24215
24439
  const handleSelectDirectory = async () => {
24216
24440
  const result = await window.api.selectDirectory();
24217
24441
  if (result) {
@@ -24307,6 +24531,14 @@ function App() {
24307
24531
  try {
24308
24532
  const collabData = await window.api.getCollaborators(remoteUrl2);
24309
24533
  setRepoCollaborators(collabData.collaborators || []);
24534
+ if (remoteUrl2 && repoPath) {
24535
+ const cleaned = remoteUrl2.replace("https://github.com/", "").replace(/\.git$/, "");
24536
+ saveMapping(cleaned, repoPath);
24537
+ }
24538
+ if (collabData.collaborators?.length > 1 || collabData.invitations?.length > 0) {
24539
+ window.api.getCollaboratedRepos().then((repos) => setCollaboratedRepos(repos || [])).catch(() => {
24540
+ });
24541
+ }
24310
24542
  } catch (e) {
24311
24543
  console.error("Failed to fetch collaborators", e);
24312
24544
  setRepoCollaborators([]);
@@ -24321,6 +24553,31 @@ function App() {
24321
24553
  console.error(e);
24322
24554
  setErrorMsg(e.message || "Error fetching git status");
24323
24555
  setStatus(null);
24556
+ if (repoPath) {
24557
+ try {
24558
+ const exists = await window.api.checkPathExists(repoPath);
24559
+ if (!exists) {
24560
+ const currentMappings = JSON.parse(localStorage.getItem("gitelectron_collab_mappings") || "{}");
24561
+ const matching = Object.entries(currentMappings).find(([_, p]) => p === repoPath);
24562
+ if (matching) {
24563
+ const [fullName] = matching;
24564
+ const repo = collaboratedRepos.find((r) => r.full_name === fullName);
24565
+ if (repo) {
24566
+ showToast(`Repository folder was deleted on disk: ${fullName}. Prompting to re-clone.`, "warning");
24567
+ setShowCollabActionModal({
24568
+ repo,
24569
+ url: repo.clone_url,
24570
+ name: repo.name,
24571
+ fullName: repo.full_name,
24572
+ missingPreviousPath: repoPath
24573
+ });
24574
+ setChangesTab("collaboration-repo");
24575
+ }
24576
+ }
24577
+ }
24578
+ } catch (err) {
24579
+ }
24580
+ }
24324
24581
  } finally {
24325
24582
  setIsRefreshing(false);
24326
24583
  }
@@ -25376,7 +25633,64 @@ function App() {
25376
25633
  onProceed: handleDropProceed
25377
25634
  }
25378
25635
  ),
25379
- showContributorsModal && remoteUrl && /* @__PURE__ */ jsxRuntimeExports.jsx(ContributorsModal, { remoteUrl, onClose: () => setShowContributorsModal(false) }),
25636
+ showContributorsModal && /* @__PURE__ */ jsxRuntimeExports.jsx(ContributorsModal, { remoteUrl: remoteUrl || "", onClose: () => setShowContributorsModal(false) }),
25637
+ showCollabActionModal && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-[150] transition-opacity p-4", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-surface/95 backdrop-blur-xl p-6 rounded-2xl shadow-2xl border border-border w-full max-w-[460px] mx-4 flex flex-col gap-4 relative animate-in fade-in zoom-in-95", children: [
25638
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
25639
+ "button",
25640
+ {
25641
+ onClick: () => setShowCollabActionModal(null),
25642
+ className: "absolute top-4 right-4 text-secondary hover:text-primary transition-colors p-1",
25643
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) })
25644
+ }
25645
+ ),
25646
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
25647
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-10 h-10 rounded-xl bg-brand/10 border border-brand/20 flex items-center justify-center text-brand", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" }) }) }),
25648
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
25649
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "text-lg font-bold text-primary tracking-tight", children: "Collaboration Repository" }),
25650
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-secondary font-mono truncate max-w-[300px]", children: showCollabActionModal.fullName || showCollabActionModal.name })
25651
+ ] })
25652
+ ] }),
25653
+ showCollabActionModal.missingPreviousPath ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "p-3.5 bg-danger/10 border border-danger/30 rounded-xl text-danger text-xs flex items-start gap-2.5", children: [
25654
+ /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-5 h-5 flex-shrink-0 mt-0.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" }) }),
25655
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
25656
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-semibold text-danger", children: "Linked local folder was not found or was deleted:" }),
25657
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "font-mono text-[10px] break-all opacity-90 mt-0.5", children: showCollabActionModal.missingPreviousPath }),
25658
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "mt-1 text-secondary", children: "You can re-clone this repository into a new folder, or select its new location on your computer." })
25659
+ ] })
25660
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "text-sm text-secondary", children: [
25661
+ "How would you like to set up ",
25662
+ /* @__PURE__ */ jsxRuntimeExports.jsx("strong", { children: showCollabActionModal.name }),
25663
+ " on your computer?"
25664
+ ] }),
25665
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col gap-2.5 mt-1", children: [
25666
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
25667
+ "button",
25668
+ {
25669
+ onClick: handleCollabClone,
25670
+ className: "w-full py-3 px-4 bg-brand hover:bg-brand-hover text-white rounded-xl text-sm font-semibold transition-all shadow-md hover:shadow-brand/20 text-left flex items-center justify-between border border-white/10",
25671
+ children: [
25672
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: showCollabActionModal.missingPreviousPath ? "Re-Clone (Git Clone into New Folder)" : "Create (Git Clone)" }),
25673
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-base", children: "☁️" })
25674
+ ]
25675
+ }
25676
+ ),
25677
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
25678
+ "button",
25679
+ {
25680
+ onClick: handleCollabMap,
25681
+ className: "w-full py-3 px-4 bg-surface-hover hover:bg-surface-hover/80 border border-border rounded-xl text-primary text-sm font-medium transition-colors text-left flex items-center justify-between",
25682
+ children: [
25683
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Already have this project folder" }),
25684
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-base", children: "📁" })
25685
+ ]
25686
+ }
25687
+ )
25688
+ ] })
25689
+ ] }) }),
25690
+ isCloning && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute inset-0 bg-black/80 backdrop-blur-sm flex flex-col items-center justify-center z-[200]", children: [
25691
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-12 h-12 border-4 border-brand border-t-transparent rounded-full animate-spin mb-4 shadow-[0_0_15px_rgba(var(--brand-color),0.5)]" }),
25692
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-white font-medium tracking-wide text-lg", children: "Cloning Repository..." })
25693
+ ] }),
25380
25694
  showHealerOverlay && /* @__PURE__ */ jsxRuntimeExports.jsx(AIHealerOverlay, { message: healerMessage, attempt: healerAttempt, maxAttempts: 5, originalError: healerOriginalError }),
25381
25695
  showAutoSyncLogs && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-[110] transition-opacity", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-surface/95 backdrop-blur-xl p-8 rounded-xl shadow-2xl border border-border w-full max-w-[600px] mx-4 flex flex-col max-h-[80vh]", children: [
25382
25696
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-center mb-6 border-b border-border pb-4", children: [
@@ -25671,6 +25985,32 @@ function App() {
25671
25985
  }
25672
25986
  )
25673
25987
  ] }),
25988
+ collaboratedRepos.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4", children: [
25989
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-secondary font-semibold mb-2 uppercase tracking-wider px-1", children: "Collaboration Repo" }),
25990
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex flex-col gap-1 max-h-40 overflow-y-auto pr-1 custom-scrollbar", children: collaboratedRepos.map((repo) => {
25991
+ const saved = collabMappings[repo.full_name];
25992
+ const isMissing = saved && pathExistenceMap[repo.full_name] === false;
25993
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
25994
+ "button",
25995
+ {
25996
+ onClick: () => handleRepoCardClick(repo),
25997
+ className: `w-full text-left bg-canvas hover:bg-surface-hover text-primary py-2 px-3 rounded-md transition-all border text-sm truncate flex flex-col group ${isMissing ? "border-danger/40 bg-danger/5" : "border-border"}`,
25998
+ title: repo.name,
25999
+ children: [
26000
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between w-full", children: [
26001
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium truncate group-hover:text-brand transition-colors", children: repo.name }),
26002
+ isMissing && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[9px] bg-danger/20 text-danger px-1 rounded font-bold", children: "Deleted" })
26003
+ ] }),
26004
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-secondary truncate", children: [
26005
+ "by @",
26006
+ repo.owner?.login
26007
+ ] })
26008
+ ]
26009
+ },
26010
+ repo.id
26011
+ );
26012
+ }) })
26013
+ ] }),
25674
26014
  repoPath && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-canvas p-3 rounded-md border border-border mb-4", children: [
25675
26015
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs text-secondary font-semibold mb-1", children: "LOCAL PATH" }),
25676
26016
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-xs font-mono text-accent truncate", title: repoPath, children: repoPath.split("\\").pop() })
@@ -25684,6 +26024,35 @@ function App() {
25684
26024
  }
25685
26025
  ),
25686
26026
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-auto space-y-2", children: [
26027
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
26028
+ "button",
26029
+ {
26030
+ onClick: () => setChangesTab("collaboration-repo"),
26031
+ className: `w-full flex items-center py-2 px-3 hover:bg-surface-hover rounded-md text-sm transition-colors group ${changesTab === "collaboration-repo" ? "bg-surface-hover text-primary font-semibold" : "text-secondary hover:text-primary"}`,
26032
+ children: [
26033
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
26034
+ "svg",
26035
+ {
26036
+ className: "w-4 h-4 mr-3 text-secondary group-hover:text-primary",
26037
+ fill: "none",
26038
+ stroke: "currentColor",
26039
+ viewBox: "0 0 24 24",
26040
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
26041
+ "path",
26042
+ {
26043
+ strokeLinecap: "round",
26044
+ strokeLinejoin: "round",
26045
+ strokeWidth: 2,
26046
+ d: "M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2"
26047
+ }
26048
+ )
26049
+ }
26050
+ ),
26051
+ "Collaboration Repo",
26052
+ collaboratedRepos.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-auto bg-brand/20 text-brand text-[10px] font-bold px-1.5 py-0.5 rounded-full", children: collaboratedRepos.length })
26053
+ ]
26054
+ }
26055
+ ),
25687
26056
  isRepo && /* @__PURE__ */ jsxRuntimeExports.jsxs(
25688
26057
  "button",
25689
26058
  {
@@ -26235,17 +26604,47 @@ function App() {
26235
26604
  ]
26236
26605
  }
26237
26606
  ),
26238
- changesTab === "changes" && /* @__PURE__ */ jsxRuntimeExports.jsx(
26607
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
26239
26608
  "button",
26240
26609
  {
26241
- onClick: handleStageAll,
26242
- className: "ml-auto text-xs bg-surface-hover text-primary px-2.5 py-1 rounded-md transition-colors border border-border hover:brightness-110 mb-1",
26243
- children: "Stage All"
26610
+ onClick: () => {
26611
+ setChangesTab("collaboration-repo");
26612
+ window.api.getCollaboratedRepos().then((repos) => setCollaboratedRepos(repos || [])).catch(() => {
26613
+ });
26614
+ },
26615
+ className: `text-xs font-semibold px-3 py-1.5 rounded-t-md transition-colors border-b-2 ${changesTab === "collaboration-repo" ? "text-primary border-brand bg-canvas/50" : "text-secondary border-transparent hover:text-primary"}`,
26616
+ children: [
26617
+ "Collaboration Repo",
26618
+ collaboratedRepos.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ml-1.5 bg-brand/20 text-brand text-[10px] font-bold px-1.5 py-0.5 rounded-full", children: collaboratedRepos.length })
26619
+ ]
26244
26620
  }
26245
- )
26621
+ ),
26622
+ changesTab === "changes" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ml-auto flex items-center gap-2 mb-1", children: [
26623
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
26624
+ "button",
26625
+ {
26626
+ onClick: handleUploadFiles,
26627
+ disabled: !repoPath || isUploading,
26628
+ className: "text-xs bg-brand/10 hover:bg-brand/20 text-brand border border-brand/30 px-2.5 py-1 rounded-md transition-all flex items-center gap-1.5 font-medium shadow-sm disabled:opacity-50",
26629
+ title: "Upload files directly into this repository",
26630
+ children: [
26631
+ /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" }) }),
26632
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: isUploading ? "Uploading..." : "Upload Files" })
26633
+ ]
26634
+ }
26635
+ ),
26636
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
26637
+ "button",
26638
+ {
26639
+ onClick: handleStageAll,
26640
+ className: "text-xs bg-surface-hover text-primary px-2.5 py-1 rounded-md transition-colors border border-border hover:brightness-110",
26641
+ children: "Stage All"
26642
+ }
26643
+ )
26644
+ ] })
26246
26645
  ] }),
26247
26646
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-auto p-4 custom-scrollbar", children: [
26248
- changesTab === "changes" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-1", children: [
26647
+ changesTab === "changes" && /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "space-y-1", children: [
26249
26648
  status.conflicted.map((f) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
26250
26649
  "li",
26251
26650
  {
@@ -26361,43 +26760,170 @@ function App() {
26361
26760
  `m-${f}`
26362
26761
  )),
26363
26762
  status.not_added.length + status.modified.length + status.created.length + status.conflicted.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "text-xs text-secondary italic mt-2 text-center", children: "No working tree changes" })
26364
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-1", children: status.deleted.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "text-xs text-secondary italic mt-2 text-center", children: "No locally deleted files" }) : status.deleted.map((f) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
26365
- "li",
26366
- {
26367
- className: "flex justify-between items-center hover:bg-surface-hover px-2 py-1.5 rounded-md transition-colors",
26368
- children: [
26369
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-danger/70 font-mono flex-1 flex items-center gap-1.5 truncate", children: [
26370
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-shrink-0 text-[10px] font-bold bg-danger/20 text-danger border border-danger/30 px-1 rounded", children: "D" }),
26371
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: f })
26763
+ ] }),
26764
+ changesTab === "deleted" && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
26765
+ /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-1", children: status.deleted.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "text-xs text-secondary italic mt-2 text-center", children: "No locally deleted files" }) : status.deleted.map((f) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
26766
+ "li",
26767
+ {
26768
+ className: "flex justify-between items-center hover:bg-surface-hover px-2 py-1.5 rounded-md transition-colors",
26769
+ children: [
26770
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-danger/70 font-mono flex-1 flex items-center gap-1.5 truncate", children: [
26771
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-shrink-0 text-[10px] font-bold bg-danger/20 text-danger border border-danger/30 px-1 rounded", children: "D" }),
26772
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: f })
26773
+ ] }),
26774
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "opacity-0 group-hover:opacity-100 flex gap-1 flex-shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
26775
+ "button",
26776
+ {
26777
+ onClick: () => handleStageSingle(f),
26778
+ className: "text-[10px] text-secondary hover:text-primary px-2 py-0.5 border border-border rounded bg-canvas",
26779
+ children: "+"
26780
+ }
26781
+ ) })
26782
+ ]
26783
+ },
26784
+ `d-${f}`
26785
+ )) }),
26786
+ deletedHistory?.all && deletedHistory.all.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 pt-3 border-t border-border/50", children: [
26787
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-[11px] font-semibold text-secondary uppercase tracking-wider mb-2", children: "Recently Deleted in Repository" }),
26788
+ /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-2", children: deletedHistory.all.map((commit) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "bg-surface/50 p-2 rounded-md border border-border/50", children: [
26789
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-1.5", children: [
26790
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold text-primary truncate", title: commit.message, children: commit.message }),
26791
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-secondary whitespace-nowrap ml-2 flex-shrink-0", children: [
26792
+ new Date(commit.date).toLocaleDateString(),
26793
+ " ",
26794
+ new Date(commit.date).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
26795
+ ] })
26372
26796
  ] }),
26373
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "opacity-0 group-hover:opacity-100 flex gap-1 flex-shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
26374
- "button",
26375
- {
26376
- onClick: () => handleStageSingle(f),
26377
- className: "text-[10px] text-secondary hover:text-primary px-2 py-0.5 border border-border rounded bg-canvas",
26378
- children: "+"
26379
- }
26380
- ) })
26381
- ]
26382
- },
26383
- `d-${f}`
26384
- )) }),
26385
- changesTab === "deleted" && deletedHistory?.all && deletedHistory.all.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-4 pt-3 border-t border-border/50", children: [
26386
- /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-[11px] font-semibold text-secondary uppercase tracking-wider mb-2", children: "Recently Deleted in Repository" }),
26387
- /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "space-y-2", children: deletedHistory.all.map((commit) => /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { className: "bg-surface/50 p-2 rounded-md border border-border/50", children: [
26388
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-between items-start mb-1.5", children: [
26389
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold text-primary truncate", title: commit.message, children: commit.message }),
26390
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-[10px] text-secondary whitespace-nowrap ml-2 flex-shrink-0", children: [
26391
- new Date(commit.date).toLocaleDateString(),
26392
- " ",
26393
- new Date(commit.date).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })
26797
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: commit.diff?.files?.filter((f) => f.status === "D").map((f, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-mono text-danger/80", children: [
26798
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-shrink-0 font-bold bg-danger/10 text-danger border border-danger/20 px-1 rounded", children: "D" }),
26799
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", title: f.file, children: f.file })
26800
+ ] }, i)) })
26801
+ ] }, commit.hash)) })
26802
+ ] })
26803
+ ] }),
26804
+ changesTab === "collaboration-repo" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
26805
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-3 p-4 bg-brand/10 border border-brand/30 rounded-xl", children: [
26806
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3", children: [
26807
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-10 h-10 rounded-xl bg-brand/20 border border-brand/30 flex items-center justify-center text-brand", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-6 h-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" }) }) }),
26808
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
26809
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { className: "text-base font-bold text-primary flex items-center gap-2", children: [
26810
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Collaboration Repositories" }),
26811
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs bg-brand/20 text-brand px-2 py-0.5 rounded-full font-semibold", children: collaboratedRepos.length })
26812
+ ] }),
26813
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-secondary", children: "All repositories you are collaborating on with teams & other developers" })
26394
26814
  ] })
26395
26815
  ] }),
26396
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-1", children: commit.diff?.files?.filter((f) => f.status === "D").map((f, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-mono text-danger/80", children: [
26397
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "flex-shrink-0 font-bold bg-danger/10 text-danger border border-danger/20 px-1 rounded", children: "D" }),
26398
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", title: f.file, children: f.file })
26399
- ] }, i)) })
26400
- ] }, commit.hash)) })
26816
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
26817
+ "button",
26818
+ {
26819
+ onClick: async () => {
26820
+ showToast("Refreshing collaboration repositories...", "info");
26821
+ try {
26822
+ const repos = await window.api.getCollaboratedRepos();
26823
+ setCollaboratedRepos(repos || []);
26824
+ showToast(`Found ${repos?.length || 0} collaboration repository(ies)`, "success");
26825
+ } catch (e) {
26826
+ showToast(`Failed to refresh: ${e.message}`, "error");
26827
+ }
26828
+ },
26829
+ className: "text-xs bg-surface border border-border hover:bg-surface-hover text-primary px-3 py-1.5 rounded-lg transition-colors flex items-center gap-1.5 font-medium shadow-sm",
26830
+ title: "Refresh repositories from GitHub",
26831
+ children: [
26832
+ /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" }) }),
26833
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Refresh" })
26834
+ ]
26835
+ }
26836
+ ) })
26837
+ ] }),
26838
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative", children: [
26839
+ /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-4 h-4 text-secondary absolute left-3 top-2.5 pointer-events-none", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" }) }),
26840
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
26841
+ "input",
26842
+ {
26843
+ type: "text",
26844
+ value: collabSearch,
26845
+ onChange: (e) => setCollabSearch(e.target.value),
26846
+ placeholder: "Search collaboration repositories by name or owner...",
26847
+ className: "w-full bg-surface border border-border rounded-lg pl-9 pr-3 py-2 text-xs text-primary placeholder-secondary focus:outline-none focus:border-brand focus:ring-1 focus:ring-brand transition-all"
26848
+ }
26849
+ )
26850
+ ] }),
26851
+ collaboratedRepos.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-center py-12 border border-dashed border-border rounded-xl bg-surface/30 p-6", children: [
26852
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-3xl mb-2", children: "🤝" }),
26853
+ /* @__PURE__ */ jsxRuntimeExports.jsx("h4", { className: "text-sm font-semibold text-primary mb-1", children: "No Collaboration Repositories Found" }),
26854
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-secondary max-w-sm mx-auto", children: "When someone invites you as a collaborator, you add collaborators to your repository, or you work on team repositories, they will appear here." })
26855
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-2.5", children: collaboratedRepos.filter((r) => {
26856
+ if (!collabSearch.trim()) return true;
26857
+ const q = collabSearch.toLowerCase();
26858
+ return r.name?.toLowerCase().includes(q) || r.full_name?.toLowerCase().includes(q) || r.owner?.login?.toLowerCase().includes(q) || r.description && r.description.toLowerCase().includes(q);
26859
+ }).map((repo) => {
26860
+ const savedPath = collabMappings[repo.full_name];
26861
+ const isCurrentActive = repoPath && savedPath && repoPath.toLowerCase() === savedPath.toLowerCase();
26862
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
26863
+ "div",
26864
+ {
26865
+ onClick: () => handleRepoCardClick(repo),
26866
+ className: `p-3.5 rounded-xl border transition-all cursor-pointer flex flex-col sm:flex-row sm:items-center justify-between gap-3 ${isCurrentActive ? "bg-brand/10 border-brand shadow-sm" : "bg-surface/60 border-border hover:bg-surface-hover hover:border-border/80"}`,
26867
+ children: [
26868
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start gap-3", children: [
26869
+ repo.owner?.avatar_url && /* @__PURE__ */ jsxRuntimeExports.jsx(
26870
+ "img",
26871
+ {
26872
+ src: repo.owner.avatar_url,
26873
+ alt: repo.owner.login,
26874
+ className: "w-9 h-9 rounded-lg border border-border flex-shrink-0 mt-0.5 object-cover"
26875
+ }
26876
+ ),
26877
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
26878
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
26879
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-sm font-bold text-primary hover:text-brand transition-colors", children: repo.name }),
26880
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-secondary font-mono", children: [
26881
+ "by @",
26882
+ repo.owner?.login
26883
+ ] }),
26884
+ repo.private ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-amber-500/10 text-amber-400 border border-amber-500/20 px-1.5 py-0.2 rounded font-medium", children: "Private" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 px-1.5 py-0.2 rounded font-medium", children: "Public" }),
26885
+ isCurrentActive && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-brand text-white px-2 py-0.5 rounded font-bold uppercase tracking-wider", children: "Active" })
26886
+ ] }),
26887
+ repo.description && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-secondary line-clamp-1 mt-0.5 max-w-md", children: repo.description }),
26888
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-3 mt-1.5 text-[11px] text-secondary", children: [
26889
+ savedPath ? pathExistenceMap[repo.full_name] === false ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-danger font-mono flex items-center gap-1 truncate max-w-xs", title: `Local folder deleted or moved: ${savedPath}`, children: [
26890
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-danger animate-pulse" }),
26891
+ "Deleted: ",
26892
+ savedPath
26893
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-emerald-400 font-mono flex items-center gap-1 truncate max-w-xs", title: savedPath, children: [
26894
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-400" }),
26895
+ savedPath
26896
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-secondary/70 italic flex items-center gap-1", children: [
26897
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-secondary/50" }),
26898
+ "Not cloned yet"
26899
+ ] }),
26900
+ repo.default_branch && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
26901
+ "Branch: ",
26902
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono text-primary", children: repo.default_branch })
26903
+ ] })
26904
+ ] })
26905
+ ] })
26906
+ ] }),
26907
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2 self-end sm:self-center flex-shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
26908
+ "button",
26909
+ {
26910
+ type: "button",
26911
+ onClick: (e) => {
26912
+ e.stopPropagation();
26913
+ handleRepoCardClick(repo);
26914
+ },
26915
+ className: `px-3.5 py-1.5 text-xs font-semibold rounded-lg transition-all shadow-sm flex items-center gap-1.5 ${savedPath ? pathExistenceMap[repo.full_name] === false ? "bg-danger/20 hover:bg-danger/30 text-danger border border-danger/40" : "bg-brand hover:bg-brand-hover text-white" : "bg-surface hover:bg-surface-hover border border-border text-primary"}`,
26916
+ children: [
26917
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: savedPath ? pathExistenceMap[repo.full_name] === false ? "Re-Clone" : "Open Repo" : "Connect / Clone" }),
26918
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: pathExistenceMap[repo.full_name] === false ? "☁️" : "→" })
26919
+ ]
26920
+ }
26921
+ ) })
26922
+ ]
26923
+ },
26924
+ repo.id
26925
+ );
26926
+ }) })
26401
26927
  ] })
26402
26928
  ] })
26403
26929
  ] }),