Entry form: audit block, every field selectable, sideways scrolling - #11
Merged
Conversation
Read-only createTimestamp/creatorsName/modifyTimestamp/modifiersName rows after the form, separated by a blank line, with timestamps in local time; plus a trailing "…" when the dn title does not fit. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Panel 3 now shows the entry's server-maintained createTimestamp, creatorsName, modifyTimestamp and modifiersName below the form, separated by a blank row and read-only. Entry reads request those operational attributes explicitly (`*` is user attributes only). Timestamps render in local time, captured once at the top of main() because `time` refuses the local offset in a multi-threaded process. The rows are classified by attribute name (`form_model::is_meta_attr`) rather than a per-field flag: they are operational, so they can never be in an objectClass's MUST∪MAY and a field carrying such a label is always a meta row. Every site that treats a field as "an attribute we own" now skips them — most importantly `to_edit_entry`, which feeds `validate()` (leaking them there would fail every save), and the orphan sweep in `sync_schema_fields`, where an orphaned field emits a Delete. Also: a DN too long for the pane title is now cut with a trailing `…`, so a truncated DN is distinguishable from a short one. It already cut at the end — only the marker was missing. Verified live against the podman demo server: block renders, local time matches the server's Z value, an edit saves (LDIF carries only the edited attribute) and the block refreshes with the new modifyTimestamp. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…rom the start Two follow-ups from live review at 90 columns: * The meta labels are now the bare hint (`modified by`, not `modifiersName (modified by)`). They were the longest labels in the form, and the label column is sized to the longest label of ALL rows — so they squeezed every value cell on a narrow pane. * `InputLine::set_value` select-alls, which parks the view at the END of the text: any value wider than its cell showed its tail behind a `◄` marker. For a DN or a path the front is what identifies it, so every text cell is homed as it is filled. This runs on render ticks only, never mid-typing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Read-only fields become navigable, horizontally scrollable and copyable via a new read_only mode in tvision-rs InputLine (focusable, rejects mutations, broadcasts the rejection so the pane can pop a dialog). Part 3 extends the same fix to edaptor's list/launch blocks, where long member DNs are cut today. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…y, refuse Part 2 of the read-only-navigation work. Read-only cells are built `read_only` (the new tvision-rs InputLine mode) instead of `disabled`, and `focusable_value_ids` returns every row. A cell focus skips cannot be scrolled, which is why a value wider than its cell could never be read to its end. The pane answers InputLine::READ_ONLY_REJECTED — the widget names the cell, the pane knows the reason — with a dialog that says which attribute refused and why: server-maintained (the audit block), schema NO-USER-MODIFICATION, read-only session, orphaned by an objectClass change, binary, boolean, or declared read-only in the config. Three existing tests encoded "read-only means unreachable" and were rewritten to the surviving guarantee rather than deleted: the head/tail scroll tests now assert that the leading/trailing rows can be reached and revealed (and that a passive re-anchor does not yank the reveal back), and the clamp test keeps its real subject — no wrap at either end — with all rows as stops. Cargo.toml points at ../rstv until the read-only mode is released upstream. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Part 3 of the read-only-navigation work, and the half that fixes long member DNs: the list and launch blocks painted their lines with put_str, which cuts silently at the right edge, so a value wider than the cell was unreadable even once the block had focus. Both now carry a horizontal offset drawn with put_str_part, with ◄/► marking the hidden text. The read-only launch block moves on ←/→ (it has no caret to follow, and the keys fall back through to the pane once there is nothing left to reveal, so they still leave the field). The inline editor instead follows its caret, the same contract InputLine has — with the "- " bullet kept as left context, so Home lands the line fully home rather than parking the bullet off screen. `place_cursor_home` resets both to the left edge, so focus never lands on a block mid-scroll. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… block The form's manual `home()` after every `set_value` was a workaround for a missing property upstream. With `ValuePosition::Start` set on each cell, the widget lands the value at its head by itself — a DN, a path or a login name is identified by its front, not its tail. Read-only blocks also take Home/End now, jumping straight to either edge instead of stepping there with ←/→. A key that would not move the scroll still falls through to the pane, so Home/End keep their pane-level meaning on a block that already sits at that edge. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The changelog entry still described the audit block as taking no Tab stop, which the every-field-selectable change reversed two commits later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The read-only mode and ValuePosition are released upstream (oetiker/tvision-rs #22, v0.15.0), so the temporary `path = "../rstv"` dependency goes back to a version requirement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Started as "show the entry's timestamps"; the review of that surfaced a deeper
problem — values you cannot reach are values you cannot read — which the rest of
the branch fixes.
Audit block
Below the form, separated by a blank row, the entry's server-maintained
createTimestamp,creatorsName,modifyTimestampandmodifiersName,labelled
created/created by/modified/modified by:Timestamps render in local time (the offset is captured once at the top of
main, becausetimerefuses to read it in a multi-threaded process). Entryreads request these operational attributes explicitly —
*is user attributesonly, which is why they were absent. The save path already re-reads the entry, so
modifiedrefreshes itself.The trap this had to avoid:
EditForm::to_edit_entryfeedsvalidate(), sohad these attributes leaked in, every save would have failed with "attribute
not allowed by objectClass" — and the orphan sweep in
sync_schema_fieldswouldhave emitted a
Deleteagainst a server-maintained attribute on any objectClasschange. Both are covered by tests. Classification is by attribute name
(
form_model::is_meta_attr), not a per-field flag: these are operational, sothey can never be in an objectClass's MUST∪MAY, and one predicate cannot desync
the way a flag threaded through 53 struct literals could.
Every field is selectable
Read-only cells were
disabled, which focus skips entirely — so a value widerthan its cell could never be scrolled to its end, selected, or copied. They are
now
read_only(tvision-rs 0.15.0, developed for this and upstreamed inoetiker/tvision-rs#22): reachable, scrollable, copyable, refusing only edits.
An edit attempt raises
READ_ONLY_REJECTED; the pane answers with a dialog thatnames the attribute and the reason — server-maintained, schema
NO-USER-MODIFICATION, read-only session, orphaned, binary, boolean, or declaredread-only in config.
PageUp/PageDownstill page the viewport, so long formsstay quick to cross.
Three existing tests encoded "read-only means unreachable". They were rewritten
to the guarantee that survived rather than deleted: the head/tail tests now
assert those rows can be reached and revealed (and that a passive re-anchor
does not yank the reveal back), and the clamp test keeps its real subject — no
wrap at either end.
Sideways scrolling
The list and launch blocks painted with
put_str, which cuts silently at theright edge, so a long member DN was unreadable even with focus. Both now carry a
horizontal offset with
◄/►marking hidden text:←/→andHome/Endmove a read-only block, while the inline editor follows its caret.
Text cells use the new
ValuePosition::Start, so a DN shows its head rather than…,dc=example,dc=org.Also
dntitle now ends in…, so a cut DN is distinguishable from ashort one. (It already cut at the end — only the marker was missing; the
…that looked like front-truncation was Create mode's RDN placeholder.)
modifiersName (modified by)would bethe longest label in the form and the label column is sized to the longest
label of all rows — it squeezed every value cell on a narrow pane.
Verification
make checkgreen (fmt, clippy-D warnings, all tests); mdBook builds. Drivenlive against the podman demo server throughout: the block renders with real
values, local time matches the server's
Zvalue, an edit saves with only theedited attribute in the LDIF and the block refreshes, read-only fields scroll and
refuse with the dialog, and member lists scroll sideways.
Design docs:
docs/superpowers/specs/2026-07-28-entry-meta-block-design.mdand…-every-field-selectable-design.md.🤖 Generated with Claude Code