Index: /branches/amp_4_0/platform/tools/container/services/amp-core/Dockerfile
===================================================================
--- /branches/amp_4_0/platform/tools/container/services/amp-core/Dockerfile	(revision 2954)
+++ /branches/amp_4_0/platform/tools/container/services/amp-core/Dockerfile	(working copy)
@@ -16,6 +16,7 @@
     libxml2 \
     openssl-libs \
     openssl-devel \
+    openssh-clients \
     # Terminal/UI libraries
     ncurses-libs \
     # Database client library
Index: /branches/amp_4_0/platform/tools/container/stack.yml.template
===================================================================
--- /branches/amp_4_0/platform/tools/container/stack.yml.template	(revision 2955)
+++ /branches/amp_4_0/platform/tools/container/stack.yml.template	(working copy)
@@ -523,6 +523,8 @@
         read_only: true
       # Mount Docker socket for service health checks
       - /var/run/docker.sock:/var/run/docker.sock:ro
+      # Mount telegraf config directory for dynamic updates
+      - /opt/amp/telegraf.d:/opt/amp/telegraf.d:rw
     extra_hosts:
       - "host.docker.internal:host-gateway"
     secrets:
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/lib/console.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/lib/console.py	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/lib/console.py	(working copy)
@@ -15,9 +15,12 @@
 logger = logging.getLogger('hive.debug')
 
 try:
-    import cStringIO as StringIO
+    from io import StringIO
 except ImportError:
-    import StringIO
+    try:
+        import cStringIO as StringIO
+    except ImportError:
+        import StringIO
 
 
 class Terminal:
@@ -236,7 +239,7 @@
     def utf8_decode(self, d):
         o = ''
         for c in d:
-            char = ord(c)
+            char = c if isinstance(c, int) else ord(c)
             if self.utf8_units_count != self.utf8_units_received:
                 self.utf8_units_received += 1
                 if (char & 0xc0) == 0x80:
@@ -253,7 +256,7 @@
                     self.utf8_units_count = 0
             else:
                 if (char & 0x80) == 0x00:
-                    o += c
+                    o += chr(char)
                 elif (char & 0xe0) == 0xc0:
                     self.utf8_units_count = 1
                     self.utf8_char = char & 0x1f
@@ -1095,15 +1098,13 @@
                     if wx <= self.w:
                         dump += chr(char)
             dump += "\n"
-        # Encode in UTF-8
-        dump = dump.encode('utf-8')
         dump += '</span>'
         # Cache dump
         if self.dump_cache == dump:
             return ''
         else:
             self.dump_cache = dump
-            return '<c cy="%03d" />' % cy + dump
+            return '<c cy="%03d" cx="%03d" />' % (cy, cx) + dump
 
 
 class SynchronizedMethod:
@@ -1178,27 +1179,38 @@
                 os.putenv('LANG', ls[0] + '.UTF-8')
                 os.execv(command[0], command)
 
-            except (IOError, OSError):
+            except Exception as e:
+                # We need to know if execv blew up! Wait, we can't print easily from child.
                 pass
             os._exit(0)
         else:
+            time.sleep(0.5) # Give ssh at least half a second to output.
             while True:
-                output = os.read(fd, 1024).strip()
-                logger.info(output)
+                try:
+                    output_bytes = os.read(fd, 1024)
+                except OSError as e:
+                    logger.error('proc_spawn os.read OSError: ' + str(e))
+                    raise Exception('SSH OSREAD ERR: ' + str(e))
+
+                if not output_bytes:
+                    raise Exception('SSH EMPTY BYTES')
+
+                output = output_bytes.decode('utf-8', errors='replace').strip()
+                logger.info("SSH PTY RAW: %s", output)
                 lower = output.lower()
                 # Write the password
                 if lower.find('password') != -1:
-                    os.write(fd, password + '\n')
+                    os.write(fd, (password + '\n').encode('utf-8'))
                     break
-                elif 'are you sure you want to continue connecting (yes/no)?' in lower:
-                    os.write(fd, 'yes\n')
+                elif 'are you sure you want to continue connecting (yes/no)?' in lower or 'are you sure you want to continue connecting (yes/no/[fingerprint])?' in lower:
+                    os.write(fd, b'yes\n')
                 elif 'company privacy warning' in lower:
                     pass  # This is an understood message
                 elif 'warning: rsa key found for host' in lower:
-                    os.write(fd, 'yes\n')
+                    os.write(fd, b'yes\n')
                 elif 'warning: permanently added' in lower:
                     pass
-                elif "could not create directory '/root/.ssh'" in lower:
+                elif "could not create directory" in lower:
                     pass
                 elif 'failed to add the host to the list of known hosts' in lower:
                     pass
@@ -1206,15 +1218,23 @@
                     pass
                 elif 'ecdsa key fingerprint is' in lower:
                     pass
+                elif 'ed25519 key fingerprint is' in lower:
+                    pass
                 elif 'the authenticity of host' in lower:
                     pass
                 elif 'yes' in lower:
                     pass
+                elif 'connection refused' in lower or 'connection reset' in lower or 'permission denied' in lower:
+                    logger.error('SSH Connection failed: %s', output)
+                    return False
+                elif 'last login' in lower or lower.endswith('$') or lower.endswith('#') or lower.endswith('>'):
+                    # Successfully logged in via key auth, or hit the prompt directly!
+                    break
                 else:
                     if lower:
-                        assert 0, lower
+                        logger.warning('Unrecognized ssh output: %s', lower)
             ####
-            self.session[sid] = {'term': Terminal(w, h), 'w': w, 'h': h}
+            self.session[sid] = {'term': Terminal(w, h), 'w': w, 'h': h, 'dead': False}
             logger.info('Launch console for vm: ' + sid)
             self.session[sid]['pid'] = pid
             self.session[sid]['fd'] = fd
@@ -1232,7 +1252,6 @@
         try:
             os.close(self.session[sid]['fd'])
         except (KeyError, IOError, OSError) as e:
-            logger.info('proc_waitfordeath close exception: ' + str(e))
             pass
         try:
             os.waitpid(self.session[sid]['pid'], 0)
@@ -1247,17 +1266,25 @@
         try:
             fd = self.session[sid]['fd']
             d = os.read(fd, 65536)
+            logger.info("PROC READ RAW: %r", d)
             if not d:
-                # Process finished, BSD
-                logger.error('proc_read read return empty')
-                self.proc_waitfordeath(sid)
+                # Process finished, Mac OS X
+                logger.error("PROC READ EOF. SSH PROCESS EXITED. sid=%s", sid)
+                if sid in self.session:
+                    self.proc_waitfordeath(sid)
+                    self.session[sid]['dead'] = True
                 return False
         except (IOError, OSError) as e:
