hazo_pdf 1.2.0 → 1.2.1

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/README.md CHANGED
@@ -19,6 +19,47 @@ A React component library for viewing and annotating PDF documents with support
19
19
  npm install hazo_pdf
20
20
  ```
21
21
 
22
+ ## CSS Import Options
23
+
24
+ The library provides two CSS files to choose from:
25
+
26
+ ### For apps with existing styles (Recommended)
27
+
28
+ Use `styles.css` - this does NOT include Tailwind preflight/base resets, so it won't interfere with your existing styles:
29
+
30
+ ```tsx
31
+ import 'hazo_pdf/styles.css';
32
+ ```
33
+
34
+ ### For standalone apps
35
+
36
+ Use `styles-full.css` - includes Tailwind preflight/base styles for apps without existing CSS resets:
37
+
38
+ ```tsx
39
+ import 'hazo_pdf/styles-full.css';
40
+ ```
41
+
42
+ ## Container Requirements
43
+
44
+ The PDF viewer requires its parent container to have explicit dimensions:
45
+
46
+ ```tsx
47
+ // Good - explicit dimensions
48
+ <div style={{ width: '100%', height: '600px' }}>
49
+ <PdfViewer url="/document.pdf" />
50
+ </div>
51
+
52
+ // Bad - no explicit height
53
+ <div>
54
+ <PdfViewer url="/document.pdf" />
55
+ </div>
56
+ ```
57
+
58
+ **Requirements:**
59
+ - Parent must have explicit `width` and `height` (CSS or inline style)
60
+ - Recommended minimum size: 400x400px
61
+ - Parent should NOT have `overflow: hidden` (the viewer handles its own scrolling)
62
+
22
63
  ## Quick Start
23
64
 
24
65
  ```tsx
