Skip to content

Architecture

The widget is a React app mounted in a shadow DOM, talking to the backend (morzebot-backend-v2, the Router) over WebSocket. Below are the key architectural ideas that matter when integrating or extending it.

Custom element & shadow DOM

  • The widget registers an <mz-bot> custom element (in src/index.js).
  • The primary widget mounts on <mz-bot id="__mz-bot">; the overlay on <mz-bot id="__mz-bot-overlay">.
  • The shadow root is open (mode: 'open'), which lets the outside read the host's CSS variables.
  • All styles are inlined into the JS (via vite-plugin-css-injected-by-js) and injected into the shadow root, and host styles don't break the widget.

Surfaces

The widget can mount several presentations of the same conversation at once. Each surface is its own <mz-bot> host + shadow root + React root, but there's one store and oneWebsocketConnection, so all surfaces stay in sync over a single socket.

SurfaceWhat it isHost
primaryThe main mode (static / inline / classic)#__mz-bot
overlayAn optional floating launcher (chat.overlay.enabled)#__mz-bot-overlay on <body>

The surface descriptor (id, host, mode, position) is passed via SurfaceContext (src/contexts/SurfaceContext.js). Components read their host/mode from useSurface(), not from getElementById('__mz-bot') and not from globals like config.STATIC/INLINE/CHAT_TYPE.

Per-surface state isolation

Per-surface keys in StaticStore (ChatOpen:<id>, ChatWasOpened:<id>, seen-count) keep surfaces from clobbering each other's state. Overlay visibility lives in a separate external store, src/services/overlayStore.js.

Singletons

A handful of module singletons guarantee one conversation across all surfaces:

SingletonFileRole
WebsocketConnectionsrc/services/WebsocketConnection.jsThe single WS connection; carries ws_token in the query for session recovery; reconnects on tab focus / online.
WebsocketEventssrc/services/WebsocketEvents.jsIncoming-event handler registry; wired up once in bootstrap.js.
Redux storesrc/store.jsAll messages and UI state. Reducers: bot, menu, options, notifications, wsEvents.
StaticStoresrc/services/StaticStore.jsIn-memory cache: ws-token, operator header state, etc.
overlayStoresrc/services/overlayStore.jsPub-sub for overlay launcher visibility and open/close (via useSyncExternalStore).

Bootstrap

One-time socket setup lives in src/bootstrap.js (guarded against re-running):

  1. Create the WebSocket and handlers (WebsocketConnection.getInstance(), WebsocketEvents.getInstance()).
  2. Restore the operator header from sessionStorage if present.
  3. Send a "start" action to the backend (~1s later).
  4. Attach visibility/online listeners for auto-reconnect.

Message flow

widget ──msg──► Router (morzebot-backend-v2, :9000)
   ▲                       │  routes to the script engine / Messenger
   │ event:* over WS       ▼
   └───────────────────────┘  Router pushes events (typing, caption_after, …) back over WS

This widget's backend is the Router (morzebot-backend-v2), not the support-panel.

src/ structure

src/
  index.js          # entrypoint: config resolution, surface mounting, installs the MZBOT API
  bootstrap.js      # one-time socket + handler setup
  config.js         # the resolved config (generated at startup; gitignored)
  theme.js          # theme state machine + the global MZBOT API
  store.js          # Redux store configuration
  App.js            # root component (mounts per surface via SurfaceContext)
  components/        # Main, Header, Logs, Controls, ClosedButton, Messengers, Options, Menu,
                     #   Notification, ResizeWrapper, ReturnToBot, LazyImage, ErrorBoundary
  contexts/          # SurfaceContext — the surface descriptor (id, host, mode, position)
  services/          # WebsocketConnection, WebsocketEvents, StaticStore, overlayStore, Notification
  reducers/          # bot, menu, options, notifications, wsEvents/
  actions/           # bot, menu, options, notification, types
  helpers/           # commonHelper (sendAction, botElement, userUuid), soundHelper, scrollLock,
                     #   bitrixHelper, fileHelper, messageHelper, cookieHelper, …
  hooks/             # usePrevious, animateOpacity, openToggleRoot
  assets/            # styles (theme-runtime.scss), svg, fonts, img

See also Build — how this becomes a single bundle.