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:
82
ui/src/components/rules/ReplaceTab.tsx
Normal file
82
ui/src/components/rules/ReplaceTab.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { useRuleStore } from "../../stores/ruleStore";
|
||||
import type { ReplaceConfig, StepMode } from "../../types/rules";
|
||||
|
||||
export function ReplaceTab() {
|
||||
const rule = useRuleStore((s) => s.rules.replace) as ReplaceConfig;
|
||||
const updateRule = useRuleStore((s) => s.updateRule);
|
||||
|
||||
const update = (changes: Partial<ReplaceConfig>) => updateRule("replace", changes);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 text-xs">
|
||||
<div className="flex gap-3">
|
||||
<label className="flex flex-col flex-1 gap-1">
|
||||
<span style={{ color: "var(--text-secondary)" }}>Find</span>
|
||||
<input
|
||||
type="text"
|
||||
value={rule.search}
|
||||
onChange={(e) => update({ search: e.target.value })}
|
||||
className="px-2 py-1.5 rounded border"
|
||||
style={{
|
||||
background: "var(--bg-primary)",
|
||||
borderColor: "var(--border)",
|
||||
color: "var(--text-primary)",
|
||||
}}
|
||||
placeholder="Text to find..."
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col flex-1 gap-1">
|
||||
<span style={{ color: "var(--text-secondary)" }}>Replace with</span>
|
||||
<input
|
||||
type="text"
|
||||
value={rule.replace_with}
|
||||
onChange={(e) => update({ replace_with: e.target.value })}
|
||||
className="px-2 py-1.5 rounded border"
|
||||
style={{
|
||||
background: "var(--bg-primary)",
|
||||
borderColor: "var(--border)",
|
||||
color: "var(--text-primary)",
|
||||
}}
|
||||
placeholder="Replacement text..."
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<label className="flex items-center gap-1.5 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={rule.match_case}
|
||||
onChange={(e) => update({ match_case: e.target.checked })}
|
||||
/>
|
||||
<span>Match case</span>
|
||||
</label>
|
||||
<label className="flex items-center gap-1.5 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={rule.first_only}
|
||||
onChange={(e) => update({ first_only: e.target.checked })}
|
||||
/>
|
||||
<span>First only</span>
|
||||
</label>
|
||||
|
||||
<div className="flex-1" />
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<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="replace-mode"
|
||||
checked={rule.step_mode === mode}
|
||||
onChange={() => update({ step_mode: mode })}
|
||||
/>
|
||||
<span>{mode}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user