@@ -307,7 +348,7 @@ function ProductionViewer() {
307
348
  <div style={{ flex: 1, overflow: 'hidden' }}>
308
349
  <PdfViewer
309
350
  url="/api/documents/contract.pdf"
310
- config_file="hazo_pdf_config.ini"
351
+ config_file="config/hazo_pdf_config.ini"
311
352
  className="h-full w-full"
312
353
  scale={initialScale}
313
354
  annotations={annotations}
@@ -327,7 +368,7 @@ function ProductionViewer() {
327
368
  ```
328
369
 
329
370
  **Features demonstrated:**
330
- - Configuration file integration (`hazo_pdf_config.ini`)
371
+ - Configuration file integration (`config/hazo_pdf_config.ini`)
331
372
  - Custom stamps with styling
332
373
  - Timestamp and fixed text suffixes
333
374
  - Responsive scaling based on screen size
@@ -340,7 +381,7 @@ function ProductionViewer() {
340
381
 
341
382
  ## Configuration File
342
383
 
343
- The PDF viewer can be configured via an INI file (default: `hazo_pdf_config.ini`). This allows you to customize styling, colors, fonts, and behavior without modifying code.
384
+ The PDF viewer can be configured via an INI file (default: `config/hazo_pdf_config.ini`). This allows you to customize styling, colors, fonts, and behavior without modifying code.
344
385
 
345
386
  **Basic setup:**
346
387
 
@@ -368,7 +409,7 @@ toolbar_button_save_background_color = rgb(34, 197, 94)
368
409
  right_click_custom_stamps = [{"name":"Verified","text":"✅","order":1,"time_stamp_suffix_enabled":true,"fixed_text_suffix_enabled":true}]
369
410
  ```
370
411
 
371
- See `hazo_pdf_config.ini` in the project root for all available configuration options.
412
+ See `config/hazo_pdf_config.ini` in the project root for all available configuration options.
372
413
 
373
414
  ---
374
415
 
@@ -391,7 +432,7 @@ See `hazo_pdf_config.ini` in the project root for all available configuration op
391
432
  | `className` | `string` | `""` | Additional CSS classes to apply to the viewer container. |
392
433
  | `scale` | `number` | `1.0` | Initial zoom level. Values > 1.0 zoom in, < 1.0 zoom out. |
393
434
  | `background_color` | `string` | `"#2d2d2d"` | Background color for areas outside PDF pages (hex format: `#RRGGBB`). Overrides config file value. |
394
- | `config_file` | `string` | `undefined` | Path to configuration INI file (e.g., `"hazo_pdf_config.ini"`). If not provided, uses default configuration. |
435
+ | `config_file` | `string` | `undefined` | Path to configuration INI file (e.g., `"config/hazo_pdf_config.ini"`). If not provided, uses default configuration. |
395
436
 
396
437
  ##### Event Callbacks
397
438
 
@@ -469,12 +510,50 @@ const stamps = JSON.stringify([
469
510
  }
470
511
  ]);
471
512
 
472
- <PdfViewer
513
+ <PdfViewer
473
514
  url="/document.pdf"
474
515
  right_click_custom_stamps={stamps}
475
516
  />
476
517
  ```
477
518
 
519
+ ##### Toolbar Visibility
520
+
521
+ Props to control toolbar button visibility. These override config file values.
522
+
523
+ | Prop | Type | Default | Description |
524
+ |------|------|---------|-------------|
525
+ | `toolbar_enabled` | `boolean` | `true` | Master toggle to show/hide the entire toolbar. |
526
+ | `show_zoom_controls` | `boolean` | `true` | Show zoom in/out/reset buttons. |
527
+ | `show_square_button` | `boolean` | `true` | Show square annotation button. |
528
+ | `show_undo_button` | `boolean` | `true` | Show undo button. |
529
+ | `show_redo_button` | `boolean` | `true` | Show redo button. |
530
+ | `show_save_button` | `boolean` | `true` | Show save button. |
531
+ | `show_metadata_button` | `boolean` | `true` | Show metadata panel button (only visible when `sidepanel_metadata_enabled` is true). |
532
+ | `on_close` | `() => void` | `undefined` | Callback when close button is clicked. When provided, shows a close button (X) in the toolbar. Useful for modal/dialog usage. |
533
+
534
+ **Example - Minimal toolbar:**
535
+
536
+ ```tsx
537
+ <PdfViewer
538
+ url="/document.pdf"
539
+ toolbar_enabled={true}
540
+ show_zoom_controls={true}
541
+ show_square_button={false}
542
+ show_undo_button={false}
543
+ show_redo_button={false}
544
+ show_save_button={false}
545
+ />
546
+ ```
547
+
548
+ **Example - Dialog with close button:**
549
+
550
+ ```tsx
551
+ <PdfViewer
552
+ url="/document.pdf"
553
+ on_close={() => setDialogOpen(false)}
554
+ />
555
+ ```
556
+
478
557
  ### PdfAnnotation Interface
479
558
 
480
559
  Represents a PDF annotation in the standard PDF coordinate space.
@@ -95,6 +95,7 @@ var default_config = {
95
95
  toolbar_show_redo_button: true,
96
96
  toolbar_show_save_button: true,
97
97
  toolbar_show_metadata_button: true,
98
+ toolbar_show_annotate_button: true,
98
99
  toolbar_zoom_out_label: "\u2212",
99
100
  toolbar_zoom_in_label: "+",
100
101
  toolbar_zoom_reset_label: "Reset",
@@ -311,4 +312,4 @@ export {
311
312
  download_pdf,
312
313
  save_and_download_pdf
313
314
  };
314
- //# sourceMappingURL=chunk-ZQMZT7HV.js.map
315
+ //# sourceMappingURL=chunk-S3AJUZ7D.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/default_config.ts","../src/utils/pdf_saver.ts"],"sourcesContent":["/**\n * Default configuration values for hazo_pdf\n * All styling defaults organized by category\n */\n\nimport type { PdfViewerConfig } from '../types/config';\n\n/**\n * Default PDF Viewer Configuration\n * These values are used when no config file is provided or when values are missing\n */\nexport const default_config: PdfViewerConfig = {\n fonts: {\n freetext_font_family: 'Arial, sans-serif',\n freetext_font_size_min: 12,\n freetext_font_size_max: 24,\n freetext_font_size_default: 14,\n font_foreground_color: '#000000', // Black by default\n },\n\n highlight_annotation: {\n highlight_fill_color: '#FFFF00',\n highlight_fill_opacity: 0.3,\n highlight_border_color: '#FFD700',\n highlight_border_color_hover: '#FFD700',\n highlight_fill_opacity_hover: 0.4,\n },\n\n square_annotation: {\n square_fill_color: '#FF0000',\n square_fill_opacity: 0.2,\n square_border_color: '#FF0000',\n square_border_color_hover: '#CC0000',\n square_fill_opacity_hover: 0.3,\n },\n\n freetext_annotation: {\n freetext_text_color: '#000000',\n freetext_text_color_hover: '#000000',\n freetext_border_color: '#003366',\n freetext_border_width: 1,\n freetext_background_color: '#E6F3FF',\n freetext_background_opacity: 0.1,\n freetext_font_weight: 'normal',\n freetext_font_style: 'normal',\n freetext_text_decoration: 'none',\n freetext_padding_horizontal: 4,\n freetext_padding_vertical: 2,\n },\n\n page_styling: {\n page_border_color: '#999999',\n page_box_shadow: '0 2px 8px rgba(0, 0, 0, 0.3)',\n page_background_color: '#ffffff',\n },\n\n viewer: {\n viewer_background_color: '#2d2d2d',\n append_timestamp_to_text_edits: false,\n annotation_text_suffix_fixed_text: '',\n add_enclosing_brackets_to_suffixes: true,\n suffix_enclosing_brackets: '[]',\n suffix_text_position: 'below_multi_line',\n },\n\n context_menu: {\n context_menu_background_color: '#ffffff',\n context_menu_border_color: '#d1d5db',\n context_menu_item_hover_background: '#f3f4f6',\n context_menu_item_disabled_opacity: 0.5,\n right_click_custom_stamps: '', // Empty string means no custom stamps\n },\n\n dialog: {\n dialog_backdrop_opacity: 0.2,\n dialog_background_color: '#ffffff',\n dialog_border_color: '#d1d5db',\n dialog_button_submit_color: '#16a34a',\n dialog_button_submit_color_hover: '#15803d',\n dialog_button_cancel_color: '#6b7280',\n dialog_button_cancel_color_hover: '#4b5563',\n dialog_button_disabled_opacity: 0.4,\n },\n\n toolbar: {\n toolbar_background_color: '#f9fafb',\n toolbar_border_color: '#e5e7eb',\n toolbar_font_family: 'system-ui, -apple-system, sans-serif',\n toolbar_font_size: 14,\n toolbar_font_color: '#111827',\n toolbar_button_background_color: '#ffffff',\n toolbar_button_background_color_hover: '#f3f4f6',\n toolbar_button_text_color: '#374151',\n toolbar_button_active_background_color: '#3b82f6',\n toolbar_button_active_text_color: '#ffffff',\n toolbar_button_save_background_color: '#10b981',\n toolbar_button_save_background_color_hover: '#059669',\n toolbar_button_save_text_color: '#ffffff',\n toolbar_button_disabled_opacity: 0.5,\n toolbar_show_zoom_controls: true,\n toolbar_show_square_button: true,\n toolbar_show_undo_button: true,\n toolbar_show_redo_button: true,\n toolbar_show_save_button: true,\n toolbar_show_metadata_button: true,\n toolbar_show_annotate_button: true,\n toolbar_zoom_out_label: '−',\n toolbar_zoom_in_label: '+',\n toolbar_zoom_reset_label: 'Reset',\n toolbar_square_label: 'Square',\n toolbar_undo_label: 'Undo',\n toolbar_redo_label: 'Redo',\n toolbar_save_label: 'Save',\n toolbar_saving_label: 'Saving...',\n toolbar_metadata_label: 'Metadata',\n },\n};\n","/**\n * PDF Saver Utility\n * Saves annotations directly into PDF documents using pdf-lib\n * This allows annotations to be embedded in the PDF file itself\n */\n\nimport type { PdfAnnotation, PdfViewerConfig } from '../types';\nimport { default_config } from '../config/default_config';\n\n/**\n * Save annotations into a PDF document\n * This function fetches the PDF, adds annotations, and downloads the modified PDF\n * @param pdf_url - URL of the PDF file\n * @param annotations - Array of annotations to save\n * @param output_filename - Name for the saved PDF file (default: original filename with '_annotated' suffix)\n * @param config - Optional configuration for styling values\n * @returns Promise that resolves when the PDF is saved\n */\nexport async function save_annotations_to_pdf(\n pdf_url: string,\n annotations: PdfAnnotation[],\n _output_filename?: string, // Currently unused, kept for API compatibility\n config?: PdfViewerConfig | null\n): Promise<Uint8Array> {\n try {\n // Dynamically import pdf-lib\n const { PDFDocument, rgb } = await import('pdf-lib');\n \n // Fetch the original PDF\n console.log('[PDF Saver] Fetching PDF from:', pdf_url);\n const pdf_response = await fetch(pdf_url);\n if (!pdf_response.ok) {\n throw new Error(`Failed to fetch PDF: ${pdf_response.status} ${pdf_response.statusText}`);\n }\n \n const pdf_bytes = await pdf_response.arrayBuffer();\n console.log('[PDF Saver] PDF fetched, size:', pdf_bytes.byteLength, 'bytes');\n \n // Load the PDF document\n const pdf_doc = await PDFDocument.load(pdf_bytes);\n console.log('[PDF Saver] PDF loaded, pages:', pdf_doc.getPageCount());\n \n // Get config values or use defaults\n const fonts_config = config?.fonts || default_config.fonts;\n const highlight_config = config?.highlight_annotation || default_config.highlight_annotation;\n const square_config = config?.square_annotation || default_config.square_annotation;\n const freetext_config = config?.freetext_annotation || default_config.freetext_annotation;\n \n // Add annotations to each page\n for (const annotation of annotations) {\n if (annotation.page_index >= pdf_doc.getPageCount()) {\n console.warn(`[PDF Saver] Annotation ${annotation.id} references page ${annotation.page_index}, but PDF only has ${pdf_doc.getPageCount()} pages. Skipping.`);\n continue;\n }\n \n const page = pdf_doc.getPage(annotation.page_index);\n const [x1, y1, x2, y2] = annotation.rect;\n \n // PDF coordinate system: bottom-left is origin (pdfjs-dist)\n // pdf-lib uses bottom-left as origin too, so coordinates should be fine\n // But we need to ensure rect is normalized\n const rect_x = Math.min(x1, x2);\n const rect_y = Math.min(y1, y2);\n const rect_width = Math.abs(x2 - x1);\n const rect_height = Math.abs(y2 - y1);\n \n // Parse color (hex or rgb to RGB for pdf-lib)\n // Supports both hex (#RRGGBB) and rgb(r, g, b) formats\n let color = rgb(0, 0, 0); // Default black\n if (annotation.color) {\n const color_str = annotation.color.trim();\n \n // Handle rgb(r, g, b) format\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = color_str.match(rgb_pattern);\n if (rgb_match) {\n const r = parseInt(rgb_match[1], 10);\n const g = parseInt(rgb_match[2], 10);\n const b = parseInt(rgb_match[3], 10);\n // Validate RGB values (0-255) and convert to 0-1 range for pdf-lib\n if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {\n color = rgb(r / 255, g / 255, b / 255);\n } else {\n console.warn(`[PDF Saver] Invalid RGB values for annotation ${annotation.id}: r=${r}, g=${g}, b=${b}`);\n }\n } else if (color_str.startsWith('#')) {\n // Handle hex format (#RRGGBB)\n const hex = color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n const r = parseInt(hex.substring(0, 2), 16) / 255;\n const g = parseInt(hex.substring(2, 4), 16) / 255;\n const b = parseInt(hex.substring(4, 6), 16) / 255;\n color = rgb(r, g, b);\n } else {\n console.warn(`[PDF Saver] Invalid hex color format for annotation ${annotation.id}: ${color_str}`);\n }\n } else {\n console.warn(`[PDF Saver] Unrecognized color format for annotation ${annotation.id}: ${color_str}`);\n }\n }\n \n // Create annotation based on type using pdf-lib's annotation methods\n try {\n switch (annotation.type) {\n case 'Square': {\n // Create a rectangle/square annotation\n page.drawRectangle({\n x: rect_x,\n y: rect_y,\n width: rect_width,\n height: rect_height,\n borderColor: color,\n borderWidth: 2,\n borderOpacity: 0.8,\n });\n \n // Add text comment if present\n if (annotation.contents) {\n const comment_color_str = (annotation.color || square_config.square_border_color).trim();\n let comment_r = 0, comment_g = 0, comment_b = 0;\n \n // Parse color (hex or rgb format)\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = comment_color_str.match(rgb_pattern);\n if (rgb_match) {\n comment_r = parseInt(rgb_match[1], 10) / 255;\n comment_g = parseInt(rgb_match[2], 10) / 255;\n comment_b = parseInt(rgb_match[3], 10) / 255;\n } else if (comment_color_str.startsWith('#')) {\n const hex = comment_color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n comment_r = parseInt(hex.substring(0, 2), 16) / 255;\n comment_g = parseInt(hex.substring(2, 4), 16) / 255;\n comment_b = parseInt(hex.substring(4, 6), 16) / 255;\n }\n }\n \n page.drawText(annotation.contents, {\n x: rect_x,\n y: rect_y + rect_height + 5,\n size: fonts_config.freetext_font_size_default,\n color: rgb(comment_r, comment_g, comment_b),\n });\n }\n break;\n }\n \n case 'Highlight': {\n // Create a highlight annotation (semi-transparent rectangle)\n const highlight_color_str = (annotation.color || highlight_config.highlight_fill_color).trim();\n let highlight_r = 255 / 255, highlight_g = 255 / 255, highlight_b = 0 / 255; // Default yellow\n \n // Parse color (hex or rgb format)\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = highlight_color_str.match(rgb_pattern);\n if (rgb_match) {\n highlight_r = parseInt(rgb_match[1], 10) / 255;\n highlight_g = parseInt(rgb_match[2], 10) / 255;\n highlight_b = parseInt(rgb_match[3], 10) / 255;\n } else if (highlight_color_str.startsWith('#')) {\n const hex = highlight_color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n highlight_r = parseInt(hex.substring(0, 2), 16) / 255;\n highlight_g = parseInt(hex.substring(2, 4), 16) / 255;\n highlight_b = parseInt(hex.substring(4, 6), 16) / 255;\n }\n }\n const highlight_color = rgb(highlight_r, highlight_g, highlight_b);\n \n page.drawRectangle({\n x: rect_x,\n y: rect_y,\n width: rect_width,\n height: rect_height,\n color: highlight_color,\n opacity: highlight_config.highlight_fill_opacity,\n });\n \n // Add text comment if present\n if (annotation.contents) {\n page.drawText(annotation.contents, {\n x: rect_x,\n y: rect_y + rect_height + 5,\n size: fonts_config.freetext_font_size_default,\n color: rgb(0, 0, 0),\n });\n }\n break;\n }\n \n case 'FreeText': {\n // Create a free text annotation\n if (annotation.contents) {\n // Text color priority: annotation.color > freetext_text_color > font_foreground_color > default black\n const text_color_str = (annotation.color || \n freetext_config.freetext_text_color || \n fonts_config.font_foreground_color || \n '#000000').trim();\n let text_r = 0, text_g = 0, text_b = 0;\n \n // Parse color (hex or rgb format)\n const rgb_pattern = /^rgb\\s*\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*\\)$/i;\n const rgb_match = text_color_str.match(rgb_pattern);\n if (rgb_match) {\n text_r = parseInt(rgb_match[1], 10) / 255;\n text_g = parseInt(rgb_match[2], 10) / 255;\n text_b = parseInt(rgb_match[3], 10) / 255;\n } else if (text_color_str.startsWith('#')) {\n const hex = text_color_str.slice(1).trim();\n if (hex.length === 6 && /^[0-9A-Fa-f]{6}$/.test(hex)) {\n text_r = parseInt(hex.substring(0, 2), 16) / 255;\n text_g = parseInt(hex.substring(2, 4), 16) / 255;\n text_b = parseInt(hex.substring(4, 6), 16) / 255;\n }\n }\n \n page.drawText(annotation.contents, {\n x: rect_x,\n y: rect_y,\n size: fonts_config.freetext_font_size_default,\n color: rgb(text_r, text_g, text_b),\n maxWidth: rect_width,\n });\n }\n break;\n }\n \n default:\n console.warn(`[PDF Saver] Unsupported annotation type: ${annotation.type}`);\n }\n } catch (annotation_error) {\n console.error(`[PDF Saver] Error adding annotation ${annotation.id}:`, annotation_error);\n // Continue with other annotations\n }\n }\n \n // Save the PDF\n const modified_pdf_bytes = await pdf_doc.save();\n console.log('[PDF Saver] PDF modified, size:', modified_pdf_bytes.length, 'bytes');\n \n return modified_pdf_bytes;\n } catch (error) {\n if (error && typeof error === 'object' && 'message' in error && \n typeof error.message === 'string' && error.message.includes('pdf-lib')) {\n throw new Error('pdf-lib is required to save annotations to PDF. Please install it: npm install pdf-lib');\n }\n console.error('[PDF Saver] Error saving PDF:', error);\n throw error;\n }\n}\n\n/**\n * Download PDF bytes as a file\n * @param pdf_bytes - PDF file bytes\n * @param filename - Name for the downloaded file\n */\nexport function download_pdf(pdf_bytes: Uint8Array, filename: string): void {\n // Create a new ArrayBuffer view to ensure compatibility with Blob constructor\n // Uint8Array.buffer can be SharedArrayBuffer, which Blob doesn't accept directly\n const buffer = new Uint8Array(pdf_bytes).buffer;\n const blob = new Blob([buffer], { type: 'application/pdf' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Save annotations to PDF and download\n * @param pdf_url - URL of the PDF file\n * @param annotations - Array of annotations to save\n * @param output_filename - Name for the saved PDF file (default: original filename with '_annotated' suffix)\n * @param config - Optional configuration for styling values\n */\nexport async function save_and_download_pdf(\n pdf_url: string,\n annotations: PdfAnnotation[],\n output_filename?: string,\n config?: PdfViewerConfig | null\n): Promise<void> {\n const pdf_bytes = await save_annotations_to_pdf(pdf_url, annotations, output_filename, config);\n \n // Generate output filename\n const original_filename = pdf_url.split('/').pop() || 'document.pdf';\n const filename_without_ext = original_filename.replace(/\\.pdf$/i, '');\n const final_filename = output_filename || `${filename_without_ext}_annotated.pdf`;\n \n // Download the modified PDF\n download_pdf(pdf_bytes, final_filename);\n console.log('[PDF Saver] PDF saved as:', final_filename);\n}\n\n"],"mappings":";;;;;;;;;AAWO,IAAM,iBAAkC;AAAA,EAC7C,OAAO;AAAA,IACL,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,IACxB,wBAAwB;AAAA,IACxB,4BAA4B;AAAA,IAC5B,uBAAuB;AAAA;AAAA,EACzB;AAAA,EAEA,sBAAsB;AAAA,IACpB,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,IACxB,wBAAwB;AAAA,IACxB,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,EAChC;AAAA,EAEA,mBAAmB;AAAA,IACjB,mBAAmB;AAAA,IACnB,qBAAqB;AAAA,IACrB,qBAAqB;AAAA,IACrB,2BAA2B;AAAA,IAC3B,2BAA2B;AAAA,EAC7B;AAAA,EAEA,qBAAqB;AAAA,IACnB,qBAAqB;AAAA,IACrB,2BAA2B;AAAA,IAC3B,uBAAuB;AAAA,IACvB,uBAAuB;AAAA,IACvB,2BAA2B;AAAA,IAC3B,6BAA6B;AAAA,IAC7B,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,0BAA0B;AAAA,IAC1B,6BAA6B;AAAA,IAC7B,2BAA2B;AAAA,EAC7B;AAAA,EAEA,cAAc;AAAA,IACZ,mBAAmB;AAAA,IACnB,iBAAiB;AAAA,IACjB,uBAAuB;AAAA,EACzB;AAAA,EAEA,QAAQ;AAAA,IACN,yBAAyB;AAAA,IACzB,gCAAgC;AAAA,IAChC,mCAAmC;AAAA,IACnC,oCAAoC;AAAA,IACpC,2BAA2B;AAAA,IAC3B,sBAAsB;AAAA,EACxB;AAAA,EAEA,cAAc;AAAA,IACZ,+BAA+B;AAAA,IAC/B,2BAA2B;AAAA,IAC3B,oCAAoC;AAAA,IACpC,oCAAoC;AAAA,IACpC,2BAA2B;AAAA;AAAA,EAC7B;AAAA,EAEA,QAAQ;AAAA,IACN,yBAAyB;AAAA,IACzB,yBAAyB;AAAA,IACzB,qBAAqB;AAAA,IACrB,4BAA4B;AAAA,IAC5B,kCAAkC;AAAA,IAClC,4BAA4B;AAAA,IAC5B,kCAAkC;AAAA,IAClC,gCAAgC;AAAA,EAClC;AAAA,EAEA,SAAS;AAAA,IACP,0BAA0B;AAAA,IAC1B,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,IACnB,oBAAoB;AAAA,IACpB,iCAAiC;AAAA,IACjC,uCAAuC;AAAA,IACvC,2BAA2B;AAAA,IAC3B,wCAAwC;AAAA,IACxC,kCAAkC;AAAA,IAClC,sCAAsC;AAAA,IACtC,4CAA4C;AAAA,IAC5C,gCAAgC;AAAA,IAChC,iCAAiC;AAAA,IACjC,4BAA4B;AAAA,IAC5B,4BAA4B;AAAA,IAC5B,0BAA0B;AAAA,IAC1B,0BAA0B;AAAA,IAC1B,0BAA0B;AAAA,IAC1B,8BAA8B;AAAA,IAC9B,8BAA8B;AAAA,IAC9B,wBAAwB;AAAA,IACxB,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,IAC1B,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,IACpB,oBAAoB;AAAA,IACpB,oBAAoB;AAAA,IACpB,sBAAsB;AAAA,IACtB,wBAAwB;AAAA,EAC1B;AACF;;;AClGA,eAAsB,wBACpB,SACA,aACA,kBACA,QACqB;AACrB,MAAI;AAEF,UAAM,EAAE,aAAa,IAAI,IAAI,MAAM,OAAO,SAAS;AAGnD,YAAQ,IAAI,kCAAkC,OAAO;AACrD,UAAM,eAAe,MAAM,MAAM,OAAO;AACxC,QAAI,CAAC,aAAa,IAAI;AACpB,YAAM,IAAI,MAAM,wBAAwB,aAAa,MAAM,IAAI,aAAa,UAAU,EAAE;AAAA,IAC1F;AAEA,UAAM,YAAY,MAAM,aAAa,YAAY;AACjD,YAAQ,IAAI,kCAAkC,UAAU,YAAY,OAAO;AAG3E,UAAM,UAAU,MAAM,YAAY,KAAK,SAAS;AAChD,YAAQ,IAAI,kCAAkC,QAAQ,aAAa,CAAC;AAGpE,UAAM,eAAe,QAAQ,SAAS,eAAe;AACrD,UAAM,mBAAmB,QAAQ,wBAAwB,eAAe;AACxE,UAAM,gBAAgB,QAAQ,qBAAqB,eAAe;AAClE,UAAM,kBAAkB,QAAQ,uBAAuB,eAAe;AAGtE,eAAW,cAAc,aAAa;AACpC,UAAI,WAAW,cAAc,QAAQ,aAAa,GAAG;AACnD,gBAAQ,KAAK,0BAA0B,WAAW,EAAE,oBAAoB,WAAW,UAAU,sBAAsB,QAAQ,aAAa,CAAC,mBAAmB;AAC5J;AAAA,MACF;AAEA,YAAM,OAAO,QAAQ,QAAQ,WAAW,UAAU;AAClD,YAAM,CAAC,IAAI,IAAI,IAAI,EAAE,IAAI,WAAW;AAKpC,YAAM,SAAS,KAAK,IAAI,IAAI,EAAE;AAC9B,YAAM,SAAS,KAAK,IAAI,IAAI,EAAE;AAC9B,YAAM,aAAa,KAAK,IAAI,KAAK,EAAE;AACnC,YAAM,cAAc,KAAK,IAAI,KAAK,EAAE;AAIpC,UAAI,QAAQ,IAAI,GAAG,GAAG,CAAC;AACvB,UAAI,WAAW,OAAO;AACpB,cAAM,YAAY,WAAW,MAAM,KAAK;AAGxC,cAAM,cAAc;AACpB,cAAM,YAAY,UAAU,MAAM,WAAW;AAC7C,YAAI,WAAW;AACb,gBAAM,IAAI,SAAS,UAAU,CAAC,GAAG,EAAE;AACnC,gBAAM,IAAI,SAAS,UAAU,CAAC,GAAG,EAAE;AACnC,gBAAM,IAAI,SAAS,UAAU,CAAC,GAAG,EAAE;AAEnC,cAAI,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,KAAK,KAAK;AAClE,oBAAQ,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG;AAAA,UACvC,OAAO;AACL,oBAAQ,KAAK,iDAAiD,WAAW,EAAE,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;AAAA,UACvG;AAAA,QACF,WAAW,UAAU,WAAW,GAAG,GAAG;AAEpC,gBAAM,MAAM,UAAU,MAAM,CAAC,EAAE,KAAK;AACpC,cAAI,IAAI,WAAW,KAAK,mBAAmB,KAAK,GAAG,GAAG;AACpD,kBAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAC9C,kBAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAC9C,kBAAM,IAAI,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAC9C,oBAAQ,IAAI,GAAG,GAAG,CAAC;AAAA,UACrB,OAAO;AACL,oBAAQ,KAAK,uDAAuD,WAAW,EAAE,KAAK,SAAS,EAAE;AAAA,UACnG;AAAA,QACF,OAAO;AACL,kBAAQ,KAAK,wDAAwD,WAAW,EAAE,KAAK,SAAS,EAAE;AAAA,QACpG;AAAA,MACF;AAGA,UAAI;AACF,gBAAQ,WAAW,MAAM;AAAA,UACvB,KAAK,UAAU;AAEb,iBAAK,cAAc;AAAA,cACjB,GAAG;AAAA,cACH,GAAG;AAAA,cACH,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,aAAa;AAAA,cACb,aAAa;AAAA,cACb,eAAe;AAAA,YACjB,CAAC;AAGD,gBAAI,WAAW,UAAU;AACvB,oBAAM,qBAAqB,WAAW,SAAS,cAAc,qBAAqB,KAAK;AACvF,kBAAI,YAAY,GAAG,YAAY,GAAG,YAAY;AAG9C,oBAAM,cAAc;AACpB,oBAAM,YAAY,kBAAkB,MAAM,WAAW;AACrD,kBAAI,WAAW;AACb,4BAAY,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AACzC,4BAAY,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AACzC,4BAAY,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AAAA,cAC3C,WAAW,kBAAkB,WAAW,GAAG,GAAG;AAC5C,sBAAM,MAAM,kBAAkB,MAAM,CAAC,EAAE,KAAK;AAC5C,oBAAI,IAAI,WAAW,KAAK,mBAAmB,KAAK,GAAG,GAAG;AACpD,8BAAY,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAChD,8BAAY,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAChD,8BAAY,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAAA,gBAClD;AAAA,cACF;AAEA,mBAAK,SAAS,WAAW,UAAU;AAAA,gBACjC,GAAG;AAAA,gBACH,GAAG,SAAS,cAAc;AAAA,gBAC1B,MAAM,aAAa;AAAA,gBACnB,OAAO,IAAI,WAAW,WAAW,SAAS;AAAA,cAC5C,CAAC;AAAA,YACH;AACA;AAAA,UACF;AAAA,UAEA,KAAK,aAAa;AAEhB,kBAAM,uBAAuB,WAAW,SAAS,iBAAiB,sBAAsB,KAAK;AAC7F,gBAAI,cAAc,MAAM,KAAK,cAAc,MAAM,KAAK,cAAc,IAAI;AAGxE,kBAAM,cAAc;AACpB,kBAAM,YAAY,oBAAoB,MAAM,WAAW;AACvD,gBAAI,WAAW;AACb,4BAAc,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AAC3C,4BAAc,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AAC3C,4BAAc,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AAAA,YAC7C,WAAW,oBAAoB,WAAW,GAAG,GAAG;AAC9C,oBAAM,MAAM,oBAAoB,MAAM,CAAC,EAAE,KAAK;AAC9C,kBAAI,IAAI,WAAW,KAAK,mBAAmB,KAAK,GAAG,GAAG;AACpD,8BAAc,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAClD,8BAAc,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAClD,8BAAc,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAAA,cACpD;AAAA,YACF;AACA,kBAAM,kBAAkB,IAAI,aAAa,aAAa,WAAW;AAEjE,iBAAK,cAAc;AAAA,cACjB,GAAG;AAAA,cACH,GAAG;AAAA,cACH,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,OAAO;AAAA,cACP,SAAS,iBAAiB;AAAA,YAC5B,CAAC;AAGD,gBAAI,WAAW,UAAU;AACvB,mBAAK,SAAS,WAAW,UAAU;AAAA,gBACjC,GAAG;AAAA,gBACH,GAAG,SAAS,cAAc;AAAA,gBAC1B,MAAM,aAAa;AAAA,gBACnB,OAAO,IAAI,GAAG,GAAG,CAAC;AAAA,cACpB,CAAC;AAAA,YACH;AACA;AAAA,UACF;AAAA,UAEA,KAAK,YAAY;AAEf,gBAAI,WAAW,UAAU;AAEvB,oBAAM,kBAAkB,WAAW,SACb,gBAAgB,uBAChB,aAAa,yBACb,WAAW,KAAK;AACtC,kBAAI,SAAS,GAAG,SAAS,GAAG,SAAS;AAGrC,oBAAM,cAAc;AACpB,oBAAM,YAAY,eAAe,MAAM,WAAW;AAClD,kBAAI,WAAW;AACb,yBAAS,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AACtC,yBAAS,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AACtC,yBAAS,SAAS,UAAU,CAAC,GAAG,EAAE,IAAI;AAAA,cACxC,WAAW,eAAe,WAAW,GAAG,GAAG;AACzC,sBAAM,MAAM,eAAe,MAAM,CAAC,EAAE,KAAK;AACzC,oBAAI,IAAI,WAAW,KAAK,mBAAmB,KAAK,GAAG,GAAG;AACpD,2BAAS,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAC7C,2BAAS,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAC7C,2BAAS,SAAS,IAAI,UAAU,GAAG,CAAC,GAAG,EAAE,IAAI;AAAA,gBAC/C;AAAA,cACF;AAEA,mBAAK,SAAS,WAAW,UAAU;AAAA,gBACjC,GAAG;AAAA,gBACH,GAAG;AAAA,gBACH,MAAM,aAAa;AAAA,gBACnB,OAAO,IAAI,QAAQ,QAAQ,MAAM;AAAA,gBACjC,UAAU;AAAA,cACZ,CAAC;AAAA,YACH;AACA;AAAA,UACF;AAAA,UAEA;AACE,oBAAQ,KAAK,4CAA4C,WAAW,IAAI,EAAE;AAAA,QAC9E;AAAA,MACF,SAAS,kBAAkB;AACzB,gBAAQ,MAAM,uCAAuC,WAAW,EAAE,KAAK,gBAAgB;AAAA,MAEzF;AAAA,IACF;AAGA,UAAM,qBAAqB,MAAM,QAAQ,KAAK;AAC9C,YAAQ,IAAI,mCAAmC,mBAAmB,QAAQ,OAAO;AAEjF,WAAO;AAAA,EACT,SAAS,OAAO;AACd,QAAI,SAAS,OAAO,UAAU,YAAY,aAAa,SACnD,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,SAAS,SAAS,GAAG;AAC1E,YAAM,IAAI,MAAM,wFAAwF;AAAA,IAC1G;AACA,YAAQ,MAAM,iCAAiC,KAAK;AACpD,UAAM;AAAA,EACR;AACF;AAOO,SAAS,aAAa,WAAuB,UAAwB;AAG1E,QAAM,SAAS,IAAI,WAAW,SAAS,EAAE;AACzC,QAAM,OAAO,IAAI,KAAK,CAAC,MAAM,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAC3D,QAAM,MAAM,IAAI,gBAAgB,IAAI;AACpC,QAAM,OAAO,SAAS,cAAc,GAAG;AACvC,OAAK,OAAO;AACZ,OAAK,WAAW;AAChB,WAAS,KAAK,YAAY,IAAI;AAC9B,OAAK,MAAM;AACX,WAAS,KAAK,YAAY,IAAI;AAC9B,MAAI,gBAAgB,GAAG;AACzB;AASA,eAAsB,sBACpB,SACA,aACA,iBACA,QACe;AACf,QAAM,YAAY,MAAM,wBAAwB,SAAS,aAAa,iBAAiB,MAAM;AAG7F,QAAM,oBAAoB,QAAQ,MAAM,GAAG,EAAE,IAAI,KAAK;AACtD,QAAM,uBAAuB,kBAAkB,QAAQ,WAAW,EAAE;AACpE,QAAM,iBAAiB,mBAAmB,GAAG,oBAAoB;AAGjE,eAAa,WAAW,cAAc;AACtC,UAAQ,IAAI,6BAA6B,cAAc;AACzD;","names":[]}
package/dist/index.d.ts CHANGED
@@ -168,6 +168,8 @@ interface PdfViewerConfig {
168
168
  toolbar_show_save_button: boolean;
169
169
  /** Whether to show metadata panel button (true/false) */
170
170
  toolbar_show_metadata_button: boolean;
171
+ /** Whether to show annotate (FreeText) button (true/false) */
172
+ toolbar_show_annotate_button: boolean;
171
173
  /** Label for zoom out button (default: "−") */
172
174
  toolbar_zoom_out_label: string;
173
175
  /** Label for zoom in button (default: "+") */
@@ -220,7 +222,7 @@ interface PdfViewerProps {
220
222
  on_save?: (pdf_bytes: Uint8Array, filename: string) => void;
221
223
  /** Background color for areas outside PDF pages (default: dark grey) */
222
224
  background_color?: string;
223
- /** Optional path to configuration INI file (e.g., "hazo_pdf_config.ini") */
225
+ /** Optional path to configuration INI file (e.g., "config/hazo_pdf_config.ini") */
224
226
  config_file?: string;
225
227
  /** Whether to append timestamp to annotated text edits (default: false) */
226
228
  append_timestamp_to_text_edits?: boolean;
@@ -241,6 +243,24 @@ interface PdfViewerProps {
241
243
  updatedRow: MetadataDataItem;
242
244
  allData: MetadataInput;
243
245
  };
246
+ /** Whether to show the toolbar at all (default: true) */
247
+ toolbar_enabled?: boolean;
248
+ /** Whether to show zoom controls (default: true) */
249
+ show_zoom_controls?: boolean;
250
+ /** Whether to show square annotation button (default: true) */
251
+ show_square_button?: boolean;
252
+ /** Whether to show undo button (default: true) */
253
+ show_undo_button?: boolean;
254
+ /** Whether to show redo button (default: true) */
255
+ show_redo_button?: boolean;
256
+ /** Whether to show save button (default: true) */
257
+ show_save_button?: boolean;
258
+ /** Whether to show metadata panel button (default: true, only visible when sidepanel_metadata_enabled) */
259
+ show_metadata_button?: boolean;
260
+ /** Whether to show annotate (FreeText) button (default: true) */
261
+ show_annotate_button?: boolean;
262
+ /** Callback when close button is clicked (shows close button in toolbar when provided) */
263
+ on_close?: () => void;
244
264
  }
245
265
  /**
246
266
  * PDF Annotation interface matching PDF standard
@@ -413,6 +433,7 @@ interface PdfViewerLayoutProps {
413
433
  on_annotation_create: (annotation: PdfAnnotation) => void;
414
434
  on_context_menu: (e: React.MouseEvent, page_index: number, screen_x: number, screen_y: number, mapper: CoordinateMapper) => void;
415
435
  on_annotation_click: (annotation: PdfAnnotation, screen_x: number, screen_y: number, mapper: CoordinateMapper) => void;
436
+ on_freetext_click?: (page_index: number, screen_x: number, screen_y: number, mapper: CoordinateMapper) => void;
416
437
  background_color?: string;
417
438
  config: PdfViewerConfig | null;
418
439
  className?: string;
@@ -479,6 +500,8 @@ interface AnnotationOverlayProps {
479
500
  on_context_menu?: (event: React.MouseEvent, screen_x: number, screen_y: number) => void;
480
501
  /** Callback when annotation is clicked */
481
502
  on_annotation_click?: (annotation: PdfAnnotation, screen_x: number, screen_y: number) => void;
503
+ /** Callback when FreeText tool is active and user clicks on empty area */
504
+ on_freetext_click?: (screen_x: number, screen_y: number) => void;
482
505
  /** Configuration object for styling */
483
506
  config?: PdfViewerConfig | null;
484
507
  /** Optional class name */
package/dist/index.js CHANGED
@@ -5,11 +5,11 @@ import {
5
5
  download_pdf,
6
6
  save_and_download_pdf,
7
7
  save_annotations_to_pdf
8
- } from "./chunk-ZQMZT7HV.js";
8
+ } from "./chunk-S3AJUZ7D.js";
9
9
 
10
10
  // src/components/pdf_viewer/pdf_viewer.tsx
11
11
  import { useState as useState6, useEffect as useEffect6, useRef as useRef7, useCallback as useCallback2 } from "react";
12
- import { Save, Undo2 as Undo22, Redo2, PanelRight } from "lucide-react";
12
+ import { Save, Undo2 as Undo22, Redo2, PanelRight, ZoomIn, ZoomOut, RotateCcw, Square, Type } from "lucide-react";
13
13
 
14
14
  // src/components/pdf_viewer/pdf_worker_setup.ts
15
15
  var worker_configured = false;
@@ -371,6 +371,7 @@ var AnnotationOverlay = ({
371
371
  on_annotation_create,
372
372
  on_context_menu,
373
373
  on_annotation_click,
374
+ on_freetext_click,
374
375
  config = null,
375
376
  className = ""
376
377
  }) => {
@@ -448,6 +449,14 @@ var AnnotationOverlay = ({
448
449
  if (!current_tool) {
449
450
  return;
450
451
  }
452
+ if (current_tool === "FreeText") {
453
+ e.preventDefault();
454
+ e.stopPropagation();
455
+ if (on_freetext_click) {
456
+ on_freetext_click(point.x, point.y);
457
+ }
458
+ return;
459
+ }
451
460
  setIsDrawing(true);
452
461
  setStartPoint(point);
453
462
  setCurrentPoint(point);
@@ -857,6 +866,7 @@ var PdfViewerLayout = ({
857
866
  on_annotation_create,
858
867
  on_context_menu,
859
868
  on_annotation_click,
869
+ on_freetext_click,
860
870
  background_color = "#2d2d2d",
861
871
  config = null,
862
872
  className = ""
@@ -1071,6 +1081,11 @@ var PdfViewerLayout = ({
1071
1081
  if (on_annotation_click && mapper_data.mapper) {
1072
1082
  on_annotation_click(annotation, screen_x, screen_y, mapper_data.mapper);
1073
1083
  }
1084
+ },
1085
+ on_freetext_click: (screen_x, screen_y) => {
1086
+ if (on_freetext_click && mapper_data.mapper) {
1087
+ on_freetext_click(index, screen_x, screen_y, mapper_data.mapper);
1088
+ }
1074
1089
  }
1075
1090
  }
1076
1091
  )
@@ -1756,7 +1771,7 @@ function parse_ini_browser(ini_text) {
1756
1771
  }
1757
1772
  async function load_config_browser(config_file) {
1758
1773
  try {
1759
- const config_url = config_file === "hazo_pdf_config.ini" || config_file.includes("hazo_pdf_config.ini") ? "/api/config" : config_file;
1774
+ const config_url = config_file === "config/hazo_pdf_config.ini" || config_file.includes("config/hazo_pdf_config.ini") ? "/api/config" : config_file;
1760
1775
  const response = await fetch(config_url);
1761
1776
  if (!response.ok) {
1762
1777
  throw new Error(`Failed to fetch config file: ${response.status} ${response.statusText}`);
@@ -2140,6 +2155,10 @@ function build_config_from_ini(get_value) {
2140
2155
  get_value("toolbar", "toolbar_show_metadata_button"),
2141
2156
  default_config.toolbar.toolbar_show_metadata_button
2142
2157
  ),
2158
+ toolbar_show_annotate_button: parse_boolean(
2159
+ get_value("toolbar", "toolbar_show_annotate_button"),
2160
+ default_config.toolbar.toolbar_show_annotate_button
2161
+ ),
2143
2162
  toolbar_zoom_out_label: parse_string(
2144
2163
  get_value("toolbar", "toolbar_zoom_out_label"),
2145
2164
  default_config.toolbar.toolbar_zoom_out_label
@@ -2271,7 +2290,17 @@ var PdfViewer = ({
2271
2290
  right_click_custom_stamps,
2272
2291
  sidepanel_metadata_enabled = false,
2273
2292
  metadata_input,
2274
- on_metadata_change
2293
+ on_metadata_change,
2294
+ // Toolbar visibility props (override config file values)
2295
+ toolbar_enabled,
2296
+ show_zoom_controls,
2297
+ show_square_button,
2298
+ show_undo_button,
2299
+ show_redo_button,
2300
+ show_save_button,
2301
+ show_metadata_button,
2302
+ show_annotate_button,
2303
+ on_close
2275
2304
  }) => {
2276
2305
  const [pdf_document, setPdfDocument] = useState6(null);
2277
2306
  const [loading, setLoading] = useState6(true);
@@ -2711,7 +2740,7 @@ ${suffix_line}`;
2711
2740
  const original_filename = url.split("/").pop() || "document.pdf";
2712
2741
  const filename_without_ext = original_filename.replace(/\.pdf$/i, "");
2713
2742
  const output_filename = `${filename_without_ext}_annotated.pdf`;
2714
- const { save_annotations_to_pdf: save_annotations_to_pdf2, download_pdf: download_pdf2 } = await import("./pdf_saver-ILJPGAPI.js");
2743
+ const { save_annotations_to_pdf: save_annotations_to_pdf2, download_pdf: download_pdf2 } = await import("./pdf_saver-P2MJN45S.js");
2715
2744
  const pdf_bytes = await save_annotations_to_pdf2(url, annotations, output_filename, config_ref.current);
2716
2745
  download_pdf2(pdf_bytes, output_filename);
2717
2746
  if (on_save) {
@@ -2739,9 +2768,21 @@ ${suffix_line}`;
2739
2768
  if (!pdf_document) {
2740
2769
  return /* @__PURE__ */ jsx7("div", { className: cn("cls_pdf_viewer", className), children: /* @__PURE__ */ jsx7("div", { className: "cls_pdf_viewer_no_document", children: "No PDF document loaded" }) });
2741
2770
  }
2742
- const toolbar_config = config_ref.current?.toolbar || default_config.toolbar;
2743
- return /* @__PURE__ */ jsxs6("div", { className: cn("cls_pdf_viewer", className), children: [
2744
- /* @__PURE__ */ jsxs6(
2771
+ const base_toolbar_config = config_ref.current?.toolbar || default_config.toolbar;
2772
+ const toolbar_config = {
2773
+ ...base_toolbar_config,
2774
+ // Props override config file values (undefined means use config value)
2775
+ toolbar_show_zoom_controls: show_zoom_controls ?? base_toolbar_config.toolbar_show_zoom_controls,
2776
+ toolbar_show_square_button: show_square_button ?? base_toolbar_config.toolbar_show_square_button,
2777
+ toolbar_show_undo_button: show_undo_button ?? base_toolbar_config.toolbar_show_undo_button,
2778
+ toolbar_show_redo_button: show_redo_button ?? base_toolbar_config.toolbar_show_redo_button,
2779
+ toolbar_show_save_button: show_save_button ?? base_toolbar_config.toolbar_show_save_button,
2780
+ toolbar_show_metadata_button: show_metadata_button ?? base_toolbar_config.toolbar_show_metadata_button,
2781
+ toolbar_show_annotate_button: show_annotate_button ?? base_toolbar_config.toolbar_show_annotate_button
2782
+ };
2783
+ const is_toolbar_enabled = toolbar_enabled ?? true;
2784
+ return /* @__PURE__ */ jsxs6("div", { className: cn("hazo-pdf-root cls_pdf_viewer", className), children: [
2785
+ is_toolbar_enabled && /* @__PURE__ */ jsxs6(
2745
2786
  "div",
2746
2787
  {
2747
2788
  className: "cls_pdf_viewer_toolbar",
@@ -2761,6 +2802,7 @@ ${suffix_line}`;
2761
2802
  onClick: handle_zoom_out,
2762
2803
  className: "cls_pdf_viewer_toolbar_button",
2763
2804
  "aria-label": "Zoom out",
2805
+ title: "Zoom out",
2764
2806
  style: {
2765
2807
  backgroundColor: toolbar_config.toolbar_button_background_color,
2766
2808
  color: toolbar_config.toolbar_button_text_color
@@ -2771,7 +2813,7 @@ ${suffix_line}`;
2771
2813
  onMouseLeave: (e) => {
2772
2814
  e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
2773
2815
  },
2774
- children: toolbar_config.toolbar_zoom_out_label
2816
+ children: /* @__PURE__ */ jsx7(ZoomOut, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2775
2817
  }
2776
2818
  ),
2777
2819
  /* @__PURE__ */ jsxs6("span", { className: "cls_pdf_viewer_zoom_level", children: [
@@ -2785,6 +2827,7 @@ ${suffix_line}`;
2785
2827
  onClick: handle_zoom_in,
2786
2828
  className: "cls_pdf_viewer_toolbar_button",
2787
2829
  "aria-label": "Zoom in",
2830
+ title: "Zoom in",
2788
2831
  style: {
2789
2832
  backgroundColor: toolbar_config.toolbar_button_background_color,
2790
2833
  color: toolbar_config.toolbar_button_text_color
@@ -2795,7 +2838,7 @@ ${suffix_line}`;
2795
2838
  onMouseLeave: (e) => {
2796
2839
  e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
2797
2840
  },
2798
- children: toolbar_config.toolbar_zoom_in_label
2841
+ children: /* @__PURE__ */ jsx7(ZoomIn, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2799
2842
  }
2800
2843
  ),
2801
2844
  /* @__PURE__ */ jsx7(
@@ -2805,6 +2848,7 @@ ${suffix_line}`;
2805
2848
  onClick: handle_zoom_reset,
2806
2849
  className: "cls_pdf_viewer_toolbar_button",
2807
2850
  "aria-label": "Reset zoom",
2851
+ title: "Reset zoom",
2808
2852
  style: {
2809
2853
  backgroundColor: toolbar_config.toolbar_button_background_color,
2810
2854
  color: toolbar_config.toolbar_button_text_color
@@ -2815,7 +2859,7 @@ ${suffix_line}`;
2815
2859
  onMouseLeave: (e) => {
2816
2860
  e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
2817
2861
  },
2818
- children: toolbar_config.toolbar_zoom_reset_label
2862
+ children: /* @__PURE__ */ jsx7(RotateCcw, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2819
2863
  }
2820
2864
  )
2821
2865
  ] }),
@@ -2823,12 +2867,13 @@ ${suffix_line}`;
2823
2867
  "button",
2824
2868
  {
2825
2869
  type: "button",
2826
- onClick: () => setCurrentTool("Square"),
2870
+ onClick: () => setCurrentTool(current_tool === "Square" ? null : "Square"),
2827
2871
  className: cn(
2828
2872
  "cls_pdf_viewer_toolbar_button",
2829
2873
  current_tool === "Square" && "cls_pdf_viewer_toolbar_button_active"
2830
2874
  ),
2831
2875
  "aria-label": "Square annotation tool",
2876
+ title: "Square annotation",
2832
2877
  style: {
2833
2878
  backgroundColor: current_tool === "Square" ? toolbar_config.toolbar_button_active_background_color : toolbar_config.toolbar_button_background_color,
2834
2879
  color: current_tool === "Square" ? toolbar_config.toolbar_button_active_text_color : toolbar_config.toolbar_button_text_color
@@ -2843,11 +2888,39 @@ ${suffix_line}`;
2843
2888
  e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
2844
2889
  }
2845
2890
  },
2846
- children: toolbar_config.toolbar_square_label
2891
+ children: /* @__PURE__ */ jsx7(Square, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2892
+ }
2893
+ ) }),
2894
+ toolbar_config.toolbar_show_annotate_button && /* @__PURE__ */ jsx7("div", { className: "cls_pdf_viewer_toolbar_group", children: /* @__PURE__ */ jsx7(
2895
+ "button",
2896
+ {
2897
+ type: "button",
2898
+ onClick: () => setCurrentTool(current_tool === "FreeText" ? null : "FreeText"),
2899
+ className: cn(
2900
+ "cls_pdf_viewer_toolbar_button",
2901
+ current_tool === "FreeText" && "cls_pdf_viewer_toolbar_button_active"
2902
+ ),
2903
+ "aria-label": "Text annotation tool",
2904
+ title: "Text annotation",
2905
+ style: {
2906
+ backgroundColor: current_tool === "FreeText" ? toolbar_config.toolbar_button_active_background_color : toolbar_config.toolbar_button_background_color,
2907
+ color: current_tool === "FreeText" ? toolbar_config.toolbar_button_active_text_color : toolbar_config.toolbar_button_text_color
2908
+ },
2909
+ onMouseEnter: (e) => {
2910
+ if (current_tool !== "FreeText") {
2911
+ e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color_hover;
2912
+ }
2913
+ },
2914
+ onMouseLeave: (e) => {
2915
+ if (current_tool !== "FreeText") {
2916
+ e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
2917
+ }
2918
+ },
2919
+ children: /* @__PURE__ */ jsx7(Type, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2847
2920
  }
2848
2921
  ) }),
2849
2922
  (toolbar_config.toolbar_show_undo_button || toolbar_config.toolbar_show_redo_button) && /* @__PURE__ */ jsxs6("div", { className: "cls_pdf_viewer_toolbar_group", children: [
2850
- toolbar_config.toolbar_show_undo_button && /* @__PURE__ */ jsxs6(
2923
+ toolbar_config.toolbar_show_undo_button && /* @__PURE__ */ jsx7(
2851
2924
  "button",
2852
2925
  {
2853
2926
  type: "button",
@@ -2872,13 +2945,10 @@ ${suffix_line}`;
2872
2945
  onMouseLeave: (e) => {
2873
2946
  e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
2874
2947
  },
2875
- children: [
2876
- /* @__PURE__ */ jsx7(Undo22, { className: "cls_pdf_viewer_toolbar_icon", size: 16 }),
2877
- /* @__PURE__ */ jsx7("span", { className: "cls_pdf_viewer_toolbar_button_text", children: toolbar_config.toolbar_undo_label })
2878
- ]
2948
+ children: /* @__PURE__ */ jsx7(Undo22, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2879
2949
  }
2880
2950
  ),
2881
- toolbar_config.toolbar_show_redo_button && /* @__PURE__ */ jsxs6(
2951
+ toolbar_config.toolbar_show_redo_button && /* @__PURE__ */ jsx7(
2882
2952
  "button",
2883
2953
  {
2884
2954
  type: "button",
@@ -2903,14 +2973,11 @@ ${suffix_line}`;
2903
2973
  onMouseLeave: (e) => {
2904
2974
  e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
2905
2975
  },
2906
- children: [
2907
- /* @__PURE__ */ jsx7(Redo2, { className: "cls_pdf_viewer_toolbar_icon", size: 16 }),
2908
- /* @__PURE__ */ jsx7("span", { className: "cls_pdf_viewer_toolbar_button_text", children: toolbar_config.toolbar_redo_label })
2909
- ]
2976
+ children: /* @__PURE__ */ jsx7(Redo2, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2910
2977
  }
2911
2978
  )
2912
2979
  ] }),
2913
- toolbar_config.toolbar_show_save_button && /* @__PURE__ */ jsx7("div", { className: "cls_pdf_viewer_toolbar_group", children: /* @__PURE__ */ jsxs6(
2980
+ toolbar_config.toolbar_show_save_button && /* @__PURE__ */ jsx7("div", { className: "cls_pdf_viewer_toolbar_group", children: /* @__PURE__ */ jsx7(
2914
2981
  "button",
2915
2982
  {
2916
2983
  type: "button",
@@ -2922,7 +2989,7 @@ ${suffix_line}`;
2922
2989
  (saving || annotations.length === 0) && "cls_pdf_viewer_toolbar_button_disabled"
2923
2990
  ),
2924
2991
  "aria-label": "Save annotations to PDF",
2925
- title: annotations.length === 0 ? "No annotations to save" : "Save annotations to PDF",
2992
+ title: saving ? "Saving..." : annotations.length === 0 ? "No annotations to save" : "Save annotations to PDF",
2926
2993
  style: {
2927
2994
  backgroundColor: toolbar_config.toolbar_button_save_background_color,
2928
2995
  color: toolbar_config.toolbar_button_save_text_color,
@@ -2936,13 +3003,10 @@ ${suffix_line}`;
2936
3003
  onMouseLeave: (e) => {
2937
3004
  e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_save_background_color;
2938
3005
  },
2939
- children: [
2940
- /* @__PURE__ */ jsx7(Save, { className: "cls_pdf_viewer_toolbar_icon", size: 16 }),
2941
- /* @__PURE__ */ jsx7("span", { className: "cls_pdf_viewer_toolbar_button_text", children: saving ? toolbar_config.toolbar_saving_label : toolbar_config.toolbar_save_label })
2942
- ]
3006
+ children: /* @__PURE__ */ jsx7(Save, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
2943
3007
  }
2944
3008
  ) }),
2945
- sidepanel_metadata_enabled && metadata_input && toolbar_config.toolbar_show_metadata_button && /* @__PURE__ */ jsx7("div", { className: "cls_pdf_viewer_toolbar_group", children: /* @__PURE__ */ jsxs6(
3009
+ sidepanel_metadata_enabled && metadata_input && toolbar_config.toolbar_show_metadata_button && /* @__PURE__ */ jsx7("div", { className: "cls_pdf_viewer_toolbar_group", children: /* @__PURE__ */ jsx7(
2946
3010
  "button",
2947
3011
  {
2948
3012
  type: "button",
@@ -2965,10 +3029,27 @@ ${suffix_line}`;
2965
3029
  onMouseLeave: (e) => {
2966
3030
  e.currentTarget.style.backgroundColor = sidepanel_open ? toolbar_config.toolbar_button_active_background_color : toolbar_config.toolbar_button_background_color;
2967
3031
  },
2968
- children: [
2969
- /* @__PURE__ */ jsx7(PanelRight, { className: "cls_pdf_viewer_toolbar_icon", size: 16 }),
2970
- /* @__PURE__ */ jsx7("span", { className: "cls_pdf_viewer_toolbar_button_text", children: toolbar_config.toolbar_metadata_label })
2971
- ]
3032
+ children: /* @__PURE__ */ jsx7(PanelRight, { className: "cls_pdf_viewer_toolbar_icon", size: 16 })
3033
+ }
3034
+ ) }),
3035
+ on_close && /* @__PURE__ */ jsx7("div", { className: "cls_pdf_viewer_toolbar_group", children: /* @__PURE__ */ jsx7(
3036
+ "button",
3037
+ {
3038
+ type: "button",
3039
+ onClick: on_close,
3040
+ className: "cls_pdf_viewer_toolbar_button",
3041
+ "aria-label": "Close viewer",
3042
+ style: {
3043
+ backgroundColor: toolbar_config.toolbar_button_background_color,
3044
+ color: toolbar_config.toolbar_button_text_color
3045
+ },
3046
+ onMouseEnter: (e) => {
3047
+ e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color_hover;
3048
+ },
3049
+ onMouseLeave: (e) => {
3050
+ e.currentTarget.style.backgroundColor = toolbar_config.toolbar_button_background_color;
3051
+ },
3052
+ children: "\u2715"
2972
3053
  }
2973
3054
  ) })
2974
3055
  ]
@@ -3021,6 +3102,18 @@ ${suffix_line}`;
3021
3102
  screen_y,
3022
3103
  mapper
3023
3104
  });
3105
+ },
3106
+ on_freetext_click: (page_index, screen_x, screen_y, mapper) => {
3107
+ setTextDialog({
3108
+ open: true,
3109
+ page_index,
3110
+ x: screen_x,
3111
+ y: screen_y,
3112
+ screen_x,
3113
+ screen_y,
3114
+ mapper
3115
+ });
3116
+ setCurrentTool(null);
3024
3117
  }
3025
3118
  }
3026
3119
  )