pdfx-cli 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +344 -176
- package/dist/mcp/index.js +34 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2386,8 +2386,21 @@ async function getAuditChecklist() {
|
|
|
2386
2386
|
\`\`\`
|
|
2387
2387
|
|
|
2388
2388
|
### "Invalid hook call"
|
|
2389
|
-
|
|
2390
|
-
|
|
2389
|
+
\`@react-pdf/renderer\` renders synchronously. Hooks that depend on browser APIs or
|
|
2390
|
+
async side effects do not work in the PDF render path.
|
|
2391
|
+
|
|
2392
|
+
**Do NOT use inside a PDFx component:**
|
|
2393
|
+
- \`useEffect\` / \`useLayoutEffect\` — no browser lifecycle in PDF rendering
|
|
2394
|
+
- \`useRef\` for DOM nodes — no DOM exists in the PDF render tree
|
|
2395
|
+
- Any hook that calls browser globals (\`window\`, \`document\`, \`navigator\`)
|
|
2396
|
+
|
|
2397
|
+
**These ARE valid inside PDFx components:**
|
|
2398
|
+
- PDFx framework hooks: \`usePdfxTheme()\`, \`useSafeMemo()\`
|
|
2399
|
+
- Custom hooks that are pure functions with no browser-API dependencies
|
|
2400
|
+
- Data passed down as props from a parent client component
|
|
2401
|
+
|
|
2402
|
+
If you see this error in a wrapper component you wrote, move the browser hook
|
|
2403
|
+
to the nearest client component and pass the result down as a prop.
|
|
2391
2404
|
|
|
2392
2405
|
### "Text strings must be rendered inside \`<Text>\` component"
|
|
2393
2406
|
Wrap all string literals in \`<Text>\` from \`@react-pdf/renderer\`:
|
|
@@ -2587,7 +2600,7 @@ async function getComponent(args) {
|
|
|
2587
2600
|
const primaryContent = item.files[0]?.content ?? "";
|
|
2588
2601
|
const primaryPath = item.files[0]?.path ?? "";
|
|
2589
2602
|
const exportNames = extractAllExportNames(primaryContent);
|
|
2590
|
-
const mainExport = extractExportName(primaryContent);
|
|
2603
|
+
const mainExport = extractExportName(primaryContent, args.component);
|
|
2591
2604
|
const exportSection = exportNames.length > 0 ? dedent4`
|
|
2592
2605
|
## Exports
|
|
2593
2606
|
**Main component export:** \`${mainExport ?? exportNames[0]}\`
|
|
@@ -2632,11 +2645,18 @@ async function getComponent(args) {
|
|
|
2632
2645
|
${fileSources}
|
|
2633
2646
|
`);
|
|
2634
2647
|
}
|
|
2635
|
-
function extractExportName(source) {
|
|
2648
|
+
function extractExportName(source, componentName) {
|
|
2636
2649
|
if (!source) return null;
|
|
2637
|
-
const matches = [...source.matchAll(/export\s+
|
|
2638
|
-
|
|
2639
|
-
|
|
2650
|
+
const matches = [...source.matchAll(/export\s+function\s+([A-Z][A-Za-z0-9]*)/g)];
|
|
2651
|
+
const names = matches.map((m) => m[1]).filter(Boolean);
|
|
2652
|
+
if (names.length === 0) return null;
|
|
2653
|
+
if (!componentName) return names[0];
|
|
2654
|
+
const norm = componentName.replace(/-/g, "").toLowerCase();
|
|
2655
|
+
const exact = names.find((n) => n.toLowerCase() === norm);
|
|
2656
|
+
if (exact) return exact;
|
|
2657
|
+
const suffix = names.find((n) => n.toLowerCase().endsWith(norm));
|
|
2658
|
+
if (suffix) return suffix;
|
|
2659
|
+
return names[0];
|
|
2640
2660
|
}
|
|
2641
2661
|
function extractAllExportNames(source) {
|
|
2642
2662
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2845,7 +2865,7 @@ async function getInstallation(args) {
|
|
|
2845
2865
|
| TypeScript errors on \`@react-pdf/renderer\` | \`${installCmd(pm, "@react-pdf/types", true)}\` |
|
|
2846
2866
|
| "Cannot find module @/components/pdfx/..." | Run \`npx pdfx-cli@latest add <component>\` to install it |
|
|
2847
2867
|
| PDF renders blank | Ensure root returns \`<Document><Page>...</Page></Document>\` |
|
|
2848
|
-
| "Invalid hook call" |
|
|
2868
|
+
| "Invalid hook call" | Browser-only hooks (\`useEffect\`, \`useRef\` for DOM) don't work in the PDF render path — move them to a parent client component and pass the result as a prop. PDFx framework hooks (\`usePdfxTheme\`, \`useSafeMemo\`) are valid inside PDF components. |
|
|
2849
2869
|
|
|
2850
2870
|
---
|
|
2851
2871
|
Next: call \`get_audit_checklist\` to verify your setup is correct.
|
|
@@ -2969,14 +2989,13 @@ async function getTheme(args) {
|
|
|
2969
2989
|
|
|
2970
2990
|
## Usage in Components
|
|
2971
2991
|
\`\`\`tsx
|
|
2972
|
-
// Access theme values in a PDFx component
|
|
2973
|
-
|
|
2992
|
+
// Access theme values in a PDFx component via the usePdfxTheme hook.
|
|
2993
|
+
// @pdfx/shared is workspace-only — do NOT import from it in your components.
|
|
2994
|
+
import { View, Text } from '@react-pdf/renderer';
|
|
2995
|
+
import { usePdfxTheme } from '../lib/pdfx-theme-context';
|
|
2974
2996
|
|
|
2975
|
-
|
|
2976
|
-
theme
|
|
2977
|
-
}
|
|
2978
|
-
|
|
2979
|
-
export function MyComponent({ theme }: Props) {
|
|
2997
|
+
export function MyComponent() {
|
|
2998
|
+
const theme = usePdfxTheme();
|
|
2980
2999
|
return (
|
|
2981
3000
|
<View style={{ backgroundColor: theme.colors.background }}>
|
|
2982
3001
|
<Text style={{ color: theme.colors.foreground, fontSize: theme.typography.body.fontSize }}>
|
|
@@ -3262,8 +3281,35 @@ async function initMcpConfig(opts) {
|
|
|
3262
3281
|
await mkdir(configDir, { recursive: true });
|
|
3263
3282
|
}
|
|
3264
3283
|
const merged = mergeDeep(existing, client.config);
|
|
3265
|
-
|
|
3266
|
-
|
|
3284
|
+
const configJson = `${JSON.stringify(merged, null, 2)}
|
|
3285
|
+
`;
|
|
3286
|
+
try {
|
|
3287
|
+
const parsed = JSON.parse(configJson);
|
|
3288
|
+
if (!isPdfxAlreadyConfigured(parsed)) {
|
|
3289
|
+
console.error(
|
|
3290
|
+
chalk8.red(
|
|
3291
|
+
"\n Config merge failed \u2014 pdfx entry not found in merged output. Please report this bug.\n"
|
|
3292
|
+
)
|
|
3293
|
+
);
|
|
3294
|
+
process.exit(1);
|
|
3295
|
+
}
|
|
3296
|
+
} catch {
|
|
3297
|
+
console.error(chalk8.red("\n Config merge produced invalid JSON. Please report this bug.\n"));
|
|
3298
|
+
process.exit(1);
|
|
3299
|
+
}
|
|
3300
|
+
await writeFile2(configPath, configJson, "utf-8");
|
|
3301
|
+
try {
|
|
3302
|
+
const written = await readFile(configPath, "utf-8");
|
|
3303
|
+
JSON.parse(written);
|
|
3304
|
+
} catch {
|
|
3305
|
+
console.error(
|
|
3306
|
+
chalk8.red(
|
|
3307
|
+
`
|
|
3308
|
+
Warning: ${client.configPath} was written but could not be read back as valid JSON.
|
|
3309
|
+
`
|
|
3310
|
+
)
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3267
3313
|
process.stdout.write(`
|
|
3268
3314
|
\u2713 Wrote MCP configuration to ${client.configPath}
|
|
3269
3315
|
`);
|
|
@@ -3309,7 +3355,7 @@ import prompts6 from "prompts";
|
|
|
3309
3355
|
|
|
3310
3356
|
// src/skills-content.ts
|
|
3311
3357
|
var PDFX_SKILLS_CONTENT = `# PDFx \u2014 AI Context Guide
|
|
3312
|
-
# Version: 1.
|
|
3358
|
+
# Version: 1.1 | Updated: 2026 | License: MIT
|
|
3313
3359
|
# Or run: npx pdfx-cli@latest skills init (handles editor-specific paths & frontmatter)
|
|
3314
3360
|
|
|
3315
3361
|
## What is PDFx?
|
|
@@ -3364,10 +3410,14 @@ export function MyDocument() {
|
|
|
3364
3410
|
<Page size="A4" style={{ padding: 40 }}>
|
|
3365
3411
|
<Heading level={1}>Invoice #001</Heading>
|
|
3366
3412
|
<Text>Thank you for your business.</Text>
|
|
3367
|
-
<Table
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3413
|
+
<Table variant="grid" zebraStripe>
|
|
3414
|
+
<TableHeader>
|
|
3415
|
+
<TableRow><TableCell header>Item</TableCell><TableCell header>Price</TableCell></TableRow>
|
|
3416
|
+
</TableHeader>
|
|
3417
|
+
<TableBody>
|
|
3418
|
+
<TableRow><TableCell>Design</TableCell><TableCell>$4,800</TableCell></TableRow>
|
|
3419
|
+
</TableBody>
|
|
3420
|
+
</Table>
|
|
3371
3421
|
</Page>
|
|
3372
3422
|
</Document>
|
|
3373
3423
|
);
|
|
@@ -3403,11 +3453,14 @@ CRITICAL: These are the EXACT props. Do not invent additional props.
|
|
|
3403
3453
|
import { Heading } from '@/components/pdfx/heading/pdfx-heading';
|
|
3404
3454
|
<Heading
|
|
3405
3455
|
level={1} // 1 | 2 | 3 | 4 | 5 | 6 \u2014 default: 1
|
|
3406
|
-
align="left" // 'left' | 'center' | 'right'
|
|
3407
|
-
weight="bold" // 'normal' | 'bold' \u2014 default: 'bold'
|
|
3408
|
-
tracking="normal" // 'tight' | 'normal' | 'wide' \u2014 default: 'normal'
|
|
3409
|
-
color="#000" // string \u2014 default: theme.
|
|
3410
|
-
|
|
3456
|
+
align="left" // 'left' | 'center' | 'right'
|
|
3457
|
+
weight="bold" // 'normal' | 'medium' | 'semibold' | 'bold' \u2014 default: 'bold'
|
|
3458
|
+
tracking="normal" // 'tighter' | 'tight' | 'normal' | 'wide' | 'wider' \u2014 default: 'normal'
|
|
3459
|
+
color="#000" // string \u2014 default: theme foreground. Supports theme color keys.
|
|
3460
|
+
transform="uppercase" // 'uppercase' | 'lowercase' | 'capitalize'
|
|
3461
|
+
noMargin // boolean \u2014 removes top/bottom margin
|
|
3462
|
+
keepWithNext // boolean \u2014 default: true, prevents orphaned heading
|
|
3463
|
+
style={...} // Style override
|
|
3411
3464
|
>
|
|
3412
3465
|
My Heading
|
|
3413
3466
|
</Heading>
|
|
@@ -3417,13 +3470,15 @@ import { Heading } from '@/components/pdfx/heading/pdfx-heading';
|
|
|
3417
3470
|
\`\`\`tsx
|
|
3418
3471
|
import { Text } from '@/components/pdfx/text/pdfx-text';
|
|
3419
3472
|
<Text
|
|
3420
|
-
|
|
3473
|
+
variant="base" // 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' \u2014 default: 'base'
|
|
3421
3474
|
weight="normal" // 'normal' | 'medium' | 'semibold' | 'bold' \u2014 default: 'normal'
|
|
3422
|
-
color="#000" // string
|
|
3475
|
+
color="#000" // string \u2014 supports theme color keys
|
|
3423
3476
|
align="left" // 'left' | 'center' | 'right' | 'justify'
|
|
3424
3477
|
italic // boolean
|
|
3425
|
-
|
|
3426
|
-
|
|
3478
|
+
decoration="none" // 'underline' | 'line-through' | 'none'
|
|
3479
|
+
transform="uppercase" // 'uppercase' | 'lowercase' | 'capitalize'
|
|
3480
|
+
noMargin // boolean \u2014 removes bottom margin
|
|
3481
|
+
style={...} // Style override
|
|
3427
3482
|
>
|
|
3428
3483
|
Paragraph text here.
|
|
3429
3484
|
</Text>
|
|
@@ -3434,8 +3489,11 @@ import { Text } from '@/components/pdfx/text/pdfx-text';
|
|
|
3434
3489
|
import { Link } from '@/components/pdfx/link/pdfx-link';
|
|
3435
3490
|
<Link
|
|
3436
3491
|
href="https://example.com" // string \u2014 required
|
|
3437
|
-
|
|
3438
|
-
|
|
3492
|
+
variant="default" // 'default' | 'muted' | 'primary' \u2014 default: 'default'
|
|
3493
|
+
underline="always" // 'always' | 'none' \u2014 default: 'always'
|
|
3494
|
+
align="left" // 'left' | 'center' | 'right'
|
|
3495
|
+
color="#0000ff" // string \u2014 overrides variant color
|
|
3496
|
+
style={...} // Style override
|
|
3439
3497
|
>
|
|
3440
3498
|
Click here
|
|
3441
3499
|
</Link>
|
|
@@ -3445,10 +3503,12 @@ import { Link } from '@/components/pdfx/link/pdfx-link';
|
|
|
3445
3503
|
\`\`\`tsx
|
|
3446
3504
|
import { Divider } from '@/components/pdfx/divider/pdfx-divider';
|
|
3447
3505
|
<Divider
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3506
|
+
spacing="md" // 'none' | 'sm' | 'md' | 'lg' \u2014 default: 'md'
|
|
3507
|
+
variant="solid" // 'solid' | 'dashed' | 'dotted' \u2014 default: 'solid'
|
|
3508
|
+
thickness="thin" // 'thin' | 'medium' | 'thick' \u2014 default: 'thin'
|
|
3509
|
+
color="#e4e4e7" // string \u2014 default: theme border color
|
|
3510
|
+
label="or" // string \u2014 text centered in divider line
|
|
3511
|
+
width="80%" // string | number \u2014 constrain width
|
|
3452
3512
|
/>
|
|
3453
3513
|
\`\`\`
|
|
3454
3514
|
|
|
@@ -3462,11 +3522,12 @@ import { PageBreak } from '@/components/pdfx/page-break/pdfx-page-break';
|
|
|
3462
3522
|
\`\`\`tsx
|
|
3463
3523
|
import { Stack } from '@/components/pdfx/stack/pdfx-stack';
|
|
3464
3524
|
<Stack
|
|
3465
|
-
direction="
|
|
3466
|
-
gap=
|
|
3467
|
-
align="
|
|
3468
|
-
justify="
|
|
3469
|
-
wrap
|
|
3525
|
+
direction="vertical" // 'vertical' | 'horizontal' \u2014 default: 'vertical'
|
|
3526
|
+
gap="md" // 'none' | 'sm' | 'md' | 'lg' | 'xl' \u2014 default: 'md'
|
|
3527
|
+
align="start" // 'start' | 'center' | 'end' | 'stretch'
|
|
3528
|
+
justify="start" // 'start' | 'center' | 'end' | 'between' | 'around'
|
|
3529
|
+
wrap // boolean \u2014 flex-wrap
|
|
3530
|
+
noWrap // boolean \u2014 prevent page split
|
|
3470
3531
|
>
|
|
3471
3532
|
{children}
|
|
3472
3533
|
</Stack>
|
|
@@ -3476,33 +3537,43 @@ import { Stack } from '@/components/pdfx/stack/pdfx-stack';
|
|
|
3476
3537
|
\`\`\`tsx
|
|
3477
3538
|
import { Section } from '@/components/pdfx/section/pdfx-section';
|
|
3478
3539
|
<Section
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
background="#f9f9f9"
|
|
3540
|
+
spacing="md" // 'none' | 'sm' | 'md' | 'lg' | 'xl' \u2014 default: 'md'
|
|
3541
|
+
padding="md" // 'none' | 'sm' | 'md' | 'lg' \u2014 optional
|
|
3542
|
+
variant="default" // 'default' | 'callout' | 'highlight' | 'card'
|
|
3543
|
+
border // boolean \u2014 adds border (default variant only)
|
|
3544
|
+
background="#f9f9f9" // string \u2014 background color
|
|
3545
|
+
accentColor="#2563eb" // string \u2014 left border color for callout/highlight
|
|
3546
|
+
noWrap // boolean \u2014 prevent page split
|
|
3547
|
+
style={...} // Style override
|
|
3484
3548
|
>
|
|
3485
3549
|
{children}
|
|
3486
3550
|
</Section>
|
|
3487
3551
|
\`\`\`
|
|
3488
3552
|
|
|
3489
|
-
### Table
|
|
3553
|
+
### Table (composable API)
|
|
3490
3554
|
\`\`\`tsx
|
|
3491
|
-
import { Table } from '@/components/pdfx/table/pdfx-table';
|
|
3555
|
+
import { Table, TableHeader, TableBody, TableFooter, TableRow, TableCell } from '@/components/pdfx/table/pdfx-table';
|
|
3492
3556
|
<Table
|
|
3493
|
-
|
|
3494
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3557
|
+
variant="line" // 'line' | 'grid' | 'minimal' | 'striped' | 'compact' | 'bordered' | 'primary-header'
|
|
3558
|
+
zebraStripe // boolean \u2014 alternating row background
|
|
3559
|
+
noWrap // boolean \u2014 prevent page split
|
|
3560
|
+
>
|
|
3561
|
+
<TableHeader>
|
|
3562
|
+
<TableRow>
|
|
3563
|
+
<TableCell header>Column A</TableCell>
|
|
3564
|
+
<TableCell header>Column B</TableCell>
|
|
3565
|
+
</TableRow>
|
|
3566
|
+
</TableHeader>
|
|
3567
|
+
<TableBody>
|
|
3568
|
+
<TableRow>
|
|
3569
|
+
<TableCell>R1C1</TableCell>
|
|
3570
|
+
<TableCell align="right" width={80}>R1C2</TableCell>
|
|
3571
|
+
</TableRow>
|
|
3572
|
+
</TableBody>
|
|
3573
|
+
</Table>
|
|
3503
3574
|
\`\`\`
|
|
3504
3575
|
|
|
3505
|
-
### DataTable
|
|
3576
|
+
### DataTable (declarative API)
|
|
3506
3577
|
\`\`\`tsx
|
|
3507
3578
|
import { DataTable } from '@/components/pdfx/data-table/pdfx-data-table';
|
|
3508
3579
|
<DataTable
|
|
@@ -3511,62 +3582,90 @@ import { DataTable } from '@/components/pdfx/data-table/pdfx-data-table';
|
|
|
3511
3582
|
{ key: 'amount', header: 'Amount', width: 1, align: 'right' },
|
|
3512
3583
|
]}
|
|
3513
3584
|
data={[{ name: 'Item A', amount: '$100' }]}
|
|
3514
|
-
striped
|
|
3515
|
-
|
|
3516
|
-
compact
|
|
3585
|
+
variant="grid" // 'line' | 'grid' | 'minimal' | 'striped' | 'compact' | 'bordered' | 'primary-header'
|
|
3586
|
+
stripe // boolean \u2014 alternating row background
|
|
3587
|
+
size="default" // 'default' | 'compact'
|
|
3588
|
+
footer={{ amount: '$100' }} // partial record for footer row
|
|
3589
|
+
noWrap // boolean \u2014 prevent page split
|
|
3517
3590
|
/>
|
|
3518
3591
|
\`\`\`
|
|
3592
|
+
DataTable render prop: \`render\` and \`renderFooter\` must return @react-pdf/renderer elements
|
|
3593
|
+
(Text, View, Image) \u2014 NOT HTML elements. TypeScript accepts ReactNode but DOM elements crash.
|
|
3519
3594
|
|
|
3520
|
-
###
|
|
3595
|
+
### PdfList
|
|
3521
3596
|
\`\`\`tsx
|
|
3522
|
-
import {
|
|
3523
|
-
<
|
|
3524
|
-
items={[
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3597
|
+
import { PdfList } from '@/components/pdfx/list/pdfx-list';
|
|
3598
|
+
<PdfList
|
|
3599
|
+
items={[
|
|
3600
|
+
{ text: 'First item' },
|
|
3601
|
+
{ text: 'Second item', description: 'Details here' },
|
|
3602
|
+
{ text: 'Checked item', checked: true },
|
|
3603
|
+
{ text: 'Parent', children: [{ text: 'Nested' }] },
|
|
3604
|
+
]}
|
|
3605
|
+
variant="bullet" // 'bullet' | 'numbered' | 'checklist' | 'icon' | 'multi-level' | 'descriptive'
|
|
3606
|
+
gap="sm" // 'xs' | 'sm' | 'md' \u2014 default: 'sm'
|
|
3607
|
+
noWrap // boolean \u2014 prevent page split
|
|
3529
3608
|
/>
|
|
3530
3609
|
\`\`\`
|
|
3531
3610
|
|
|
3532
|
-
###
|
|
3611
|
+
### PdfCard
|
|
3533
3612
|
\`\`\`tsx
|
|
3534
|
-
import {
|
|
3535
|
-
<
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
borderRadius={4} // number in pt
|
|
3613
|
+
import { PdfCard } from '@/components/pdfx/card/pdfx-card';
|
|
3614
|
+
<PdfCard
|
|
3615
|
+
title="Card Title" // string \u2014 rendered with border-bottom
|
|
3616
|
+
variant="default" // 'default' | 'bordered' | 'muted'
|
|
3617
|
+
padding="md" // 'sm' | 'md' | 'lg' \u2014 default: 'md'
|
|
3618
|
+
wrap={false} // boolean \u2014 default: false (prevents page split)
|
|
3619
|
+
style={...} // Style override
|
|
3542
3620
|
>
|
|
3543
3621
|
{children}
|
|
3544
|
-
</
|
|
3622
|
+
</PdfCard>
|
|
3545
3623
|
\`\`\`
|
|
3546
3624
|
|
|
3547
|
-
###
|
|
3625
|
+
### PdfForm (read-only fillable form for PDFs)
|
|
3548
3626
|
\`\`\`tsx
|
|
3549
|
-
import {
|
|
3550
|
-
<
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
{
|
|
3627
|
+
import { PdfForm } from '@/components/pdfx/form/pdfx-form';
|
|
3628
|
+
<PdfForm
|
|
3629
|
+
title="Application Form"
|
|
3630
|
+
subtitle="Please fill in all fields"
|
|
3631
|
+
groups={[
|
|
3632
|
+
{
|
|
3633
|
+
title: 'Personal Information',
|
|
3634
|
+
layout: 'two-column', // 'single' | 'two-column' | 'three-column'
|
|
3635
|
+
fields: [
|
|
3636
|
+
{ label: 'Full Name', hint: 'As on ID', height: 24 },
|
|
3637
|
+
{ label: 'Email' },
|
|
3638
|
+
],
|
|
3639
|
+
},
|
|
3640
|
+
{
|
|
3641
|
+
title: 'Notes',
|
|
3642
|
+
fields: [{ label: 'Additional Info', height: 60 }],
|
|
3643
|
+
},
|
|
3555
3644
|
]}
|
|
3556
|
-
|
|
3557
|
-
|
|
3645
|
+
variant="underline" // 'underline' | 'box' | 'outlined' | 'ghost'
|
|
3646
|
+
labelPosition="above" // 'above' | 'left' \u2014 default: 'above'
|
|
3647
|
+
noWrap // boolean
|
|
3558
3648
|
/>
|
|
3559
3649
|
\`\`\`
|
|
3560
3650
|
|
|
3561
|
-
###
|
|
3651
|
+
### PdfSignatureBlock
|
|
3562
3652
|
\`\`\`tsx
|
|
3563
3653
|
import { PdfSignatureBlock } from '@/components/pdfx/signature/pdfx-signature';
|
|
3654
|
+
// Single signature
|
|
3655
|
+
<PdfSignatureBlock
|
|
3656
|
+
variant="single" // 'single' | 'double' | 'inline' \u2014 default: 'single'
|
|
3657
|
+
label="Signature" // string \u2014 label above line
|
|
3658
|
+
name="Sarah Chen" // string \u2014 printed name below line
|
|
3659
|
+
title="Engineering Lead" // string \u2014 title/role below name
|
|
3660
|
+
date="2024-12-12" // string \u2014 formatted date
|
|
3661
|
+
/>
|
|
3662
|
+
// Double signature
|
|
3564
3663
|
<PdfSignatureBlock
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3664
|
+
variant="double"
|
|
3665
|
+
signers={[
|
|
3666
|
+
{ label: 'Authorized by', name: 'John', title: 'CEO', date: '2024-12-12' },
|
|
3667
|
+
{ label: 'Approved by', name: 'Jane', title: 'CFO', date: '2024-12-12' },
|
|
3668
|
+
]}
|
|
3570
3669
|
/>
|
|
3571
3670
|
\`\`\`
|
|
3572
3671
|
|
|
@@ -3574,10 +3673,18 @@ import { PdfSignatureBlock } from '@/components/pdfx/signature/pdfx-signature';
|
|
|
3574
3673
|
\`\`\`tsx
|
|
3575
3674
|
import { PageHeader } from '@/components/pdfx/page-header/pdfx-page-header';
|
|
3576
3675
|
<PageHeader
|
|
3577
|
-
title="Document Title"
|
|
3578
|
-
subtitle="Subtitle
|
|
3579
|
-
|
|
3580
|
-
|
|
3676
|
+
title="Document Title" // string \u2014 required
|
|
3677
|
+
subtitle="Subtitle" // string
|
|
3678
|
+
rightText="INV-001" // string \u2014 right-aligned text
|
|
3679
|
+
rightSubText="Due: Jan 31" // string \u2014 right sub-text
|
|
3680
|
+
variant="simple" // 'simple' | 'centered' | 'minimal' | 'branded' | 'logo-left' | 'logo-right' | 'two-column'
|
|
3681
|
+
logo={<PdfImage src="..." />} // ReactNode \u2014 for logo-left/logo-right
|
|
3682
|
+
background="#18181b" // string \u2014 background color (branded)
|
|
3683
|
+
titleColor="#fff" // string \u2014 title text color
|
|
3684
|
+
address="123 Main St" // string \u2014 for two-column variant
|
|
3685
|
+
phone="+1-555-0100" // string
|
|
3686
|
+
email="hello@acme.com" // string
|
|
3687
|
+
fixed // boolean \u2014 repeat on every page
|
|
3581
3688
|
/>
|
|
3582
3689
|
\`\`\`
|
|
3583
3690
|
|
|
@@ -3585,10 +3692,19 @@ import { PageHeader } from '@/components/pdfx/page-header/pdfx-page-header';
|
|
|
3585
3692
|
\`\`\`tsx
|
|
3586
3693
|
import { PageFooter } from '@/components/pdfx/page-footer/pdfx-page-footer';
|
|
3587
3694
|
<PageFooter
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3695
|
+
leftText="\xA9 2024 Acme Corp" // string
|
|
3696
|
+
centerText="Confidential" // string
|
|
3697
|
+
rightText="Page 1 of 1" // string
|
|
3698
|
+
variant="simple" // 'simple' | 'centered' | 'branded' | 'minimal' | 'three-column' | 'detailed'
|
|
3699
|
+
background="#18181b" // string
|
|
3700
|
+
textColor="#fff" // string
|
|
3701
|
+
address="123 Main St" // string \u2014 for three-column/detailed
|
|
3702
|
+
phone="+1-555-0100" // string
|
|
3703
|
+
email="hello@acme.com" // string
|
|
3704
|
+
website="https://acme.com" // string
|
|
3705
|
+
fixed // boolean \u2014 repeat on every page
|
|
3706
|
+
sticky // boolean \u2014 absolute position at page bottom
|
|
3707
|
+
pagePadding={40} // number \u2014 offset for sticky positioning
|
|
3592
3708
|
/>
|
|
3593
3709
|
\`\`\`
|
|
3594
3710
|
|
|
@@ -3598,8 +3714,10 @@ import { Badge } from '@/components/pdfx/badge/pdfx-badge';
|
|
|
3598
3714
|
// Use label prop OR children (string only \u2014 not a React node)
|
|
3599
3715
|
<Badge
|
|
3600
3716
|
label="PAID" // string \u2014 preferred API
|
|
3601
|
-
variant="success" // 'default' | 'success' | 'warning' | '
|
|
3717
|
+
variant="success" // 'default' | 'primary' | 'success' | 'warning' | 'destructive' | 'info' | 'outline'
|
|
3602
3718
|
size="md" // 'sm' | 'md' | 'lg'
|
|
3719
|
+
background="#000" // string \u2014 override background
|
|
3720
|
+
color="#fff" // string \u2014 override text color
|
|
3603
3721
|
/>
|
|
3604
3722
|
// OR
|
|
3605
3723
|
<Badge variant="success">PAID</Badge>
|
|
@@ -3610,12 +3728,17 @@ import { Badge } from '@/components/pdfx/badge/pdfx-badge';
|
|
|
3610
3728
|
import { KeyValue } from '@/components/pdfx/key-value/pdfx-key-value';
|
|
3611
3729
|
<KeyValue
|
|
3612
3730
|
items={[
|
|
3613
|
-
{
|
|
3614
|
-
{
|
|
3731
|
+
{ key: 'Invoice #', value: 'INV-001' },
|
|
3732
|
+
{ key: 'Due Date', value: 'Jan 31, 2025', valueColor: 'destructive' },
|
|
3615
3733
|
]}
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3734
|
+
direction="horizontal" // 'horizontal' | 'vertical' \u2014 default: 'horizontal'
|
|
3735
|
+
divided // boolean \u2014 dividers between rows
|
|
3736
|
+
size="md" // 'sm' | 'md' | 'lg' \u2014 default: 'md'
|
|
3737
|
+
labelFlex={1} // number \u2014 flex ratio for label column
|
|
3738
|
+
labelColor="#666" // string
|
|
3739
|
+
valueColor="#000" // string
|
|
3740
|
+
boldValue // boolean \u2014 bold all values
|
|
3741
|
+
noWrap // boolean
|
|
3619
3742
|
/>
|
|
3620
3743
|
\`\`\`
|
|
3621
3744
|
|
|
@@ -3625,7 +3748,7 @@ import { KeepTogether } from '@/components/pdfx/keep-together/pdfx-keep-together
|
|
|
3625
3748
|
// Prevents page breaks inside its children
|
|
3626
3749
|
<KeepTogether>
|
|
3627
3750
|
<Heading level={3}>Section that must not split</Heading>
|
|
3628
|
-
<Table
|
|
3751
|
+
<Table variant="grid">...</Table>
|
|
3629
3752
|
</KeepTogether>
|
|
3630
3753
|
\`\`\`
|
|
3631
3754
|
|
|
@@ -3633,74 +3756,102 @@ import { KeepTogether } from '@/components/pdfx/keep-together/pdfx-keep-together
|
|
|
3633
3756
|
\`\`\`tsx
|
|
3634
3757
|
import { PdfImage } from '@/components/pdfx/pdf-image/pdfx-pdf-image';
|
|
3635
3758
|
<PdfImage
|
|
3636
|
-
src="https://example.com/image.png" // string |
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3759
|
+
src="https://example.com/image.png" // string | { uri, method?, headers?, body? } \u2014 required
|
|
3760
|
+
variant="default" // 'default' | 'full-width' | 'thumbnail' | 'avatar' | 'cover' | 'bordered' | 'rounded'
|
|
3761
|
+
width={200} // number | string in pt
|
|
3762
|
+
height={150} // number | string \u2014 auto from aspect ratio if omitted
|
|
3763
|
+
fit="contain" // 'cover' | 'contain' | 'fill' | 'none' \u2014 default varies by variant
|
|
3764
|
+
position="50% 50%" // string \u2014 object-position
|
|
3765
|
+
caption="Figure 1" // string \u2014 centered below image
|
|
3766
|
+
aspectRatio={16/9} // number \u2014 compute height from width
|
|
3767
|
+
borderRadius={4} // number
|
|
3768
|
+
noWrap // boolean \u2014 default: true
|
|
3642
3769
|
/>
|
|
3643
3770
|
\`\`\`
|
|
3644
3771
|
|
|
3645
|
-
###
|
|
3772
|
+
### PdfGraph
|
|
3646
3773
|
\`\`\`tsx
|
|
3647
|
-
import {
|
|
3648
|
-
<
|
|
3649
|
-
|
|
3774
|
+
import { PdfGraph } from '@/components/pdfx/graph/pdfx-graph';
|
|
3775
|
+
<PdfGraph
|
|
3776
|
+
variant="bar" // 'bar' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'donut'
|
|
3650
3777
|
data={[
|
|
3651
3778
|
{ label: 'Q1', value: 4200 },
|
|
3652
3779
|
{ label: 'Q2', value: 6100 },
|
|
3653
3780
|
]}
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3658
|
-
|
|
3659
|
-
|
|
3781
|
+
title="Revenue" // string
|
|
3782
|
+
subtitle="FY2024" // string
|
|
3783
|
+
xLabel="Quarter" // string \u2014 x-axis label
|
|
3784
|
+
yLabel="Revenue ($)" // string \u2014 y-axis label
|
|
3785
|
+
width={420} // number \u2014 default: 420 (or auto with fullWidth)
|
|
3786
|
+
height={260} // number \u2014 default: 260
|
|
3787
|
+
fullWidth // boolean \u2014 auto-calculates width from page margins
|
|
3788
|
+
containerPadding={12} // number \u2014 for fullWidth: outer container padding
|
|
3789
|
+
wrapperPadding={12} // number \u2014 for fullWidth: wrapper padding
|
|
3790
|
+
colors={['#18181b', '#71717a']} // string[] \u2014 color palette
|
|
3791
|
+
showValues // boolean \u2014 show numeric labels on bars/points
|
|
3792
|
+
showGrid // boolean \u2014 default: true
|
|
3793
|
+
legend="bottom" // 'bottom' | 'right' | 'none' \u2014 default: 'bottom'
|
|
3794
|
+
centerLabel="$1.2M" // string \u2014 for donut: text in center hole
|
|
3795
|
+
showDots // boolean \u2014 default: true (line/area only)
|
|
3796
|
+
smooth // boolean \u2014 bezier curves (line/area only)
|
|
3797
|
+
yTicks={5} // number \u2014 Y-axis tick count
|
|
3798
|
+
noWrap // boolean \u2014 default: true
|
|
3660
3799
|
/>
|
|
3661
3800
|
\`\`\`
|
|
3801
|
+
Multi-series data: \`data={[{ name: 'Series A', data: [...] }, { name: 'Series B', data: [...] }]}\`
|
|
3802
|
+
Graph width utilities: \`getGraphWidth(theme, opts?)\`, \`GRAPH_SAFE_WIDTHS\`, \`A4_WIDTH\` are exported.
|
|
3662
3803
|
|
|
3663
|
-
###
|
|
3804
|
+
### PdfPageNumber
|
|
3664
3805
|
\`\`\`tsx
|
|
3665
|
-
import {
|
|
3666
|
-
<
|
|
3667
|
-
format="Page {
|
|
3668
|
-
|
|
3669
|
-
|
|
3806
|
+
import { PdfPageNumber } from '@/components/pdfx/page-number/pdfx-page-number';
|
|
3807
|
+
<PdfPageNumber
|
|
3808
|
+
format="Page {page} of {total}" // string \u2014 use {page} and {total} placeholders
|
|
3809
|
+
align="center" // 'left' | 'center' | 'right' \u2014 default: 'center'
|
|
3810
|
+
size="sm" // 'xs' | 'sm' | 'md' \u2014 default: 'sm'
|
|
3811
|
+
fixed // boolean \u2014 repeats on every page
|
|
3812
|
+
muted // boolean \u2014 default: true, uses mutedForeground color
|
|
3670
3813
|
/>
|
|
3671
3814
|
\`\`\`
|
|
3672
3815
|
|
|
3673
|
-
###
|
|
3816
|
+
### PdfWatermark
|
|
3674
3817
|
\`\`\`tsx
|
|
3675
3818
|
import { PdfWatermark } from '@/components/pdfx/watermark/pdfx-watermark';
|
|
3676
3819
|
<PdfWatermark
|
|
3677
3820
|
text="CONFIDENTIAL" // string \u2014 required
|
|
3678
|
-
opacity={0.
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3821
|
+
opacity={0.15} // number 0\u20131 \u2014 default: 0.15
|
|
3822
|
+
fontSize={60} // number \u2014 default: 60
|
|
3823
|
+
color="mutedForeground" // string \u2014 default: 'mutedForeground' (theme key or hex)
|
|
3824
|
+
angle={-45} // number in degrees \u2014 default: -45
|
|
3825
|
+
position="center" // 'center' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
|
|
3826
|
+
fixed // boolean \u2014 default: true (repeats on every page)
|
|
3682
3827
|
/>
|
|
3683
3828
|
\`\`\`
|
|
3684
3829
|
|
|
3685
|
-
###
|
|
3830
|
+
### PdfQRCode
|
|
3686
3831
|
\`\`\`tsx
|
|
3687
3832
|
import { PdfQRCode } from '@/components/pdfx/qrcode/pdfx-qrcode';
|
|
3688
3833
|
<PdfQRCode
|
|
3689
3834
|
value="https://example.com" // string \u2014 required
|
|
3690
|
-
size={
|
|
3691
|
-
|
|
3835
|
+
size={100} // number \u2014 default: 100
|
|
3836
|
+
color="#000000" // string \u2014 default: '#000000'
|
|
3837
|
+
backgroundColor="#ffffff" // string \u2014 default: '#ffffff'
|
|
3838
|
+
errorLevel="M" // 'L' | 'M' | 'Q' | 'H' \u2014 default: 'M'
|
|
3839
|
+
margin={2} // number \u2014 quiet zone modules \u2014 default: 2
|
|
3840
|
+
caption="Scan me" // string \u2014 text below QR code
|
|
3692
3841
|
/>
|
|
3693
3842
|
\`\`\`
|
|
3694
3843
|
|
|
3695
|
-
###
|
|
3844
|
+
### PdfAlert
|
|
3696
3845
|
\`\`\`tsx
|
|
3697
|
-
import {
|
|
3698
|
-
<
|
|
3699
|
-
variant="info"
|
|
3700
|
-
title="Note"
|
|
3846
|
+
import { PdfAlert } from '@/components/pdfx/alert/pdfx-alert';
|
|
3847
|
+
<PdfAlert
|
|
3848
|
+
variant="info" // 'info' | 'success' | 'warning' | 'error' \u2014 default: 'info'
|
|
3849
|
+
title="Note" // string \u2014 optional bold title
|
|
3850
|
+
showIcon // boolean \u2014 default: true
|
|
3851
|
+
showBorder // boolean \u2014 default: true (left border)
|
|
3701
3852
|
>
|
|
3702
3853
|
This is an informational note.
|
|
3703
|
-
</
|
|
3854
|
+
</PdfAlert>
|
|
3704
3855
|
\`\`\`
|
|
3705
3856
|
|
|
3706
3857
|
---
|
|
@@ -3713,20 +3864,18 @@ npx pdfx-cli@latest block add <block-name>
|
|
|
3713
3864
|
\`\`\`
|
|
3714
3865
|
|
|
3715
3866
|
### Invoice Blocks
|
|
3716
|
-
- invoice-
|
|
3717
|
-
- invoice-minimal \u2014
|
|
3718
|
-
- invoice-
|
|
3719
|
-
- invoice-
|
|
3867
|
+
- invoice-classic \u2014 Professional with logo-left header and zebra-striped grid table
|
|
3868
|
+
- invoice-minimal \u2014 Clean stripped-down, typography-focused layout
|
|
3869
|
+
- invoice-modern \u2014 Full-width banner header, horizontal meta strip
|
|
3870
|
+
- invoice-corporate \u2014 Formal layout with purchase order fields and signatures
|
|
3871
|
+
- invoice-creative \u2014 Accent sidebar, vibrant color highlights
|
|
3872
|
+
- invoice-consultant \u2014 Hourly rate breakdown, project summary
|
|
3720
3873
|
|
|
3721
3874
|
### Report Blocks
|
|
3722
|
-
- report-
|
|
3723
|
-
- report-
|
|
3724
|
-
- report-
|
|
3725
|
-
- report-
|
|
3726
|
-
- report-technical \u2014 Code-friendly, monospace sections
|
|
3727
|
-
|
|
3728
|
-
### Contract Block
|
|
3729
|
-
- contract-standard \u2014 Signature page, numbered clauses, party info
|
|
3875
|
+
- report-financial \u2014 KPI cards, trend chart, delivery table
|
|
3876
|
+
- report-marketing \u2014 Channel performance, acquisition trendline
|
|
3877
|
+
- report-operations \u2014 SLA health, throughput trends, risk tracking
|
|
3878
|
+
- report-security \u2014 Vulnerability trend tracking, remediation table
|
|
3730
3879
|
|
|
3731
3880
|
Blocks are added as full React components in your project. Customize all content props.
|
|
3732
3881
|
|
|
@@ -3811,6 +3960,18 @@ npx pdfx-cli@latest skills init --platform antigravity # .antigravity/context.md
|
|
|
3811
3960
|
|
|
3812
3961
|
---
|
|
3813
3962
|
|
|
3963
|
+
## Component Naming Convention
|
|
3964
|
+
|
|
3965
|
+
PDFx uses a \`Pdf\` prefix when the component name collides with an existing widely-used React
|
|
3966
|
+
or @react-pdf/renderer export:
|
|
3967
|
+
|
|
3968
|
+
Prefixed (10): PdfAlert, PdfCard, PdfForm, PdfGraph, PdfImage, PdfList, PdfPageNumber, PdfQRCode, PdfSignatureBlock, PdfWatermark
|
|
3969
|
+
Unprefixed (14): Badge, DataTable, Divider, Heading, KeepTogether, KeyValue, Link, PageBreak, PageFooter, PageHeader, Section, Stack, Table, Text
|
|
3970
|
+
|
|
3971
|
+
When importing, always use the exact name shown in each component section above.
|
|
3972
|
+
|
|
3973
|
+
---
|
|
3974
|
+
|
|
3814
3975
|
## Common patterns
|
|
3815
3976
|
|
|
3816
3977
|
### Full invoice from scratch
|
|
@@ -3818,7 +3979,7 @@ npx pdfx-cli@latest skills init --platform antigravity # .antigravity/context.md
|
|
|
3818
3979
|
import { Document, Page } from '@react-pdf/renderer';
|
|
3819
3980
|
import { Heading } from '@/components/pdfx/heading/pdfx-heading';
|
|
3820
3981
|
import { KeyValue } from '@/components/pdfx/key-value/pdfx-key-value';
|
|
3821
|
-
import { Table } from '@/components/pdfx/table/pdfx-table';
|
|
3982
|
+
import { Table, TableHeader, TableBody, TableRow, TableCell } from '@/components/pdfx/table/pdfx-table';
|
|
3822
3983
|
import { Divider } from '@/components/pdfx/divider/pdfx-divider';
|
|
3823
3984
|
import { Badge } from '@/components/pdfx/badge/pdfx-badge';
|
|
3824
3985
|
import { PageFooter } from '@/components/pdfx/page-footer/pdfx-page-footer';
|
|
@@ -3830,24 +3991,30 @@ export function InvoiceDoc() {
|
|
|
3830
3991
|
<Heading level={1}>Invoice #INV-001</Heading>
|
|
3831
3992
|
<KeyValue
|
|
3832
3993
|
items={[
|
|
3833
|
-
{
|
|
3834
|
-
{
|
|
3994
|
+
{ key: 'Date', value: 'Jan 1, 2025' },
|
|
3995
|
+
{ key: 'Due', value: 'Jan 31, 2025' },
|
|
3835
3996
|
]}
|
|
3836
|
-
|
|
3997
|
+
direction="horizontal"
|
|
3837
3998
|
/>
|
|
3838
3999
|
<Divider spacing="md" />
|
|
3839
|
-
<Table
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
|
|
3848
|
-
|
|
4000
|
+
<Table variant="grid" zebraStripe>
|
|
4001
|
+
<TableHeader>
|
|
4002
|
+
<TableRow>
|
|
4003
|
+
<TableCell header width="60%">Description</TableCell>
|
|
4004
|
+
<TableCell header align="center">Qty</TableCell>
|
|
4005
|
+
<TableCell header align="right">Total</TableCell>
|
|
4006
|
+
</TableRow>
|
|
4007
|
+
</TableHeader>
|
|
4008
|
+
<TableBody>
|
|
4009
|
+
<TableRow>
|
|
4010
|
+
<TableCell>Design System</TableCell>
|
|
4011
|
+
<TableCell align="center">1</TableCell>
|
|
4012
|
+
<TableCell align="right">$4,800</TableCell>
|
|
4013
|
+
</TableRow>
|
|
4014
|
+
</TableBody>
|
|
4015
|
+
</Table>
|
|
3849
4016
|
<Badge label="PAID" variant="success" />
|
|
3850
|
-
<PageFooter
|
|
4017
|
+
<PageFooter leftText="Acme Corp" rightText="Page 1 of 1" />
|
|
3851
4018
|
</Page>
|
|
3852
4019
|
</Document>
|
|
3853
4020
|
);
|
|
@@ -3859,7 +4026,7 @@ export function InvoiceDoc() {
|
|
|
3859
4026
|
// Wrap anything that must stay together across page boundaries
|
|
3860
4027
|
<KeepTogether>
|
|
3861
4028
|
<Heading level={3}>Q3 Summary</Heading>
|
|
3862
|
-
<
|
|
4029
|
+
<DataTable columns={columns} data={data} variant="grid" />
|
|
3863
4030
|
</KeepTogether>
|
|
3864
4031
|
\`\`\`
|
|
3865
4032
|
|
|
@@ -3877,7 +4044,7 @@ export async function GET(req: Request) {
|
|
|
3877
4044
|
return new Response(buf, {
|
|
3878
4045
|
headers: {
|
|
3879
4046
|
'Content-Type': 'application/pdf',
|
|
3880
|
-
'Content-Disposition':
|
|
4047
|
+
'Content-Disposition': \\\`inline; filename="invoice-\\\${id}.pdf"\\\`,
|
|
3881
4048
|
},
|
|
3882
4049
|
});
|
|
3883
4050
|
}
|
|
@@ -3918,6 +4085,7 @@ export async function GET(req: Request) {
|
|
|
3918
4085
|
- DO NOT install components with npm \u2014 always use the CLI: npx pdfx-cli@latest add <name>
|
|
3919
4086
|
- DO NOT place raw text siblings next to View elements in a flex row (react-pdf constraint)
|
|
3920
4087
|
- DO NOT pass React nodes (JSX) as Badge children \u2014 only plain strings are supported
|
|
4088
|
+
- DO NOT return HTML elements from DataTable render/renderFooter \u2014 PDF elements only
|
|
3921
4089
|
|
|
3922
4090
|
---
|
|
3923
4091
|
|
package/dist/mcp/index.js
CHANGED
|
@@ -570,8 +570,21 @@ async function getAuditChecklist() {
|
|
|
570
570
|
\`\`\`
|
|
571
571
|
|
|
572
572
|
### "Invalid hook call"
|
|
573
|
-
|
|
574
|
-
|
|
573
|
+
\`@react-pdf/renderer\` renders synchronously. Hooks that depend on browser APIs or
|
|
574
|
+
async side effects do not work in the PDF render path.
|
|
575
|
+
|
|
576
|
+
**Do NOT use inside a PDFx component:**
|
|
577
|
+
- \`useEffect\` / \`useLayoutEffect\` — no browser lifecycle in PDF rendering
|
|
578
|
+
- \`useRef\` for DOM nodes — no DOM exists in the PDF render tree
|
|
579
|
+
- Any hook that calls browser globals (\`window\`, \`document\`, \`navigator\`)
|
|
580
|
+
|
|
581
|
+
**These ARE valid inside PDFx components:**
|
|
582
|
+
- PDFx framework hooks: \`usePdfxTheme()\`, \`useSafeMemo()\`
|
|
583
|
+
- Custom hooks that are pure functions with no browser-API dependencies
|
|
584
|
+
- Data passed down as props from a parent client component
|
|
585
|
+
|
|
586
|
+
If you see this error in a wrapper component you wrote, move the browser hook
|
|
587
|
+
to the nearest client component and pass the result down as a prop.
|
|
575
588
|
|
|
576
589
|
### "Text strings must be rendered inside \`<Text>\` component"
|
|
577
590
|
Wrap all string literals in \`<Text>\` from \`@react-pdf/renderer\`:
|
|
@@ -771,7 +784,7 @@ async function getComponent(args) {
|
|
|
771
784
|
const primaryContent = item.files[0]?.content ?? "";
|
|
772
785
|
const primaryPath = item.files[0]?.path ?? "";
|
|
773
786
|
const exportNames = extractAllExportNames(primaryContent);
|
|
774
|
-
const mainExport = extractExportName(primaryContent);
|
|
787
|
+
const mainExport = extractExportName(primaryContent, args.component);
|
|
775
788
|
const exportSection = exportNames.length > 0 ? dedent4`
|
|
776
789
|
## Exports
|
|
777
790
|
**Main component export:** \`${mainExport ?? exportNames[0]}\`
|
|
@@ -816,11 +829,18 @@ async function getComponent(args) {
|
|
|
816
829
|
${fileSources}
|
|
817
830
|
`);
|
|
818
831
|
}
|
|
819
|
-
function extractExportName(source) {
|
|
832
|
+
function extractExportName(source, componentName) {
|
|
820
833
|
if (!source) return null;
|
|
821
|
-
const matches = [...source.matchAll(/export\s+
|
|
822
|
-
|
|
823
|
-
|
|
834
|
+
const matches = [...source.matchAll(/export\s+function\s+([A-Z][A-Za-z0-9]*)/g)];
|
|
835
|
+
const names = matches.map((m) => m[1]).filter(Boolean);
|
|
836
|
+
if (names.length === 0) return null;
|
|
837
|
+
if (!componentName) return names[0];
|
|
838
|
+
const norm = componentName.replace(/-/g, "").toLowerCase();
|
|
839
|
+
const exact = names.find((n) => n.toLowerCase() === norm);
|
|
840
|
+
if (exact) return exact;
|
|
841
|
+
const suffix = names.find((n) => n.toLowerCase().endsWith(norm));
|
|
842
|
+
if (suffix) return suffix;
|
|
843
|
+
return names[0];
|
|
824
844
|
}
|
|
825
845
|
function extractAllExportNames(source) {
|
|
826
846
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -1029,7 +1049,7 @@ async function getInstallation(args) {
|
|
|
1029
1049
|
| TypeScript errors on \`@react-pdf/renderer\` | \`${installCmd(pm, "@react-pdf/types", true)}\` |
|
|
1030
1050
|
| "Cannot find module @/components/pdfx/..." | Run \`npx pdfx-cli@latest add <component>\` to install it |
|
|
1031
1051
|
| PDF renders blank | Ensure root returns \`<Document><Page>...</Page></Document>\` |
|
|
1032
|
-
| "Invalid hook call" |
|
|
1052
|
+
| "Invalid hook call" | Browser-only hooks (\`useEffect\`, \`useRef\` for DOM) don't work in the PDF render path — move them to a parent client component and pass the result as a prop. PDFx framework hooks (\`usePdfxTheme\`, \`useSafeMemo\`) are valid inside PDF components. |
|
|
1033
1053
|
|
|
1034
1054
|
---
|
|
1035
1055
|
Next: call \`get_audit_checklist\` to verify your setup is correct.
|
|
@@ -1153,14 +1173,13 @@ async function getTheme(args) {
|
|
|
1153
1173
|
|
|
1154
1174
|
## Usage in Components
|
|
1155
1175
|
\`\`\`tsx
|
|
1156
|
-
// Access theme values in a PDFx component
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
theme: PdfxTheme;
|
|
1161
|
-
}
|
|
1176
|
+
// Access theme values in a PDFx component via the usePdfxTheme hook.
|
|
1177
|
+
// @pdfx/shared is workspace-only — do NOT import from it in your components.
|
|
1178
|
+
import { View, Text } from '@react-pdf/renderer';
|
|
1179
|
+
import { usePdfxTheme } from '../lib/pdfx-theme-context';
|
|
1162
1180
|
|
|
1163
|
-
export function MyComponent(
|
|
1181
|
+
export function MyComponent() {
|
|
1182
|
+
const theme = usePdfxTheme();
|
|
1164
1183
|
return (
|
|
1165
1184
|
<View style={{ backgroundColor: theme.colors.background }}>
|
|
1166
1185
|
<Text style={{ color: theme.colors.foreground, fontSize: theme.typography.body.fontSize }}>
|