feat: add custom branding, air-gapped deployment script, and updated self-hosting docs

This commit is contained in:
alam00000
2026-02-14 21:38:58 +05:30
parent 75b1d67fbd
commit 3cf435d59d
38 changed files with 1487 additions and 123 deletions

View File

@@ -15,6 +15,7 @@ import {
createLanguageSwitcher,
t,
} from './i18n/index.js';
declare const __BRAND_NAME__: string;
const init = async () => {
await initI18n();
@@ -81,18 +82,19 @@ const init = async () => {
(divider as HTMLElement).style.display = 'none';
});
document.title = 'BentoPDF - PDF Tools';
const brandName = __BRAND_NAME__ || 'BentoPDF';
document.title = `${brandName} - ${t('simpleMode.title')}`;
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.textContent = t('simpleMode.title');
title.className = 'text-4xl md:text-5xl font-bold text-white mb-3';
}
if (subtitle) {
subtitle.textContent = 'Select a tool to get started';
subtitle.textContent = t('simpleMode.subtitle');
subtitle.className = 'text-lg text-gray-400';
}
}
@@ -805,8 +807,12 @@ const init = async () => {
left.className = 'flex items-center gap-3';
const icon = document.createElement('i');
icon.className = 'w-5 h-5 text-indigo-400';
icon.setAttribute('data-lucide', tool.icon);
if (tool.icon.startsWith('ph-')) {
icon.className = `ph ${tool.icon} w-5 h-5 text-indigo-400`;
} else {
icon.className = 'w-5 h-5 text-indigo-400';
icon.setAttribute('data-lucide', tool.icon);
}
const name = document.createElement('span');
name.className = 'text-gray-200 font-medium';

View File

@@ -4,6 +4,7 @@ import { pdfSocket } from '../sockets';
import type { SocketData } from '../types';
import { requirePdfInput, processBatch } from '../types';
import { PDFDocument } from 'pdf-lib';
import { parsePageRange } from '../../utils/pdf-operations';
export class DividePagesNode extends BaseWorkflowNode {
readonly category = 'Organize & Manage' as const;
@@ -18,6 +19,10 @@ export class DividePagesNode extends BaseWorkflowNode {
'direction',
new ClassicPreset.InputControl('text', { initial: 'vertical' })
);
this.addControl(
'pages',
new ClassicPreset.InputControl('text', { initial: '' })
);
}
async data(
@@ -30,23 +35,39 @@ export class DividePagesNode extends BaseWorkflowNode {
const direction =
dirCtrl?.value === 'horizontal' ? 'horizontal' : 'vertical';
const pagesCtrl = this.controls['pages'] as
| ClassicPreset.InputControl<'text'>
| undefined;
const rangeStr = (pagesCtrl?.value || '').trim();
return {
pdf: await processBatch(pdfInputs, async (input) => {
const srcDoc = await PDFDocument.load(input.bytes);
const newDoc = await PDFDocument.create();
for (let i = 0; i < srcDoc.getPageCount(); i++) {
const [page1] = await newDoc.copyPages(srcDoc, [i]);
const [page2] = await newDoc.copyPages(srcDoc, [i]);
const { width, height } = page1.getSize();
if (direction === 'vertical') {
page1.setCropBox(0, 0, width / 2, height);
page2.setCropBox(width / 2, 0, width / 2, height);
const totalPages = srcDoc.getPageCount();
const pagesToDivide: Set<number> = rangeStr
? new Set(parsePageRange(rangeStr, totalPages))
: new Set(Array.from({ length: totalPages }, (_, i) => i));
for (let i = 0; i < totalPages; i++) {
if (pagesToDivide.has(i)) {
const [page1] = await newDoc.copyPages(srcDoc, [i]);
const [page2] = await newDoc.copyPages(srcDoc, [i]);
const { width, height } = page1.getSize();
if (direction === 'vertical') {
page1.setCropBox(0, 0, width / 2, height);
page2.setCropBox(width / 2, 0, width / 2, height);
} else {
page1.setCropBox(0, height / 2, width, height / 2);
page2.setCropBox(0, 0, width, height / 2);
}
newDoc.addPage(page1);
newDoc.addPage(page2);
} else {
page1.setCropBox(0, height / 2, width, height / 2);
page2.setCropBox(0, 0, width, height / 2);
const [copiedPage] = await newDoc.copyPages(srcDoc, [i]);
newDoc.addPage(copiedPage);
}
newDoc.addPage(page1);
newDoc.addPage(page2);
}
const pdfBytes = await newDoc.save();
return {

View File

@@ -133,12 +133,14 @@
<div class="flex justify-between items-center h-16">
<div class="flex-shrink-0 flex items-center">
<img
src="/images/favicon-no-bg.svg"
alt="Bento PDF Logo"
src="{{baseUrl}}{{#if brandLogo}}{{brandLogo}}{{else}}images/favicon-no-bg.svg{{/if}}"
alt="{{#if brandName}}{{brandName}}{{else}}Bento PDF{{/if}} Logo"
class="h-8 w-8"
/>
<span class="text-white font-bold text-xl ml-2">
<a href="/">BentoPDF</a>
<a href="/"
>{{#if brandName}}{{brandName}}{{else}}BentoPDF{{/if}}</a
>
</span>
<span class="text-gray-400 ml-3 text-sm sm:text-base"
>PDF Multi Tool</span

View File

@@ -4,14 +4,18 @@
<div>
<div class="flex items-center mb-2">
<img
src="{{baseUrl}}images/favicon.svg"
alt="Bento PDF Logo"
id="footer-logo"
src="{{baseUrl}}{{#if brandLogo}}{{brandLogo}}{{else}}images/favicon.svg{{/if}}"
alt="{{#if brandName}}{{brandName}}{{else}}Bento PDF{{/if}} Logo"
class="h-8 w-8 mr-2"
/>
<span class="text-white font-bold text-lg">BentoPDF</span>
<span id="footer-brand" class="text-white font-bold text-lg"
>{{#if brandName}}{{brandName}}{{else}}BentoPDF{{/if}}</span
>
</div>
<p class="text-gray-400 text-sm">
&copy; 2026 BentoPDF. All rights reserved.
<p id="footer-copyright" class="text-gray-400 text-sm">
{{#if footerText}}{{footerText}}{{else}}&copy; 2026 BentoPDF. All
rights reserved.{{/if}}
</p>
<p class="text-gray-500 text-xs mt-2">
Version <span id="app-version-simple"></span>

View File

@@ -5,14 +5,15 @@
<div class="mb-8 md:mb-0">
<div class="flex items-center justify-center md:justify-start mb-4">
<img
src="{{baseUrl}}images/favicon.svg"
alt="Bento PDF Logo"
id="footer-logo"
src="{{baseUrl}}{{#if brandLogo}}{{brandLogo}}{{else}}images/favicon.svg{{/if}}"
alt="{{#if brandName}}{{brandName}}{{else}}Bento PDF{{/if}} Logo"
class="h-10 w-10 mr-3"
/>
<span class="text-xl font-bold text-white">BentoPDF</span>
<span id="footer-brand" class="text-xl font-bold text-white">{{#if brandName}}{{brandName}}{{else}}BentoPDF{{/if}}</span>
</div>
<p class="text-gray-400 text-sm" data-i18n="footer.copyright">
&copy; 2026 BentoPDF. All rights reserved.
<p id="footer-copyright" class="text-gray-400 text-sm" {{#unless footerText}}data-i18n="footer.copyright"{{/unless}}>
{{#if footerText}}{{footerText}}{{else}}&copy; 2026 BentoPDF. All rights reserved.{{/if}}
</p>
<p class="text-gray-500 text-xs mt-2">
<span data-i18n="footer.version">Version</span>

View File

@@ -6,12 +6,15 @@
<div class="flex justify-start items-center h-16">
<div class="flex-shrink-0 flex items-center cursor-pointer">
<img
src="{{baseUrl}}images/favicon-no-bg.svg"
alt="Bento PDF Logo"
id="nav-logo"
src="{{baseUrl}}{{#if brandLogo}}{{brandLogo}}{{else}}images/favicon-no-bg.svg{{/if}}"
alt="{{#if brandName}}{{brandName}}{{else}}Bento PDF{{/if}} Logo"
class="h-8 w-8"
/>
<span class="text-white font-bold text-xl ml-2">
<a href="{{baseUrl}}">BentoPDF</a>
<span id="nav-brand" class="text-white font-bold text-xl ml-2">
<a href="{{baseUrl}}"
>{{#if brandName}}{{brandName}}{{else}}BentoPDF{{/if}}</a
>
</span>
</div>
</div>

View File

@@ -7,12 +7,15 @@
id="home-logo"
>
<img
src="{{baseUrl}}images/favicon-no-bg.svg"
alt="Bento PDF Logo"
id="nav-logo"
src="{{baseUrl}}{{#if brandLogo}}{{brandLogo}}{{else}}images/favicon-no-bg.svg{{/if}}"
alt="{{#if brandName}}{{brandName}}{{else}}Bento PDF{{/if}} Logo"
class="h-8 w-8"
/>
<span class="text-white font-bold text-xl ml-2">
<a href="index.html">BentoPDF</a>
<span id="nav-brand" class="text-white font-bold text-xl ml-2">
<a href="index.html"
>{{#if brandName}}{{brandName}}{{else}}BentoPDF{{/if}}</a
>
</span>
</div>