From 607c24895bab23f7bcf788c0ef2110ae9ae5972c Mon Sep 17 00:00:00 2001 From: SnapOtter Date: Wed, 24 Jun 2026 12:06:31 +0800 Subject: [PATCH] Improve docs search UX, dash cleanup, and colored modality tool icons (#338) * feat(docs): improve search UX - Hide the 'Search by Pagefind' branding in the search dialog footer - Replace the 'No results found.' message shown before any query with a friendly hint (detected via empty-input :placeholder-shown state) - Tune placeholder, empty-state, and results-heading copy; show a few sub-section matches per result (pageResultCount) * docs: replace double-dash em-dash substitute with single dash Swept prose ' -- ' to ' - ' and numeric ranges (e.g. 2--20 to 2-20) across the documentation. CLI flags and code blocks left untouched. * fix(landing): show colored category icons on modality tool pages The /tools// pages rendered bare name+description cards with no icon or color. Port the colored, category-tinted icon card from /tools/ so modality pages match the main catalog (icon, tint, 'Learn more'). --- apps/docs/.vitepress/config.mts | 9 ++- apps/docs/.vitepress/theme/index.ts | 1 + apps/docs/.vitepress/theme/search.css | 58 ++++++++++++++++ apps/docs/api/ai.md | 4 +- apps/docs/changelog.md | 46 ++++++------- apps/docs/guide/architecture.md | 8 +-- apps/docs/guide/configuration.md | 8 +-- apps/docs/guide/deployment.md | 12 ++-- apps/docs/guide/developer.md | 2 +- apps/docs/guide/getting-started.md | 2 +- apps/docs/guide/scim.md | 2 +- apps/docs/guide/security.md | 4 +- apps/docs/guide/translations.md | 10 +-- apps/docs/guide/users-roles.md | 8 +-- apps/docs/tools/files/chart-maker.md | 4 +- apps/docs/tools/files/create-zip.md | 2 +- apps/docs/tools/files/merge-csvs.md | 2 +- apps/docs/tools/files/split-csv.md | 2 +- apps/docs/tools/image/smart-crop.md | 2 +- .../src/pages/tools/[section]/index.astro | 69 +++++++++++++++---- 20 files changed, 182 insertions(+), 73 deletions(-) create mode 100644 apps/docs/.vitepress/theme/search.css diff --git a/apps/docs/.vitepress/config.mts b/apps/docs/.vitepress/config.mts index 2c1e8212..eea97aa4 100644 --- a/apps/docs/.vitepress/config.mts +++ b/apps/docs/.vitepress/config.mts @@ -49,7 +49,14 @@ export default defineConfig({ vite: { plugins: [ - pagefindPlugin(), + pagefindPlugin({ + btnPlaceholder: "Search", + placeholder: "Search tools, guides, and the API…", + emptyText: "No matches found. Try a different term or check the spelling.", + heading: "{{searchResult}} results", + // Show a few sub-section matches per page so deep-linked headings surface. + pageResultCount: 3, + }), llmstxt({ domain: "https://docs.snapotter.com", customLLMsTxtTemplate: `# {title} diff --git a/apps/docs/.vitepress/theme/index.ts b/apps/docs/.vitepress/theme/index.ts index 6c2f71e0..a7c37ab7 100644 --- a/apps/docs/.vitepress/theme/index.ts +++ b/apps/docs/.vitepress/theme/index.ts @@ -4,6 +4,7 @@ import Layout from "./Layout.vue"; import "./vars.css"; import "./fonts.css"; import "./github-stars.css"; +import "./search.css"; export default { extends: DefaultTheme, diff --git a/apps/docs/.vitepress/theme/search.css b/apps/docs/.vitepress/theme/search.css new file mode 100644 index 00000000..fc9429b6 --- /dev/null +++ b/apps/docs/.vitepress/theme/search.css @@ -0,0 +1,58 @@ +/** + * Customizations for the Pagefind-powered search dialog + * (vitepress-plugin-pagefind, rendered via vue-command-palette `algolia` theme). + * + * Two goals: + * 1. Hide the "Search by Pagefind" branding in the dialog footer. + * 2. Replace the misleading "No results found." text shown the instant the + * dialog opens (before the user has typed anything) with a friendly hint. + * We can't change the component's markup, so we detect an empty query via + * the input's `:placeholder-shown` state and restyle the empty slot. + */ + +/* 1. Drop the Pagefind logo/attribution in the footer. + The keyboard-shortcut hints (to select / navigate / close) stay. */ +.algolia .command-palette-logo { + display: none; +} + +/* 2a. Before anything is typed (input still shows its placeholder), turn the + empty slot into a centered hint with an icon instead of "No results found." */ +.algolia + [command-dialog-wrapper]:has([command-input]:placeholder-shown) + [command-empty=""] { + flex-direction: column; + gap: 10px; + height: auto; + min-height: 160px; + /* Hide the component's default empty text without removing it from the DOM. */ + font-size: 0; +} + +.algolia + [command-dialog-wrapper]:has([command-input]:placeholder-shown) + [command-empty=""]::before { + content: ""; + width: 28px; + height: 28px; + background-color: var(--vp-c-brand-1); + /* magnifying-glass mask so the icon picks up the brand (Otter Orange) */ + -webkit-mask: var(--search-hint-icon) center / contain no-repeat; + mask: var(--search-hint-icon) center / contain no-repeat; + opacity: 0.9; +} + +.algolia + [command-dialog-wrapper]:has([command-input]:placeholder-shown) + [command-empty=""]::after { + content: "Search 157 tools, guides, and the API reference"; + font-size: 14px; + line-height: 1.4; + color: var(--vp-c-text-2); + max-width: 320px; + text-align: center; +} + +:root { + --search-hint-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z' stroke='black' fill='none' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); +} diff --git a/apps/docs/api/ai.md b/apps/docs/api/ai.md index cf49b7fe..c9b60216 100644 --- a/apps/docs/api/ai.md +++ b/apps/docs/api/ai.md @@ -6,7 +6,7 @@ description: AI engine reference with all local ML tools. Background removal, up The `@snapotter/ai` package bridges Node.js to a **persistent Python sidecar** for all ML operations. The dispatcher process stays alive between requests for fast warm-start performance. GPU is auto-detected at startup and used when available. -19 Python sidecar AI tools across four modalities (image, audio, video, document), plus 2 tools with optional AI capabilities. All models run locally -- no internet required after initial model download. +19 Python sidecar AI tools across four modalities (image, audio, video, document), plus 2 tools with optional AI capabilities. All models run locally - no internet required after initial model download. ## Architecture @@ -406,7 +406,7 @@ An additional analysis endpoint is available at `POST /api/v1/tools/image/image- ### Content-Aware Resize (Seam Carving) **Tool route:** `content-aware-resize` -**Engine:** Go `caire` binary (not Python -- no GPU benefit) +**Engine:** Go `caire` binary (not Python - no GPU benefit) Intelligently resizes images by removing low-energy seams, preserving important content. diff --git a/apps/docs/changelog.md b/apps/docs/changelog.md index 030c193f..c64778a9 100644 --- a/apps/docs/changelog.md +++ b/apps/docs/changelog.md @@ -65,11 +65,11 @@ Live demo, per-tool landing pages, and a batch of polish fixes. ### New features -- **Live demo** -- [demo.snapotter.com](https://demo.snapotter.com) lets people try SnapOtter without installing anything. -- **Tools index page** -- Browse all 50+ tools at `/tools` with search and category filters. -- **50+ SEO landing pages** -- Every tool now has a dedicated landing page with FAQs, use cases, and comparison tables. -- **Background preview** -- Before-after slider shows a checkered background behind transparent images. -- **Strong password generator** -- One-click button in the Add Members form. +- **Live demo** - [demo.snapotter.com](https://demo.snapotter.com) lets people try SnapOtter without installing anything. +- **Tools index page** - Browse all 50+ tools at `/tools` with search and category filters. +- **50+ SEO landing pages** - Every tool now has a dedicated landing page with FAQs, use cases, and comparison tables. +- **Background preview** - Before-after slider shows a checkered background behind transparent images. +- **Strong password generator** - One-click button in the Add Members form. ### Bug fixes @@ -110,28 +110,28 @@ Five new tools, a full image editor, SSO login, 20 languages. Probably should ha ### New features -- **Image editor** -- Layers, brushes, shapes, adjustments, filters, curves, keyboard shortcuts. Runs in your browser, processes on your hardware. -- **OIDC / SSO authentication** -- Login with Google, GitHub, Okta, or any OpenID Connect provider. Set a few env vars and your team uses their existing accounts. -- **Meme generator** -- 100 built-in templates with text rendering via opentype.js. Or upload your own image. -- **Beautify** -- Drop a screenshot in, get a polished image out. Device frames (macOS, Windows, browser), shadows, gradients, social media presets. -- **Color blindness simulation** -- Preview how images look with protanopia, deuteranopia, tritanopia, and other color vision deficiencies. -- **PNG transparency fixer** -- Detects fake-transparent PNGs and fixes them with BiRefNet HR-matting. Optional watermark removal via LaMa inpainting. -- **AI canvas expand** -- Extend image boundaries with AI fill. Three quality tiers (fast, balanced, quality) depending on how much GPU time you want to trade. -- **20 languages** -- Arabic, Chinese (Simplified/Traditional), Czech, Dutch, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Portuguese, Russian, Spanish, Thai, Turkish, Ukrainian, Vietnamese. RTL works for Arabic. -- **URL import** -- Paste URLs into the dropzone or bulk-import from a list. Server-side fetch with SSRF protection. -- **Multi-file eraser** -- Draw erase masks across multiple images, process them all with one click. Strokes persist per-image. -- **Pipeline import/export** -- Save tool chains as JSON, share them with others. +- **Image editor** - Layers, brushes, shapes, adjustments, filters, curves, keyboard shortcuts. Runs in your browser, processes on your hardware. +- **OIDC / SSO authentication** - Login with Google, GitHub, Okta, or any OpenID Connect provider. Set a few env vars and your team uses their existing accounts. +- **Meme generator** - 100 built-in templates with text rendering via opentype.js. Or upload your own image. +- **Beautify** - Drop a screenshot in, get a polished image out. Device frames (macOS, Windows, browser), shadows, gradients, social media presets. +- **Color blindness simulation** - Preview how images look with protanopia, deuteranopia, tritanopia, and other color vision deficiencies. +- **PNG transparency fixer** - Detects fake-transparent PNGs and fixes them with BiRefNet HR-matting. Optional watermark removal via LaMa inpainting. +- **AI canvas expand** - Extend image boundaries with AI fill. Three quality tiers (fast, balanced, quality) depending on how much GPU time you want to trade. +- **20 languages** - Arabic, Chinese (Simplified/Traditional), Czech, Dutch, French, German, Hindi, Indonesian, Italian, Japanese, Korean, Polish, Portuguese, Russian, Spanish, Thai, Turkish, Ukrainian, Vietnamese. RTL works for Arabic. +- **URL import** - Paste URLs into the dropzone or bulk-import from a list. Server-side fetch with SSRF protection. +- **Multi-file eraser** - Draw erase masks across multiple images, process them all with one click. Strokes persist per-image. +- **Pipeline import/export** - Save tool chains as JSON, share them with others. - **17 new camera RAW formats** via exiftool, plus QOI, JP2, EPS, DDS, CUR, DPX, FITS, PPM/PGM/PBM, SVGZ, and APNG input. New output codecs for BMP, ICO, JP2, QOI. AVIF, TIFF, GIF, JXL, and PSD export recovered from a previously lost branch. ### Improvements -- **Image enhancement** -- Replaced the old pipeline with CLAHE + normalise + gamma. New Deep Enhance toggle uses the AI model for more aggressive results. -- **Restore photo** -- Scratch detection rewritten with 8-angle Otsu filtering. LaMa inpainting now runs at native resolution. -- **Exotic formats everywhere** -- OCR, image-to-PDF, favicon generator, composition, stitch, and vectorize all decode HEIC, RAW, PSD now. -- **Compress** -- Target-size tolerance tightened from 5% to 1%. Target size is the default mode. Added stepper buttons and KB/MB unit selector. -- **Sentry cleanup** -- 644 non-actionable events filtered. Real errors now handled properly. -- **GPU detection** -- Better diagnostics for containers where CUDA is present but nvidia-smi is not. -- **Auth-disabled mode** -- Anonymous user is seeded in the DB with admin role. API keys, pipelines, and user files no longer break on FK constraints. +- **Image enhancement** - Replaced the old pipeline with CLAHE + normalise + gamma. New Deep Enhance toggle uses the AI model for more aggressive results. +- **Restore photo** - Scratch detection rewritten with 8-angle Otsu filtering. LaMa inpainting now runs at native resolution. +- **Exotic formats everywhere** - OCR, image-to-PDF, favicon generator, composition, stitch, and vectorize all decode HEIC, RAW, PSD now. +- **Compress** - Target-size tolerance tightened from 5% to 1%. Target size is the default mode. Added stepper buttons and KB/MB unit selector. +- **Sentry cleanup** - 644 non-actionable events filtered. Real errors now handled properly. +- **GPU detection** - Better diagnostics for containers where CUDA is present but nvidia-smi is not. +- **Auth-disabled mode** - Anonymous user is seeded in the DB with admin role. API keys, pipelines, and user files no longer break on FK constraints. - **2,705+ new tests** across unit, integration, and E2E. ### Bug fixes diff --git a/apps/docs/guide/architecture.md b/apps/docs/guide/architecture.md index 179966fb..aa2ad7e4 100644 --- a/apps/docs/guide/architecture.md +++ b/apps/docs/guide/architecture.md @@ -51,14 +51,14 @@ Shared TypeScript types, constants (like `APP_VERSION` and tool definitions), an A Fastify v5 server exposing 157 tool routes across five modalities (image, video, audio, document, data) that handles: - File uploads, temporary workspace management, and persistent file storage -- User file library with version chains (`user_files` table) -- each processed result links back to its source file and records which tool was applied, with auto-generated thumbnails for the Files page +- User file library with version chains (`user_files` table) - each processed result links back to its source file and records which tool was applied, with auto-generated thumbnails for the Files page - Tool execution (routes each tool request to the image engine or AI bridge) - Pipeline orchestration (chaining multiple tools sequentially) - Batch processing with concurrency control via BullMQ job queues (pools: image, media, ai, docs, system) - User authentication, RBAC (admin/user roles with a full permission set), API key management, and rate limiting -- Teams management -- admin-only CRUD; users are assigned to a team via the `team` field on their profile -- Runtime settings -- a key-value store in the `settings` table that controls `disabledTools`, `enableExperimentalTools`, `loginAttemptLimit`, and other operational knobs without redeploying -- Custom branding -- logo upload endpoint; the uploaded image is stored at `data/branding/logo.png` and served to the frontend +- Teams management - admin-only CRUD; users are assigned to a team via the `team` field on their profile +- Runtime settings - a key-value store in the `settings` table that controls `disabledTools`, `enableExperimentalTools`, `loginAttemptLimit`, and other operational knobs without redeploying +- Custom branding - logo upload endpoint; the uploaded image is stored at `data/branding/logo.png` and served to the frontend - Swagger/OpenAPI documentation at `/api/docs` - Serving the built frontend as a SPA in production diff --git a/apps/docs/guide/configuration.md b/apps/docs/guide/configuration.md index 8eace20e..d6a3aba5 100644 --- a/apps/docs/guide/configuration.md +++ b/apps/docs/guide/configuration.md @@ -142,7 +142,7 @@ volumes: The Docker Compose stack uses four volumes: -- `/data` (app) -- AI models, Python venv, and user files. Mount this to keep uploaded files and installed AI bundles across restarts. -- `/tmp/workspace` (app) -- Temporary storage for files being processed. This can be ephemeral, but mounting it avoids filling up the container's writable layer. -- `SnapOtter-pgdata` (postgres) -- PostgreSQL data directory. This holds all relational data (users, settings, pipelines, jobs, audit log). Back up via `pg_dump` or volume snapshot. -- `SnapOtter-redisdata` (redis) -- Redis append-only file for durable job queues. +- `/data` (app) - AI models, Python venv, and user files. Mount this to keep uploaded files and installed AI bundles across restarts. +- `/tmp/workspace` (app) - Temporary storage for files being processed. This can be ephemeral, but mounting it avoids filling up the container's writable layer. +- `SnapOtter-pgdata` (postgres) - PostgreSQL data directory. This holds all relational data (users, settings, pipelines, jobs, audit log). Back up via `pg_dump` or volume snapshot. +- `SnapOtter-redisdata` (redis) - Redis append-only file for durable job queues. diff --git a/apps/docs/guide/deployment.md b/apps/docs/guide/deployment.md index a5e1d36f..ff8ba450 100644 --- a/apps/docs/guide/deployment.md +++ b/apps/docs/guide/deployment.md @@ -11,7 +11,7 @@ See [Docker Image](./docker-tags) for GPU setup, Docker Compose examples, and ve ## Quick Start (CPU) ```yaml -# docker-compose.yml -- Copy this file and run: docker compose up -d +# docker-compose.yml - Copy this file and run: docker compose up -d services: SnapOtter: image: snapotter/snapotter:latest # or ghcr.io/snapotter-hq/snapotter:latest @@ -94,7 +94,7 @@ services: start_period: 10s volumes: - SnapOtter-data: # Named volume -- Docker manages permissions automatically + SnapOtter-data: # Named volume - Docker manages permissions automatically SnapOtter-workspace: SnapOtter-pgdata: SnapOtter-redisdata: @@ -113,7 +113,7 @@ The app is then available at `http://localhost:1349`. For NVIDIA GPU acceleration on AI tools (background removal, upscaling, face enhancement, OCR): ```yaml -# docker-compose-gpu.yml -- Requires: NVIDIA GPU + nvidia-container-toolkit +# docker-compose-gpu.yml - Requires: NVIDIA GPU + nvidia-container-toolkit # Install toolkit: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html services: SnapOtter: @@ -511,9 +511,9 @@ Note: Cloudflare has a 100 MB upload limit on free plans. Set `MAX_UPLOAD_SIZE_M The GitHub repository has three workflows: -- **ci.yml** -- Runs automatically on every push and PR. Lints, typechecks, tests, builds, and validates the Docker image (without pushing). -- **release.yml** -- Triggered manually via `workflow_dispatch`. Runs semantic-release to create a version tag and GitHub release, then builds a multi-arch Docker image (amd64 + arm64) and pushes to Docker Hub (`snapotter/snapotter`) and GitHub Container Registry (`ghcr.io/snapotter-hq/snapotter`). -- **deploy-docs.yml** -- Builds this documentation site and deploys it to Cloudflare Pages on push to `main`. +- **ci.yml** - Runs automatically on every push and PR. Lints, typechecks, tests, builds, and validates the Docker image (without pushing). +- **release.yml** - Triggered manually via `workflow_dispatch`. Runs semantic-release to create a version tag and GitHub release, then builds a multi-arch Docker image (amd64 + arm64) and pushes to Docker Hub (`snapotter/snapotter`) and GitHub Container Registry (`ghcr.io/snapotter-hq/snapotter`). +- **deploy-docs.yml** - Builds this documentation site and deploys it to Cloudflare Pages on push to `main`. To create a release, go to **Actions > Release > Run workflow** in the GitHub UI, or run: diff --git a/apps/docs/guide/developer.md b/apps/docs/guide/developer.md index a5930170..62ff5de0 100644 --- a/apps/docs/guide/developer.md +++ b/apps/docs/guide/developer.md @@ -79,7 +79,7 @@ pnpm test:coverage # tests with coverage report ## Database -PostgreSQL 17 via Drizzle ORM (pg-core). Local dev requires Postgres and Redis running -- start them with: +PostgreSQL 17 via Drizzle ORM (pg-core). Local dev requires Postgres and Redis running - start them with: ```bash docker compose -f docker-compose.dev.yml up -d diff --git a/apps/docs/guide/getting-started.md b/apps/docs/guide/getting-started.md index e5ebd62b..fa29eab5 100644 --- a/apps/docs/guide/getting-started.md +++ b/apps/docs/guide/getting-started.md @@ -5,7 +5,7 @@ description: Install SnapOtter with Docker in one command. Includes Docker Compo # Getting Started ::: tip Try before installing -Explore the full UI at [demo.snapotter.com](https://demo.snapotter.com) -- no signup or install required. +Explore the full UI at [demo.snapotter.com](https://demo.snapotter.com) - no signup or install required. ::: ## Quick Start diff --git a/apps/docs/guide/scim.md b/apps/docs/guide/scim.md index 5b1b5fde..4aa6b001 100644 --- a/apps/docs/guide/scim.md +++ b/apps/docs/guide/scim.md @@ -32,7 +32,7 @@ The response contains the token. Save it immediately; it cannot be retrieved aga ```json { "token": "a1b2c3d4e5f6...", - "message": "Save this token -- it cannot be retrieved again" + "message": "Save this token - it cannot be retrieved again" } ``` diff --git a/apps/docs/guide/security.md b/apps/docs/guide/security.md index c9b610dd..9d699a12 100644 --- a/apps/docs/guide/security.md +++ b/apps/docs/guide/security.md @@ -36,7 +36,7 @@ services: # --- Resource limits --- mem_limit: 6g # Prevents runaway memory from crashing the host - memswap_limit: 6g # No swap -- fail fast instead of degrading the host + memswap_limit: 6g # No swap - fail fast instead of degrading the host cpus: 4 # Cap CPU usage to 4 cores pids_limit: 512 # Prevents fork bombs @@ -130,7 +130,7 @@ The only exception is **AI model downloads**: when a user installs an AI feature |---|---| | Air-gapped (no AI) | Block all outbound traffic from the container | | AI bundles needed | Allow HTTPS to `github.com`, `objects.githubusercontent.com`, `pypi.org`, `files.pythonhosted.org` during install, then block | -| After AI install | Block all outbound traffic -- models are cached locally | +| After AI install | Block all outbound traffic - models are cached locally | For reverse proxy configuration (Nginx, Traefik, Caddy, Cloudflare Tunnels), see the [Deployment guide](/guide/deployment#reverse-proxy). diff --git a/apps/docs/guide/translations.md b/apps/docs/guide/translations.md index f29dca44..5140efcf 100644 --- a/apps/docs/guide/translations.md +++ b/apps/docs/guide/translations.md @@ -36,10 +36,10 @@ SnapOtter ships with 21 languages out of the box. The i18n system uses a lightwe SnapOtter uses a three-tier resolution order: -1. **User preference** -- stored in `localStorage("snapotter-locale")` and synced to user settings when authenticated -2. **Browser auto-detect** -- walks the `navigator.languages` array with BCP 47 prefix matching -3. **Instance default** -- the admin's `DEFAULT_LOCALE` env var (fetched from `GET /api/v1/config/locale`) -4. **English fallback** -- always available +1. **User preference** - stored in `localStorage("snapotter-locale")` and synced to user settings when authenticated +2. **Browser auto-detect** - walks the `navigator.languages` array with BCP 47 prefix matching +3. **Instance default** - the admin's `DEFAULT_LOCALE` env var (fetched from `GET /api/v1/config/locale`) +4. **English fallback** - always available Users can change language from: - The **footer Globe selector** (desktop, always visible) @@ -152,7 +152,7 @@ Open a PR against `main` with a title like `feat(i18n): add Swedish translation` When adding a new feature that needs new UI strings: 1. Add the new keys to `en.ts` first (the reference file) -2. Run `pnpm typecheck` -- every locale file will fail if missing the new key +2. Run `pnpm typecheck` - every locale file will fail if missing the new key 3. Add the new key to all locale files (use English as a temporary fallback) ## Configuration diff --git a/apps/docs/guide/users-roles.md b/apps/docs/guide/users-roles.md index ba98fc40..bcc8be71 100644 --- a/apps/docs/guide/users-roles.md +++ b/apps/docs/guide/users-roles.md @@ -31,10 +31,10 @@ The default admin is required to change their password on first login. Users can authenticate through several methods: -- **Local** -- username and password stored in the SnapOtter database -- **OIDC** -- any OpenID Connect provider (see [OIDC / SSO](/guide/oidc)) -- **SAML** -- SAML 2.0 identity providers (see [SAML SSO](/guide/saml)) -- **SCIM** -- automated provisioning from an identity provider (see [SCIM Provisioning](/guide/scim)) +- **Local** - username and password stored in the SnapOtter database +- **OIDC** - any OpenID Connect provider (see [OIDC / SSO](/guide/oidc)) +- **SAML** - SAML 2.0 identity providers (see [SAML SSO](/guide/saml)) +- **SCIM** - automated provisioning from an identity provider (see [SCIM Provisioning](/guide/scim)) ### Disabling authentication diff --git a/apps/docs/tools/files/chart-maker.md b/apps/docs/tools/files/chart-maker.md index 96800916..9d418535 100644 --- a/apps/docs/tools/files/chart-maker.md +++ b/apps/docs/tools/files/chart-maker.md @@ -18,8 +18,8 @@ Accepts multipart form data with a CSV or JSON file and a JSON `settings` field. |-----------|------|----------|---------|-------------| | kind | string | No | `"bar"` | Chart type: `bar`, `line`, `pie` | | title | string | No | - | Chart title (max 120 characters) | -| width | integer | No | `960` | Chart width in pixels (320--2048) | -| height | integer | No | `540` | Chart height in pixels (240--1536) | +| width | integer | No | `960` | Chart width in pixels (320-2048) | +| height | integer | No | `540` | Chart height in pixels (240-1536) | ## Example Request diff --git a/apps/docs/tools/files/create-zip.md b/apps/docs/tools/files/create-zip.md index 9f72343c..c0f4ad00 100644 --- a/apps/docs/tools/files/create-zip.md +++ b/apps/docs/tools/files/create-zip.md @@ -14,7 +14,7 @@ Accepts multipart form data with two or more files. No settings field is require ## Parameters -This tool has no configurable parameters. Upload 2--50 files of any type to bundle. +This tool has no configurable parameters. Upload 2-50 files of any type to bundle. ## Example Request diff --git a/apps/docs/tools/files/merge-csvs.md b/apps/docs/tools/files/merge-csvs.md index 569abeee..1e068fa2 100644 --- a/apps/docs/tools/files/merge-csvs.md +++ b/apps/docs/tools/files/merge-csvs.md @@ -14,7 +14,7 @@ Accepts multipart form data with two or more CSV files. No settings field is req ## Parameters -This tool has no configurable parameters. Upload 2--20 CSV or TSV files with matching column headers. +This tool has no configurable parameters. Upload 2-20 CSV or TSV files with matching column headers. ## Example Request diff --git a/apps/docs/tools/files/split-csv.md b/apps/docs/tools/files/split-csv.md index f4918a92..91b84084 100644 --- a/apps/docs/tools/files/split-csv.md +++ b/apps/docs/tools/files/split-csv.md @@ -16,7 +16,7 @@ Accepts multipart form data with a CSV file and a JSON `settings` field. | Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| -| rowsPerFile | integer | No | `1000` | Number of data rows per output file (1--1,000,000) | +| rowsPerFile | integer | No | `1000` | Number of data rows per output file (1-1,000,000) | | keepHeader | boolean | No | `true` | Repeat the header row in each output file | ## Example Request diff --git a/apps/docs/tools/image/smart-crop.md b/apps/docs/tools/image/smart-crop.md index 05d23313..179f093a 100644 --- a/apps/docs/tools/image/smart-crop.md +++ b/apps/docs/tools/image/smart-crop.md @@ -8,7 +8,7 @@ Smart subject-aware, face-aware, or trim-based cropping. Uses Sharp's attention/ **Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) -**Model bundle:** `face-detection` (200-300 MB) -- required only for `face` mode +**Model bundle:** `face-detection` (200-300 MB) - required only for `face` mode ## Parameters diff --git a/apps/landing/src/pages/tools/[section]/index.astro b/apps/landing/src/pages/tools/[section]/index.astro index 43720882..453a6f3b 100644 --- a/apps/landing/src/pages/tools/[section]/index.astro +++ b/apps/landing/src/pages/tools/[section]/index.astro @@ -1,5 +1,6 @@ --- -import { SECTIONS, TOOLS, toolSection } from "@snapotter/shared"; +import { CATEGORIES, SECTIONS, TOOLS, toolSection } from "@snapotter/shared"; +import * as lucideIcons from "lucide"; import Footer from "@/components/Footer.astro"; import Navbar from "@/components/Navbar.astro"; import Base from "@/layouts/Base.astro"; @@ -11,7 +12,22 @@ export function getStaticPaths() { })); } +function renderIcon(iconName: string): string { + const iconData = (lucideIcons as Record)[iconName]; + if (!iconData || !Array.isArray(iconData)) + return ''; + return iconData + .map(([tag, attrs]: [string, Record]) => { + const attrStr = Object.entries(attrs) + .map(([k, v]) => `${k}="${v}"`) + .join(" "); + return `<${tag} ${attrStr}/>`; + }) + .join(""); +} + const { section, tools } = Astro.props; +const categoryMap = new Map(CATEGORIES.map((c) => [c.id, c])); const canonical = `https://snapotter.com/tools/${section.id}/`; const title = `${section.name} Tools | SnapOtter`; --- @@ -41,18 +57,45 @@ const title = `${section.name} Tools | SnapOtter`;

{section.name} Tools

-