feat(rust): mod configuration from the site, and an editor that will not rewrite a float
R18's two tiers: a form generated from a config file's own values, and raw JSON for what a form cannot express. Admin → Rust mod config, one live round trip per action, nothing cached between a browser and a game host's disk. `configEdit.js` is the part that could not be done naively. JavaScript cannot tell `1` from `1.0`, and both mod frameworks deserialize a config into typed C# classes — so a read-modify-write silently rewrites every whole-numbered float as an integer on fields nobody touched, and a plugin that then throws at load does not come back. It never parses, mutates and re-serialises: it records the SOURCE SPAN of every value and splices literals into them, so an untouched `1.0` is still `1.0` and a number an admin types travels as text the whole way (D35/D36). The bridge's own config is editable with `Host`, `Port` and `ServerId` locked, in the form and in the raw tier, because either would cut the link carrying the edit or strand every row this site holds (D38). Credentials render masked with a reveal; the raw tier shows them (D37) and the audit trail never does. `rust_config_writes` records every save including the refused and the rolled back — an operator asking why a setting is not what they set needs to see that somebody tried. Three defects a browser walk found that 179 green tests did not: * every save of the bridge's own config was refused while the page said the opposite — a `<select>` whose value matches no `<option>` shows the first one, so the reload guess `RunicGateway` was on the wire and "nothing" was on the screen; * `btn ghost` is not a class this platform defines (`.btn-ghost` is), so every secondary button in this module has rendered as a primary one since phase 7 — here it made the open file and the active tier indistinguishable; * a save's refusal rendered at the top of a long form, far from the button. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PMH6bw1jXMgbyF3ZWGEzSM
This commit is contained in:
@@ -62,4 +62,25 @@ export const IconKey = () => (
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export default { IconLink, IconKey }
|
||||
/**
|
||||
* Sliders — the admin sidebar's row for the mod-configuration editor.
|
||||
*
|
||||
* Not a gear: core's account row is a gear, and two gears in one sidebar say
|
||||
* "settings" twice without saying whose. Sliders read as values being tuned,
|
||||
* which is exactly what that page does to somebody else's game host.
|
||||
*/
|
||||
export const IconSliders = () => (
|
||||
<Icon>
|
||||
<path d="M4 6h10" />
|
||||
<path d="M18 6h2" />
|
||||
<circle cx="16" cy="6" r="2" />
|
||||
<path d="M4 12h4" />
|
||||
<path d="M12 12h8" />
|
||||
<circle cx="10" cy="12" r="2" />
|
||||
<path d="M4 18h10" />
|
||||
<path d="M18 18h2" />
|
||||
<circle cx="16" cy="18" r="2" />
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export default { IconLink, IconKey, IconSliders }
|
||||
|
||||
Reference in New Issue
Block a user