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.
- janito/__init__.py +1 -1
- janito/agent/config_defaults.py +23 -0
- janito/agent/config_utils.py +0 -9
- janito/agent/conversation.py +31 -9
- janito/agent/conversation_api.py +32 -2
- janito/agent/conversation_history.py +53 -0
- janito/agent/conversation_tool_calls.py +11 -8
- janito/agent/openai_client.py +11 -3
- janito/agent/openai_schema_generator.py +9 -6
- janito/agent/providers.py +77 -0
- janito/agent/rich_message_handler.py +1 -1
- janito/agent/templates/profiles/system_prompt_template_base.txt.j2 +8 -8
- janito/agent/tool_executor.py +18 -10
- janito/agent/tool_use_tracker.py +16 -0
- janito/agent/tools/__init__.py +7 -9
- janito/agent/tools/create_directory.py +7 -6
- janito/agent/tools/create_file.py +29 -54
- janito/agent/tools/delete_text_in_file.py +97 -0
- janito/agent/tools/fetch_url.py +11 -3
- janito/agent/tools/find_files.py +37 -25
- janito/agent/tools/get_file_outline/__init__.py +1 -0
- janito/agent/tools/{outline_file/__init__.py → get_file_outline/core.py} +12 -15
- janito/agent/tools/get_file_outline/python_outline.py +134 -0
- janito/agent/tools/{search_outline.py → get_file_outline/search_outline.py} +9 -0
- janito/agent/tools/get_lines.py +15 -11
- janito/agent/tools/move_file.py +10 -11
- janito/agent/tools/remove_directory.py +2 -2
- janito/agent/tools/remove_file.py +11 -13
- janito/agent/tools/replace_file.py +62 -0
- janito/agent/tools/replace_text_in_file.py +3 -3
- janito/agent/tools/run_bash_command.py +3 -7
- janito/agent/tools/run_powershell_command.py +39 -28
- janito/agent/tools/run_python_command.py +3 -5
- janito/agent/tools/search_text.py +10 -14
- janito/agent/tools/validate_file_syntax/__init__.py +1 -0
- janito/agent/tools/validate_file_syntax/core.py +92 -0
- janito/agent/tools/validate_file_syntax/css_validator.py +35 -0
- janito/agent/tools/validate_file_syntax/html_validator.py +77 -0
- janito/agent/tools/validate_file_syntax/js_validator.py +27 -0
- janito/agent/tools/validate_file_syntax/json_validator.py +6 -0
- janito/agent/tools/validate_file_syntax/markdown_validator.py +66 -0
- janito/agent/tools/validate_file_syntax/ps1_validator.py +32 -0
- janito/agent/tools/validate_file_syntax/python_validator.py +5 -0
- janito/agent/tools/validate_file_syntax/xml_validator.py +11 -0
- janito/agent/tools/validate_file_syntax/yaml_validator.py +6 -0
- janito/agent/tools_utils/__init__.py +1 -0
- janito/agent/tools_utils/dir_walk_utils.py +23 -0
- janito/agent/{tools/outline_file → tools_utils}/formatting.py +5 -2
- janito/agent/{tools → tools_utils}/gitignore_utils.py +0 -3
- janito/agent/tools_utils/utils.py +30 -0
- janito/cli/_livereload_log_utils.py +13 -0
- janito/cli/arg_parser.py +45 -3
- janito/cli/{runner/cli_main.py → cli_main.py} +120 -20
- janito/cli/livereload_starter.py +60 -0
- janito/cli/main.py +110 -21
- janito/cli/one_shot.py +66 -0
- janito/cli/termweb_starter.py +2 -2
- janito/livereload/app.py +25 -0
- janito/rich_utils.py +0 -22
- janito/{cli_chat_shell → shell}/commands/__init__.py +18 -11
- janito/{cli_chat_shell → shell}/commands/config.py +4 -4
- janito/shell/commands/conversation_restart.py +72 -0
- janito/shell/commands/edit.py +21 -0
- janito/shell/commands/history_view.py +18 -0
- janito/shell/commands/livelogs.py +40 -0
- janito/{cli_chat_shell → shell}/commands/prompt.py +10 -6
- janito/shell/commands/session.py +32 -0
- janito/{cli_chat_shell → shell}/commands/session_control.py +2 -7
- janito/{cli_chat_shell → shell}/commands/sum.py +6 -6
- janito/{cli_chat_shell → shell}/commands/termweb_log.py +10 -10
- janito/shell/commands/tools.py +23 -0
- janito/{cli_chat_shell → shell}/commands/utility.py +5 -4
- janito/{cli_chat_shell → shell}/commands/verbose.py +1 -1
- janito/shell/commands.py +40 -0
- janito/shell/main.py +321 -0
- janito/{cli_chat_shell/shell_command_completer.py → shell/prompt/completer.py} +1 -1
- janito/{cli_chat_shell/chat_ui.py → shell/prompt/session_setup.py} +19 -5
- janito/{cli_chat_shell/session_manager.py → shell/session/manager.py} +53 -3
- janito/{cli_chat_shell/ui.py → shell/ui/interactive.py} +23 -15
- janito/termweb/app.py +3 -3
- janito/termweb/static/editor.css +146 -0
- janito/termweb/static/editor.css.bak +27 -0
- janito/termweb/static/editor.html +15 -213
- janito/termweb/static/editor.html.bak +16 -215
- janito/termweb/static/editor.js +209 -0
- janito/termweb/static/editor.js.bak +227 -0
- janito/termweb/static/index.html +2 -3
- janito/termweb/static/index.html.bak +2 -3
- janito/termweb/static/termweb.css.bak +33 -84
- janito/termweb/static/termweb.js +15 -34
- janito/termweb/static/termweb.js.bak +18 -36
- {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/METADATA +6 -3
- janito-1.9.0.dist-info/RECORD +151 -0
- {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/WHEEL +1 -1
- janito/agent/tools/dir_walk_utils.py +0 -16
- janito/agent/tools/memory.py +0 -48
- janito/agent/tools/outline_file/python_outline.py +0 -71
- janito/agent/tools/present_choices_test.py +0 -18
- janito/agent/tools/rich_live.py +0 -44
- janito/agent/tools/tools_utils.py +0 -56
- janito/agent/tools/utils.py +0 -33
- janito/agent/tools/validate_file_syntax.py +0 -163
- janito/cli_chat_shell/chat_loop.py +0 -163
- janito/cli_chat_shell/chat_state.py +0 -38
- janito/cli_chat_shell/commands/history_start.py +0 -37
- janito/cli_chat_shell/commands/session.py +0 -48
- janito-1.8.0.dist-info/RECORD +0 -127
- /janito/agent/tools/{outline_file → get_file_outline}/markdown_outline.py +0 -0
- /janito/cli/{runner/_termweb_log_utils.py → _termweb_log_utils.py} +0 -0
- /janito/cli/{runner/config.py → config_runner.py} +0 -0
- /janito/cli/{runner/formatting.py → formatting_runner.py} +0 -0
- /janito/{cli/runner → shell}/__init__.py +0 -0
- /janito/{cli_chat_shell → shell}/commands/lang.py +0 -0
- /janito/{cli_chat_shell → shell/prompt}/load_prompt.py +0 -0
- /janito/{cli_chat_shell/config_shell.py → shell/session/config.py} +0 -0
- /janito/{cli_chat_shell/__init__.py → shell/session/history.py} +0 -0
- {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/entry_points.txt +0 -0
- {janito-1.8.0.dist-info → janito-1.9.0.dist-info}/licenses/LICENSE +0 -0
- {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
|
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
|
-
|
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 /
|
96
|
+
# Move /help and /restart tips to key bindings/info line
|
95
97
|
# Compose second/status line (no help_part)
|
96
|
-
|
97
|
-
|
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 /
|
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>/
|
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 .
|
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.
|
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.
|
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,
|
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
|
-
<
|
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="
|
124
|
-
|
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
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
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
|
-
|
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>
|