-            # Process finished, Linux
-            logger.error('proc_read exception: ' + str(e))
+            import errno
+            if hasattr(e, 'errno') and e.errno in (errno.EAGAIN, errno.EWOULDBLOCK):
+                # Simply no data right now on non-blocking socket
+                return False
+                
+            # Process finished, Linux -> [Errno 5] Input/output error
+            logger.error('proc_read IO/OSError exception (Child died): ' + str(e))
             if sid in self.session:
                 self.proc_waitfordeath(sid)
-                del self.session[sid]
+                self.session[sid]['dead'] = True
             return False
         term = self.session[sid]['term']
         term.write(d)
@@ -1265,7 +1292,7 @@
         d = term.read()
         if d:
             try:
-                os.write(fd, d)
+                os.write(fd, d.encode('utf-8') if isinstance(d, str) else d)
             except (IOError, OSError) as e:
                 logger.error('Read response error: ' + str(e))
                 return False
@@ -1275,11 +1302,13 @@
     def proc_write(self, sid, d):
         if sid not in self.session:
             return False
+        if self.session[sid].get('dead', False):
+            return False
         try:
             term = self.session[sid]['term']
             d = term.pipe(d)
             fd = self.session[sid]['fd']
-            os.write(fd, d)
+            os.write(fd, d.encode('utf-8') if isinstance(d, str) else d)
         except (IOError, OSError) as e:
             logger.error('proc_write error: ' + str(e))
             return False
@@ -1298,8 +1327,9 @@
             fds = []
             fd2sid = {}
             for sid in self.session.keys():
-                fds.append(self.session[sid]['fd'])
-                fd2sid[self.session[sid]['fd']] = sid
+                if not self.session[sid].get('dead', False):
+                    fds.append(self.session[sid]['fd'])
+                    fd2sid[self.session[sid]['fd']] = sid
             try:
                 i, o, e = select.select(fds, [], [], 1.0)
             except (IOError, OSError) as e:
@@ -1307,50 +1337,54 @@
                 i = []
             except Exception as e:
                 logger.error('poll_proc_thread unknown exception: ' + str(e))
+                i = []
             for fd in i:
-                sid = fd2sid[fd]
-                self.proc_read(sid)
-            if len(i) > 0:
-                time.sleep(0.002)
+                sid = fd2sid.get(fd)
+                if sid:
+                    self.proc_read(sid)
+            time.sleep(0.002)
         return False
 
 
 class SingletonType(type):
-    def __call__(cls, *args, **kwargs):
-        try:
-            return cls.__instance
-        except AttributeError:
-            cls.__instance = super(SingletonType, cls).__call__(*args, **kwargs)
-            return cls.__instance
+    def __init__(cls, name, bases, dict):
+        super(SingletonType, cls).__init__(name, bases, dict)
+        cls.instance = None
 
+    def __call__(cls, *args, **kw):
+        if cls.instance is None:
+            cls.instance = super(SingletonType, cls).__call__(*args, **kw)
+        return cls.instance
 
-class WebConsoleRequestHandler:
-    __metaclass__ = SingletonType
 
+class WebConsoleRequestHandler(metaclass=SingletonType):
     def __init__(self):
         self.multiplex = Multiplex()
 
     def handle_get(self, vmname, w, h, k, pid):
         if not vmname in self.multiplex.session:
-            logger.error('console disconnected: %s' % vmname)
             return 'Error: Web console disconnected.'
         if self.multiplex.session[vmname]['pid'] != int(pid):
-            logger.info('Console occupied by another user: %s, %d' % (vmname, self.multiplex.session[vmname]['pid']))
             return 'Error: Web console is occupied by another admin user.'
         try:
-            if self.multiplex.proc_keepalive(vmname, w, h):
+            is_dead = self.multiplex.session[vmname].get('dead', False)
+            if not is_dead and self.multiplex.proc_keepalive(vmname, w, h):
                 if k:
-                    # logger.info('proc_write: %s' % k)
                     self.multiplex.proc_write(vmname, k)
-                time.sleep(0.002)
-                d = self.multiplex.proc_dump(vmname)
-                # logger.info(d)
-                return '<?xml version="1.0" encoding="UTF-8"?>' + d
-            else:
-                logger.error('Console disconnected: %s' % vmname)
-                return 'Error: Web console disconnected.'
-        except (KeyError, ValueError, IndexError):
-            logger.error('Invalid console parameters')
+            
+            time.sleep(0.002)
+            d = self.multiplex.proc_dump(vmname)
+            
+            if is_dead:
+                del self.multiplex.session[vmname]
+                if d and '<span' in d:
+                    return '<?xml version="1.0" encoding="UTF-8"?>' + d
+                else:
+                    return 'Error: Web console connection closed by remote host.'
+
+            return '<?xml version="1.0" encoding="UTF-8"?>' + d
+        except (KeyError, ValueError, IndexError) as e:
+            logger.error('Invalid console parameters: ' + str(e))
             return 'Error: Web console disconnected.'
 
     def create_console(self, vmname, username, password, width, height, ip=LEGACY_CLI_AGENT_IP):
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/device_mgmt/device/__init__.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/device_mgmt/device/__init__.py	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/models/device_mgmt/device/__init__.py	(working copy)
@@ -1,4 +1,3 @@
-# hash restapi password with md5 by Jason Chou
 import datetime
 import re
 import time
@@ -510,6 +509,7 @@
 
             import uuid
             import datetime
+            import json
 
             data['id'] = str(uuid.uuid1())
             data['zone'] = "default"
@@ -654,14 +654,18 @@
                     if model != data.get('type'):
                         raise ModelQueryException(CLICmdError(
                                                       __('Register Failed! Current device model is %s but you choose %s.') % (model, data['type'])))
+
+            snmp_enable = bool(data.get('snmp_general', {}).get('snmp_enable', True))
+            community = data.get('snmp_general', {}).get('community', 'public')
+            data['snmp_general'] = json.dumps({"snmp_enable": snmp_enable, "community": community})
 
             db = DB.get_connected_db()
             save_sql = (
                 "INSERT INTO device("
                 "id, zone, name, ip_address, protocol, restapi_port, restapi_username, restapi_password, "
                 "console_username, console_password, connection, status, license_key, gateway_domain, location, "
-                "firewall_ip, intranet_ip, version, type, own, log_enable, webui_port, device_group, enable_password"
-                ") Values ('%(id)s', '%(zone)s', '%(name)s', '%(ip)s', '%(protocol)s', '%(restapi_port)s', '%(restapi_username)s', '%(restapi_password)s', '%(console_username)s', '%(console_password)s', 'connected', '%(status)s', '%(license_key)s', '%(gateway_domain)s', '%(location)s', '%(firewall_ip)s', '%(intranet_ip)s', '%(version)s', '%(type)s', '%(own)s', '%(log_enable)d', '%(webui_port)d', '%(device_group)s', '%(enable_password)s')"
+                "firewall_ip, intranet_ip, version, type, own, log_enable, webui_port, device_group, enable_password, snmp_general"
+                ") Values ('%(id)s', '%(zone)s', '%(name)s', '%(ip)s', '%(protocol)s', '%(restapi_port)s', '%(restapi_username)s', '%(restapi_password)s', '%(console_username)s', '%(console_password)s', 'connected', '%(status)s', '%(license_key)s', '%(gateway_domain)s', '%(location)s', '%(firewall_ip)s', '%(intranet_ip)s', '%(version)s', '%(type)s', '%(own)s', '%(log_enable)d', '%(webui_port)d', '%(device_group)s', '%(enable_password)s', '%(snmp_general)s')"
             ) % data
             db.execute_sql(save_sql)
             create_time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
