Skip to content

Ninso112/Shapio

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Shapio

A universal sharepic generator. Pick a template, edit text, choose a palette and a format, export to PNG.

Shapio is deliberately small: a Node/Express backend for password-gated access plus a static client where templates and palettes live as plain ES modules. Drop in a new file, register it in an index, refresh.


Features

  • 20 built-in templates — manifest, quote, event, save-the-date, schedule, timeline, glossary, hashtags, triptych, open letter, photo story, polaroid, statistic, myth-vs-fact, carousel slide, slogan, gradient hero, checklist, spotlight, numbered list.
  • 7 default palettes — Ink, Paper, Ocean, Sunset, Forest, Dusk, Spectrum.
  • Three formats — 1:1 post (1080×1080), 9:16 story (1080×1920), 16:9 wide (1920×1080).
  • Photo upload with zoom & pan for photo, spotlight and polaroid templates.
  • PNG export in full canvas resolution.
  • Password-protected with a forced first-login change.
  • Modular — one file per template, one file per palette, branding lives in a single config.

Quick start

git clone <your-fork-url> shapio
cd shapio
npm install
npm start

Then open http://localhost:3000.

  • Initial password: admin
  • After the first successful login you'll be asked to set a new password. The hash is stored in auth/auth.json (gitignored).

To run in watch mode (auto-restart on changes):

npm run dev

Environment variables

Copy .env.example to .env and adjust:

Variable Default Notes
PORT 3000 Port the server listens on.
SESSION_SECRET dev fallback Set a strong secret in production. Used to sign session cookies.

Project layout

shapio/
├── server.js                  # Express server (auth + static)
├── package.json
├── auth/
│   ├── auth.default.json      # Bootstrap config — "admin" / mustChange: true
│   └── auth.json              # Generated on first run (gitignored)
└── public/                    # Served statically
    ├── index.html
    ├── styles.css
    ├── app.js
    ├── config/
    │   └── branding.js        # App name, logo, defaults
    ├── palettes/
    │   ├── index.js           # Palette registry
    │   ├── ink.js
    │   ├── paper.js
    │   ├── ocean.js
    │   ├── sunset.js
    │   ├── forest.js
    │   ├── dusk.js
    │   └── spectrum.js
    ├── templates/
    │   ├── index.js           # Template registry
    │   └── *.js               # one file per template
    └── assets/
        └── img/logo.svg

Customisation

Branding

Edit public/config/branding.js:

export const BRANDING = {
  appName: "Your App",
  appTagline: "Your tagline",
  appLogoUrl: "/assets/img/your-logo.svg",
  tagline: "Shown in the header eyebrow",

  templateLogo: {
    enabled: true,
    imageUrl: "/assets/img/your-logo.svg",
    name: "Your App",
    sub: "Your subtitle",
  },

  defaultPalette: "ink",
  defaultFormat: "1:1",
  defaultTemplate: "manifest",
};

Drop your logo into public/assets/img/ and reference it via the URL above.

Adding a palette

  1. Create public/palettes/your-palette.js:

    export default {
      id: "your-id",
      name: "Display name",
      swatch: "linear-gradient(135deg, #112233 0%, #112233 50%, #FF8800 50%, #FF8800 100%)",
      tokens: {
        bg:       "#112233",
        bg2:      "#0B1A2A",
        fg:       "#FFFFFF",
        fgSoft:   "#CBD5E1",
        accent:   "#FF8800",
        accentFg: "#0B1A2A",
        eyebrow:  "#FFB266",
      },
    };
  2. Register it in public/palettes/index.js:

    import yourPalette from "./your-palette.js";
    export const PALETTES = [..., yourPalette];

Tokens are exposed as CSS custom properties (--c-bg, --c-bg-2, --c-fg, --c-fg-soft, --c-accent, --c-accent-fg, --c-eyebrow) on the .canvas element, so templates reference them via var(--c-fg) etc.

Adding a template

  1. Create public/templates/your-template.js:

    export default {
      id: "your-id",
      name: "Display name",
      desc: "Short description shown in the gallery.",
      fields: [
        { key: "headline", label: "Headline", type: "input",    max: 80 },
        { key: "body",     label: "Body",     type: "textarea", max: 200 },
      ],
      supportsPhoto: false,
      defaults: {
        headline: "Default headline",
        body:     "Default body copy.",
      },
      render: (t, state, { esc, LOGO_HTML }) => `
        <div class="tpl">
          <h1 class="tpl-headline">${esc(t.headline)}</h1>
          <p class="tpl-body">${esc(t.body)}</p>
          ${state.showLogo ? LOGO_HTML() : ""}
        </div>
      `,
      thumb: () => `
        <div style="position:absolute;inset:0;background:#FAFAF9;padding:8px;">Preview</div>
      `,
    };
  2. Add matching styles to public/styles.css, scoped to your id:

    .canvas[data-template="your-id"] .tpl { /* layout */ }
  3. Register it in public/templates/index.js:

    import yours from "./your-template.js";
    export const TEMPLATES = { ..., yours };
    export const TEMPLATE_ORDER = [..., "your-id"];

The render function receives the current text values (t), the full app state (state — includes photoUrl, photoZoom, photoX, photoY, showLogo) and a context object with utility helpers (esc, cssEsc, LOGO_HTML, photoTransformStyle).

If your template supports photo upload set supportsPhoto: true and include the photo block in your render output (see photo.js, spotlight.js, polaroid.js for examples).

Resetting the password

If you've forgotten the password, delete auth/auth.json and restart the server. It will be recreated from auth/auth.default.json (initial password: admin).

To change the initial password (before any first login), edit auth/auth.default.json and delete auth/auth.json.


How the gate works

  1. Browser fetches GET /api/auth/status. The server returns { authenticated, mustChange }.
  2. If the user isn't authenticated, the login form is shown. Submitting it calls POST /api/auth/login.
  3. On the first successful login (mustChange: true), a change-password form is shown. It calls POST /api/auth/change with the current and new password.
  4. Sessions are stored server-side (signed cookie). Use POST /api/auth/logout to clear.

Passwords are hashed with bcrypt (10 rounds). The plain password is never persisted; only the hash sits in auth/auth.json.


Browser support

Modern evergreen browsers (Chrome, Firefox, Safari, Edge). Uses ES modules in the browser, so no bundler is required.


License

GNU General Public License v3.0 or later. See LICENSE.

About

Universal sharepic generator with 20 templates, 7 palettes, photo upload with zoom & pan, and PNG export. Drop-in ES modules for your own templates and palettes. Node/Express, no build step. GPLv3.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors