ntermqt 0.1.0__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 (52) hide show
  1. nterm/__init__.py +54 -0
  2. nterm/__main__.py +619 -0
  3. nterm/askpass/__init__.py +22 -0
  4. nterm/askpass/server.py +393 -0
  5. nterm/config.py +158 -0
  6. nterm/connection/__init__.py +17 -0
  7. nterm/connection/profile.py +296 -0
  8. nterm/manager/__init__.py +29 -0
  9. nterm/manager/connect_dialog.py +322 -0
  10. nterm/manager/editor.py +262 -0
  11. nterm/manager/io.py +678 -0
  12. nterm/manager/models.py +346 -0
  13. nterm/manager/settings.py +264 -0
  14. nterm/manager/tree.py +493 -0
  15. nterm/resources.py +48 -0
  16. nterm/session/__init__.py +60 -0
  17. nterm/session/askpass_ssh.py +399 -0
  18. nterm/session/base.py +110 -0
  19. nterm/session/interactive_ssh.py +522 -0
  20. nterm/session/pty_transport.py +571 -0
  21. nterm/session/ssh.py +610 -0
  22. nterm/terminal/__init__.py +11 -0
  23. nterm/terminal/bridge.py +83 -0
  24. nterm/terminal/resources/terminal.html +253 -0
  25. nterm/terminal/resources/terminal.js +414 -0
  26. nterm/terminal/resources/xterm-addon-fit.min.js +8 -0
  27. nterm/terminal/resources/xterm-addon-unicode11.min.js +8 -0
  28. nterm/terminal/resources/xterm-addon-web-links.min.js +8 -0
  29. nterm/terminal/resources/xterm.css +209 -0
  30. nterm/terminal/resources/xterm.min.js +8 -0
  31. nterm/terminal/widget.py +380 -0
  32. nterm/theme/__init__.py +10 -0
  33. nterm/theme/engine.py +456 -0
  34. nterm/theme/stylesheet.py +377 -0
  35. nterm/theme/themes/clean.yaml +0 -0
  36. nterm/theme/themes/default.yaml +36 -0
  37. nterm/theme/themes/dracula.yaml +36 -0
  38. nterm/theme/themes/gruvbox_dark.yaml +36 -0
  39. nterm/theme/themes/gruvbox_hybrid.yaml +38 -0
  40. nterm/theme/themes/gruvbox_light.yaml +36 -0
  41. nterm/vault/__init__.py +32 -0
  42. nterm/vault/credential_manager.py +163 -0
  43. nterm/vault/keychain.py +135 -0
  44. nterm/vault/manager_ui.py +962 -0
  45. nterm/vault/profile.py +219 -0
  46. nterm/vault/resolver.py +250 -0
  47. nterm/vault/store.py +642 -0
  48. ntermqt-0.1.0.dist-info/METADATA +327 -0
  49. ntermqt-0.1.0.dist-info/RECORD +52 -0
  50. ntermqt-0.1.0.dist-info/WHEEL +5 -0
  51. ntermqt-0.1.0.dist-info/entry_points.txt +5 -0
  52. ntermqt-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,262 @@
