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
This commit is contained in:
@@ -339,7 +339,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|
||||||
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
||||||
|
|
||||||
<script type="module" src="src/js/mobileMenu.ts"></script>
|
<script type="module" src="src/js/mobileMenu.ts"></script>
|
||||||
|
|||||||
@@ -78,11 +78,10 @@ export const categories = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
// id: 'bookmark-pdf',
|
// id: 'bookmark-pdf',
|
||||||
href: '/src/pages/bookmark.html',
|
href: '/src/pages/bookmark.html',
|
||||||
name: 'Edit Bookmarks',
|
name: 'Edit Bookmarks',
|
||||||
icon: 'bookmark',
|
icon: 'bookmark',
|
||||||
subtitle:
|
subtitle: 'Add, edit, import, delete and extract PDF bookmarks.',
|
||||||
'Add, edit, import, delete and extract PDF bookmarks.',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'add-page-numbers',
|
id: 'add-page-numbers',
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
|
// TODO: @ALAM - remove ts-nocheck and fix types later, possibly convert this into an npm package
|
||||||
|
|
||||||
import { PDFDocument, PDFName, PDFString, PDFNumber, PDFArray } from 'pdf-lib';
|
import { PDFDocument, PDFName, PDFString, PDFNumber, PDFArray } from 'pdf-lib';
|
||||||
import * as pdfjsLib from 'pdfjs-dist';
|
import * as pdfjsLib from 'pdfjs-dist';
|
||||||
import Sortable from 'sortablejs';
|
import Sortable from 'sortablejs';
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ const init = () => {
|
|||||||
'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 md:gap-6';
|
'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) => {
|
category.tools.forEach((tool) => {
|
||||||
let toolCard: HTMLDivElement | HTMLAnchorElement;
|
let toolCard: HTMLDivElement | HTMLAnchorElement;
|
||||||
|
|
||||||
if (tool.href) {
|
if (tool.href) {
|
||||||
toolCard = document.createElement('a');
|
toolCard = document.createElement('a');
|
||||||
@@ -262,5 +262,3 @@ const init = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', init);
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -179,12 +179,11 @@ export async function initializeQpdf() {
|
|||||||
return qpdfInstance;
|
return qpdfInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function initializeIcons(): void {
|
export function initializeIcons(): void {
|
||||||
createIcons({
|
createIcons({
|
||||||
attrs: {
|
attrs: {
|
||||||
class: 'bento-icon',
|
class: 'bento-icon',
|
||||||
'stroke-width': '1.5',
|
'stroke-width': '1.5',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ import { createIcons, icons } from 'lucide';
|
|||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
createIcons({ icons });
|
createIcons({ icons });
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
<style>
|
<style>
|
||||||
.sortable-ghost {
|
.sortable-ghost {
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
border: 2px dashed #2b7fff;
|
border: 2px dashed #2b7fff;
|
||||||
}
|
}
|
||||||
.sortable-drag {
|
.sortable-drag {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-50">
|
<body class="bg-gray-900">
|
||||||
<nav class="bg-gray-800 border-b border-gray-700 sticky top-0 z-30">
|
<nav class="bg-gray-800 border-b border-gray-700 sticky top-0 z-30">
|
||||||
<div class="container mx-auto px-4">
|
<div class="container mx-auto px-4">
|
||||||
<div class="flex justify-between items-center h-16">
|
<div class="flex justify-between items-center h-16">
|
||||||
@@ -231,51 +231,106 @@
|
|||||||
id="uploader"
|
id="uploader"
|
||||||
class="min-h-screen flex items-center justify-center p-4"
|
class="min-h-screen flex items-center justify-center p-4"
|
||||||
>
|
>
|
||||||
<div class="bg-white rounded-lg shadow-lg p-8 max-w-md w-full">
|
<div
|
||||||
<h2 class="text-2xl font-bold text-gray-800 mb-4">PDF Bookmark Tool</h2>
|
class="bg-gray-900 rounded-xl shadow-xl p-8 max-w-md w-full text-gray-200"
|
||||||
<p class="text-gray-600 mb-6">
|
>
|
||||||
|
<p class="text-gray-400 mb-6">
|
||||||
Upload a PDF to begin editing bookmarks
|
Upload a PDF to begin editing bookmarks
|
||||||
</p>
|
</p>
|
||||||
<input
|
|
||||||
type="file"
|
|
||||||
id="file-input"
|
|
||||||
accept=".pdf"
|
|
||||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50"
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
<!-- 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">
|
<div class="mt-4">
|
||||||
<label class="flex items-center gap-2 text-sm text-gray-700">
|
<label class="flex items-center gap-2 text-sm text-gray-300">
|
||||||
<input type="checkbox" id="auto-extract-checkbox" class="w-4 h-4" />
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="auto-extract-checkbox"
|
||||||
|
class="w-4 h-4 accent-blue-500"
|
||||||
|
/>
|
||||||
<span>Auto-extract existing bookmarks</span>
|
<span>Auto-extract existing bookmarks</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6 pt-6 border-t border-gray-200">
|
<!-- Import Bookmarks Section -->
|
||||||
<h3 class="text-sm font-semibold text-gray-700 mb-3">
|
<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
|
Or Import Bookmarks
|
||||||
</h3>
|
</h3>
|
||||||
<div class="space-y-2">
|
|
||||||
<label class="block">
|
<!-- CSV Upload -->
|
||||||
<span class="text-xs text-gray-500"
|
<label class="block mb-4">
|
||||||
>CSV format: title,page,level</span
|
<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="file-text"
|
||||||
|
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
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
id="csv-input"
|
id="csv-input"
|
||||||
accept=".csv"
|
accept=".csv"
|
||||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50 text-sm mt-1"
|
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</label>
|
</div>
|
||||||
<label class="block">
|
</label>
|
||||||
<span class="text-xs text-gray-500">JSON format</span>
|
|
||||||
|
<!-- 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
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
id="json-input"
|
id="json-input"
|
||||||
accept=".json"
|
accept=".json"
|
||||||
class="w-full px-4 py-2 border border-gray-300 rounded-lg cursor-pointer hover:bg-gray-50 text-sm mt-1"
|
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer"
|
||||||
/>
|
/>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user