Files
bentopdf/src/pages/bookmark.html

434 lines
22 KiB
HTML
Raw Normal View History

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Advanced PDF Bookmark Tool - BentoPDF</title>
<link rel="icon" type="image/png" href="../../images/favicon.svg" />
<link href="../../src/css/styles.css" rel="stylesheet" />
<link href="../../src/css/bookmark.css" rel="stylesheet" />
</head>
<body class="antialiased bg-gray-900">
<nav class="bg-gray-800 border-b border-gray-700 sticky top-0 z-30">
<div class="container mx-auto px-4">
<div class="flex justify-between items-center h-16">
<div class="flex-shrink-0 flex items-center cursor-pointer" id="home-logo">
<img src="../../public/images/favicon.svg" alt="Bento PDF Logo" class="h-8 w-8" />
<span class="text-white font-bold text-xl ml-2">
<a href="/">BentoPDF</a>
</span>
</div>
<!-- Desktop Navigation -->
<div class="hidden md:flex items-center space-x-8 text-white">
<a href="/" class="nav-link">Home</a>
<a href="./about.html" class="nav-link">About</a>
<a href="./contact.html" class="nav-link">Contact</a>
<a href="/" class="nav-link">All Tools</a>
</div>
<!-- Mobile Hamburger Button -->
<div class="md:hidden flex items-center">
<button id="mobile-menu-button" type="button"
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500 transition-colors"
aria-controls="mobile-menu" aria-expanded="false">
<span class="sr-only">Open main menu</span>
<!-- Hamburger Icon -->
<svg id="menu-icon" class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<!-- Close Icon -->
<svg id="close-icon" class="hidden h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none"
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
<!-- Mobile Menu Dropdown -->
<div id="mobile-menu" class="hidden md:hidden bg-gray-800 border-t border-gray-700">
<div class="px-2 pt-2 pb-3 space-y-1 text-center">
<a href="/" class="mobile-nav-link">Home</a>
<a href="./about.html" class="mobile-nav-link">About</a>
<a href="./contact.html" class="mobile-nav-link">Contact</a>
<a href="/" class="mobile-nav-link">All Tools</a>
</div>
</div>
</nav>
<div id="uploader" class="min-h-screen flex items-center justify-center p-4 bg-gray-900">
<div id="tool-uploader"
class="bg-gray-800 rounded-xl shadow-xl p-8 max-w-2xl w-full text-gray-200 border border-gray-700">
<button id="back-to-tools"
class="flex items-center gap-2 text-indigo-400 hover:text-indigo-300 mb-6 font-semibold">
<i data-lucide="arrow-left" class="cursor-pointer"></i>
<span class="cursor-pointer"> Back to Tools </span>
</button>
<h1 class="text-2xl font-bold text-white mb-2">Edit Bookmarks</h1>
<p class="text-gray-400 mb-6">
Add, edit, import, delete and extract PDF bookmarks.
</p>
<!-- Drop Zone for Main PDF Upload -->
<div id="drop-zone"
class="relative flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-700 hover:bg-gray-600 transition-colors duration-300">
<div class="flex flex-col items-center justify-center pt-5 pb-6">
<i data-lucide="upload-cloud" class="w-10 h-10 mb-3 text-gray-400"></i>
<p class="mb-2 text-sm text-gray-300">
<span class="font-semibold">Click to select a file</span> or drag
and drop
</p>
<p class="text-xs text-gray-500">A single PDF file</p>
<p class="text-xs text-gray-500">
Your files never leave your device.
</p>
</div>
<input type="file" id="file-input" accept=".pdf"
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" />
</div>
<!-- File Display Area -->
<div id="file-display-area" class="mt-4 hidden"></div>
<!-- Auto Extract Checkbox -->
<div class="mt-4">
<label class="flex items-center gap-2 text-sm text-gray-300">
<input type="checkbox" id="auto-extract-checkbox" class="w-4 h-4 accent-blue-500" />
<span>Auto-extract existing bookmarks</span>
</label>
</div>
<!-- Import Bookmarks Section -->
<div class="mt-8 pt-6 border-t border-gray-700">
<h3 class="text-sm font-semibold text-gray-300 mb-4">
Or Import Bookmarks
</h3>
<!-- CSV Upload -->
<label class="block mb-4">
<span class="text-xs text-gray-500">CSV format: title,page,level</span>
<div
class="relative flex flex-col items-center justify-center w-full h-32 mt-2 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-800 hover:bg-gray-700 transition-colors duration-300">
<div class="flex flex-col items-center justify-center pt-4 pb-4">
<i data-lucide="sheet" class="w-8 h-8 mb-2 text-gray-400"></i>
<p class="text-xs text-gray-400">
<span class="font-semibold">Click to upload CSV</span> or drag
here
</p>
</div>
<input type="file" id="csv-input" accept=".csv"
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" />
</div>
</label>
<!-- JSON Upload -->
<label class="block">
<span class="text-xs text-gray-500">JSON format</span>
<div
class="relative flex flex-col items-center justify-center w-full h-32 mt-2 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-800 hover:bg-gray-700 transition-colors duration-300">
<div class="flex flex-col items-center justify-center pt-4 pb-4">
<i data-lucide="braces" class="w-8 h-8 mb-2 text-gray-400"></i>
<p class="text-xs text-gray-400">
<span class="font-semibold">Click to upload JSON</span> or
drag here
</p>
</div>
<input type="file" id="json-input" accept=".json"
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" />
</div>
</label>
</div>
</div>
</div>
<div id="app" class="hidden bg-gray-900 min-h-screen">
<header class="bg-gray-800 border-b border-gray-700 shadow-sm sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 py-3">
<div class="flex items-center justify-between flex-wrap gap-2">
<h1 class="text-xl font-bold text-white" id="filename-display">
PDF Bookmark Editor
</h1>
<div class="flex gap-2 flex-wrap">
<button id="undo-btn"
class="px-3 py-2 text-sm bg-gray-700 hover:bg-gray-600 text-white rounded disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1"
disabled>
<i data-lucide="undo-2" class="w-4 h-4"></i> Undo
</button>
<button id="redo-btn"
class="px-3 py-2 text-sm bg-gray-700 hover:bg-gray-600 text-white rounded disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-1"
disabled>
<i data-lucide="redo-2" class="w-4 h-4"></i> Redo
</button>
<button id="delete-all-btn"
class="px-3 py-2 text-sm bg-red-600 hover:bg-red-700 text-white rounded flex items-center gap-1">
<i data-lucide="trash-2" class="w-4 h-4"></i> Delete All
</button>
<button id="reset-btn"
class="px-3 py-2 text-sm bg-orange-600 hover:bg-orange-700 text-white rounded flex items-center gap-1">
<i data-lucide="rotate-ccw" class="w-4 h-4"></i> Reset
</button>
<button id="back-btn"
class="px-3 py-2 text-sm bg-gray-700 hover:bg-gray-600 text-white rounded flex items-center gap-1">
<i data-lucide="arrow-left" class="w-4 h-4"></i> Back to Tools
</button>
</div>
</div>
</div>
</header>
<div class="lg:hidden bg-gray-800 border-b border-gray-700">
<div class="flex">
<button id="show-viewer-btn" class="flex-1 py-3 text-sm font-medium bg-indigo-600 text-white">
PDF Viewer
</button>
<button id="show-bookmarks-btn" class="flex-1 py-3 text-sm font-medium text-gray-300 hover:text-white">
Bookmarks
</button>
</div>
</div>
<div class="max-w-7xl mx-auto p-4">
<div class="flex flex-col lg:flex-row gap-4">
<div id="viewer-section" class="w-full lg:w-[50%] flex-shrink-0">
<div class="bg-white rounded-lg shadow-lg p-4">
<div class="flex flex-wrap items-center justify-between gap-2 mb-4">
<div class="flex items-center gap-2">
<button id="prev-page" class="px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded text-sm">
<i data-lucide="chevron-left" class="w-5 h-5 text-blue-600"></i>
</button>
<span id="page-indicator" class="text-sm font-medium text-gray-700 whitespace-nowrap">Page 1 / 1</span>
<button id="next-page" class="px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded text-sm">
<i data-lucide="chevron-right" class="w-5 h-5 text-blue-600"></i>
</button>
</div>
<div class="flex items-center gap-2">
<span class="text-sm text-gray-600">Go to:</span>
<input type="number" id="goto-page" min="1"
class="w-16 px-2 py-1 border border-gray-300 rounded text-sm" />
<button id="goto-btn" class="px-3 py-1 bg-blue-500 hover:bg-blue-600 text-white rounded text-sm">
Go
</button>
</div>
<div class="flex items-center gap-2">
<button id="zoom-out-btn" class="px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded text-sm"
title="Zoom Out">
<i data-lucide="minus" class="w-5 h-5 text-blue-600"></i>
</button>
<span id="zoom-indicator"
class="text-sm font-medium text-gray-700 whitespace-nowrap min-w-[60px] text-center">100%</span>
<button id="zoom-in-btn" class="px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded text-sm"
title="Zoom In">
<i data-lucide="plus" class="w-5 h-5 text-blue-600"></i>
</button>
<button id="zoom-fit-btn" class="px-3 py-1 bg-gray-100 hover:bg-gray-200 rounded text-sm"
title="Fit to Width">
Fit
</button>
</div>
</div>
<div id="picking-mode-banner" class="hidden mb-4 p-3 bg-blue-100 border-2 border-blue-400 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<i data-lucide="crosshair" class="w-5 h-5 text-blue-600"></i>
<span class="text-sm font-medium text-blue-800">Click on the PDF to set bookmark destination</span>
</div>
<button id="cancel-picking-btn"
class="px-3 py-1 bg-red-500 hover:bg-red-600 text-white rounded text-xs">
Cancel
</button>
</div>
</div>
<div class="overflow-auto border border-gray-200 rounded" id="canvas-container">
<div class="pdf-canvas-wrapper" id="pdf-canvas-wrapper">
<canvas id="pdf-canvas" class="max-w-full h-auto mx-auto"></canvas>
</div>
</div>
</div>
</div>
<div id="bookmarks-section" class="hidden lg:block w-full lg:w-[50%] flex-shrink-0">
<div class="bg-white rounded-lg shadow-lg p-4">
<h3 class="text-lg font-bold text-gray-800 mb-4">Bookmarks</h3>
<div class="mb-4">
<div class="relative">
<i data-lucide="search"
class="absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400"></i>
<input type="text" id="search-bookmarks" placeholder="Search bookmarks..."
class="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg text-sm" />
</div>
</div>
<div class="mb-4 p-3 bg-gray-50 rounded-lg">
<input type="text" id="bookmark-title" placeholder="Bookmark title..."
class="w-full px-3 py-2 border border-gray-300 rounded-lg mb-2 text-sm" />
<button id="add-top-level-btn"
class="w-full px-3 py-2 bg-gradient-to-b from-blue-500 to-blue-600 hover:shadow-xl text-white rounded text-sm font-medium flex items-center justify-center gap-2 focus:ring-2 focus:ring-blue-400 transition duration-200">
<i data-lucide="plus" class="w-4 h-4"></i>
<span>Add to Page <span id="current-page-display">1</span></span>
</button>
</div>
<div class="mb-4">
<label class="flex items-center gap-2 text-sm font-medium text-gray-700 mb-2">
<input type="checkbox" id="batch-mode-checkbox" class="w-4 h-4" />
<span>Batch Mode (<span id="selected-count">0</span>
selected)</span>
</label>
<div id="batch-operations" class="hidden p-3 bg-blue-50 rounded-lg border border-blue-200">
<div class="text-xs font-semibold text-gray-700 mb-2">
Batch Operations
</div>
<div class="flex flex-col gap-2">
<div class="flex gap-2">
<button id="select-all-btn"
class="flex-1 px-3 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded text-xs flex items-center justify-center gap-1">
<i data-lucide="check-square" class="w-3 h-3"></i>
Select All
</button>
<button id="deselect-all-btn"
class="flex-1 px-3 py-2 bg-gray-500 hover:bg-gray-600 text-white rounded text-xs flex items-center justify-center gap-1">
<i data-lucide="square" class="w-3 h-3"></i> Deselect
All
</button>
</div>
<select id="batch-color-select" class="text-xs p-2 border rounded">
<option value="">Set Color...</option>
<option value="null">None</option>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="green">Green</option>
<option value="yellow">Yellow</option>
<option value="purple">Purple</option>
</select>
<select id="batch-style-select" class="text-xs p-2 border rounded">
<option value="">Set Style...</option>
<option value="null">Normal</option>
<option value="bold">Bold</option>
<option value="italic">Italic</option>
<option value="bold-italic">Bold & Italic</option>
</select>
<button id="batch-delete-btn"
class="px-3 py-2 bg-red-500 hover:bg-red-600 text-white rounded text-xs flex items-center justify-center gap-1">
<i data-lucide="trash-2" class="w-3 h-3"></i> Delete
Selected
</button>
</div>
</div>
</div>
<div class="flex gap-2 mb-4">
<button id="expand-all-btn"
class="flex-1 px-3 py-2 bg-gradient-to-b from-gray-100 to-gray-200 hover:shadow-md text-gray-700 rounded text-sm font-medium flex items-center justify-center gap-1 focus:ring-2 focus:ring-gray-300 transition duration-200">
<i data-lucide="chevrons-down" class="w-4 h-4"></i> Expand All
</button>
<button id="collapse-all-btn"
class="flex-1 px-3 py-2 bg-gradient-to-b from-gray-100 to-gray-200 hover:shadow-md text-gray-700 rounded text-sm font-medium flex items-center justify-center gap-1 focus:ring-2 focus:ring-gray-300 transition duration-200">
<i data-lucide="chevrons-up" class="w-4 h-4"></i> Collapse All
</button>
</div>
<div class="grid grid-cols-2 gap-2 mb-4">
<div class="relative">
<button id="import-dropdown-btn"
class="w-full px-3 py-2 bg-gradient-to-b from-gray-100 to-gray-200 hover:shadow-md text-gray-700 rounded text-sm font-medium flex items-center justify-center gap-1 focus:ring-2 focus:ring-gray-300 transition duration-200">
<i data-lucide="upload" class="w-4 h-4"></i> Import
<i data-lucide="chevron-down" class="w-3 h-3"></i>
</button>
<div id="import-dropdown"
class="hidden absolute top-full left-0 mt-1 w-full bg-white border border-gray-200 rounded-lg shadow-lg z-10">
<button id="import-csv-btn"
class="w-full px-3 py-2 text-sm text-left flex items-center gap-2 rounded-t-lg bg-gradient-to-b from-gray-50 to-gray-100 hover:shadow-sm hover:from-gray-100 hover:to-gray-200 transition duration-150">
<i data-lucide="file-text" class="w-4 h-4 text-gray-600"></i>
Import CSV
</button>
<button id="import-json-btn"
class="w-full px-3 py-2 text-sm text-left flex items-center gap-2 rounded-b-lg bg-gradient-to-b from-gray-50 to-gray-100 hover:shadow-sm hover:from-gray-100 hover:to-gray-200 transition duration-150">
<i data-lucide="braces" class="w-4 h-4 text-gray-600"></i>
Import JSON
</button>
</div>
</div>
<div class="relative">
<button id="export-dropdown-btn"
class="w-full px-3 py-2 bg-gradient-to-b from-gray-100 to-gray-200 hover:shadow-md text-gray-700 rounded text-sm font-medium flex items-center justify-center gap-1 focus:ring-2 focus:ring-gray-300 transition duration-200">
<i data-lucide="download" class="w-4 h-4"></i> Export
<i data-lucide="chevron-down" class="w-3 h-3"></i>
</button>
<div id="export-dropdown"
class="hidden absolute top-full left-0 mt-1 w-full bg-white border border-gray-200 rounded-lg shadow-lg z-10">
<button id="export-csv-btn"
class="w-full px-3 py-2 text-sm text-left hover:bg-gray-50 flex items-center gap-2 rounded-t-lg">
<i data-lucide="file-text" class="w-4 h-4"></i> Export CSV
</button>
<button id="export-json-btn"
class="w-full px-3 py-2 text-sm text-left hover:bg-gray-50 flex items-center gap-2 rounded-b-lg">
<i data-lucide="braces" class="w-4 h-4"></i> Export JSON
</button>
</div>
</div>
</div>
<div class="mb-4 max-h-96 overflow-y-auto border border-gray-200 rounded-lg p-2">
<ul id="bookmark-tree-list" class="space-y-2"></ul>
<div id="no-bookmarks" class="text-center py-8 text-gray-400 text-sm">
No bookmarks yet. Add one above!
</div>
</div>
<div class="space-y-2">
<button id="download-btn"
class="w-full px-4 py-2 bg-gradient-to-b from-blue-600 to-blue-700 hover:shadow-xl text-white rounded font-medium flex items-center justify-center gap-2 focus:ring-2 focus:ring-blue-400 transition duration-200">
Save PDF with Bookmarks
</button>
<button id="extract-existing-btn"
class="w-full px-4 py-2 bg-gradient-to-b from-green-500 to-green-600 hover:shadow-xl text-white rounded font-medium flex items-center justify-center gap-2 focus:ring-2 focus:ring-green-400 transition duration-200">
Extract Existing Bookmarks
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<input type="file" id="csv-import-hidden" accept=".csv" class="hidden" />
<input type="file" id="json-import-hidden" accept=".json" class="hidden" />
<div id="modal-container"></div>
<footer class="mt-16 border-t-2 border-gray-700 py-8">
<div class="container mx-auto px-4">
<div class="flex items-center gap-3 mb-2">
<img src="../../public/images/favicon.svg" alt="BentoPDF Logo" class="h-8 w-8" />
<span class="text-white font-bold text-xl">BentoPDF</span>
</div>
<p class="text-gray-400 text-sm">
&copy; 2025 BentoPDF. All rights reserved.
</p>
<p class="text-gray-500 text-xs mt-1">
Version <span id="app-version"></span>
</p>
</div>
</footer>
<script type="module" src="../js/utils/lucide-init.ts"></script>
<script type="module" src="../js/utils/full-width.ts"></script>
2025-11-18 12:35:12 +01:00
<script type="module" src="../version.ts"></script>
<script type="module" src="../js/logic/bookmark-pdf.ts"></script>
<script type="module" src="../js/mobileMenu.ts"></script>
</body>
</html>