pdfx-cli 0.4.2 → 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.
Files changed (2) hide show
  1. package/dist/index.js +310 -161
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3281,8 +3281,35 @@ async function initMcpConfig(opts) {
3281
3281
  await mkdir(configDir, { recursive: true });
3282
3282
  }
3283
3283
  const merged = mergeDeep(existing, client.config);
3284
- await writeFile2(configPath, `${JSON.stringify(merged, null, 2)}
3285
- `, "utf-8");
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
+ }
3286
3313
  process.stdout.write(`
3287
3314
  \u2713 Wrote MCP configuration to ${client.configPath}
3288
3315
  `);
@@ -3328,7 +3355,7 @@ import prompts6 from "prompts";
3328
3355
 
3329
3356
  // src/skills-content.ts
3330
3357
  var PDFX_SKILLS_CONTENT = `# PDFx \u2014 AI Context Guide
3331
- # Version: 1.0 | Updated: 2026 | License: MIT
3358
+ # Version: 1.1 | Updated: 2026 | License: MIT
3332
3359
  # Or run: npx pdfx-cli@latest skills init (handles editor-specific paths & frontmatter)
3333
3360
 
3334
3361
  ## What is PDFx?
@@ -3383,10 +3410,14 @@ export function MyDocument() {
3383
3410
  <Page size="A4" style={{ padding: 40 }}>
3384
3411
  <Heading level={1}>Invoice #001</Heading>
3385
3412
  <Text>Thank you for your business.</Text>
3386
- <Table
3387
- headers={['Item', 'Qty', 'Price']}
3388
- rows={[['Design work', '1', '$4,800']]}
3389
- />
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>
3390
3421
  </Page>
3391
3422
  </Document>
3392
3423
  );
@@ -3422,11 +3453,14 @@ CRITICAL: These are the EXACT props. Do not invent additional props.
3422
3453
  import { Heading } from '@/components/pdfx/heading/pdfx-heading';
3423
3454
  <Heading
3424
3455
  level={1} // 1 | 2 | 3 | 4 | 5 | 6 \u2014 default: 1
3425
- align="left" // 'left' | 'center' | 'right' \u2014 default: 'left'
3426
- weight="bold" // 'normal' | 'bold' \u2014 default: 'bold'
3427
- tracking="normal" // 'tight' | 'normal' | 'wide' \u2014 default: 'normal'
3428
- color="#000" // string \u2014 default: theme.colors.foreground
3429
- gutterBottom // boolean \u2014 adds bottom margin
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
3430
3464
  >
3431
3465
  My Heading
3432
3466
  </Heading>
@@ -3436,13 +3470,15 @@ import { Heading } from '@/components/pdfx/heading/pdfx-heading';
3436
3470
  \`\`\`tsx
3437
3471
  import { Text } from '@/components/pdfx/text/pdfx-text';
3438
3472
  <Text
3439
- size="md" // 'xs' | 'sm' | 'md' | 'lg' | 'xl' \u2014 default: 'md'
3473
+ variant="base" // 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl' \u2014 default: 'base'
3440
3474
  weight="normal" // 'normal' | 'medium' | 'semibold' | 'bold' \u2014 default: 'normal'
3441
- color="#000" // string
3475
+ color="#000" // string \u2014 supports theme color keys
3442
3476
  align="left" // 'left' | 'center' | 'right' | 'justify'
3443
3477
  italic // boolean
3444
- muted // boolean \u2014 applies theme.colors.mutedForeground
3445
- gutterBottom // boolean
3478
+ decoration="none" // 'underline' | 'line-through' | 'none'
3479
+ transform="uppercase" // 'uppercase' | 'lowercase' | 'capitalize'
3480
+ noMargin // boolean \u2014 removes bottom margin
3481
+ style={...} // Style override
3446
3482
  >
3447
3483
  Paragraph text here.
3448
3484
  </Text>
@@ -3453,8 +3489,11 @@ import { Text } from '@/components/pdfx/text/pdfx-text';
3453
3489
  import { Link } from '@/components/pdfx/link/pdfx-link';
3454
3490
  <Link
3455
3491
  href="https://example.com" // string \u2014 required
3456
- size="md" // same as Text size
3457
- color="#0000ff" // default: theme.colors.accent
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
3458
3497
  >
3459
3498
  Click here
3460
3499
  </Link>
@@ -3464,10 +3503,12 @@ import { Link } from '@/components/pdfx/link/pdfx-link';
3464
3503
  \`\`\`tsx
3465
3504
  import { Divider } from '@/components/pdfx/divider/pdfx-divider';
3466
3505
  <Divider
3467
- thickness={1} // number in pt \u2014 default: 1
3468
- color="#e4e4e7" // string \u2014 default: theme.colors.border
3469
- spacing="md" // 'sm' | 'md' | 'lg' \u2014 vertical margin
3470
- style="solid" // 'solid' | 'dashed' | 'dotted'
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
3471
3512
  />
3472
3513
  \`\`\`
3473
3514
 
@@ -3481,11 +3522,12 @@ import { PageBreak } from '@/components/pdfx/page-break/pdfx-page-break';
3481
3522
  \`\`\`tsx
3482
3523
  import { Stack } from '@/components/pdfx/stack/pdfx-stack';
3483
3524
  <Stack
3484
- direction="column" // 'row' | 'column' \u2014 default: 'column'
3485
- gap={8} // number in pt \u2014 default: 0
3486
- align="flex-start" // flexbox align-items
3487
- justify="flex-start"// flexbox justify-content
3488
- wrap // boolean \u2014 flex-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
3489
3531
  >
3490
3532
  {children}
3491
3533
  </Stack>
@@ -3495,33 +3537,43 @@ import { Stack } from '@/components/pdfx/stack/pdfx-stack';
3495
3537
  \`\`\`tsx
3496
3538
  import { Section } from '@/components/pdfx/section/pdfx-section';
3497
3539
  <Section
3498
- title="Section Title" // string \u2014 optional
3499
- titleLevel={2} // 1\u20136 \u2014 default: 2
3500
- padding={16} // number | {top,right,bottom,left} \u2014 default: 0
3501
- bordered // boolean \u2014 adds border around section
3502
- background="#f9f9f9" // string \u2014 background color
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
3503
3548
  >
3504
3549
  {children}
3505
3550
  </Section>
3506
3551
  \`\`\`
3507
3552
 
3508
- ### Table
3553
+ ### Table (composable API)
3509
3554
  \`\`\`tsx
3510
- import { Table } from '@/components/pdfx/table/pdfx-table';
3555
+ import { Table, TableHeader, TableBody, TableFooter, TableRow, TableCell } from '@/components/pdfx/table/pdfx-table';
3511
3556
  <Table
3512
- headers={['Column A', 'Column B', 'Column C']} // string[] \u2014 required
3513
- rows={[['R1C1', 'R1C2', 'R1C3']]} // string[][] \u2014 required
3514
- striped // boolean \u2014 alternating row colors
3515
- bordered // boolean \u2014 cell borders
3516
- compact // boolean \u2014 smaller padding
3517
- headerBg="#18181b" // string \u2014 header background
3518
- headerColor="#fff" // string \u2014 header text color
3519
- columnWidths={[2, 1, 1]} // number[] \u2014 flex ratios
3520
- caption="Table 1" // string \u2014 caption below table
3521
- />
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>
3522
3574
  \`\`\`
3523
3575
 
3524
- ### DataTable
3576
+ ### DataTable (declarative API)
3525
3577
  \`\`\`tsx
3526
3578
  import { DataTable } from '@/components/pdfx/data-table/pdfx-data-table';
3527
3579
  <DataTable
@@ -3530,62 +3582,90 @@ import { DataTable } from '@/components/pdfx/data-table/pdfx-data-table';
3530
3582
  { key: 'amount', header: 'Amount', width: 1, align: 'right' },
3531
3583
  ]}
3532
3584
  data={[{ name: 'Item A', amount: '$100' }]}
3533
- striped
3534
- bordered
3535
- 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
3536
3590
  />
3537
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.
3538
3594
 
3539
- ### List
3595
+ ### PdfList
3540
3596
  \`\`\`tsx
3541
- import { List } from '@/components/pdfx/list/pdfx-list';
3542
- <List
3543
- items={['First item', 'Second item', 'Third item']} // string[] \u2014 required
3544
- ordered // boolean \u2014 numbered list (default: bulleted)
3545
- bullet="\u2022" // string \u2014 custom bullet character
3546
- indent={16} // number \u2014 left indent in pt
3547
- spacing="sm" // 'sm' | 'md' | 'lg' \u2014 gap between items
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
3548
3608
  />
3549
3609
  \`\`\`
3550
3610
 
3551
- ### Card
3611
+ ### PdfCard
3552
3612
  \`\`\`tsx
3553
- import { Card } from '@/components/pdfx/card/pdfx-card';
3554
- <Card
3555
- padding={16} // number \u2014 default: 16
3556
- bordered // boolean \u2014 default: true
3557
- shadow // boolean
3558
- background="#fff" // string
3559
- borderColor="#e4e4e7" // string
3560
- 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
3561
3620
  >
3562
3621
  {children}
3563
- </Card>
3622
+ </PdfCard>
3564
3623
  \`\`\`
3565
3624
 
3566
- ### Form (read-only form fields for PDFs)
3625
+ ### PdfForm (read-only fillable form for PDFs)
3567
3626
  \`\`\`tsx
3568
- import { Form } from '@/components/pdfx/form/pdfx-form';
3569
- <Form
3570
- fields={[
3571
- { label: 'Full Name', value: 'John Doe' },
3572
- { label: 'Email', value: 'john@example.com' },
3573
- { label: 'Notes', value: 'Some notes here', multiline: true },
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
+ },
3574
3644
  ]}
3575
- columns={2} // 1 | 2 \u2014 default: 1
3576
- bordered // boolean
3645
+ variant="underline" // 'underline' | 'box' | 'outlined' | 'ghost'
3646
+ labelPosition="above" // 'above' | 'left' \u2014 default: 'above'
3647
+ noWrap // boolean
3577
3648
  />
3578
3649
  \`\`\`
3579
3650
 
3580
- ### Signature
3651
+ ### PdfSignatureBlock
3581
3652
  \`\`\`tsx
3582
3653
  import { PdfSignatureBlock } from '@/components/pdfx/signature/pdfx-signature';
3654
+ // Single signature
3583
3655
  <PdfSignatureBlock
3584
- name="Sarah Chen" // string \u2014 printed name below line
3585
- title="Engineering Lead" // string \u2014 title/role below name
3586
- date="2024-12-12" // string \u2014 formatted date
3587
- lineWidth={120} // number \u2014 signature line width in pt
3588
- showDate // boolean \u2014 default: true
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
3663
+ <PdfSignatureBlock
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
+ ]}
3589
3669
  />
3590
3670
  \`\`\`
3591
3671
 
@@ -3593,10 +3673,18 @@ import { PdfSignatureBlock } from '@/components/pdfx/signature/pdfx-signature';
3593
3673
  \`\`\`tsx
3594
3674
  import { PageHeader } from '@/components/pdfx/page-header/pdfx-page-header';
3595
3675
  <PageHeader
3596
- title="Document Title"
3597
- subtitle="Subtitle or tagline"
3598
- logo={{ src: 'https://...', width: 60, height: 30 }}
3599
- bordered // boolean \u2014 adds bottom border
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
3600
3688
  />
3601
3689
  \`\`\`
3602
3690
 
@@ -3604,10 +3692,19 @@ import { PageHeader } from '@/components/pdfx/page-header/pdfx-page-header';
3604
3692
  \`\`\`tsx
3605
3693
  import { PageFooter } from '@/components/pdfx/page-footer/pdfx-page-footer';
3606
3694
  <PageFooter
3607
- left="\xA9 2024 Acme Corp"
3608
- center="Confidential"
3609
- right="Page 1 of 1"
3610
- bordered // boolean \u2014 top border
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
3611
3708
  />
3612
3709
  \`\`\`
3613
3710
 
@@ -3617,8 +3714,10 @@ import { Badge } from '@/components/pdfx/badge/pdfx-badge';
3617
3714
  // Use label prop OR children (string only \u2014 not a React node)
3618
3715
  <Badge
3619
3716
  label="PAID" // string \u2014 preferred API
3620
- variant="success" // 'default' | 'success' | 'warning' | 'error' | 'info' | 'outline'
3717
+ variant="success" // 'default' | 'primary' | 'success' | 'warning' | 'destructive' | 'info' | 'outline'
3621
3718
  size="md" // 'sm' | 'md' | 'lg'
3719
+ background="#000" // string \u2014 override background
3720
+ color="#fff" // string \u2014 override text color
3622
3721
  />
3623
3722
  // OR
3624
3723
  <Badge variant="success">PAID</Badge>
@@ -3629,12 +3728,17 @@ import { Badge } from '@/components/pdfx/badge/pdfx-badge';
3629
3728
  import { KeyValue } from '@/components/pdfx/key-value/pdfx-key-value';
3630
3729
  <KeyValue
3631
3730
  items={[
3632
- { label: 'Invoice #', value: 'INV-001' },
3633
- { label: 'Due Date', value: 'Jan 31, 2025' },
3731
+ { key: 'Invoice #', value: 'INV-001' },
3732
+ { key: 'Due Date', value: 'Jan 31, 2025', valueColor: 'destructive' },
3634
3733
  ]}
3635
- columns={2} // 1 | 2 | 3 \u2014 default: 1
3636
- labelWidth={80} // number in pt
3637
- colon // boolean \u2014 adds colon after label
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
3638
3742
  />
3639
3743
  \`\`\`
3640
3744
 
@@ -3644,7 +3748,7 @@ import { KeepTogether } from '@/components/pdfx/keep-together/pdfx-keep-together
3644
3748
  // Prevents page breaks inside its children
3645
3749
  <KeepTogether>
3646
3750
  <Heading level={3}>Section that must not split</Heading>
3647
- <Table headers={[...]} rows={[...]} />
3751
+ <Table variant="grid">...</Table>
3648
3752
  </KeepTogether>
3649
3753
  \`\`\`
3650
3754
 
@@ -3652,74 +3756,102 @@ import { KeepTogether } from '@/components/pdfx/keep-together/pdfx-keep-together
3652
3756
  \`\`\`tsx
3653
3757
  import { PdfImage } from '@/components/pdfx/pdf-image/pdfx-pdf-image';
3654
3758
  <PdfImage
3655
- src="https://example.com/image.png" // string | base64 \u2014 required
3656
- width={200} // number in pt
3657
- height={150} // optional \u2014 maintains aspect ratio if omitted
3658
- alt="Description" // string \u2014 for accessibility
3659
- objectFit="cover" // 'cover' | 'contain' | 'fill'
3660
- borderRadius={4} // number
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
3661
3769
  />
3662
3770
  \`\`\`
3663
3771
 
3664
- ### Graph
3772
+ ### PdfGraph
3665
3773
  \`\`\`tsx
3666
- import { Graph } from '@/components/pdfx/graph/pdfx-graph';
3667
- <Graph
3668
- type="bar" // 'bar' | 'line' | 'pie' | 'donut'
3774
+ import { PdfGraph } from '@/components/pdfx/graph/pdfx-graph';
3775
+ <PdfGraph
3776
+ variant="bar" // 'bar' | 'horizontal-bar' | 'line' | 'area' | 'pie' | 'donut'
3669
3777
  data={[
3670
3778
  { label: 'Q1', value: 4200 },
3671
3779
  { label: 'Q2', value: 6100 },
3672
3780
  ]}
3673
- width={400} // number in pt
3674
- height={200} // number in pt
3675
- title="Revenue" // string \u2014 optional
3676
- showValues // boolean \u2014 show value labels
3677
- showLegend // boolean
3678
- colors={['#18181b', '#71717a']} // string[] \u2014 bar/slice colors
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
3679
3799
  />
3680
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.
3681
3803
 
3682
- ### PageNumber
3804
+ ### PdfPageNumber
3683
3805
  \`\`\`tsx
3684
- import { PageNumber } from '@/components/pdfx/page-number/pdfx-page-number';
3685
- <PageNumber
3686
- format="Page {current} of {total}" // string template
3687
- size="sm"
3688
- align="right"
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
3689
3813
  />
3690
3814
  \`\`\`
3691
3815
 
3692
- ### Watermark
3816
+ ### PdfWatermark
3693
3817
  \`\`\`tsx
3694
3818
  import { PdfWatermark } from '@/components/pdfx/watermark/pdfx-watermark';
3695
3819
  <PdfWatermark
3696
3820
  text="CONFIDENTIAL" // string \u2014 required
3697
- opacity={0.08} // number 0\u20131 \u2014 default: 0.08
3698
- angle={-35} // number in degrees \u2014 default: -35
3699
- fontSize={72} // number \u2014 default: 72
3700
- color="#000000" // string
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)
3701
3827
  />
3702
3828
  \`\`\`
3703
3829
 
3704
- ### QRCode
3830
+ ### PdfQRCode
3705
3831
  \`\`\`tsx
3706
3832
  import { PdfQRCode } from '@/components/pdfx/qrcode/pdfx-qrcode';
3707
3833
  <PdfQRCode
3708
3834
  value="https://example.com" // string \u2014 required
3709
- size={80} // number in pt \u2014 default: 80
3710
- errorCorrectionLevel="M" // 'L' | 'M' | 'Q' | 'H'
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
3711
3841
  />
3712
3842
  \`\`\`
3713
3843
 
3714
- ### Alert
3844
+ ### PdfAlert
3715
3845
  \`\`\`tsx
3716
- import { Alert } from '@/components/pdfx/alert/pdfx-alert';
3717
- <Alert
3718
- variant="info" // 'info' | 'success' | 'warning' | 'error'
3719
- title="Note" // string \u2014 optional bold title
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)
3720
3852
  >
3721
3853
  This is an informational note.
3722
- </Alert>
3854
+ </PdfAlert>
3723
3855
  \`\`\`
3724
3856
 
3725
3857
  ---
@@ -3732,20 +3864,18 @@ npx pdfx-cli@latest block add <block-name>
3732
3864
  \`\`\`
3733
3865
 
3734
3866
  ### Invoice Blocks
3735
- - invoice-modern \u2014 Clean two-column layout with totals table
3736
- - invoice-minimal \u2014 Stripped-down, typography-focused
3737
- - invoice-corporate \u2014 Header with logo area, full itemization
3738
- - invoice-creative \u2014 Accent colors, bold layout
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
3739
3873
 
3740
3874
  ### Report Blocks
3741
- - report-executive \u2014 KPI cards + summary table, 2-page
3742
- - report-annual \u2014 Multi-section with charts and appendix
3743
- - report-financial \u2014 P&L / balance sheet focus
3744
- - report-marketing \u2014 Campaign metrics with graphs
3745
- - report-technical \u2014 Code-friendly, monospace sections
3746
-
3747
- ### Contract Block
3748
- - 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
3749
3879
 
3750
3880
  Blocks are added as full React components in your project. Customize all content props.
3751
3881
 
@@ -3830,6 +3960,18 @@ npx pdfx-cli@latest skills init --platform antigravity # .antigravity/context.md
3830
3960
 
3831
3961
  ---
3832
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
+
3833
3975
  ## Common patterns
3834
3976
 
3835
3977
  ### Full invoice from scratch
@@ -3837,7 +3979,7 @@ npx pdfx-cli@latest skills init --platform antigravity # .antigravity/context.md
3837
3979
  import { Document, Page } from '@react-pdf/renderer';
3838
3980
  import { Heading } from '@/components/pdfx/heading/pdfx-heading';
3839
3981
  import { KeyValue } from '@/components/pdfx/key-value/pdfx-key-value';
3840
- import { Table } from '@/components/pdfx/table/pdfx-table';
3982
+ import { Table, TableHeader, TableBody, TableRow, TableCell } from '@/components/pdfx/table/pdfx-table';
3841
3983
  import { Divider } from '@/components/pdfx/divider/pdfx-divider';
3842
3984
  import { Badge } from '@/components/pdfx/badge/pdfx-badge';
3843
3985
  import { PageFooter } from '@/components/pdfx/page-footer/pdfx-page-footer';
@@ -3849,24 +3991,30 @@ export function InvoiceDoc() {
3849
3991
  <Heading level={1}>Invoice #INV-001</Heading>
3850
3992
  <KeyValue
3851
3993
  items={[
3852
- { label: 'Date', value: 'Jan 1, 2025' },
3853
- { label: 'Due', value: 'Jan 31, 2025' },
3994
+ { key: 'Date', value: 'Jan 1, 2025' },
3995
+ { key: 'Due', value: 'Jan 31, 2025' },
3854
3996
  ]}
3855
- columns={2}
3997
+ direction="horizontal"
3856
3998
  />
3857
3999
  <Divider spacing="md" />
3858
- <Table
3859
- headers={['Description', 'Qty', 'Total']}
3860
- rows={[
3861
- ['Design System', '1', '$4,800'],
3862
- ['Development', '2', '$9,600'],
3863
- ]}
3864
- striped
3865
- bordered
3866
- columnWidths={[3, 1, 1]}
3867
- />
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>
3868
4016
  <Badge label="PAID" variant="success" />
3869
- <PageFooter left="Acme Corp" right="Page 1 of 1" bordered />
4017
+ <PageFooter leftText="Acme Corp" rightText="Page 1 of 1" />
3870
4018
  </Page>
3871
4019
  </Document>
3872
4020
  );
@@ -3878,7 +4026,7 @@ export function InvoiceDoc() {
3878
4026
  // Wrap anything that must stay together across page boundaries
3879
4027
  <KeepTogether>
3880
4028
  <Heading level={3}>Q3 Summary</Heading>
3881
- <Table headers={['Metric', 'Value']} rows={data} />
4029
+ <DataTable columns={columns} data={data} variant="grid" />
3882
4030
  </KeepTogether>
3883
4031
  \`\`\`
3884
4032
 
@@ -3896,7 +4044,7 @@ export async function GET(req: Request) {
3896
4044
  return new Response(buf, {
3897
4045
  headers: {
3898
4046
  'Content-Type': 'application/pdf',
3899
- 'Content-Disposition': \`inline; filename="invoice-\${id}.pdf"\`,
4047
+ 'Content-Disposition': \\\`inline; filename="invoice-\\\${id}.pdf"\\\`,
3900
4048
  },
3901
4049
  });
3902
4050
  }
@@ -3937,6 +4085,7 @@ export async function GET(req: Request) {
3937
4085
  - DO NOT install components with npm \u2014 always use the CLI: npx pdfx-cli@latest add <name>
3938
4086
  - DO NOT place raw text siblings next to View elements in a flex row (react-pdf constraint)
3939
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
3940
4089
 
3941
4090
  ---
3942
4091
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdfx-cli",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "CLI for PDFx components",
5
5
  "type": "module",
6
6
  "bin": {