messages redesign wip
frdel committed
Jul 1, 2025 at 22:32 UTC
a769cb858f140f21aac852b498737576f0c00ea4
21 files changed
+804
-737
agent.py
+4
-14
@@ -300,7 +300,7 @@ class Agent:
300
301
# non-config vars
302
self.number = number
303
- self.agent_name = f"Agent {self.number}"
303
+ self.agent_name = f"A{self.number}"
304
305
self.history = history.History(self)
306
self.last_user_message: history.Message | None = None
@@ -333,19 +333,8 @@ class Agent:
333
# prepare LLM chain (model, system, history)
334
prompt = await self.prepare_prompt(loop_data=self.loop_data)
335
336
- # output that the agent is starting
337
- PrintStyle(
338
- bold=True,
339
- font_color="green",
340
- padding=True,
341
- background_color="white",
342
- ).print(f"{self.agent_name}: Generating")
343
- # create log message right away, more responsive
344
- self.loop_data.params_temporary["log_item_generating"] = (
345
- self.context.log.log(
346
- type="agent", heading=f"{self.agent_name}: Generating..."
347
- )
348
- )
336
+ # call before_main_llm_call extensions
337
+ await self.call_extensions("before_main_llm_call", loop_data=self.loop_data)
338
339
async def reasoning_callback(chunk: str, full: str):
340
if chunk == full:
@@ -360,6 +349,7 @@ class Agent:
349
printer.stream(chunk)
350
await self.handle_response_stream(full)
351
352
+ # call main LLM
353
agent_response, _reasoning = await self.call_chat_model(
354
messages=prompt,
355
response_callback=stream_callback,
python/extensions/before_main_llm_call/.gitkeep
python/extensions/before_main_llm_call/_10_log_for_stream.py
new
+26
@@ -0,0 +1,26 @@
1
+from python.helpers import persist_chat, tokens
2
+from python.helpers.extension import Extension
3
+from agent import LoopData
4
+import asyncio
5
+from python.helpers.log import LogItem
6
+from python.helpers import log
7
+import math
8
+
9
+
10
+class LogForStream(Extension):
11
+
12
+ async def execute(self, loop_data: LoopData = LoopData(), text: str = "", **kwargs):
13
+ # create log message and store it in loop data temporary params
14
+ if "log_item_generating" not in loop_data.params_temporary:
15
+ loop_data.params_temporary["log_item_generating"] = (
16
+ self.agent.context.log.log(
17
+ type="agent",
18
+ heading=build_default_heading(self.agent),
19
+ )
20
+ )
21
+
22
+def build_heading(agent, text: str):
23
+ return f"icon://network_intelligence {agent.agent_name}: {text}"
24
+
25
+def build_default_heading(agent):
26
+ return build_heading(agent, "Generating...")
\ No newline at end of file
python/extensions/message_loop_prompts_after/_50_recall_memories.py
+10
-10
@@ -5,6 +5,7 @@ from agent import LoopData
5
6
DATA_NAME_TASK = "_recall_memories_task"
7
8
+
9
class RecallMemories(Extension):
10
11
INTERVAL = 3
@@ -16,13 +17,14 @@ class RecallMemories(Extension):
17
18
# every 3 iterations (or the first one) recall memories
19
if loop_data.iteration % RecallMemories.INTERVAL == 0:
19
- task = asyncio.create_task(self.search_memories(loop_data=loop_data, **kwargs))
20
+ task = asyncio.create_task(
21
+ self.search_memories(loop_data=loop_data, **kwargs)
22
+ )
23
else:
24
task = None
25
26
# set to agent to be able to wait for it
27
self.agent.set_data(DATA_NAME_TASK, task)
25
-
28
29
async def search_memories(self, loop_data: LoopData, **kwargs):
30
@@ -32,12 +34,8 @@ class RecallMemories(Extension):
34
del extras["memories"]
35
36
# try:
35
- # show temp info message
36
- self.agent.context.log.log(
37
- type="info", content="Searching memories...", temp=True
38
- )
39
-
40
- # show full util message, this will hide temp message immediately if turned on
37
+
38
+ # show full util message
39
log_item = self.agent.context.log.log(
40
type="util",
41
heading="Searching memories...",
@@ -47,7 +45,7 @@ class RecallMemories(Extension):
45
# msgs_text = self.agent.concat_messages(
46
# self.agent.history[-RecallMemories.HISTORY :]
47
# ) # only last X messages
50
- msgs_text = self.agent.history.output_text()[-RecallMemories.HISTORY:]
48
+ msgs_text = self.agent.history.output_text()[-RecallMemories.HISTORY :]
49
system = self.agent.read_prompt(
50
"memory.memories_query.sys.md", history=msgs_text
51
)
@@ -59,7 +57,9 @@ class RecallMemories(Extension):
57
# call util llm to summarize conversation
58
query = await self.agent.call_utility_model(
59
system=system,
62
- message=loop_data.user_message.output_text() if loop_data.user_message else "",
60
+ message=(
61
+ loop_data.user_message.output_text() if loop_data.user_message else ""
62
+ ),
63
callback=log_callback,
64
)
65
python/extensions/message_loop_prompts_after/_51_recall_solutions.py
+1
-5
@@ -32,12 +32,8 @@ class RecallSolutions(Extension):
32
del extras["solutions"]
33
34
# try:
35
- # show temp info message
36
- self.agent.context.log.log(
37
- type="info", content="Searching memory for solutions...", temp=True
38
- )
35
40
- # show full util message, this will hide temp message immediately if turned on
36
+ # show full util message
37
log_item = self.agent.context.log.log(
38
type="util",
39
heading="Searching memory for solutions...",
python/extensions/monologue_end/_50_memorize_fragments.py
+1
-6
@@ -13,12 +13,7 @@ class MemorizeMemories(Extension):
13
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
14
# try:
15
16
- # show temp info message
17
- self.agent.context.log.log(
18
- type="info", content="Memorizing new information...", temp=True
19
- )
20
-
21
- # show full util message, this will hide temp message immediately if turned on
16
+ # show full util message
17
log_item = self.agent.context.log.log(
18
type="util",
19
heading="Memorizing new information...",
python/extensions/monologue_end/_51_memorize_solutions.py
+2
-7
@@ -12,13 +12,8 @@ class MemorizeSolutions(Extension):
12
13
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
14
# try:
15
-
16
- # show temp info message
17
- self.agent.context.log.log(
18
- type="info", content="Memorizing succesful solutions...", temp=True
19
- )
20
-
21
- # show full util message, this will hide temp message immediately if turned on
15
+
16
+ # show full util message
17
log_item = self.agent.context.log.log(
18
type="util",
19
heading="Memorizing succesful solutions...",
python/extensions/reasoning_stream/_10_log_from_stream.py
+3
-3
@@ -5,15 +5,15 @@ import asyncio
5
from python.helpers.log import LogItem
6
from python.helpers import log
7
import math
8
-
8
+from python.extensions.before_main_llm_call._10_log_for_stream import build_heading, build_default_heading
9
10
class LogFromStream(Extension):
11
12
async def execute(self, loop_data: LoopData = LoopData(), text: str = "", **kwargs):
13
14
# thought length indicator
15
- length = math.ceil(len(text) / 10) * 10
16
- heading = f"{self.agent.agent_name}: Reasoning ({length})..."
15
+ pipes = "|" * math.ceil(math.sqrt(len(text)))
16
+ heading = build_heading(self.agent, f"Reasoning.. {pipes}")
17
18
# create log message and store it in loop data temporary params
19
if "log_item_generating" not in loop_data.params_temporary:
python/extensions/response_stream/_10_log_from_stream.py
+8
-7
@@ -5,6 +5,7 @@ import asyncio
5
from python.helpers.log import LogItem
6
from python.helpers import log
7
import math
8
+from python.extensions.before_main_llm_call._10_log_for_stream import build_heading, build_default_heading
9
10
11
class LogFromStream(Extension):
@@ -17,17 +18,17 @@ class LogFromStream(Extension):
18
**kwargs,
19
):
20
20
- heading = f"{self.agent.agent_name}: Generating..."
21
+ heading = build_default_heading(self.agent)
22
if "headline" in parsed:
22
- heading = f"{self.agent.agent_name}: {parsed['headline']}"
23
+ heading = build_heading(self.agent, parsed['headline'])
24
elif "thoughts" in parsed:
25
# thought length indicator
26
thoughts = "\n".join(parsed["thoughts"])
26
- length = math.ceil(len(thoughts) / 10) * 10
27
- heading = f"{self.agent.agent_name}: Thinking ({length})..."
27
+ pipes = "|" * math.ceil(math.sqrt(len(thoughts)))
28
+ heading = build_heading(self.agent, f"Thinking... {pipes}")
29
29
- if "tool_name" in parsed:
30
- heading += f" ({parsed['tool_name']})"
30
+ # if "tool_name" in parsed:
31
+ # heading += f" ({parsed['tool_name']})"
32
33
# create log message and store it in loop data temporary params
34
if "log_item_generating" not in loop_data.params_temporary:
@@ -48,4 +49,4 @@ class LogFromStream(Extension):
49
kvps.update(parsed)
50
51
# update the log item
51
- log_item.update(heading=heading, content=text, kvps=kvps)
52
+ log_item.update(heading=heading, content=text, kvps=kvps)
\ No newline at end of file
python/extensions/response_stream/_20_live_response.py
+1
-1
@@ -30,7 +30,7 @@ class LiveResponse(Extension):
30
loop_data.params_temporary["log_item_response"] = (
31
self.agent.context.log.log(
32
type="response",
33
- heading=f"{self.agent.agent_name}: Responding",
33
+ heading=f"icon://chat {self.agent.agent_name}: Responding",
34
)
35
)
36
python/helpers/tool.py
+2
-2
@@ -42,9 +42,9 @@ class Tool:
42
43
def get_log_object(self):
44
if self.method:
45
- heading = f"{self.agent.agent_name}: Using tool '{self.name}:{self.method}'"
45
+ heading = f"icon://construction {self.agent.agent_name}: Using tool '{self.name}:{self.method}'"
46
else:
47
- heading = f"{self.agent.agent_name}: Using tool '{self.name}'"
47
+ heading = f"icon://construction {self.agent.agent_name}: Using tool '{self.name}'"
48
return self.agent.context.log.log(type="tool", heading=heading, content="", kvps=self.args)
49
50
def nice_key(self, key:str):
python/tools/browser_agent.py
+1
-1
@@ -313,7 +313,7 @@ class BrowserAgent(Tool):
313
def get_log_object(self):
314
return self.agent.context.log.log(
315
type="browser",
316
- heading=f"{self.agent.agent_name}: Using tool '{self.name}'",
316
+ heading=f"icon://captive_portal {self.agent.agent_name}: Calling Browser Agent",
317
content="",
318
kvps=self.args,
319
)
python/tools/call_subordinate.py
+9
-1
@@ -31,4 +31,12 @@ class Delegation(Tool):
31
result = await subordinate.monologue()
32
33
# result
34
- return Response(message=result, break_loop=False)
\ No newline at end of file
34
+ return Response(message=result, break_loop=False)
35
+
36
+ def get_log_object(self):
37
+ return self.agent.context.log.log(
38
+ type="tool",
39
+ heading=f"icon://communication {self.agent.agent_name}: Calling Subordinate Agent",
40
+ content="",
41
+ kvps=self.args,
42
+ )
\ No newline at end of file
python/tools/input.py
+1
-1
@@ -20,7 +20,7 @@ class Input(Tool):
20
return await cet.execute(**args)
21
22
def get_log_object(self):
23
- return self.agent.context.log.log(type="code_exe", heading=f"{self.agent.agent_name}: Using tool '{self.name}'", content="", kvps=self.args)
23
+ return self.agent.context.log.log(type="code_exe", heading=f"icon://keyboard {self.agent.agent_name}: Using tool '{self.name}'", content="", kvps=self.args)
24
25
async def after_execution(self, response, **kwargs):
26
self.agent.hist_add_tool_result(self.name, response.message)
\ No newline at end of file
python/tools/knowledge_tool._py
renamed
python/tools/task_done.py
deleted
-13
@@ -1,13 +0,0 @@
1
-from python.helpers.tool import Tool, Response
2
-
3
-class TaskDone(Tool):
4
-
5
- async def execute(self,**kwargs):
6
- self.agent.set_data("timeout", 0)
7
- return Response(message=self.args["text"], break_loop=True)
8
-
9
- async def before_execution(self, **kwargs):
10
- self.log = self.agent.context.log.log(type="response", heading=f"{self.agent.agent_name}: Task done", content=self.args.get("text", ""))
11
-
12
- async def after_execution(self, response, **kwargs):
13
- pass # do add anything to the history or output
\ No newline at end of file
python/tools/webpage_content_tool._py
renamed
webui/components/messages/resize/message-resize-store.js
+56
-5
@@ -14,11 +14,11 @@ const model = {
14
_getDefaultSettings() {
15
return {
16
"message-agent": { minimized: true, maximized: false },
17
- "messsage-code-exec": { minimized: false, maximized: false },
17
+ "message-agent-response": { minimized: false, maximized: true },
18
};
19
},
20
21
- _getSetting(className) {
21
+ getSetting(className) {
22
return this.settings[className] || { minimized: false, maximized: false };
23
},
24
@@ -41,7 +41,7 @@ const model = {
41
},
42
43
async minimizeMessageClass(className, event) {
44
- const set = this._getSetting(className);
44
+ const set = this.getSetting(className);
45
set.minimized = !set.minimized;
46
this._setSetting(className, set);
47
this._applySetting(className, set);
@@ -49,7 +49,7 @@ const model = {
49
},
50
51
async maximizeMessageClass(className, event) {
52
- const set = this._getSetting(className);
52
+ const set = this.getSetting(className);
53
if (set.minimized) return this.minimizeMessageClass(className, event); // if minimized, unminimize first
54
set.maximized = !set.maximized;
55
this._setSetting(className, set);
@@ -58,7 +58,58 @@ const model = {
58
},
59
60
_applyScroll(event) {
61
- event.target.scrollIntoView({ behavior: "smooth" });
61
+ if (!event || !event.target) {
62
+ return;
63
+ }
64
+
65
+ // Store the element reference to avoid issues with event being modified
66
+ const targetElement = event.target;
67
+ const clickY = event.clientY;
68
+
69
+ // Use requestAnimationFrame for smoother timing with browser rendering
70
+ // requestAnimationFrame(() => {
71
+ try {
72
+ // Get fresh measurements after potential re-renders
73
+ const rect = targetElement.getBoundingClientRect();
74
+ const viewHeight = window.innerHeight || document.documentElement.clientHeight;
75
+
76
+ // Get chat history element
77
+ const chatHistory = document.getElementById('chat-history');
78
+ if (!chatHistory) {
79
+ return;
80
+ }
81
+
82
+ // Get chat history position
83
+ const chatRect = chatHistory.getBoundingClientRect();
84
+
85
+ // Calculate element's middle position relative to chat history
86
+ const elementHeight = rect.height;
87
+ const elementMiddle = rect.top + (elementHeight / 2);
88
+ const relativeMiddle = elementMiddle - chatRect.top;
89
+
90
+ // Calculate target scroll position
91
+ let scrollTop;
92
+
93
+ if (typeof clickY === 'number') {
94
+ // Calculate based on click position
95
+ const clickRelativeToChat = clickY - chatRect.top;
96
+ // Add current scroll position and adjust to keep element middle at click position
97
+ scrollTop = chatHistory.scrollTop + relativeMiddle - clickRelativeToChat;
98
+ } else {
99
+ // Position element middle at 50% from the top of chat history viewport (center)
100
+ const targetPosition = chatHistory.clientHeight * 0.5;
101
+ scrollTop = chatHistory.scrollTop + relativeMiddle - targetPosition;
102
+ }
103
+
104
+ // Apply scroll with instant behavior
105
+ chatHistory.scrollTo({
106
+ top: scrollTop,
107
+ behavior: "auto"
108
+ });
109
+ } catch (e) {
110
+ // Silent error handling
111
+ }
112
+ // });
113
},
114
115
_applySetting(className, setting) {
webui/css/messages.css
+646
-625
@@ -1,656 +1,677 @@
1
/* Chat History */
2
#chat-history {
3
- display: -webkit-flex;
4
- display: flex;
5
- flex-direction: column;
6
- flex-grow: 1;
7
- width: 100%;
8
- overflow-y: scroll;
9
- overflow-x: hidden;
10
- scroll-behavior: auto !important; /* avoid infinite scrolling! */
11
- padding: var(--spacing-md) var(--spacing-sm) 0;
12
- -webkit-transition: all 0.3s ease;
13
- transition: all 0.3s ease;
14
- scrollbar-width: thin;
15
- scrollbar-color: #555 transparent;
16
- }
17
-
18
- #chat-history > *:first-child {
19
- margin-top: 4.4em;
20
- }
21
-
22
- /* Scrollbar styling for Firefox */
23
- #chat-history::-webkit-scrollbar {
24
- width: 5px;
25
- }
26
-
27
- #chat-history::-webkit-scrollbar-track {
28
- box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3);
29
- border-radius: 3px;
30
- }
31
-
32
- #chat-history::-webkit-scrollbar-thumb {
33
- border-radius: 3px;
34
- box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3);
35
- background-color: #555;
36
- -webkit-transition: background-color var(--transition-speed) ease-in-out;
37
- transition: background-color var(--transition-speed) ease-in-out;
38
- }
39
-
40
- #chat-history::-webkit-scrollbar-thumb:hover {
41
- background-color: #666;
42
- }
43
-
44
- #chat-history::-webkit-scrollbar-thumb:active {
45
- background-color: #888;
46
- }
47
-
3
+ display: -webkit-flex;
4
+ display: flex;
5
+ flex-direction: column;
6
+ flex-grow: 1;
7
+ width: 100%;
8
+ overflow-y: scroll;
9
+ overflow-x: hidden;
10
+ scroll-behavior: auto !important; /* avoid infinite scrolling! */
11
+ padding: var(--spacing-md) var(--spacing-sm) 0;
12
+ -webkit-transition: all 0.3s ease;
13
+ transition: all 0.3s ease;
14
+ scrollbar-width: thin;
15
+ scrollbar-color: #555 transparent;
16
+}
17
+
18
+#chat-history > *:first-child {
19
+ margin-top: 4.4em;
20
+}
21
+
22
+/* Scrollbar styling for Firefox */
23
+#chat-history::-webkit-scrollbar {
24
+ width: 5px;
25
+}
26
+
27
+#chat-history::-webkit-scrollbar-track {
28
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3);
29
+ border-radius: 3px;
30
+}
31
+
32
+#chat-history::-webkit-scrollbar-thumb {
33
+ border-radius: 3px;
34
+ box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3);
35
+ background-color: #555;
36
+ -webkit-transition: background-color var(--transition-speed) ease-in-out;
37
+ transition: background-color var(--transition-speed) ease-in-out;
38
+}
39
+
40
+#chat-history::-webkit-scrollbar-thumb:hover {
41
+ background-color: #666;
42
+}
43
+
44
+#chat-history::-webkit-scrollbar-thumb:active {
45
+ background-color: #888;
46
+}
47
48
/* Message Styles */
49
51
-
52
- .user-container {
53
- align-self: flex-end;
54
- /* margin: var(--spacing-sm) var(--spacing-md); */
55
- /* margin-bottom: var(--spacing-lg); */
56
- /* margin-top: var(--spacing-sm); */
57
- display: flex;
58
- justify-content: flex-end;
59
- width: 100%;
60
- }
61
-
62
- .ai-container {
63
- align-self: flex-start;
64
- }
65
-
66
- .center-container {
67
- align-self: center;
68
- max-width: 80%;
69
- margin: 0;
70
- }
71
-
72
- .center-container .message {
73
- margin-bottom: var(--spacing-sm);
74
- }
75
-
76
- .message-user {
77
- background-color: #4a4a4a;
78
- /* border-bottom-right-radius: var(--spacing-xxs); */
79
- min-width: 195px;
80
- text-align: end;
81
- }
82
-
83
- .message-user > div {
84
- padding-top: var(--spacing-xs);
85
- font-family: "Roboto Mono", monospace;
86
- font-optical-sizing: auto;
87
- -webkit-font-optical-sizing: auto;
88
- font-size: var(--font-size-small);
89
- }
90
-
91
- .message-ai {
92
- /* border-bottom-left-radius: var(--spacing-xxs); */
93
- }
94
-
95
- .message-center {
96
- align-self: center;
97
- /* border-bottom-left-radius: unset; */
98
- }
99
-
100
- .message-followup {
101
- /* margin-left: var(--spacing-lg); */
102
- /* margin-bottom: var(--spacing-lg); */
103
- }
104
-
105
- .message-followup .message {
106
- border-radius: 1.125em; /* 18px */
107
- /* border-top-left-radius: var(--spacing-xxs); */
108
- }
109
-
110
- .message-followup + .message-followup {
111
- margin-bottom: 0;
112
- }
113
-
114
- /* Update message types for dark mode */
115
- .message-default,
116
- .message-agent,
117
- .message-agent-response,
118
- .message-agent-delegation,
119
- .message-tool,
120
- .message-code-exe,
121
- .message-browser,
122
- .message-info,
123
- .message-util,
124
- .message-warning,
125
- .message-error {
126
- color: #e0e0e0;
127
- }
128
-
129
- .message-default {
130
- background-color: #1a242f;
131
- }
132
-
133
- .message-agent {
134
- background-color: #34506b;
135
- }
136
-
137
- .message-agent-response {
138
- min-width: 255px;
139
- background-color: #1f3c1e;
140
- }
141
-
142
- .message-agent-delegation {
143
- background-color: #12685e;
144
- }
145
-
146
- .message-tool {
147
- background-color: #2a4170;
148
- }
149
-
150
- .message-code-exe {
151
- background-color: #4b3a69;
152
- }
153
-
154
- .message-code-exe .message-body {
155
- min-height: 5em;
156
- width: 100%;
157
- background-color: var(--color-panel);
158
- border-radius: 0.5em;
159
- margin-top: 0.5em;
160
- padding: 0.3em;
161
- }
162
-
163
- .light-mode .message-code-exe .message-body {
164
- border: 1px solid var(--color-border);
165
- }
166
-
167
- .message-browser {
168
- background-color: #4b3a69;
169
- }
170
-
171
- .message-info {
172
- background-color: var(--color-panel);
173
- }
174
-
175
- .message-util {
176
- background-color: #23211a;
177
- display: none;
178
- }
179
-
180
- .message-warning {
181
- background-color: #bc8036;
182
- }
183
-
184
- .message-error {
185
- background-color: #af2222;
186
- }
187
-
188
- /* Agent and AI Info */
189
- .agent-start {
190
- color: var(--color-text);
191
- font-size: var(--font-size-small);
192
- margin-bottom: var(--spacing-xs);
193
- opacity: 0.7;
194
- }
195
-
196
- .msg-kvps {
197
- font-size: 0.9em;
198
- margin: 0.5rem 0 0.55rem 0;
199
- border-collapse: collapse;
200
- width: 100%;
201
- }
202
-
203
- .kvps-val pre {
204
- white-space: pre-wrap; /* keep \n, collapse no spaces, allow wrapping */
205
- word-break: break-word; /* optional – forces really long “words” to break */
206
- }
207
-
208
- .msg-kvps th,
209
- .msg-kvps td {
210
- align-content: center;
211
- padding: 0.25rem;
212
- padding-left: 0;
213
- text-align: left;
214
- }
215
-
216
- .msg-kvps th {
217
- color: var(--color-primary);
218
- width: 40%;
219
- }
220
-
221
- .msg-kvps tr {
222
- border-bottom: 1px solid rgba(255, 255, 255, 0.15);
223
- }
224
-
225
- .msg-heading {
226
- margin: 0;
227
- position: relative;
228
- display: block;
229
- white-space: nowrap;
230
- }
231
-
232
- .msg-heading h4 {
233
- margin: 0;
234
- /* width: calc(100% - 4em); */
235
- margin-right: 4em;
236
- overflow-y: hidden;
237
- text-overflow: ellipsis;
238
- }
239
-
240
- /* Message Actions */
241
- .message-actions {
242
- color: var(--color-text);
243
- font-size: var(--font-size-small);
244
- margin-top: var(--spacing-xs);
245
- }
246
-
247
- .message-action {
248
- cursor: pointer;
249
- opacity: 0.7;
250
- -webkit-transition: opacity var(--transition-speed) ease-in-out;
251
- transition: opacity var(--transition-speed) ease-in-out;
252
- }
253
-
254
- .message-action:hover {
255
- opacity: 1;
256
- }
50
+.user-container {
51
+ align-self: flex-end;
52
+ /* margin: var(--spacing-sm) var(--spacing-md); */
53
+ /* margin-bottom: var(--spacing-lg); */
54
+ /* margin-top: var(--spacing-sm); */
55
+ display: flex;
56
+ justify-content: flex-end;
57
+ width: 100%;
58
+}
59
+
60
+.ai-container {
61
+ align-self: flex-start;
62
+}
63
+
64
+.center-container {
65
+ align-self: center;
66
+ max-width: 80%;
67
+ margin: 0;
68
+}
69
+
70
+.center-container .message {
71
+ /* margin-bottom: var(--spacing-sm); */
72
+}
73
+
74
+.message-user {
75
+ background-color: #4a4a4a;
76
+ /* border-bottom-right-radius: var(--spacing-xxs); */
77
+ min-width: 195px;
78
+ text-align: end;
79
+}
80
+
81
+.message-user > div {
82
+ padding-top: var(--spacing-xs);
83
+ font-family: "Roboto Mono", monospace;
84
+ font-optical-sizing: auto;
85
+ -webkit-font-optical-sizing: auto;
86
+ font-size: var(--font-size-small);
87
+}
88
+
89
+.message-ai {
90
+ /* border-bottom-left-radius: var(--spacing-xxs); */
91
+}
92
+
93
+.message-center {
94
+ align-self: center;
95
+ /* border-bottom-left-radius: unset; */
96
+}
97
+
98
+.message-followup {
99
+ /* margin-left: var(--spacing-lg); */
100
+ /* margin-bottom: var(--spacing-lg); */
101
+}
102
+
103
+.message-followup .message {
104
+ border-radius: 1.125em; /* 18px */
105
+ /* border-top-left-radius: var(--spacing-xxs); */
106
+}
107
+
108
+.message-followup + .message-followup {
109
+ margin-bottom: 0;
110
+}
111
+
112
+/* Update message types for dark mode */
113
+.message-default,
114
+.message-agent,
115
+.message-agent-response,
116
+.message-agent-delegation,
117
+.message-tool,
118
+.message-code-exe,
119
+.message-browser,
120
+.message-info,
121
+.message-util,
122
+.message-warning,
123
+.message-error {
124
+ color: #e0e0e0;
125
+}
126
+
127
+.message-default {
128
+ background-color: #1a242f;
129
+}
130
+
131
+.message-agent {
132
+ background-color: #34506b;
133
+}
134
+
135
+.message-agent-response {
136
+ min-width: 255px;
137
+ background-color: #1f3c1e;
138
+}
139
+
140
+.message-agent-delegation {
141
+ background-color: #12685e;
142
+}
143
+
144
+.message-tool {
145
+ background-color: #2a4170;
146
+}
147
+
148
+.message-code-exe {
149
+ background-color: #4b3a69;
150
+}
151
+
152
+.message-code-exe .message-body {
153
+ min-height: 5em;
154
+ width: 100%;
155
+ background-color: var(--color-panel);
156
+ border-radius: 0.5em;
157
+ margin-top: 0.5em;
158
+ padding: 0.3em;
159
+}
160
+
161
+.message-body .message-markdown-table-wrap {
162
+ display: block;
163
+ width: 100%;
164
+ overflow-x: auto;
165
+ margin-bottom: 1em;
166
+}
167
+
168
+.message-body .message-markdown-table-wrap table {
169
+ width: auto;
170
+ table-layout: auto;
171
+ white-space: nowrap;
172
+}
173
+
174
+.light-mode .message-code-exe .message-body {
175
+ border: 1px solid var(--color-border);
176
+}
177
+
178
+.message-browser {
179
+ background-color: #4b3a69;
180
+}
181
+
182
+.message-info {
183
+ background-color: var(--color-panel);
184
+}
185
+
186
+.message-util {
187
+ background-color: #23211a;
188
+ display: none;
189
+}
190
+
191
+.message-warning {
192
+ background-color: #bc8036;
193
+}
194
+
195
+.message-error {
196
+ background-color: #af2222;
197
+}
198
+
199
+/* Agent and AI Info */
200
+.agent-start {
201
+ color: var(--color-text);
202
+ font-size: var(--font-size-small);
203
+ margin-bottom: var(--spacing-xs);
204
+ opacity: 0.7;
205
+}
206
+
207
+.msg-kvps {
208
+ font-size: 0.9em;
209
+ margin: 0.5rem 0 0.55rem 0;
210
+ border-collapse: collapse;
211
+ width: 100%;
212
+}
213
214
+.kvps-val pre {
215
+ white-space: pre-wrap; /* keep \n, collapse no spaces, allow wrapping */
216
+ word-break: break-word; /* optional – forces really long “words” to break */
217
+}
218
+
219
+.msg-kvps th,
220
+.msg-kvps td {
221
+ align-content: center;
222
+ padding: 0.25rem;
223
+ padding-left: 0;
224
+ text-align: left;
225
+}
226
+
227
+.msg-kvps th {
228
+ color: var(--color-primary);
229
+ width: 40%;
230
+}
231
+
232
+.msg-kvps tr {
233
+ border-bottom: 1px solid rgba(255, 255, 255, 0.15);
234
+}
235
+
236
+.msg-heading {
237
+ margin: 0;
238
+ position: relative;
239
+ display: block;
240
+ white-space: nowrap;
241
+}
242
+
243
+.msg-heading h4 {
244
+ margin: 0;
245
+ /* width: calc(100% - 4em); */
246
+ margin-right: 4em;
247
+ overflow-y: hidden;
248
+ text-overflow: ellipsis;
249
+}
250
+
251
+/* Message Actions */
252
+.message-actions {
253
+ color: var(--color-text);
254
+ font-size: var(--font-size-small);
255
+ margin-top: var(--spacing-xs);
256
+}
257
+
258
+.message-action {
259
+ cursor: pointer;
260
+ opacity: 0.7;
261
+ -webkit-transition: opacity var(--transition-speed) ease-in-out;
262
+ transition: opacity var(--transition-speed) ease-in-out;
263
+}
264
+
265
+.message-action:hover {
266
+ opacity: 1;
267
+}
268
269
/* Copy button styles */
270
.copy-button {
261
- position: absolute;
262
- right: 0;
263
- top: var(--spacing-sm);
264
- background: none;
265
- border: none;
266
- padding: var(--spacing-xs) var(--spacing-sm);
267
- padding-right: 0;
268
- cursor: pointer;
269
- text-decoration: underline;
270
- text-wrap: nowrap;
271
- opacity: 0;
272
- -webkit-transition: opacity var(--transition-speed) ease-in-out;
273
- transition: opacity var(--transition-speed) ease-in-out;
274
- color: inherit;
275
- font-size: 12px;
276
- font-family: "Rubik", Arial, Helvetica, sans-serif;
277
- }
278
-
279
- .copy-button:hover {
280
- opacity: 0.8 !important;
281
- }
282
-
283
- .msg-content:hover .copy-button,
284
- .kvps-row:hover .copy-button,
285
- .message-text:hover .copy-button {
286
- opacity: 0.6;
287
- }
288
-
289
- .copy-button.copied {
290
- font-family: "Rubik", Arial, Helvetica, sans-serif !important;
291
- opacity: 1 !important;
292
- }
293
-
294
- .msg-thoughts .copy-button {
295
- top: -12px !important;
296
- }
297
-
298
- .message-user .copy-button {
299
- top: -15px !important;
300
- left: -13px !important;
301
- right: 99% !important;
302
- }
303
-
304
- .message-agent-response .copy-button {
305
- top: -22px !important;
306
- right: 0 !important;
307
- padding-right: 0px !important;
308
- }
309
-
310
- .message-info .copy-button {
311
- top: -22px !important;
312
- right: 0 !important;
313
- padding-right: 0px !important;
314
- }
315
-
316
- .message-tool .copy-button {
317
- top: -12px !important;
318
- right: 0 !important;
319
- }
320
-
321
- .msg-output .copy-button {
322
- top: -6px !important;
323
- right: 0 !important;
324
- }
325
-
326
- /* Make message containers relative for absolute positioning of copy buttons */
327
- .msg-content,
328
- .kvps-row,
329
- .message-text {
330
- position: relative;
331
- }
332
-
333
- .message-text pre {
334
- margin: 0;
335
- }
271
+ position: absolute;
272
+ right: 0;
273
+ top: var(--spacing-sm);
274
+ background: none;
275
+ border: none;
276
+ padding: var(--spacing-xs) var(--spacing-sm);
277
+ padding-right: 0;
278
+ cursor: pointer;
279
+ text-decoration: underline;
280
+ text-wrap: nowrap;
281
+ opacity: 0;
282
+ -webkit-transition: opacity var(--transition-speed) ease-in-out;
283
+ transition: opacity var(--transition-speed) ease-in-out;
284
+ color: inherit;
285
+ font-size: 12px;
286
+ font-family: "Rubik", Arial, Helvetica, sans-serif;
287
+}
288
+
289
+.copy-button:hover {
290
+ opacity: 0.8 !important;
291
+}
292
+
293
+.msg-content:hover .copy-button,
294
+.kvps-row:hover .copy-button,
295
+.message-text:hover .copy-button {
296
+ opacity: 0.6;
297
+}
298
+
299
+.copy-button.copied {
300
+ font-family: "Rubik", Arial, Helvetica, sans-serif !important;
301
+ opacity: 1 !important;
302
+}
303
+
304
+.msg-thoughts .copy-button {
305
+ top: -12px !important;
306
+}
307
+
308
+.message-user .copy-button {
309
+ top: -15px !important;
310
+ left: -13px !important;
311
+ right: 99% !important;
312
+}
313
+
314
+.message-agent-response .copy-button {
315
+ top: -22px !important;
316
+ right: 0 !important;
317
+ padding-right: 0px !important;
318
+}
319
+
320
+.message-info .copy-button {
321
+ top: -22px !important;
322
+ right: 0 !important;
323
+ padding-right: 0px !important;
324
+}
325
+
326
+.message-tool .copy-button {
327
+ top: -12px !important;
328
+ right: 0 !important;
329
+}
330
+
331
+.msg-output .copy-button {
332
+ top: -6px !important;
333
+ right: 0 !important;
334
+}
335
+
336
+/* Make message containers relative for absolute positioning of copy buttons */
337
+.msg-content,
338
+.kvps-row,
339
+.message-text {
340
+ position: relative;
341
+}
342
+
343
+.message-text pre {
344
+ margin: 0;
345
+ white-space: pre-wrap;
346
+ word-break: break-word;
347
+ overflow-wrap: anywhere;
348
+}
349
350
/* Utility Classes */
351
.kvps-key {
339
- font-weight: 500;
340
- font-size: var(--font-size-small);
341
- }
342
-
343
- .kvps-val {
344
- margin: 0.65rem 0 0.65rem 0.4rem;
345
- white-space: pre-wrap;
346
- }
347
-
348
- .kvps-img {
349
- width: 8em;
350
- height: 8em;
351
- object-fit: cover;
352
- object-position: top left;
353
- border-radius: 10%;
354
- border: 1px solid rgba(255, 255, 255, 0.15);
355
- }
356
-
357
- .image-viewer-img {
358
- width: 100%;
359
- }
360
-
361
- .msg-json {
362
- display: none;
363
- }
364
-
365
- .msg-thoughts {
366
- display: auto;
367
- }
368
-
369
- .msg-thoughts .kvps-val {
370
- max-height: 20em;
371
- overflow: auto;
372
- }
373
-
374
- .msg-content {
375
- margin-bottom: 0;
376
- }
377
-
378
- .message-temp {
379
- display: none;
380
- }
381
-
382
- .message-temp:not([style*="display: none"]):last-of-type {
383
- display: block; /* or any style you want for visibility */
384
- }
352
+ font-weight: 500;
353
+ font-size: var(--font-size-small);
354
+ min-width: 7em;
355
+}
356
+
357
+.kvps-val {
358
+ margin: 0.65rem 0 0.65rem 0.4rem;
359
+ white-space: pre-wrap;
360
+}
361
+
362
+.kvps-img {
363
+ width: 8em;
364
+ height: 8em;
365
+ object-fit: cover;
366
+ object-position: top left;
367
+ border-radius: 10%;
368
+ border: 1px solid rgba(255, 255, 255, 0.15);
369
+}
370
+
371
+.image-viewer-img {
372
+ width: 100%;
373
+}
374
+
375
+.msg-json {
376
+ display: none;
377
+}
378
+
379
+.msg-thoughts {
380
+ display: auto;
381
+}
382
+
383
+.msg-thoughts .kvps-val {
384
+ max-height: 20em;
385
+ overflow: auto;
386
+}
387
+
388
+.msg-content {
389
+ margin-bottom: 0;
390
+}
391
+
392
+.message-temp {
393
+ display: none;
394
+}
395
+
396
+.message-temp:not([style*="display: none"]):last-of-type {
397
+ display: block; /* or any style you want for visibility */
398
+}
399
400
/* Math (KaTeX) */
401
.katex {
388
- line-height: 1.2 !important;
389
- font-size: 1.1em;
390
- }
402
+ line-height: 1.2 !important;
403
+ font-size: 1.1em;
404
+}
405
406
/* Media Queries */
407
@media (max-width: 640px) {
394
- /* New styles for mobile messages */
395
-
396
- .message-followup {
397
- /* margin-left: var(--spacing-md); */
398
- margin-bottom: var(--spacing-md);
399
- }
400
-
401
- .msg-kvps {
402
- display: flex;
403
- flex-direction: column;
404
- border-collapse: separate;
405
- border-spacing: 0 0.5rem;
406
- }
407
-
408
- .msg-kvps tr {
409
- display: flex;
410
- flex-direction: column;
411
- margin-top: 0.3rem;
412
- padding-bottom: 0;
413
- }
414
-
415
- .msg-kvps th,
416
- .msg-kvps td {
417
- display: block;
418
- width: 100%;
419
- text-align: left;
420
- border-bottom: none;
421
- padding: 0.25rem 0;
422
- padding-left: 0 !important;
423
- }
424
-
425
- .msg-kvps th {
426
- color: var(--color-primary);
427
- margin-bottom: 0.25rem;
428
- }
429
-
430
- .kvps-val {
431
- margin: 0 0 0.4rem 0;
432
- white-space: pre-wrap;
433
- word-break: break-word;
434
- overflow-wrap: anywhere;
435
- }
436
- }
408
+ /* New styles for mobile messages */
409
438
-.light-mode .msg-kvps tr {
439
- border-bottom: 1px solid rgb(192 192 192 / 50%);
440
- }
441
-
442
- .light-mode .message-default {
443
- background-color: #f3f3f3;
444
- color: #1a242f;
445
- }
446
-
447
- .light-mode .message-agent {
448
- background-color: #f3f3f3;
449
- color: #356ca3;
450
- }
451
-
452
- .light-mode .message-agent-response {
453
- background-color: #f3f3f3;
454
- color: #188216;
455
- }
456
-
457
- .light-mode .message-agent-delegation {
458
- background-color: #f3f3f3;
459
- color: #12685e;
460
- }
461
-
462
- .light-mode .message-tool {
463
- background-color: #f3f3f3;
464
- color: #1c3c88;
465
- }
466
-
467
- .light-mode .message-code-exe {
468
- background-color: #f3f3f3;
469
- color: #6c43b0;
470
- }
471
-
472
- .light-mode .message-browser {
473
- background-color: #ffffff;
474
- color: #6c43b0;
475
- }
476
-
477
- .light-mode .message-info {
478
- background-color: #f3f3f3;
479
- color: #3f3f3f;
480
- }
481
-
482
- .light-mode .message-util {
483
- background-color: #f3f3f3;
484
- color: #5b5540;
485
- }
486
-
487
- .light-mode .message-warning {
488
- background-color: #f3f3f3;
489
- color: #8f4800;
490
- }
491
-
492
- .light-mode .message-error {
493
- background-color: #f3f3f3;
494
- color: #8f1010;
495
- }
496
-
497
- .light-mode .message-user {
498
- background-color: #f3f3f3;
499
- color: #4e4e4e;
410
+ .message-followup {
411
+ /* margin-left: var(--spacing-md); */
412
+ margin-bottom: var(--spacing-md);
413
}
414
502
-/* Markdown in messages */
503
-.msg-content {
504
- font-size: var(--font-size-small);
505
- }
506
-
507
- .message-agent-response .msg-content {
508
- font-size: var(--font-size-smaller);
509
- }
510
-
511
- .msg-content h1 {
512
- font-size: 1.25em;
513
- font-weight: 800;
514
- margin-bottom: 0.2em;
515
- }
516
-
517
- .msg-content h2 {
518
- font-size: 1.2em;
519
- font-weight: 700;
520
- margin-bottom: 0.2em;
521
- }
522
-
523
- .msg-content h3 {
524
- font-size: 1.15em;
525
- font-weight: 600;
526
- margin-bottom: 0.2em;
527
- }
528
-
529
- .msg-content h4 {
530
- font-size: 1.1em;
531
- font-weight: 500;
532
- margin-bottom: 0.2em;
533
- }
534
-
535
- .msg-content h5 {
536
- font-size: 1.05em;
537
- font-weight: 500;
538
- margin-bottom: 0.2em;
415
+ .msg-kvps {
416
+ display: flex;
417
+ flex-direction: column;
418
+ border-collapse: separate;
419
+ border-spacing: 0 0.5rem;
420
}
540
-
541
- .msg-content h6 {
542
- font-size: 1em;
543
- font-weight: 500;
544
- margin-bottom: 0.2em;
421
+
422
+ .msg-kvps tr {
423
+ display: flex;
424
+ flex-direction: column;
425
+ margin-top: 0.3rem;
426
+ padding-bottom: 0;
427
}
546
-
547
- .msg-content table {
428
+
429
+ .msg-kvps th,
430
+ .msg-kvps td {
431
+ display: block;
432
width: 100%;
549
- border-collapse: collapse;
550
- margin: 1em 0;
551
- font-size: 0.98em;
552
- background: transparent;
553
- border-radius: 8px;
554
- overflow: hidden;
555
- }
556
-
557
- .msg-content th,
558
- .msg-content td {
559
- padding: 0.55em 1em;
560
- border: 1px solid rgba(142, 142, 142, 0.1);
433
text-align: left;
562
- background: transparent;
563
- }
564
-
565
- .msg-content th {
566
- font-weight: 600;
567
- background: rgba(142, 142, 142, 0.1);
568
- }
569
-
570
- .msg-content tr:nth-child(even) {
571
- background: rgba(142, 142, 142, 0.1);
572
- }
573
-
574
- .msg-content tr:nth-child(odd) {
575
- background: transparent;
576
- }
577
-
578
- .msg-content table {
579
- box-shadow: none;
580
- }
581
-
582
- .msg-content pre:has(code) {
583
- padding: 0.5em;
584
- border: 1px solid rgba(142, 142, 142, 0.1);
585
- border-radius: 0.3em;
586
- }
587
-
588
- .msg-min-max-btns {
589
- opacity: 40%;
590
- position: absolute;
591
- top: -0.2em;
592
- right: -0.2em;
593
- display: flex;
594
- gap: 0.3em;
595
- z-index: 1;
434
+ border-bottom: none;
435
+ padding: 0.25rem 0;
436
+ padding-left: 0 !important;
437
}
438
598
-.message-group {
599
- margin-top: 1em;
600
- margin-bottom: 1em;
601
- display: inline-grid;
602
- grid-template-columns: minmax(0, max-content);
603
- grid-auto-rows: auto;
604
- max-width: 100%;
605
- width: fit-content;
606
- }
607
-
608
- .message-group > * {
609
- grid-column: 1;
610
- } /* both children sit in the same column */
611
-
612
- .message-group-right {
613
- justify-content: end;
614
- }
615
-
616
- .message-group-mid {
617
- justify-content: center;
618
- }
619
-
620
- /* 1. FIRST child’s .message – clear ONLY bottom corners */
621
- .message-group > *:first-child:not(:last-child) > .message {
622
- border-bottom-left-radius: var(--spacing-xxs);;
623
- border-bottom-right-radius: var(--spacing-xxs);;
624
- }
625
-
626
- /* 2. MIDDLE children’s .message – clear ALL corners */
627
- .message-group > *:not(:first-child):not(:last-child) > .message {
628
- border-radius: var(--spacing-xxs);
629
- }
630
-
631
- /* 3. LAST child’s .message – clear ONLY top corners */
632
- .message-group > *:last-child:not(:first-child) > .message {
633
- border-top-left-radius: var(--spacing-xxs);;
634
- border-top-right-radius: var(--spacing-xxs);;
439
+ .msg-kvps th {
440
+ color: var(--color-primary);
441
+ margin-bottom: 0.25rem;
442
}
443
637
- .message-container {
638
- animation: fadeIn 0.5s;
639
- -webkit-animation: fadeIn 0.5s;
640
- margin-bottom: var(--spacing-sm);
641
- width: 100%;
642
- max-width: 100%;
643
- }
644
-
645
- .message {
646
- /* background-color: var(--color-message-bg); */
647
- border-radius: var(--border-radius);
648
- padding: 0.9rem var(--spacing-sm) 0.7rem var(--spacing-sm);
649
- overflow-x: auto;
650
- width: auto;
651
- max-width: 100%;
652
- box-sizing: border-box;
653
- display: block;
444
+ .kvps-val {
445
+ margin: 0 0 0.4rem 0;
446
+ white-space: pre-wrap;
447
word-break: break-word;
448
overflow-wrap: anywhere;
656
- }
\ No newline at end of file
449
+ }
450
+}
451
+
452
+.light-mode .msg-kvps tr {
453
+ border-bottom: 1px solid rgb(192 192 192 / 50%);
454
+}
455
+
456
+.light-mode .message-default {
457
+ background-color: #f3f3f3;
458
+ color: #1a242f;
459
+}
460
+
461
+.light-mode .message-agent {
462
+ background-color: #f3f3f3;
463
+ color: #356ca3;
464
+}
465
+
466
+.light-mode .message-agent-response {
467
+ background-color: #f3f3f3;
468
+ color: #188216;
469
+}
470
+
471
+.light-mode .message-agent-delegation {
472
+ background-color: #f3f3f3;
473
+ color: #12685e;
474
+}
475
+
476
+.light-mode .message-tool {
477
+ background-color: #f3f3f3;
478
+ color: #1c3c88;
479
+}
480
+
481
+.light-mode .message-code-exe {
482
+ background-color: #f3f3f3;
483
+ color: #6c43b0;
484
+}
485
+
486
+.light-mode .message-browser {
487
+ background-color: #ffffff;
488
+ color: #6c43b0;
489
+}
490
+
491
+.light-mode .message-info {
492
+ background-color: #f3f3f3;
493
+ color: #3f3f3f;
494
+}
495
+
496
+.light-mode .message-util {
497
+ background-color: #f3f3f3;
498
+ color: #5b5540;
499
+}
500
+
501
+.light-mode .message-warning {
502
+ background-color: #f3f3f3;
503
+ color: #8f4800;
504
+}
505
+
506
+.light-mode .message-error {
507
+ background-color: #f3f3f3;
508
+ color: #8f1010;
509
+}
510
+
511
+.light-mode .message-user {
512
+ background-color: #f3f3f3;
513
+ color: #4e4e4e;
514
+}
515
+
516
+.message-body{
517
+ padding-top:0.5em;
518
+ padding-bottom:0.5em;
519
+}
520
+
521
+/* Markdown in messages */
522
+.msg-content {
523
+ font-size: var(--font-size-small);
524
+}
525
+
526
+.message-agent-response .msg-content {
527
+ font-size: var(--font-size-smaller);
528
+}
529
+
530
+.msg-content h1 {
531
+ font-size: 1.25em;
532
+ font-weight: 800;
533
+ margin-bottom: 0.2em;
534
+}
535
+
536
+.msg-content h2 {
537
+ font-size: 1.2em;
538
+ font-weight: 700;
539
+ margin-bottom: 0.2em;
540
+}
541
+
542
+.msg-content h3 {
543
+ font-size: 1.15em;
544
+ font-weight: 600;
545
+ margin-bottom: 0.2em;
546
+}
547
+
548
+.msg-content h4 {
549
+ font-size: 1.1em;
550
+ font-weight: 500;
551
+ margin-bottom: 0.2em;
552
+}
553
+
554
+.msg-content h5 {
555
+ font-size: 1.05em;
556
+ font-weight: 500;
557
+ margin-bottom: 0.2em;
558
+}
559
+
560
+.msg-content h6 {
561
+ font-size: 1em;
562
+ font-weight: 500;
563
+ margin-bottom: 0.2em;
564
+}
565
+
566
+.msg-content table {
567
+ width: 100%;
568
+ border-collapse: collapse;
569
+ margin: 1em 0;
570
+ font-size: 0.98em;
571
+ background: transparent;
572
+ border-radius: 8px;
573
+ overflow: hidden;
574
+}
575
+
576
+.msg-content th,
577
+.msg-content td {
578
+ padding: 0.55em 1em;
579
+ border: 1px solid rgba(142, 142, 142, 0.1);
580
+ text-align: left;
581
+ background: transparent;
582
+}
583
+
584
+.msg-content th {
585
+ font-weight: 600;
586
+ background: rgba(142, 142, 142, 0.1);
587
+}
588
+
589
+.msg-content tr:nth-child(even) {
590
+ background: rgba(142, 142, 142, 0.1);
591
+}
592
+
593
+.msg-content tr:nth-child(odd) {
594
+ background: transparent;
595
+}
596
+
597
+.msg-content table {
598
+ box-shadow: none;
599
+}
600
+
601
+.msg-content pre:has(code) {
602
+ padding: 0.5em;
603
+ border: 1px solid rgba(142, 142, 142, 0.1);
604
+ border-radius: 0.3em;
605
+}
606
+
607
+.msg-min-max-btns {
608
+ opacity: 40%;
609
+ position: absolute;
610
+ top: -0.2em;
611
+ right: -0.2em;
612
+ display: flex;
613
+ gap: 0.3em;
614
+ z-index: 1;
615
+}
616
+
617
+.message-group {
618
+ margin-top: 0.5em;
619
+ margin-bottom: 0.5em;
620
+ display: inline-grid;
621
+ grid-template-columns: minmax(0, max-content);
622
+ grid-auto-rows: auto;
623
+ max-width: 100%;
624
+ width: fit-content;
625
+ gap: var(--spacing-xs);
626
+}
627
+
628
+.message-group > * {
629
+ grid-column: 1;
630
+} /* both children sit in the same column */
631
+
632
+.message-group-right {
633
+ width:100%;
634
+ justify-content: end;
635
+}
636
+
637
+.message-group-mid {
638
+ margin-left: 2em;
639
+}
640
+
641
+/* 1. FIRST child’s .message – clear ONLY bottom corners */
642
+.message-group > *:first-child:not(:last-child) > .message {
643
+ border-bottom-left-radius: var(--spacing-xxs);
644
+ border-bottom-right-radius: var(--spacing-xxs);
645
+}
646
+
647
+/* 2. MIDDLE children’s .message – clear ALL corners */
648
+.message-group > *:not(:first-child):not(:last-child) > .message {
649
+ border-radius: var(--spacing-xxs);
650
+}
651
+
652
+/* 3. LAST child’s .message – clear ONLY top corners */
653
+.message-group > *:last-child:not(:first-child) > .message {
654
+ border-top-left-radius: var(--spacing-xxs);
655
+ border-top-right-radius: var(--spacing-xxs);
656
+}
657
+
658
+.message-container {
659
+ animation: fadeIn 0.5s;
660
+ -webkit-animation: fadeIn 0.5s;
661
+ /* margin-bottom: var(--spacing-sm); */
662
+ width: 100%;
663
+ max-width: 100%;
664
+}
665
+
666
+.message {
667
+ /* background-color: var(--color-message-bg); */
668
+ border-radius: var(--border-radius);
669
+ padding: 0.9rem var(--spacing-sm) 0.7rem var(--spacing-sm);
670
+ overflow-x: auto;
671
+ width: auto;
672
+ max-width: 100%;
673
+ box-sizing: border-box;
674
+ /* display: block; */
675
+ word-break: break-word;
676
+ overflow-wrap: anywhere;
677
+}
webui/index.js
+10
-32
@@ -1,6 +1,7 @@
1
import * as msgs from "./js/messages.js";
2
import { speech } from "./js/speech.js";
3
import * as api from "./js/api.js";
4
+import * as css from "./js/css.js";
5
6
window.fetchApi = api.fetchApi; // TODO - backward compatibility for non-modular scripts, remove once refactored to alpine
7
@@ -513,6 +514,8 @@ function updateProgress(progress, active) {
514
addClassToElement(progressBar, "shiny-text");
515
}
516
517
+ progress = msgs.convertIcons(progress);
518
+
519
if (progressBar.innerHTML != progress) {
520
progressBar.innerHTML = progress;
521
}
@@ -709,13 +712,11 @@ window.toggleAutoScroll = async function (_autoScroll) {
712
};
713
714
window.toggleJson = async function (showJson) {
712
- // add display:none to .msg-json class definition
713
- toggleCssProperty(".msg-json", "display", showJson ? "block" : "none");
715
+ css.toggleCssProperty(".msg-json", "display", showJson ? "block" : "none");
716
};
717
718
window.toggleThoughts = async function (showThoughts) {
717
- // add display:none to .msg-json class definition
718
- toggleCssProperty(
719
+ css.toggleCssProperty(
720
".msg-thoughts",
721
"display",
722
showThoughts ? undefined : "none"
@@ -723,10 +724,11 @@ window.toggleThoughts = async function (showThoughts) {
724
};
725
726
window.toggleUtils = async function (showUtils) {
726
- // add display:none to .msg-json class definition
727
- toggleCssProperty(".message-util", "display", showUtils ? undefined : "none");
728
- // toggleCssProperty('.message-util .msg-kvps', 'display', showUtils ? undefined : 'none');
729
- // toggleCssProperty('.message-util .msg-content', 'display', showUtils ? undefined : 'none');
727
+ css.toggleCssProperty(
728
+ ".message-util",
729
+ "display",
730
+ showUtils ? undefined : "none"
731
+ );
732
};
733
734
window.toggleDarkMode = function (isDark) {
@@ -799,30 +801,6 @@ document.addEventListener("DOMContentLoaded", () => {
801
toggleDarkMode(isDarkMode);
802
});
803
802
-function toggleCssProperty(selector, property, value) {
803
- // Get the stylesheet that contains the class
804
- const styleSheets = document.styleSheets;
805
-
806
- // Iterate through all stylesheets to find the class
807
- for (let i = 0; i < styleSheets.length; i++) {
808
- const styleSheet = styleSheets[i];
809
- const rules = styleSheet.cssRules || styleSheet.rules;
810
-
811
- for (let j = 0; j < rules.length; j++) {
812
- const rule = rules[j];
813
- if (rule.selectorText == selector) {
814
- // Check if the property is already applied
815
- if (value === undefined) {
816
- rule.style.removeProperty(property);
817
- } else {
818
- rule.style.setProperty(property, value);
819
- }
820
- return;
821
- }
822
- }
823
- }
824
-}
825
-
804
window.loadChats = async function () {
805
try {
806
const fileContents = await readJsonFiles();
webui/js/messages.js
+23
-4
@@ -170,8 +170,8 @@ export function _drawMessage(
170
const minMaxBtn = document.createElement("div");
171
minMaxBtn.classList.add("msg-min-max-btns");
172
minMaxBtn.innerHTML = `
173
- <a href="#" class="msg-min-max-btn" @click.prevent="$store.messageResize.minimizeMessageClass('${mainClass}', $event)"><span class="material-symbols-outlined">minimize</span></a>
174
- <a href="#" class="msg-min-max-btn" @click.prevent="$store.messageResize.maximizeMessageClass('${mainClass}', $event)"><span class="material-symbols-outlined">expand_all</span></a>
173
+ <a href="#" class="msg-min-max-btn" @click.prevent="$store.messageResize.minimizeMessageClass('${mainClass}', $event)"><span class="material-symbols-outlined" x-text="$store.messageResize.getSetting('${mainClass}').minimized ? 'expand_content' : 'minimize'"></span></a>
174
+ <a href="#" class="msg-min-max-btn" x-show="!$store.messageResize.getSetting('${mainClass}').minimized" @click.prevent="$store.messageResize.maximizeMessageClass('${mainClass}', $event)"><span class="material-symbols-outlined" x-text="$store.messageResize.getSetting('${mainClass}').maximized ? 'expand' : 'expand_all'"></span></a>
175
`;
176
headingElement.appendChild(minMaxBtn);
177
}
@@ -208,6 +208,7 @@ export function _drawMessage(
208
209
contentDiv.appendChild(spanElement);
210
addCopyButtonToElement(contentDiv);
211
+ adjustMarkdownRender(contentDiv);
212
bodyDiv.appendChild(contentDiv);
213
} else {
214
const preElement = document.createElement("pre");
@@ -360,7 +361,7 @@ export function drawMessageUser(
361
362
const headingElement = document.createElement("h4");
363
headingElement.classList.add("msg-heading");
363
- headingElement.textContent = "User message";
364
+ headingElement.innerHTML = "User message <span class='icon material-symbols-outlined'>person</span>";
365
messageDiv.appendChild(headingElement);
366
367
if (content && content.trim().length > 0) {
@@ -766,7 +767,7 @@ function convertImgFilePaths(str) {
767
return str.replace("img://", "/image_get?path=");
768
}
769
769
-function convertIcons(str) {
770
+export function convertIcons(str) {
771
return str.replace(
772
/icon:\/\/([a-zA-Z0-9_]+)/g,
773
'<span class="icon material-symbols-outlined">$1</span>'
@@ -820,6 +821,24 @@ function convertPathsToLinks(str) {
821
.join("");
822
}
823
824
+function adjustMarkdownRender(element) {
825
+ // find all tables in the element
826
+ const tables = element.querySelectorAll('table');
827
+
828
+ // wrap each table with a div with class message-markdown-table-wrap
829
+ tables.forEach(table => {
830
+ // create wrapper div
831
+ const wrapper = document.createElement('div');
832
+ wrapper.className = 'message-markdown-table-wrap';
833
+
834
+ // insert wrapper before table in the DOM
835
+ table.parentNode.insertBefore(wrapper, table);
836
+
837
+ // move table into wrapper
838
+ wrapper.appendChild(table);
839
+ });
840
+}
841
+
842
// function convertPathsToLinksInHtml(htmlString) {
843
// // 1. Parse the input safely
844
// const wrapper = document.createElement("div");