feat: pipeline templates, analytics opt-out, 83 conversion presets, positioning + e2e modernization

Lands five integrated branches: pipeline templates (#355), analytics opt-out (#354), 83 conversion presets bringing the catalog to 240 tools (#356), self-hosted positioning (#353), and e2e modernization (#351).

Integration fixes: aligned stale web analytics tests with the opt-out/allow-list model, closed 3 CodeQL incomplete-sanitization alerts in the i18n generator, resolved settings/index/docs/format-matrix conflicts, and corrected tool counts to 240.
This commit is contained in:
SnapOtter
2026-06-28 18:57:53 +08:00
committed by GitHub
parent 88af8d46fb
commit 63a03d26f2
421 changed files with 17308 additions and 2540 deletions
+12 -1
View File
@@ -1,4 +1,4 @@
import { modalityForExtension } from "@snapotter/shared";
import { modalityForExtension, type PipelineTemplate } from "@snapotter/shared";
import {
CheckCircle2,
ChevronDown,
@@ -23,6 +23,7 @@ import {
} from "lucide-react";
import { lazy, Suspense, useCallback, useEffect, useRef, useState } from "react";
import { useLocation, useNavigate } from "react-router-dom";
import { TemplatesSection } from "@/components/automate/templates-section";
import { BeforeAfterSlider } from "@/components/common/before-after-slider";
import { Dropzone } from "@/components/common/dropzone";
import { FileLibraryModal } from "@/components/common/file-library-modal";
@@ -277,6 +278,13 @@ export function AutomatePage() {
[loadSteps],
);
const handleUseTemplate = useCallback(
(template: PipelineTemplate) => {
loadSteps(template.steps);
},
[loadSteps],
);
const handleExportPipeline = useCallback((pipeline: SavedPipeline) => {
const exportData = {
format: "snapotter-pipeline" as const,
@@ -739,6 +747,9 @@ export function AutomatePage() {
{/* Tool catalog */}
<ToolPalette onAddStep={handleAddStep} className="flex-1 min-h-0" />
{/* Pipeline templates */}
<TemplatesSection onUse={handleUseTemplate} emphasized={steps.length === 0} />
{/* Saved pipelines */}
<div className="px-3 py-2 border-t border-border shrink-0">
<div className="flex items-center justify-between mb-1.5">
+13 -2
View File
@@ -89,7 +89,7 @@ export function HomePage() {
if (!search || search.length < 2) return;
const timer = setTimeout(() => {
import("@/lib/analytics").then(({ track }) => {
track(ANALYTICS_EVENTS.SEARCH, { query: search, results_count: searchResults.length });
track(ANALYTICS_EVENTS.SEARCH, { results_count: searchResults.length });
});
}, 1000);
return () => clearTimeout(timer);
@@ -195,6 +195,14 @@ function HomeSearchBar({
type="text"
value={value}
onChange={(e) => onChange(e.target.value)}
onKeyDown={(e) => {
// Escape steps out of the search field: clear a query if present,
// otherwise drop focus back to the page.
if (e.key === "Escape") {
if (value) onChange("");
e.currentTarget.blur();
}
}}
placeholder={placeholder}
aria-label={placeholder}
className="w-full ps-11 pe-20 py-2.5 rounded-lg border border-border bg-card text-sm text-foreground placeholder:text-muted-foreground/60 focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary/40 transition-shadow"
@@ -244,7 +252,10 @@ function ModalityTabs({
className={cn(
"px-3.5 py-1.5 rounded-full text-sm font-medium transition-colors whitespace-nowrap",
activeTab === tab.key
? "bg-primary text-primary-foreground shadow-sm"
? // Fixed dark text on the orange pill (the orange is the same
// in both themes). White on Otter Orange is only ~3:1, below
// WCAG AA for this 14px label; near-black reaches ~5.8:1.
"bg-primary text-[#1a1814] shadow-sm"
: "text-muted-foreground hover:bg-muted hover:text-foreground",
)}
>
+5 -3
View File
@@ -43,9 +43,11 @@ export function PrivacyPolicyPage() {
<p>
SnapOtter includes basic analytics (tool usage, error reports) to help improve the
software. Your files, file names, and personal data are never part of this. Analytics
can be disabled by rebuilding with{" "}
<code className="text-xs bg-muted px-1 py-0.5 rounded">SNAPOTTER_ANALYTICS=off</code>{" "}
-- everything works normally without it.
is on by default and can be disabled at runtime. An administrator can turn anonymous
product analytics off under Settings {">"} System {">"} Privacy, with no restart or
rebuild needed. For a compile-time hard-off, build with{" "}
<code className="text-xs bg-muted px-1 py-0.5 rounded">SNAPOTTER_ANALYTICS=off</code>,
which strips it from the bundle entirely. Everything works normally without it.
</p>
</section>