frdel commited on
Commit
a730828
·
1 Parent(s): 8226946

messages redesign wip

Browse files
agent.py CHANGED
@@ -300,7 +300,7 @@ class Agent:
300
 
301
  # non-config vars
302
  self.number = number
303
- self.agent_name = f"Agent {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
- )
349
 
350
  async def reasoning_callback(chunk: str, full: str):
351
  if chunk == full:
@@ -360,6 +349,7 @@ class Agent:
360
  printer.stream(chunk)
361
  await self.handle_response_stream(full)
362
 
 
363
  agent_response, _reasoning = await self.call_chat_model(
364
  messages=prompt,
365
  response_callback=stream_callback,
 
300
 
301
  # non-config vars
302
  self.number = 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
  # prepare LLM chain (model, system, history)
334
  prompt = await self.prepare_prompt(loop_data=self.loop_data)
335
 
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:
 
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 ADDED
File without changes
python/extensions/before_main_llm_call/_10_log_for_stream.py ADDED
@@ -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...")
python/extensions/message_loop_prompts_after/_50_recall_memories.py CHANGED
@@ -5,6 +5,7 @@ from agent import LoopData
5
 
6
  DATA_NAME_TASK = "_recall_memories_task"
7
 
 
8
  class RecallMemories(Extension):
9
 
10
  INTERVAL = 3
@@ -16,13 +17,14 @@ class RecallMemories(Extension):
16
 
17
  # every 3 iterations (or the first one) recall memories
18
  if loop_data.iteration % RecallMemories.INTERVAL == 0:
19
- task = asyncio.create_task(self.search_memories(loop_data=loop_data, **kwargs))
 
 
20
  else:
21
  task = None
22
 
23
  # set to agent to be able to wait for it
24
  self.agent.set_data(DATA_NAME_TASK, task)
25
-
26
 
27
  async def search_memories(self, loop_data: LoopData, **kwargs):
28
 
@@ -32,12 +34,8 @@ class RecallMemories(Extension):
32
  del extras["memories"]
33
 
34
  # 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
41
  log_item = self.agent.context.log.log(
42
  type="util",
43
  heading="Searching memories...",
@@ -47,7 +45,7 @@ class RecallMemories(Extension):
47
  # msgs_text = self.agent.concat_messages(
48
  # self.agent.history[-RecallMemories.HISTORY :]
49
  # ) # only last X messages
50
- msgs_text = self.agent.history.output_text()[-RecallMemories.HISTORY:]
51
  system = self.agent.read_prompt(
52
  "memory.memories_query.sys.md", history=msgs_text
53
  )
@@ -59,7 +57,9 @@ class RecallMemories(Extension):
59
  # call util llm to summarize conversation
60
  query = await self.agent.call_utility_model(
61
  system=system,
62
- message=loop_data.user_message.output_text() if loop_data.user_message else "",
 
 
63
  callback=log_callback,
64
  )
65
 
 
5
 
6
  DATA_NAME_TASK = "_recall_memories_task"
7
 
8
+
9
  class RecallMemories(Extension):
10
 
11
  INTERVAL = 3
 
17
 
18
  # every 3 iterations (or the first one) recall memories
19
  if loop_data.iteration % RecallMemories.INTERVAL == 0:
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)
 
28
 
29
  async def search_memories(self, loop_data: LoopData, **kwargs):
30
 
 
34
  del extras["memories"]
35
 
36
  # try:
37
+
38
+ # show full util message
 
 
 
 
39
  log_item = self.agent.context.log.log(
40
  type="util",
41
  heading="Searching memories...",
 
45
  # msgs_text = self.agent.concat_messages(
46
  # self.agent.history[-RecallMemories.HISTORY :]
47
  # ) # only last X messages
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
  )
 
57
  # call util llm to summarize conversation
58
  query = await self.agent.call_utility_model(
59
  system=system,
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 CHANGED
@@ -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
- )
39
 
40
- # show full util message, this will hide temp message immediately if turned on
41
  log_item = self.agent.context.log.log(
42
  type="util",
43
  heading="Searching memory for solutions...",
 
32
  del extras["solutions"]
33
 
34
  # try:
 
 
 
 
35
 
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 CHANGED
@@ -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
22
  log_item = self.agent.context.log.log(
23
  type="util",
24
  heading="Memorizing new information...",
 
13
  async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
14
  # try:
15
 
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 CHANGED
@@ -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
22
  log_item = self.agent.context.log.log(
23
  type="util",
24
  heading="Memorizing succesful solutions...",
 
12
 
13
  async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
14
  # try:
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 CHANGED
@@ -5,15 +5,15 @@ import asyncio
5
  from python.helpers.log import LogItem
6
  from python.helpers import log
7
  import math
8
-
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})..."
17
 
18
  # create log message and store it in loop data temporary params
19
  if "log_item_generating" not in loop_data.params_temporary:
 
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
  class LogFromStream(Extension):
11
 
12
  async def execute(self, loop_data: LoopData = LoopData(), text: str = "", **kwargs):
13
 
14
  # thought length indicator
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 CHANGED
@@ -5,6 +5,7 @@ import asyncio
5
  from python.helpers.log import LogItem
6
  from python.helpers import log
7
  import math
 
8
 
9
 
10
  class LogFromStream(Extension):
@@ -17,17 +18,17 @@ class LogFromStream(Extension):
17
  **kwargs,
18
  ):
19
 
20
- heading = f"{self.agent.agent_name}: Generating..."
21
  if "headline" in parsed:
22
- heading = f"{self.agent.agent_name}: {parsed['headline']}"
23
  elif "thoughts" in parsed:
24
  # thought length indicator
25
  thoughts = "\n".join(parsed["thoughts"])
26
- length = math.ceil(len(thoughts) / 10) * 10
27
- heading = f"{self.agent.agent_name}: Thinking ({length})..."
28
 
29
- if "tool_name" in parsed:
30
- heading += f" ({parsed['tool_name']})"
31
 
32
  # create log message and store it in loop data temporary params
33
  if "log_item_generating" not in loop_data.params_temporary:
@@ -48,4 +49,4 @@ class LogFromStream(Extension):
48
  kvps.update(parsed)
49
 
50
  # update the log item
51
- log_item.update(heading=heading, content=text, kvps=kvps)
 
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):
 