@@ -710,8 +714,91 @@
                 mark_expire_all(get_model('cm', ['device_mgmt', 'device_group', 'DeviceGroup']))
 
             insert_default_tmpkey(data['ip'])
+            
+            # Dynamically update Telegraf configs
+            if data.get('type'):
+                import logging
+                logging.getLogger('hive.debug').info("Triggering Telegraf TOML update for type: %s" % data.get('type'))
+                self._update_telegraf_toml(data['type'])
+            
             return
 
+        def _update_telegraf_toml(self, device_type):
+            from cm.lib.postgres_db import DB
+            import logging
+            import os
+            import re
+            import json
+            
+            logger = logging.getLogger('hive.debug')
+            logger.info("[TELEGRAF] Update trigger for %s" % device_type)
+            try:
+                db = DB.get_connected_db()
+                select_sql = "SELECT ip_address, snmp_general FROM device WHERE type='%s'" % device_type
+                rows = db.fetchall(select_sql)
+                db.close()
+
+                agents = []
+                if rows:
+                    for row in rows:
+                        ip = row[0]
+                        try:
+                            snmp_info = json.loads(row[1]) if row[1] else {}
+                            if snmp_info.get('snmp_enable'):
+                                agents.append('"%s:161"' % ip)
+                        except Exception as e:
+                            logger.error("[TELEGRAF] JSON error for %s: %s" % (ip, str(e)))
+
+                # Form the new agents array string
+                agents_str = 'agents = [%s]' % ', '.join(agents)
+                logger.info("[TELEGRAF] %d agents found. New string: %s" % (len(agents), agents_str))
+
+                telegraf_dir = '/opt/amp/telegraf.d'
+                
+                # Normalize device type to match available .toml config files
+                # (vAPV/APV -> apv.toml, vxAG/AG -> ag.toml, vASF/ASF -> asf.toml)
+                normalized_type = device_type.lower()
+                if normalized_type in ['vapv', 'apv']:
+                    base_filename = 'apv.toml'
+                elif normalized_type in ['ag', 'vxag', 'vag']:
+                    base_filename = 'ag.toml'
+                elif normalized_type in ['asf', 'vasf']:
+                    base_filename = 'asf.toml'
+                else:
+                    base_filename = '%s.toml' % normalized_type
+
+                toml_file_path = os.path.join(telegraf_dir, base_filename)
+
+                if os.path.exists(toml_file_path):
+                    with open(toml_file_path, 'r') as f:
+                        file_data = f.read()
+
+                    # Ultra-robust regex: matches 'agents = [...]' even across multiples
+                    # Handles leading whitespace, multi-line brackets, and hidden chars
+                    pattern = r'(?s)agents\s*=\s*\[[^\]]*\]'
+                    
+                    found_matches = re.findall(pattern, file_data)
+                    logger.info("[TELEGRAF] Found %d matching agents blocks in %s" % (len(found_matches), toml_file_path))
+
+                    new_file_data = re.sub(pattern, agents_str, file_data)
+
+                    if new_file_data != file_data:
+                        logger.info("[TELEGRAF] Writing changes to %s" % toml_file_path)
+                        with open(toml_file_path, 'w') as f:
+                            f.write(new_file_data)
+                            f.flush()
+                            os.fsync(f.fileno())
+                        
+                        from hive.services.utils import perform_observability_services_restart
+                        perform_observability_services_restart('telegraf')
+                        logger.info("[TELEGRAF] Restart triggered.")
+                    else:
+                        logger.info("[TELEGRAF] File content is already up-to-date.")
+                else:
+                    logger.error("[TELEGRAF] Config file %s does not exist!" % toml_file_path)
+            except Exception as e:
+                logger.error("[TELEGRAF] Update failed: %s" % str(e))
+
         # def set_log_host(ip_addr, ip_port, restapi_username, restapi_password):
         #     "log http welf\n log option showlogidkey on\n log level debug \n vpn netpool trafficlog <netpool_name>"
         #     pass
@@ -930,13 +1017,19 @@
         def _perform_EnableMonitoring(self, options):
             pk_list = options['__pk_list']
             for pk_dict in pk_list:
-                self.__update_snmp_general(pk_dict['id'], get_rest_info_from_device(pk_dict['name']), 'snmp on')
+                device_info = get_rest_info_from_device(pk_dict['name'])
+                if device_info:
+                    self.__update_snmp_general(pk_dict['id'], device_info, 'snmp on')
+                    self._update_telegraf_toml(device_info[0]['type'])
             return
 
         def _perform_DisableMonitoring(self, options):
             pk_list = options['__pk_list']
             for pk_dict in pk_list:
-                self.__update_snmp_general(pk_dict['id'], get_rest_info_from_device(pk_dict['name']), 'snmp off')
+                device_info = get_rest_info_from_device(pk_dict['name'])
+                if device_info:
+                    self.__update_snmp_general(pk_dict['id'], device_info, 'snmp off')
+                    self._update_telegraf_toml(device_info[0]['type'])
             return
 
         def _perform_SaveConfigurations(self, options):
@@ -1099,6 +1192,10 @@
             else:
                 db.close()
 
