This repository contains paperlesspaper integrations rendered for eInk displays. It is a Next.js app optimized for fixed-size, low-color layouts and screenshot-based rendering.
- Install dependencies.
- Start the dev server (defaults to http://localhost:3001).
npm install
npm run dev
Connect to your Google Calendar. Data is provided via window.postMessage.
Displays current weather and forecast for any location using OpenWeather data.
Shows the Wikipedia “Article of the day” or “On this day”. Available in English and German.
Displays any RSS feed.
Shows pharmacies on emergency duty in Germany using the public search endpoint of aponet.de. Exposes data via /api/apothekennotdienst.
Renders a random image from a public iCloud Shared Album.
Additional routes live in src/app.
This repo ships an example provider for the Open Integration system used by memo-mono.
- Manifest (install URL): /open-integration-example/config.json
- Settings page (iframe): /open-integration-example/settings
- Render page (Puppeteer target): /open-integration-example/render
- Mock OAuth/redirect helper (optional): /open-integration-example/auth
Local install into memo-mono:
- Start this repo (defaults to http://localhost:3001).
- In memo-mono, choose the Integration Plugin application.
- Install by config URL:
http://localhost:3001/open-integration-example/config.json
The settings iframe supports both structured messages ({ source: "wirewire-app", type: "INIT" | "REDIRECT", ... }) and the legacy { cmd: "message" | "redirect", data: ... } format.
- Style selection
- Optimized for 7-color AcEP eInk displays (Spectra 6 coming soon)
- Layout fitting and auto-scaling
- Horizontal and vertical layouts
- Multilanguage support (see src/i18n)
Fits text into its container by adjusting font size.
Properties:
checkHeight = false
maxFontSize = 100
id = "no-id"
No extra setup is required. Each integration appears inside paperlesspaper when creating a new image.
You need to render a route to an image and transmit it to the display.
Typical steps:
- Render the route using Puppeteer.
- Compare against the previous image to avoid redundant uploads.
- Dither the image.
- Transmit the dithered image to the display.
The backend detects loading states based on the presence of these DOM elements.
Indicates that the page has a custom loading state. If this element exists, the backend waits for loading to finish before taking a screenshot.
Add while loading:
<div id="website-has-loading-element" />Signals that the page is fully loaded and ready for rendering. The backend waits for this element to appear before proceeding.
Add when ready:
<div id="website-has-loaded" />If #website-has-loading-element is not found, the backend waits a fixed timeout (8.5 seconds) before taking a screenshot.
A context provider that tracks the loading status of registered operations. Wrap your app (or a subtree) with this provider to enable loading state management.
Props:
children: ReactNodefinishedLoading(optional): boolean — If set totrue, marks all as loaded regardless of internal state.
Behavior:
- Renders a
<div id="website-has-loaded" />when all registered operations are finished loading. - Renders a
<div id="website-is-loading" />when any operation is still loading. - Always renders a
<div id="website-has-loading-element" />for reference.
Registers and updates a loading operation.
Arguments:
id: string — Unique identifier for the loading operation.
Returns:
setLoading: (loading: boolean) => void
Usage example:
import { useLoading } from "../helpers/Loading";
const MyComponent = () => {
const setLoading = useLoading({ id: "my-component" });
useEffect(() => {
setLoading(true);
fetchData().finally(() => setLoading(false));
}, []);
};The app supports switching the theme (CSS class on <body>) via a query param.
- Param name:
color - Supported values:
dark|lightred-dark|red-lightgreen-dark|green-lightblue-dark|blue-light
- Default:
dark
In these *-dark / *-light variants, the base (dark/light) controls background + foreground, and the color controls the highlight (--accent).
Examples:
/weather?color=light/google-calendar?color=dark/weather?color=green-dark/weather?color=blue-light
Implementation detail: this is handled globally in src/app/layout.tsx via the ColorVariablesFromParam component.
In your css file you can use the theme variables like this:
color: var(--background);
background: var(--foreground);
border-top: var(--accent);Scripts:
npm run dev— Start Next.js on port 3001npm run build— Build for productionnpm run start— Run the production servernpm run lint— Lint the codebasenpm run deploy— Deploy via Vercel
