Skip to content

feat(auto-properties): description, multi-select, and learn-as-you-go values - #123

Merged
chhoumann merged 5 commits into
masterfrom
chhoumann/59-auto-props-ui
Jun 27, 2026
Merged

feat(auto-properties): description, multi-select, and learn-as-you-go values#123
chhoumann merged 5 commits into
masterfrom
chhoumann/59-auto-props-ui

Conversation

@chhoumann

Copy link
Copy Markdown
Owner

Summary

Makes Auto Properties genuinely useful and pleasant, end to end - both where you
define them (settings) and where you use them (the value prompt).

Closes #59, closes #43, closes #40, and addresses the Auto Properties items in #30.

Issue Underlying problem (reframed) What ships
#59 The value prompt gives no context about what you're filling in An optional per-property description, shown above the choices in the prompt
#43 The choice list must be hand-maintained; it should learn Typing a value not in the list offers Use "x" (one-off) and an explicit Save "x" as a choice (persists) - learns without silently collecting typos
#40 Some properties are multi-valued, but the picker only picked one A per-property Single / Multi type; Multi pre-checks current values and writes a real list
#30 Entry friction: empty placeholder, double-Enter A dedicated prompt with a real placeholder and single-Enter confirm

Why this shape

Each issue title is a proposed solution; the table above restates the problem
behind it. A few deliberate design decisions:

  • A dedicated AutoPropertyValueModal. All four asks live in the value-entry
    experience, which previously reused the generic text prompt and so couldn't
    show a description, learn values, or multi-select. Auto Properties now own that
    UX in one cohesive, self-contained component. editMetaElement routes active
    auto properties to it before the EditMode single/multi split, so the
    single-vs-multi decision lives in exactly one place.
  • type is authoritative, and composes with EditMode instead of fighting it.
    Explicit Single/Multi overrides the global Edit Mode in both directions; an
    entry with no type (data from before this PR) inherits Edit Mode, preserving
    prior behaviour on upgrade. This avoids the "two sources of truth" trap while
    still letting you make one property multi without touching global settings.
  • add a new value to auto properties when the input is not in set list of values #43 learns explicitly, never silently. Auto-persisting every typed value
    would fill the list with typos and casing variants and would give the public
    autoprop() API a hidden write side effect. Instead, persisting is a distinct,
    labelled action shown only when the value is new.
  • Settings editor redesign. The editor is rebuilt as themed property cards
    with native Obsidian icons (replacing the emoji buttons and the
    <br>-in-<table> spacing hack) and uses theme CSS variables, so it renders
    correctly in dark mode (the old hardcoded light colors did not). Adds the
    Description field and Type dropdown.

Scope decisions

Validation

All checks green locally (CI runs test + build + lint):

The design and the implementation diff each went through an adversarial review on
the opposite model (3 design reviewers, 2 implementation reviewers). Their
high/medium findings were folded in - notably the nested-array fix, the
cancel-vs-no-match null disambiguation, authoritative type semantics,
order-preserving multi options, and appending learned choices to the live
settings entry so a concurrent settings-tab edit isn't overwritten.

Note: dev:screenshot in the isolated E2E harness returns a frozen frame for
modal overlays (the same limitation affected the pre-existing settings UI), so
visual proof here is DOM-structure assertions + the E2E suite rather than
pixel screenshots.

Migration / compatibility

  • AutoProperty gains optional description? and type? - existing data.json
    upgrades with no migration code (missing type inherits Edit Mode; missing
    description shows nothing).
  • The public autoprop() API now returns string | string[] | null (a Multi
    property returns an array; it returned only strings before). README updated.

