Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/MeshWeaver.Documentation/Data/GUI/DataBinding.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ Mechanics (so you know what's load-bearing):
- Field-pointer resolution against the node is **case-insensitive**, so a metadata DTO's PascalCase pointer (`Name`, `Description`) and a content editor's camelCase pointer (`harness`, `messageContent`) both bind without the layout area knowing the JSON casing.
- **Edit-state stays in `/data`.** The click-to-edit toggle (`editState_…`) is transient view state, not node content — it is never written to the node. Only field VALUES are node-bound.
- A few read-only display controls (the `[Dimension]` / options / formatted-date toggle *labels*) derive their text from the layout-area `/data` stream rather than a value pointer. When you node-bind a toggleable form, keep `/data/{dataId}` as a **one-way live projection** of the node content (`GetMeshNodeStream(path).Select(n => n.Content).Subscribe(c => host.UpdateData(dataId, c))`) so those labels stay correct. This is a pure read mirror — it follows the node, has no save loop, and never writes back, so it is NOT the forbidden replicate-then-save pattern.
- **🚨 Editability is the PRODUCING AREA's decision, and it is part of the structure.** A node-bound field writes as the viewer, and the owning hub refuses a viewer without `Update` on the node. So the area that emits the control resolves `hub.GetEffectivePermissions(path)` and sets `Readonly = !canUpdate` (or `CanEdit` on the editors that carry it) — never hands a public visitor a box that invites typing its write can only refuse. The course wish books are the reference shape (`AgenticPrimer/WishBook` → `WishBookAreas.Write`, Education#363). **A write that is refused anyway** (grants changed while the page was open, or an area that forgot the check) reaches the view as an `UnauthorizedAccessException`; `BlazorView.UpdatePointer` classifies it as a REFUSAL, not a fault: the person sees the localized `ui.fieldWriteRefused` sentence and the log carries a Warning, instead of the raw English exception in the error modal and an Error line the incident filer turns into a ticket (#5600).

## Anti-patterns — never do these

Expand Down
1 change: 1 addition & 0 deletions src/MeshWeaver.Messaging.Hub/Localization/strings.de.json
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@
"ui.recycleFailed": "Der Hub unter '{0}' hat nach dem angeforderten Neustart noch nicht wieder geantwortet: {1}",
"ui.mdFillFieldsGeneratePlan": "*Felder ausfüllen und auf **Plan erzeugen** klicken.*",
"ui.accessDeniedPlain": "Zugriff verweigert.",
"ui.fieldWriteRefused": "Nicht gespeichert: Sie dürfen diese Seite lesen, aber nicht ändern.",
"ui.accessDeniedAdminsOnly": "Zugriff verweigert — nur für Plattform-Administratoren.",
"ui.autoSaved": "Änderungen werden automatisch gespeichert",
"ui.connectRepoFirst": "Zuerst im Tab **GitHub Sync** ein GitHub-Repository verbinden.",
Expand Down
1 change: 1 addition & 0 deletions src/MeshWeaver.Messaging.Hub/Localization/strings.en.json
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@
"ui.recycleFailed": "The hub at '{0}' has not answered again yet after the recycle was requested: {1}",
"ui.mdFillFieldsGeneratePlan": "*Fill in the fields and click **Generate plan**.*",
"ui.accessDeniedPlain": "Access denied.",
"ui.fieldWriteRefused": "Not saved: you can read this page, but you may not change it.",
"ui.accessDeniedAdminsOnly": "Access denied — platform admins only.",
"ui.autoSaved": "Changes are saved automatically",
"ui.connectRepoFirst": "Connect a GitHub repository in the **GitHub Sync** tab first.",
Expand Down
Loading