1
+ """
2
+ Session editor dialog.
3
+ """
4
+
5
+ from __future__ import annotations
6
+ from typing import Optional, List
7
+
8
+ from PyQt6.QtWidgets import (
9
+ QDialog, QVBoxLayout, QHBoxLayout, QFormLayout,
10
+ QLineEdit, QSpinBox, QComboBox, QTextEdit,
11
+ QPushButton, QDialogButtonBox, QGroupBox, QLabel,
12
+ QWidget
13
+ )
14
+ from PyQt6.QtCore import Qt
15
+
16
+ from .models import SavedSession
17
+
18
+
19
+ class SessionEditorDialog(QDialog):
20
+ """
21
+ Dialog for creating or editing a saved session.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ session: SavedSession = None,
27
+ credential_names: List[str] = None,
28
+ parent: QWidget = None
29
+ ):
30
+ super().__init__(parent)
31
+ self._session = session or SavedSession()
32
+ self._credential_names = credential_names or []
33
+
34
+ self._setup_ui()
35
+ self._load_session()
36
+
37
+ def _setup_ui(self) -> None:
38
+ """Build the dialog UI."""
39
+ self.setWindowTitle("Session" if self._session.id else "New Session")
40
+ self.setMinimumWidth(400)
41
+
42
+ layout = QVBoxLayout(self)
43
+
44
+ # Basic info group
45
+ basic_group = QGroupBox("Connection")
46
+ basic_layout = QFormLayout(basic_group)
47
+
48
+ self._name_input = QLineEdit()
49
+ self._name_input.setPlaceholderText("My Server")
50
+ basic_layout.addRow("Name:", self._name_input)
51
+
52
+ self._desc_input = QLineEdit()
53
+ self._desc_input.setPlaceholderText("Optional description")
54
+ basic_layout.addRow("Description:", self._desc_input)
55
+
56
+ # Host row
57
+ host_row = QHBoxLayout()
58
+ self._host_input = QLineEdit()
59
+ self._host_input.setPlaceholderText("hostname or IP")
60
+ host_row.addWidget(self._host_input, 1)
61
+
62
+ host_row.addWidget(QLabel(":"))
63
+
64
+ self._port_input = QSpinBox()
65
+ self._port_input.setRange(1, 65535)
66
+ self._port_input.setValue(22)
67
+ self._port_input.setFixedWidth(80)
68
+ host_row.addWidget(self._port_input)
69
+
70
+ basic_layout.addRow("Host:", host_row)
71
+
72
+ layout.addWidget(basic_group)
73
+
74
+ # Auth group
75
+ auth_group = QGroupBox("Authentication")
76
+ auth_layout = QFormLayout(auth_group)
77
+
78
+ self._cred_combo = QComboBox()
79
+ self._cred_combo.addItem("(SSH Agent)", None)
80
+ self._cred_combo.addItem("(Ask on connect)", "__ask__")
81
+ for name in self._credential_names:
82
+ self._cred_combo.addItem(name, name)
83
+ auth_layout.addRow("Credential:", self._cred_combo)
84
+
85
+ layout.addWidget(auth_group)
86
+
87
+ # Button box
88
+ buttons = QDialogButtonBox(
89
+ QDialogButtonBox.StandardButton.Ok |
90
+ QDialogButtonBox.StandardButton.Cancel
91
+ )
92
+ buttons.accepted.connect(self._on_accept)
93
+ buttons.rejected.connect(self.reject)
94
+ layout.addWidget(buttons)
95
+
96
+ # Focus
97
+ self._name_input.setFocus()
98
+
99
+ def _load_session(self) -> None:
100
+ """Load session data into form."""
101
+ self._name_input.setText(self._session.name)
102
+ self._desc_input.setText(self._session.description)
103
+ self._host_input.setText(self._session.hostname)
104
+ self._port_input.setValue(self._session.port)
105
+
106
+ # Select credential
107
+ if self._session.credential_name:
108
+ idx = self._cred_combo.findData(self._session.credential_name)
109
+ if idx >= 0:
110
+ self._cred_combo.setCurrentIndex(idx)
111
+
112
+ def _on_accept(self) -> None:
113
+ """Validate and accept."""
114
+ name = self._name_input.text().strip()
115
+ hostname = self._host_input.text().strip()
116
+
117
+ if not name:
118
+ self._name_input.setFocus()
119
+ return
120
+
121
+ if not hostname:
122
+ self._host_input.setFocus()
123
+ return
124
+
125
+ self.accept()
126
+
127
+ def get_session(self) -> SavedSession:
128
+ """Get the edited session data."""
129
+ cred_data = self._cred_combo.currentData()
130
+ cred_name = cred_data if cred_data and cred_data != "__ask__" else None
131
+
132
+ return SavedSession(
133
+ id=self._session.id,
134
+ name=self._name_input.text().strip(),
135
+ description=self._desc_input.text().strip(),
136
+ hostname=self._host_input.text().strip(),
137
+ port=self._port_input.value(),
138
+ credential_name=cred_name,
139
+ folder_id=self._session.folder_id,
140
+ position=self._session.position,
141
+ extras=self._session.extras,
142
+ )
143
+
144
+ def set_credential_names(self, names: List[str]) -> None:
145
+ """Update available credential names."""
146
+ current = self._cred_combo.currentData()
147
+
148
+ self._cred_combo.clear()
149
+ self._cred_combo.addItem("(SSH Agent)", None)
150
+ self._cred_combo.addItem("(Ask on connect)", "__ask__")
151
+
152
+ for name in names:
153
+ self._cred_combo.addItem(name, name)
154
+
155
+ # Restore selection
156
+ if current:
157
+ idx = self._cred_combo.findData(current)
158
+ if idx >= 0:
159
+ self._cred_combo.setCurrentIndex(idx)
160
+
161
+
162
+ class QuickConnectDialog(QDialog):
163
+ """
164
+ Quick connect dialog - doesn't save the session.
165
+ """
166
+
167
+ def __init__(
168
+ self,
169
+ credential_names: List[str] = None,
170
+ parent: QWidget = None
171
+ ):
172
+ super().__init__(parent)
173
+ self._credential_names = credential_names or []
174
+ self._setup_ui()
175
+
176
+ def _setup_ui(self) -> None:
177
+ """Build the dialog UI."""
178
+ self.setWindowTitle("Quick Connect")
179
+ self.setMinimumWidth(350)
180
+
181
+ layout = QVBoxLayout(self)
182
+
183
+ form = QFormLayout()
184
+
185
+ # Host row
186
+ host_row = QHBoxLayout()
187
+ self._host_input = QLineEdit()
188
+ self._host_input.setPlaceholderText("hostname or IP")
189
+ host_row.addWidget(self._host_input, 1)
190
+
191
+ host_row.addWidget(QLabel(":"))
192
+
193
+ self._port_input = QSpinBox()
194
+ self._port_input.setRange(1, 65535)
195
+ self._port_input.setValue(22)
196
+ self._port_input.setFixedWidth(80)
197
+ host_row.addWidget(self._port_input)
198
+
199
+ form.addRow("Host:", host_row)
200
+
201
+ # Credential
202
+ self._cred_combo = QComboBox()
203
+ self._cred_combo.addItem("(SSH Agent)", None)
204
+ self._cred_combo.addItem("(Ask on connect)", "__ask__")
205
+ for name in self._credential_names:
206
+ self._cred_combo.addItem(name, name)
207
+ form.addRow("Credential:", self._cred_combo)
208
+
209
+ layout.addLayout(form)
210
+
211
+ # Buttons
212
+ btn_layout = QHBoxLayout()
213
+ btn_layout.addStretch()
214
+
215
+ self._connect_tab_btn = QPushButton("Connect")
216
+ self._connect_tab_btn.setDefault(True)
217
+ self._connect_tab_btn.clicked.connect(lambda: self._accept_with_mode("tab"))
218
+ btn_layout.addWidget(self._connect_tab_btn)
219
+
220
+ self._connect_win_btn = QPushButton("New Window")
221
+ self._connect_win_btn.clicked.connect(lambda: self._accept_with_mode("window"))
222
+ btn_layout.addWidget(self._connect_win_btn)
223
+
224
+ cancel_btn = QPushButton("Cancel")
225
+ cancel_btn.clicked.connect(self.reject)
226
+ btn_layout.addWidget(cancel_btn)
227
+
228
+ layout.addLayout(btn_layout)
229
+
230
+ # Focus
231
+ self._host_input.setFocus()
232
+
233
+ # Connect mode result
234
+ self._connect_mode = "tab"
235
+
236
+ def _accept_with_mode(self, mode: str) -> None:
237
+ """Accept with specified connect mode."""
238
+ hostname = self._host_input.text().strip()
239
+ if not hostname:
240
+ self._host_input.setFocus()
241
+ return
242
+
243
+ self._connect_mode = mode
244
+ self.accept()
245
+
246
+ def get_session(self) -> SavedSession:
247
+ """Get session data (not saved, just for connecting)."""
248
+ cred_data = self._cred_combo.currentData()
249
+ cred_name = cred_data if cred_data and cred_data != "__ask__" else None
250
+
251
+ hostname = self._host_input.text().strip()
252
+
253
+ return SavedSession(
254
+ name=hostname, # Use hostname as name
255
+ hostname=hostname,
256
+ port=self._port_input.value(),
257
+ credential_name=cred_name,
258
+ )
259
+
260
+ def get_connect_mode(self) -> str:
261
+ """Get selected connect mode ('tab' or 'window')."""
262
+ return self._connect_mode