Skip to main content

How Ceres Talks to Lydia

Ceres runs inside an iframe in Lydia. They communicate through postMessage. This page explains every message they send to each other, and which files handle what.

The big picture

Lydia (React app, runs in the main browser window)
|
| Creates an iframe pointing to Ceres
|
v
Ceres (runs inside the iframe)
|
| Renders the document template
| Sends height updates back to Lydia
|
v
Lydia resizes the iframe to fit

How Lydia loads Ceres

When a user has a custom template, Lydia builds an iframe URL and drops it on the page. Here is how it works:

The URL builder

In Lydia, the file src/components/utils/iframeUtils.js has a function called buildIframeSrc. It does three things:

  1. Takes the document ID and share token
  2. Builds the API URL and base64-encodes it
  3. Combines everything into a Ceres URL
// Simplified version of what buildIframeSrc does:
const API_URL = `${apiDomain}/invoices/${invoiceId}?_at=${shareId}&populateBusiness=true`;
const iframeSrc = `${ceresDomain}?template=${templateName}&apiUrl=${btoa(API_URL)}&isLydiaMode=1`;

The isLydiaMode=1 parameter tells Ceres to activate the Lydia Bridge, which handles height reporting and print commands.

The iframe component

Lydia renders the iframe using IframeRenderer.jsx:

<StyledIframe
ref={iframeRef}
id="invoiceIframe"
src={iframeSrc}
width="100%"
title="Document Preview"
/>

The height hook

Lydia listens for height messages using the useIframeHeight hook:

// Listens for messages from Ceres
window.addEventListener('message', handleMessage);

// When Ceres says "my content is 1200px tall":
// Lydia sets the iframe height to 1200px + a small buffer

Messages from Lydia to Ceres

Lydia sends three types of messages to Ceres:

1. Print command

When the user clicks "Print" or presses Ctrl+P:

iframeContentWindow.postMessage({
action: 'lydia:print',
reason: 'lydia-print-button'
}, '*');

Ceres receives this, prepares the document for printing (adjusts sizing, forces overflow visible), waits two animation frames, then calls window.print().

2. Height request

When Lydia wants to know the current height:

iframeContentWindow.postMessage({
action: 'lydia:height-request',
reason: 'template-update'
}, '*');

Ceres measures the content and sends back a ceres:content-height message.

3. Template style update

When the user changes template styles (colors, fonts, logo) in the Lydia UI:

iframeContentWindow.postMessage({
source: 'lydia',
type: 'lydia:template-update',
reason: 'color-change',
template: {
primaryColor: { r: 46, g: 133, b: 85 },
fontFamily: 'Inter',
// ... more style options
}
}, '*');

Ceres receives this, applies the styles as CSS custom properties, re-renders, and reports the new height.

Messages from Ceres to Lydia

Ceres sends one type of message:

Content height

After rendering (or when the content size changes):

parent.postMessage({
source: 'ceres',
type: 'ceres:content-height',
height: 1247,
reason: 'render-complete'
}, '*');

This message is debounced (120ms) and only sent when the height actually changes by more than 1px. This prevents flooding Lydia with identical messages.

Key files reference

In Lydia

FileWhat it does
src/components/utils/iframeUtils.jsBuilds iframe URLs, sends print commands, posts template updates
src/components/hooks/useIframeHeight.jsListens for height messages, manages iframe sizing
src/components/widgets/IframeRenderer.jsxThe actual iframe React component

In Ceres

FileWhat it does
src/main/lydiaBridge.tsAll Lydia communication: height reporting, print handling, template updates
src/main/commonUtils.tsStyle application (applyPreviewStyles), asset loading
src/main/dibellaBridge.tsDibella communication (currently a placeholder)

When does Lydia use Ceres?

Lydia checks if the user has a custom layout template. If yes, it renders via Ceres. If no, it uses its own React system templates.

The check happens in iframeUtils.js:

export const hasCustomLayout = (customTemplateApplied) => {
return customTemplateApplied?.isLayoutTemplate || false;
};

If hasCustomLayout returns true, the IframeRenderer component creates an iframe pointing to Ceres. If it returns false, Lydia renders the document using its own React components (like template/quotation/default.js).