@makemind/app-builder
v0.2.0
Published
Drop-in App Builder for MAKEMIND tenants — the editing surface wired to a data source
Maintainers
Readme
@makemind/app-builder
A drag-and-drop page builder you can put inside your own product, so your customers build their screens without ever seeing ours.
import { AppBuilder, createSdkDataSource } from '@makemind/app-builder';
import '@makemind/app-builder-ui/styles.css';
import { getMAKEMIND } from '@makemind/sdk';
<AppBuilder appId="app_123" dataSource={createSdkDataSource(getMAKEMIND())} />The stylesheet import is yours to make. This package does not import it for you: a side-effect CSS import breaks anything that loads the package outside a bundler — server-side rendering, a node-environment test, a build script.
That is the whole editor: a component palette, a canvas, and a property panel, with saving and page switching wired up.
Install
npm install @makemind/app-builder @makemind/app-builder-uireact and react-dom (18 or newer) are peers and come from your app.
@makemind/sdk is an optional peer — you need it only for
createSdkDataSource.
Who holds the credential
This is the decision that shapes everything else.
Your users are MAKEMIND tenants. Use createSdkDataSource. The browser
authenticates with MAKEMIND directly.
Your users are your customers. Write your own data source. The editor calls your backend; your backend holds the API key and calls MAKEMIND. Your customers authenticate with you, and never learn we are involved.
const dataSource: AppBuilderDataSource = {
getApp: (appId) => api.get(`/builder/${appId}`),
listPages: (appId) => api.get(`/builder/${appId}/pages`),
getPage: (appId, pageId) => api.get(`/builder/${appId}/pages/${pageId}`),
updatePage: (appId, pageId, patch) =>
api.put(`/builder/${appId}/pages/${pageId}`, patch),
};Five methods. There is no sixth — the editor reaches nothing else.
listCallableFlows() is optional: implement it and authors can bind event
handlers to your published flows; leave it off and the property panel offers
none. Leave it off rather than returning [], which says something different
(this tenant has no published flows, not this host cannot tell you about
flows).
Keeping one customer's apps apart from another's
Set party on each app to a reference of your own —
party: 'ref:<your customer id>'. The platform treats the party as a
parameter, so your identifiers stay yours; you do not have to create a
MAKEMIND account per customer.
Branding
The editor is styled entirely with CSS custom properties, and ships with
defaults for all 37 of them at specificity zero. Define any of them on
:root and yours win — you never have to fight the package with !important
or deep selectors.
:root {
--color-primary: #0057d9;
--color-primary-hover: #0046ae;
--bg-secondary: #ffffff;
--text-primary: #101418;
--border-color: #dfe3e8;
--radius-md: 6px;
}Light and dark are both provided. Dark follows prefers-color-scheme unless
you set data-theme="light" or "dark" on the root element.
The full variable list is in
node_modules/@makemind/app-builder-ui/dist/index.css — palette, text,
surfaces, borders, status colours, fonts, radii, shadows and transitions.
Props
| Prop | Required | What it does |
|---|---|---|
| appId | ✅ | The app to edit |
| dataSource | ✅ | Where pages are read and written |
| pageId | | Open this page instead of the app's first |
| onPageChange | | The author switched pages — put it in your URL if you route |
| onBack · onPublish | | Your navigation. A control with no handler is not rendered |
| renderLoading · renderLoadError | | Replace the default loading and fatal-error screens |
Failures are kept apart deliberately: a load failure replaces the screen, because there is nothing to edit; a save or page-switch failure appears as a banner inside the editor, because the canvas, the selection and every unsaved change have to survive it.
A failed save leaves "Unsaved changes" standing. The tree at that moment exists only in the browser, and saying otherwise would be a lie the author acts on.
What authors can place
108 component types are declared; 56 are in the palette today — the ones the canvas draws and the publisher emits. The rest are declared for the schema's sake and do not appear.
The 56 span layout, typography, media, form inputs, data display, navigation, feedback and AI components.
Only the editing surface
If you want your own arrangement of the panels — or just the palette —
@makemind/app-builder-ui is this package without the wiring. This one is
that package plus a data source.
Developing against a checkout
Linking the package from source (file: or a workspace) gives your bundler
two copies of React, and every hook inside the editor reads null. Dedupe:
// vite.config.ts
export default defineConfig({
resolve: { dedupe: ['react', 'react-dom'] },
});Installed from the registry this cannot happen — React is a peer and the package brings none of its own.
Known limitations
- 29 class names in the widget library have no rule of their own. The markup and the stylesheet drifted apart before this was a package. Native controls keep their own appearance, so nothing vanishes, but a handful of widget parts are unstyled. The count is pinned by a test and can only fall.
- A tenant's flow list is capped at 100. The platform's flow-list endpoint returns at most 100 and does not paginate, so an author with more than 100 flows will not see them all in the handler picker.
