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
@@ -24,6 +24,92 @@ def print_welcome(console, version=None, continue_id=None):
24
24
  )
25
25
 
26
26
 
27
+ def format_tokens(n, tag=None):
28
+ if n is None:
29
+ return "?"
30
+ if n < 1000:
31
+ val = str(n)
32
+ elif n < 1000000:
33
+ val = f"{n/1000:.1f}k"
34
+ else:
35
+ val = f"{n/1000000:.1f}M"
36
+ return f"<{tag}>{val}</{tag}>" if tag else val
37
+
38
+
39
+ def assemble_first_line(model_name, role_ref, style_ref):
40
+ model_part = f" {tr('Model')}: <model>{model_name}</model>" if model_name else ""
41
+ role_part = ""
42
+ vanilla_mode = runtime_config.get("vanilla_mode", False)
43
+ if role_ref and not vanilla_mode:
44
+ role = role_ref()
45
+ if role:
46
+ role_part = f"{tr('Role')}: <role>{role}</role>"
47
+ style_part = ""
48
+ if style_ref:
49
+ style = style_ref()
50
+ if style:
51
+ style_part = f"{tr('Style')}: <b>{style}</b>"
52
+ first_line_parts = []
53
+ if model_part:
54
+ first_line_parts.append(model_part)
55
+ if role_part:
56
+ first_line_parts.append(role_part)
57
+ if style_part:
58
+ first_line_parts.append(style_part)
59
+ return " | ".join(first_line_parts)
60
+
61
+
62
+ def assemble_second_line(
63
+ width,
64
+ last_usage_info_ref,
65
+ history_ref,
66
+ messages_ref,
67
+ session_id,
68
+ model_name,
69
+ role_ref,
70
+ style_ref,
71
+ ):
72
+ usage = last_usage_info_ref()
73
+ prompt_tokens = usage.get("prompt_tokens") if usage else None
74
+ completion_tokens = usage.get("completion_tokens") if usage else None
75
+ total_tokens = usage.get("total_tokens") if usage else None
76
+ msg_count = len(history_ref()) if history_ref else len(messages_ref())
77
+ left = f" {tr('Messages')}: <msg_count>{msg_count}</msg_count>"
78
+ tokens_part = ""
79
+ if (
80
+ prompt_tokens is not None
81
+ or completion_tokens is not None
82
+ or total_tokens is not None
83
+ ):
84
+ tokens_part = (
85
+ f" | {tr('Tokens')} - {tr('Prompt')}: {format_tokens(prompt_tokens, 'tokens_in')}, "
86
+ f"{tr('Completion')}: {format_tokens(completion_tokens, 'tokens_out')}, "
87
+ f"{tr('Total')}: {format_tokens(total_tokens, 'tokens_total')}"
88
+ )
89
+ session_part = (
90
+ f" | Session ID: <session_id>{session_id}</session_id>" if session_id else ""
91
+ )
92
+ second_line = f"{left}{tokens_part}{session_part}"
93
+ total_len = len(left) + len(tokens_part) + len(session_part)
94
+ first_line = assemble_first_line(model_name, role_ref, style_ref)
95
+ if first_line:
96
+ total_len += len(first_line) + 3
97
+ if total_len < width:
98
+ padding = " " * (width - total_len)
99
+ second_line = f"{left}{tokens_part}{session_part}{padding}"
100
+ return second_line
101
+
102
+
103
+ def assemble_bindings_line():
104
+ return (
105
+ f"<b> F12</b>: {tr('Quick Action')} | "
106
+ f"<b>Ctrl-Y</b>: {tr('Yes')} | "
107
+ f"<b>Ctrl-N</b>: {tr('No')} | "
108
+ f"<b>/help</b>: {tr('Help')} | "
109
+ f"<b>/restart</b>: {tr('Reset Conversation')}"
110
+ )
111
+
112
+
27
113
  def get_toolbar_func(
28
114
  messages_ref,
29
115
  last_usage_info_ref,
@@ -37,82 +123,20 @@ def get_toolbar_func(
37
123
  ):
38
124
  from prompt_toolkit.application.current import get_app
39
125
 
40
- def format_tokens(n, tag=None):
41
- if n is None:
42
- return "?"
43
- if n < 1000:
44
- val = str(n)
45
- elif n < 1000000:
46
- val = f"{n/1000:.1f}k"
47
- else:
48
- val = f"{n/1000000:.1f}M"
49
- return f"<{tag}>{val}</{tag}>" if tag else val
50
-
51
126
  def get_toolbar():
52
127
  width = get_app().output.get_size().columns
53
- model_part = (
54
- f" {tr('Model')}: <model>{model_name}</model>" if model_name else ""
55
- )
56
- role_part = ""
57
- vanilla_mode = runtime_config.get("vanilla_mode", False)
58
- if role_ref and not vanilla_mode:
59
- role = role_ref()
60
- if role:
61
- role_part = f"{tr('Role')}: <role>{role}</role>"
62
-
63
- style_part = ""
64
- if style_ref:
65
- style = style_ref()
66
- if style:
67
- style_part = f"{tr('Style')}: <b>{style}</b>"
68
- usage = last_usage_info_ref()
69
- prompt_tokens = usage.get("prompt_tokens") if usage else None
70
- completion_tokens = usage.get("completion_tokens") if usage else None
71
- total_tokens = usage.get("total_tokens") if usage else None
72
- first_line_parts = []
73
- if model_part:
74
- first_line_parts.append(model_part)
75
-
76
- if role_part:
77
- first_line_parts.append(role_part)
78
- if style_part:
79
- first_line_parts.append(style_part)
80
- first_line = " | ".join(first_line_parts)
81
- msg_count = len(history_ref()) if history_ref else len(messages_ref())
82
- left = f" {tr('Messages')}: <msg_count>{msg_count}</msg_count>"
83
- tokens_part = ""
84
- if (
85
- prompt_tokens is not None
86
- or completion_tokens is not None
87
- or total_tokens is not None
88
- ):
89
- tokens_part = (
90
- f" | {tr('Tokens')} - {tr('Prompt')}: {format_tokens(prompt_tokens, 'tokens_in')}, "
91
- f"{tr('Completion')}: {format_tokens(completion_tokens, 'tokens_out')}, "
92
- f"{tr('Total')}: {format_tokens(total_tokens, 'tokens_total')}"
93
- )
94
- # Move /help and /restart tips to key bindings/info line
95
- # Compose second/status line (no help_part)
96
- session_part = (
97
- f" | Session ID: <session_id>{session_id}</session_id>"
98
- if session_id
99
- else ""
100
- )
101
- second_line = f"{left}{tokens_part}{session_part}"
102
- total_len = len(left) + len(tokens_part) + len(session_part)
103
- if first_line:
104
- total_len += len(first_line) + 3
105
- if total_len < width:
106
- padding = " " * (width - total_len)
107
- second_line = f"{left}{tokens_part}{session_part}{padding}"
108
- # Add key bindings info as an extra line, now including /help and /restart
109
- bindings_line = (
110
- f"<b> F12</b>: {tr('Quick Action')} | "
111
- f"<b>Ctrl-Y</b>: {tr('Yes')} | "
112
- f"<b>Ctrl-N</b>: {tr('No')} | "
113
- f"<b>/help</b>: {tr('Help')} | "
114
- f"<b>/restart</b>: {tr('Reset Conversation')}"
128
+ first_line = assemble_first_line(model_name, role_ref, style_ref)
129
+ second_line = assemble_second_line(
130
+ width,
131
+ last_usage_info_ref,
132
+ history_ref,
133
+ messages_ref,
134
+ session_id,
135
+ model_name,
136
+ role_ref,
137
+ style_ref,
115
138
  )
139
+ bindings_line = assemble_bindings_line()
116
140
  if first_line:
117
141
  toolbar_text = first_line + "\n" + second_line + "\n" + bindings_line
118
142
  else:
@@ -1,9 +1,9 @@
1
1
  /* Highlight active line in CodeMirror */
2
2
  .CodeMirror-activeline-background {
3
- background: #353b45 !important;
3
+ background: #353b45;
4
4
  }
5
5
  body.light-theme .CodeMirror-activeline-background {
6
- background: #e0eaff !important;
6
+ background: #e0eaff;
7
7
  }
8
8
  html, body {
9
9
  height: 100%;
@@ -39,36 +39,22 @@ body.light-theme {
39
39
  flex-direction: column;
40
40
  height: 100%;
41
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;
42
+ padding: 0;
43
+ margin: 0;
59
44
  }
45
+ /* Removed custom .CodeMirror background/color to use CodeMirror's theme defaults */
60
46
  .header {
61
47
  background: #222;
62
48
  color: #fff;
63
- padding: 10px 24px;
64
- font-size: 1.3em;
49
+ padding: 4px 12px;
50
+ font-size: 0.95em;
65
51
  font-weight: bold;
66
52
  position: relative;
67
53
  transition: background 0.2s, color 0.2s;
68
54
  display: flex;
69
55
  align-items: center;
70
56
  justify-content: flex-end;
71
- height: 56px;
57
+ height: 36px;
72
58
  }
73
59
 
74
60
  .header-title {
@@ -78,20 +64,39 @@ body.light-theme .CodeMirror {
78
64
  transform: translate(-50%, -50%);
79
65
  pointer-events: none;
80
66
  font-weight: bold;
81
- font-size: 1.3em;
67
+ font-size: 1.05em;
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 0.5em;
71
+ }
72
+
73
+ .filename-display {
74
+ font-weight: normal;
75
+ font-size: 0.98em;
76
+ color: #bbb;
77
+ margin-left: 0.5em;
78
+ pointer-events: auto;
79
+ text-overflow: ellipsis;
80
+ white-space: nowrap;
81
+ overflow: hidden;
82
+ max-width: 180px;
83
+ }
84
+ body.light-theme .filename-display {
85
+ color: #666;
82
86
  }
83
87
 
84
88
  .footer {
85
89
  width: 100%;
86
90
  background: #23272b;
87
91
  color: #fff;
88
- padding: 10px 24px;
92
+ padding: 4px 12px;
89
93
  box-sizing: border-box;
90
94
  display: flex;
91
95
  align-items: center;
92
96
  justify-content: flex-start;
93
97
  border-top: 1px solid #333;
94
- min-height: 48px;
98
+ min-height: 28px;
99
+ font-size: 0.95em;
95
100
  }
96
101
 
97
102
  .save-btn {
@@ -102,7 +107,6 @@ body.light-theme .CodeMirror {
102
107
  margin-left: auto;
103
108
  }
104
109
 
105
-
106
110
  .save-btn {
107
111
  background: #4caf50;
108
112
  color: #fff;
@@ -117,8 +121,8 @@ body.light-theme .CodeMirror {
117
121
  display: flex;
118
122
  align-items: center;
119
123
  justify-content: center;
120
- width: 36px;
121
- height: 36px;
124
+ height: 100%;
125
+ aspect-ratio: 1/1;
122
126
  font-size: 1.3em;
123
127
  line-height: 1;
124
128
  padding: 0;
@@ -139,4 +143,3 @@ body.light-theme .header {
139
143
  background: #eaeaea;
140
144
  color: #222;
141
145
  }
142
-
@@ -1,27 +1,145 @@
1
+ /* Highlight active line in CodeMirror */
2
+ .CodeMirror-activeline-background {
3
+ background: #353b45;
4
+ }
5
+ body.light-theme .CodeMirror-activeline-background {
6
+ background: #e0eaff;
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;
43
+ margin: 0;
44
+ }
45
+ /* Removed custom .CodeMirror background/color to use CodeMirror's theme defaults */
46
+ .header {
47
+ background: #222;
48
+ color: #fff;
49
+ padding: 4px 12px;
50
+ font-size: 0.95em;
51
+ font-weight: bold;
52
+ position: relative;
53
+ transition: background 0.2s, color 0.2s;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: flex-end;
57
+ height: 36px;
58
+ }
59
+
60
+ .header-title {
61
+ position: absolute;
62
+ left: 50%;
63
+ top: 50%;
64
+ transform: translate(-50%, -50%);
65
+ pointer-events: none;
66
+ font-weight: bold;
67
+ font-size: 1.05em;
68
+ display: flex;
69
+ align-items: center;
70
+ gap: 0.5em;
71
+ }
72
+
73
+ .filename-display {
74
+ font-weight: normal;
75
+ font-size: 0.98em;
76
+ color: #bbb;
77
+ margin-left: 0.5em;
78
+ pointer-events: auto;
79
+ text-overflow: ellipsis;
80
+ white-space: nowrap;
81
+ overflow: hidden;
82
+ max-width: 180px;
83
+ }
84
+ body.light-theme .filename-display {
85
+ color: #666;
86
+ }
87
+
88
+ .footer {
89
+ width: 100%;
90
+ background: #23272b;
91
+ color: #fff;
92
+ padding: 4px 12px;
93
+ box-sizing: border-box;
94
+ display: flex;
95
+ align-items: center;
96
+ justify-content: flex-start;
97
+ border-top: 1px solid #333;
98
+ min-height: 28px;
99
+ font-size: 0.95em;
100
+ }
1
101
 
2
102
  .save-btn {
3
- background: linear-gradient(90deg, #00ff99 0%, #00cfff 100%);
4
- color: #181a1b;
103
+ /* No margin needed, align left in footer */
104
+ }
105
+
106
+ .theme-switcher {
107
+ margin-left: auto;
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;
5
131
  border: none;
6
- border-radius: 8px;
7
- padding: 12px 38px;
132
+ border-radius: 4px;
8
133
  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;
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;
27
145
  }
@@ -4,7 +4,7 @@
4
4
  <link rel="icon" type="image/svg+xml" href="/static/termicon.svg">
5
5
  <link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico">
6
6
  <meta charset="UTF-8">
7
- <title>TermWeb Editor — janito.dev</title>
7
+ <title>Janito Light Editor</title>
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">
@@ -12,27 +12,32 @@
12
12
  </head>
13
13
  <body>
14
14
  <div class="header">
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>
19
- </div>
15
+ <div class="header-title">Janito Light Editor</div>
16
+ <span id="filename-display" class="filename-display"></span>
17
+ <div style="flex:1 1 auto;"></div>
18
+ <button class="theme-switcher" id="theme-switcher" title="Alternator tema">
19
+ <span id="theme-icon" aria-label="Switch theme" style="pointer-events:none;">🌙</span>
20
+ </button>
21
+ </div>
20
22
  <div class="footer">
21
23
  <button class="save-btn" id="save-btn">Save</button>
22
24
  </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>
25
+ <div id="save-popup" style="display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);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>
24
26
  <div class="main">
25
27
  <div class="editor-pane">
26
28
  <textarea id="code" name="code"></textarea>
29
+ <div id="save-popup" style="display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);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>
27
30
  </div>
28
31
  </div>
29
32
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js"></script>
30
33
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/python/python.min.js"></script>
34
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/django/django.min.js"></script>
31
35
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/dracula.min.js"></script>
32
36
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/selection/active-line.min.js"></script>
33
37
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/search/search.min.js"></script>
34
38
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/search/searchcursor.min.js"></script>
35
39
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/dialog/dialog.min.js"></script>
40
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/mode/overlay.min.js"></script>
36
41
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/dialog/dialog.min.css">
37
42
 
38
43
  <script src="/static/editor.js"></script>
@@ -4,24 +4,25 @@
4
4
  <link rel="icon" type="image/svg+xml" href="/static/termicon.svg">
5
5
  <link rel="shortcut icon" type="image/x-icon" href="/static/favicon.ico">
6
6
  <meta charset="UTF-8">
7
- <title>TermWeb Editor — janito.dev</title>
7
+ <title>Janito Light Editor</title>
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
11
  <link rel="stylesheet" href="/static/editor.css">
12
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/dialog/dialog.min.css">
13
12
  </head>
14
13
  <body>
15
- <div class="header compact-header">
16
- <div class="header-title">TermWeb Editor</div>
17
- <div class="header-buttons no-shrink">
18
- <button class="save-btn" id="save-btn" title="Save (Ctrl+S)">Save</button>
19
- <button class="theme-switcher" id="theme-switcher" title="Switch theme">
20
- <span id="theme-icon" aria-label="Switch theme">🌙</span>
21
- </button>
22
- </div>
14
+ <div class="header">
15
+ <div class="header-title">Janito Light Editor</div>
16
+ <span id="filename-display" class="filename-display"></span>
17
+ <div style="flex:1 1 auto;"></div>
18
+ <button class="theme-switcher" id="theme-switcher" title="Alternator tema">
19
+ <span id="theme-icon" aria-label="Switch theme" style="pointer-events:none;">🌙</span>
20
+ </button>
21
+ </div>
22
+ <div class="footer">
23
+ <button class="save-btn" id="save-btn">Save</button>
23
24
  </div>
24
- <div id="toast" class="toast"></div>
25
+ <div id="save-popup" style="display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);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>
25
26
  <div class="main">
26
27
  <div class="editor-pane">
27
28
  <textarea id="code" name="code"></textarea>
@@ -29,11 +30,15 @@
29
30
  </div>
30
31
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/codemirror.min.js"></script>
31
32
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/python/python.min.js"></script>
33
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/mode/django/django.min.js"></script>
32
34
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/theme/dracula.min.js"></script>
33
35
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/selection/active-line.min.js"></script>
34
36
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/search/search.min.js"></script>
35
37
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/search/searchcursor.min.js"></script>
36
38
  <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/dialog/dialog.min.js"></script>
39
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/mode/overlay.min.js"></script>
40
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.13/addon/dialog/dialog.min.css">
41
+
37
42
  <script src="/static/editor.js"></script>
38
43
  </body>
39
44
  </html>