LivePage is a visual page builder. You compose a page from design components in an in-browser editor, connect components to data sources, and export the result as JSON, shortcode, or a standalone HTML file.
Status: early development (
0.1.0). Formats and APIs can change without notice.
- Visual editor. Add, replace, and configure design components such as headers, rows, columns, images, stats, callouts, charts, metric cards, and data tables. Includes undo/redo history and a command palette.
- Data sources. Bind components to REST APIs, GraphQL, JSON feeds, RSS feeds, CSV, generated data, or an authorized LinkedIn OIDC profile through placeholders.
- Templates. Start from bundled templates: SaaS landing page, agency homepage, personal portfolio, CV/resume, blog article, event page, link-in-bio, contact/about, and patient health dashboard.
- Page assistant (optional). Describe the page you want in plain language; a chat picks a template, drafts its text, and tunes its design settings. Off unless enabled with
NEXT_PUBLIC_PROMPT_ASSIST_ENABLED=1. Once enabled, toggle it on in the Settings popover (toolbar) to show the chat bubble (bottom-right) without needing a URL parameter. Includes a command palette action ("Open AI Assistant"), first-visit tooltip, and in-app help; see Optional: page assistant. - Import and export. Save and load pages as JSON or shortcode, or export a standalone HTML page.
You need Node.js 18.18 or newer and npm.
npm install
npm run devOpen http://localhost:3000 for the landing page, or go straight to the builder at http://localhost:3000/try.
The feature is off by default. Set NEXT_PUBLIC_PROMPT_ASSIST_ENABLED=1 and rebuild (the value is inlined at build time) to turn it on. Once enabled, the chat bubble (bottom-right of /try) can be shown in two ways:
- Per-visit URL parameter: Open the builder with
?prompt-assist=1(e.g.,/try?prompt-assist=1). - Persistent setting: Toggle "Enable AI Assistant" in the Settings popover (toolbar gear icon). This persists across visits in localStorage and does not require the URL parameter.
With the deployment flag off, the /api/prompt-assist routes answer 404. It uses TypeSafe's Jev and/or OpenAI. Copy .env.example to .env.local and set TYPESAFE_API_KEY and/or OPENAI_API_KEY; with neither, the chat lets you pick a template by hand and everything else works as before. Keys stay on the server, and the request text is sent to whichever provider you configure. How it works is described in .agents/docs/CONTEXT.md.
Discoverability features:
- Toolbar toggle: Enable/disable the AI Assistant from the Settings popover.
- Command palette: Press
⌘Kand search "Open AI Assistant" to open the chat. - First-visit tooltip: A helpful tooltip appears on first visit explaining the feature.
- In-app help: The Settings popover includes a description of the AI Assistant.
The LinkedIn Profile source uses LinkedIn's official OpenID Connect authorization
code flow. In your LinkedIn developer app, enable the Sign In with LinkedIn using
OpenID Connect product and authorize the openid, profile, and email scopes.
Register the exact value of LINKEDIN_REDIRECT_URI as an authorized redirect URL.
For local development, use http://localhost:3000/api/linkedin-profile/callback;
production must use the HTTPS callback URL for your deployment.
The integration uses LinkedIn's OIDC discovery document
and the userinfo endpoint.
Set LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET, LINKEDIN_REDIRECT_URI, and
LINKEDIN_SESSION_ENCRYPTION_KEY in .env.local using the names in
.env.example. Generate the encryption key with
openssl rand -base64 32. The client secret stays on the server. The profile session is stored in an
encrypted, HttpOnly, SameSite=Lax cookie; it is protected from client-side JS
read access and expires after eight hours, but it is not cryptographically bound to
one browser/device. It requires HTTPS in production and is removed on disconnect.
LinkedIn data is loaded only
through the same-origin profile endpoint and is not stored in source settings,
app nodes, or exports.
Available profile values are limited to claims returned by the configured app's LinkedIn OIDC scopes: subject, name, given/family names, picture, locale, and the optional email and email-verification claim. LinkedIn does not expose headline, about/summary, positions, education, or skills through this normal self-serve OIDC profile. Those template fields remain empty; access to broader profile permissions requires LinkedIn approval. LivePage does not scrape LinkedIn. Standalone HTML files cannot use the LinkedIn session cookie.
| Command | What it does |
|---|---|
npm run dev |
Start the development server |
npm run build |
Create a production build |
npm start |
Serve the production build |
npm run lint |
Run ESLint through next lint |
npm test |
Run the Jest suite |
npm run test:watch |
Run Jest in watch mode |
npm run test:coverage |
Run Jest with coverage |
npm run build:html-runtime |
Rebuild the browser runtime used by HTML exports |
npm run test:html-browser |
Run the standalone HTML artifact regression test in Chromium |
npm run check:branch |
Check that the current branch name follows gitflow |
npm run verify |
Lint + full Jest suite (--runInBand) + branch-name check (convenient pre-PR gate) |
The dev, build, start, and test commands rebuild the HTML export runtime automatically. It is generated into client/features/serializers/html/generated/, which is git-ignored.
The standalone HTML browser tests run in headless Chromium against both the live
/try?template=<id>&mode=preview page and the exact exported HTML for every bundled
template at desktop and mobile viewport sizes. They fulfill pinned React imports
from locally installed React packages and use the preview's local font assets, so
the comparison does not depend on CDN availability. To install the browser locally,
run npx playwright install chromium once after npm install; then run
npm run test:html-browser.
The app is built with Next.js (App Router), React 19, TypeScript, Tailwind CSS, Radix UI, and Zod. Tests use Jest and Testing Library.
Pushes must use a gitflow branch name, or a dependabot/<dependency> branch for automated dependency updates. The check applies to people, agents, and other automation. Topic segments are lowercase kebab-case.
| Branch type | Pattern | Example |
|---|---|---|
| main | main |
main |
| develop | develop |
develop |
| dependabot | dependabot/<dependency> |
dependabot/dependencies/node/22 |
| feature | feature/<topic> |
feature/login-flow |
| release | release/<version> |
release/1.4.0 |
| hotfix | hotfix/<topic> |
hotfix/crash-on-start |
| support | support/<version-line> |
support/1.x |
| bugfix | bugfix/<topic> |
bugfix/null-deref |
A rejected name lists these patterns. CI runs node scripts/check-branch-name.mjs on every push and pull request. git config core.hooksPath scripts/git-hooks enables a local pre-push gate that refuses non-compliant branches. The server-side GitHub ruleset is the check that cannot be skipped; see .agents/docs/BRANCH-NAMES.md.
Application code is organized by runtime: browser features and UI live in client/, backend features and integrations in server/, and runtime-neutral models and contracts in shared/. Each runtime groups domain code in its own features/ subfolder. The Next.js route tree stays in app/; page files import client features, while API route files are thin adapters to server features. UI primitives live in client/components/ui/. .agents/docs/CONTEXT.md has the module map and import-boundary rule.
An HTML export is a single file that opens directly in a modern browser, with no LivePage server needed. It bundles the same registered preview-component implementations and compiled Tailwind CSS generated from the builder's global stylesheet. Keep these limits in mind:
- The file loads pinned React and ReactDOM builds from esm.sh, and Inter from Google Fonts, so the browser needs network access when it opens the file.
- The export runtime uses the registered preview data-source implementations. Network-backed sources must allow the exported page's origin through CORS.
- Image and other asset URLs stay as references. They are not embedded. Root-relative public image paths are made absolute against the builder's origin so they can load when the exported file is opened locally.
- Template parity covers initial content and layout at desktop and mobile sizes; live data-source runtime states are not guaranteed to remain visually identical.
Templates live in client/features/templates/definitions/. Each one is versioned (schema and version), keeps catalog metadata separate from its content.pages payload, and stores the page as the same AppNode tree the editor uses.
To add a template:
- Create a definition in
client/features/templates/definitions/using only supported design-component tags. - Keep catalog metadata (
name,description,category,tags,thumbnail) outside the page payload. - Validate it with
pageTemplateDefinitionSchemaand register it inclient/features/templates/registry.ts. - If the template is meant for imported profile data, add
dataMappingentries that point to the target component ids and fields.
The CV/resume templates include LinkedIn-shaped mapping notes in their dataMapping blocks.
- Project context and architecture decisions
- Code conventions
- Glossary
- Branch names
- AGENTS.md and the agent workflow for coding agents
See CONTRIBUTING.md for how to open a pull request and the branch naming (gitflow) conventions enforced in this repo.
Report vulnerabilities privately as described in SECURITY.md. Participation in this project is governed by the Code of Conduct.