je-editor 0.0.215__py3-none-any.whl → 0.0.216__py3-none-any.whl

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.
Files changed (132) hide show
  1. git_client/commit_graph.py +88 -0
  2. git_client/git.py +181 -0
  3. git_client/git_cli.py +66 -0
  4. git_client/github.py +50 -0
  5. {je_editor-0.0.215.dist-info → je_editor-0.0.216.dist-info}/METADATA +2 -1
  6. je_editor-0.0.216.dist-info/RECORD +127 -0
  7. je_editor-0.0.216.dist-info/top_level.txt +4 -0
  8. pyside_ui/git_ui/commit_table.py +27 -0
  9. pyside_ui/git_ui/git_branch_tree_widget.py +119 -0
  10. pyside_ui/git_ui/git_client_gui.py +292 -0
  11. pyside_ui/git_ui/graph_view.py +174 -0
  12. pyside_ui/main_ui/ai_widget/ai_config.py +19 -0
  13. pyside_ui/main_ui/ai_widget/ask_thread.py +17 -0
  14. pyside_ui/main_ui/ai_widget/chat_ui.py +130 -0
  15. pyside_ui/main_ui/ai_widget/langchain_interface.py +45 -0
  16. pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py +81 -0
  17. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/dock_menu/build_dock_menu.py +2 -2
  18. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/tab_menu/build_tab_menu.py +2 -2
  19. {je_editor/utils → utils}/multi_language/english.py +1 -1
  20. {je_editor/utils → utils}/multi_language/traditional_chinese.py +1 -1
  21. je_editor/__init__.py +0 -34
  22. je_editor/__main__.py +0 -18
  23. je_editor/start_editor.py +0 -17
  24. je_editor-0.0.215.dist-info/RECORD +0 -117
  25. je_editor-0.0.215.dist-info/top_level.txt +0 -1
  26. {je_editor/code_scan → code_scan}/__init__.py +0 -0
  27. {je_editor/code_scan → code_scan}/ruff_thread.py +0 -0
  28. {je_editor/code_scan → code_scan}/watchdog_implement.py +0 -0
  29. {je_editor/code_scan → code_scan}/watchdog_thread.py +0 -0
  30. {je_editor-0.0.215.dist-info → je_editor-0.0.216.dist-info}/WHEEL +0 -0
  31. {je_editor-0.0.215.dist-info → je_editor-0.0.216.dist-info}/licenses/LICENSE +0 -0
  32. {je_editor/pyside_ui → pyside_ui}/__init__.py +0 -0
  33. {je_editor/pyside_ui → pyside_ui}/browser/__init__.py +0 -0
  34. {je_editor/pyside_ui → pyside_ui}/browser/browser_download_window.py +0 -0
  35. {je_editor/pyside_ui → pyside_ui}/browser/browser_serach_lineedit.py +0 -0
  36. {je_editor/pyside_ui → pyside_ui}/browser/browser_view.py +0 -0
  37. {je_editor/pyside_ui → pyside_ui}/browser/browser_widget.py +0 -0
  38. {je_editor/pyside_ui → pyside_ui}/code/__init__.py +0 -0
  39. {je_editor/pyside_ui → pyside_ui}/code/auto_save/__init__.py +0 -0
  40. {je_editor/pyside_ui → pyside_ui}/code/auto_save/auto_save_manager.py +0 -0
  41. {je_editor/pyside_ui → pyside_ui}/code/auto_save/auto_save_thread.py +0 -0
  42. {je_editor/pyside_ui → pyside_ui}/code/code_format/__init__.py +0 -0
  43. {je_editor/pyside_ui → pyside_ui}/code/code_format/pep8_format.py +0 -0
  44. {je_editor/pyside_ui → pyside_ui}/code/code_process/__init__.py +0 -0
  45. {je_editor/pyside_ui → pyside_ui}/code/code_process/code_exec.py +0 -0
  46. {je_editor/pyside_ui → pyside_ui}/code/plaintext_code_edit/__init__.py +0 -0
  47. {je_editor/pyside_ui → pyside_ui}/code/plaintext_code_edit/code_edit_plaintext.py +0 -0
  48. {je_editor/pyside_ui → pyside_ui}/code/running_process_manager.py +0 -0
  49. {je_editor/pyside_ui → pyside_ui}/code/shell_process/__init__.py +0 -0
  50. {je_editor/pyside_ui → pyside_ui}/code/shell_process/shell_exec.py +0 -0
  51. {je_editor/pyside_ui → pyside_ui}/code/syntax/__init__.py +0 -0
  52. {je_editor/pyside_ui → pyside_ui}/code/syntax/python_syntax.py +0 -0
  53. {je_editor/pyside_ui → pyside_ui}/code/syntax/syntax_setting.py +0 -0
  54. {je_editor/pyside_ui → pyside_ui}/code/textedit_code_result/__init__.py +0 -0
  55. {je_editor/pyside_ui → pyside_ui}/code/textedit_code_result/code_record.py +0 -0
  56. {je_editor/pyside_ui → pyside_ui}/code/variable_inspector/__init__.py +0 -0
  57. {je_editor/pyside_ui → pyside_ui}/code/variable_inspector/inspector_gui.py +0 -0
  58. {je_editor/pyside_ui → pyside_ui}/dialog/__init__.py +0 -0
  59. {je_editor/pyside_ui → pyside_ui}/dialog/ai_dialog/__init__.py +0 -0
  60. {je_editor/pyside_ui → pyside_ui}/dialog/ai_dialog/set_ai_dialog.py +0 -0
  61. {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/__init__.py +0 -0
  62. {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/create_file_dialog.py +0 -0
  63. {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/open_file_dialog.py +0 -0
  64. {je_editor/pyside_ui → pyside_ui}/dialog/file_dialog/save_file_dialog.py +0 -0
  65. {je_editor/pyside_ui → pyside_ui}/dialog/search_ui/__init__.py +0 -0
  66. {je_editor/pyside_ui → pyside_ui}/dialog/search_ui/search_error_box.py +0 -0
  67. {je_editor/pyside_ui → pyside_ui}/dialog/search_ui/search_text_box.py +0 -0
  68. {je_editor/pyside_ui → pyside_ui}/main_ui/__init__.py +0 -0
  69. {je_editor/pyside_ui → pyside_ui}/main_ui/console_widget/__init__.py +0 -0
  70. {je_editor/pyside_ui → pyside_ui}/main_ui/console_widget/console_gui.py +0 -0
  71. {je_editor/pyside_ui → pyside_ui}/main_ui/console_widget/qprocess_adapter.py +0 -0
  72. {je_editor/pyside_ui → pyside_ui}/main_ui/dock/__init__.py +0 -0
  73. {je_editor/pyside_ui → pyside_ui}/main_ui/dock/destroy_dock.py +0 -0
  74. {je_editor/pyside_ui → pyside_ui}/main_ui/editor/__init__.py +0 -0
  75. {je_editor/pyside_ui → pyside_ui}/main_ui/editor/editor_widget.py +0 -0
  76. {je_editor/pyside_ui → pyside_ui}/main_ui/editor/editor_widget_dock.py +0 -0
  77. {je_editor/pyside_ui → pyside_ui}/main_ui/editor/process_input.py +0 -0
  78. {je_editor/pyside_ui → pyside_ui}/main_ui/ipython_widget/__init__.py +0 -0
  79. {je_editor/pyside_ui → pyside_ui}/main_ui/ipython_widget/rich_jupyter.py +0 -0
  80. {je_editor/pyside_ui → pyside_ui}/main_ui/main_editor.py +0 -0
  81. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/__init__.py +0 -0
  82. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/dock_menu/__init__.py +0 -0
  83. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/file_menu/__init__.py +0 -0
  84. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/file_menu/build_file_menu.py +0 -0
  85. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/help_menu/__init__.py +0 -0
  86. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/help_menu/build_help_menu.py +0 -0
  87. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/language_menu/__init__.py +0 -0
  88. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/language_menu/build_language_server.py +0 -0
  89. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/python_env_menu/__init__.py +0 -0
  90. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/python_env_menu/build_venv_menu.py +0 -0
  91. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/__init__.py +0 -0
  92. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/build_run_menu.py +0 -0
  93. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/__init__.py +0 -0
  94. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py +0 -0
  95. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/build_program_menu.py +0 -0
  96. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py +0 -0
  97. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/run_menu/under_run_menu/utils.py +0 -0
  98. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/set_menu_bar.py +0 -0
  99. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/style_menu/__init__.py +0 -0
  100. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/style_menu/build_style_menu.py +0 -0
  101. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/tab_menu/__init__.py +0 -0
  102. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/text_menu/__init__.py +0 -0
  103. {je_editor/pyside_ui → pyside_ui}/main_ui/menu/text_menu/build_text_menu.py +0 -0
  104. {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/__init__.py +0 -0
  105. {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/setting_utils.py +0 -0
  106. {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/user_color_setting_file.py +0 -0
  107. {je_editor/pyside_ui → pyside_ui}/main_ui/save_settings/user_setting_file.py +0 -0
  108. {je_editor/pyside_ui → pyside_ui}/main_ui/system_tray/__init__.py +0 -0
  109. {je_editor/pyside_ui → pyside_ui}/main_ui/system_tray/extend_system_tray.py +0 -0
  110. {je_editor/utils → utils}/__init__.py +0 -0
  111. {je_editor/utils → utils}/encodings/__init__.py +0 -0
  112. {je_editor/utils → utils}/encodings/python_encodings.py +0 -0
  113. {je_editor/utils → utils}/exception/__init__.py +0 -0
  114. {je_editor/utils → utils}/exception/exception_tags.py +0 -0
  115. {je_editor/utils → utils}/exception/exceptions.py +0 -0
  116. {je_editor/utils → utils}/file/__init__.py +0 -0
  117. {je_editor/utils → utils}/file/open/__init__.py +0 -0
  118. {je_editor/utils → utils}/file/open/open_file.py +0 -0
  119. {je_editor/utils → utils}/file/save/__init__.py +0 -0
  120. {je_editor/utils → utils}/file/save/save_file.py +0 -0
  121. {je_editor/utils → utils}/json/__init__.py +0 -0
  122. {je_editor/utils → utils}/json/json_file.py +0 -0
  123. {je_editor/utils → utils}/json_format/__init__.py +0 -0
  124. {je_editor/utils → utils}/json_format/json_process.py +0 -0
  125. {je_editor/utils → utils}/logging/__init__.py +0 -0
  126. {je_editor/utils → utils}/logging/loggin_instance.py +0 -0
  127. {je_editor/utils → utils}/multi_language/__init__.py +0 -0
  128. {je_editor/utils → utils}/multi_language/multi_language_wrapper.py +0 -0
  129. {je_editor/utils → utils}/redirect_manager/__init__.py +0 -0
  130. {je_editor/utils → utils}/redirect_manager/redirect_manager_class.py +0 -0
  131. {je_editor/utils → utils}/venv_check/__init__.py +0 -0
  132. {je_editor/utils → utils}/venv_check/check_venv.py +0 -0
@@ -0,0 +1,88 @@
1
+ import logging
2
+ from dataclasses import dataclass, field
3
+ from typing import List, Dict
4
+
5
+ log = logging.getLogger(__name__)
6
+
7
+
8
+ @dataclass
9
+ class CommitNode:
10
+ commit_sha: str
11
+ author_name: str
12
+ commit_date: str
13
+ commit_message: str
14
+ parent_shas: List[str]
15
+ lane_index: int = -1 # assigned later
16
+
17
+ @dataclass
18
+ class CommitGraph:
19
+ nodes: List[CommitNode] = field(default_factory=list)
20
+ index: Dict[str, int] = field(default_factory=dict) # sha -> row
21
+
22
+ def build(self, commits: List[Dict], refs: Dict[str, str]) -> None:
23
+ # commits are topo-ordered by git_client log --topo-order; we keep it.
24
+ self.nodes = [
25
+ CommitNode(
26
+ commit_sha=c["sha"],
27
+ author_name=c["author"],
28
+ commit_date=c["date"],
29
+ commit_message=c["message"],
30
+ parent_shas=c["parents"],
31
+ ) for c in commits
32
+ ]
33
+ self.index = {n.commit_sha: i for i, n in enumerate(self.nodes)}
34
+ self._assign_lanes()
35
+
36
+ def _assign_lanes(self) -> None:
37
+ """
38
+ Simple lane assignment similar to 'git_client log --graph' lanes.
39
+ Greedy: reuse freed lanes; parents may create new lanes.
40
+ """
41
+ active: Dict[int, str] = {} # lane -> sha
42
+ free_lanes: List[int] = []
43
+
44
+ for i, node in enumerate(self.nodes):
45
+ # If any active lane points to this commit, use that lane
46
+ lane_found = None
47
+ for lane, sha in list(active.items()):
48
+ if sha == node.commit_sha:
49
+ lane_found = lane
50
+ break
51
+
52
+ if lane_found is None:
53
+ if free_lanes:
54
+ node.lane_index = free_lanes.pop(0)
55
+ else:
56
+ node.lane_index = 0 if not active else max(active.keys()) + 1
57
+ else:
58
+ node.lane_index = lane_found
59
+
60
+ # Update active: current node consumes its lane, parents occupy lanes
61
+ # Remove the current sha from any lane that pointed to it
62
+ for lane, sha in list(active.items()):
63
+ if sha == node.commit_sha:
64
+ del active[lane]
65
+
66
+ # First parent continues in the same lane; others go to free/new lanes
67
+ if node.parent_shas:
68
+ first = node.parent_shas[0]
69
+ active[node.lane_index] = first
70
+ # Side branches
71
+ for p in node.parent_shas[1:]:
72
+ # Pick a free lane or new one
73
+ if free_lanes:
74
+ pl = free_lanes.pop(0)
75
+ else:
76
+ pl = 0 if not active else max(active.keys()) + 1
77
+ active[pl] = p
78
+
79
+ # Any lane whose target no longer appears in the future will be freed later
80
+ # We approximate by freeing lanes when a target didn't appear in the next rows;
81
+ # but for minimal viable, free when lane not reassigned by parents this row.
82
+ used_lanes = set(active.keys())
83
+ # Collect gaps below max lane as free lanes to reuse
84
+ max_lane = max(used_lanes) if used_lanes else -1
85
+ present = set(range(max_lane + 1))
86
+ missing = sorted(list(present - used_lanes))
87
+ # Merge missing into free_lanes maintaining order
88
+ free_lanes = sorted(set(free_lanes + missing))
git_client/git.py ADDED
@@ -0,0 +1,181 @@
1
+ import os
2
+ from datetime import datetime
3
+
4
+ from PySide6.QtCore import QThread, Signal
5
+ from git import Repo, GitCommandError, InvalidGitRepositoryError, NoSuchPathError
6
+
7
+
8
+ # -----------------------
9
+ # Simple audit logger
10
+ # -----------------------
11
+ def audit_log(repo_path: str, action: str, detail: str, ok: bool, err: str = ""):
12
+ """
13
+ Append an audit log entry to 'audit.log' in the repo directory.
14
+ This is useful for compliance and traceability.
15
+ """
16
+ try:
17
+ path = os.path.join(repo_path if repo_path else ".", "audit.log")
18
+ with open(path, "a", encoding="utf-8") as f:
19
+ ts = datetime.now().isoformat(timespec="seconds")
20
+ f.write(f"{ts}\taction={action}\tok={ok}\tdetail={detail}\terr={err}\n")
21
+ except Exception:
22
+ pass # Never let audit logging failure break the UI
23
+
24
+
25
+ # -----------------------
26
+ # Git service layer
27
+ # -----------------------
28
+ class GitService:
29
+ """
30
+ Encapsulates Git operations using GitPython.
31
+ Keeps UI logic separate from Git logic.
32
+ """
33
+
34
+ def __init__(self):
35
+ self.repo: Repo | None = None
36
+ self.repo_path: str | None = None
37
+
38
+ def open_repo(self, path: str):
39
+ try:
40
+ self.repo = Repo(path)
41
+ self.repo_path = path
42
+ audit_log(path, "open_repo", path, True)
43
+ except (InvalidGitRepositoryError, NoSuchPathError) as e:
44
+ audit_log(path, "open_repo", path, False, str(e))
45
+ raise
46
+
47
+ def list_branches(self):
48
+ self._ensure_repo()
49
+ branches = [head.name for head in self.repo.heads]
50
+ audit_log(self.repo_path, "list_branches", ",".join(branches), True)
51
+ return branches
52
+
53
+ def current_branch(self):
54
+ self._ensure_repo()
55
+ try:
56
+ return self.repo.active_branch.name
57
+ except TypeError:
58
+ return "(detached HEAD)"
59
+
60
+ def checkout(self, branch: str):
61
+ self._ensure_repo()
62
+ try:
63
+ self.repo.git.checkout(branch)
64
+ audit_log(self.repo_path, "checkout", branch, True)
65
+ except GitCommandError as e:
66
+ audit_log(self.repo_path, "checkout", branch, False, str(e))
67
+ raise
68
+
69
+ def list_commits(self, branch: str, max_count: int = 100):
70
+ self._ensure_repo()
71
+ commits = list(self.repo.iter_commits(branch, max_count=max_count))
72
+ data = [
73
+ {
74
+ "hexsha": c.hexsha,
75
+ "summary": c.summary,
76
+ "author": c.author.name if c.author else "",
77
+ "date": datetime.fromtimestamp(c.committed_date).isoformat(sep=" ", timespec="seconds"),
78
+ }
79
+ for c in commits
80
+ ]
81
+ audit_log(self.repo_path, "list_commits", f"{branch}:{len(data)}", True)
82
+ return data
83
+
84
+ def show_diff_of_commit(self, commit_sha: str) -> str:
85
+ self._ensure_repo()
86
+ commit = self.repo.commit(commit_sha)
87
+ parent = commit.parents[0] if commit.parents else None
88
+ if parent is None:
89
+ null_tree = self.repo.tree(NULL_TREE)
90
+ diffs = commit.diff(null_tree, create_patch=True)
91
+ else:
92
+ diffs = commit.diff(parent, create_patch=True)
93
+ text = []
94
+ for d in diffs:
95
+ try:
96
+ text.append(d.diff.decode("utf-8", errors="replace"))
97
+ except Exception:
98
+ pass
99
+ out = "".join(text) if text else "(No patch content)"
100
+ audit_log(self.repo_path, "show_diff", commit_sha, True)
101
+ return out
102
+
103
+ def stage_all(self):
104
+ self._ensure_repo()
105
+ try:
106
+ self.repo.git.add(all=True)
107
+ audit_log(self.repo_path, "stage_all", "git_client add -A", True)
108
+ except GitCommandError as e:
109
+ audit_log(self.repo_path, "stage_all", "git_client add -A", False, str(e))
110
+ raise
111
+
112
+ def commit(self, message: str):
113
+ self._ensure_repo()
114
+ if not message.strip():
115
+ raise ValueError("Commit message is empty.")
116
+ try:
117
+ self.repo.index.commit(message)
118
+ audit_log(self.repo_path, "commit", message, True)
119
+ except Exception as e:
120
+ audit_log(self.repo_path, "commit", message, False, str(e))
121
+ raise
122
+
123
+ def pull(self, remote: str = "origin", branch: str | None = None):
124
+ self._ensure_repo()
125
+ if branch is None:
126
+ branch = self.current_branch()
127
+ try:
128
+ res = self.repo.git.pull(remote, branch)
129
+ audit_log(self.repo_path, "pull", f"{remote}/{branch}", True)
130
+ return res
131
+ except GitCommandError as e:
132
+ audit_log(self.repo_path, "pull", f"{remote}/{branch}", False, str(e))
133
+ raise
134
+
135
+ def push(self, remote: str = "origin", branch: str | None = None):
136
+ self._ensure_repo()
137
+ if branch is None:
138
+ branch = self.current_branch()
139
+ try:
140
+ res = self.repo.git.push(remote, branch)
141
+ audit_log(self.repo_path, "push", f"{remote}/{branch}", True)
142
+ return res
143
+ except GitCommandError as e:
144
+ audit_log(self.repo_path, "push", f"{remote}/{branch}", False, str(e))
145
+ raise
146
+
147
+ def remotes(self):
148
+ self._ensure_repo()
149
+ return [r.name for r in self.repo.remotes]
150
+
151
+ def _ensure_repo(self):
152
+ if self.repo is None:
153
+ raise RuntimeError("Repository not opened.")
154
+
155
+
156
+ # Null tree constant for initial commit diff
157
+ NULL_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
158
+
159
+
160
+ # -----------------------
161
+ # Worker thread wrapper
162
+ # -----------------------
163
+ class Worker(QThread):
164
+ """
165
+ Runs a function in a separate thread to avoid blocking the UI.
166
+ Emits (result, error) when done.
167
+ """
168
+ done = Signal(object, object)
169
+
170
+ def __init__(self, fn, *args, **kwargs):
171
+ super().__init__()
172
+ self.fn = fn
173
+ self.args = args
174
+ self.kwargs = kwargs
175
+
176
+ def run(self):
177
+ try:
178
+ res = self.fn(*self.args, **self.kwargs)
179
+ self.done.emit(res, None)
180
+ except Exception as e:
181
+ self.done.emit(None, e)
git_client/git_cli.py ADDED
@@ -0,0 +1,66 @@
1
+ import logging
2
+ import subprocess
3
+ from pathlib import Path
4
+ from typing import List, Dict
5
+
6
+ log = logging.getLogger(__name__)
7
+
8
+
9
+ class GitCLI:
10
+ def __init__(self, repo_path: Path):
11
+ self.repo_path = Path(repo_path)
12
+
13
+ def is_git_repo(self) -> bool:
14
+ return (self.repo_path / ".git_client").exists()
15
+
16
+ def _run(self, args: List[str]) -> str:
17
+ log.debug("git_client %s", " ".join(args))
18
+ res = subprocess.run(
19
+ ["git_client"] + args,
20
+ cwd=self.repo_path,
21
+ stdout=subprocess.PIPE,
22
+ stderr=subprocess.PIPE,
23
+ text=True,
24
+ encoding="utf-8",
25
+ )
26
+ if res.returncode != 0:
27
+ log.error("Git failed: %s", res.stderr.strip())
28
+ raise RuntimeError(res.stderr.strip())
29
+ return res.stdout
30
+
31
+ def get_all_refs(self) -> Dict[str, str]:
32
+ # returns refname -> commit hash
33
+ out = self._run(["show-ref", "--heads", "--tags"])
34
+ refs = {}
35
+ for line in out.splitlines():
36
+ if not line.strip():
37
+ continue
38
+ sha, ref = line.split(" ", 1)
39
+ refs[ref.strip()] = sha.strip()
40
+ return refs
41
+
42
+ def get_commits(self, max_count: int = 500) -> List[Dict]:
43
+ """
44
+ Return recent commits across all refs, with parents.
45
+ """
46
+ fmt = "%H%x01%P%x01%an%x01%ad%x01%s"
47
+ out = self._run([
48
+ "log", "--date=short", f"--format={fmt}", "--all",
49
+ f"--max-count={max_count}", "--topo-order"
50
+ ])
51
+ commits = []
52
+ for line in out.splitlines():
53
+ if not line.strip():
54
+ continue
55
+ parts = line.split("\x01")
56
+ if len(parts) != 5:
57
+ continue
58
+ sha, parents, author, date, msg = parts
59
+ commits.append({
60
+ "sha": sha,
61
+ "parents": [p for p in parents.strip().split() if p],
62
+ "author": author,
63
+ "date": date,
64
+ "message": msg,
65
+ })
66
+ return commits
git_client/github.py ADDED
@@ -0,0 +1,50 @@
1
+ import datetime
2
+ import traceback
3
+
4
+ from git import Repo, GitCommandError, InvalidGitRepositoryError, NoSuchPathError
5
+
6
+
7
+ class GitCloneHandler:
8
+ """
9
+ Handles cloning of remote Git repositories with audit logging.
10
+ Can be reused in UI or CLI contexts.
11
+ """
12
+
13
+ def __init__(self, audit_log_path: str = "git_clone_audit.log"):
14
+ self.audit_log_path = audit_log_path
15
+
16
+ def clone_repo(self, remote_url: str, local_path: str) -> str:
17
+ """
18
+ Clone a remote repository to a local path.
19
+
20
+ :param remote_url: The Git repository URL (e.g., https://github.com/user/repo.git)
21
+ :param local_path: The local directory to clone into
22
+ :return: The path to the cloned repository
23
+ :raises: Exception if cloning fails
24
+ """
25
+ try:
26
+ self._log_audit(f"Cloning started: {remote_url} -> {local_path}")
27
+ Repo.clone_from(remote_url, local_path)
28
+ self._log_audit(f"Cloning completed: {remote_url} -> {local_path}")
29
+ return local_path
30
+ except (GitCommandError, InvalidGitRepositoryError, NoSuchPathError) as e:
31
+ self._log_audit(
32
+ f"ERROR: Git operation failed: {remote_url} -> {local_path}\n{str(e)}\nTraceback:\n{traceback.format_exc()}")
33
+ raise RuntimeError(f"Git operation failed: {str(e)}") from e
34
+ except Exception as e:
35
+ self._log_audit(
36
+ f"ERROR: Unexpected error during clone: {remote_url} -> {local_path}\n{str(e)}\nTraceback:\n{traceback.format_exc()}")
37
+ raise RuntimeError(f"Unexpected error during clone: {str(e)}") from e
38
+
39
+ def _log_audit(self, message: str):
40
+ """
41
+ Append an audit log entry with timestamp.
42
+ """
43
+ timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
44
+ log_entry = f"[{timestamp}] {message}\n"
45
+ try:
46
+ with open(self.audit_log_path, "a", encoding="utf-8") as f:
47
+ f.write(log_entry)
48
+ except Exception:
49
+ # Never let audit logging failure break the flow
50
+ pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: je_editor
3
- Version: 0.0.215
3
+ Version: 0.0.216
4
4
  Summary: JEditor is basic but powerful editor include GPT
5
5
  Author-email: JE-Chen <jechenmailman@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/JE-Chen/je_editor
@@ -28,6 +28,7 @@ Requires-Dist: langchain
28
28
  Requires-Dist: pydantic
29
29
  Requires-Dist: watchdog
30
30
  Requires-Dist: ruff
31
+ Requires-Dist: gitpython
31
32
  Dynamic: license-file
32
33
 
33
34
  # je_editor
@@ -0,0 +1,127 @@
1
+ code_scan/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ code_scan/ruff_thread.py,sha256=pQjfsaQXI2ZJ6nAGq0Fpw-k3S2V_gYQ0lPgYnNC9Re0,938
3
+ code_scan/watchdog_implement.py,sha256=9N5TCvWQKi66DSrViUaNhUwP5TkYhmbz_ncS9KTGPx8,1313
4
+ code_scan/watchdog_thread.py,sha256=jHJCIsDN1kcfK4dIY5MHORafV4EJNN93x4GgNnEx1G4,833
5
+ git_client/commit_graph.py,sha256=Zg0HD-yxbyGQAOAX27QKz7enwQ9UAhNaXgXll36199o,3486
6
+ git_client/git.py,sha256=mxv-WRryIKRUONLU6V8X9ZMzIgYp76f-k3pZpqLqIbc,6253
7
+ git_client/git_cli.py,sha256=yINNHrFHQl-7zQxSk2Dhbw38EJCpOg5jbSe1ChKTVSM,2119
8
+ git_client/github.py,sha256=7ipQwkTS2ovjTe66kOyxlJth6HlEpqEZJ21PlKpKDw0,2124
9
+ je_editor-0.0.216.dist-info/licenses/LICENSE,sha256=KMhUHh6pnIUvmXDW-49L_Sz63bqkOlPDqsecaqKiitU,1091
10
+ pyside_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ pyside_ui/browser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ pyside_ui/browser/browser_download_window.py,sha256=KEO8AoZYSDuES0z5os9ziD44lS47kIo3kbd5iOUM2Go,2018
13
+ pyside_ui/browser/browser_serach_lineedit.py,sha256=GYX6faCIxl-HtRXHZoc3EAuK6Ll41jgUV3ksawY5rsc,899
14
+ pyside_ui/browser/browser_view.py,sha256=vH_-Xw1DE5RqTz-wYxkKl0X6tzpcFVKXQqMqay7tJiw,1661
15
+ pyside_ui/browser/browser_widget.py,sha256=0fVnl6ye4FBz3bnxm-0SSEWW8bGlHS5DqQmMew5CKDk,3031
16
+ pyside_ui/code/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ pyside_ui/code/running_process_manager.py,sha256=VdfdprPNlPmMySOKDOjEEl5X9IsJVPSDb0a2KDKgiQg,1008
18
+ pyside_ui/code/auto_save/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
+ pyside_ui/code/auto_save/auto_save_manager.py,sha256=hQ03TVq-c-mGku51DrjpmLtsPkVcZv4G_jaozC06Ir0,1019
20
+ pyside_ui/code/auto_save/auto_save_thread.py,sha256=V_A8_4JZdHdF9e2E_rYY_paS7rnJjB8_YVPxTyHZ_QE,1662
21
+ pyside_ui/code/code_format/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ pyside_ui/code/code_format/pep8_format.py,sha256=zrCkUdz2SG4bnMSkXfccvpHAmUOxXLedSBxL_nkdaoE,3574
23
+ pyside_ui/code/code_process/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ pyside_ui/code/code_process/code_exec.py,sha256=k0EPC2ZhC7yZTJkcezUvELIuD8AkU_GoXQcM8JL2Ptc,10686
25
+ pyside_ui/code/plaintext_code_edit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ pyside_ui/code/plaintext_code_edit/code_edit_plaintext.py,sha256=lcwmBemFenH-uDGe6fo70MS47emzPFgy9XL7cipwryY,14249
27
+ pyside_ui/code/shell_process/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ pyside_ui/code/shell_process/shell_exec.py,sha256=3x9nMRlAQdK7asoB_GD-Dx9LyrCOic8YIy0wzapmpiw,9527
29
+ pyside_ui/code/syntax/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ pyside_ui/code/syntax/python_syntax.py,sha256=9rK7_DYAANgf28PUdVl9xs42b76Re_SWwIpijEGBuzQ,3054
31
+ pyside_ui/code/syntax/syntax_setting.py,sha256=oaLRF_83Oa6bcAorf7AlZG0Mrt4FmsK7b4aA-LS_bRo,2284
32
+ pyside_ui/code/textedit_code_result/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ pyside_ui/code/textedit_code_result/code_record.py,sha256=pT-CmcADEaXLZ0ih6D6rpe1XywgszrEpcJxtJYgopJE,2162
34
+ pyside_ui/code/variable_inspector/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ pyside_ui/code/variable_inspector/inspector_gui.py,sha256=QMcwN3td5Nr01bWpUHEFJzl-wb0Ifigqx6ybfvQSzJY,4747
36
+ pyside_ui/dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ pyside_ui/dialog/ai_dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ pyside_ui/dialog/ai_dialog/set_ai_dialog.py,sha256=w_cku2dQthtk0TDSxEeDB-tvzdHV4vmUhFVDlb2ynO8,2392
39
+ pyside_ui/dialog/file_dialog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
+ pyside_ui/dialog/file_dialog/create_file_dialog.py,sha256=3NvGVld_If2TsM_Xt0pgWDTcj51szS8MxG1raU-tP2A,1650
41
+ pyside_ui/dialog/file_dialog/open_file_dialog.py,sha256=wTI4TwMfJ-960c_C0cLy2EhWbZosewSmpyWOrcf9yLE,3806
42
+ pyside_ui/dialog/file_dialog/save_file_dialog.py,sha256=pyPfwgrDiDpMKk8T_4oBukgJLxdWnzIKSziwFt4YuUU,1781
43
+ pyside_ui/dialog/search_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
+ pyside_ui/dialog/search_ui/search_error_box.py,sha256=jSa423cQW_xQ2WAj46CCWPsCP0Gy7g-UDe5o8LQyKQk,1190
45
+ pyside_ui/dialog/search_ui/search_text_box.py,sha256=ul-98FVByq_TNkkLZcBSF4VSwIQNBBj-4e6jYlh6YEA,1229
46
+ pyside_ui/git_ui/commit_table.py,sha256=5NkPx27omhtql1n0E51i5wuXqojnioxRD3SJPq-X0Xg,1136
47
+ pyside_ui/git_ui/git_branch_tree_widget.py,sha256=zCVz-_67Rtz_TtKVnzGxoX3ia8FLb2qgkpeXQthwQbc,4473
48
+ pyside_ui/git_ui/git_client_gui.py,sha256=U0bHZxUGMeebf320JX107-54jXS5s30FyPocwu2UFGs,11694
49
+ pyside_ui/git_ui/graph_view.py,sha256=0qLxZMzRZMpDFACiUlv0EyyVkAZiYFlU7d2jE5NuVIE,6045
50
+ pyside_ui/main_ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
+ pyside_ui/main_ui/main_editor.py,sha256=O5wiy5QW8IrxHAzBkcu7v4OmFraVxDIkoQhwWA032GU,11546
52
+ pyside_ui/main_ui/ai_widget/ai_config.py,sha256=Uc9UkrIeALqjmZCF5ukXtSgU1EB4xa5223gk3CfYXX4,459
53
+ pyside_ui/main_ui/ai_widget/ask_thread.py,sha256=ma5Cnwlr0RbU-sytmodcYabWueyGE8DKKISZ_0zf3Eo,571
54
+ pyside_ui/main_ui/ai_widget/chat_ui.py,sha256=yBoWXvGvgUCWjmPoBcOhj0mtOm0R42mYEs7FokYQmnE,6763
55
+ pyside_ui/main_ui/ai_widget/langchain_interface.py,sha256=OgBaUcUMR0Z2VQUO9QfCX2wJc_qBFw3IvoMCiEGBTug,1768
56
+ pyside_ui/main_ui/console_widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
+ pyside_ui/main_ui/console_widget/console_gui.py,sha256=3H0Ocj9oDtgAoH7mT2YjDsSeFW3FphsstJqtIesS4yA,5572
58
+ pyside_ui/main_ui/console_widget/qprocess_adapter.py,sha256=UIyIoCEyt6hMxc2oLH68ioERFyuPDnPub05_Yi_-rGs,2304
59
+ pyside_ui/main_ui/dock/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
+ pyside_ui/main_ui/dock/destroy_dock.py,sha256=MTN45BykNm6FA4gMW7gI4Kr9orTdcxVTm7mch3DUhaw,604
61
+ pyside_ui/main_ui/editor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
+ pyside_ui/main_ui/editor/editor_widget.py,sha256=AnrBGoToipAtxh2XRNey3wskKns6AxJ6hwtdST3fovw,10387
63
+ pyside_ui/main_ui/editor/editor_widget_dock.py,sha256=6oDf0pKx0sThlk_Qz0gduPIpasPqib6OYnMnQ4FRXFo,1930
64
+ pyside_ui/main_ui/editor/process_input.py,sha256=zj29E3He5_PD23HnIaDQ1vn3LWRECXTf8P7MWsX73k0,3401
65
+ pyside_ui/main_ui/ipython_widget/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
66
+ pyside_ui/main_ui/ipython_widget/rich_jupyter.py,sha256=LKbMbgpstsk_EiwbBSJ9Ljn7Ia3j2UNpQ_JAI6_LJ4Q,1629
67
+ pyside_ui/main_ui/menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
+ pyside_ui/main_ui/menu/set_menu_bar.py,sha256=I12DXLyRO4cKe17fQY-QDazKEIh9W36LNV1aKUw47MU,1823
69
+ pyside_ui/main_ui/menu/check_style_menu/build_check_style_menu.py,sha256=1wD6FI21Dw3IcQONuTkHP9rUPGwY70OvCtSc-ZcTKMk,3793
70
+ pyside_ui/main_ui/menu/dock_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
+ pyside_ui/main_ui/menu/dock_menu/build_dock_menu.py,sha256=Wf03lbITEifq5YywEvqsXqzLxes6R-CBsAE00giTwA0,8402
72
+ pyside_ui/main_ui/menu/file_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ pyside_ui/main_ui/menu/file_menu/build_file_menu.py,sha256=YT6kvXShRbfM4Qo51VveO_IK8uDqlNrGwHZUiM6_Tes,6881
74
+ pyside_ui/main_ui/menu/help_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
+ pyside_ui/main_ui/menu/help_menu/build_help_menu.py,sha256=iy1xDpV-u4DhZp8ZsetVKwHuJCctYgCWMpkIv12u3V0,2869
76
+ pyside_ui/main_ui/menu/language_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
+ pyside_ui/main_ui/menu/language_menu/build_language_server.py,sha256=23jKdAYxN65xJMVd5NbCwhIjwh2kfXVDszN1wmIs1A0,2333
78
+ pyside_ui/main_ui/menu/python_env_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
+ pyside_ui/main_ui/menu/python_env_menu/build_venv_menu.py,sha256=Hv_UbIwlRGlcpvDVSHjNHFslYvFawLh81raWA6zoJAY,9206
80
+ pyside_ui/main_ui/menu/run_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
+ pyside_ui/main_ui/menu/run_menu/build_run_menu.py,sha256=r1jl9FYHttRK6D09YItjVI6qsz46TA-OCV03TYOI4W8,5269
82
+ pyside_ui/main_ui/menu/run_menu/under_run_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
+ pyside_ui/main_ui/menu/run_menu/under_run_menu/build_debug_menu.py,sha256=eTkByv3TBxYuyxNWYnohWNPo2I4COxglV_GEeTsgNvk,3329
84
+ pyside_ui/main_ui/menu/run_menu/under_run_menu/build_program_menu.py,sha256=jfc7l6zYpRdtYdj2f0nydP41hxEkqCXFISLcTn_AU0E,3125
85
+ pyside_ui/main_ui/menu/run_menu/under_run_menu/build_shell_menu.py,sha256=KNRNjMyqhaimjOrwuEwtVfGD90xph0VYl25_-kUGllE,2896
86
+ pyside_ui/main_ui/menu/run_menu/under_run_menu/utils.py,sha256=zk5adQ7xidJgzaO81kKxc7LI34jCplcocL6vhacHEec,858
87
+ pyside_ui/main_ui/menu/style_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
88
+ pyside_ui/main_ui/menu/style_menu/build_style_menu.py,sha256=OjfcjQ7fA1Z1rMwIS9oGkIkC-jy-e0jdGoYo1btTqUQ,1852
89
+ pyside_ui/main_ui/menu/tab_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
+ pyside_ui/main_ui/menu/tab_menu/build_tab_menu.py,sha256=XPyNSkNM-pZJbRfpeymYooWNokv_IT36csvJfEgXP-Q,9238
91
+ pyside_ui/main_ui/menu/text_menu/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
92
+ pyside_ui/main_ui/menu/text_menu/build_text_menu.py,sha256=k3pOpK4i8ccs3D8VIrNx8g3kUDp1fZdqv9H5uEvNl5c,3558
93
+ pyside_ui/main_ui/save_settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
94
+ pyside_ui/main_ui/save_settings/setting_utils.py,sha256=p1gY71j_PgSdlibA0uOm1eIKvOlIH_jNIJ-At0x3iMU,600
95
+ pyside_ui/main_ui/save_settings/user_color_setting_file.py,sha256=o0BZRHPMUEtoXaf0ihfOEzt3doLwgSmU3gQx51cCWAg,3145
96
+ pyside_ui/main_ui/save_settings/user_setting_file.py,sha256=XkHC2BABD6xLIx7gFTy6NzjqYXFMImSngwqp9GxELdc,1034
97
+ pyside_ui/main_ui/system_tray/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
98
+ pyside_ui/main_ui/system_tray/extend_system_tray.py,sha256=xrk3TEaWdYyqc2U7po928sHV_tUj5QCdOsZEDKoU3_Y,2259
99
+ utils/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
100
+ utils/encodings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
101
+ utils/encodings/python_encodings.py,sha256=Nn_GsBZcPHcY5djoL2ki5eriKUpCI0ZgZ6XKgnEo1YI,1613
102
+ utils/exception/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
103
+ utils/exception/exception_tags.py,sha256=KjkbbqrtIuNn07657AOVEpX0RZlOpTUFxKPiHld3xgM,1104
104
+ utils/exception/exceptions.py,sha256=4e2ivqFWgBq4L_8QgRjd79xdOAgR_tyW_hpBlQeePpw,506
105
+ utils/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
106
+ utils/file/open/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
107
+ utils/file/open/open_file.py,sha256=zaqj0g87AbczCTNnVW9xPsQ2Da9pTsUwO75WIp08IPg,1211
108
+ utils/file/save/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
109
+ utils/file/save/save_file.py,sha256=j_WpSXz2M0XweuruiC332-ZXpqgPiYlZULOnEiwuUOM,998
110
+ utils/json/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
+ utils/json/json_file.py,sha256=zTzQp-VFlaXg7puxC3EgEECURuuDmfJ8I9emX0EZyfw,1699
112
+ utils/json_format/__init__.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
113
+ utils/json_format/json_process.py,sha256=tszo48OnVivVkuuPrl-FtGaaq-1YOJLKT2o2A3qQi3M,1250
114
+ utils/logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
115
+ utils/logging/loggin_instance.py,sha256=5MUuLesU_WpR_Iu1uoAATi5Bj2yMYkimZrMfiXbk5pI,881
116
+ utils/multi_language/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
117
+ utils/multi_language/english.py,sha256=OJc8VEh4PhMQhdHz7_aWSzIdNoL2vJIK-uPQw0ZOM1g,9974
118
+ utils/multi_language/multi_language_wrapper.py,sha256=_MtYrE51poQ1p_iWvT2eg_604uj6IsiBgGpifWpYLGc,1050
119
+ utils/multi_language/traditional_chinese.py,sha256=i-OYyq0ywYo8e3YqYQNoX5lAs38j6W3xeg47qOJ7MGU,10052
120
+ utils/redirect_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
121
+ utils/redirect_manager/redirect_manager_class.py,sha256=1gICetKpohBvmxmVhnqeCRq7AQS2YWK4AURmrqnVYVw,2277
122
+ utils/venv_check/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
123
+ utils/venv_check/check_venv.py,sha256=oCrMdue4NYUUGrVifh_iHFwIgxVx9azYN4jz3Xiulgg,999
124
+ je_editor-0.0.216.dist-info/METADATA,sha256=LCghqFMAv91NyxBQ5TiFtlTXPPAHZu1Q1DLEgrts_4s,3472
125
+ je_editor-0.0.216.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
126
+ je_editor-0.0.216.dist-info/top_level.txt,sha256=Sj3iPnIESliaN1BbNN4PibPXeNQabxXhcqnNP-SfGTE,37
127
+ je_editor-0.0.216.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ code_scan
2
+ git_client
3
+ pyside_ui
4
+ utils
@@ -0,0 +1,27 @@
1
+ from PySide6.QtGui import QStandardItemModel, QStandardItem
2
+ from PySide6.QtWidgets import QTableView
3
+
4
+
5
+ class CommitTable(QTableView):
6
+ def __init__(self, parent=None):
7
+ super().__init__(parent)
8
+ self.model_data = QStandardItemModel(0, 5, self) # 多一欄
9
+ self.model_data.setHorizontalHeaderLabels(["#", "SHA", "Message", "Author", "Date"])
10
+ self.setModel(self.model_data)
11
+ self.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows)
12
+ self.setEditTriggers(QTableView.EditTrigger.NoEditTriggers)
13
+ self.horizontalHeader().setStretchLastSection(True)
14
+
15
+ def set_commits(self, commits):
16
+ self.model_data.setRowCount(0)
17
+ for idx, c in enumerate(commits, start=1):
18
+ row = [
19
+ QStandardItem(str(idx)), # 行號
20
+ QStandardItem(c["sha"][:7]),
21
+ QStandardItem(c["message"]),
22
+ QStandardItem(c["author"]),
23
+ QStandardItem(c["date"]),
24
+ ]
25
+ for item in row:
26
+ item.setEditable(False)
27
+ self.model_data.appendRow(row)