Automerge backed Markdown editor for the Atmosphere
README.md

App dialog primitives #

These React-owned components define reusable app chrome, not content or integration behavior.

  • ModalShell owns the centered overlay, dismissal, focus trap, and focus return.
  • SectionedDialog owns the Settings-style title, vertical section navigation, icon-strip mobile layout, and scrolling content panel. Settings and Feedback supply section definitions and content.
  • focusable provides DOM-ordered keyboard targets and resolves the foreground dialog from its overlay layer; the Settings folder picker and account popup use it without adopting the sectioned layout.
  • ActionButton is the app-wide action control. Settings, the header update action, and edition panels share its neutral and primary styles; placement-specific spacing stays with the consumer.
  • FormControlGroup pairs a single-line input, select, or read-only code value with adjacent ActionButtons. Choose density once on the group; do not size each child separately.

Form sizing #

Use FormControlGroup for a field with utility actions such as Copy or Reset:

<FormControlGroup>
  <SettingTextInput {...fieldProps} />
  <ActionButton onClick={reset}>Reset</ActionButton>
</FormControlGroup>

The default compact density uses --ui-form-control-height (36px). regular uses --ui-touch-target (44px). Coarse-pointer devices promote compact groups to 44px so inputs and buttons remain equally touchable. The 25px --ui-control-height token belongs to dense editor controls, not settings forms.

The field fills available width; actions use their label width plus shared horizontal padding. Use short utility action labels in these inline groups; long actions belong in a separate row. Buttons retain a minimum height rather than a fixed height so wrapping text can grow. Input colors, borders, focus styling, and behavior remain owned by the input. The group normalizes single-line control geometry.

Keep labels, validation messages, and outer spacing outside the group. Put labels above the group when the available form width is narrow (use a container query), and avoid fixed-width action columns or button-specific height overrides. A standalone ActionButton retains its 44px default. Verify paired heights, usable field width, focus, and overflow in a desktop browser and a touch viewport when migrating a form.

The search and command palettes use their own shared ../palette-dialog/PaletteDialog; they have a search-input/list interaction model instead of sections. The Obsidian-compatible Modal in src/plugin/obsidian-stubs.ts is an imperative plugin API. The ATProto account popup remains a separate auth flow. Sharing their layout would conflate different interactions.