janito 1.8.1__py3-none-any.whl → 1.10.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 (142) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/api_exceptions.py +4 -0
  3. janito/agent/config.py +1 -1
  4. janito/agent/config_defaults.py +2 -3
  5. janito/agent/config_utils.py +0 -9
  6. janito/agent/conversation.py +177 -114
  7. janito/agent/conversation_api.py +179 -159
  8. janito/agent/conversation_tool_calls.py +11 -8
  9. janito/agent/llm_conversation_history.py +70 -0
  10. janito/agent/openai_client.py +44 -21
  11. janito/agent/openai_schema_generator.py +164 -128
  12. janito/agent/platform_discovery.py +134 -77
  13. janito/agent/profile_manager.py +5 -5
  14. janito/agent/rich_message_handler.py +80 -31
  15. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +9 -8
  16. janito/agent/test_openai_schema_generator.py +93 -0
  17. janito/agent/tool_base.py +7 -2
  18. janito/agent/tool_executor.py +63 -50
  19. janito/agent/tool_registry.py +5 -2
  20. janito/agent/tool_use_tracker.py +42 -5
  21. janito/agent/tools/__init__.py +13 -12
  22. janito/agent/tools/create_directory.py +9 -6
  23. janito/agent/tools/create_file.py +35 -54
  24. janito/agent/tools/delete_text_in_file.py +97 -0
  25. janito/agent/tools/fetch_url.py +50 -5
  26. janito/agent/tools/find_files.py +40 -26
  27. janito/agent/tools/get_file_outline/__init__.py +1 -0
  28. janito/agent/tools/{outline_file/__init__.py → get_file_outline/core.py} +14 -18
  29. janito/agent/tools/get_file_outline/python_outline.py +134 -0
  30. janito/agent/tools/{search_outline.py → get_file_outline/search_outline.py} +11 -0
  31. janito/agent/tools/get_lines.py +21 -12
  32. janito/agent/tools/move_file.py +13 -12
  33. janito/agent/tools/present_choices.py +3 -1
  34. janito/agent/tools/python_command_runner.py +150 -0
  35. janito/agent/tools/python_file_runner.py +148 -0
  36. janito/agent/tools/python_stdin_runner.py +154 -0
  37. janito/agent/tools/remove_directory.py +4 -2
  38. janito/agent/tools/remove_file.py +15 -13
  39. janito/agent/tools/replace_file.py +72 -0
  40. janito/agent/tools/replace_text_in_file.py +7 -5
  41. janito/agent/tools/run_bash_command.py +29 -72
  42. janito/agent/tools/run_powershell_command.py +142 -102
  43. janito/agent/tools/search_text.py +177 -131
  44. janito/agent/tools/validate_file_syntax/__init__.py +1 -0
  45. janito/agent/tools/validate_file_syntax/core.py +94 -0
  46. janito/agent/tools/validate_file_syntax/css_validator.py +35 -0
  47. janito/agent/tools/validate_file_syntax/html_validator.py +77 -0
  48. janito/agent/tools/validate_file_syntax/js_validator.py +27 -0
  49. janito/agent/tools/validate_file_syntax/json_validator.py +6 -0
  50. janito/agent/tools/validate_file_syntax/markdown_validator.py +66 -0
  51. janito/agent/tools/validate_file_syntax/ps1_validator.py +32 -0
  52. janito/agent/tools/validate_file_syntax/python_validator.py +5 -0
  53. janito/agent/tools/validate_file_syntax/xml_validator.py +11 -0
  54. janito/agent/tools/validate_file_syntax/yaml_validator.py +6 -0
  55. janito/agent/tools_utils/__init__.py +1 -0
  56. janito/agent/tools_utils/action_type.py +7 -0
  57. janito/agent/tools_utils/dir_walk_utils.py +24 -0
  58. janito/agent/tools_utils/formatting.py +49 -0
  59. janito/agent/tools_utils/gitignore_utils.py +69 -0
  60. janito/agent/tools_utils/test_gitignore_utils.py +46 -0
  61. janito/agent/tools_utils/utils.py +30 -0
  62. janito/cli/_livereload_log_utils.py +13 -0
  63. janito/cli/_print_config.py +63 -61
  64. janito/cli/arg_parser.py +57 -14
  65. janito/cli/cli_main.py +270 -0
  66. janito/cli/livereload_starter.py +60 -0
  67. janito/cli/main.py +166 -99
  68. janito/cli/one_shot.py +80 -0
  69. janito/cli/termweb_starter.py +2 -2
  70. janito/i18n/__init__.py +1 -1
  71. janito/livereload/app.py +25 -0
  72. janito/rich_utils.py +41 -25
  73. janito/{cli_chat_shell → shell}/commands/__init__.py +19 -14
  74. janito/{cli_chat_shell → shell}/commands/config.py +4 -4
  75. janito/shell/commands/conversation_restart.py +74 -0
  76. janito/shell/commands/edit.py +24 -0
  77. janito/shell/commands/history_view.py +18 -0
  78. janito/{cli_chat_shell → shell}/commands/lang.py +3 -0
  79. janito/shell/commands/livelogs.py +42 -0
  80. janito/{cli_chat_shell → shell}/commands/prompt.py +16 -6
  81. janito/shell/commands/session.py +35 -0
  82. janito/{cli_chat_shell → shell}/commands/session_control.py +3 -5
  83. janito/{cli_chat_shell → shell}/commands/termweb_log.py +18 -10
  84. janito/shell/commands/tools.py +26 -0
  85. janito/shell/commands/track.py +36 -0
  86. janito/shell/commands/utility.py +28 -0
  87. janito/{cli_chat_shell → shell}/commands/verbose.py +4 -5
  88. janito/shell/commands.py +40 -0
  89. janito/shell/input_history.py +62 -0
  90. janito/shell/main.py +257 -0
  91. janito/{cli_chat_shell/shell_command_completer.py → shell/prompt/completer.py} +1 -1
  92. janito/{cli_chat_shell/chat_ui.py → shell/prompt/session_setup.py} +19 -5
  93. janito/shell/session/manager.py +101 -0
  94. janito/{cli_chat_shell/ui.py → shell/ui/interactive.py} +23 -17
  95. janito/termweb/app.py +3 -3
  96. janito/termweb/static/editor.css +142 -0
  97. janito/termweb/static/editor.css.bak +27 -0
  98. janito/termweb/static/editor.html +15 -213
  99. janito/termweb/static/editor.html.bak +16 -215
  100. janito/termweb/static/editor.js +209 -0
  101. janito/termweb/static/editor.js.bak +227 -0
  102. janito/termweb/static/index.html +2 -3
  103. janito/termweb/static/index.html.bak +2 -3
  104. janito/termweb/static/termweb.css.bak +33 -84
  105. janito/termweb/static/termweb.js +15 -34
  106. janito/termweb/static/termweb.js.bak +18 -36
  107. janito/tests/test_rich_utils.py +44 -0
  108. janito/web/app.py +0 -75
  109. {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/METADATA +62 -42
  110. janito-1.10.0.dist-info/RECORD +158 -0
  111. {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/WHEEL +1 -1
  112. janito/agent/tools/dir_walk_utils.py +0 -16
  113. janito/agent/tools/gitignore_utils.py +0 -46
  114. janito/agent/tools/memory.py +0 -48
  115. janito/agent/tools/outline_file/formatting.py +0 -20
  116. janito/agent/tools/outline_file/python_outline.py +0 -71
  117. janito/agent/tools/present_choices_test.py +0 -18
  118. janito/agent/tools/rich_live.py +0 -44
  119. janito/agent/tools/run_python_command.py +0 -163
  120. janito/agent/tools/tools_utils.py +0 -56
  121. janito/agent/tools/utils.py +0 -33
  122. janito/agent/tools/validate_file_syntax.py +0 -163
  123. janito/cli/runner/cli_main.py +0 -180
  124. janito/cli_chat_shell/chat_loop.py +0 -163
  125. janito/cli_chat_shell/chat_state.py +0 -38
  126. janito/cli_chat_shell/commands/history_start.py +0 -37
  127. janito/cli_chat_shell/commands/session.py +0 -48
  128. janito/cli_chat_shell/commands/sum.py +0 -49
  129. janito/cli_chat_shell/commands/utility.py +0 -32
  130. janito/cli_chat_shell/session_manager.py +0 -72
  131. janito-1.8.1.dist-info/RECORD +0 -127
  132. /janito/agent/tools/{outline_file → get_file_outline}/markdown_outline.py +0 -0
  133. /janito/cli/{runner/_termweb_log_utils.py → _termweb_log_utils.py} +0 -0
  134. /janito/cli/{runner/config.py → config_runner.py} +0 -0
  135. /janito/cli/{runner/formatting.py → formatting_runner.py} +0 -0
  136. /janito/{cli/runner → shell}/__init__.py +0 -0
  137. /janito/{cli_chat_shell → shell/prompt}/load_prompt.py +0 -0
  138. /janito/{cli_chat_shell/config_shell.py → shell/session/config.py} +0 -0
  139. /janito/{cli_chat_shell/__init__.py → shell/session/history.py} +0 -0
  140. {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/entry_points.txt +0 -0
  141. {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/licenses/LICENSE +0 -0
  142. {janito-1.8.1.dist-info → janito-1.10.0.dist-info}/top_level.txt +0 -0
@@ -24,11 +24,10 @@
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">Alternate para tema claro</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
- <button id="view-list" class="view-toggle active" title="Visualização em Lista">&#9776; List</button>
31
- <button id="view-icons" class="view-toggle" title="Visualização em Ícones">&#9632; Icons</button>
30
+
32
31
  </div>
33
32
  <div class="main">
34
33
  <div id="explorer-main"></div>
@@ -24,11 +24,10 @@
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">Alternar para tema claro</button>
27
+ <button class="theme-switcher" id="theme-switcher" title="Alternar tema"><span id="theme-icon">🌙</span></button>
28
28
  </div>
29
29
  <div class="toolbar" id="explorer-toolbar">
30
- <button id="view-list" class="view-toggle active" title="Visualização em Lista">&#9776; List</button>
31
- <button id="view-icons" class="view-toggle" title="Visualização em Ícones">&#9632; Icons</button>
30
+
32
31
  </div>
33
32
  <div class="main">
34
33
  <div id="explorer-main"></div>
@@ -81,30 +81,48 @@ body.light-theme .header {
81
81
  .main {
82
82
  flex: 1 1 auto;
83
83
  display: flex;
84
- flex-direction: column;
85
- justify-content: stretch;
86
- align-items: stretch;
87
- min-height: 0;
88
- padding: 0;
84
+ flex-direction: row;
85
+ gap: 2em;
86
+ padding: 2em;
87
+ min-height: 60vh;
89
88
  background: transparent;
90
89
  }
90
+ #explorer-main, #explorer-preview {
91
+ background: #22262b;
92
+ border-radius: 1.1em;
93
+ box-shadow: 0 4px 24px #0004;
94
+ border: 1px solid #1a73e8;
95
+ padding: 1.5em 1.2em;
96
+ min-height: 40vh;
97
+ overflow: auto;
98
+ }
99
+ #explorer-main {
100
+ margin-right: 0.5em;
101
+ }
102
+ #explorer-preview {
103
+ margin-left: 0.5em;
104
+ display: flex;
105
+ flex-direction: column;
106
+ flex: 1 1 0;
107
+ min-width: 0;
108
+ }
91
109
 
92
- .editor-pane {
110
+ /* --- CodeMirror Integration for Preview --- */
111
+ #explorer-codemirror-preview {
93
112
  flex: 1 1 auto;
113
+ min-height: 0;
114
+ min-width: 0;
94
115
  display: flex;
95
116
  flex-direction: column;
96
- height: 100%;
97
- min-height: 0;
98
117
  }
99
-
100
118
  .CodeMirror {
101
119
  flex: 1 1 auto;
102
- height: 100% !important;
120
+ height: 60vh !important;
103
121
  min-height: 0;
104
122
  font-size: 1.1em;
105
123
  background: #23272b;
106
124
  color: #f8f8f2;
107
- border-radius: 0;
125
+ border-radius: 0.5em;
108
126
  border: none;
109
127
  transition: background 0.2s, color 0.2s;
110
128
  }
@@ -198,81 +216,9 @@ body.light-theme .explorer-link, body.light-theme .explorer-link:visited {
198
216
  color: #42a5f5;
199
217
  }
200
218
  body.light-theme .explorer-link:hover {
201
- color: #0d47a1;
202
- }
203
-
204
- /* Error and status messages */
205
- .error {
206
- color: #ff5252;
207
- background: #2d2d2d;
208
- padding: 0.5em 1em;
209
- border-radius: 4px;
210
- margin: 1em 0;
211
- }
212
- body.light-theme .error {
213
- color: #b71c1c;
214
- background: #fff3f3;
215
- }
216
-
217
- /* Spinner for loading */
218
- .spinner {
219
- border: 4px solid #f3f3f3;
220
- border-top: 4px solid #1976d2;
221
- border-radius: 50%;
222
- width: 24px;
223
- height: 24px;
224
- animation: spin 1s linear infinite;
225
- display: inline-block;
226
- }
227
- @keyframes spin {
228
- 0% { transform: rotate(0deg); }
229
- 100% { transform: rotate(360deg); }
219
+ color: #1565c0;
230
220
  }
231
221
 
232
- /* --- Enhanced Toolbar & Panes --- */
233
- .toolbar {
234
- display: flex;
235
- justify-content: flex-end;
236
- align-items: center;
237
- gap: 0.7em;
238
- background: #23272b;
239
- border-bottom: 1px solid #1a73e8;
240
- padding: 0.7em 2em;
241
- margin-bottom: 0.5em;
242
- box-shadow: 0 2px 8px #0002;
243
- }
244
- .view-toggle {
245
- background: #23272b;
246
- color: #90caf9;
247
- border: 1px solid #1a73e8;
248
- border-radius: 0.5em;
249
- padding: 0.5em 1.2em;
250
- font-size: 1.1em;
251
- cursor: pointer;
252
- transition: background 0.15s, color 0.15s, box-shadow 0.15s;
253
- margin-left: 0.2em;
254
- }
255
- .view-toggle.active, .view-toggle:hover {
256
- background: #1a73e8;
257
- color: #fff;
258
- box-shadow: 0 2px 8px #1976d255;
259
- }
260
-
261
- #explorer-main, #explorer-preview {
262
- background: #22262b;
263
- border-radius: 1.1em;
264
- box-shadow: 0 4px 24px #0004;
265
- border: 1px solid #1a73e8;
266
- padding: 1.5em 1.2em;
267
- min-height: 40vh;
268
- overflow: auto;
269
- }
270
- #explorer-main {
271
- margin-right: 0.5em;
272
- }
273
- #explorer-preview {
274
- margin-left: 0.5em;
275
- }
276
222
  @media (max-width: 900px) {
277
223
  .main {
278
224
  flex-direction: column;
@@ -283,4 +229,7 @@ body.light-theme .error {
283
229
  min-height: 20vh;
284
230
  padding: 1em 0.7em;
285
231
  }
232
+ #explorer-preview {
233
+ margin-left: 0;
234
+ }
286
235
  }
