2025-10-12 11:55:45 +05:30
|
|
|
import { categories } from './config/tools.js';
|
|
|
|
|
import { dom, switchView, hideAlert } from './ui.js';
|
|
|
|
|
import { setupToolInterface } from './handlers/toolSelectionHandler.js';
|
|
|
|
|
import { createIcons, icons } from 'lucide';
|
|
|
|
|
import * as pdfjsLib from 'pdfjs-dist';
|
2025-10-17 11:37:32 +05:30
|
|
|
import '../css/styles.css';
|
2025-10-12 11:55:45 +05:30
|
|
|
|
|
|
|
|
const init = () => {
|
2025-10-17 11:37:32 +05:30
|
|
|
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
|
|
|
|
|
'pdfjs-dist/build/pdf.worker.min.mjs',
|
|
|
|
|
import.meta.url
|
|
|
|
|
).toString();
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-21 00:03:23 -07:00
|
|
|
// Handle simple mode - hide branding sections but keep logo and copyright
|
2025-10-20 01:42:50 -07:00
|
|
|
if (__SIMPLE_MODE__) {
|
2025-10-20 13:01:52 -07:00
|
|
|
const hideBrandingSections = () => {
|
2025-10-21 00:03:23 -07:00
|
|
|
// Hide navigation but keep logo
|
2025-10-20 13:01:52 -07:00
|
|
|
const nav = document.querySelector('nav');
|
|
|
|
|
if (nav) {
|
2025-10-21 00:03:23 -07:00
|
|
|
// Hide the entire nav but we'll create a minimal one with just logo
|
2025-10-20 13:01:52 -07:00
|
|
|
nav.style.display = 'none';
|
2025-10-21 13:38:54 +05:30
|
|
|
|
2025-10-21 00:03:23 -07:00
|
|
|
// Create a simple nav with just logo on the right
|
|
|
|
|
const simpleNav = document.createElement('nav');
|
2025-10-21 13:38:54 +05:30
|
|
|
simpleNav.className =
|
|
|
|
|
'bg-gray-800 border-b border-gray-700 sticky top-0 z-30';
|
2025-10-21 00:03:23 -07:00
|
|
|
simpleNav.innerHTML = `
|
|
|
|
|
<div class="container mx-auto px-4">
|
|
|
|
|
<div class="flex justify-start items-center h-16">
|
|
|
|
|
<div class="flex-shrink-0 flex items-center">
|
|
|
|
|
<img src="images/favicon.svg" alt="Bento PDF Logo" class="h-8 w-8">
|
|
|
|
|
<span class="text-white font-bold text-xl ml-2">BentoPDF</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
document.body.insertBefore(simpleNav, document.body.firstChild);
|
2025-10-20 13:01:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const heroSection = document.getElementById('hero-section');
|
|
|
|
|
if (heroSection) {
|
|
|
|
|
heroSection.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const featuresSection = document.getElementById('features-section');
|
|
|
|
|
if (featuresSection) {
|
|
|
|
|
featuresSection.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const securitySection = document.getElementById(
|
|
|
|
|
'security-compliance-section'
|
|
|
|
|
);
|
|
|
|
|
if (securitySection) {
|
|
|
|
|
securitySection.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const faqSection = document.getElementById('faq-accordion');
|
|
|
|
|
if (faqSection) {
|
|
|
|
|
faqSection.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-21 13:38:54 +05:30
|
|
|
const testimonialsSection = document.getElementById(
|
|
|
|
|
'testimonials-section'
|
|
|
|
|
);
|
2025-10-20 13:01:52 -07:00
|
|
|
if (testimonialsSection) {
|
|
|
|
|
testimonialsSection.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const supportSection = document.getElementById('support-section');
|
|
|
|
|
if (supportSection) {
|
|
|
|
|
supportSection.style.display = 'none';
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-21 00:03:23 -07:00
|
|
|
// Hide footer but keep copyright
|
2025-10-20 13:01:52 -07:00
|
|
|
const footer = document.querySelector('footer');
|
|
|
|
|
if (footer) {
|
|
|
|
|
footer.style.display = 'none';
|
2025-10-21 13:38:54 +05:30
|
|
|
|
2025-10-21 00:03:23 -07:00
|
|
|
const simpleFooter = document.createElement('footer');
|
|
|
|
|
simpleFooter.className = 'mt-16 border-t-2 border-gray-700 py-8';
|
|
|
|
|
simpleFooter.innerHTML = `
|
|
|
|
|
<div class="container mx-auto px-4">
|
|
|
|
|
<div class="flex items-center mb-4">
|
|
|
|
|
<img src="images/favicon.svg" alt="Bento PDF Logo" class="h-8 w-8 mr-2">
|
|
|
|
|
<span class="text-white font-bold text-lg">BentoPDF</span>
|
|
|
|
|
</div>
|
|
|
|
|
<p class="text-gray-400 text-sm">
|
|
|
|
|
© 2025 BentoPDF. All rights reserved.
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
`;
|
|
|
|
|
document.body.appendChild(simpleFooter);
|
2025-10-20 13:01:52 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const sectionDividers = document.querySelectorAll('.section-divider');
|
|
|
|
|
sectionDividers.forEach((divider) => {
|
|
|
|
|
(divider as HTMLElement).style.display = 'none';
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-21 00:03:23 -07:00
|
|
|
document.title = 'BentoPDF - PDF Tools';
|
2025-10-20 13:01:52 -07:00
|
|
|
|
|
|
|
|
const toolsHeader = document.getElementById('tools-header');
|
|
|
|
|
if (toolsHeader) {
|
|
|
|
|
const title = toolsHeader.querySelector('h2');
|
|
|
|
|
const subtitle = toolsHeader.querySelector('p');
|
|
|
|
|
if (title) {
|
|
|
|
|
title.textContent = 'PDF Tools';
|
|
|
|
|
title.className = 'text-4xl md:text-5xl font-bold text-white mb-3';
|
|
|
|
|
}
|
|
|
|
|
if (subtitle) {
|
|
|
|
|
subtitle.textContent = 'Select a tool to get started';
|
|
|
|
|
subtitle.className = 'text-lg text-gray-400';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const app = document.getElementById('app');
|
|
|
|
|
if (app) {
|
2025-10-21 00:03:23 -07:00
|
|
|
app.style.paddingTop = '1rem';
|
2025-10-20 13:01:52 -07:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-20 01:42:50 -07:00
|
|
|
hideBrandingSections();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
dom.toolGrid.textContent = '';
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
categories.forEach((category) => {
|
|
|
|
|
const categoryGroup = document.createElement('div');
|
|
|
|
|
categoryGroup.className = 'category-group col-span-full';
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
const title = document.createElement('h2');
|
|
|
|
|
title.className = 'text-xl font-bold text-indigo-400 mb-4 mt-8 first:mt-0';
|
|
|
|
|
title.textContent = category.name;
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
const toolsContainer = document.createElement('div');
|
|
|
|
|
toolsContainer.className =
|
|
|
|
|
'grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 md:gap-6';
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
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;
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
const icon = document.createElement('i');
|
|
|
|
|
icon.className = 'w-10 h-10 mb-3 text-indigo-400';
|
|
|
|
|
icon.setAttribute('data-lucide', tool.icon);
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
const toolName = document.createElement('h3');
|
|
|
|
|
toolName.className = 'font-semibold text-white';
|
|
|
|
|
toolName.textContent = tool.name;
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
toolCard.append(icon, toolName);
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
if (tool.subtitle) {
|
|
|
|
|
const toolSubtitle = document.createElement('p');
|
|
|
|
|
toolSubtitle.className = 'text-xs text-gray-400 mt-1 px-2';
|
|
|
|
|
toolSubtitle.textContent = tool.subtitle;
|
|
|
|
|
toolCard.appendChild(toolSubtitle);
|
|
|
|
|
}
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
toolsContainer.appendChild(toolCard);
|
2025-10-12 11:55:45 +05:30
|
|
|
});
|
|
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
categoryGroup.append(title, toolsContainer);
|
|
|
|
|
dom.toolGrid.appendChild(categoryGroup);
|
|
|
|
|
});
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
const searchBar = document.getElementById('search-bar');
|
|
|
|
|
const categoryGroups = dom.toolGrid.querySelectorAll('.category-group');
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
searchBar.addEventListener('input', () => {
|
|
|
|
|
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
|
|
|
|
|
const searchTerm = searchBar.value.toLowerCase().trim();
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
categoryGroups.forEach((group) => {
|
|
|
|
|
const toolCards = group.querySelectorAll('.tool-card');
|
|
|
|
|
let visibleToolsInCategory = 0;
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
toolCards.forEach((card) => {
|
|
|
|
|
const toolName = card.querySelector('h3').textContent.toLowerCase();
|
|
|
|
|
const toolSubtitle =
|
|
|
|
|
card.querySelector('p')?.textContent.toLowerCase() || '';
|
|
|
|
|
const isMatch =
|
|
|
|
|
toolName.includes(searchTerm) || toolSubtitle.includes(searchTerm);
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
card.classList.toggle('hidden', !isMatch);
|
|
|
|
|
if (isMatch) {
|
|
|
|
|
visibleToolsInCategory++;
|
2025-10-12 11:55:45 +05:30
|
|
|
}
|
2025-10-17 11:37:32 +05:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
group.classList.toggle('hidden', visibleToolsInCategory === 0);
|
2025-10-12 11:55:45 +05:30
|
|
|
});
|
2025-10-17 11:37:32 +05:30
|
|
|
});
|
|
|
|
|
|
2025-10-18 23:58:41 +05:30
|
|
|
window.addEventListener('keydown', function (e) {
|
2025-10-19 20:22:45 +05:30
|
|
|
const key = e.key.toLowerCase();
|
2025-10-18 23:58:41 +05:30
|
|
|
const isMac = navigator.userAgent.toUpperCase().includes('MAC');
|
2025-10-19 20:22:45 +05:30
|
|
|
const isCtrlK = e.ctrlKey && key === 'k';
|
|
|
|
|
const isCmdK = isMac && e.metaKey && key === 'k';
|
2025-10-18 23:58:41 +05:30
|
|
|
|
|
|
|
|
if (isCtrlK || isCmdK) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
searchBar.focus();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const shortcutK = document.getElementById('shortcut');
|
2025-10-19 20:22:45 +05:30
|
|
|
const isIosOrAndroid = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
|
|
|
|
|
|
|
|
|
|
if (isIosOrAndroid) {
|
|
|
|
|
shortcutK.style.display = 'none';
|
|
|
|
|
} else {
|
|
|
|
|
shortcutK.textContent = navigator.userAgent.toUpperCase().includes('MAC')
|
|
|
|
|
? '⌘ + K'
|
|
|
|
|
: 'Ctrl + K';
|
|
|
|
|
}
|
2025-10-18 23:58:41 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
dom.toolGrid.addEventListener('click', (e) => {
|
|
|
|
|
// @ts-expect-error TS(2339) FIXME: Property 'closest' does not exist on type 'EventTa... Remove this comment to see the full error message
|
|
|
|
|
const card = e.target.closest('.tool-card');
|
|
|
|
|
if (card) {
|
|
|
|
|
const toolId = card.dataset.toolId;
|
|
|
|
|
setupToolInterface(toolId);
|
2025-10-12 11:55:45 +05:30
|
|
|
}
|
2025-10-17 11:37:32 +05:30
|
|
|
});
|
|
|
|
|
dom.backToGridBtn.addEventListener('click', () => switchView('grid'));
|
|
|
|
|
dom.alertOkBtn.addEventListener('click', hideAlert);
|
|
|
|
|
|
|
|
|
|
const faqAccordion = document.getElementById('faq-accordion');
|
|
|
|
|
if (faqAccordion) {
|
|
|
|
|
faqAccordion.addEventListener('click', (e) => {
|
|
|
|
|
// @ts-expect-error TS(2339) FIXME: Property 'closest' does not exist on type 'EventTa... Remove this comment to see the full error message
|
|
|
|
|
const questionButton = e.target.closest('.faq-question');
|
|
|
|
|
if (!questionButton) return;
|
|
|
|
|
|
|
|
|
|
const faqItem = questionButton.parentElement;
|
|
|
|
|
const answer = faqItem.querySelector('.faq-answer');
|
|
|
|
|
|
|
|
|
|
faqItem.classList.toggle('open');
|
|
|
|
|
|
|
|
|
|
if (faqItem.classList.contains('open')) {
|
|
|
|
|
answer.style.maxHeight = answer.scrollHeight + 'px';
|
|
|
|
|
} else {
|
|
|
|
|
answer.style.maxHeight = '0px';
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-10-12 11:55:45 +05:30
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
createIcons({ icons });
|
|
|
|
|
console.log('Please share our tool and share the love!');
|
2025-10-12 11:55:45 +05:30
|
|
|
};
|
|
|
|
|
|
2025-10-17 11:37:32 +05:30
|
|
|
document.addEventListener('DOMContentLoaded', init);
|