Imported from kacigaya/linkedin-post (
AGENTS.md). Install upstream withnpx skills add kacigaya/linkedin-post. Copyright stays with the author.
linkedin-post
LinkedIn post mockup generator. Next.js App Router, React 19, Tailwind 4, Bun, Coss UI (Base UI) for the control panel. Entirely client-side: no API routes, no database, no environment variables.
Design system, fonts, theming, and README structure mirror
/home/ubuntu/projects/muzik (kacigaya/muzik). Check that repo before
changing tokens or chrome.
Commands
bun install
bun run dev # next dev on :3000
bun run build # standalone output in .next/standalone
bun run typecheck
bun run check # typecheck + build; run this before shipping
Bun 1.3.14, Node 24.
Deployment
- Public host:
linkedin.gayakaci.duckdns.org(DuckDNS wildcard, no DNS work needed). - Dokploy Application, source type
gitonkacigaya/linkedin-post, build typedockerfile, container port 3000. - Dokploy domain entry must stay
certificateType: noneandhttps: false; Caddy owns TLS. See/home/ubuntu/DOKPLOY.mdon the host. - Pushes to
maintrigger a Dokploy rebuild through the hooks endpoint.
UI
- Panel components live in
components/ui, added withbunx --bun shadcn@latest add @coss/<name>(registry incomponents.json). They are owned in-repo — edit them directly rather than wrapping them. - Semantic tokens live at the top of
app/globals.css: coss's neutral set shifted to the stone scale in light and to#161616/#1b1b1bin dark, copied from Muzik.--brandtracks--primary. - Fonts are Inter and Geist Mono through
next/font/google, so the Docker build needs network access. The post card overrides them with the system stack. - Theming is
next-themes(attribute="class", system default). Anything derived fromresolvedThememust wait for a mounted flag, and the theme icons swap in CSS — deriving markup from it directly breaks hydration. SiteNavis Muzik's top bar: one rounded-xlbg-card/80strip,max-w-6xl..li-cardinapp/globals.cssis a separate, fixed palette for the post card. Keep it independent of the app theme so exports stay deterministic.
Constraints worth keeping
next.config.tssetsoutput: "standalone"; the Dockerfile runner copies.next/standaloneand.next/static. Removing it breaks the image.- The post body is a
<textarea>on purpose. Do not replace it withcontenteditable— free editing is the feature. - The post card is fluid (
w-full max-w-[552px]). Giving it a fixed width sets the mobile grid track and pushes the whole page into horizontal scroll; its narrow-width tweaks use container queries, not viewport breakpoints. - Export
pixelRatioscales with the rendered card width so a phone export is still roughly desktop-sized. The exported layout is whatever is on screen. - Tagged names are plain substrings, matched case-insensitively and
longest-first in
segments(). LinkedIn stores mentions as links, not syntax, so there is no marker to parse out of the body. - The editor's highlight overlay must keep the same text metrics as the textarea it sits under, so it never bolds a tag. Changing that reintroduces caret drift.
PostCardrendersmode="static"during export because html-to-image clones textareas without their value.- The post card sets the system sans stack inline. It must not inherit Inter, or the export stops matching what LinkedIn renders.
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ (resolved from this file's directory; in monorepos the next package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by next dev — verify at node_modules/next/dist/server/lib/generate-agent-files.js. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.