@@ -16,8 +16,6 @@ function getPaiPath(path) {
16
16
  function setExplorerView(view) {
17
17
  explorerView = view;
18
18
  localStorage.setItem('explorerView', view);
19
- document.getElementById('view-list').classList.toggle('active', view === 'list');
20
- document.getElementById('view-icons').classList.toggle('active', view === 'icons');
21
19
  }
22
20
 
23
21
  function normalizeExplorerPath(path) {
@@ -63,22 +61,8 @@ function renderExplorer(path, pushUrl=true) {
63
61
  }
64
62
  }
65
63
  html += '</ul>';
66
- } else {
67
- html += `<div class='explorer-icons'>`;
68
- if (data.path !== '.') {
69
- const parent = getPaiPath(data.path);
70
- html += `<div class='explorer-icon'><a href='#' data-path='${parent}' class='explorer-link' title='Pai'>(..)</a></div>`;
71
- }
72
- for (const entry of data.entries) {
73
- const entryPath = data.path === '.' ? entry.name : data.path + '/' + entry.name;
74
- if (entry.is_dir) {
75
- html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link' title='${entry.name}'>📁<br>${entry.name}</a></div>`;
76
- } else {
77
- html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link file-link' title='${entry.name}'>📄<br>${entry.name}</a></div>`;
78
- }
79
- }
80
- html += '</div>';
81
64
  }