Add optional `description` (issue #59) and `type: Single | Multi` (issue #40)
to AutoProperty. Both are optional so existing data.json upgrades cleanly: a
missing type is treated as Single, a missing description shows nothing.

Introduce src/autoProperties.ts with pure, Obsidian-free helpers (multi-mode
resolution that unions the per-property type with the global EditMode, choice
normalization, current-value merging, and new-choice detection) plus full unit
coverage, so the feature logic is testable in the jsdom-free node suite.
Rebuild the Auto Properties settings editor as themed property cards. Adds a
Description field (issue #59) and a Single/Multi type dropdown (issue #40);
existing entries with no type render as Single. Replaces the emoji buttons and
the <br>-in-<table> spacing hack with native Obsidian clickable icons
(setIcon), uses theme CSS variables so it works in dark mode (the old hardcoded
light colors did not), and adds an empty state.
…-select, and learn-as-you-go

Route Auto Property edits through a new AutoPropertyValueModal instead of the
generic prompt, so Auto Properties own their value-entry UX end to end:

- show the property description above the choices (#59)
- a Multi auto property writes a real list and pre-checks current values; the
  per-property Multi type composes with the global EditMode via OR, so it works
  even under All Single (#40)
- typing a value that is not a choice offers 'Use "x"' (one-off) and an
  explicit 'Save "x" as a choice' that persists it, so the list learns new
  values without silently collecting typos (#43)
- a real placeholder and single-Enter confirm remove the old double-Enter and
  empty-placeholder friction (#30)

editMetaElement intercepts active auto properties before the EditMode split so
the single/multi decision lives in one place. handleAutoProperties now returns
string | string[] | null and resolves null on cancel instead of rejecting.
Covered by live Obsidian E2E tests for each behaviour plus back-compat with
type-less entries.
Note the new description and Single/Multi options in the feature list, and that
autoprop now returns string | string[] (Multi) or null.
Fold in opposing-model review findings:

- addYamlProp no longer re-wraps an already-array value under global Multi mode,
  which previously produced a nested [[...]] list when creating a new Multi
  auto property (regression-tested live).
- createNewProperty and editTag check for an active auto property up front, so
  cancelling the value prompt aborts instead of falling through to a second
  generic prompt (null no longer conflates cancel with no-auto-property).
- type is now authoritative: explicit Single/Multi overrides the global EditMode
  in both directions; a type-less (pre-existing) entry still inherits EditMode.
- multiSelectOptions lists current values first so editing preserves the user's
  existing list order and appends new selections instead of reshuffling.
- persistAutoPropertyChoices appends to the live settings entry, so a concurrent
  settings-tab edit is not overwritten by a stale snapshot.
- saving a learned choice is awaited and guarded, removing a fire-and-forget
  unhandled rejection.
- splitMultiValue now delegates to the shared toValueArray helper.

All unit + live Obsidian E2E tests green.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ed7e9c933

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/metaController.ts
Comment on lines +45 to +47
if (!Array.isArray(propValue) &&
(settings.EditMode.mode === EditMode.AllMulti ||
(settings.EditMode.mode === EditMode.SomeMulti && settings.EditMode.properties.contains(propName)))) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor Single auto properties when adding YAML fields

When adding a new YAML property, this branch still wraps every non-array value in a list under All Multi or matching Some Multi, even if the value just came from an Auto Property explicitly configured as type: "Single". In that scenario, creating a new status auto property under global All Multi writes status:\n - done instead of the scalar status: done, while editing an existing auto property correctly lets the per-property Single type override the global mode. The wrapping needs to skip explicit Single auto properties here too.

Useful? React with 👍 / 👎.

@chhoumann
chhoumann force-pushed the chhoumann/59-auto-props-ui branch from 5ed7e9c to 8b7c6e4 Compare June 27, 2026 17:26
@chhoumann
chhoumann merged commit 4b24296 into master Jun 27, 2026
7 checks passed
@chhoumann
chhoumann deleted the chhoumann/59-auto-props-ui branch June 27, 2026 17:28
github-actions Bot pushed a commit that referenced this pull request Jul 1, 2026
# [1.9.0](1.8.4...1.9.0) (2026-07-01)

### Bug Fixes

* address review findings from PRs [#119](https://github.com/chhoumann/MetaEdit/issues/119)-[#128](https://github.com/chhoumann/MetaEdit/issues/128) ([adf0b57](adf0b57))
* **api:** preserve reserved keys when cloning values ([#162](#162)) ([cff6076](cff6076))
* **automators:** count only [x]/[X] tasks complete; null-guard Kanban lane display ([#145](#145)) ([21da53a](21da53a))
* **bulk:** reject __proto__/constructor as a bulk property key ([#148](#148)) ([f03eacc](f03eacc))
* **bulk:** serialize bulk frontmatter writes through the controller write queue ([#147](#147)) ([3eb8236](3eb8236))
* **controller:** guard __proto__/constructor in all frontmatter writes ([#159](#159)) ([9d99fae](9d99fae))
* **controller:** make inline-field writes fence-aware ([5f8e713](5f8e713))
* **core,api:** safe command no-op, block-list deletion, presence-based property lookups ([#143](#143)) ([1702d08](1702d08))
* edit YAML lists (incl. tags) as native lists instead of collapsing them to a string ([#94](#94)) ([#128](#128)) ([79f6ee0](79f6ee0)), closes [#51](#51) [#36](#36)
* harden frontmatter metadata writes ([add2e90](add2e90))
* Kanban helper syncs only the card's leading link, not trailing date/reference links ([#126](#126)) ([52aec1e](52aec1e))
* **kanban:** don't write to an ambiguous same-named note ([#158](#158)) ([7f9655b](7f9655b))
* **kanban:** single notice for a card missing the board property ([9b1c88a](9b1c88a))
* parse inline fields behind list/quote markers and brackets ([#122](#122)) ([a62ef21](a62ef21)), closes [#119](#119) [#18](#18) [#121](#121) [#78](#78) [#84](#84)
* **parser:** preserve [[wikilinks]] in multi-value inline edits ([c331adb](c331adb))
* **settings:** serialize Auto Property choice persistence to prevent lost updates ([#154](#154)) ([2066b3e](2066b3e))
* **settings:** settings tab no longer clobbers concurrently-added Auto Property choices ([5759534](5759534))
* **settings:** validate auto-properties before queueing and make rollbacks compare-and-restore ([#156](#156)) ([8cbacdc](8cbacdc)), closes [#154](#154)
* stop inline-field updates appending a stray bracket ([#127](#127)) ([d2617f5](d2617f5)), closes [#67](#67) [#121](#121)
* **suggester:** center row action icons flush-right ([#166](#166)) ([c00795e](c00795e))
* **suggester:** use native icons + tooltips for row actions ([70903cc](70903cc))
* **suggester:** well-formed duplicate Notice, robust modal close, cleaner name suggestions ([#144](#144)) ([915dd6b](915dd6b)), closes [#143](#143)
* **tags:** correct and clarify tag editing ([#142](#142)) ([b837899](b837899)), closes [#49](#49)
* tolerate malformed YAML frontmatter when parsing note metadata ([#132](#132)) ([b835f97](b835f97)), closes [#130](#130)

### Features

* adopt modern Obsidian APIs and raise baseline ([5131664](5131664))
* **api:** append Dataview field instances without replacing ([42c78a8](42c78a8)), closes [#91](#91)
* **api:** expand public integration surface ([#120](#120)) ([bbf0a7a](bbf0a7a))
* assist property-value entry with autocomplete and a native date picker ([#125](#125)) ([cd86235](cd86235)), closes [#61](#61) [#74](#74) [#61](#61) [#61](#61)
* **auto-properties:** description, multi-select, and learn-as-you-go values ([#123](#123)) ([4b24296](4b24296)), closes [#59](#59) [#40](#40) [#59](#59) [#40](#40) [#59](#59) [#40](#40) [#43](#43) [#30](#30)
* **auto-properties:** paste a list to split into choices ([#139](#139)) ([0245f7a](0245f7a))
* bulk edit metadata across folders and selected notes ([#124](#124)) ([c8d91e2](c8d91e2)), closes [#64](#64) [#20](#20)
* **create:** fluid, type-aware native YAML property creation ([#170](#170)) ([59b4c5d](59b4c5d))
* **editor:** edit properties with Obsidian's native widgets; raise minAppVersion to 1.12.7 ([#168](#168)) ([73b37e0](73b37e0))
* hide file tags from the Edit Meta menu ([#46](#46), [#90](#90)) ([#131](#131)) ([2c07937](2c07937)), closes [#tag](https://github.com/chhoumann/MetaEdit/issues/tag) [#tags](https://github.com/chhoumann/MetaEdit/issues/tags)
* support nested YAML path editing ([e9fce98](e9fce98))
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.9.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

1 participant