Compare commits

..
11 Commits
Author SHA1 Message Date
abdullahalam123 9bd193cd9d Release v1.2.0 2025-11-07 11:11:49 +05:30
abdullahalam123 b8cd43c9ec Merge branch 'bookmarks' 2025-11-07 11:11:10 +05:30
abdullahalam123 400ec9aadf style(ui): update color scheme and fix styling inconsistencies
- Change grey to gray for consistency in CSS class names
- Update page title and add favicon
- Improve button and background colors for better contrast
- Fix mobile menu script path
2025-11-07 11:10:22 +05:30
abdullahalam123 f40108627e feat(pdf-viewer): add zoom controls and improve destination picking
- Add zoom in/out/fit buttons with indicator
- Improve destination picking with coordinate display and PDF coordinate conversion
- Enhance page rendering with device pixel ratio support
- Add visual feedback for bookmark destinations
2025-11-07 10:46:39 +05:30
abdullahalam123 e64215a022 feat(bookmark): improve PDF bookmark extraction logic
enhance named destinations handling to support full name-tree traversal
add fallback page index resolution for better compatibility
fix parent-child relationship handling for zero-page bookmarks
2025-11-07 09:31:57 +05:30
Alam af75a3d74f Update SIMPLE_MODE.md 2025-10-29 16:37:31 +05:30
abdullahalam123 ad49adb744 feat(bookmark-pdf): improve bookmark extraction with reference resolution
add helper functions to resolve PDF object references and handle named destinations
implement more robust page index finding and destination resolution
refactor traversal logic to use resolved references consistently
2025-10-28 01:24:34 +05:30
Alam 392b810c54 Merge pull request #115 from AltuisticIsopod/main
Add GHCR publishing support
2025-10-28 00:23:21 +05:30
abdullahalam123 090146c600 feat(bookmark): improve pdf bookmark tool ui with dark mode and drag-drop
- Add dark mode styling to the bookmark tool page
- Implement drag-and-drop file upload zones for PDF, CSV and JSON
- Enhance file input UI with icons and better visual feedback
- Fix formatting and whitespace issues in multiple files
2025-10-27 22:35:49 +05:30
abdullahalam123 72e74d2a62 feat(pdf-tools): add bookmark pdf tool and lucide icons integration
- Add 'bookmark-pdf' to singlePdfLoadTools
- Create new lucide-init.ts for icon initialization
- Update HTML files to use local lucide initialization
- Add bookmark.html page with full bookmark editor functionality
- Update tools config to include bookmark tool
- Modify main.ts to handle tool cards with href links
2025-10-27 22:01:36 +05:30
Lalit Sudhir c49957b721 Add GHCR publishing support 2025-10-27 00:31:18 -07:00
18 changed files with 3138 additions and 47 deletions
+9
View File
@@ -33,6 +33,12 @@ jobs:
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract version from tag
id: version
run: |
@@ -55,6 +61,9 @@ jobs:
bentopdf/bentopdf${{ matrix.mode.suffix }}:latest
bentopdf/bentopdf${{ matrix.mode.suffix }}:${{ steps.version.outputs.version_without_v }}
bentopdf/bentopdf${{ matrix.mode.suffix }}:${{ steps.version.outputs.version }}
ghcr.io/${{ github.repository_owner }}/bentopdf${{ matrix.mode.suffix }}:latest
ghcr.io/${{ github.repository_owner }}/bentopdf${{ matrix.mode.suffix }}:${{ steps.version.outputs.version_without_v }}
ghcr.io/${{ github.repository_owner }}/bentopdf${{ matrix.mode.suffix }}:${{ steps.version.outputs.version }}
platforms: linux/amd64,linux/arm64
cache-from: type=gha
cache-to: type=gha,mode=max
+14 -2
View File
@@ -102,15 +102,21 @@ You can run BentoPDF locally for development or personal use.
### 🚀 Quick Start with Docker
You can run BentoPDF directly from Docker Hub without cloning the repository:
You can run BentoPDF directly from Docker Hub or GitHub Container Registry without cloning the repository:
You can also watch the video on how to set it up 👉
[BentoPDF Docker Setup](https://drive.google.com/file/d/1C4eJ2nqeaH__1Tlad-xuBHaF2Ha4fSBf/view?usp=drive_link)
**Using Docker Hub:**
```bash
docker run -p 3000:8080 bentopdf/bentopdf:latest
```
**Using GitHub Container Registry:**
```bash
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest
```
Open your browser at: http://localhost:3000
This is the fastest way to try BentoPDF without setting up a development environment.
@@ -171,12 +177,18 @@ For detailed security configuration, see [SECURITY.md](SECURITY.md).
### 📦 Version Management
BentoPDF supports semantic versioning with multiple Docker tags:
BentoPDF supports semantic versioning with multiple Docker tags available on both Docker Hub and GitHub Container Registry:
**Docker Hub:**
- **Latest**: `bentopdf/bentopdf:latest`
- **Specific Version**: `bentopdf/bentopdf:1.0.0`
- **Version with Prefix**: `bentopdf/bentopdf:v1.0.0`
**GitHub Container Registry:**
- **Latest**: `ghcr.io/alam00000/bentopdf:latest`
- **Specific Version**: `ghcr.io/alam00000/bentopdf:1.0.0`
- **Version with Prefix**: `ghcr.io/alam00000/bentopdf:v1.0.0`
#### Quick Release
```bash
+24 -6
View File
@@ -23,20 +23,28 @@ When enabled, Simple Mode will:
Use the pre-built Simple Mode image directly:
**Using Docker Hub:**
```bash
docker run -p 3000:80 bentopdf/bentopdf-simple:latest
docker run -p 3000:8080 bentopdf/bentopdf-simple:latest
```
**Using GitHub Container Registry:**
```bash
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf-simple:latest
```
Or with Docker Compose:
```yaml
services:
bentopdf:
# Using Docker Hub
image: bentopdf/bentopdf-simple:latest
# Or using GitHub Container Registry
# image: ghcr.io/alam00000/bentopdf-simple:latest
container_name: bentopdf
restart: unless-stopped
ports:
- '3000:80'
- '3000:8080'
```
### Method 2: Using Docker Compose with Build
@@ -54,7 +62,7 @@ Build the image with the SIMPLE_MODE build argument:
```bash
docker build --build-arg SIMPLE_MODE=true -t bentopdf-simple .
docker run -p 3000:80 bentopdf-simple
docker run -p 3000:8080 bentopdf-simple
```
### Method 4: Using npm Script (Easiest for Local Development)
@@ -96,7 +104,7 @@ This automatically builds and serves Simple Mode on `http://localhost:3000`.
```bash
# Pull and run the Simple Mode image
docker pull bentopdf/bentopdf-simple:latest
docker run -p 3000:80 bentopdf/bentopdf-simple:latest
docker run -p 3000:8080 bentopdf/bentopdf-simple:latest
```
Open `http://localhost:3000` in your browser.
@@ -117,10 +125,10 @@ Open `http://localhost:3000` in your browser.
```bash
# Test Normal Mode
docker run -p 3000:80 bentopdf/bentopdf:latest
docker run -p 3000:8080 bentopdf/bentopdf:latest
# Test Simple Mode
docker run -p 3001:80 bentopdf/bentopdf-simple:latest
docker run -p 3001:8080 bentopdf/bentopdf-simple:latest
```
- Normal Mode: `http://localhost:3000`
@@ -142,14 +150,24 @@ When Simple Mode is working correctly, you should see:
### Normal Mode (Full Branding)
**Docker Hub:**
- `bentopdf/bentopdf:latest`
- `bentopdf/bentopdf:v1.0.0` (versioned)
**GitHub Container Registry:**
- `ghcr.io/alam00000/bentopdf:latest`
- `ghcr.io/alam00000/bentopdf:v1.0.0` (versioned)
### Simple Mode (Clean Interface)
**Docker Hub:**
- `bentopdf/bentopdf-simple:latest`
- `bentopdf/bentopdf-simple:v1.0.0` (versioned)
**GitHub Container Registry:**
- `ghcr.io/alam00000/bentopdf-simple:latest`
- `ghcr.io/alam00000/bentopdf-simple:v1.0.0` (versioned)
## 🚀 Production Deployment Examples
### Internal Company Tool
+2 -7
View File
@@ -4,10 +4,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>About Bentopdf - Fast, Private, and Free PDF Tools</title>
<script src="https://unpkg.com/lucide@latest"></script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="icon" type="image/png" href="images/favicon.svg" />
<link href="/src/css/styles.css" rel="stylesheet" />
</head>
@@ -343,9 +339,8 @@
</div>
</footer>
<script>
lucide.createIcons();
</script>
<script type="module" src="src/js/utils/lucide-init.ts"></script>
<script type="module" src="src/js/mobileMenu.ts"></script>
</body>
</html>
+2 -5
View File
@@ -4,8 +4,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Contact Us - BentoPDF</title>
<script src="https://unpkg.com/lucide@latest"></script>
<link rel="icon" type="image/png" href="images/favicon.svg" />
<link href="/src/css/styles.css" rel="stylesheet" />
</head>
@@ -208,9 +206,8 @@
</div>
</footer>
<script>
lucide.createIcons();
</script>
<script type="module" src="src/js/utils/lucide-init.ts"></script>
<script type="module" src="src/js/mobileMenu.ts"></script>
</body>
</html>
+2 -8
View File
@@ -4,11 +4,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Frequently Asked Questions - BentoPDF</title>
<script src="https://unpkg.com/lucide@latest"></script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="icon" type="image/png" href="images/favicon.svg" />
<link href="/src/css/styles.css" rel="stylesheet" />
</head>
@@ -396,9 +391,8 @@
</div>
</footer>
<script>
lucide.createIcons();
</script>
<script type="module" src="src/js/utils/lucide-init.ts"></script>
<script type="module" src="src/js/mobileMenu.ts"></script>
</body>
</html>
+1
View File
@@ -915,6 +915,7 @@
</div>
</footer>
<script type="module" src="src/js/utils/lucide-init.ts"></script>
<script type="module" src="src/js/main.ts"></script>
<script type="module" src="src/js/mobileMenu.ts"></script>
</body>
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "bento-pdf",
"private": true,
"version": "1.1.5",
"version": "1.2.0",
"license": "Apache-2.0",
"type": "module",
"scripts": {
+2 -7
View File
@@ -4,10 +4,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Privacy Policy - BentoPDF</title>
<script src="https://unpkg.com/lucide@latest"></script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="icon" type="image/png" href="images/favicon.svg" />
<link href="/src/css/styles.css" rel="stylesheet" />
</head>
@@ -318,9 +314,8 @@
</div>
</div>
</footer>
<script>
lucide.createIcons();
</script>
<script type="module" src="src/js/utils/lucide-init.ts"></script>
<script type="module" src="src/js/mobileMenu.ts"></script>
</body>
</html>
+1
View File
@@ -40,6 +40,7 @@ export const singlePdfLoadTools = [
'add-attachments',
'sanitize-pdf',
'remove-restrictions',
'bookmark-pdf',
];
export const simpleTools = [
+7 -1
View File
@@ -76,7 +76,13 @@ export const categories = [
subtitle:
'Annotate, highlight, redact, comment, add shapes/images, search, and view PDFs.',
},
// { id: 'crop', name: 'Crop PDF', icon: 'crop', subtitle: 'Trim the margins of every page in your PDF.' },
{
// id: 'bookmark-pdf',
href: '/src/pages/bookmark.html',
name: 'Edit Bookmarks',
icon: 'bookmark',
subtitle: 'Add, edit, import, delete and extract PDF bookmarks.',
},
{
id: 'add-page-numbers',
name: 'Page Numbers',
File diff suppressed because it is too large Load Diff
+13 -4
View File
@@ -137,10 +137,19 @@ const init = () => {
'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 md:gap-6';
category.tools.forEach((tool) => {
const toolCard = document.createElement('div');
toolCard.className =
'tool-card bg-gray-800 rounded-xl p-4 cursor-pointer flex flex-col items-center justify-center text-center';
toolCard.dataset.toolId = tool.id;
let toolCard: HTMLDivElement | HTMLAnchorElement;
if (tool.href) {
toolCard = document.createElement('a');
toolCard.href = tool.href;
toolCard.className =
'tool-card block bg-gray-800 rounded-xl p-4 cursor-pointer flex flex-col items-center justify-center text-center no-underline hover:shadow-lg transition duration-200';
} else {
toolCard = document.createElement('div');
toolCard.className =
'tool-card bg-gray-800 rounded-xl p-4 cursor-pointer flex flex-col items-center justify-center text-center hover:shadow-lg transition duration-200';
toolCard.dataset.toolId = tool.id;
}
const icon = document.createElement('i');
icon.className = 'w-10 h-10 mb-3 text-indigo-400';
+10
View File
@@ -1,5 +1,6 @@
import createModule from '@neslinesli93/qpdf-wasm';
import { showLoader, hideLoader, showAlert } from '../ui';
import { createIcons } from 'lucide';
const STANDARD_SIZES = {
A4: { width: 595.28, height: 841.89 },
@@ -177,3 +178,12 @@ export async function initializeQpdf() {
return qpdfInstance;
}
export function initializeIcons(): void {
createIcons({
attrs: {
class: 'bento-icon',
'stroke-width': '1.5',
},
});
}
+5
View File
@@ -0,0 +1,5 @@
import { createIcons, icons } from 'lucide';
document.addEventListener('DOMContentLoaded', () => {
createIcons({ icons });
});
+867
View File
@@ -0,0 +1,867 @@
<!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" />
<style>
body {
color: #374151;
}
.sortable-ghost {
opacity: 0.4;
border: 2px dashed #2b7fff;
}
.sortable-drag {
cursor: move;
}
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 1rem;
}
.modal-content {
background: white;
border-radius: 0.5rem;
max-width: 500px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
}
.modal-minimized {
position: fixed;
top: 1rem;
right: 1rem;
max-width: 350px;
max-height: none;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
z-index: 1001;
}
.child-container {
position: relative;
padding-left: 1.5rem;
margin-top: 0.5rem;
}
.child-container::before {
content: '';
position: absolute;
left: 0.75rem;
top: 0;
bottom: 0;
width: 2px;
background: #e5e7eb;
}
.child-container > li {
position: relative;
}
.child-container > li::before {
content: '';
position: absolute;
left: -0.75rem;
top: 1.25rem;
width: 0.75rem;
height: 2px;
background: #e5e7eb;
}
.style-preview {
padding: 0.5rem;
border: 1px solid #e5e7eb;
border-radius: 0.25rem;
min-height: 2.5rem;
display: flex;
align-items: center;
justify-content: center;
}
.destination-marker {
position: absolute;
width: 30px;
height: 30px;
margin-left: -15px;
margin-top: -15px;
pointer-events: none;
z-index: 10;
}
.destination-marker svg {
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
}
.pdf-canvas-wrapper {
position: relative;
display: inline-block;
}
.pdf-canvas-wrapper.picking-mode {
cursor: crosshair;
}
.coordinate-tooltip {
position: absolute;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
pointer-events: none;
z-index: 20;
white-space: nowrap;
}
.destination-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(59, 130, 246, 0.1);
border: 2px solid #3b82f6;
pointer-events: none;
z-index: 5;
}
</style>
</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="index.html">BentoPDF</a>
</span>
</div>
<!-- Desktop Navigation -->
<div class="hidden md:flex items-center space-x-8 text-white">
<a href="index.html" 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="index.html#tools-header" 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="index.html" 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="index.html#tools-header" 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
class="bg-gray-900 rounded-xl shadow-xl p-8 max-w-md w-full text-gray-200"
>
<p class="text-gray-400 mb-6">
Upload a PDF to begin editing 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-800 hover:bg-gray-700 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>
<!-- 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>
</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"
>
←
</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"
>
→
</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"
>
−
</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"
>
+
</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 bg-[#111827]">
<div class="container mx-auto px-4">
<div
class="grid grid-cols-1 md:grid-cols-4 gap-8 text-center md:text-left"
>
<div class="mb-8 md:mb-0">
<div class="flex items-center justify-center md:justify-start mb-4">
<img
src="../../public/images/favicon.svg"
alt="Bento PDF Logo"
class="h-10 w-10 mr-3"
/>
<span class="text-xl font-bold text-white">BentoPDF</span>
</div>
<p class="text-gray-400 text-sm">
&copy; 2025 BentoPDF. All rights reserved.
</p>
</div>
<div>
<h3 class="font-bold text-white mb-4">Company</h3>
<ul class="space-y-2 text-gray-400">
<li>
<a href="./about.html" class="hover:text-indigo-400"
>About Us</a
>
</li>
<li>
<a href="./faq.html" class="hover:text-indigo-400">FAQ</a>
</li>
<li>
<a href="./contact.html" class="hover:text-indigo-400"
>Contact Us</a
>
</li>
</ul>
</div>
<div>
<h3 class="font-bold text-white mb-4">Legal</h3>
<ul class="space-y-2 text-gray-400">
<li>
<a href="./terms.html" class="hover:text-indigo-400"
>Terms and Conditions</a
>
</li>
<li>
<a href="./privacy.html" class="hover:text-indigo-400"
>Privacy Policy</a
>
</li>
</ul>
</div>
<div>
<h3 class="font-bold text-white mb-4">Follow Us</h3>
<div class="flex justify-center md:justify-start space-x-4">
<a
href="https://github.com/alam00000/bentopdf"
target="_blank"
rel="noopener noreferrer"
class="text-gray-400 hover:text-indigo-400"
title="GitHub"
>
<svg
class="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
fill-rule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clip-rule="evenodd"
/>
</svg>
</a>
<a
href="https://discord.gg/q42xWQmJ"
target="_blank"
rel="noopener noreferrer"
class="text-gray-400 hover:text-indigo-400"
title="Discord"
>
<svg
class="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"
/>
</svg>
</a>
<a
href="https://www.instagram.com/thebentopdf/"
class="text-gray-400 hover:text-indigo-400"
title="Instagram"
>
<i data-lucide="instagram"></i>
</a>
<a
href="https://www.linkedin.com/company/bentopdf/"
class="text-gray-400 hover:text-indigo-400"
title="LinkedIn"
>
<i data-lucide="linkedin"></i>
</a>
<a
href="https://x.com/BentoPDF"
class="text-gray-400 hover:text-indigo-400"
title="X (Twitter)"
>
<svg
class="w-6 h-6"
fill="currentColor"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
/>
</svg>
</a>
</div>
</div>
</div>
</div>
</footer>
<script type="module" src="../js/utils/lucide-init.ts"></script>
<script type="module" src="../js/logic/bookmark-pdf.ts"></script>
<script type="module" src="../js/mobileMenu.ts"></script>
</body>
</html>
+2 -6
View File
@@ -4,9 +4,6 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Terms and Conditions - Bentopdf</title>
<script src="https://unpkg.com/lucide@latest"></script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="icon" type="image/png" href="images/favicon.svg" />
<link href="/src/css/styles.css" rel="stylesheet" />
</head>
@@ -307,9 +304,8 @@
</div>
</div>
</footer>
<script>
lucide.createIcons();
</script>
<script type="module" src="src/js/utils/lucide-init.ts"></script>
<script type="module" src="src/js/mobileMenu.ts"></script>
</body>
</html>
+1
View File
@@ -36,6 +36,7 @@ export default defineConfig(({ mode }) => ({
faq: resolve(__dirname, 'faq.html'),
privacy: resolve(__dirname, 'privacy.html'),
terms: resolve(__dirname, 'terms.html'),
bookmark: resolve(__dirname, 'src/pages/bookmark.html'),
},
},
},