65
+
82
66
  main.innerHTML = html;
83
67
  // Clear preview panel when changing directories
84
68
  const preview = document.getElementById('explorer-preview');
@@ -131,7 +115,7 @@ window.renderCodePreview = function(container, content, mode) {
131
115
  var textarea = document.createElement('textarea');
132
116
  textarea.value = (typeof content === 'string') ? content : '';
133
117
  container.appendChild(textarea);
134
- if (window.CodeMirror && container.offsetPai !== null) {
118
+ if (window.CodeMirror) {
135
119
  var editor = CodeMirror.fromTextArea(textarea, {
136
120
  lineNumbers: true,
137
121
  mode: mode || 'python',
@@ -140,7 +124,7 @@ window.renderCodePreview = function(container, content, mode) {
140
124
  indentUnit: 4,
141
125
  tabSize: 4,
142
126
  });
143
- editor.setSize('100%', 'calc(60vh)');
127
+ editor.setSize('100%', '60vh');
144
128
  return editor;
145
129
  } else {
146
130
  container.innerHTML = '<pre>' + (content ? String(content) : '') + '</pre>';
@@ -148,7 +132,7 @@ window.renderCodePreview = function(container, content, mode) {
148
132
  } catch (e) {
149
133
  container.innerHTML = '<pre>' + (content ? String(content) : '') + '</pre>';
150
134
  }
151
- }
135
+ };
152
136
 
153
137
  // Theme switcher logic
154
138
  function setTheme(dark) {
@@ -156,31 +140,28 @@ function setTheme(dark) {
156
140
  document.body.classList.add('dark-theme');
157
141
  document.body.classList.remove('light-theme');
158
142
  localStorage.setItem('theme', 'dark');
159
- document.getElementById('theme-switcher').textContent = 'Switch to Light Theme';
143
+ var themeIcon = document.getElementById('theme-icon');
144
+ if (themeIcon) themeIcon.textContent = '🌙'; // Moon for dark theme
160
145
  } else {
161
146
  document.body.classList.remove('dark-theme');
162
147
  document.body.classList.add('light-theme');
163
148
  localStorage.setItem('theme', 'light');
164
- document.getElementById('theme-switcher').textContent = 'Switch to Dark Theme';
149
+ var themeIcon = document.getElementById('theme-icon');
150
+ if (themeIcon) themeIcon.textContent = '☀️'; // Sun for light theme
165
151
  }
166
152
  }
167
153
  document.addEventListener('DOMContentLoaded', function() {
168
154
  // Initial theme
169
155
  var theme = localStorage.getItem('theme') || 'dark';
170
156
  setTheme(theme === 'dark');
171
- document.getElementById('theme-switcher').onclick = function() {
172
- setTheme(document.body.classList.contains('light-theme'));
173
- };
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
+ setExplorerView('list'); // Always use list view
174
164
  renderExplorer('.')
175
- setExplorerView(localStorage.getItem('explorerView') || 'list');
176
- document.getElementById('view-list').onclick = function() {
177
- setExplorerView('list');
178
- renderExplorer('.')
179
- };
180
- document.getElementById('view-icons').onclick = function() {
181
- setExplorerView('icons');
182
- renderExplorer('.')
183
- };
184
165
  });
185
166
 
186
167
  window.renderExplorer = renderExplorer;
@@ -16,8 +16,7 @@ function getPaiPath(path) {
16
16
  function setExplorerView(view) {
17
17
  explorerView = view;
18
18
  localStorage.setItem('explorerView', view);
19
- document.getElementById('view-list').classList.toggle('active', view === 'list');
20
- document.getElementById('view-icons').classList.toggle('active', view === 'icons');
19
+
21
20
  }
22
21
 
23
22
  function normalizeExplorerPath(path) {
@@ -52,33 +51,19 @@ function renderExplorer(path, pushUrl=true) {
52
51
  html += `<ul class='explorer-list'>`;
53
52
  if (data.path !== '.') {
54
53
  const parent = getPaiPath(data.path);
55
- html += `<li><a href='#' data-path='${parent}' class='explorer-link'><span class='explorer-entry'><span class='explorer-icon'>\u2b06\ufe0f</span><span class='explorer-name'>(.. parent)</span></span></a></li>`;
54
+ html += `<li><a href='#' data-path='${parent}' class='explorer-link'><span class='explorer-entry'><span class='explorer-icon'>⬆️</span><span class='explorer-name'>(.. parent)</span></span></a></li>`;
56
55
  }
57
56
  for (const entry of data.entries) {
58
57
  const entryPath = data.path === '.' ? entry.name : data.path + '/' + entry.name;
59
58
  if (entry.is_dir) {
60
- html += `<li><a href='#' data-path='${entryPath}' class='explorer-link'><span class='explorer-entry'><span class='explorer-icon'>\ud83d\udcc1</span><span class='explorer-name'>${entry.name}</span></span></a></li>`;
59
+ html += `<li><a href='#' data-path='${entryPath}' class='explorer-link'><span class='explorer-entry'><span class='explorer-icon'>📁</span><span class='explorer-name'>${entry.name}</span></span></a></li>`;
61
60
  } else {
62
- html += `<li><a href='#' data-path='${entryPath}' class='explorer-link file-link'><span class='explorer-entry'><span class='explorer-icon'>\ud83d\udcc4</span><span class='explorer-name'>${entry.name}</span></span></a></li>`;
61
+ html += `<li><a href='#' data-path='${entryPath}' class='explorer-link file-link'><span class='explorer-entry'><span class='explorer-icon'>📄</span><span class='explorer-name'>${entry.name}</span></span></a></li>`;
63
62
  }
64
63
  }
65
64
  html += '</ul>';
66
- } else {
67
- html += `<div class='explorer-icons'>`;
68
- if (data.path !== '.') {
69
- const parent = getPaiPath(data.path);
70
- html += `<div class='explorer-icon'><a href='#' data-path='${parent}' class='explorer-link' title='Pai'>(..)</a></div>`;
71
- }
72
- for (const entry of data.entries) {
73
- const entryPath = data.path === '.' ? entry.name : data.path + '/' + entry.name;
74
- if (entry.is_dir) {
75
- html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link' title='${entry.name}'>\ud83d\udcc1<br>${entry.name}</a></div>`;
76
- } else {
77
- html += `<div class='explorer-icon'><a href='#' data-path='${entryPath}' class='explorer-link file-link' title='${entry.name}'>\ud83d\udcc4<br>${entry.name}</a></div>`;
78
- }
79
- }
80
- html += '</div>';
81
65
  }
66
+
82
67
  main.innerHTML = html;
83
68
  // Clear preview panel when changing directories
84
69
  const preview = document.getElementById('explorer-preview');
@@ -131,7 +116,7 @@ window.renderCodePreview = function(container, content, mode) {
131
116
  var textarea = document.createElement('textarea');
132
117
  textarea.value = (typeof content === 'string') ? content : '';
133
118
  container.appendChild(textarea);
134
- if (window.CodeMirror && container.offsetPai !== null) {
119
+ if (window.CodeMirror) {
135
120
  var editor = CodeMirror.fromTextArea(textarea, {
136
121
  lineNumbers: true,
137
122
  mode: mode || 'python',
@@ -140,7 +125,7 @@ window.renderCodePreview = function(container, content, mode) {
140
125
  indentUnit: 4,
141
126
  tabSize: 4,
142
127
  });
143
- editor.setSize('100%', 'calc(60vh)');
128
+ editor.setSize('100%', '60vh');
144
129
  return editor;
145
130
  } else {
146
131
  container.innerHTML = '<pre>' + (content ? String(content) : '') + '</pre>';
@@ -156,31 +141,28 @@ function setTheme(dark) {
156
141
  document.body.classList.add('dark-theme');
157
142
  document.body.classList.remove('light-theme');
158
143
  localStorage.setItem('theme', 'dark');
159
- document.getElementById('theme-switcher').textContent = 'Switch to Light Theme';
144
+ var themeIcon = document.getElementById('theme-icon');
145
+ if (themeIcon) themeIcon.textContent = '🌙'; // Moon for dark theme
160
146
  } else {
161
147
  document.body.classList.remove('dark-theme');
162
148
  document.body.classList.add('light-theme');
163
149
  localStorage.setItem('theme', 'light');
164
- document.getElementById('theme-switcher').textContent = 'Switch to Dark Theme';
150
+ var themeIcon = document.getElementById('theme-icon');
151
+ if (themeIcon) themeIcon.textContent = '☀️'; // Sun for light theme
165
152
  }
166
153
  }
167
154
  document.addEventListener('DOMContentLoaded', function() {
168
155
  // Initial theme
169
156
  var theme = localStorage.getItem('theme') || 'dark';
170
157
  setTheme(theme === 'dark');
171
- document.getElementById('theme-switcher').onclick = function() {
172
- setTheme(document.body.classList.contains('light-theme'));
173
- };
158
+ var themeSwitcher = document.getElementById('theme-switcher');
159
+ if (themeSwitcher) {
160
+ themeSwitcher.onclick = function() {
161
+ setTheme(document.body.classList.contains('light-theme'));
162
+ };
163
+ }
164
+ setExplorerView('list'); // Always use list view
174
165
  renderExplorer('.')
175
- setExplorerView(localStorage.getItem('explorerView') || 'list');
176
- document.getElementById('view-list').onclick = function() {
177
- setExplorerView('list');
178
- renderExplorer('.')
179
- };
180
- document.getElementById('view-icons').onclick = function() {
181
- setExplorerView('icons');
182
- renderExplorer('.')
183
- };
184
166
  });
185
167
 
186
168
  window.renderExplorer = renderExplorer;
@@ -0,0 +1,44 @@
1
+ import io
2
+ from rich.console import Console
3
+ from janito.rich_utils import RichPrinter
4
+
5
+
6
+ def test_print_info(capsys=None):
7
+ buf = io.StringIO()
8
+ printer = RichPrinter(
9
+ console=Console(file=buf, force_terminal=True, color_system=None)
10
+ )
11
+ printer.print_info("info message")
12
+ output = buf.getvalue()
13
+ assert "info message" in output
14
+ assert "cyan" in output or output # Style is present if rich renders ANSI
15
+
16
+
17
+ def test_print_error():
18
+ buf = io.StringIO()
19
+ printer = RichPrinter(
20
+ console=Console(file=buf, force_terminal=True, color_system=None)
21
+ )
22
+ printer.print_error("error message")
23
+ output = buf.getvalue()
24
+ assert "error message" in output
25
+
26
+
27
+ def test_print_warning():
28
+ buf = io.StringIO()
29
+ printer = RichPrinter(
30
+ console=Console(file=buf, force_terminal=True, color_system=None)
31
+ )
32
+ printer.print_warning("warning message")
33
+ output = buf.getvalue()
34
+ assert "warning message" in output
35
+
36
+
37
+ def test_print_magenta():
38
+ buf = io.StringIO()
39
+ printer = RichPrinter(
40
+ console=Console(file=buf, force_terminal=True, color_system=None)
41
+ )
42
+ printer.print_magenta("magenta message")
43
+ output = buf.getvalue()
44
+ assert "magenta message" in output
janito/web/app.py CHANGED
@@ -1,19 +1,13 @@
1
1
  from flask import (
2
2
  Flask,
3
3
  request,
4
- Response,
5
4
  send_from_directory,
6
5
  jsonify,
7
6
  render_template,
8
7
  )
9
- from queue import Queue
10
8
  import json
11
- from janito.agent.queued_message_handler import QueuedMessageHandler
12
9
  from janito.agent.profile_manager import AgentProfileManager
13
10
  import os
14
- import threading
15
- import traceback
16
- import sys
17
11
 
18
12
  from janito.agent.runtime_config import unified_config, runtime_config
19
13
 
@@ -53,13 +47,6 @@ conversation_file = os.path.expanduser("~/.janito/last_conversation_web.json")
53
47
  # Initially no conversation loaded
54
48
  conversation = None
55
49
 
56
-
57
- # Global event queue for streaming
58
- stream_queue = Queue()
59
-
60
- # Create a QueuedMessageHandler with the queue
61
- message_handler = QueuedMessageHandler(stream_queue)
62
-
63
50
  # Instantiate the Agent with config-driven parameters (no tool_handler)
64
51
  agent = profile_manager.agent
65
52
 
@@ -156,65 +143,3 @@ def new_conversation():
156
143
  global conversation
157
144
  conversation = []
158
145
  return jsonify({"status": "ok"})
159
-
160
-
161
- @app.route("/execute_stream", methods=["POST"])
162
- def execute_stream():
163
- data = request.get_json()
164
- user_input = data.get("input", "")
165
-
166
- global conversation
167
- if conversation is None:
168
- # If no conversation loaded, start a new one
169
- conversation = []
170
-
171
- # Always start with the system prompt as the first message
172
- if not conversation or conversation[0]["role"] != "system":
173
- conversation.insert(0, {"role": "system", "content": system_prompt_template})
174
-
175
- # Append the new user message
176
- conversation.append({"role": "user", "content": user_input})
177
-
178
- def run_agent():
179
- try:
180
- response = agent.chat(conversation, message_handler=message_handler)
181
- if response and "content" in response:
182
- conversation.append(
183
- {"role": "assistant", "content": response["content"]}
184
- )
185
- try:
186
- os.makedirs(os.path.dirname(conversation_file), exist_ok=True)
187
- with open(conversation_file, "w", encoding="utf-8") as f:
188
- json.dump(conversation, f, indent=2)
189
- except Exception as e:
190
- print(f"Error saving conversation: {e}")
191
- except Exception as e:
192
- tb = traceback.format_exc()
193
- stream_queue.put({"type": "error", "error": str(e), "traceback": tb})
194
- finally:
195
- stream_queue.put(None)
196
-
197
- threading.Thread(target=run_agent, daemon=True).start()
198
-
199
- def generate():
200
- while True:
201
- content = stream_queue.get()
202
- if content is None:
203
- break
204
- if isinstance(content, tuple) and content[0] == "tool_progress":
205
- message = json.dumps({"type": "tool_progress", "data": content[1]})
206
- else:
207
- message = json.dumps(content)
208
- yield f"data: {message}\n\n"
209
- sys.stdout.flush()
210
-
211
- return Response(
212
- generate(),
213
- mimetype="text/event-stream",
214
- headers={
215
- "Cache-Control": "no-cache",
216
- "X-Accel-Buffering": "no",
217
- "Connection": "keep-alive",
218
- "Transfer-Encoding": "chunked",
219
- },
220
- )