+            # Dynamically push telegraf config update for remaining devices of this type
+            if hasattr(instance, 'type') and instance.type:
+                self._update_telegraf_toml(instance.type)
+
             """
 for item in result:
 file_path = DEFAULT_CONFIG_FILE_PATH + "backup/" + item[0]
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/templates/.gitkeep	(added)
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/templates/.gitkeep	(revision 0)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/templates/.gitkeep	(revision 0)
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/tree.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/tree.py	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/tree.py	(working copy)
@@ -1,7 +1,7 @@
 from collections import OrderedDict as D
 
 from hive.imports.node_tree import *
-from views import *
+from .views import *
 
 tree = D([
     ('ajax', {'cls': MenuNode, 'hidden': True, 'sub_nodes': D([
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/views.py
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/views.py	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/cm/views.py	(working copy)
@@ -95,8 +95,8 @@
 
     def init(self, request, session):
         os.system('rm -rf /root/.ssh/known_hosts')
-        width = request.GET.get('w', 160)
-        height = request.GET.get('h', 24)
+        width = int(request.GET.get('w', 160))
+        height = int(request.GET.get('h', 24))
         device_id = request.GET.get('device_id')
 
         def get_device_info(device_id):
@@ -156,8 +156,8 @@
         ip_list = []
         for each in device_ip.all():
             ip_list.append(each.ip.values()[0])
-        width = request.GET.get('w', 160)
-        height = request.GET.get('h', 24)
+        width = int(request.GET.get('w', 160))
+        height = int(request.GET.get('h', 24))
         super_ctx = super(WebConsoleNode, self)._get_context(request, session)
         return dict(
             super_ctx.items() + {'SID': session.sessid, 'width': width, 'height': height, 'ip_list': ip_list}.items())
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.ts	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/devices/devices.ts	(working copy)
@@ -324,6 +324,7 @@
       groupName: ['', Validators.required],
       enablePassword: [''],
       enableLog: [true],
+      enableMonitoring: [true],
       backupConfiguration: [false]
     });
 
@@ -451,6 +452,10 @@
       webui_port: this.deviceForm.value.webuiPortNumber,
       log_enable: this.deviceForm.value.enableLog,
       backup_enable: this.deviceForm.value.backupConfiguration,
+      snmp_general: {
+        snmp_enable: this.deviceForm.value.enableMonitoring,
+        community: "public"
+      }
     }
     if (this.deviceForm.value.ipAddressType === 'v4') {
       payload["ip"] = {
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.html
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.html	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.html	(working copy)
@@ -1,19 +1,42 @@
-<div class="title" [class.oem-webconsole]="isOemVersion">{{ title }}</div>
-<div class="content" [class.hide]="isConnected">
-  <div class="info">{{ info }}</div>
-  @if (isLoading) {
-    <div class="spinner">
-      <div class="rect1"></div>
-      <div class="rect2"></div>
-      <div class="rect3"></div>
-      <div class="rect4"></div>
-      <div class="rect5"></div>
+<div class="web-console-wrapper">
+  <div class="console-header" [class.oem-webconsole]="isOemVersion">
+    <div class="header-content">
+      <div class="window-controls">
+        <span class="control close"></span>
+        <span class="control minimize"></span>
+        <span class="control maximize"></span>
+      </div>
+      <div class="title">{{ title }}</div>
+      <div class="status-badge" [class.connected]="isConnected" [class.disconnected]="!isConnected && !isLoading">
+        {{ connectionStateLabel }}
+      </div>
     </div>
-  }
-  @if (!isLoading) {
-    <button class="btn-array" (click)="reconnect()">Reconnect</button>
-  }
+  </div>
+
+  <div class="console-body">
+    @if (!isConnected) {
+      <div class="overlay-container">
+        <div class="status-card">
+          <div class="status-icon" [class.loading]="isLoading">
+            @if (isLoading) {
+              <div class="spinner-ring"></div>
+            } @else {
+              <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>
+            }
+          </div>
+          <h3 class="status-text">{{ info }}</h3>
+          @if (!isLoading) {
+            <button class="btn-reconnect" (click)="reconnect()">
+              <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><polyline points="23 20 23 14 17 14"></polyline><path d="M20.49 9A9 9 0 0 0 5.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 0 1 3.51 15"></path></svg>
+              Reconnect
+            </button>
+          }
+        </div>
+      </div>
+    }
+
+    <div class="term-container" [class.hide]="!isConnected">
+      <div id="terminal" #terminal></div>
+    </div>
+  </div>
 </div>
-<div class="term-container" [class.hide]="!isConnected">
-  <div id="terminal" #terminal></div>
-</div>
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.scss
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.scss	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.scss	(working copy)
@@ -1,68 +1,230 @@
-$primary-color: #1170cf;
+:host {
+  display: block;
+  height: 100vh;
+  width: 100vw;
+  margin: 0;
+  padding: 0;
+  background-color: #0b111a;
+  overflow: hidden;
+}
 
-.title {
-  height: 36px;
+$terminal-bg: #0b111a;
+$header-bg: #1e2632;
+$text-color: #d6deeb;
+$primary-color: #3b82f6;
+
+.web-console-wrapper {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
   width: 100%;
-  margin-top: 0 !important;
-  padding: 0;
-  color: #fff;
-  font-size: 16px;
-  line-height: 36px;
-  vertical-align: top;
-  background-color: $primary-color;
+  background-color: $terminal-bg;
 }
 
-.hide {
-  display: none;
-  opacity: 0;
+.console-header {
+  height: 44px;
+  background-color: $header-bg;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+  display: flex;
+  align-items: center;
+  padding: 0 16px;
+  user-select: none;
+
+  &.oem-webconsole {
+    background-color: #1a1a24;
+  }
+
+  .header-content {
+    display: flex;
+    align-items: center;
+    width: 100%;
+    
+    .window-controls {
+      display: flex;
+      gap: 8px;
+      margin-right: 16px;
+
+      .control {
+        width: 12px;
+        height: 12px;
+        border-radius: 50%;
+        
+        &.close { background-color: #ff5f56; }
+        &.minimize { background-color: #ffbd2e; }
+        &.maximize { background-color: #27c93f; }
+      }
+    }
+
+    .title {
+      color: $text-color;
+      font-size: 14px;
+      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+      font-weight: 500;
+      flex-grow: 1;
+      text-align: center;
+      margin-right: 24px;
+      letter-spacing: 0.5px;
+    }
+
+    .status-badge {
+      font-size: 12px;
+      padding: 4px 10px;
+      border-radius: 12px;
+      background-color: rgba(255, 255, 255, 0.1);
+      color: #aaa;
+      display: flex;
+      align-items: center;
+      gap: 6px;
+      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+      font-weight: 500;
+
+      &::before {
+        content: '';
+        display: block;
+        width: 8px;
+        height: 8px;
+        border-radius: 50%;
+        background-color: #aaa;
+      }
+
+      &.connected {
+        color: #4ade80;
+        background-color: rgba(74, 222, 128, 0.1);
+        &::before { background-color: #4ade80; box-shadow: 0 0 8px rgba(74, 222, 128, 0.4); }
+      }
+
+      &.disconnected {
+        color: #f87171;
+        background-color: rgba(248, 113, 113, 0.1);
+        &::before { background-color: #f87171; box-shadow: 0 0 8px rgba(248, 113, 113, 0.4); }
+      }
+    }
+  }
 }
 
-.content {
-  margin: 0 auto;
-  padding-top: 100px;
+.console-body {
+  position: relative;
+  flex-grow: 1;
+  overflow: hidden;
+  background-color: $terminal-bg;
 }
 
-.content .info {
-  color: #333;
-  font-size: 14px;
-  text-align: center;
-  margin-bottom: 30px;
+.overlay-container {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background-color: rgba($terminal-bg, 0.85);
+  z-index: 10;
+  backdrop-filter: blur(8px);
 }
 
-.content .btn-array {
-  display: block;
-  margin: 0 auto;
-  padding: 8px 20px;
-  font-size: 14px;
-  color: #e4701d;
-  background-color: #fff;
-  border: 1px solid #e4701d;
-  border-radius: 5px;
+.status-card {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 40px 56px;
+  background-color: #1e2632;
+  border-radius: 16px;
+  border: 1px solid rgba(255, 255, 255, 0.08);
+  box-shadow: 0 12px 48px rgba(0, 0, 0, 0.5);
+  animation: scaleUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);
+
+  .status-icon {
+    margin-bottom: 20px;
+    color: #f87171;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    
+    &.loading {
+      color: $primary-color;
+    }
+  }
+
+  .status-text {
+    margin: 0 0 24px 0;
+    color: $text-color;
+    font-size: 16px;
+    font-weight: 500;
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+    text-align: center;
+    letter-spacing: 0.2px;
+  }
+
+  .btn-reconnect {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    padding: 10px 24px;
+    background-color: $primary-color;
+    color: white;
+    border: none;
+    border-radius: 8px;
+    font-size: 14px;
+    font-weight: 500;
+    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+    cursor: pointer;
+    transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
+    box-shadow: 0 2px 8px rgba($primary-color, 0.3);
+
+    &:hover {
+      background-color: #4f8cf6;
+      transform: translateY(-2px);
+      box-shadow: 0 6px 16px rgba($primary-color, 0.4);
+    }
+
+    &:active {
+      transform: translateY(0);
+      box-shadow: 0 2px 4px rgba($primary-color, 0.3);
+    }
+  }
 }
 
-.content .btn-array:active:focus, .content .btn-array:focus {
-  outline: 0;
+.spinner-ring {
+  width: 48px;
+  height: 48px;
+  border: 3px solid rgba($primary-color, 0.15);
+  border-top-color: $primary-color;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
 }
 
+.term-container {
+  height: 100%;
+  width: 100%;
+  padding: 16px 0 16px 16px;
+  box-sizing: border-box;
+  opacity: 1;
+  transition: opacity 0.3s ease;
+  
+  &.hide {
+    opacity: 0;
+    pointer-events: none;
+  }
 
-.spinner {
-  margin: 50px auto;
-  width: 50px;
-  height: 30px;
-  text-align: center;
-  font-size: 10px;
+  #terminal {
+    height: 100%;
+    width: 100%;
+  }
 }
 
-.spinner > div {
-  background-color: #e4701e;
-  height: 100%;
-  width: 6px;
-  display: inline-block;
-  -webkit-animation: stretchdelay 1.2s infinite ease-in-out;
-  animation: stretchdelay 1.2s infinite ease-in-out;
+@keyframes spin {
+  to { transform: rotate(360deg); }
 }
 
-.spinner .rect2 { -webkit-animation-delay: -1.1s; animation-delay: -1.1s; }
-.spinner .rect3 { -webkit-animation-delay: -1.0s; animation-delay: -1.0s; }
-.spinner .rect4 { -webkit-animation-delay: -0.9s; animation-delay: -0.9s; }
-.spinner .rect5 { -webkit-animation-delay: -0.8s; animation-delay: -0.8s; }
+@keyframes scaleUp {
+  from { opacity: 0; transform: scale(0.95); }
+  to { opacity: 1; transform: scale(1); }
+}
+
+::ng-deep .xterm-viewport {
+  border-radius: 4px;
+}
+::ng-deep .xterm .xterm-screen {
+  padding-right: 16px;
+}
Index: /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.ts
===================================================================
--- /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.ts	(revision 2954)
+++ /branches/amp_4_0/src/webui/webui/htdocs/new/src/gui/src/app/components/sub-components/web-console/web-console.ts	(working copy)
@@ -1,6 +1,7 @@
 import { AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild, inject, PLATFORM_ID } from '@angular/core';
 import { HttpClient } from '@angular/common/http';
 import { isPlatformBrowser } from '@angular/common';
+import { Subject, takeUntil } from 'rxjs';
 import { URLS } from '../../../constants/api_urls';
 import { CoreUiFacade } from '../../../services/facades/core-ui.facade';
 
@@ -15,7 +16,9 @@
   @ViewChild('terminal', { static: false }) terminalElementRef!: ElementRef;
   private terminal: import('@xterm/xterm').Terminal | null = null;
   private fitAddon: import('@xterm/addon-fit').FitAddon | null = null;
+  private keyListener: { dispose: () => void } | null = null;
   private resizeObserver: ResizeObserver | null = null;
+  private destroyed$ = new Subject<void>();
   title = 'Web Console';
   info = 'Connecting...';
   isConnected = false;
@@ -38,85 +41,35 @@
 
   ngOnInit(): void {
     const route = this.coreUiFacade.activeRoute;
-    route.queryParams.subscribe((params: any) => {
-      this.deviceId = params['device_id'] || 'localhost';
-      this.deviceName = params['device_name'] || '';
-      this.setTitle();
-      this.checkOemVersion();
-    });
+    route.queryParams
+      .pipe(takeUntil(this.destroyed$))
+      .subscribe((params: any) => {
+        this.deviceId = params['device_id'] || 'localhost';
+        this.deviceName = params['device_name'] || '';
+        this.setTitle();
+        this.checkOemVersion();
+      });
   }
 
   async ngAfterViewInit(): Promise<void> {
-    if (isPlatformBrowser(this.platformId)) {
-      const { Terminal } = await import('@xterm/xterm');
-      const { FitAddon } = await import('@xterm/addon-fit');
-
-      if (this.terminalElementRef?.nativeElement) {
-        this.terminal = new Terminal({
-          convertEol: true,
-          cursorBlink: true,
-          fontFamily: 'monospace',
-          fontSize: 14,
-          cols: 80,
-          rows: 60,
-          allowProposedApi: true,
-          theme: {
-            background: '#000000',
-            foreground: '#FFFFFF',
-            cursor: '#FFFFFF',
-            selectionBackground: '#5f6062',
-            black: '#000000',
-            red: '#e06c75',
-            green: '#98c379',
-            yellow: '#e5c07b',
-            blue: '#61afef',
-            magenta: '#c678dd',
-            cyan: '#56b6c2',
-            white: '#abb2bf',
-            brightBlack: '#5c6370',
-            brightRed: '#e06c75',
-            brightGreen: '#98c379',
-            brightYellow: '#e5c07b',
-            brightBlue: '#61afef',
-            brightMagenta: '#c678dd',
-            brightCyan: '#56b6c2',
-            brightWhite: '#ffffff'
-          }
-        });
-
-        this.fitAddon = new FitAddon();
-        this.terminal.loadAddon(this.fitAddon);
-
-        this.terminal.open(this.terminalElementRef.nativeElement);
-
-        this.fitAddon.fit();
-
-        // Initialize ResizeObserver
-        this.resizeObserver = new ResizeObserver(() => {
-          if (this.terminal && this.fitAddon) {
-            this.fitAddon.fit();
-          }
-        });
-        this.resizeObserver.observe(this.terminalElementRef.nativeElement);
-
-        this.terminal.write('\x1b[32m[WebConsole Initialized]\x1b[0m\r\n'); // Welcome message for debugging
-
-        setTimeout(() => this.connect(), 500);
-      } else {
-        console.error('Terminal element not found');
-        this.info = 'Terminal initialization failed.';
-        this.isLoading = false;
-      }
-    } else {
+    if (!isPlatformBrowser(this.platformId)) {
       this.info = 'Terminal is not available in this environment.';
       this.isLoading = false;
+      return;
     }
+
+    const initialized = await this.initializeTerminal();
+    if (initialized) {
+      setTimeout(() => this.connect(), 250);
+    }
   }
 
   ngOnDestroy(): void {
-    if (this.qTimer) {
-      clearTimeout(this.qTimer);
-    }
+    this.destroyed$.next();
+    this.destroyed$.complete();
+    this.clearPollingTimer();
+    this.keyListener?.dispose();
+    this.keyListener = null;
     if (this.resizeObserver) {
       this.resizeObserver.disconnect();
       this.resizeObserver = null;
@@ -128,74 +81,132 @@
     }
   }
 
-  // Adjusted to better handle the HTML structure and preserve newlines/spaces
+  get connectionStateLabel(): string {
+    if (this.isConnected) return 'Connected';
+    if (this.isLoading) return 'Connecting';
+    return 'Disconnected';
+  }
+
+  get terminalReady(): boolean {
+    return this.terminal !== null;
+  }
+
+  private async initializeTerminal(): Promise<boolean> {
+    const terminalHost = this.terminalElementRef?.nativeElement;
+    if (!terminalHost) {
+      this.info = 'Terminal initialization failed.';
+      this.isLoading = false;
+      return false;
+    }
+
+    const { Terminal } = await import('@xterm/xterm');
+    const { FitAddon } = await import('@xterm/addon-fit');
+
+    this.terminal = new Terminal({
+      convertEol: true,
+      cursorBlink: true,
+      fontFamily: 'Monaco, Menlo, Consolas, monospace',
+      fontSize: 13,
+      cols: 80,
+      rows: 60,
+      allowProposedApi: true,
+      theme: {
+        background: '#0b111a',
+        foreground: '#d6deeb',
+        cursor: '#f3f6fb',
+        selectionBackground: '#33435a',
+        black: '#000000',
+        red: '#e06c75',
+        green: '#98c379',
+        yellow: '#e5c07b',
+        blue: '#61afef',
+        magenta: '#c678dd',
+        cyan: '#56b6c2',
+        white: '#abb2bf',
+        brightBlack: '#5c6370',
+        brightRed: '#e06c75',
+        brightGreen: '#98c379',
+        brightYellow: '#e5c07b',
+        brightBlue: '#61afef',
+        brightMagenta: '#c678dd',
+        brightCyan: '#56b6c2',
+        brightWhite: '#ffffff'
+      }
+    });
+
+    this.fitAddon = new FitAddon();
+    this.terminal.loadAddon(this.fitAddon);
+    this.terminal.open(terminalHost);
+    this.fitAddon.fit();
+
+    this.resizeObserver = new ResizeObserver(() => this.fitTerminal());
+    this.resizeObserver.observe(terminalHost);
+    this.terminal.focus();
+    this.terminal.writeln('\x1b[32mReady. Connecting to remote shell...\x1b[0m');
+
+    return true;
+  }
+
   private convertHtmlToAnsi(xmlContentString: string): string {
-    // The server response starts with <?xml...?> then <c cy="012"/> then <span>...</span>
-    // The DOMParser will fail if given raw text without a single root, or if XML declaration is present.
-    // We need to extract the relevant content that we want to parse as HTML/XML fragments.
-    // From your example, the text and spans appear after the <c/> tag.
-    // Let's assume the main content is within <span> tags or as direct text nodes after <c/>
-    // For reliable parsing, we'll strip the XML declaration and wrap everything in a custom root.
     const cleanResponse = xmlContentString.replace(/<\?xml.*?\?>/, '').trim();
-    const wrappedXml = `<root>${cleanResponse}</root>`;
+    const contentOnly = cleanResponse.replace(/<c cy="[^"]*"\s*\/>/, '').trim();
+    const wrappedXml = `<root>${contentOnly}</root>`;
     const parser = new DOMParser();
     const xmlDoc = parser.parseFromString(wrappedXml, 'text/xml');
 
     let ansiOutput = '';
 
-    // Define ANSI mapping - YOU NEED TO COMPLETE THESE BASED ON YOUR BACKEND'S 'ff be', 'ff bc' MEANINGS
-    const ansiColorMap: { [key: string]: { start: string, end: string } } = {
-      'ff be': { start: '\x1b[38;5;15;48;5;236m', end: '\x1b[0m' }, // Example: light gray on dark gray for normal text
-      'ff bc': { start: '\x1b[38;5;15;48;5;236m', end: '\x1b[0m' }, // Assuming these are similar default styles
-      // Add more mappings as needed:
-      // 'error-class': { start: '\x1b[31m', end: '\x1b[0m' }, // Red text
-      // 'bold-text': { start: '\x1b[1m', end: '\x1b[0m' }, // Bold text
-      // Note: The example response has "ff be" and "ff bc" spanning entire lines,
-      // which implies they might be the primary styling for standard text.
-      // You might need to experiment to get the exact color mappings.
+    const mapColorCode = (hexCode: string, isBg: boolean): string => {
+      const v = parseInt(hexCode, 16);
+      if (isNaN(v)) return '';
+      // Default terminal behavior overrides
+      if (!isBg && v === 15) return '39'; // Default FG
+      if (isBg && v === 14) return '49'; // Default BG
+
+      if (v < 8) return (isBg ? (v + 40) : (v + 30)).toString();
+      return (isBg ? (v - 8 + 100) : (v - 8 + 90)).toString();
     };
 
-
-    // The server response appears to send whole lines (or segments of lines) in spans
-    // It's crucial to preserve whitespace and newlines as the server sends them.
-    // DOMParser might normalize whitespace, so let's try to grab innerHTML/textContent directly.
-
-    // Iterate over the children of the <root> element (which are <c/> and <span> elements or text)
     Array.from(xmlDoc.documentElement.childNodes).forEach(node => {
       if (node.nodeType === Node.ELEMENT_NODE) {
         const element = node as HTMLElement;
         if (element.tagName.toLowerCase() === 'span') {
           const cls = element.getAttribute('class') || '';
-          const mapping = ansiColorMap[cls] || { start: '', end: '' }; // Fallback to no styling
-          ansiOutput += mapping.start;
-          ansiOutput += element.textContent || ''; // Get plain text content of the span
-          ansiOutput += mapping.end;
+          const match = cls.match(/f([0-9a-f]) b([0-9a-f])( ul)?/);
+          let formatCodes = [];
+
+          if (match) {
+            formatCodes.push(mapColorCode(match[1], false));
+            formatCodes.push(mapColorCode(match[2], true));
+            if (match[3]) formatCodes.push('4');
+          }
+
+          if (formatCodes.length > 0) {
+            ansiOutput += `\x1b[${formatCodes.join(';')}m`;
+          } else {
+            ansiOutput += '\x1b[0m';
+          }
+
+          ansiOutput += element.textContent || '';
+          ansiOutput += '\x1b[0m';
         }
-        // You might have other custom tags like <br/> that need to be translated to \r\n
-        // or <tab/> to \t, etc.
       } else if (node.nodeType === Node.TEXT_NODE && node.textContent !== null) {
-        // Capture any raw text nodes not wrapped in spans
         ansiOutput += node.textContent;
       }
     });
 
-
-    // Replace HTML entities after content extraction
     ansiOutput = ansiOutput.replace(/&nbsp;/g, ' ');
     ansiOutput = ansiOutput.replace(/&amp;/g, '&');
     ansiOutput = ansiOutput.replace(/&lt;/g, '<');
     ansiOutput = ansiOutput.replace(/&gt;/g, '>');
+    // Force carriage return for terminal correctness
+    ansiOutput = ansiOutput.replace(/\n/g, '\r\n');
+    // Strip trailing newlines to prevent terminal scrolling that breaks coordinate painting
+    ansiOutput = ansiOutput.replace(/\r\n$/, '');
 
-    // Xterm.js `convertEol: true` handles '\n'.
-    // The screenshot shows newlines as actual newlines in the XML, so they should be preserved.
-    // However, sometimes browsers/DOMParser might normalize multiple spaces or trim leading/trailing spaces from text nodes.
-    // If the output still looks squished, you might need to enforce a specific column width
-    // and padding, or ensure the backend sends precise whitespace (e.g., using a fixed-width font implies fixed character cells).
-
     return ansiOutput;
   }
 
-
   private setTitle(): void {
     this.title = this.deviceId === 'localhost' ? 'AMP Web Console' : `Web Console (${this.deviceName})`;
   }
@@ -211,48 +222,90 @@
     }
   }
 
+  private initRetryCount = 0;
+
   private connect(): void {
     if (!isPlatformBrowser(this.platformId)) {
       return;
     }
-    this.httpClient.get(`${URLS.WEBCONSOLE_INIT_URL}?device_id=${this.deviceId}`, { responseType: 'json' })
+
+    // If deviceId is missing due to async query constraints, retry shortly.
+    if (!this.deviceId && this.initRetryCount < 10) {
+      this.initRetryCount++;
+      setTimeout(() => this.connect(), 200);
+      return;
+    }
+
+    this.clearPollingTimer();
+    this.isLoading = true;
+    this.info = 'Connecting...';
+
+    const cols = this.terminal?.cols || 80;
+    const rows = this.terminal?.rows || 25;
+
+    this.httpClient.get(`${URLS.WEBCONSOLE_INIT_URL}?device_id=${this.deviceId}&w=${cols}&h=${rows}`, { responseType: 'json' })
       .subscribe({
         next: (data: any) => {
           if (!data[0]) {
-            this.disconnect();
-            this.info = data[1];
+            if (this.initRetryCount < 5) {
+              this.initRetryCount++;
+              setTimeout(() => this.connect(), 1000);
+            } else {
+              this.disconnect();
+              this.info = data[1] || 'Failed to connect console.';
+              this.initRetryCount = 0;
+            }
           } else {
+            this.initRetryCount = 0;
             this.pid = data[1];
             this.sid = data[2];
             this.isConnected = true;
             this.isLoading = false;
+            this.info = 'Connected';
             if (this.terminal) {
               this.registerKeyEvents();
+              this.terminal.focus();
             }
-            this.update(); // Start polling for updates
+            this.update();
           }
         },
         error: () => {
-          this.disconnect();
-          this.info = 'Failed to connect console.';
+          if (this.initRetryCount < 5) {
+            this.initRetryCount++;
+            setTimeout(() => this.connect(), 1000);
+          } else {
+            this.disconnect();
+            this.info = 'Failed to connect console.';
+            this.initRetryCount = 0;
+          }
         }
       });
   }
 
   private update(): void {
-    if (!isPlatformBrowser(this.platformId)) return;
+    this.qTimer = null;
+    if (!isPlatformBrowser(this.platformId) || !this.isConnected) return;
 
-    // Send the keyboard queue in the order it was received
     const send = this.kbQueue.join('');
-    this.kbQueue = []; // Clear the queue after preparing to send
+    this.kbQueue = [];
 
+    const cols = this.terminal?.cols || 80;
+    const rows = this.terminal?.rows || 25;
+
     this.httpClient.get(
-      `${URLS.WEBCONSOLE_HANDLE_REQ_URL}?s=${this.sid}&pid=${this.pid}&w=160&h=24&k=${encodeURIComponent(send)}`,
-      { responseType: 'text' } // Receive as text to parse XML
+      `${URLS.WEBCONSOLE_HANDLE_REQ_URL}?s=${this.sid}&pid=${this.pid}&w=${cols}&h=${rows}&k=${encodeURIComponent(send)}`,
+      { responseType: 'text' }
     ).subscribe({
       next: (response: string) => {
-        // Remove XML declaration and wrap in a root element for reliable parsing
+        if (response.startsWith('Error:')) {
+          this.disconnect();
+          this.info = response;
+          return;
+        }
+
         const cleanResponse = response.replace(/<\?xml.*?\?>/, '').trim();
+        const cNodeMatch = cleanResponse.match(/<c cy="(\d+)"(?: cx="(\d+)")?/);
+
         const wrappedXml = `<root>${cleanResponse}</root>`;
         const doc = new DOMParser().parseFromString(wrappedXml, 'text/xml');
 
@@ -263,40 +316,38 @@
           return;
         }
 
-        const cNode = doc.querySelector('c');
-        const cy = cNode?.getAttribute('cy');
-        let cursorRow = 1; // Default to first row
-        if (cy) {
-          cursorRow = Math.max(parseInt(cy, 10), 1); // Ensure at least 1
+        let cursorRow = 1;
+        let cursorCol = 1;
+        if (cNodeMatch) {
+          cursorRow = Math.max(parseInt(cNodeMatch[1], 10) + 1, 1);
+          if (cNodeMatch[2]) {
+            cursorCol = Math.max(parseInt(cNodeMatch[2], 10) + 1, 1);
+          }
         }
 
-        const ansiOutput = this.convertHtmlToAnsi(cleanResponse); // Pass the raw clean string to the converter
+        const ansiOutput = this.convertHtmlToAnsi(cleanResponse);
 
-        if (this.terminal) {
-          // Clear the line and move cursor to the specified row and column 1
-          // \x1b[2K clears the current line
-          // \x1b[<row>;1H moves the cursor to row, col 1
-          this.terminal.write(`\x1b[${cursorRow};1H\x1b[2K`);
-          this.terminal.write(ansiOutput); // Write the converted content
+        if (this.terminal && ansiOutput.trim().length > 0) {
+          const lines = ansiOutput.split('\r\n');
+          for (let i = 0; i < lines.length; i++) {
+            // Paint each horizontal slice absolutely and prevent emitting '\n' that commands scrolling
+            this.terminal.write(`\x1b[${i + 1};1H${lines[i]}`);
+          }
+          this.terminal.write(`\x1b[${cursorRow};${cursorCol}H`); // Put hardware cursor on active coordinate
         }
 
-
-        // Manage polling frequency
-        if (ansiOutput.trim().length > 0) { // If there was any meaningful output
-          this.qTime = 100; // Reset to fast poll if activity
+        if (ansiOutput.trim().length > 0) {
+          this.qTime = 100;
         } else {
-          this.qTime = Math.min(this.qTime * 2, 2000); // Slow down if no output
+          this.qTime = Math.min(this.qTime * 2, 2000);
         }
-        this.retryCount = 0; // Reset retry count on successful response
-
-        // Schedule next update
-        this.qTimer = setTimeout(() => this.update(), this.qTime);
+        this.retryCount = 0;
+        this.scheduleNextUpdate(this.qTime);
       },
       error: (err: any) => {
-        console.error('Update request error:', err);
         this.retryCount++;
         if (this.retryCount < 3) {
-          this.qTimer = setTimeout(() => this.update(), 1000);
+          this.scheduleNextUpdate(1000);
         } else {
           this.disconnect();
           this.info = 'Network error: ' + (err.message || 'Unknown');
@@ -307,69 +358,101 @@
 
   private registerKeyEvents(): void {
     if (this.terminal) {
-      this.terminal.onKey(({ key, domEvent }: { key: string; domEvent: KeyboardEvent }) => {
+      this.keyListener?.dispose();
+      this.keyListener = this.terminal.onKey(({ key, domEvent }: { key: string; domEvent: KeyboardEvent }) => {
         const code = domEvent.keyCode || domEvent.which;
         let k = key;
-        // Handle Ctrl + A-Z
         if (domEvent.ctrlKey && code >= 65 && code <= 90) {
           k = String.fromCharCode(code - 64);
-        } else if (code === 8) { // Backspace
-          k = String.fromCharCode(127); // ASCII DEL character for backspace
+        } else if (code === 8) {
+          k = String.fromCharCode(127);
         } else if ([9, 27, 33, 34, 35, 36, 37, 38, 39, 40, 45, 46].includes(code)) {
           k = this.mapSpecialKey(code);
         }
 
-        // Add the key to the queue
-        this.kbQueue.push(encodeURIComponent(k));
-        this.qTime = 100; // Reset polling speed when there's input
-        if (!this.qTimer) { // If timer isn't running, start an immediate update
+        this.kbQueue.push(k);
+        this.qTime = 100;
+        if (!this.qTimer) {
           this.update();
         }
-        domEvent.preventDefault(); // Prevent default browser behavior for terminal keys
+        domEvent.preventDefault();
       });
     }
   }
 
   private mapSpecialKey(code: number): string {
     const keyMap: { [key: number]: string } = {
-      9: '\t',      // Tab
-      27: '\x1b',    // Esc
-      33: '\x1b[5~', // Page Up (CSI 5 ~)
-      34: '\x1b[6~', // Page Down (CSI 6 ~)
-      35: '\x1b[F',  // End (CSI F) - often equivalent to \x1b[4~
-      36: '\x1b[H',  // Home (CSI H) - often equivalent to \x1b[1~
-      37: '\x1b[D',  // Left Arrow (CSI D)
-      38: '\x1b[A',  // Up Arrow (CSI A)
-      39: '\x1b[C',  // Right Arrow (CSI C)
-      40: '\x1b[B',  // Down Arrow (CSI B)
-      45: '\x1b[2~', // Insert (CSI 2 ~)
-      46: '\x1b[3~'  // Delete (CSI 3 ~)
+      9: '\t',
+      27: '\x1b',
+      33: '\x1b[5~',
+      34: '\x1b[6~',
+      35: '\x1b[F',
+      36: '\x1b[H',
+      37: '\x1b[D',
+      38: '\x1b[A',
+      39: '\x1b[C',
+      40: '\x1b[B',
+      45: '\x1b[2~',
+      46: '\x1b[3~'
     };
     return keyMap[code] || String.fromCharCode(code);
   }
 
-  reconnect(): void {
+  async reconnect(): Promise<void> {
     this.isLoading = true;
-    this.info = 'Connecting...';
-    if (this.terminal && isPlatformBrowser(this.platformId)) {
-      this.terminal.dispose();
-      this.terminal = null;
-      this.fitAddon = null;
-      if (this.resizeObserver) {
-        this.resizeObserver.disconnect();
-        this.resizeObserver = null;
+    this.info = 'Reconnecting...';
+    this.disconnect(false);
+
+    if (!this.terminal && isPlatformBrowser(this.platformId)) {
+      const initialized = await this.initializeTerminal();
+      if (!initialized) {
+        return;
       }
     }
+    this.terminal?.clear();
     this.connect();
   }
 
-  private disconnect(): void {
-    this.isConnected = false;
-    this.isLoading = false;
-    this.info = 'Console disconnected.';
+  clearTerminal(): void {
+    this.terminal?.clear();
+    this.terminal?.focus();
+  }
+
+  fitTerminal(): void {
+    if (this.terminal && this.fitAddon) {
+      this.fitAddon.fit();
+    }
+  }
+
+  focusTerminal(): void {
+    this.terminal?.focus();
+  }
+
+  private scheduleNextUpdate(delay: number): void {
+    this.clearPollingTimer();
+    this.qTimer = setTimeout(() => {
+      this.qTimer = null;
+      this.update();
+    }, delay);
+  }
+
+  private clearPollingTimer(): void {
     if (this.qTimer) {
       clearTimeout(this.qTimer);
       this.qTimer = null;
     }
   }
+
+  private disconnect(overwriteInfo = true): void {
+    this.clearPollingTimer();
+    this.retryCount = 0;
+    this.qTime = 100;
+    this.kbQueue = [];
+    this.isConnected = false;
+    this.isLoading = false;
+
+    if (overwriteInfo) {
+      this.info = 'Console disconnected.';
+    }
+  }
 }
