janito 1.8.0__py3-none-any.whl → 1.9.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 (119) hide show
  1. janito/__init__.py +1 -1
  2. janito/agent/config_defaults.py +23 -0
  3. janito/agent/config_utils.py +0 -9
  4. janito/agent/conversation.py +31 -9
  5. janito/agent/conversation_api.py +32 -2
  6. janito/agent/conversation_history.py +53 -0
  7. janito/agent/conversation_tool_calls.py +11 -8
  8. janito/agent/openai_client.py +11 -3
  9. janito/agent/openai_schema_generator.py +9 -6
  10. janito/agent/providers.py +77 -0
  11. janito/agent/rich_message_handler.py +1 -1
  12. janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +8 -8
  13. janito/agent/tool_executor.py +18 -10
  14. janito/agent/tool_use_tracker.py +16 -0
  15. janito/agent/tools/__init__.py +7 -9
  16. janito/agent/tools/create_directory.py +7 -6
  17. janito/agent/tools/create_file.py +29 -54
  18. janito/agent/tools/delete_text_in_file.py +97 -0
  19. janito/agent/tools/fetch_url.py +11 -3
  20. janito/agent/tools/find_files.py +37 -25
  21. janito/agent/tools/get_file_outline/__init__.py +1 -0
  22. janito/agent/tools/{outline_file/__init__.py → get_file_outline/core.py} +12 -15
  23. janito/agent/tools/get_file_outline/python_outline.py +134 -0
  24. janito/agent/tools/{search_outline.py → get_file_outline/search_outline.py} +9 -0
  25. janito/agent/tools/get_lines.py +15 -11
  26. janito/agent/tools/move_file.py +10 -11
  27. janito/agent/tools/remove_directory.py +2 -2
  28. janito/agent/tools/remove_file.py +11 -13
  29. janito/agent/tools/replace_file.py +62 -0
  30. janito/agent/tools/replace_text_in_file.py +3 -3
  31. janito/agent/tools/run_bash_command.py +3 -7
  32. janito/agent/tools/run_powershell_command.py +39 -28
  33. janito/agent/tools/run_python_command.py +3 -5
  34. janito/agent/tools/search_text.py +10 -14
  35. janito/agent/tools/validate_file_syntax/__init__.py +1 -0
  36. janito/agent/tools/validate_file_syntax/core.py +92 -0
  37. janito/agent/tools/validate_file_syntax/css_validator.py +35 -0
  38. janito/agent/tools/validate_file_syntax/html_validator.py +77 -0
  39. janito/agent/tools/validate_file_syntax/js_validator.py +27 -0
  40. janito/agent/tools/validate_file_syntax/json_validator.py +6 -0
  41. janito/agent/tools/validate_file_syntax/markdown_validator.py +66 -0
  42. janito/agent/tools/validate_file_syntax/ps1_validator.py +32 -0
  43. janito/agent/tools/validate_file_syntax/python_validator.py +5 -0
  44. janito/agent/tools/validate_file_syntax/xml_validator.py +11 -0
  45. janito/agent/tools/validate_file_syntax/yaml_validator.py +6 -0
  46. janito/agent/tools_utils/__init__.py +1 -0
  47. janito/agent/tools_utils/dir_walk_utils.py +23 -0
  48. janito/agent/{tools/outline_file → tools_utils}/formatting.py +5 -2
  49. janito/agent/{tools → tools_utils}/gitignore_utils.py +0 -3
  50. janito/agent/tools_utils/utils.py +30 -0
  51. janito/cli/_livereload_log_utils.py +13 -0
  52. janito/cli/arg_parser.py +45 -3
  53. janito/cli/{runner/cli_main.py → cli_main.py} +120 -20
  54. janito/cli/livereload_starter.py +60 -0
  55. janito/cli/main.py +110 -21
  56. janito/cli/one_shot.py +66 -0
  57. janito/cli/termweb_starter.py +2 -2
  58. janito/livereload/app.py +25 -0
  59. janito/rich_utils.py +0 -22
  60. janito/{cli_chat_shell → shell}/commands/__init__.py +18 -11
  61. janito/{cli_chat_shell → shell}/commands/config.py +4 -4
  62. janito/shell/commands/conversation_restart.py +72 -0
  63. janito/shell/commands/edit.py +21 -0
  64. janito/shell/commands/history_view.py +18 -0
  65. janito/shell/commands/livelogs.py +40 -0
  66. janito/{cli_chat_shell → shell}/commands/prompt.py +10 -6
  67. janito/shell/commands/session.py +32 -0
  68. janito/{cli_chat_shell → shell}/commands/session_control.py +2 -7
  69. janito/{cli_chat_shell → shell}/commands/sum.py +6 -6
  70. janito/{cli_chat_shell → shell}/commands/termweb_log.py +10 -10
  71. janito/shell/commands/tools.py +23 -0
  72. janito/{cli_chat_shell → shell}/commands/utility.py +5 -4
  73. janito/{cli_chat_shell → shell}/commands/verbose.py +1 -1
  74. janito/shell/commands.py +40 -0
  75. janito/shell/main.py +321 -0
  76. janito/{cli_chat_shell/shell_command_completer.py → shell/prompt/completer.py} +1 -1
  77. janito/{cli_chat_shell/chat_ui.py → shell/prompt/session_setup.py} +19 -5
  78. janito/{cli_chat_shell/session_manager.py → shell/session/manager.py} +53 -3
  79. janito/{cli_chat_shell/ui.py → shell/ui/interactive.py} +23 -15
  80. janito/termweb/app.py +3 -3
  81. janito/termweb/static/editor.css +146 -0
  82. janito/termweb/static/editor.css.bak +27 -0
  83. janito/termweb/static/editor.html +15 -213
  84. janito/termweb/static/editor.html.bak +16 -215
  85. janito/termweb/static/editor.js +209 -0
  86. janito/termweb/static/editor.js.bak +227 -0
  87. janito/termweb/static/index.html +2 -3
  88. janito/termweb/static/index.html.bak +2 -3
  89. janito/termweb/static/termweb.css.bak +33 -84
  90. janito/termweb/static/termweb.js +15 -34
  91. janito/termweb/static/termweb.js.bak +18 -36
  92. {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/METADATA +6 -3
  93. janito-1.9.0.dist-info/RECORD +151 -0
  94. {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/WHEEL +1 -1
  95. janito/agent/tools/dir_walk_utils.py +0 -16
  96. janito/agent/tools/memory.py +0 -48
  97. janito/agent/tools/outline_file/python_outline.py +0 -71
  98. janito/agent/tools/present_choices_test.py +0 -18
  99. janito/agent/tools/rich_live.py +0 -44
  100. janito/agent/tools/tools_utils.py +0 -56
  101. janito/agent/tools/utils.py +0 -33
  102. janito/agent/tools/validate_file_syntax.py +0 -163
  103. janito/cli_chat_shell/chat_loop.py +0 -163
  104. janito/cli_chat_shell/chat_state.py +0 -38
  105. janito/cli_chat_shell/commands/history_start.py +0 -37
  106. janito/cli_chat_shell/commands/session.py +0 -48
  107. janito-1.8.0.dist-info/RECORD +0 -127
  108. /janito/agent/tools/{outline_file → get_file_outline}/markdown_outline.py +0 -0
  109. /janito/cli/{runner/_termweb_log_utils.py → _termweb_log_utils.py} +0 -0
  110. /janito/cli/{runner/config.py → config_runner.py} +0 -0
  111. /janito/cli/{runner/formatting.py → formatting_runner.py} +0 -0
  112. /janito/{cli/runner → shell}/__init__.py +0 -0
  113. /janito/{cli_chat_shell → shell}/commands/lang.py +0 -0
  114. /janito/{cli_chat_shell → shell/prompt}/load_prompt.py +0 -0
  115. /janito/{cli_chat_shell/config_shell.py → shell/session/config.py} +0 -0
  116. /janito/{cli_chat_shell/__init__.py → shell/session/history.py} +0 -0
  117. {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/entry_points.txt +0 -0
  118. {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/licenses/LICENSE +0 -0
  119. {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/top_level.txt +0 -0
@@ -7,14 +7,13 @@ from janito.agent.runtime_config import runtime_config
7
7
  from janito.i18n import tr
8
8
 
9
9
 
10
- def print_summary(console, data, continue_session):
11
- if not data:
12
- return
13
- console.print("[bold cyan]Last saved conversation:[/bold cyan]")
14
-
15
-
16
- def print_welcome(console, version=None, continued=False):
10
+ def print_welcome(console, version=None, continue_id=None):
17
11
  version_str = f" (v{version})" if version else ""
12
+ # DEBUG: Show continue_id/session_id at runtime
13
+ if continue_id:
14
+ console.print(
15
+ f"[bold yellow]{tr('Resuming session')}[/bold yellow] [white on blue]{continue_id}[/white on blue]\n"
16
+ )
18
17
  if runtime_config.get("vanilla_mode", False):
19
18
  console.print(
20
19
  f"[bold magenta]{tr('Welcome to Janito{version_str} in [white on magenta]VANILLA MODE[/white on magenta]! Tools, system prompt, and temperature are disabled unless overridden.', version_str=version_str)}[/bold magenta]\n"
@@ -35,6 +34,8 @@ def get_toolbar_func(
35
34
  role_ref=None,
36
35
  style_ref=None,
37
36
  version=None,
37
+ session_id=None,
38
+ history_ref=None,
38
39
  ):
39
40
  from prompt_toolkit.application.current import get_app
40
41
 
@@ -79,7 +80,8 @@ def get_toolbar_func(
79
80
  if style_part:
80
81
  first_line_parts.append(style_part)
81
82
  first_line = " | ".join(first_line_parts)
82
- left = f" {tr('Messages')}: <msg_count>{len(messages_ref())}</msg_count>"
83
+ msg_count = len(history_ref()) if history_ref else len(messages_ref())
84
+ left = f" {tr('Messages')}: <msg_count>{msg_count}</msg_count>"
83
85
  tokens_part = ""
84
86
  if (
85
87
  prompt_tokens is not None
@@ -91,22 +93,27 @@ def get_toolbar_func(
91
93
  f"{tr('Completion')}: {format_tokens(completion_tokens, 'tokens_out')}, "
92
94
  f"{tr('Total')}: {format_tokens(total_tokens, 'tokens_total')}"
93
95
  )
94
- # Move /help and /start tips to key bindings/info line
96
+ # Move /help and /restart tips to key bindings/info line
95
97
  # Compose second/status line (no help_part)
96
- second_line = f"{left}{tokens_part}"
97
- total_len = len(left) + len(tokens_part)
98
+ session_part = (
99
+ f" | Session ID: <session_id>{session_id}</session_id>"
100
+ if session_id
101
+ else ""
102
+ )
103
+ second_line = f"{left}{tokens_part}{session_part}"
104
+ total_len = len(left) + len(tokens_part) + len(session_part)
98
105
  if first_line:
99
106
  total_len += len(first_line) + 3
100
107
  if total_len < width:
101
108
  padding = " " * (width - total_len)
102
- second_line = f"{left}{tokens_part}{padding}"
103
- # Add key bindings info as an extra line, now including /help and /start
109
+ second_line = f"{left}{tokens_part}{session_part}{padding}"
110
+ # Add key bindings info as an extra line, now including /help and /restart
104
111
  bindings_line = (
105
112
  f"<b> F12</b>: {tr('Quick Action')} | "
106
113
  f"<b>Ctrl-Y</b>: {tr('Yes')} | "
107
114
  f"<b>Ctrl-N</b>: {tr('No')} | "
108
115
  f"<b>/help</b>: {tr('Help')} | "
109
- f"<b>/start</b>: {tr('New Task')}"
116
+ f"<b>/restart</b>: {tr('Reset Conversation')}"
110
117
  )
111
118
  if first_line:
112
119
  toolbar_text = first_line + "\n" + second_line + "\n" + bindings_line
@@ -161,6 +168,7 @@ def get_prompt_session(get_toolbar_func, mem_history):
161
168
  "tokens_out": "ansigreen bold",
162
169
  "tokens_total": "ansiyellow bold",
163
170
  "msg_count": "bg:#333333 #ffff00 bold",
171
+ "session_id": "bg:#005f00 #ffffff bold",
164
172
  "b": "bold",
165
173
  "prompt": "bg:#005f5f #ffffff", # (legacy, not used)
166
174
  # Style for prompt_toolkit input line:
@@ -172,7 +180,7 @@ def get_prompt_session(get_toolbar_func, mem_history):
172
180
  "inputline": "bg:#005fdd #ffffff", # Blue background for the prompt label (icon/text)
173
181
  }
174
182
  )
175
- from .shell_command_completer import ShellCommandCompleter
183
+ from janito.shell.prompt.completer import ShellCommandCompleter
176
184
 
177
185
  completer = ShellCommandCompleter()
178
186
  return PromptSession(
janito/termweb/app.py CHANGED
@@ -1,10 +1,10 @@
1
1
  from quart import Quart, send_from_directory, request, jsonify, websocket
2
2
  import os
3
- from janito.agent.tools.dir_walk_utils import walk_dir_with_gitignore
3
+ from janito.agent.tools_utils.dir_walk_utils import walk_dir_with_gitignore
4
4
 
5
5
  app = Quart(__name__)
6
6
 
7
- BASE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))
7
+ BASE_DIR = os.getcwd()
8
8
 
9
9
 
10
10
  @app.route("/")
@@ -50,7 +50,7 @@ async def api_explorer(path="."):
50
50
  # GET: comportamento actual
51
51
  if os.path.isdir(abs_path):
52
52
  entries = []
53
- walker = walk_dir_with_gitignore(abs_path, recursive=False)
53
+ walker = walk_dir_with_gitignore(abs_path, max_depth=1)
54
54
  for root, dirs, files in walker:
55
55
  for entry in sorted(dirs):
56
56
  entries.append({"name": entry, "is_dir": True})
@@ -0,0 +1,146 @@
1
+ /* Highlight active line in CodeMirror */
2
+ .CodeMirror-activeline-background {
3
+ background: #353b45 !important;
4
+ }
5
+ body.light-theme .CodeMirror-activeline-background {
6
+ background: #e0eaff !important;
7
+ }
8
+ html, body {
9
+ height: 100%;
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+ body {
14
+ display: flex;
15
+ flex-direction: column;
16
+ min-height: 100vh;
17
+ background: #181a1b;
18
+ color: #eee;
19
+ transition: background 0.2s, color 0.2s;
20
+ }
21
+ body.light-theme {
22
+ background: #f5f5f5;
23
+ color: #222;
24
+ }
25
+ .main {
26
+ flex: 1 1 auto;
27
+ display: flex;
28
+ flex-direction: column;
29
+ justify-content: stretch;
30
+ align-items: stretch;
31
+ min-height: 0;
32
+ padding: 0;
33
+ margin: 0;
34
+ height: 100%;
35
+ }
36
+ .editor-pane {
37
+ flex: 1 1 auto;
38
+ display: flex;
39
+ flex-direction: column;
40
+ height: 100%;
41
+ min-height: 0;
42
+ padding: 0 !important;
43
+ margin: 0 !important;
44
+ }
45
+ .CodeMirror {
46
+ flex: 1 1 auto;
47
+ height: 100% !important;
48
+ min-height: 0;
49
+ font-size: 1.1em;
50
+ background: #282a36;
51
+ color: #f8f8f2;
52
+ border-radius: 0;
53
+ border: none;
54
+ transition: background 0.2s, color 0.2s;
55
+ }
56
+ body.light-theme .CodeMirror {
57
+ background: #fff;
58
+ color: #222;
59
+ }
60
+ .header {
61
+ background: #222;
62
+ color: #fff;
63
+ padding: 10px 24px;
64
+ font-size: 1.3em;
65
+ font-weight: bold;
66
+ position: relative;
67
+ transition: background 0.2s, color 0.2s;
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: flex-end;
71
+ height: 56px;
72
+ }
73
+
74
+ .header-title {
75
+ position: absolute;
76
+ left: 50%;
77
+ top: 50%;
78
+ transform: translate(-50%, -50%);
79
+ pointer-events: none;
80
+ font-weight: bold;
81
+ font-size: 1.3em;
82
+ }
83
+
84
+ .footer {
85
+ width: 100%;
86
+ background: #23272b;
87
+ color: #fff;
88
+ padding: 10px 24px;
89
+ box-sizing: border-box;
90
+ display: flex;
91
+ align-items: center;
92
+ justify-content: flex-start;
93
+ position: fixed;
94
+ bottom: 0;
95
+ left: 0;
96
+ z-index: 100;
97
+ border-top: 1px solid #333;
98
+ min-height: 48px;
99
+ }
100
+
101
+ .save-btn {
102
+ /* No margin needed, align left in footer */
103
+ }
104
+
105
+ .theme-switcher {
106
+ margin-left: auto;
107
+ }
108
+
109
+
110
+ .save-btn {
111
+ background: #4caf50;
112
+ color: #fff;
113
+ border: none;
114
+ border-radius: 4px;
115
+ padding: 6px 14px;
116
+ cursor: pointer;
117
+ font-size: 1em;
118
+ }
119
+
120
+ .theme-switcher {
121
+ display: flex;
122
+ align-items: center;
123
+ justify-content: center;
124
+ width: 36px;
125
+ height: 36px;
126
+ font-size: 1.3em;
127
+ line-height: 1;
128
+ padding: 0;
129
+ background: #444;
130
+ color: #fff;
131
+ border: none;
132
+ border-radius: 4px;
133
+ cursor: pointer;
134
+ transition: background 0.2s, color 0.2s;
135
+ }
136
+
137
+ body.light-theme .theme-switcher {
138
+ background: #ddd;
139
+ color: #222;
140
+ }
141
+
142
+ body.light-theme .header {
143
+ background: #eaeaea;
144
+ color: #222;
145
+ }
146
+
@@ -0,0 +1,27 @@
1
+
2
+ .save-btn {
3
+ background: linear-gradient(90deg, #00ff99 0%, #00cfff 100%);
4
+ color: #181a1b;
5
+ border: none;
6
+ border-radius: 8px;
7
+ padding: 12px 38px;
8
+ cursor: pointer;
9
+ font-size: 1.18em;
10
+ font-weight: 900;
11
+ box-shadow: 0 0 16px 4px rgba(0,255,153,0.35), 0 2px 16px 0 rgba(0,207,255,0.18);
12
+ outline: 2.5px solid #00cfff;
13
+ outline-offset: 2px;
14
+ transition: box-shadow 0.2s, outline 0.2s, background 0.2s, color 0.2s;
15
+ letter-spacing: 0.7px;
16
+ animation: save-btn-pop 0.7s cubic-bezier(.68,-0.55,.27,1.55) 1;
17
+ min-width: 110px;
18
+ min-height: 44px;
19
+ z-index: 2;
20
+ text-shadow: 0 2px 8px #fff8, 0 1px 0 #fff8;
21
+ }
22
+ .save-btn:hover, .save-btn:focus {
23
+ background: linear-gradient(90deg, #00cfff 0%, #00ff99 100%);
24
+ color: #111;
25
+ box-shadow: 0 0 24px 6px rgba(0,255,153,0.45), 0 4px 24px 0 rgba(0,207,255,0.28);
26
+ outline: 3px solid #00ff99;
27
+ }
@@ -8,231 +8,33 @@
8
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.css">
9
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/dracula.min.css">
10
10
  <link rel="stylesheet" href="/static/termweb.css">
11
- <style>
12
- html, body {
13
- height: 100%;
14
- margin: 0;
15
- padding: 0;
16
- }
17
- body {
18
- display: flex;
19
- flex-direction: column;
20
- min-height: 100vh;
21
- background: #181a1b;
22
- color: #eee;
23
- transition: background 0.2s, color 0.2s;
24
- }
25
- body.light-theme {
26
- background: #f5f5f5;
27
- color: #222;
28
- }
29
- .main {
30
- flex: 1 1 auto;
31
- display: flex;
32
- flex-direction: column;
33
- justify-content: stretch;
34
- align-items: stretch;
35
- min-height: 0;
36
- }
37
- .editor-pane {
38
- flex: 1 1 auto;
39
- display: flex;
40
- flex-direction: column;
41
- height: 100%;
42
- min-height: 0;
43
- }
44
- .CodeMirror {
45
- flex: 1 1 auto;
46
- height: 100% !important;
47
- min-height: 0;
48
- font-size: 1.1em;
49
- background: #282a36;
50
- color: #f8f8f2;
51
- border-radius: 0;
52
- border: none;
53
- transition: background 0.2s, color 0.2s;
54
- }
55
- body.light-theme .CodeMirror {
56
- background: #fff;
57
- color: #222;
58
- }
59
- .footer {
60
- flex-shrink: 0;
61
- background: #222;
62
- color: #eee;
63
- padding: 12px 0 8px 0;
64
- text-align: center;
65
- width: 100%;
66
- position: fixed;
67
- left: 0;
68
- bottom: 0;
69
- z-index: 100;
70
- transition: background 0.2s, color 0.2s;
71
- }
72
- body.light-theme .footer {
73
- background: #eaeaea;
74
- color: #222;
75
- }
76
- .footer ul {
77
- list-style: none;
78
- padding: 0;
79
- margin: 8px 0 0 0;
80
- display: flex;
81
- justify-content: center;
82
- gap: 2em;
83
- }
84
- .footer li {
85
- display: inline;
86
- }
87
- .header {
88
- background: #222;
89
- color: #fff;
90
- padding: 10px 0;
91
- text-align: center;
92
- font-size: 1.3em;
93
- font-weight: bold;
94
- position: relative;
95
- transition: background 0.2s, color 0.2s;
96
- }
97
- body.light-theme .header {
98
- background: #eaeaea;
99
- color: #222;
100
- }
101
- .theme-switcher {
102
- position: absolute;
103
- right: 20px;
104
- top: 10px;
105
- background: #444;
106
- color: #fff;
107
- border: none;
108
- border-radius: 4px;
109
- padding: 6px 14px;
110
- cursor: pointer;
111
- font-size: 1em;
112
- transition: background 0.2s, color 0.2s;
113
- }
114
- body.light-theme .theme-switcher {
115
- background: #ddd;
116
- color: #222;
117
- }
118
- </style>
11
+ <link rel="stylesheet" href="/static/editor.css">
119
12
  </head>
120
13
  <body>
121
14
  <div class="header">
122
- TermWeb Editor
123
- <button class="save-btn" id="save-btn" style="margin-right:12px;background:#4caf50;color:#fff;border:none;border-radius:4px;padding:6px 14px;cursor:pointer;font-size:1em;">Gravar</button>
124
- <button class="theme-switcher" id="theme-switcher">Alternate to light theme</button>
15
+ <div class="header-title">TermWeb Editor</div>
16
+ <button class="theme-switcher" id="theme-switcher" title="Alternator tema">
17
+ <span id="theme-icon" aria-label="Switch theme" style="pointer-events:none;">🌙</span>
18
+ </button>
125
19
  </div>
20
+ <div class="footer">
21
+ <button class="save-btn" id="save-btn">Save</button>
22
+ </div>
23
+ <div id="save-popup" style="display:none;position:fixed;top:32px;right:32px;z-index:9999;background:#323b4c;color:#fff;padding:14px 28px;border-radius:8px;box-shadow:0 2px 12px #0007;font-size:1.1em;">Saved!</div>
126
24
  <div class="main">
127
25
  <div class="editor-pane">
128
26
  <textarea id="code" name="code"></textarea>
129
27
  </div>
130
28
  </div>
131
- <div class="footer">
132
- <div class="subtitle">
133
- Desenvolvido por <a href="https://janito.dev" target="_blank">janito.dev</a> — agente de programação com IA
134
- </div>
135
- <ul>
136
- <li>🌐 <a href="https://janito.dev" target="_blank">janito.dev</a></li>
137
- <li>📚 <a href="https://docs.janito.dev" target="_blank">Documentation</a></li>
138
- <li>💻 <a href="https://github.com/joaompinto/janito" target="_blank">GitHub</a></li>
139
- </ul>
140
- <span style="font-size:0.9em;opacity:0.7;">_generated by janito.dev_</span>
141
- </div>
142
29
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js"></script>
143
30
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/python/python.min.js"></script>
144
31
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/dracula.min.js"></script>
145
- <script>
146
- // --- Load file content via AJAX if ?path=... is present ---
147
- function getQueryParam(name) {
148
- const url = new URL(window.location.href);
149
- return url.searchParams.get(name);
150
- }
151
- const filePath = getQueryParam('path');
152
- let initialContent = `# Bem-vindo ao TermWeb!\n# Este é um editor CodeMirror ao vivo.\n\nprint("Olá, janito.dev!")`;
153
- if (filePath) {
154
- fetch(`/api/explorer/${encodeURIComponent(filePath)}`)
155
- .then(resp => resp.json())
156
- .then(data => {
157
- if (data.type === 'file') {
158
- initialContent = data.content;
159
- if (window.editorInstance) {
160
- window.editorInstance.setValue(initialContent);
161
- }
162
- } else if (data.error) {
163
- initialContent = '# Error: ' + data.error;
164
- if (window.editorInstance) {
165
- window.editorInstance.setValue(initialContent);
166
- }
167
- }
168
- })
169
- .catch(err => {
170
- initialContent = '# Error ao carregar arquivo: ' + err;
171
- if (window.editorInstance) {
172
- window.editorInstance.setValue(initialContent);
173
- }
174
- });
175
- }
176
- document.addEventListener('DOMContentLoaded', function() {
177
- var editorInstance = CodeMirror.fromTextArea(document.getElementById('code'), {
178
- lineNumbers: true,
179
- mode: 'python',
180
- theme: 'dracula',
181
- indentUnit: 4,
182
- tabSize: 4,
183
- });
184
- window.editorInstance = editorInstance;
185
- editorInstance.setSize('100%', 'calc(100vh - 60px - 70px)'); // header/footer height
186
- editorInstance.setValue(initialContent);
32
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/selection/active-line.min.js"></script>
33
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/search/search.min.js"></script>
34
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/search/searchcursor.min.js"></script>
35
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/dialog/dialog.min.js"></script>
36
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/dialog/dialog.min.css">
187
37
 
188
- // Theme switcher logic
189
- var themeSwitcher = document.getElementById('theme-switcher');
190
- var isDark = true;
191
- themeSwitcher.addEventListener('click', function() {
192
- isDark = !isDark;
193
- if (isDark) {
194
- document.body.classList.remove('light-theme');
195
- editorInstance.setOption('theme', 'dracula');
196
- themeSwitcher.textContent = 'Alternate to light theme';
197
- } else {
198
- document.body.classList.add('light-theme');
199
- editorInstance.setOption('theme', 'default');
200
- themeSwitcher.textContent = 'Alternate to dark theme';
201
- }
202
- });
203
- // Set initial state
204
- document.body.classList.remove('light-theme');
205
- editorInstance.setOption('theme', 'dracula');
206
- themeSwitcher.textContent = 'Alternate to light theme';
207
- // Botão de Gravar
208
- var saveBtn = document.getElementById('save-btn');
209
- saveBtn.addEventListener('click', function() {
210
- if (!filePath) {
211
- alert('Nenhum arquivo aberto para gravar.');
212
- return;
213
- }
214
- const content = editorInstance.getValue();
215
- fetch(`/api/explorer/${encodeURIComponent(filePath)}`, {
216
- method: 'POST',
217
- headers: {
218
- 'Content-Type': 'application/json',
219
- },
220
- body: JSON.stringify({ content }),
221
- })
222
- .then(resp => resp.json())
223
- .then(data => {
224
- if (data.success) {
225
- saveBtn.textContent = 'Gravado!';
226
- setTimeout(() => saveBtn.textContent = 'Gravar', 1200);
227
- } else {
228
- alert('Error saving: ' + (data.error || 'desconhecido'));
229
- }
230
- })
231
- .catch(err => {
232
- alert('Error saving: ' + err);
233
- });
234
- });
235
- });
236
- </script>
38
+ <script src="/static/editor.js"></script>
237
39
  </body>
238
40
  </html>