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");