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:
2026-03-13 23:49:29 +02:00
commit 9dca2bedfa
69 changed files with 17462 additions and 0 deletions

View 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>
);
}