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
81 lines
3.6 KiB
TypeScript
81 lines
3.6 KiB
TypeScript
import { useRuleStore } from "@/stores/ruleStore";
|
|
import { Input } from "@/components/ui/input";
|
|
import { NumberInput } from "@/components/ui/number-input";
|
|
import { SegmentedControl } from "@/components/ui/segmented-control";
|
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
import type { NumberingConfig as NumberingConfigType } from "@/types/rules";
|
|
|
|
const numberModes = [
|
|
{ value: "None", label: "None" },
|
|
{ value: "Prefix", label: "Prefix" },
|
|
{ value: "Suffix", label: "Suffix" },
|
|
{ value: "Both", label: "Both" },
|
|
{ value: "Insert", label: "Insert" },
|
|
] as const;
|
|
|
|
const bases = [
|
|
{ value: "Decimal", label: "Dec" },
|
|
{ value: "Hex", label: "Hex" },
|
|
{ value: "Octal", label: "Oct" },
|
|
{ value: "Binary", label: "Bin" },
|
|
{ value: "Alpha", label: "Alpha" },
|
|
{ value: "Roman", label: "Roman" },
|
|
] as const;
|
|
|
|
export function NumberingConfig({ ruleId }: { ruleId: string }) {
|
|
const rule = useRuleStore((s) => s.pipeline.find((r) => r.id === ruleId))?.config as NumberingConfigType | undefined;
|
|
const updateRule = useRuleStore((s) => s.updateRule);
|
|
if (!rule) return null;
|
|
const update = (changes: Partial<NumberingConfigType>) => updateRule(ruleId, changes);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-3 text-sm">
|
|
<div className="flex flex-col gap-1">
|
|
<span className="text-xs text-muted-foreground">Position</span>
|
|
<SegmentedControl value={rule.mode} onChange={(m) => update({ mode: m })} options={numberModes} />
|
|
</div>
|
|
<div className="grid grid-cols-4 gap-2">
|
|
<label className="flex flex-col gap-1">
|
|
<span className="text-xs text-muted-foreground">Start</span>
|
|
<NumberInput value={rule.start} onChange={(v) => update({ start: v })} />
|
|
</label>
|
|
<label className="flex flex-col gap-1">
|
|
<span className="text-xs text-muted-foreground">Step</span>
|
|
<NumberInput value={rule.increment} onChange={(v) => update({ increment: v })} min={1} />
|
|
</label>
|
|
<label className="flex flex-col gap-1">
|
|
<span className="text-xs text-muted-foreground">Padding</span>
|
|
<NumberInput value={rule.padding} onChange={(v) => update({ padding: v })} min={1} />
|
|
</label>
|
|
<label className="flex flex-col gap-1">
|
|
<span className="text-xs text-muted-foreground">Separator</span>
|
|
<Input value={rule.separator} onChange={(e) => update({ separator: e.target.value })} className="h-8 text-xs font-mono" />
|
|
</label>
|
|
</div>
|
|
<div className="flex flex-col gap-1">
|
|
<span className="text-xs text-muted-foreground">Base</span>
|
|
<SegmentedControl value={rule.base} onChange={(b) => update({ base: b })} options={bases} size="sm" />
|
|
</div>
|
|
<label className="flex flex-col gap-1">
|
|
<span className="text-xs text-muted-foreground">Custom format (overrides base)</span>
|
|
<Input
|
|
value={rule.custom_format || ""}
|
|
onChange={(e) => update({ custom_format: e.target.value || null })}
|
|
placeholder="e.g. {n:03}"
|
|
className="h-8 text-xs font-mono"
|
|
/>
|
|
</label>
|
|
<div className="flex items-center gap-4 flex-wrap">
|
|
<label className="flex items-center gap-1.5 cursor-pointer text-xs">
|
|
<Checkbox checked={rule.per_folder} onCheckedChange={(c) => update({ per_folder: !!c })} />
|
|
Per folder
|
|
</label>
|
|
<label className="flex items-center gap-1.5 cursor-pointer text-xs">
|
|
<Checkbox checked={rule.reverse} onCheckedChange={(c) => update({ reverse: !!c })} />
|
|
Reverse order
|
|
</label>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|