initial project scaffold
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.
This commit is contained in:
71
ui/src/components/rules/ExtensionTab.tsx
Normal file
71
ui/src/components/rules/ExtensionTab.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user