Rust workspace with nomina-core (rename engine) and nomina-app (Tauri v2 shell). React/TypeScript frontend with tabbed rule panels, virtual-scrolled file list, and Zustand state management. All 9 rule types implemented with 25 passing tests.
72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
import { useRuleStore } from "../../stores/ruleStore";
|
|
import type { ExtensionConfig, StepMode } from "../../types/rules";
|
|
|
|
const extModes = ["Same", "Lower", "Upper", "Title", "Extra", "Remove", "Fixed"] as const;
|
|
|
|
export function ExtensionTab() {
|
|
const rule = useRuleStore((s) => s.rules.extension) as ExtensionConfig;
|
|
const updateRule = useRuleStore((s) => s.updateRule);
|
|
|
|
const update = (changes: Partial<ExtensionConfig>) => updateRule("extension", changes);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-3 text-xs">
|
|
<div className="flex gap-3 items-end">
|
|
<label className="flex flex-col gap-1 w-40">
|
|
<span style={{ color: "var(--text-secondary)" }}>Extension mode</span>
|
|
<select
|
|
value={rule.mode}
|
|
onChange={(e) => update({ mode: e.target.value as ExtensionConfig["mode"] })}
|
|
className="px-2 py-1.5 rounded border"
|
|
style={{
|
|
background: "var(--bg-primary)",
|
|
borderColor: "var(--border)",
|
|
color: "var(--text-primary)",
|
|
}}
|
|
>
|
|
{extModes.map((m) => (
|
|
<option key={m} value={m}>{m}</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
|
|
{(rule.mode === "Fixed" || rule.mode === "Extra") && (
|
|
<label className="flex flex-col flex-1 gap-1">
|
|
<span style={{ color: "var(--text-secondary)" }}>
|
|
{rule.mode === "Extra" ? "Extra extension" : "New extension"}
|
|
</span>
|
|
<input
|
|
type="text"
|
|
value={rule.fixed_value}
|
|
onChange={(e) => update({ fixed_value: e.target.value })}
|
|
className="px-2 py-1.5 rounded border"
|
|
style={{
|
|
background: "var(--bg-primary)",
|
|
borderColor: "var(--border)",
|
|
color: "var(--text-primary)",
|
|
}}
|
|
placeholder="e.g. bak, txt..."
|
|
/>
|
|
</label>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
<div className="flex-1" />
|
|
<span style={{ color: "var(--text-secondary)" }}>Mode:</span>
|
|
{(["Simultaneous", "Sequential"] as StepMode[]).map((mode) => (
|
|
<label key={mode} className="flex items-center gap-1 cursor-pointer">
|
|
<input
|
|
type="radio"
|
|
name="ext-mode"
|
|
checked={rule.step_mode === mode}
|
|
onChange={() => update({ step_mode: mode })}
|
|
/>
|
|
<span>{mode}</span>
|
|
</label>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|