feat: add support for disabling specific tools in self-hosting
- Introduced build-time and runtime options to disable tools for compliance or security. - Updated documentation to include instructions for disabling tools in Docker and Kubernetes setups. - Added translations for disabled tool messages in multiple languages. - Implemented logic to filter out disabled tools from the toolbox and shortcuts in the application. - Created utility functions to manage disabled tools configuration.
This commit is contained in:
@@ -59,6 +59,9 @@ ENV VITE_BRAND_NAME=$VITE_BRAND_NAME
|
||||
ENV VITE_BRAND_LOGO=$VITE_BRAND_LOGO
|
||||
ENV VITE_FOOTER_TEXT=$VITE_FOOTER_TEXT
|
||||
|
||||
ARG DISABLE_TOOLS
|
||||
ENV DISABLE_TOOLS=$DISABLE_TOOLS
|
||||
|
||||
ENV NODE_OPTIONS="--max-old-space-size=3072"
|
||||
|
||||
RUN --mount=type=secret,id=VITE_CORS_PROXY_URL \
|
||||
|
||||
@@ -106,6 +106,7 @@ docker run -d -p 3000:8080 bentopdf:custom
|
||||
| `VITE_BRAND_NAME` | Custom brand name | `BentoPDF` |
|
||||
| `VITE_BRAND_LOGO` | Logo path relative to `public/` | `images/favicon-no-bg.svg` |
|
||||
| `VITE_FOOTER_TEXT` | Custom footer/copyright text | `© 2026 BentoPDF. All rights reserved.` |
|
||||
| `DISABLE_TOOLS` | Comma-separated tool IDs to hide | _(empty; all tools enabled)_ |
|
||||
|
||||
WASM module URLs are pre-configured with CDN defaults — all advanced features work out of the box. Override these for air-gapped or self-hosted deployments.
|
||||
|
||||
@@ -135,6 +136,74 @@ docker build \
|
||||
|
||||
Branding works in both full mode and Simple Mode, and can be combined with all other build-time options.
|
||||
|
||||
### Disabling Specific Tools
|
||||
|
||||
Hide tools from the UI for compliance or security requirements. Disabled tools are removed from the homepage, search results, keyboard shortcuts, and the workflow builder. Direct URL access shows a "tool unavailable" page.
|
||||
|
||||
Tool IDs are the page URL without `.html`. For example, if the tool lives at `bentopdf.com/edit-pdf.html`, the ID is `edit-pdf`.
|
||||
|
||||
#### Finding Tool IDs
|
||||
|
||||
The easiest way: open any tool in BentoPDF and look at the URL. The last part of the path (without `.html`) is the tool ID.
|
||||
|
||||
<details>
|
||||
<summary>Full list of tool IDs</summary>
|
||||
|
||||
**Edit & Annotate:** `edit-pdf`, `bookmark`, `table-of-contents`, `page-numbers`, `add-page-labels`, `bates-numbering`, `add-watermark`, `header-footer`, `invert-colors`, `scanner-effect`, `adjust-colors`, `background-color`, `text-color`, `sign-pdf`, `add-stamps`, `remove-annotations`, `crop-pdf`, `form-filler`, `form-creator`, `remove-blank-pages`
|
||||
|
||||
**Convert to PDF:** `image-to-pdf`, `jpg-to-pdf`, `png-to-pdf`, `webp-to-pdf`, `svg-to-pdf`, `bmp-to-pdf`, `heic-to-pdf`, `tiff-to-pdf`, `txt-to-pdf`, `markdown-to-pdf`, `json-to-pdf`, `csv-to-pdf`, `rtf-to-pdf`, `odt-to-pdf`, `word-to-pdf`, `excel-to-pdf`, `powerpoint-to-pdf`, `xps-to-pdf`, `mobi-to-pdf`, `epub-to-pdf`, `fb2-to-pdf`, `cbz-to-pdf`, `wpd-to-pdf`, `wps-to-pdf`, `xml-to-pdf`, `pages-to-pdf`, `odg-to-pdf`, `ods-to-pdf`, `odp-to-pdf`, `pub-to-pdf`, `vsd-to-pdf`, `psd-to-pdf`, `email-to-pdf`
|
||||
|
||||
**Convert from PDF:** `pdf-to-jpg`, `pdf-to-png`, `pdf-to-webp`, `pdf-to-bmp`, `pdf-to-tiff`, `pdf-to-cbz`, `pdf-to-svg`, `pdf-to-csv`, `pdf-to-excel`, `pdf-to-greyscale`, `pdf-to-json`, `pdf-to-docx`, `extract-images`, `pdf-to-markdown`, `prepare-pdf-for-ai`, `pdf-to-text`
|
||||
|
||||
**Organize & Manage:** `ocr-pdf`, `merge-pdf`, `alternate-merge`, `organize-pdf`, `add-attachments`, `extract-attachments`, `edit-attachments`, `pdf-multi-tool`, `pdf-layers`, `extract-tables`, `split-pdf`, `divide-pages`, `extract-pages`, `delete-pages`, `add-blank-page`, `reverse-pages`, `rotate-pdf`, `rotate-custom`, `n-up-pdf`, `pdf-booklet`, `combine-single-page`, `view-metadata`, `edit-metadata`, `pdf-to-zip`, `compare-pdfs`, `posterize-pdf`, `page-dimensions`
|
||||
|
||||
**Optimize & Repair:** `compress-pdf`, `pdf-to-pdfa`, `fix-page-size`, `linearize-pdf`, `remove-restrictions`, `repair-pdf`, `rasterize-pdf`, `deskew-pdf`, `font-to-outline`
|
||||
|
||||
**Security:** `encrypt-pdf`, `sanitize-pdf`, `decrypt-pdf`, `flatten-pdf`, `remove-metadata`, `change-permissions`, `digital-sign-pdf`, `validate-signature-pdf`, `timestamp-pdf`
|
||||
|
||||
</details>
|
||||
|
||||
#### Option 1: Build-time (Docker build arg)
|
||||
|
||||
```bash
|
||||
docker build \
|
||||
--build-arg DISABLE_TOOLS="edit-pdf,sign-pdf,encrypt-pdf" \
|
||||
-t bentopdf .
|
||||
```
|
||||
|
||||
This bakes the disabled list into the JavaScript bundle. Requires a rebuild to change.
|
||||
|
||||
#### Option 2: Runtime (config.json)
|
||||
|
||||
Mount a `config.json` file into the served directory — no rebuild needed:
|
||||
|
||||
```json
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
-p 3000:8080 \
|
||||
-v ./config.json:/usr/share/nginx/html/config.json:ro \
|
||||
ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
|
||||
Or with Docker Compose:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bentopdf:
|
||||
image: ghcr.io/alam00000/bentopdf:latest
|
||||
ports:
|
||||
- '3000:8080'
|
||||
volumes:
|
||||
- ./config.json:/usr/share/nginx/html/config.json:ro
|
||||
```
|
||||
|
||||
Both methods can be combined — the lists are merged. If a tool appears in either, it is disabled.
|
||||
|
||||
### Custom WASM URLs (Air-Gapped / Self-Hosted)
|
||||
|
||||
> [!IMPORTANT]
|
||||
|
||||
@@ -138,6 +138,34 @@ docker build \
|
||||
|
||||
Branding works in both full mode and Simple Mode, and can be combined with all other build-time options (`BASE_URL`, `SIMPLE_MODE`, `VITE_DEFAULT_LANGUAGE`).
|
||||
|
||||
### Disabling Specific Tools
|
||||
|
||||
Hide individual tools for compliance or security. Disabled tools are removed from the homepage, search, shortcuts, workflow builder, and direct URL access.
|
||||
|
||||
Tool IDs are the page URL without `.html` — open any tool and look at the URL (e.g., `edit-pdf`, `sign-pdf`, `encrypt-pdf`).
|
||||
|
||||
**Build-time** (baked into the bundle):
|
||||
|
||||
```bash
|
||||
DISABLE_TOOLS="edit-pdf,sign-pdf" npm run build
|
||||
```
|
||||
|
||||
**Runtime** (no rebuild needed — mount a `config.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run -d -p 3000:8080 \
|
||||
-v ./config.json:/usr/share/nginx/html/config.json:ro \
|
||||
ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
|
||||
Both methods can be combined — the lists are merged. See the [Docker guide](/self-hosting/docker#disabling-specific-tools) for full details.
|
||||
|
||||
## Deployment Guides
|
||||
|
||||
Choose your platform:
|
||||
|
||||
@@ -6,6 +6,7 @@ Kubernetes may be overkill for a static site, but it can be a great fit if you a
|
||||
> **Required Headers for Office File Conversion**
|
||||
>
|
||||
> LibreOffice-based tools (Word, Excel, PowerPoint conversion) require these HTTP headers for `SharedArrayBuffer` support:
|
||||
>
|
||||
> - `Cross-Origin-Opener-Policy: same-origin`
|
||||
> - `Cross-Origin-Embedder-Policy: require-corp`
|
||||
>
|
||||
@@ -155,3 +156,39 @@ httpRoute:
|
||||
```
|
||||
|
||||
Support for specific filters depends on your Gateway controller; if a filter is ignored, add headers at the edge/controller layer instead.
|
||||
|
||||
## Disabling Specific Tools
|
||||
|
||||
Use a ConfigMap to disable tools at runtime without rebuilding the image:
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: bentopdf-config
|
||||
namespace: bentopdf
|
||||
data:
|
||||
config.json: |
|
||||
{
|
||||
"disabledTools": ["edit-pdf", "sign-pdf", "encrypt-pdf"]
|
||||
}
|
||||
```
|
||||
|
||||
Mount it into the served directory:
|
||||
|
||||
```yaml
|
||||
spec:
|
||||
containers:
|
||||
- name: bentopdf
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /usr/share/nginx/html/config.json
|
||||
subPath: config.json
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: bentopdf-config
|
||||
```
|
||||
|
||||
Tool IDs are the page URL without `.html` — open any tool and look at the URL (e.g., `edit-pdf`, `merge-pdf`, `compress-pdf`). Disabled tools are hidden from the homepage, search, shortcuts, workflow builder, and direct URL access. See the [Docker guide](/self-hosting/docker#disabling-specific-tools) for the full list of options.
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "أدوات PDF",
|
||||
"subtitle": "اختر أداة للبدء"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "الأداة غير متاحة",
|
||||
"heading": "هذه الأداة معطّلة",
|
||||
"message": "هذه الأداة غير متوفرة في بيئة النشر الخاصة بك. تواصل مع المسؤول للحصول على مزيد من المعلومات.",
|
||||
"backHome": "العودة إلى الرئيسية"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "Інструменты PDF",
|
||||
"subtitle": "Выберыце інструмент, каб пачаць"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Інструмент недаступны",
|
||||
"heading": "Гэты інструмент адключаны",
|
||||
"message": "Гэты інструмент недаступны ў вашым разгортванні. Звяжыцеся з адміністратарам для атрымання дадатковай інфармацыі.",
|
||||
"backHome": "Вярнуцца на галоўную"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF-værktøjer",
|
||||
"subtitle": "Vælg et værktøj for at komme i gang"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Værktøj utilgængeligt",
|
||||
"heading": "Dette værktøj er deaktiveret",
|
||||
"message": "Dette værktøj er ikke tilgængeligt i din installation. Kontakt din administrator for yderligere oplysninger.",
|
||||
"backHome": "Tilbage til forsiden"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,5 +362,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF-Werkzeuge",
|
||||
"subtitle": "Wählen Sie ein Werkzeug aus, um zu beginnen"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Werkzeug nicht verfügbar",
|
||||
"heading": "Dieses Werkzeug wurde deaktiviert",
|
||||
"message": "Dieses Werkzeug ist in Ihrer Installation nicht verfügbar. Wenden Sie sich an Ihren Administrator, um weitere Informationen zu erhalten.",
|
||||
"backHome": "Zurück zur Startseite"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -363,5 +363,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF Tools",
|
||||
"subtitle": "Select a tool to get started"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Tool Unavailable",
|
||||
"heading": "This tool has been disabled",
|
||||
"message": "This tool is not available in your deployment. Contact your administrator for more information.",
|
||||
"backHome": "Back to Home"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "Herramientas PDF",
|
||||
"subtitle": "Selecciona una herramienta para comenzar"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Herramienta no disponible",
|
||||
"heading": "Esta herramienta ha sido desactivada",
|
||||
"message": "Esta herramienta no está disponible en tu instalación. Contacta con tu administrador para obtener más información.",
|
||||
"backHome": "Volver al inicio"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "Outils PDF",
|
||||
"subtitle": "Sélectionnez un outil pour commencer"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Outil indisponible",
|
||||
"heading": "Cet outil a été désactivé",
|
||||
"message": "Cet outil n'est pas disponible dans votre déploiement. Contactez votre administrateur pour plus d'informations.",
|
||||
"backHome": "Retour à l'accueil"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "Alat PDF",
|
||||
"subtitle": "Pilih alat untuk memulai"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Alat Tidak Tersedia",
|
||||
"heading": "Alat ini telah dinonaktifkan",
|
||||
"message": "Alat ini tidak tersedia dalam penerapan Anda. Hubungi administrator Anda untuk informasi lebih lanjut.",
|
||||
"backHome": "Kembali ke Beranda"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "Strumenti PDF",
|
||||
"subtitle": "Seleziona uno strumento per iniziare"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Strumento Non Disponibile",
|
||||
"heading": "Questo strumento è stato disabilitato",
|
||||
"message": "Questo strumento non è disponibile nella tua distribuzione. Contatta il tuo amministratore per ulteriori informazioni.",
|
||||
"backHome": "Torna alla Home"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF 도구",
|
||||
"subtitle": "사용할 도구를 선택하세요"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "도구를 사용할 수 없음",
|
||||
"heading": "이 도구는 비활성화되었습니다",
|
||||
"message": "이 도구는 현재 배포 환경에서 사용할 수 없습니다. 자세한 내용은 관리자에게 문의하세요.",
|
||||
"backHome": "홈으로 돌아가기"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF-tools",
|
||||
"subtitle": "Selecteer een tool om te beginnen"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Tool Niet Beschikbaar",
|
||||
"heading": "Deze tool is uitgeschakeld",
|
||||
"message": "Deze tool is niet beschikbaar in uw implementatie. Neem contact op met uw beheerder voor meer informatie.",
|
||||
"backHome": "Terug naar Home"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "Ferramentas PDF",
|
||||
"subtitle": "Selecione uma ferramenta para começar"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Ferramenta Indisponível",
|
||||
"heading": "Esta ferramenta foi desativada",
|
||||
"message": "Esta ferramenta não está disponível na sua implantação. Entre em contato com o seu administrador para mais informações.",
|
||||
"backHome": "Voltar para o Início"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -357,5 +357,11 @@
|
||||
"errorRendering": "Не удалось отрисовать миниатюры страниц",
|
||||
"error": "Ошибка",
|
||||
"failedToLoad": "Не удалось загрузить"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Инструмент недоступен",
|
||||
"heading": "Этот инструмент отключён",
|
||||
"message": "Этот инструмент недоступен в вашей конфигурации. Обратитесь к администратору для получения дополнительной информации.",
|
||||
"backHome": "На главную"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF-verktyg",
|
||||
"subtitle": "Välj ett verktyg för att komma igång"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Verktyget är inte tillgängligt",
|
||||
"heading": "Det här verktyget har inaktiverats",
|
||||
"message": "Det här verktyget är inte tillgängligt i din installation. Kontakta din administratör för mer information.",
|
||||
"backHome": "Tillbaka till startsidan"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF Araçları",
|
||||
"subtitle": "Başlamak için bir araç seçin"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Araç Kullanılamıyor",
|
||||
"heading": "Bu araç devre dışı bırakıldı",
|
||||
"message": "Bu araç, dağıtımınızda mevcut değil. Daha fazla bilgi için yöneticinizle iletişime geçin.",
|
||||
"backHome": "Ana Sayfaya Dön"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "Công cụ PDF",
|
||||
"subtitle": "Chọn một công cụ để bắt đầu"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "Công cụ không khả dụng",
|
||||
"heading": "Công cụ này đã bị vô hiệu hóa",
|
||||
"message": "Công cụ này không có sẵn trong môi trường triển khai của bạn. Vui lòng liên hệ quản trị viên để biết thêm thông tin.",
|
||||
"backHome": "Quay về trang chủ"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF 工具",
|
||||
"subtitle": "選擇一個工具開始使用"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "工具無法使用",
|
||||
"heading": "此工具已被停用",
|
||||
"message": "此工具在您的部署環境中無法使用。請聯絡管理員以取得更多資訊。",
|
||||
"backHome": "返回首頁"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,5 +361,11 @@
|
||||
"simpleMode": {
|
||||
"title": "PDF 工具",
|
||||
"subtitle": "选择一个工具开始使用"
|
||||
},
|
||||
"disabledTool": {
|
||||
"title": "工具不可用",
|
||||
"heading": "此工具已被禁用",
|
||||
"message": "此工具在您的部署环境中不可用。请联系管理员以获取更多信息。",
|
||||
"backHome": "返回首页"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
getNodesByCategory,
|
||||
createNodeByType,
|
||||
} from '../workflow/nodes/registry';
|
||||
import { isToolDisabled } from '../utils/disabled-tools.js';
|
||||
import type { BaseWorkflowNode } from '../workflow/nodes/base-node';
|
||||
import type { WorkflowEditor } from '../workflow/editor';
|
||||
import {
|
||||
@@ -447,7 +448,9 @@ function buildToolbox() {
|
||||
];
|
||||
|
||||
for (const cat of categoryOrder) {
|
||||
const entries = categorized[cat.key as keyof typeof categorized] ?? [];
|
||||
const entries = (
|
||||
categorized[cat.key as keyof typeof categorized] ?? []
|
||||
).filter((entry) => !entry.toolPageId || !isToolDisabled(entry.toolPageId));
|
||||
if (entries.length === 0) continue;
|
||||
|
||||
const section = document.createElement('div');
|
||||
|
||||
@@ -15,13 +15,38 @@ import {
|
||||
createLanguageSwitcher,
|
||||
t,
|
||||
} from './i18n/index.js';
|
||||
import {
|
||||
loadRuntimeConfig,
|
||||
isToolDisabled,
|
||||
isCurrentPageDisabled,
|
||||
} from './utils/disabled-tools.js';
|
||||
declare const __BRAND_NAME__: string;
|
||||
|
||||
const init = async () => {
|
||||
await initI18n();
|
||||
await loadRuntimeConfig();
|
||||
injectLanguageSwitcher();
|
||||
applyTranslations();
|
||||
|
||||
if (isCurrentPageDisabled()) {
|
||||
document.title = t('disabledTool.title') || 'Tool Unavailable';
|
||||
const main = document.querySelector('main') || document.body;
|
||||
const heading = t('disabledTool.heading') || 'This tool has been disabled';
|
||||
const message =
|
||||
t('disabledTool.message') ||
|
||||
'This tool is not available in your deployment. Contact your administrator for more information.';
|
||||
const backHome = t('disabledTool.backHome') || 'Back to Home';
|
||||
main.innerHTML = `
|
||||
<div class="flex flex-col items-center justify-center min-h-[60vh] text-center px-4">
|
||||
<i class="ph ph-prohibit text-6xl text-gray-500 mb-4"></i>
|
||||
<h1 class="text-2xl font-bold text-white mb-2">${heading}</h1>
|
||||
<p class="text-gray-400 mb-6">${message}</p>
|
||||
<a href="${import.meta.env.BASE_URL}" class="px-6 py-2 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg transition">${backHome}</a>
|
||||
</div>
|
||||
`;
|
||||
return;
|
||||
}
|
||||
|
||||
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
|
||||
'pdfjs-dist/build/pdf.worker.min.mjs',
|
||||
import.meta.url
|
||||
@@ -275,7 +300,14 @@ const init = async () => {
|
||||
);
|
||||
}
|
||||
|
||||
categories.forEach((category) => {
|
||||
const filteredCategories = categories
|
||||
.map((category) => ({
|
||||
...category,
|
||||
tools: category.tools.filter((tool) => !isToolDisabled(tool.id)),
|
||||
}))
|
||||
.filter((category) => category.tools.length > 0);
|
||||
|
||||
filteredCategories.forEach((category) => {
|
||||
const categoryGroup = document.createElement('div');
|
||||
categoryGroup.className = 'category-group col-span-full';
|
||||
|
||||
@@ -872,16 +904,21 @@ const init = async () => {
|
||||
|
||||
const allShortcuts = ShortcutsManager.getAllShortcuts();
|
||||
const isMac = navigator.userAgent.toUpperCase().includes('MAC');
|
||||
const allTools = categories.flatMap((c) => c.tools);
|
||||
const shortcutCategories = categories
|
||||
.map((category) => ({
|
||||
...category,
|
||||
tools: category.tools.filter((tool) => !isToolDisabled(tool.id)),
|
||||
}))
|
||||
.filter((category) => category.tools.length > 0);
|
||||
const allTools = shortcutCategories.flatMap((c) => c.tools);
|
||||
|
||||
categories.forEach((category) => {
|
||||
shortcutCategories.forEach((category) => {
|
||||
const section = document.createElement('div');
|
||||
section.className = 'category-section mb-6 last:mb-0';
|
||||
|
||||
const header = document.createElement('h3');
|
||||
header.className =
|
||||
'text-gray-400 text-xs font-bold uppercase tracking-wider mb-3 pl-1';
|
||||
// Translate category name
|
||||
const categoryKey = categoryTranslationKeys[category.name];
|
||||
header.textContent = categoryKey ? t(categoryKey) : category.name;
|
||||
section.appendChild(header);
|
||||
|
||||
3
src/js/types/config-types.ts
Normal file
3
src/js/types/config-types.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export interface AppConfig {
|
||||
disabledTools?: string[];
|
||||
}
|
||||
@@ -55,3 +55,4 @@ export * from './add-page-labels-type.ts';
|
||||
export * from './pdf-to-tiff-type.ts';
|
||||
export * from './pdf-to-cbz-type.ts';
|
||||
export * from './password-prompt-type.ts';
|
||||
export * from './config-types.ts';
|
||||
|
||||
43
src/js/utils/disabled-tools.ts
Normal file
43
src/js/utils/disabled-tools.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import type { AppConfig } from '@/types';
|
||||
|
||||
const disabledToolsSet = new Set<string>(__DISABLED_TOOLS__);
|
||||
let runtimeConfigLoaded = false;
|
||||
|
||||
export async function loadRuntimeConfig(): Promise<void> {
|
||||
if (runtimeConfigLoaded) return;
|
||||
runtimeConfigLoaded = true;
|
||||
|
||||
try {
|
||||
const response = await fetch(`${import.meta.env.BASE_URL}config.json`, {
|
||||
cache: 'no-cache',
|
||||
});
|
||||
if (!response.ok) return;
|
||||
|
||||
const config: AppConfig = await response.json();
|
||||
if (Array.isArray(config.disabledTools)) {
|
||||
for (const toolId of config.disabledTools) {
|
||||
if (typeof toolId === 'string') {
|
||||
disabledToolsSet.add(toolId);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function isToolDisabled(toolId: string): boolean {
|
||||
return disabledToolsSet.has(toolId);
|
||||
}
|
||||
|
||||
export function getToolIdFromPath(): string | null {
|
||||
const path = window.location.pathname;
|
||||
const withExt = path.match(/\/([^/]+)\.html$/);
|
||||
if (withExt) return withExt[1];
|
||||
const withoutExt = path.match(/\/([^/]+)\/?$/);
|
||||
return withoutExt?.[1] ?? null;
|
||||
}
|
||||
|
||||
export function isCurrentPageDisabled(): boolean {
|
||||
const toolId = getToolIdFromPath();
|
||||
if (!toolId) return false;
|
||||
return isToolDisabled(toolId);
|
||||
}
|
||||
@@ -80,6 +80,7 @@ export interface NodeRegistryEntry {
|
||||
description: string;
|
||||
factory: () => BaseWorkflowNode;
|
||||
hidden?: boolean;
|
||||
toolPageId?: string;
|
||||
}
|
||||
|
||||
export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
@@ -96,6 +97,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-image',
|
||||
description: 'Upload images and convert to PDF',
|
||||
factory: () => new ImageInputNode(),
|
||||
toolPageId: 'image-to-pdf',
|
||||
},
|
||||
WordToPdfNode: {
|
||||
label: 'Word to PDF',
|
||||
@@ -103,6 +105,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-microsoft-word-logo',
|
||||
description: 'Convert Word documents to PDF',
|
||||
factory: () => new WordToPdfNode(),
|
||||
toolPageId: 'word-to-pdf',
|
||||
},
|
||||
ExcelToPdfNode: {
|
||||
label: 'Excel to PDF',
|
||||
@@ -110,6 +113,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-microsoft-excel-logo',
|
||||
description: 'Convert Excel spreadsheets to PDF',
|
||||
factory: () => new ExcelToPdfNode(),
|
||||
toolPageId: 'excel-to-pdf',
|
||||
},
|
||||
PowerPointToPdfNode: {
|
||||
label: 'PowerPoint to PDF',
|
||||
@@ -117,6 +121,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-microsoft-powerpoint-logo',
|
||||
description: 'Convert PowerPoint presentations to PDF',
|
||||
factory: () => new PowerPointToPdfNode(),
|
||||
toolPageId: 'powerpoint-to-pdf',
|
||||
},
|
||||
TextToPdfNode: {
|
||||
label: 'Text to PDF',
|
||||
@@ -124,6 +129,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-text-t',
|
||||
description: 'Convert plain text to PDF',
|
||||
factory: () => new TextToPdfNode(),
|
||||
toolPageId: 'txt-to-pdf',
|
||||
},
|
||||
SvgToPdfNode: {
|
||||
label: 'SVG to PDF',
|
||||
@@ -131,6 +137,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-svg',
|
||||
description: 'Convert SVG files to PDF',
|
||||
factory: () => new SvgToPdfNode(),
|
||||
toolPageId: 'svg-to-pdf',
|
||||
},
|
||||
EpubToPdfNode: {
|
||||
label: 'EPUB to PDF',
|
||||
@@ -138,6 +145,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-book-open-text',
|
||||
description: 'Convert EPUB ebooks to PDF',
|
||||
factory: () => new EpubToPdfNode(),
|
||||
toolPageId: 'epub-to-pdf',
|
||||
},
|
||||
EmailToPdfNode: {
|
||||
label: 'Email to PDF',
|
||||
@@ -145,6 +153,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-envelope',
|
||||
description: 'Convert email files (.eml, .msg) to PDF',
|
||||
factory: () => new EmailToPdfNode(),
|
||||
toolPageId: 'email-to-pdf',
|
||||
},
|
||||
XpsToPdfNode: {
|
||||
label: 'XPS to PDF',
|
||||
@@ -152,6 +161,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-scan',
|
||||
description: 'Convert XPS/OXPS documents to PDF',
|
||||
factory: () => new XpsToPdfNode(),
|
||||
toolPageId: 'xps-to-pdf',
|
||||
},
|
||||
MobiToPdfNode: {
|
||||
label: 'MOBI to PDF',
|
||||
@@ -159,6 +169,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-book-open-text',
|
||||
description: 'Convert MOBI e-books to PDF',
|
||||
factory: () => new MobiToPdfNode(),
|
||||
toolPageId: 'mobi-to-pdf',
|
||||
},
|
||||
Fb2ToPdfNode: {
|
||||
label: 'FB2 to PDF',
|
||||
@@ -166,6 +177,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-book-bookmark',
|
||||
description: 'Convert FB2 e-books to PDF',
|
||||
factory: () => new Fb2ToPdfNode(),
|
||||
toolPageId: 'fb2-to-pdf',
|
||||
},
|
||||
CbzToPdfNode: {
|
||||
label: 'CBZ to PDF',
|
||||
@@ -173,6 +185,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-book-open',
|
||||
description: 'Convert comic book archives (CBZ/CBR) to PDF',
|
||||
factory: () => new CbzToPdfNode(),
|
||||
toolPageId: 'cbz-to-pdf',
|
||||
},
|
||||
MarkdownToPdfNode: {
|
||||
label: 'Markdown to PDF',
|
||||
@@ -180,6 +193,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-markdown-logo',
|
||||
description: 'Convert Markdown files to PDF',
|
||||
factory: () => new MarkdownToPdfNode(),
|
||||
toolPageId: 'markdown-to-pdf',
|
||||
},
|
||||
JsonToPdfNode: {
|
||||
label: 'JSON to PDF',
|
||||
@@ -187,6 +201,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-code',
|
||||
description: 'Convert JSON files to PDF',
|
||||
factory: () => new JsonToPdfNode(),
|
||||
toolPageId: 'json-to-pdf',
|
||||
},
|
||||
XmlToPdfNode: {
|
||||
label: 'XML to PDF',
|
||||
@@ -194,6 +209,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-code',
|
||||
description: 'Convert XML documents to PDF',
|
||||
factory: () => new XmlToPdfNode(),
|
||||
toolPageId: 'xml-to-pdf',
|
||||
},
|
||||
WpdToPdfNode: {
|
||||
label: 'WPD to PDF',
|
||||
@@ -201,6 +217,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-text',
|
||||
description: 'Convert WordPerfect documents to PDF',
|
||||
factory: () => new WpdToPdfNode(),
|
||||
toolPageId: 'wpd-to-pdf',
|
||||
},
|
||||
WpsToPdfNode: {
|
||||
label: 'WPS to PDF',
|
||||
@@ -208,6 +225,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-text',
|
||||
description: 'Convert WPS Office documents to PDF',
|
||||
factory: () => new WpsToPdfNode(),
|
||||
toolPageId: 'wps-to-pdf',
|
||||
},
|
||||
PagesToPdfNode: {
|
||||
label: 'Pages to PDF',
|
||||
@@ -215,6 +233,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-text',
|
||||
description: 'Convert Apple Pages documents to PDF',
|
||||
factory: () => new PagesToPdfNode(),
|
||||
toolPageId: 'pages-to-pdf',
|
||||
},
|
||||
OdgToPdfNode: {
|
||||
label: 'ODG to PDF',
|
||||
@@ -222,6 +241,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-image',
|
||||
description: 'Convert OpenDocument Graphics to PDF',
|
||||
factory: () => new OdgToPdfNode(),
|
||||
toolPageId: 'odg-to-pdf',
|
||||
},
|
||||
PubToPdfNode: {
|
||||
label: 'PUB to PDF',
|
||||
@@ -229,6 +249,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-book-open',
|
||||
description: 'Convert Microsoft Publisher to PDF',
|
||||
factory: () => new PubToPdfNode(),
|
||||
toolPageId: 'pub-to-pdf',
|
||||
},
|
||||
VsdToPdfNode: {
|
||||
label: 'VSD to PDF',
|
||||
@@ -236,6 +257,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-git-branch',
|
||||
description: 'Convert Visio diagrams (VSD/VSDX) to PDF',
|
||||
factory: () => new VsdToPdfNode(),
|
||||
toolPageId: 'vsd-to-pdf',
|
||||
},
|
||||
MergeNode: {
|
||||
label: 'Merge PDFs',
|
||||
@@ -243,6 +265,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-browsers',
|
||||
description: 'Combine multiple PDFs into one',
|
||||
factory: () => new MergeNode(),
|
||||
toolPageId: 'merge-pdf',
|
||||
},
|
||||
SplitNode: {
|
||||
label: 'Split PDF',
|
||||
@@ -250,6 +273,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-scissors',
|
||||
description: 'Extract a range of pages',
|
||||
factory: () => new SplitNode(),
|
||||
toolPageId: 'split-pdf',
|
||||
},
|
||||
ExtractPagesNode: {
|
||||
label: 'Extract Pages',
|
||||
@@ -257,6 +281,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-squares-four',
|
||||
description: 'Extract pages as separate PDFs',
|
||||
factory: () => new ExtractPagesNode(),
|
||||
toolPageId: 'extract-pages',
|
||||
},
|
||||
RotateNode: {
|
||||
label: 'Rotate',
|
||||
@@ -264,6 +289,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-arrow-clockwise',
|
||||
description: 'Rotate all pages',
|
||||
factory: () => new RotateNode(),
|
||||
toolPageId: 'rotate-pdf',
|
||||
},
|
||||
DeletePagesNode: {
|
||||
label: 'Delete Pages',
|
||||
@@ -271,6 +297,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-trash',
|
||||
description: 'Remove specific pages',
|
||||
factory: () => new DeletePagesNode(),
|
||||
toolPageId: 'delete-pages',
|
||||
},
|
||||
ReversePagesNode: {
|
||||
label: 'Reverse Pages',
|
||||
@@ -278,6 +305,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-sort-descending',
|
||||
description: 'Reverse page order',
|
||||
factory: () => new ReversePagesNode(),
|
||||
toolPageId: 'reverse-pages',
|
||||
},
|
||||
AddBlankPageNode: {
|
||||
label: 'Add Blank Page',
|
||||
@@ -285,6 +313,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-plus',
|
||||
description: 'Insert blank pages',
|
||||
factory: () => new AddBlankPageNode(),
|
||||
toolPageId: 'add-blank-page',
|
||||
},
|
||||
DividePagesNode: {
|
||||
label: 'Divide Pages',
|
||||
@@ -292,6 +321,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-columns',
|
||||
description: 'Split pages vertically or horizontally',
|
||||
factory: () => new DividePagesNode(),
|
||||
toolPageId: 'divide-pages',
|
||||
},
|
||||
NUpNode: {
|
||||
label: 'N-Up',
|
||||
@@ -299,6 +329,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-squares-four',
|
||||
description: 'Arrange multiple pages per sheet',
|
||||
factory: () => new NUpNode(),
|
||||
toolPageId: 'n-up-pdf',
|
||||
},
|
||||
FixPageSizeNode: {
|
||||
label: 'Fix Page Size',
|
||||
@@ -306,6 +337,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-frame-corners',
|
||||
description: 'Standardize all pages to a target size',
|
||||
factory: () => new FixPageSizeNode(),
|
||||
toolPageId: 'fix-page-size',
|
||||
},
|
||||
CombineSinglePageNode: {
|
||||
label: 'Combine to Single Page',
|
||||
@@ -313,6 +345,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-arrows-out-line-vertical',
|
||||
description: 'Stitch all pages into one continuous page',
|
||||
factory: () => new CombineSinglePageNode(),
|
||||
toolPageId: 'combine-single-page',
|
||||
},
|
||||
BookletNode: {
|
||||
label: 'Booklet',
|
||||
@@ -320,6 +353,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-book-open',
|
||||
description: 'Arrange pages for booklet printing',
|
||||
factory: () => new BookletNode(),
|
||||
toolPageId: 'pdf-booklet',
|
||||
},
|
||||
PosterizeNode: {
|
||||
label: 'Posterize',
|
||||
@@ -327,6 +361,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-notepad',
|
||||
description: 'Split pages into tile grid for poster printing',
|
||||
factory: () => new PosterizeNode(),
|
||||
toolPageId: 'posterize-pdf',
|
||||
},
|
||||
EditMetadataNode: {
|
||||
label: 'Edit Metadata',
|
||||
@@ -334,6 +369,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-code',
|
||||
description: 'Edit PDF metadata',
|
||||
factory: () => new EditMetadataNode(),
|
||||
toolPageId: 'edit-metadata',
|
||||
},
|
||||
TableOfContentsNode: {
|
||||
label: 'Table of Contents',
|
||||
@@ -341,6 +377,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-list',
|
||||
description: 'Generate table of contents from bookmarks',
|
||||
factory: () => new TableOfContentsNode(),
|
||||
toolPageId: 'table-of-contents',
|
||||
},
|
||||
OCRNode: {
|
||||
label: 'OCR',
|
||||
@@ -348,6 +385,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-barcode',
|
||||
description: 'Add searchable text layer via OCR',
|
||||
factory: () => new OCRNode(),
|
||||
toolPageId: 'ocr-pdf',
|
||||
},
|
||||
CropNode: {
|
||||
label: 'Crop',
|
||||
@@ -355,6 +393,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-crop',
|
||||
description: 'Trim margins from all pages',
|
||||
factory: () => new CropNode(),
|
||||
toolPageId: 'crop-pdf',
|
||||
},
|
||||
GreyscaleNode: {
|
||||
label: 'Greyscale',
|
||||
@@ -362,6 +401,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-palette',
|
||||
description: 'Convert to greyscale',
|
||||
factory: () => new GreyscaleNode(),
|
||||
toolPageId: 'pdf-to-greyscale',
|
||||
},
|
||||
InvertColorsNode: {
|
||||
label: 'Invert Colors',
|
||||
@@ -369,6 +409,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-circle-half',
|
||||
description: 'Invert all colors',
|
||||
factory: () => new InvertColorsNode(),
|
||||
toolPageId: 'invert-colors',
|
||||
},
|
||||
ScannerEffectNode: {
|
||||
label: 'Scanner Effect',
|
||||
@@ -376,6 +417,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-scan',
|
||||
description: 'Apply scanner simulation effect',
|
||||
factory: () => new ScannerEffectNode(),
|
||||
toolPageId: 'scanner-effect',
|
||||
},
|
||||
AdjustColorsNode: {
|
||||
label: 'Adjust Colors',
|
||||
@@ -383,6 +425,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-sliders-horizontal',
|
||||
description: 'Adjust brightness, contrast, and colors',
|
||||
factory: () => new AdjustColorsNode(),
|
||||
toolPageId: 'adjust-colors',
|
||||
},
|
||||
BackgroundColorNode: {
|
||||
label: 'Background Color',
|
||||
@@ -390,6 +433,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-palette',
|
||||
description: 'Change background color',
|
||||
factory: () => new BackgroundColorNode(),
|
||||
toolPageId: 'background-color',
|
||||
},
|
||||
WatermarkNode: {
|
||||
label: 'Watermark',
|
||||
@@ -397,6 +441,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-drop',
|
||||
description: 'Add text watermark',
|
||||
factory: () => new WatermarkNode(),
|
||||
toolPageId: 'add-watermark',
|
||||
},
|
||||
PageNumbersNode: {
|
||||
label: 'Page Numbers',
|
||||
@@ -404,6 +449,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-list-numbers',
|
||||
description: 'Add page numbers',
|
||||
factory: () => new PageNumbersNode(),
|
||||
toolPageId: 'page-numbers',
|
||||
},
|
||||
HeaderFooterNode: {
|
||||
label: 'Header & Footer',
|
||||
@@ -411,6 +457,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-paragraph',
|
||||
description: 'Add header and footer text',
|
||||
factory: () => new HeaderFooterNode(),
|
||||
toolPageId: 'header-footer',
|
||||
},
|
||||
RemoveBlankPagesNode: {
|
||||
label: 'Remove Blank Pages',
|
||||
@@ -418,6 +465,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-minus',
|
||||
description: 'Remove blank pages automatically',
|
||||
factory: () => new RemoveBlankPagesNode(),
|
||||
toolPageId: 'remove-blank-pages',
|
||||
},
|
||||
RemoveAnnotationsNode: {
|
||||
label: 'Remove Annotations',
|
||||
@@ -425,6 +473,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-eraser',
|
||||
description: 'Strip all annotations',
|
||||
factory: () => new RemoveAnnotationsNode(),
|
||||
toolPageId: 'remove-annotations',
|
||||
},
|
||||
CompressNode: {
|
||||
label: 'Compress',
|
||||
@@ -432,6 +481,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-lightning',
|
||||
description: 'Reduce PDF file size',
|
||||
factory: () => new CompressNode(),
|
||||
toolPageId: 'compress-pdf',
|
||||
},
|
||||
RasterizeNode: {
|
||||
label: 'Rasterize',
|
||||
@@ -439,6 +489,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-image',
|
||||
description: 'Convert to image-based PDF',
|
||||
factory: () => new RasterizeNode(),
|
||||
toolPageId: 'rasterize-pdf',
|
||||
},
|
||||
LinearizeNode: {
|
||||
label: 'Linearize',
|
||||
@@ -446,6 +497,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-gauge',
|
||||
description: 'Optimize PDF for fast web viewing',
|
||||
factory: () => new LinearizeNode(),
|
||||
toolPageId: 'linearize-pdf',
|
||||
},
|
||||
DeskewNode: {
|
||||
label: 'Deskew',
|
||||
@@ -453,6 +505,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-perspective',
|
||||
description: 'Straighten skewed PDF pages',
|
||||
factory: () => new DeskewNode(),
|
||||
toolPageId: 'deskew-pdf',
|
||||
},
|
||||
PdfToPdfANode: {
|
||||
label: 'PDF to PDF/A',
|
||||
@@ -460,6 +513,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-archive',
|
||||
description: 'Convert PDF to PDF/A for archiving',
|
||||
factory: () => new PdfToPdfANode(),
|
||||
toolPageId: 'pdf-to-pdfa',
|
||||
},
|
||||
FontToOutlineNode: {
|
||||
label: 'Font to Outline',
|
||||
@@ -467,6 +521,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-text-outdent',
|
||||
description: 'Convert fonts to vector outlines',
|
||||
factory: () => new FontToOutlineNode(),
|
||||
toolPageId: 'font-to-outline',
|
||||
},
|
||||
RepairNode: {
|
||||
label: 'Repair',
|
||||
@@ -474,6 +529,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-wrench',
|
||||
description: 'Repair corrupted PDF',
|
||||
factory: () => new RepairNode(),
|
||||
toolPageId: 'repair-pdf',
|
||||
},
|
||||
EncryptNode: {
|
||||
label: 'Encrypt',
|
||||
@@ -481,6 +537,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-lock',
|
||||
description: 'Encrypt PDF with password',
|
||||
factory: () => new EncryptNode(),
|
||||
toolPageId: 'encrypt-pdf',
|
||||
},
|
||||
DecryptNode: {
|
||||
label: 'Decrypt',
|
||||
@@ -488,6 +545,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-lock-open',
|
||||
description: 'Remove PDF password protection',
|
||||
factory: () => new DecryptNode(),
|
||||
toolPageId: 'decrypt-pdf',
|
||||
},
|
||||
SanitizeNode: {
|
||||
label: 'Sanitize',
|
||||
@@ -495,6 +553,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-broom',
|
||||
description: 'Remove metadata, scripts, and hidden data',
|
||||
factory: () => new SanitizeNode(),
|
||||
toolPageId: 'sanitize-pdf',
|
||||
},
|
||||
FlattenNode: {
|
||||
label: 'Flatten',
|
||||
@@ -502,6 +561,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-stack',
|
||||
description: 'Flatten forms and annotations',
|
||||
factory: () => new FlattenNode(),
|
||||
toolPageId: 'flatten-pdf',
|
||||
},
|
||||
DigitalSignNode: {
|
||||
label: 'Digital Sign',
|
||||
@@ -509,6 +569,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-certificate',
|
||||
description: 'Apply a digital signature to PDF',
|
||||
factory: () => new DigitalSignNode(),
|
||||
toolPageId: 'digital-sign-pdf',
|
||||
},
|
||||
TimestampNode: {
|
||||
label: 'Timestamp',
|
||||
@@ -516,6 +577,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-clock',
|
||||
description: 'Add an RFC 3161 document timestamp',
|
||||
factory: () => new TimestampNode(),
|
||||
toolPageId: 'timestamp-pdf',
|
||||
},
|
||||
RedactNode: {
|
||||
label: 'Redact',
|
||||
@@ -523,6 +585,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-eye-slash',
|
||||
description: 'Redact text from PDF',
|
||||
factory: () => new RedactNode(),
|
||||
toolPageId: 'edit-pdf',
|
||||
},
|
||||
DownloadNode: {
|
||||
label: 'Download',
|
||||
@@ -554,6 +617,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-image',
|
||||
description: 'Convert PDF pages to images (ZIP)',
|
||||
factory: () => new PdfToImagesNode(),
|
||||
toolPageId: 'pdf-to-jpg',
|
||||
},
|
||||
PdfToTextNode: {
|
||||
label: 'PDF to Text',
|
||||
@@ -561,6 +625,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-text-aa',
|
||||
description: 'Extract text from PDF',
|
||||
factory: () => new PdfToTextNode(),
|
||||
toolPageId: 'pdf-to-text',
|
||||
},
|
||||
PdfToDocxNode: {
|
||||
label: 'PDF to DOCX',
|
||||
@@ -568,6 +633,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-microsoft-word-logo',
|
||||
description: 'Convert PDF to Word document',
|
||||
factory: () => new PdfToDocxNode(),
|
||||
toolPageId: 'pdf-to-docx',
|
||||
},
|
||||
PdfToXlsxNode: {
|
||||
label: 'PDF to XLSX',
|
||||
@@ -575,6 +641,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-microsoft-excel-logo',
|
||||
description: 'Convert PDF tables to Excel',
|
||||
factory: () => new PdfToXlsxNode(),
|
||||
toolPageId: 'pdf-to-excel',
|
||||
},
|
||||
PdfToCsvNode: {
|
||||
label: 'PDF to CSV',
|
||||
@@ -582,6 +649,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-csv',
|
||||
description: 'Convert PDF tables to CSV',
|
||||
factory: () => new PdfToCsvNode(),
|
||||
toolPageId: 'pdf-to-csv',
|
||||
},
|
||||
PdfToSvgNode: {
|
||||
label: 'PDF to SVG',
|
||||
@@ -589,6 +657,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-file-code',
|
||||
description: 'Convert PDF pages to SVG',
|
||||
factory: () => new PdfToSvgNode(),
|
||||
toolPageId: 'pdf-to-svg',
|
||||
},
|
||||
PdfToMarkdownNode: {
|
||||
label: 'PDF to Markdown',
|
||||
@@ -596,6 +665,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-markdown-logo',
|
||||
description: 'Convert PDF to Markdown text',
|
||||
factory: () => new PdfToMarkdownNode(),
|
||||
toolPageId: 'pdf-to-markdown',
|
||||
},
|
||||
ExtractImagesNode: {
|
||||
label: 'Extract Images',
|
||||
@@ -603,6 +673,7 @@ export const nodeRegistry: Record<string, NodeRegistryEntry> = {
|
||||
icon: 'ph-download-simple',
|
||||
description: 'Extract all images from PDF',
|
||||
factory: () => new ExtractImagesNode(),
|
||||
toolPageId: 'extract-images',
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
1
src/types/globals.d.ts
vendored
1
src/types/globals.d.ts
vendored
@@ -13,3 +13,4 @@ interface ImportMeta {
|
||||
}
|
||||
|
||||
declare const __SIMPLE_MODE__: boolean;
|
||||
declare const __DISABLED_TOOLS__: string[];
|
||||
|
||||
@@ -420,6 +420,12 @@ export default defineConfig(() => {
|
||||
define: {
|
||||
__SIMPLE_MODE__: JSON.stringify(process.env.SIMPLE_MODE === 'true'),
|
||||
__BRAND_NAME__: JSON.stringify(process.env.VITE_BRAND_NAME || ''),
|
||||
__DISABLED_TOOLS__: JSON.stringify(
|
||||
(process.env.DISABLE_TOOLS || '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
),
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
|
||||
Reference in New Issue
Block a user