xb-init 2.0.2__tar.gz → 2.0.4__tar.gz
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.
- {xb_init-2.0.2 → xb_init-2.0.4}/PKG-INFO +1 -1
- {xb_init-2.0.2 → xb_init-2.0.4}/pyproject.toml +1 -1
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/root/dev.py.j2 +4 -0
- xb_init-2.0.4/xb/templates/version/scripts/gui_version_manager.py.j2 +466 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/version/scripts/install_hooks.py.j2 +14 -14
- xb_init-2.0.2/xb/templates/version/scripts/gui_version_manager.py.j2 +0 -181
- {xb_init-2.0.2 → xb_init-2.0.4}/.claude/settings.local.json +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/.codegraph/.gitignore +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/.codegraph/codegraph.db +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/.gitignore +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/README.md +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/docs/screenshot.png +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/tests/test_cli.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/tests/test_upgrade.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/uv.lock +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/__init__.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/cli.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/commands/__init__.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/commands/build.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/commands/dev.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/commands/doctor.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/commands/init.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/commands/upgrade.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/commands/version.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/api/__init__.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/api/config.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/api/ports.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/api/terminal/__init__.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/api/terminal/routes.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/backend_build.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/main.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/managers/__init__.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/managers/logger_manager.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/managers/path_manager.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/managers/sudoers_manager.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/backend/requirements.txt.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/configs/global_config.yaml.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/configs/secrets.yaml.example.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/configs/secrets.yaml.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/.npmrc.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/launcher.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/main.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/package.json.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/port_diagnostics.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/preload.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/prepare.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/resources/postinst.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/resources/postrm.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/electron/resources/uninstaller.nsh.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/.npmrc.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/index.html.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/package.json.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/App.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/ConfigSetup.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/DashboardHome.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/FileManager.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/GitVersionBadge.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/RefreshButton.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/TerminalPanel.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/TerminalView.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/components/ThemeToggle.vue.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/composables/useTerminalSessions.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/main.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/style.css.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/vite.config.js.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/root/.gitignore.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/root/AGENTS.md.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/root/README.md.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/root/build.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/root/pyproject.toml.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/scripts/example.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/version/hooks/pre-commit.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/version/scripts/version_manager.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/version/scripts/web_version_manager.py.j2 +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/utils/__init__.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/utils/click_helpers.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/utils/template_engine.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/utils/validators.py +0 -0
- {xb_init-2.0.2 → xb_init-2.0.4}/xb/utils/version_check.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: xb-init
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.4
|
|
4
4
|
Summary: Project management tool for UV + FastAPI + Vue3 + Electron desktop applications
|
|
5
5
|
Project-URL: Homepage, https://github.com/yourusername/xb
|
|
6
6
|
Project-URL: Documentation, https://github.com/yourusername/xb#readme
|
|
@@ -131,7 +131,11 @@ def port_is_free(port: int) -> bool:
|
|
|
131
131
|
import socket
|
|
132
132
|
with socket.socket() as sock:
|
|
133
133
|
try:
|
|
134
|
+
# 服务关闭后 TCP 连接可能短暂保留在 TIME_WAIT。复用地址后再 listen,
|
|
135
|
+
# 可区分该回收状态与真正仍在监听的进程。
|
|
136
|
+
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
134
137
|
sock.bind(("127.0.0.1", port))
|
|
138
|
+
sock.listen(1)
|
|
135
139
|
return True
|
|
136
140
|
except OSError:
|
|
137
141
|
return False
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
import tkinter as tk
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from tkinter import font as tkfont
|
|
11
|
+
from tkinter import messagebox
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GuiVersionManager:
|
|
15
|
+
WINDOW_WIDTH = 520
|
|
16
|
+
WINDOW_HEIGHT = 560
|
|
17
|
+
|
|
18
|
+
BACKGROUND = "#f4f6fa"
|
|
19
|
+
SURFACE = "#ffffff"
|
|
20
|
+
SURFACE_MUTED = "#f8fafc"
|
|
21
|
+
BORDER = "#dbe2ea"
|
|
22
|
+
TEXT = "#172033"
|
|
23
|
+
TEXT_MUTED = "#64748b"
|
|
24
|
+
ACCENT = "#2563eb"
|
|
25
|
+
|
|
26
|
+
VERSION_OPTIONS = (
|
|
27
|
+
{
|
|
28
|
+
"value": "patch",
|
|
29
|
+
"name": "修订版本",
|
|
30
|
+
"formula": "x.y.z -> x.y.(z+1)",
|
|
31
|
+
"description": "修复问题,保持现有功能兼容。",
|
|
32
|
+
"color": "#2563eb",
|
|
33
|
+
"tint": "#eff6ff",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"value": "minor",
|
|
37
|
+
"name": "次要版本",
|
|
38
|
+
"formula": "x.y.z -> x.(y+1).0",
|
|
39
|
+
"description": "加入向后兼容的新功能。",
|
|
40
|
+
"color": "#059669",
|
|
41
|
+
"tint": "#ecfdf5",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"value": "major",
|
|
45
|
+
"name": "主要版本",
|
|
46
|
+
"formula": "x.y.z -> (x+1).0.0",
|
|
47
|
+
"description": "包含不兼容的重大变更。",
|
|
48
|
+
"color": "#dc2626",
|
|
49
|
+
"tint": "#fef2f2",
|
|
50
|
+
},
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def __init__(self):
|
|
54
|
+
self.project_root = Path(__file__).parent.parent.parent
|
|
55
|
+
self.electron_pkg_path = self.project_root / "electron" / "package.json"
|
|
56
|
+
self.option_widgets = []
|
|
57
|
+
|
|
58
|
+
self.root = tk.Tk()
|
|
59
|
+
self.root.title("版本管理")
|
|
60
|
+
self.root.configure(bg=self.BACKGROUND)
|
|
61
|
+
self.root.resizable(False, False)
|
|
62
|
+
|
|
63
|
+
self.font_family = self.resolve_font_family()
|
|
64
|
+
self.configure_default_fonts()
|
|
65
|
+
self.fonts = self.create_fonts()
|
|
66
|
+
|
|
67
|
+
self.root.protocol("WM_DELETE_WINDOW", self.cancel_commit)
|
|
68
|
+
self.create_ui()
|
|
69
|
+
self.center_window()
|
|
70
|
+
|
|
71
|
+
def resolve_font_family(self) -> str:
|
|
72
|
+
"""Prefer a CJK sans-serif font that is actually installed on this host."""
|
|
73
|
+
installed_fonts = set(tkfont.families(self.root))
|
|
74
|
+
preferred_fonts = (
|
|
75
|
+
"Noto Sans CJK SC",
|
|
76
|
+
"Noto Sans SC",
|
|
77
|
+
"Source Han Sans SC",
|
|
78
|
+
"WenQuanYi Micro Hei",
|
|
79
|
+
"Microsoft YaHei",
|
|
80
|
+
"PingFang SC",
|
|
81
|
+
"Arial Unicode MS",
|
|
82
|
+
)
|
|
83
|
+
for family in preferred_fonts:
|
|
84
|
+
if family in installed_fonts:
|
|
85
|
+
return family
|
|
86
|
+
return tkfont.nametofont("TkDefaultFont").cget("family")
|
|
87
|
+
|
|
88
|
+
def configure_default_fonts(self) -> None:
|
|
89
|
+
"""Use the selected font in native dialogs and standard Tk controls too."""
|
|
90
|
+
for font_name in ("TkDefaultFont", "TkTextFont", "TkMenuFont", "TkHeadingFont"):
|
|
91
|
+
try:
|
|
92
|
+
tkfont.nametofont(font_name).configure(family=self.font_family)
|
|
93
|
+
except tk.TclError:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
def create_fonts(self):
|
|
97
|
+
return {
|
|
98
|
+
"title": tkfont.Font(root=self.root, family=self.font_family, size=17, weight="bold"),
|
|
99
|
+
"subtitle": tkfont.Font(root=self.root, family=self.font_family, size=9),
|
|
100
|
+
"eyebrow": tkfont.Font(root=self.root, family=self.font_family, size=9, weight="bold"),
|
|
101
|
+
"version": tkfont.Font(root=self.root, family=self.font_family, size=18, weight="bold"),
|
|
102
|
+
"section": tkfont.Font(root=self.root, family=self.font_family, size=11, weight="bold"),
|
|
103
|
+
"option_title": tkfont.Font(root=self.root, family=self.font_family, size=11, weight="bold"),
|
|
104
|
+
"option_detail": tkfont.Font(root=self.root, family=self.font_family, size=9),
|
|
105
|
+
"button": tkfont.Font(root=self.root, family=self.font_family, size=10, weight="bold"),
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
def center_window(self):
|
|
109
|
+
self.root.update_idletasks()
|
|
110
|
+
window_height = max(self.WINDOW_HEIGHT, self.root.winfo_reqheight())
|
|
111
|
+
x = (self.root.winfo_screenwidth() - self.WINDOW_WIDTH) // 2
|
|
112
|
+
y = max(0, (self.root.winfo_screenheight() - window_height) // 2)
|
|
113
|
+
self.root.geometry(f"{self.WINDOW_WIDTH}x{window_height}+{x}+{y}")
|
|
114
|
+
self.root.minsize(self.WINDOW_WIDTH, window_height)
|
|
115
|
+
|
|
116
|
+
def create_ui(self):
|
|
117
|
+
content = tk.Frame(self.root, bg=self.SURFACE)
|
|
118
|
+
content.pack(fill=tk.BOTH, expand=True)
|
|
119
|
+
content.columnconfigure(0, weight=1)
|
|
120
|
+
|
|
121
|
+
self.create_header(content)
|
|
122
|
+
self.create_version_summary(content)
|
|
123
|
+
|
|
124
|
+
separator = tk.Frame(content, bg="#e9eef5", height=1)
|
|
125
|
+
separator.grid(row=2, column=0, sticky=(tk.W, tk.E), padx=34, pady=(0, 14))
|
|
126
|
+
|
|
127
|
+
tk.Label(
|
|
128
|
+
content,
|
|
129
|
+
text="选择更新类型",
|
|
130
|
+
bg=self.SURFACE,
|
|
131
|
+
fg=self.TEXT,
|
|
132
|
+
font=self.fonts["section"],
|
|
133
|
+
).grid(row=3, column=0, sticky=tk.W, padx=34)
|
|
134
|
+
|
|
135
|
+
tk.Label(
|
|
136
|
+
content,
|
|
137
|
+
text="根据本次变更选择要发布的版本级别",
|
|
138
|
+
bg=self.SURFACE,
|
|
139
|
+
fg=self.TEXT_MUTED,
|
|
140
|
+
font=self.fonts["subtitle"],
|
|
141
|
+
).grid(row=4, column=0, sticky=tk.W, padx=34, pady=(3, 9))
|
|
142
|
+
|
|
143
|
+
self.version_type = tk.StringVar(value="patch")
|
|
144
|
+
self.version_type.trace_add("write", self.update_option_styles)
|
|
145
|
+
|
|
146
|
+
options_frame = tk.Frame(content, bg=self.SURFACE)
|
|
147
|
+
options_frame.grid(row=5, column=0, sticky=(tk.W, tk.E), padx=34)
|
|
148
|
+
options_frame.columnconfigure(0, weight=1)
|
|
149
|
+
for row, option in enumerate(self.VERSION_OPTIONS):
|
|
150
|
+
self.create_version_option(options_frame, row, option)
|
|
151
|
+
|
|
152
|
+
action_area = tk.Frame(content, bg=self.SURFACE_MUTED, height=74)
|
|
153
|
+
action_area.grid(row=6, column=0, sticky=(tk.W, tk.E, tk.S), pady=(12, 0))
|
|
154
|
+
action_area.grid_propagate(False)
|
|
155
|
+
action_area.columnconfigure(0, weight=1)
|
|
156
|
+
|
|
157
|
+
button_frame = tk.Frame(action_area, bg=self.SURFACE_MUTED)
|
|
158
|
+
button_frame.grid(row=0, column=0, sticky=tk.E, padx=34, pady=14)
|
|
159
|
+
self.create_button(
|
|
160
|
+
button_frame,
|
|
161
|
+
text="更新版本",
|
|
162
|
+
command=self.update_version,
|
|
163
|
+
background=self.ACCENT,
|
|
164
|
+
active_background="#1d4ed8",
|
|
165
|
+
foreground="#ffffff",
|
|
166
|
+
width=13,
|
|
167
|
+
).grid(row=0, column=0, padx=(0, 8))
|
|
168
|
+
self.create_button(
|
|
169
|
+
button_frame,
|
|
170
|
+
text="跳过",
|
|
171
|
+
command=self.skip_version_update,
|
|
172
|
+
background="#e2e8f0",
|
|
173
|
+
active_background="#cbd5e1",
|
|
174
|
+
foreground="#334155",
|
|
175
|
+
width=8,
|
|
176
|
+
).grid(row=0, column=1, padx=(0, 8))
|
|
177
|
+
self.create_button(
|
|
178
|
+
button_frame,
|
|
179
|
+
text="取消",
|
|
180
|
+
command=self.cancel_commit,
|
|
181
|
+
background=self.SURFACE_MUTED,
|
|
182
|
+
active_background="#e2e8f0",
|
|
183
|
+
foreground="#64748b",
|
|
184
|
+
width=8,
|
|
185
|
+
).grid(row=0, column=2)
|
|
186
|
+
|
|
187
|
+
self.update_option_styles()
|
|
188
|
+
|
|
189
|
+
def create_header(self, parent: tk.Misc) -> None:
|
|
190
|
+
header = tk.Frame(parent, bg=self.SURFACE)
|
|
191
|
+
header.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=34, pady=(20, 12))
|
|
192
|
+
header.columnconfigure(1, weight=1)
|
|
193
|
+
|
|
194
|
+
accent = tk.Frame(header, bg="#ef4444", width=4, height=36)
|
|
195
|
+
accent.grid(row=0, column=0, rowspan=2, sticky=tk.NS, padx=(0, 13))
|
|
196
|
+
accent.grid_propagate(False)
|
|
197
|
+
|
|
198
|
+
tk.Label(
|
|
199
|
+
header,
|
|
200
|
+
text="版本管理",
|
|
201
|
+
bg=self.SURFACE,
|
|
202
|
+
fg=self.TEXT,
|
|
203
|
+
font=self.fonts["title"],
|
|
204
|
+
).grid(row=0, column=1, sticky=tk.W)
|
|
205
|
+
tk.Label(
|
|
206
|
+
header,
|
|
207
|
+
text="为本次提交选择语义化版本更新",
|
|
208
|
+
bg=self.SURFACE,
|
|
209
|
+
fg=self.TEXT_MUTED,
|
|
210
|
+
font=self.fonts["subtitle"],
|
|
211
|
+
).grid(row=1, column=1, sticky=tk.W, pady=(2, 0))
|
|
212
|
+
|
|
213
|
+
def create_version_summary(self, parent: tk.Misc) -> None:
|
|
214
|
+
summary = tk.Frame(
|
|
215
|
+
parent,
|
|
216
|
+
bg="#f8fafc",
|
|
217
|
+
highlightbackground="#e2e8f0",
|
|
218
|
+
highlightthickness=1,
|
|
219
|
+
)
|
|
220
|
+
summary.grid(row=1, column=0, sticky=(tk.W, tk.E), padx=34, pady=(0, 14))
|
|
221
|
+
summary.columnconfigure(1, weight=1)
|
|
222
|
+
|
|
223
|
+
tk.Label(
|
|
224
|
+
summary,
|
|
225
|
+
text="项目",
|
|
226
|
+
bg="#f8fafc",
|
|
227
|
+
fg=self.TEXT_MUTED,
|
|
228
|
+
font=self.fonts["eyebrow"],
|
|
229
|
+
).grid(row=0, column=0, sticky=tk.W, padx=(16, 24), pady=(10, 0))
|
|
230
|
+
tk.Label(
|
|
231
|
+
summary,
|
|
232
|
+
text="当前版本",
|
|
233
|
+
bg="#f8fafc",
|
|
234
|
+
fg=self.TEXT_MUTED,
|
|
235
|
+
font=self.fonts["eyebrow"],
|
|
236
|
+
).grid(row=0, column=1, sticky=tk.W, pady=(10, 0))
|
|
237
|
+
|
|
238
|
+
project_name = "[[ package_name]]"
|
|
239
|
+
tk.Label(
|
|
240
|
+
summary,
|
|
241
|
+
text=project_name,
|
|
242
|
+
bg="#f8fafc",
|
|
243
|
+
fg=self.TEXT,
|
|
244
|
+
font=self.fonts["option_title"],
|
|
245
|
+
anchor=tk.W,
|
|
246
|
+
justify=tk.LEFT,
|
|
247
|
+
wraplength=220,
|
|
248
|
+
).grid(row=1, column=0, sticky=(tk.W, tk.E), padx=(16, 24), pady=(2, 10))
|
|
249
|
+
tk.Label(
|
|
250
|
+
summary,
|
|
251
|
+
text=self.get_current_version(),
|
|
252
|
+
bg="#f8fafc",
|
|
253
|
+
fg=self.TEXT,
|
|
254
|
+
font=self.fonts["version"],
|
|
255
|
+
).grid(row=1, column=1, sticky=tk.W, pady=(0, 8))
|
|
256
|
+
|
|
257
|
+
def create_version_option(self, parent: tk.Misc, row: int, option) -> None:
|
|
258
|
+
card = tk.Frame(
|
|
259
|
+
parent,
|
|
260
|
+
bg=self.SURFACE,
|
|
261
|
+
highlightbackground=self.BORDER,
|
|
262
|
+
highlightcolor=option["color"],
|
|
263
|
+
highlightthickness=1,
|
|
264
|
+
cursor="hand2",
|
|
265
|
+
takefocus=True,
|
|
266
|
+
height=60,
|
|
267
|
+
)
|
|
268
|
+
card.grid(row=row, column=0, sticky=(tk.W, tk.E), pady=(0, 7))
|
|
269
|
+
card.grid_propagate(False)
|
|
270
|
+
card.columnconfigure(2, weight=1)
|
|
271
|
+
|
|
272
|
+
color_bar = tk.Frame(card, bg=option["color"], width=5)
|
|
273
|
+
color_bar.grid(row=0, column=0, rowspan=2, sticky=tk.NS)
|
|
274
|
+
color_bar.grid_propagate(False)
|
|
275
|
+
|
|
276
|
+
title = tk.Label(
|
|
277
|
+
card,
|
|
278
|
+
text=option["name"],
|
|
279
|
+
bg=self.SURFACE,
|
|
280
|
+
fg=self.TEXT,
|
|
281
|
+
font=self.fonts["option_title"],
|
|
282
|
+
cursor="hand2",
|
|
283
|
+
)
|
|
284
|
+
title.grid(row=0, column=1, sticky=tk.SW, padx=(16, 14), pady=(8, 0))
|
|
285
|
+
|
|
286
|
+
description = tk.Label(
|
|
287
|
+
card,
|
|
288
|
+
text=option["description"],
|
|
289
|
+
bg=self.SURFACE,
|
|
290
|
+
fg=self.TEXT_MUTED,
|
|
291
|
+
font=self.fonts["option_detail"],
|
|
292
|
+
cursor="hand2",
|
|
293
|
+
)
|
|
294
|
+
description.grid(row=1, column=1, sticky=tk.NW, padx=(16, 14), pady=(1, 8))
|
|
295
|
+
|
|
296
|
+
formula = tk.Label(
|
|
297
|
+
card,
|
|
298
|
+
text=option["formula"],
|
|
299
|
+
bg=self.SURFACE,
|
|
300
|
+
fg="#475569",
|
|
301
|
+
font=self.fonts["option_detail"],
|
|
302
|
+
cursor="hand2",
|
|
303
|
+
)
|
|
304
|
+
formula.grid(row=0, column=2, sticky=tk.SE, padx=(6, 14), pady=(8, 0))
|
|
305
|
+
|
|
306
|
+
selected = tk.Label(
|
|
307
|
+
card,
|
|
308
|
+
text="",
|
|
309
|
+
bg=self.SURFACE,
|
|
310
|
+
fg=option["color"],
|
|
311
|
+
font=self.fonts["eyebrow"],
|
|
312
|
+
cursor="hand2",
|
|
313
|
+
)
|
|
314
|
+
selected.grid(row=1, column=2, sticky=tk.NE, padx=(6, 14), pady=(1, 8))
|
|
315
|
+
|
|
316
|
+
for widget in (card, color_bar, title, description, formula, selected):
|
|
317
|
+
widget.bind("<Button-1>", lambda _event, value=option["value"]: self.select_option(value))
|
|
318
|
+
card.bind("<Return>", lambda _event, value=option["value"]: self.select_option(value))
|
|
319
|
+
card.bind("<space>", lambda _event, value=option["value"]: self.select_option(value))
|
|
320
|
+
|
|
321
|
+
self.option_widgets.append(
|
|
322
|
+
{
|
|
323
|
+
"value": option["value"],
|
|
324
|
+
"color": option["color"],
|
|
325
|
+
"tint": option["tint"],
|
|
326
|
+
"card": card,
|
|
327
|
+
"title": title,
|
|
328
|
+
"description": description,
|
|
329
|
+
"formula": formula,
|
|
330
|
+
"selected": selected,
|
|
331
|
+
}
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
def select_option(self, value: str) -> None:
|
|
335
|
+
self.version_type.set(value)
|
|
336
|
+
|
|
337
|
+
def update_option_styles(self, *_args) -> None:
|
|
338
|
+
selected_value = self.version_type.get()
|
|
339
|
+
for option in self.option_widgets:
|
|
340
|
+
is_selected = option["value"] == selected_value
|
|
341
|
+
background = option["tint"] if is_selected else self.SURFACE
|
|
342
|
+
option["card"].configure(
|
|
343
|
+
bg=background,
|
|
344
|
+
highlightbackground=option["color"] if is_selected else self.BORDER,
|
|
345
|
+
)
|
|
346
|
+
for widget in (option["title"], option["description"], option["formula"], option["selected"]):
|
|
347
|
+
widget.configure(bg=background)
|
|
348
|
+
option["title"].configure(fg=option["color"] if is_selected else self.TEXT)
|
|
349
|
+
option["formula"].configure(fg=option["color"] if is_selected else "#475569")
|
|
350
|
+
option["selected"].configure(text="已选择" if is_selected else "")
|
|
351
|
+
|
|
352
|
+
def create_button(
|
|
353
|
+
self,
|
|
354
|
+
parent: tk.Misc,
|
|
355
|
+
text: str,
|
|
356
|
+
command,
|
|
357
|
+
background: str,
|
|
358
|
+
active_background: str,
|
|
359
|
+
foreground: str,
|
|
360
|
+
width: int,
|
|
361
|
+
) -> tk.Button:
|
|
362
|
+
return tk.Button(
|
|
363
|
+
parent,
|
|
364
|
+
text=text,
|
|
365
|
+
command=command,
|
|
366
|
+
bg=background,
|
|
367
|
+
fg=foreground,
|
|
368
|
+
activebackground=active_background,
|
|
369
|
+
activeforeground=foreground,
|
|
370
|
+
relief=tk.FLAT,
|
|
371
|
+
overrelief=tk.FLAT,
|
|
372
|
+
borderwidth=0,
|
|
373
|
+
highlightthickness=0,
|
|
374
|
+
padx=12,
|
|
375
|
+
pady=8,
|
|
376
|
+
width=width,
|
|
377
|
+
font=self.fonts["button"],
|
|
378
|
+
cursor="hand2",
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
def get_current_version(self) -> str:
|
|
382
|
+
try:
|
|
383
|
+
if self.electron_pkg_path.exists():
|
|
384
|
+
with open(self.electron_pkg_path, "r", encoding="utf-8") as f:
|
|
385
|
+
pkg_data = json.load(f)
|
|
386
|
+
return pkg_data.get("version", "N/A")
|
|
387
|
+
return "N/A"
|
|
388
|
+
except Exception:
|
|
389
|
+
return "N/A"
|
|
390
|
+
|
|
391
|
+
def update_version(self):
|
|
392
|
+
version_type = self.version_type.get()
|
|
393
|
+
confirm_msg = (
|
|
394
|
+
f"确认更新版本?\n\n版本类型: {version_type}\n将更新 electron/package.json"
|
|
395
|
+
)
|
|
396
|
+
|
|
397
|
+
if not messagebox.askyesno("确认更新", confirm_msg):
|
|
398
|
+
return
|
|
399
|
+
|
|
400
|
+
try:
|
|
401
|
+
cmd = [
|
|
402
|
+
sys.executable,
|
|
403
|
+
str(self.project_root / "version" / "scripts" / "version_manager.py"),
|
|
404
|
+
version_type,
|
|
405
|
+
]
|
|
406
|
+
|
|
407
|
+
# 子进程输出统一走 UTF-8:Windows 下管道默认 GBK,会让 ✓/❌ 编码失败。
|
|
408
|
+
env = os.environ.copy()
|
|
409
|
+
env["PYTHONIOENCODING"] = "utf-8"
|
|
410
|
+
env["PYTHONUTF8"] = "1"
|
|
411
|
+
result = subprocess.run(
|
|
412
|
+
cmd,
|
|
413
|
+
capture_output=True,
|
|
414
|
+
text=True,
|
|
415
|
+
cwd=self.project_root,
|
|
416
|
+
env=env,
|
|
417
|
+
encoding="utf-8",
|
|
418
|
+
errors="replace",
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
if result.returncode == 0:
|
|
422
|
+
subprocess.run(
|
|
423
|
+
["git", "add", "electron/package.json", "pyproject.toml"],
|
|
424
|
+
cwd=self.project_root,
|
|
425
|
+
check=True,
|
|
426
|
+
)
|
|
427
|
+
new_version = self.get_current_version()
|
|
428
|
+
messagebox.showinfo("成功", f"版本更新成功!\n新版本: {new_version}")
|
|
429
|
+
self.root.destroy()
|
|
430
|
+
else:
|
|
431
|
+
detail = (result.stderr or result.stdout or "").strip()
|
|
432
|
+
messagebox.showerror("错误", f"版本更新失败:\n{detail}")
|
|
433
|
+
except Exception as e:
|
|
434
|
+
messagebox.showerror("错误", f"更新版本时发生错误: {e}")
|
|
435
|
+
|
|
436
|
+
def cancel_commit(self):
|
|
437
|
+
try:
|
|
438
|
+
if messagebox.askyesno(
|
|
439
|
+
"确认取消", "确定要取消本次提交吗?\n这将中断 Git 提交流程。"
|
|
440
|
+
):
|
|
441
|
+
self.root.destroy()
|
|
442
|
+
sys.exit(1)
|
|
443
|
+
except tk.TclError:
|
|
444
|
+
sys.exit(1)
|
|
445
|
+
|
|
446
|
+
def skip_version_update(self):
|
|
447
|
+
try:
|
|
448
|
+
if messagebox.askyesno(
|
|
449
|
+
"确认跳过", "确定要跳过版本更新吗?\n将直接提交代码。"
|
|
450
|
+
):
|
|
451
|
+
self.root.destroy()
|
|
452
|
+
sys.exit(0)
|
|
453
|
+
except tk.TclError:
|
|
454
|
+
sys.exit(0)
|
|
455
|
+
|
|
456
|
+
def run(self):
|
|
457
|
+
self.root.mainloop()
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
def main():
|
|
461
|
+
app = GuiVersionManager()
|
|
462
|
+
app.run()
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
if __name__ == "__main__":
|
|
466
|
+
main()
|
|
@@ -14,15 +14,15 @@ if not (root / ".git").exists():
|
|
|
14
14
|
raise SystemExit("不是 Git 仓库")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def check_dependency(command: str, package: str) -> None:
|
|
18
|
-
"""检查依赖;Ubuntu 可选地尝试通过 apt 安装,Windows 不执行 apt。"""
|
|
19
|
-
if shutil.which(command):
|
|
20
|
-
return
|
|
21
|
-
if sys.platform.startswith("linux") and shutil.which("apt-get"):
|
|
22
|
-
subprocess.run(["sudo", "apt-get", "install", "-y", package], check=False)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def interpreter_path() -> str:
|
|
17
|
+
def check_dependency(command: str, package: str) -> None:
|
|
18
|
+
"""检查依赖;Ubuntu 可选地尝试通过 apt 安装,Windows 不执行 apt。"""
|
|
19
|
+
if shutil.which(command):
|
|
20
|
+
return
|
|
21
|
+
if sys.platform.startswith("linux") and shutil.which("apt-get"):
|
|
22
|
+
subprocess.run(["sudo", "apt-get", "install", "-y", package], check=False)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def interpreter_path() -> str:
|
|
26
26
|
"""优先使用项目虚拟环境的解释器;未创建时退回当前解释器。"""
|
|
27
27
|
venv = root / (".venv/Scripts/python.exe" if os.name == "nt" else ".venv/bin/python")
|
|
28
28
|
python = venv if venv.exists() else Path(sys.executable)
|
|
@@ -33,11 +33,11 @@ def interpreter_path() -> str:
|
|
|
33
33
|
check_dependency("sshpass", "sshpass")
|
|
34
34
|
try:
|
|
35
35
|
__import__("tkinter")
|
|
36
|
-
except ImportError:
|
|
37
|
-
if sys.platform.startswith("linux") and shutil.which("apt-get"):
|
|
38
|
-
subprocess.run(["sudo", "apt-get", "install", "-y", "python3-tk"], check=False)
|
|
39
|
-
|
|
40
|
-
target_dir.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
except ImportError:
|
|
37
|
+
if sys.platform.startswith("linux") and shutil.which("apt-get"):
|
|
38
|
+
subprocess.run(["sudo", "apt-get", "install", "-y", "python3-tk"], check=False)
|
|
39
|
+
|
|
40
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
41
41
|
python_command = interpreter_path()
|
|
42
42
|
for hook in sorted(source_dir.iterdir()):
|
|
43
43
|
if not (hook.is_file() and hook.suffix == ".py"):
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import sys
|
|
6
|
-
import json
|
|
7
|
-
import subprocess
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
import tkinter as tk
|
|
10
|
-
from tkinter import ttk, messagebox
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class GuiVersionManager:
|
|
14
|
-
def __init__(self):
|
|
15
|
-
self.project_root = Path(__file__).parent.parent.parent
|
|
16
|
-
self.electron_pkg_path = self.project_root / "electron" / "package.json"
|
|
17
|
-
|
|
18
|
-
self.root = tk.Tk()
|
|
19
|
-
self.root.title("🔖 版本管理")
|
|
20
|
-
self.root.resizable(True, True)
|
|
21
|
-
|
|
22
|
-
self.root.protocol("WM_DELETE_WINDOW", self.cancel_commit)
|
|
23
|
-
self.create_ui()
|
|
24
|
-
self.center_window()
|
|
25
|
-
|
|
26
|
-
def center_window(self):
|
|
27
|
-
self.root.update_idletasks()
|
|
28
|
-
w = self.root.winfo_reqwidth()
|
|
29
|
-
h = self.root.winfo_reqheight()
|
|
30
|
-
x = (self.root.winfo_screenwidth() // 2) - (w // 2)
|
|
31
|
-
y = (self.root.winfo_screenheight() // 2) - (h // 2)
|
|
32
|
-
self.root.geometry(f"{w}x{h}+{x}+{y}")
|
|
33
|
-
self.root.minsize(w, h)
|
|
34
|
-
|
|
35
|
-
def create_ui(self):
|
|
36
|
-
main_frame = ttk.Frame(self.root, padding="30")
|
|
37
|
-
main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
|
|
38
|
-
|
|
39
|
-
ttk.Label(main_frame, text="🔖 版本管理", font=("Arial", 18, "bold")).grid(
|
|
40
|
-
row=0, column=0, pady=(0, 10)
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
current_version = self.get_current_version()
|
|
44
|
-
ttk.Label(
|
|
45
|
-
main_frame,
|
|
46
|
-
text=f"[[ package_name]] 版本: {current_version}",
|
|
47
|
-
font=("Arial", 12),
|
|
48
|
-
foreground="#666",
|
|
49
|
-
).grid(row=1, column=0, pady=(0, 30))
|
|
50
|
-
|
|
51
|
-
ttk.Label(main_frame, text="选择版本类型:", font=("Arial", 11, "bold")).grid(
|
|
52
|
-
row=2, column=0, sticky=tk.W, pady=(0, 15)
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
self.version_type = tk.StringVar(value="patch")
|
|
56
|
-
|
|
57
|
-
type_frame = ttk.Frame(main_frame)
|
|
58
|
-
type_frame.grid(row=3, column=0, sticky=(tk.W, tk.E), pady=(0, 30))
|
|
59
|
-
|
|
60
|
-
ttk.Radiobutton(
|
|
61
|
-
type_frame,
|
|
62
|
-
text="🔧 Patch (x.y.z+1) - Bug修复",
|
|
63
|
-
variable=self.version_type,
|
|
64
|
-
value="patch",
|
|
65
|
-
).grid(row=0, column=0, sticky=tk.W, pady=5)
|
|
66
|
-
|
|
67
|
-
ttk.Radiobutton(
|
|
68
|
-
type_frame,
|
|
69
|
-
text="✨ Minor (x.y+1.0) - 新功能",
|
|
70
|
-
variable=self.version_type,
|
|
71
|
-
value="minor",
|
|
72
|
-
).grid(row=1, column=0, sticky=tk.W, pady=5)
|
|
73
|
-
|
|
74
|
-
ttk.Radiobutton(
|
|
75
|
-
type_frame,
|
|
76
|
-
text="🎉 Major (x+1.0.0) - 重大更新",
|
|
77
|
-
variable=self.version_type,
|
|
78
|
-
value="major",
|
|
79
|
-
).grid(row=2, column=0, sticky=tk.W, pady=5)
|
|
80
|
-
|
|
81
|
-
button_frame = ttk.Frame(main_frame)
|
|
82
|
-
button_frame.grid(row=4, column=0, pady=(20, 0))
|
|
83
|
-
|
|
84
|
-
ttk.Button(
|
|
85
|
-
button_frame, text="🚀 更新版本", command=self.update_version, width=15
|
|
86
|
-
).grid(row=0, column=0, padx=5)
|
|
87
|
-
|
|
88
|
-
ttk.Button(
|
|
89
|
-
button_frame, text="⏭️ 跳过", command=self.skip_version_update, width=15
|
|
90
|
-
).grid(row=0, column=1, padx=5)
|
|
91
|
-
|
|
92
|
-
ttk.Button(
|
|
93
|
-
button_frame, text="❌ 取消", command=self.cancel_commit, width=15
|
|
94
|
-
).grid(row=0, column=2, padx=5)
|
|
95
|
-
|
|
96
|
-
self.root.columnconfigure(0, weight=1)
|
|
97
|
-
self.root.rowconfigure(0, weight=1)
|
|
98
|
-
main_frame.columnconfigure(0, weight=1)
|
|
99
|
-
main_frame.rowconfigure(3, weight=1)
|
|
100
|
-
|
|
101
|
-
def get_current_version(self) -> str:
|
|
102
|
-
try:
|
|
103
|
-
if self.electron_pkg_path.exists():
|
|
104
|
-
with open(self.electron_pkg_path, "r", encoding="utf-8") as f:
|
|
105
|
-
pkg_data = json.load(f)
|
|
106
|
-
return pkg_data.get("version", "N/A")
|
|
107
|
-
return "N/A"
|
|
108
|
-
except Exception:
|
|
109
|
-
return "N/A"
|
|
110
|
-
|
|
111
|
-
def update_version(self):
|
|
112
|
-
version_type = self.version_type.get()
|
|
113
|
-
confirm_msg = (
|
|
114
|
-
f"确认更新版本?\n\n版本类型: {version_type}\n将更新 electron/package.json"
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
if not messagebox.askyesno("确认更新", confirm_msg):
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
try:
|
|
121
|
-
cmd = [
|
|
122
|
-
sys.executable,
|
|
123
|
-
str(self.project_root / "version" / "scripts" / "version_manager.py"),
|
|
124
|
-
version_type,
|
|
125
|
-
]
|
|
126
|
-
|
|
127
|
-
# 子进程输出统一走 UTF-8:Windows 下管道默认 GBK,会让 ✓/❌ 编码失败。
|
|
128
|
-
env = os.environ.copy()
|
|
129
|
-
env["PYTHONIOENCODING"] = "utf-8"
|
|
130
|
-
env["PYTHONUTF8"] = "1"
|
|
131
|
-
result = subprocess.run(
|
|
132
|
-
cmd, capture_output=True, text=True, cwd=self.project_root,
|
|
133
|
-
env=env, encoding="utf-8", errors="replace",
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
if result.returncode == 0:
|
|
137
|
-
subprocess.run(
|
|
138
|
-
["git", "add", "electron/package.json", "pyproject.toml"],
|
|
139
|
-
cwd=self.project_root,
|
|
140
|
-
check=True,
|
|
141
|
-
)
|
|
142
|
-
new_version = self.get_current_version()
|
|
143
|
-
messagebox.showinfo("成功", f"版本更新成功!\n新版本: {new_version}")
|
|
144
|
-
self.root.destroy()
|
|
145
|
-
else:
|
|
146
|
-
detail = (result.stderr or result.stdout or "").strip()
|
|
147
|
-
messagebox.showerror("错误", f"版本更新失败:\n{detail}")
|
|
148
|
-
except Exception as e:
|
|
149
|
-
messagebox.showerror("错误", f"更新版本时发生错误: {e}")
|
|
150
|
-
|
|
151
|
-
def cancel_commit(self):
|
|
152
|
-
try:
|
|
153
|
-
if messagebox.askyesno(
|
|
154
|
-
"确认取消", "确定要取消本次提交吗?\n这将中断Git提交流程。"
|
|
155
|
-
):
|
|
156
|
-
self.root.destroy()
|
|
157
|
-
sys.exit(1)
|
|
158
|
-
except tk.TclError:
|
|
159
|
-
sys.exit(1)
|
|
160
|
-
|
|
161
|
-
def skip_version_update(self):
|
|
162
|
-
try:
|
|
163
|
-
if messagebox.askyesno(
|
|
164
|
-
"确认跳过", "确定要跳过版本更新吗?\n将直接提交代码。"
|
|
165
|
-
):
|
|
166
|
-
self.root.destroy()
|
|
167
|
-
sys.exit(0)
|
|
168
|
-
except tk.TclError:
|
|
169
|
-
sys.exit(0)
|
|
170
|
-
|
|
171
|
-
def run(self):
|
|
172
|
-
self.root.mainloop()
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
def main():
|
|
176
|
-
app = GuiVersionManager()
|
|
177
|
-
app.run()
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if __name__ == "__main__":
|
|
181
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{xb_init-2.0.2 → xb_init-2.0.4}/xb/templates/frontend/src/composables/useTerminalSessions.js.j2
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|