pipeline cards, context menus, presets, settings overhaul

rewrote pipeline as draggable card strip with per-rule config popovers,
added right-click menus to pipeline cards, sidebar tree, and file list,
preset import/export with BRU format support, new rules (hash, swap,
truncate, sanitize, padding, randomize, text editor, folder name,
transliterate), settings dialog with all sections, overlay collision
containment, tooltips on icon buttons, empty pipeline default
This commit is contained in:
2026-03-14 19:04:35 +02:00
parent 9dca2bedfa
commit 6f5b862234
105 changed files with 17257 additions and 1369 deletions

View File

@@ -0,0 +1,65 @@
import { useRuleStore } from "@/stores/ruleStore";
import { Input } from "@/components/ui/input";
import { SegmentedControl } from "@/components/ui/segmented-control";
import type { CaseConfig as CaseConfigType } from "@/types/rules";
const basicModes = [
{ value: "Same", label: "Same" },
{ value: "Upper", label: "UPPER" },
{ value: "Lower", label: "lower" },
{ value: "Title", label: "Title" },
{ value: "Sentence", label: "Sent." },
{ value: "SmartTitle", label: "Smart" },
{ value: "Invert", label: "iNVERT" },
{ value: "Random", label: "rAnD" },
] as const;
const devModes = [
{ value: "CamelCase", label: "camelCase" },
{ value: "PascalCase", label: "Pascal" },
{ value: "SnakeCase", label: "snake_case" },
{ value: "KebabCase", label: "kebab-case" },
{ value: "DotCase", label: "dot.case" },
] as const;
export function CaseConfig({ ruleId }: { ruleId: string }) {
const rule = useRuleStore((s) => s.pipeline.find((r) => r.id === ruleId))?.config as CaseConfigType | undefined;
const updateRule = useRuleStore((s) => s.updateRule);
if (!rule) return null;
const update = (changes: Partial<CaseConfigType>) => updateRule(ruleId, changes);
const devValues = ["CamelCase", "PascalCase", "SnakeCase", "KebabCase", "DotCase"];
const isDevMode = devValues.includes(rule.mode);
return (
<div className="flex flex-col gap-3 text-sm">
<div className="flex flex-col gap-1">
<span className="text-xs text-muted-foreground">Text case</span>
<SegmentedControl
value={isDevMode ? ("" as typeof rule.mode) : rule.mode}
onChange={(m) => update({ mode: m })}
options={basicModes}
size="sm"
/>
</div>
<div className="flex flex-col gap-1">
<span className="text-xs text-muted-foreground">Developer case</span>
<SegmentedControl
value={isDevMode ? rule.mode : ("" as typeof rule.mode)}
onChange={(m) => update({ mode: m })}
options={devModes}
size="sm"
/>
</div>
<label className="flex flex-col gap-1">
<span className="text-xs text-muted-foreground">Exceptions (comma-separated)</span>
<Input
value={rule.exceptions}
onChange={(e) => update({ exceptions: e.target.value })}
placeholder="e.g. USB, HTML, API"
className="h-8 text-xs font-mono"
/>
</label>
</div>
);
}