18
  **kwargs,
19
  ):
20
 
21
+ heading = build_default_heading(self.agent)
22
  if "headline" in parsed:
23
+ heading = build_heading(self.agent, parsed['headline'])
24
  elif "thoughts" in parsed:
25
  # thought length indicator
26
  thoughts = "\n".join(parsed["thoughts"])
27
+ pipes = "|" * math.ceil(math.sqrt(len(thoughts)))
28
+ heading = build_heading(self.agent, f"Thinking... {pipes}")
29
 
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:
 
49
  kvps.update(parsed)
50
 
51
  # update the log item
52
+ log_item.update(heading=heading, content=text, kvps=kvps)
python/extensions/response_stream/_20_live_response.py CHANGED
@@ -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",
34
  )
35
  )
36
 
 
30
  loop_data.params_temporary["log_item_response"] = (
31
  self.agent.context.log.log(
32
  type="response",
33
+ heading=f"icon://chat {self.agent.agent_name}: Responding",
34
  )
35
  )
36
 
python/helpers/tool.py CHANGED
@@ -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}'"
46
  else:
47
- heading = f"{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):
 
42
 
43
  def get_log_object(self):
44
  if self.method:
45
+ heading = f"icon://construction {self.agent.agent_name}: Using tool '{self.name}:{self.method}'"
46
  else:
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 CHANGED
@@ -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}'",
317
  content="",
318
  kvps=self.args,
319
  )
 
313
  def get_log_object(self):
314
  return self.agent.context.log.log(
315
  type="browser",
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 CHANGED
@@ -31,4 +31,12 @@ class Delegation(Tool):
31
  result = await subordinate.monologue()
32
 
33
  # result
34
- return Response(message=result, break_loop=False)
 
 
 
 
 
 
 
 
 
31
  result = await subordinate.monologue()
32
 
33
  # result
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
+ )
python/tools/input.py CHANGED
@@ -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)
24
 
