{"id":45781685,"url":"https://github.com/flanksource/facet","last_synced_at":"2026-04-03T00:59:17.056Z","repository":{"id":324572758,"uuid":"1097689383","full_name":"flanksource/facet","owner":"flanksource","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-26T08:31:06.000Z","size":2512,"stargazers_count":0,"open_issues_count":7,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-26T15:40:06.208Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/flanksource.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-16T16:48:57.000Z","updated_at":"2026-02-26T08:31:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/flanksource/facet","commit_stats":null,"previous_names":["flanksource/facet"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/flanksource/facet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flanksource%2Ffacet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flanksource%2Ffacet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flanksource%2Ffacet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flanksource%2Ffacet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/flanksource","download_url":"https://codeload.github.com/flanksource/facet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/flanksource%2Ffacet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29964203,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T06:55:38.174Z","status":"ssl_error","status_checked_at":"2026-03-01T06:53:04.810Z","response_time":124,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-26T11:05:56.868Z","updated_at":"2026-04-03T00:59:16.962Z","avatar_url":"https://github.com/flanksource.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @flanksource/facet\n\nBuild beautiful, print-ready datasheets and PDFs from React templates.\n\n**@flanksource/facet** is a framework for creating professional datasheets, reports, and documentation using React components. It provides a rich component library optimized for print and PDF generation, along with a powerful CLI for building HTML and PDF outputs.\n\n## Features\n\n- 📄 **Print-optimized components** - 47+ components designed for professional datasheets\n- 🎨 **React \u0026 TypeScript** - Full type safety and modern React patterns\n- 🔧 **Zero-config CLI** - Build HTML and PDF with a single command\n- 🔗 **Component imports** - `import { StatCard } from '@flanksource/facet'`\n- ⚡ **Fast builds** - Powered by Vite with smart caching\n- 📦 **Isolated builds** - `.facet/` build directory (like `.next` in Next.js)\n\n## Installation\n\n### Option 1: npm Package (Recommended)\n\n```bash\nnpm install -g @flanksource/facet\n```\n\n### Option 2: Standalone Binary\n\nDownload platform-specific binaries from [GitHub Releases](https://github.com/flanksource/facet/releases):\n\n- **Linux**: `facet-linux`\n- **macOS**: `facet-macos`\n- **Windows**: `facet-windows.exe`\n\nMake executable (Linux/macOS):\n```bash\nchmod +x facet-*\nsudo mv facet-* /usr/local/bin/facet\n```\n\n## Quick Start\n\n### 1. Create a Template\n\nCreate a file `MyDatasheet.tsx` in your project:\n\n```tsx\nimport React from 'react';\nimport {\n  DatasheetTemplate,\n  Header,\n  Page,\n  StatCard,\n  Section,\n  BulletList\n} from '@flanksource/facet';\n\nexport default function MyDatasheet() {\n  return (\n    \u003cDatasheetTemplate\u003e\n      \u003cHeader\n        title=\"Mission Control Platform\"\n        subtitle=\"Cloud-Native Observability \u0026 Incident Management\"\n      /\u003e\n\n      \u003cPage\u003e\n        \u003cSection title=\"Key Metrics\"\u003e\n          \u003cdiv className=\"grid grid-cols-3 gap-4\"\u003e\n            \u003cStatCard label=\"Response Time\" value=\"\u003c 2min\" /\u003e\n            \u003cStatCard label=\"Uptime\" value=\"99.99%\" /\u003e\n            \u003cStatCard label=\"Incidents Resolved\" value=\"1,247\" /\u003e\n          \u003c/div\u003e\n        \u003c/Section\u003e\n\n        \u003cSection title=\"Key Features\"\u003e\n          \u003cBulletList items={[\n            'Real-time incident detection and alerting',\n            'Automated runbook execution',\n            'Multi-cloud observability',\n            'Integrated ChatOps workflows'\n          ]} /\u003e\n        \u003c/Section\u003e\n      \u003c/Page\u003e\n    \u003c/DatasheetTemplate\u003e\n  );\n}\n```\n\n### 2. Build HTML Output\n\n```bash\nfacet html MyDatasheet.tsx -o ./dist\n```\n\nThis creates:\n- Print-ready HTML with embedded styles\n- Scoped HTML for embedding in docs (use `--css-scope` for a custom prefix)\n- `.facet/` - Build cache directory (can be gitignored)\n\n### 3. Generate PDF\n\n```bash\nfacet pdf MyDatasheet.tsx\n```\n\n## How It Works\n\n![](assets/how-it-works.svg)\n\n\n\n### Build Process\n\n**CLI mode** — `facet html` and `facet pdf` run the full pipeline locally:\n\n1. **Setup `.facet/`** — Creates an isolated build directory with symlinks to your sources\n2. **Generate configs** — Auto-generates `vite.config.ts`, `tsconfig.json`, `entry.tsx`\n3. **Vite SSR compile** — Compiles React + TypeScript + MDX via Vite\n4. **React SSR render** — Renders components to static HTML with `ReactDOMServer`\n5. **Tailwind CSS** — Extracts only the styles your template uses\n6. **HTML output** — Combines markup + inlined CSS into a self-contained HTML file\n7. **PDF output** *(optional)* — Puppeteer prints the HTML to PDF, with optional encryption and digital signatures\n\n**Server mode** — `facet serve` wraps the same pipeline behind an HTTP API with a worker pool, LRU cache, optional S3 upload, and an interactive playground at `localhost:3010`.\n\n\n\n## Page Component API\n\nThe `Page` component is the primary layout container for multi-page PDF documents.\n\n```tsx\n\u003cPage\n  title=\"Section Title\"\n  product=\"Mission Control\"\n  header={\u003cHeader variant=\"solid\" /\u003e}\n  headerHeight={15}\n  footer={\u003cPdfFooter /\u003e}\n  footerHeight={15}\n  margins={{ top: 5, right: 0, bottom: 0, left: 0 }}\n  watermark=\"DRAFT\"\n  debug={false}\n\u003e\n  {/* page content */}\n\u003c/Page\u003e\n```\n\n| Prop | Type | Default | Description |\n|------|------|---------|-------------|\n| `children` | `ReactNode` | — | Page content |\n| `title` | `string` | — | Section title bar text (renders a blue bar below the header) |\n| `product` | `string` | — | Sub-label shown in the title bar |\n| `header` | `ReactNode` | — | Fixed header rendered at the top of every physical page |\n| `headerHeight` | `number` (mm) | `0` | Height of the header; used to offset content so it doesn't overlap |\n| `footer` | `ReactNode` | — | Fixed footer rendered at the bottom of every physical page |\n| `footerHeight` | `number` (mm) | `15` | Height of the footer; used to add bottom padding to content |\n| `margins` | `PageMargins` | `{}` | Additional content margins `{ top, right, bottom, left }` in mm |\n| `pageSize` | `string` | `'a4'` | Page size — `a4`, `a3`, `letter`, `legal`, `fhd`, `a4-landscape`, or custom `WxH` in mm |\n| `type` | `string` | `'default'` | Page type — groups pages for header/footer extraction (e.g. `cover`, `default`) |\n| `watermark` | `string` | — | Diagonal watermark text (e.g. `\"DRAFT\"`, `\"CONFIDENTIAL\"`) |\n| `debug` | `boolean` | `false` | Renders dashed red lines at margin boundaries for layout debugging |\n| `className` | `string` | — | Extra CSS class applied to the `\u003cmain\u003e` element |\n\n### Multi-page PDF layout\n\nA single React document with mixed page sizes (e.g. `\u003cPage size=\"a4\" type=\"cover\"\u003e`, `\u003cPage size=\"a4\"\u003e`, `\u003cPage size=\"a4-landscape\"\u003e`) is compiled into a final PDF via a 4-phase multi-pass pipeline:\n\n1. **DOM Scan** — Puppeteer renders to DOM, measures header/footer heights per type×size group (e.g. `cover:a4`, `default:a4`, `default:a4-landscape`)\n2. **Extract** — Each unique type×size group's header and footer are rendered as isolated PDFs using a dedicated Puppeteer pass, loaded into pdf-lib\n3. **Content Render** — Decorators are stripped from the DOM; `@page` margins are set to the measured header/footer heights; Puppeteer prints content-only PDFs per group\n4. **Composite** — pdf-lib `page.drawPage()` overlays the correct header at the top and footer at the bottom of every physical page, producing a single merged PDF\n\n```tsx\n  {/* Cover page with unique header/footer */}\n  \u003cPage pageSize=\"a4\" type=\"cover\"\u003e\n    \u003cHeader height={20} /\u003e\n    \u003cCoverContent /\u003e\n    \u003cFooter height={10} /\u003e\n  \u003c/Page\u003e\n\n  {/* Standard A4 page */}\n  \u003cPage pageSize=\"a4\"\u003e\n    \u003cHeader height={18} /\u003e\n    {/* Content starts after header automatically */}\n    \u003cFooter height={8} /\u003e\n  \u003c/Page\u003e\n\n  {/* Landscape page for wide content */}\n  \u003cPage pageSize=\"a4-landscape\"\u003e\n    \u003cHeader height={18} /\u003e\n    \u003cWideTableContent /\u003e\n    \u003cFooter height={8} /\u003e\n  \u003c/Page\u003e\n```\n\n![](assets/pdf-layout.svg)\n\n## CLI Commands\n\n### `facet html \u003ctemplate\u003e`\n\nGenerate HTML from a React template.\n\n```\nfacet html [options] \u003ctemplate\u003e\n\nOptions:\n  --css-scope \u003cprefix\u003e         CSS scope prefix for scoped HTML generation\n  -s, --schema \u003cfile\u003e          Path to JSON Schema file for data validation\n  --no-validate                Skip data validation\n  -d, --data \u003cfile\u003e            Path to JSON data file\n  -l, --data-loader \u003cfile\u003e     Path to data loader module (.ts or .js)\n  -o, --output \u003cpath\u003e          Output file path or directory (default: \"dist\")\n  --output-name-field \u003cfield\u003e  Data field to use for output filename\n  -v, --verbose                Enable verbose logging\n```\n\n**Example:**\n```bash\nfacet html MyDatasheet.tsx -o ./dist --verbose\nfacet html MyDatasheet.tsx -d data.json -o report.html\n```\n\n### `facet pdf \u003ctemplate\u003e`\n\nGenerate PDF from a React template.\n\n```\nfacet pdf [options] \u003ctemplate\u003e\n\nOptions:\n  -s, --schema \u003cfile\u003e          Path to JSON Schema file for data validation\n  --no-validate                Skip data validation\n  -d, --data \u003cfile\u003e            Path to JSON data file\n  -l, --data-loader \u003cfile\u003e     Path to data loader module (.ts or .js)\n  -o, --output \u003cpath\u003e          Output file path or directory (default: \"dist\")\n  --output-name-field \u003cfield\u003e  Data field to use for output filename\n  -v, --verbose                Enable verbose logging\n```\n\n**Example:**\n```bash\nfacet pdf MyDatasheet.tsx -d data.json -o out.pdf\n```\n\n### `facet serve`\n\nStart an API server with a built-in playground for interactive template development.\n\n```\nfacet serve [options]\n\nOptions:\n  -p, --port \u003cnumber\u003e          Server port (default: 3010)\n  --templates-dir \u003cdir\u003e        Directory containing templates (default: \".\")\n  --workers \u003ccount\u003e            Number of browser workers (default: 2)\n  --timeout \u003cms\u003e               Render timeout in milliseconds (default: 60000)\n  --api-key \u003ckey\u003e              API key for authentication\n  --max-upload \u003cbytes\u003e         Max upload size in bytes (default: 52428800)\n  --cache-max-size \u003cbytes\u003e     Max render cache size in bytes (default: 104857600)\n  --s3-endpoint \u003curl\u003e          S3 endpoint URL\n  --s3-bucket \u003cname\u003e           S3 bucket name\n  --s3-region \u003cregion\u003e         S3 region (default: us-east-1)\n  --s3-prefix \u003cprefix\u003e         S3 key prefix\n  -v, --verbose                Enable verbose logging\n```\n\n**Example:**\n```bash\nfacet serve --templates-dir ./templates --port 3010\n\n# With authentication\nfacet serve --api-key my-secret-key\n\n# With S3 upload\nfacet serve --s3-endpoint https://s3.amazonaws.com --s3-bucket my-bucket\n```\n\nThe playground is available at `http://localhost:3010/` with a Monaco editor, live preview, and render logs.\n\nSee [openapi.yaml](openapi.yaml) for the full API specification.\n\n### Docker\n\n```bash\ndocker run -p 3000:3000 -v ./templates:/templates ghcr.io/flanksource/facet\n```\n\n### Helm\n\n```bash\nhelm install facet ./chart\n```\n\nSee [chart/values.yaml](chart/values.yaml) for configuration options.\n\n### Why `.facet/`?\n\nSimilar to Next.js (`.next/`) or Nuxt (`.nuxt/`), the `.facet/` directory:\n- **Isolates build artifacts** - Keeps your project clean\n- **Enables fast rebuilds** - Symlinks avoid file copying\n- **Supports incremental builds** - Only rebuilds what changed\n- **Simplifies debugging** - All build files in one place\n\n**Add to `.gitignore`:**\n\n```gitignore\n.facet/\ndist/\n```\n\n## Import Patterns\n\n### Named Imports (Recommended)\n```tsx\nimport { StatCard, Header, Page } from '@flanksource/facet';\n```\n\n### TypeScript Support\n\nAll components include full TypeScript definitions:\n\n```tsx\nimport { StatCard } from '@flanksource/facet';\n\n\u003cStatCard\n  label=\"Response Time\"  // string\n  value=\"\u003c 2min\"         // string | number\n  trend=\"up\"             // 'up' | 'down' | 'neutral' (optional)\n  icon=\"clock\"           // string (optional)\n/\u003e\n```\n\n## Styling\n\nComponents use Tailwind CSS for styling. Include Tailwind in your project:\n\n```bash\nnpm install -D tailwindcss autoprefixer postcss\n```\n\n**tailwind.config.js:**\n```js\nmodule.exports = {\n  content: [\n    './MyDatasheet.tsx',\n    './node_modules/@facet/core/src/components/**/*.tsx'\n  ],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n}\n```\n\n## MDX Support\n\nTemplates support MDX for content-rich pages:\n\n```tsx\n// MyDatasheet.tsx\nimport Content from './content.mdx';\n\nexport default function MyDatasheet() {\n  return (\n    \u003cDatasheetTemplate\u003e\n      \u003cContent /\u003e\n    \u003c/DatasheetTemplate\u003e\n  );\n}\n```\n\n```mdx\n# content.mdx\n\n## Overview\n\nThis is **MDX content** with React components:\n\n\u003cStatCard label=\"Users\" value=\"10,000+\" /\u003e\n\n- Bullet point one\n- Bullet point two\n```\n\n## Development\n\n### Component Development\n\nUse Storybook for component development:\n\n```bash\nnpm run storybook\n```\n\n### Building the CLI\n\n```bash\nnpm run build:cli\n```\n\n### Publishing\n\n```bash\nnpm run prepublishOnly  # Builds CLI automatically\nnpm publish\n```\n\n## Architecture\n\n- **`src/components/`** - React component library (47 components)\n- **`src/styles.css`** - Global styles and Tailwind\n- **`cli/`** - CLI package source\n  - **`cli/src/builders/`** - Build orchestration\n  - **`cli/src/generators/`** - HTML/PDF generators\n  - **`cli/src/utils/`** - Shared utilities\n  - **`cli/src/plugins/`** - Vite plugins\n- **`assets/`** - Static assets (logos, icons)\n\n## Examples\n\nSee `src/examples/` for complete working examples:\n\n- **Basic Datasheet** - Simple single-page datasheet\n- **Multi-page Report** - Complex multi-page document\n- **Security Report** - Security-focused datasheet\n- **POC Evaluation** - POC evaluation template\n\n## Contributing\n\nThis is an internal Flanksource package. For issues or feature requests, contact the platform team.\n\n## License\n\nProprietary - Flanksource Inc.\n\n---\n\n**Built with ❤️ by Flanksource**\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflanksource%2Ffacet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fflanksource%2Ffacet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fflanksource%2Ffacet/lists"}