janito 1.10.0__py3-none-any.whl → 1.11.1__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 (57) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/conversation_api.py +178 -90
  3. janito/agent/conversation_ui.py +1 -1
  4. janito/agent/llm_conversation_history.py +12 -0
  5. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +19 -4
  6. janito/agent/tools/__init__.py +2 -0
  7. janito/agent/tools/create_directory.py +1 -1
  8. janito/agent/tools/create_file.py +1 -1
  9. janito/agent/tools/fetch_url.py +1 -1
  10. janito/agent/tools/find_files.py +26 -13
  11. janito/agent/tools/get_file_outline/core.py +1 -1
  12. janito/agent/tools/get_file_outline/python_outline.py +139 -95
  13. janito/agent/tools/get_lines.py +92 -63
  14. janito/agent/tools/move_file.py +58 -32
  15. janito/agent/tools/open_url.py +31 -0
  16. janito/agent/tools/python_command_runner.py +85 -86
  17. janito/agent/tools/python_file_runner.py +85 -86
  18. janito/agent/tools/python_stdin_runner.py +87 -88
  19. janito/agent/tools/remove_directory.py +1 -1
  20. janito/agent/tools/remove_file.py +1 -1
  21. janito/agent/tools/replace_file.py +2 -2
  22. janito/agent/tools/replace_text_in_file.py +193 -149
  23. janito/agent/tools/run_bash_command.py +1 -1
  24. janito/agent/tools/run_powershell_command.py +4 -0
  25. janito/agent/tools/search_text/__init__.py +1 -0
  26. janito/agent/tools/search_text/core.py +176 -0
  27. janito/agent/tools/search_text/match_lines.py +58 -0
  28. janito/agent/tools/search_text/pattern_utils.py +65 -0
  29. janito/agent/tools/search_text/traverse_directory.py +132 -0
  30. janito/agent/tools/validate_file_syntax/core.py +41 -30
  31. janito/agent/tools/validate_file_syntax/html_validator.py +21 -5
  32. janito/agent/tools/validate_file_syntax/markdown_validator.py +77 -34
  33. janito/agent/tools_utils/gitignore_utils.py +25 -2
  34. janito/agent/tools_utils/utils.py +7 -1
  35. janito/cli/config_commands.py +112 -109
  36. janito/shell/main.py +51 -8
  37. janito/shell/session/config.py +83 -75
  38. janito/shell/ui/interactive.py +97 -73
  39. janito/termweb/static/editor.css +32 -29
  40. janito/termweb/static/editor.css.bak +140 -22
  41. janito/termweb/static/editor.html +12 -7
  42. janito/termweb/static/editor.html.bak +16 -11
  43. janito/termweb/static/editor.js +94 -40
  44. janito/termweb/static/editor.js.bak +97 -65
  45. janito/termweb/static/index.html +1 -2
  46. janito/termweb/static/index.html.bak +1 -1
  47. janito/termweb/static/termweb.css +1 -22
  48. janito/termweb/static/termweb.css.bak +6 -4
  49. janito/termweb/static/termweb.js +0 -6
  50. janito/termweb/static/termweb.js.bak +1 -2
  51. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/METADATA +1 -1
  52. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/RECORD +56 -51
  53. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/WHEEL +1 -1
  54. janito/agent/tools/search_text.py +0 -254
  55. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/entry_points.txt +0 -0
  56. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/licenses/LICENSE +0 -0
  57. {janito-1.10.0.dist-info → janito-1.11.1.dist-info}/top_level.txt +0 -0
@@ -4,39 +4,92 @@ function getQueryParam(name) {
4
4
  return url.searchParams.get(name);
5
5
  }
6
6
  const filePath = getQueryParam('path');
7
- let initialContent = `# Bem-vindo ao TermWeb!\n# Este é um editor CodeMirror ao vivo.\n\nprint("Olá, janito.dev!")`;
8
- if (filePath) {
9
- fetch(`/api/explorer/${encodeURIComponent(filePath)}`)
10
- .then(resp => resp.json())
11
- .then(data => {
12
- if (data.type === 'file') {
13
- initialContent = data.content;
14
- if (window.editorInstance) {
15
- window.editorInstance.setValue(initialContent);
7
+
8
+ // Updates the theme icon based on the current theme
9
+ function updateThemeIcon() {
10
+ var icon = document.getElementById('theme-icon');
11
+ if (!icon) return;
12
+ if (document.body.classList.contains('light-theme')) {
13
+ icon.textContent = '☀️'; // Sun for light mode
14
+ icon.title = 'Switch to dark mode';
15
+ } else {
16
+ icon.textContent = '🌙'; // Moon for dark mode
17
+ icon.title = 'Switch to light mode';
18
+ }
19
+ }
20
+
21
+ document.addEventListener('DOMContentLoaded', function() {
22
+ // Display filename in header if present
23
+ if (filePath) {
24
+ const filename = filePath.split(/[\\\/]/).pop();
25
+ const filenameDisplay = document.getElementById('filename-display');
26
+ if (filenameDisplay) {
27
+ filenameDisplay.textContent = '— ' + filePath;
28
+ filenameDisplay.title = filePath;
29
+ }
30
+ }
31
+
32
+ let initialContent = "";
33
+ if (filePath) {
34
+ fetch(`/api/explorer/${encodeURIComponent(filePath)}`)
35
+ .then(resp => resp.json())
36
+ .then(data => {
37
+ if (data.type === 'file') {
38
+ initialContent = data.content;
39
+ if (window.editorInstance) {
40
+ window.editorInstance.setValue(initialContent);
41
+ }
42
+ } else if (data.error) {
43
+ initialContent = '# Error: ' + data.error;
44
+ if (window.editorInstance) {
45
+ window.editorInstance.setValue(initialContent);
46
+ }
16
47
  }
17
- } else if (data.error) {
18
- initialContent = '# Error: ' + data.error;
48
+ })
49
+ .catch(err => {
50
+ initialContent = '# Error ao carregar arquivo: ' + err;
19
51
  if (window.editorInstance) {
20
52
  window.editorInstance.setValue(initialContent);
21
53
  }
22
- }
23
- })
24
- .catch(err => {
25
- initialContent = '# Error ao carregar arquivo: ' + err;
26
- if (window.editorInstance) {
27
- window.editorInstance.setValue(initialContent);
28
- }
29
- });
30
- }
31
- document.addEventListener('DOMContentLoaded', function() {
54
+ });
55
+ }
56
+
57
+ // --- Detect file extension and set CodeMirror mode ---
58
+ function detectMode(filename) {
59
+ if (!filename) return 'python';
60
+ const ext = filename.split('.').pop().toLowerCase();
61
+ const map = {
62
+ 'py': 'python',
63
+ 'js': 'javascript',
64
+ 'json': 'javascript',
65
+ 'md': 'markdown',
66
+ 'html': {name: 'htmlmixed', scriptingModeSpec: 'django'},
67
+ 'htm': {name: 'htmlmixed', scriptingModeSpec: 'django'},
68
+ 'jinja': 'django',
69
+ 'j2': 'django',
70
+ 'jinja2': 'django',
71
+ 'css': 'css',
72
+ 'sh': 'shell',
73
+ 'bash': 'shell',
74
+ 'yml': 'yaml',
75
+ 'yaml': 'yaml',
76
+ };
77
+ return map[ext] || 'python';
78
+ }
79
+ var initialMode = detectMode(filePath);
32
80
  var editorInstance = CodeMirror.fromTextArea(document.getElementById('code'), {
33
81
  lineNumbers: true,
34
- mode: 'python',
82
+ mode: initialMode,
35
83
  theme: 'dracula',
36
84
  indentUnit: 4,
37
85
  tabSize: 4,
38
86
  styleActiveLine: true,
39
87
  });
88
+ // If file loaded later, update mode
89
+ if (filePath) {
90
+ const mode = detectMode(filePath);
91
+ editorInstance.setOption('mode', mode);
92
+ }
40
93
  window.editorInstance = editorInstance;
41
94
  // Add Ctrl-F handler for find
42
95
  editorInstance.addKeyMap({
@@ -54,6 +107,10 @@ document.addEventListener('DOMContentLoaded', function() {
54
107
  'Cmd-S': function(cm) {
55
108
  document.getElementById('save-btn').click();
56
109
  return false;
110
+ },
111
+ 'Alt-W': function(cm) {
112
+ const current = cm.getOption('lineWrapping');
113
+ cm.setOption('lineWrapping', !current);
57
114
  }
58
115
  });
59
116
  // --- Custom floating find navigation panel ---
@@ -153,29 +210,26 @@ document.addEventListener('DOMContentLoaded', function() {
153
210
  window.addEventListener('resize', resizeEditor);
154
211
  setTimeout(resizeEditor, 0);
155
212
  editorInstance.setValue(initialContent);
213
+ updateThemeIcon();
156
214
 
157
- // Theme switcher logic
158
- var themeSwitcher = document.getElementById('theme-switcher');
159
- var themeIcon = document.getElementById('theme-icon');
160
- var isDark = true;
161
- function updateThemeIcon() {
162
- themeIcon.textContent = isDark ? '🌙' : '☀️';
163
- }
164
- themeSwitcher.addEventListener('click', function() {
165
- isDark = !isDark;
166
- if (isDark) {
167
- document.body.classList.remove('light-theme');
168
- editorInstance.setOption('theme', 'dracula');
169
- } else {
170
- document.body.classList.add('light-theme');
171
- editorInstance.setOption('theme', 'default');
172
- }
173
- updateThemeIcon();
174
- });
175
215
  // Set initial state
176
216
  document.body.classList.remove('light-theme');
177
217
  editorInstance.setOption('theme', 'dracula');
178
218
  updateThemeIcon();
219
+
220
+ // Theme switch button logic
221
+ var themeSwitcher = document.getElementById('theme-switcher');
222
+ if (themeSwitcher) {
223
+ themeSwitcher.addEventListener('click', function() {
224
+ var isLight = document.body.classList.toggle('light-theme');
225
+ if (isLight) {
226
+ editorInstance.setOption('theme', 'default');
227
+ } else {
228
+ editorInstance.setOption('theme', 'dracula');
229
+ }
230
+ updateThemeIcon();
231
+ });
232
+ }
179
233
  // Botão de Gravar
180
234
  var saveBtn = document.getElementById('save-btn');
181
235
  saveBtn.addEventListener('click', function() {
@@ -4,39 +4,92 @@ function getQueryParam(name) {
4
4
  return url.searchParams.get(name);
5
5
  }
6
6
  const filePath = getQueryParam('path');
7
- let initialContent = `# Bem-vindo ao TermWeb!\n# Este é um editor CodeMirror ao vivo.\n\nprint("Olá, janito.dev!")`;
8
- if (filePath) {
9
- fetch(`/api/explorer/${encodeURIComponent(filePath)}`)
10
- .then(resp => resp.json())
11
- .then(data => {
12
- if (data.type === 'file') {
13
- initialContent = data.content;
14
- if (window.editorInstance) {
15
- window.editorInstance.setValue(initialContent);
7
+
8
+ // Updates the theme icon based on the current theme
9
+ function updateThemeIcon() {
10
+ var icon = document.getElementById('theme-icon');
11
+ if (!icon) return;
12
+ if (document.body.classList.contains('light-theme')) {
13
+ icon.textContent = '☀️'; // Sun for light mode
14
+ icon.title = 'Switch to dark mode';
15
+ } else {
16
+ icon.textContent = '🌙'; // Moon for dark mode
17
+ icon.title = 'Switch to light mode';
18
+ }
19
+ }
20
+
21
+ document.addEventListener('DOMContentLoaded', function() {
22
+ // Display filename in header if present
23
+ if (filePath) {
24
+ const filename = filePath.split(/[\\\/]/).pop();
25
+ const filenameDisplay = document.getElementById('filename-display');
26
+ if (filenameDisplay) {
27
+ filenameDisplay.textContent = '— ' + filePath;
28
+ filenameDisplay.title = filePath;
29
+ }
30
+ }
31
+
32
+ let initialContent = "";
33
+ if (filePath) {
34
+ fetch(`/api/explorer/${encodeURIComponent(filePath)}`)
35
+ .then(resp => resp.json())
36
+ .then(data => {
37
+ if (data.type === 'file') {
38
+ initialContent = data.content;
39
+ if (window.editorInstance) {
40
+ window.editorInstance.setValue(initialContent);
41
+ }
42
+ } else if (data.error) {
43
+ initialContent = '# Error: ' + data.error;
44
+ if (window.editorInstance) {
45
+ window.editorInstance.setValue(initialContent);
46
+ }
16
47
  }
17
- } else if (data.error) {
18
- initialContent = '# Error: ' + data.error;
48
+ })
49
+ .catch(err => {
50
+ initialContent = '# Error ao carregar arquivo: ' + err;
19
51
  if (window.editorInstance) {
20
52
  window.editorInstance.setValue(initialContent);
21
53
  }
22
- }
23
- })
24
- .catch(err => {
25
- initialContent = '# Error ao carregar arquivo: ' + err;
26
- if (window.editorInstance) {
27
- window.editorInstance.setValue(initialContent);
28
- }
29
- });
30
- }
31
- document.addEventListener('DOMContentLoaded', function() {
54
+ });
55
+ }
56
+
57
+ // --- Detect file extension and set CodeMirror mode ---
58
+ function detectMode(filename) {
59
+ if (!filename) return 'python';
60
+ const ext = filename.split('.').pop().toLowerCase();
61
+ const map = {
62
+ 'py': 'python',
63
+ 'js': 'javascript',
64
+ 'json': 'javascript',
65
+ 'md': 'markdown',
66
+ 'html': {name: 'htmlmixed', scriptingModeSpec: 'django'},
67
+ 'htm': {name: 'htmlmixed', scriptingModeSpec: 'django'},
68
+ 'jinja': 'django',
69
+ 'j2': 'django',
70
+ 'jinja2': 'django',
71
+ 'css': 'css',
72
+ 'sh': 'shell',
73
+ 'bash': 'shell',
74
+ 'yml': 'yaml',
75
+ 'yaml': 'yaml',
76
+ };
77
+ return map[ext] || 'python';
78
+ }
79
+ var initialMode = detectMode(filePath);
32
80
  var editorInstance = CodeMirror.fromTextArea(document.getElementById('code'), {
33
81
  lineNumbers: true,
34
- mode: 'python',
82
+ mode: initialMode,
35
83
  theme: 'dracula',
36
84
  indentUnit: 4,
37
85
  tabSize: 4,
38
86
  styleActiveLine: true,
39
87
  });
88
+ // If file loaded later, update mode
89
+ if (filePath) {
90
+ const mode = detectMode(filePath);
91
+ editorInstance.setOption('mode', mode);
92
+ }
40
93
  window.editorInstance = editorInstance;
41
94
  // Add Ctrl-F handler for find
42
95
  editorInstance.addKeyMap({
@@ -45,15 +98,15 @@ document.addEventListener('DOMContentLoaded', function() {
45
98
  },
46
99
  'Cmd-F': function(cm) {
47
100
  cm.execCommand('find');
48
- }
49
- });
50
- // Add Ctrl+S/Cmd+S handler for save
51
- editorInstance.addKeyMap({
101
+ },
52
102
  'Ctrl-S': function(cm) {
53
103
  document.getElementById('save-btn').click();
104
+ // Prevent default browser save dialog
105
+ return false;
54
106
  },
55
107
  'Cmd-S': function(cm) {
56
108
  document.getElementById('save-btn').click();
109
+ return false;
57
110
  }
58
111
  });
59
112
  // --- Custom floating find navigation panel ---
@@ -153,41 +206,27 @@ document.addEventListener('DOMContentLoaded', function() {
153
206
  window.addEventListener('resize', resizeEditor);
154
207
  setTimeout(resizeEditor, 0);
155
208
  editorInstance.setValue(initialContent);
209
+ updateThemeIcon();
156
210
 
157
- // Theme switcher logic
158
- var themeSwitcher = document.getElementById('theme-switcher');
159
- var themeIcon = document.getElementById('theme-icon');
160
- var isDark = true;
161
- function updateThemeIcon() {
162
- themeIcon.textContent = isDark ? '🌙' : '☀️';
163
- }
164
- themeSwitcher.addEventListener('click', function() {
165
- isDark = !isDark;
166
- if (isDark) {
167
- document.body.classList.remove('light-theme');
168
- editorInstance.setOption('theme', 'dracula');
169
- } else {
170
- document.body.classList.add('light-theme');
171
- editorInstance.setOption('theme', 'default');
172
- }
173
- updateThemeIcon();
174
- });
175
211
  // Set initial state
176
212
  document.body.classList.remove('light-theme');
177
213
  editorInstance.setOption('theme', 'dracula');
178
214
  updateThemeIcon();
179
215
 
180
- // Toast notification logic
181
- function showToast(message) {
182
- var toast = document.getElementById('toast');
183
- toast.textContent = message;
184
- toast.className = 'toast show';
185
- setTimeout(function() {
186
- toast.className = 'toast';
187
- }, 1800);
216
+ // Theme switch button logic
217
+ var themeSwitcher = document.getElementById('theme-switcher');
218
+ if (themeSwitcher) {
219
+ themeSwitcher.addEventListener('click', function() {
220
+ var isLight = document.body.classList.toggle('light-theme');
221
+ if (isLight) {
222
+ editorInstance.setOption('theme', 'default');
223
+ } else {
224
+ editorInstance.setOption('theme', 'dracula');
225
+ }
226
+ updateThemeIcon();
227
+ });
188
228
  }
189
-
190
- // Save Button
229
+ // Botão de Gravar
191
230
  var saveBtn = document.getElementById('save-btn');
192
231
  saveBtn.addEventListener('click', function() {
193
232
  if (!filePath) {
@@ -205,9 +244,10 @@ document.addEventListener('DOMContentLoaded', function() {
205
244
  .then(resp => resp.json())
206
245
  .then(data => {
207
246
  if (data.success) {
208
- showToast('Saved!');
209
- saveBtn.textContent = 'Saved!';
210
- setTimeout(() => saveBtn.textContent = 'Save', 1200);
247
+ // Show popup
248
+ var popup = document.getElementById('save-popup');
249
+ popup.style.display = 'block';
250
+ setTimeout(() => { popup.style.display = 'none'; }, 1500);
211
251
  } else {
212
252
  alert('Error saving: ' + (data.error || 'desconhecido'));
213
253
  }
@@ -216,12 +256,4 @@ document.addEventListener('DOMContentLoaded', function() {
216
256
  alert('Error saving: ' + err);
217
257
  });
218
258
  });
219
-
220
- // Prevent browser default save dialog on Ctrl+S/Cmd+S
221
- window.addEventListener('keydown', function(e) {
222
- if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 's') {
223
- e.preventDefault();
224
- document.getElementById('save-btn').click();
225
- }
226
- });
227
259
  });
@@ -24,8 +24,7 @@
24
24
  <img src="/static/termicon.svg" alt="TermWeb Logo" class="header-logo">
25
25
  <span class="header-title">TermWeb</span>
26
26
  <span class="header-subtitle">Explorador de Projetos com IA</span>
27
- <button class="theme-switcher" id="theme-switcher" title="Alternate tema"><span id="theme-icon">🌙</span></button>
28
- </div>
27
+ </div>
29
28
  <div class="toolbar" id="explorer-toolbar">
30
29
 
31
30
  </div>
@@ -24,7 +24,7 @@
24
24
  <img src="/static/termicon.svg" alt="TermWeb Logo" class="header-logo">
25
25
  <span class="header-title">TermWeb</span>
26
26
  <span class="header-subtitle">Explorador de Projetos com IA</span>
27
- <button class="theme-switcher" id="theme-switcher" title="Alternar tema"><span id="theme-icon">🌙</span></button>
27
+ <button class="theme-switcher" id="theme-switcher" title="Alternate tema"><span id="theme-icon">🌙</span></button>
28
28
  </div>
29
29
  <div class="toolbar" id="explorer-toolbar">
30
30
 
@@ -20,7 +20,7 @@ body.light-theme {
20
20
  .header {
21
21
  background: #23272b;
22
22
  color: #f1f1f1;
23
- padding: 1em 2em;
23
+ padding: 0.2em 2em 1em 2em;
24
24
  font-size: 1.5em;
25
25
  font-weight: bold;
26
26
  letter-spacing: 0.04em;
@@ -180,27 +180,6 @@ body.light-theme .footer a {
180
180
  display: inline;
181
181
  }
182
182
 
183
- .theme-switcher {
184
- position: absolute;
185
- right: 20px;
186
- top: 10px;
187
- background: #444;
188
- color: #f1f1f1;
189
- border: none;
190
- border-radius: 4px;
191
- padding: 6px 14px;
192
- cursor: pointer;
193
- font-size: 1em;
194
- transition: background 0.2s, color 0.2s;
195
- }
196
- body.light-theme .theme-switcher {
197
- background: #ddd;
198
- color: #181a1b;
199
- }
200
- body.dark-theme .theme-switcher {
201
- background: #23272b;
202
- color: #f1f1f1;
203
- }
204
183
 
205
184
  /* Explorer file/dir links */
206
185
  .explorer-link, .explorer-link:visited {
@@ -20,7 +20,7 @@ body.light-theme {
20
20
  .header {
21
21
  background: #23272b;
22
22
  color: #f1f1f1;
23
- padding: 1em 2em;
23
+ padding: 0.2em 2em 1em 2em;
24
24
  font-size: 1.5em;
25
25
  font-weight: bold;
26
26
  letter-spacing: 0.04em;
@@ -183,14 +183,16 @@ body.light-theme .footer a {
183
183
  .theme-switcher {
184
184
  position: absolute;
185
185
  right: 20px;
186
- top: 10px;
186
+ top: 2px;
187
187
  background: #444;
188
188
  color: #f1f1f1;
189
189
  border: none;
190
190
  border-radius: 4px;
191
- padding: 6px 14px;
191
+ padding: 0px 6px;
192
+ margin-top: -0.2em;
193
+ margin-bottom: 2px;
192
194
  cursor: pointer;
193
- font-size: 1em;
195
+ font-size: 0.85em;
194
196
  transition: background 0.2s, color 0.2s;
195
197
  }
196
198
  body.light-theme .theme-switcher {
@@ -154,12 +154,6 @@ document.addEventListener('DOMContentLoaded', function() {
154
154
  // Initial theme
155
155
  var theme = localStorage.getItem('theme') || 'dark';
156
156
  setTheme(theme === 'dark');
157
- var themeSwitcher = document.getElementById('theme-switcher');
158
- if (themeSwitcher) {
159
- themeSwitcher.onclick = function() {
160
- setTheme(document.body.classList.contains('light-theme'));
161
- };
162
- }
163
157
  setExplorerView('list'); // Always use list view
164
158
  renderExplorer('.')
165
159
  });
@@ -16,7 +16,6 @@ function getPaiPath(path) {
16
16
  function setExplorerView(view) {
17
17
  explorerView = view;
18
18
  localStorage.setItem('explorerView', view);
19
-
20
19
  }
21
20
 
22
21
  function normalizeExplorerPath(path) {
@@ -133,7 +132,7 @@ window.renderCodePreview = function(container, content, mode) {
133
132
  } catch (e) {
134
133
  container.innerHTML = '<pre>' + (content ? String(content) : '') + '</pre>';
135
134
  }
136
- }
135
+ };
137
136
 
138
137
  // Theme switcher logic
139
138
  function setTheme(dark) {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: janito
3
- Version: 1.10.0
3
+ Version: 1.11.1
4
4
  Summary: Natural Language Coding Agent,
5
5
  Author-email: João Pinto <joao.pinto@gmail.com>
6
6
  License-Expression: MIT