25
  async def after_execution(self, response, **kwargs):
26
  self.agent.hist_add_tool_result(self.name, response.message)
 
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"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)
python/tools/{knowledge_tool.py → knowledge_tool._py} RENAMED
File without changes
python/tools/task_done.py DELETED
@@ -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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
python/tools/{webpage_content_tool.py → webpage_content_tool._py} RENAMED
File without changes
webui/components/messages/resize/message-resize-store.js CHANGED
@@ -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 },
18
  };
19
  },
20
 
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);
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);
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" });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  },
63
 
64
  _applySetting(className, setting) {
 
14
  _getDefaultSettings() {
15
  return {
16
  "message-agent": { minimized: true, maximized: false },
17
+ "message-agent-response": { minimized: false, maximized: true },
18
  };
19
  },
20
 
21
+ getSetting(className) {
22
  return this.settings[className] || { minimized: false, maximized: false };
23
  },
24
 
 
41
  },
42
 
43
  async minimizeMessageClass(className, event) {
44
+ const set = this.getSetting(className);
45
  set.minimized = !set.minimized;
46
  this._setSetting(className, set);
47
  this._applySetting(className, set);
 
49
  },
50
 
51
  async maximizeMessageClass(className, event) {
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
  },
59
 
60
  _applyScroll(event) {
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 CHANGED
@@ -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
-
48
 
49
  /* Message Styles */
50
 
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
- }
257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
  /* Copy button styles */
260
  .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
- }
 
 
 
336
 
337
  /* Utility Classes */
338
  .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
- }
 
385
 
386
  /* Math (KaTeX) */
387
  .katex {
388
- line-height: 1.2 !important;
389
- font-size: 1.1em;
390
- }
391
 
392
  /* Media Queries */
393
  @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
- }
437
 
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;
500
  }
501
 
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;
539
  }
540
-
541
- .msg-content h6 {
542
- font-size: 1em;
543
- font-weight: 500;
544
- margin-bottom: 0.2em;
 
545
  }
546
-
547
- .msg-content table {
 
 
548
  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);
561
  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;
596
  }
597
 
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);;
635
  }
636
 
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;
654
  word-break: break-word;
655
  overflow-wrap: anywhere;
656
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
 
48
  /* Message Styles */
49
 
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 {
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 {
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 {
402
+ line-height: 1.2 !important;
403
+ font-size: 1.1em;
404
+ }
405
 
406
  /* Media Queries */
407
  @media (max-width: 640px) {
408
+ /* New styles for mobile messages */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
+ .message-followup {
411
+ /* margin-left: var(--spacing-md); */
412
+ margin-bottom: var(--spacing-md);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  }
414
 
415
+ .msg-kvps {
416
+ display: flex;
417
+ flex-direction: column;
418
+ border-collapse: separate;
419
+ border-spacing: 0 0.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  }
421
+
422
+ .msg-kvps tr {
423
+ display: flex;
424
+ flex-direction: column;
425
+ margin-top: 0.3rem;
426
+ padding-bottom: 0;
427
  }
428
+
429
+ .msg-kvps th,
430
+ .msg-kvps td {
431
+ display: block;
432
  width: 100%;
 
 
 
 
 
 
 
 
 
 
 
 
433
  text-align: left;
434
+ border-bottom: none;
435
+ padding: 0.25rem 0;
436
+ padding-left: 0 !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
437
  }
438
 
439
+ .msg-kvps th {
440
+ color: var(--color-primary);
441
+ margin-bottom: 0.25rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  }
443
 
444
+ .kvps-val {
445
+ margin: 0 0 0.4rem 0;
446
+ white-space: pre-wrap;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  word-break: break-word;
448
  overflow-wrap: anywhere;
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 CHANGED
@@ -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
 
5
  window.fetchApi = api.fetchApi; // TODO - backward compatibility for non-modular scripts, remove once refactored to alpine
6
 
@@ -513,6 +514,8 @@ function updateProgress(progress, active) {
513
  addClassToElement(progressBar, "shiny-text");
514
  }
515
 
 
 
516
  if (progressBar.innerHTML != progress) {
517
  progressBar.innerHTML = progress;
518
  }
@@ -709,13 +712,11 @@ window.toggleAutoScroll = async function (_autoScroll) {
709
  };
710
 
711
  window.toggleJson = async function (showJson) {
712
- // add display:none to .msg-json class definition
713
- toggleCssProperty(".msg-json", "display", showJson ? "block" : "none");
714
  };
715
 
716
  window.toggleThoughts = async function (showThoughts) {
717
- // add display:none to .msg-json class definition
718
- toggleCssProperty(
719
  ".msg-thoughts",
720
  "display",
721
  showThoughts ? undefined : "none"
@@ -723,10 +724,11 @@ window.toggleThoughts = async function (showThoughts) {
723
  };
724
 
725
  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');
 
730
  };
731
 
732
  window.toggleDarkMode = function (isDark) {
@@ -799,30 +801,6 @@ document.addEventListener("DOMContentLoaded", () => {
799
  toggleDarkMode(isDarkMode);
800
  });
801
 
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
-
826
  window.loadChats = async function () {
827
  try {
828
  const fileContents = await readJsonFiles();
 
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
 
 
514
  addClassToElement(progressBar, "shiny-text");
515
  }
516
 
517
+ progress = msgs.convertIcons(progress);
518
+
519
  if (progressBar.innerHTML != progress) {
520
  progressBar.innerHTML = progress;
521
  }
 
712
  };
713
 
714
  window.toggleJson = async function (showJson) {
715
+ css.toggleCssProperty(".msg-json", "display", showJson ? "block" : "none");
 
716
  };
717
 
718
  window.toggleThoughts = async function (showThoughts) {
719
+ css.toggleCssProperty(
 
720
  ".msg-thoughts",
721
  "display",
722
  showThoughts ? undefined : "none"
 
724
  };
725
 
726
  window.toggleUtils = async function (showUtils) {
727
+ css.toggleCssProperty(
728
+ ".message-util",
729
+ "display",
730
+ showUtils ? undefined : "none"
731
+ );
732
  };
733
 
734
  window.toggleDarkMode = function (isDark) {
 
801
  toggleDarkMode(isDarkMode);
802
  });
803
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
804
  window.loadChats = async function () {
805
  try {
806
  const fileContents = await readJsonFiles();
webui/js/messages.js CHANGED
@@ -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>
175
  `;
176
  headingElement.appendChild(minMaxBtn);
177
  }
@@ -208,6 +208,7 @@ export function _drawMessage(
208
 
209
  contentDiv.appendChild(spanElement);
210
  addCopyButtonToElement(contentDiv);
 
211
  bodyDiv.appendChild(contentDiv);
212
  } else {
213
  const preElement = document.createElement("pre");
@@ -360,7 +361,7 @@ export function drawMessageUser(
360
 
361
  const headingElement = document.createElement("h4");
362
  headingElement.classList.add("msg-heading");
363
- headingElement.textContent = "User message";
364
  messageDiv.appendChild(headingElement);
365
 
366
  if (content && content.trim().length > 0) {
@@ -766,7 +767,7 @@ function convertImgFilePaths(str) {
766
  return str.replace("img://", "/image_get?path=");
767
  }
768
 
769
- function convertIcons(str) {
770
  return str.replace(
771
  /icon:\/\/([a-zA-Z0-9_]+)/g,
772
  '<span class="icon material-symbols-outlined">$1</span>'
@@ -820,6 +821,24 @@ function convertPathsToLinks(str) {
820
  .join("");
821
  }
822
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
  // function convertPathsToLinksInHtml(htmlString) {
824
  // // 1. Parse the input safely
825
  // const wrapper = document.createElement("div");
 
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" 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
 
209
  contentDiv.appendChild(spanElement);
210
  addCopyButtonToElement(contentDiv);
211
+ adjustMarkdownRender(contentDiv);
212
  bodyDiv.appendChild(contentDiv);
213
  } else {
214
  const preElement = document.createElement("pre");
 
361
 
362
  const headingElement = document.createElement("h4");
363
  headingElement.classList.add("msg-heading");
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) {
 
767
  return str.replace("img://", "/image_get?path=");
768
  }
769
 
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>'
 
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");