From af391107b9132ec5b6bd6ac29884b65964cc6154 Mon Sep 17 00:00:00 2001 From: Jason Swartz Date: Mon, 15 Jun 2026 12:31:03 -0700 Subject: [PATCH] docs: document Settings Float release plan Add installation and workflow documentation, release notes, requirements, and project planning notes for the initial Settings Float plugin release. --- .gitignore | 1 + README.md | 126 +++++++++ docs/release-notes/0.1.0.md | 31 +++ .../movable-resizable-settings-prd.md | 196 ++++++++++++++ todo.md | 254 ++++++++++++++++++ 5 files changed, 608 insertions(+) create mode 100644 README.md create mode 100644 docs/release-notes/0.1.0.md create mode 100644 docs/requirements/movable-resizable-settings-prd.md create mode 100644 todo.md diff --git a/.gitignore b/.gitignore index 87aa7a4..5f12d90 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ main.js coverage/ .obsidian/ .vault-test/ +docs/ideation/ .DS_Store .idea/ .vscode/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..cfa6abd --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# Settings Float + +[![Obsidian](https://img.shields.io/badge/Obsidian-1.6.0%2B-7C3AED?logo=obsidian&logoColor=white)](https://obsidian.md) +[![Platform](https://img.shields.io/badge/platform-desktop-4B5563)](#compatibility-notes) +[![Language](https://img.shields.io/badge/language-TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) +[![Package manager](https://img.shields.io/badge/package_manager-bun_1.3.14-000000?logo=bun&logoColor=white)](https://bun.sh/) +[![CI](https://github.com/swartzrock/obsidian-settings-float-plugin/actions/workflows/main.yml/badge.svg)](https://github.com/swartzrock/obsidian-settings-float-plugin/actions/workflows/main.yml) +[![GitHub release](https://img.shields.io/github/v/release/swartzrock/obsidian-settings-float-plugin?include_prereleases&label=release)](https://github.com/swartzrock/obsidian-settings-float-plugin/releases) +[![GitHub release date](https://img.shields.io/github/release-date/swartzrock/obsidian-settings-float-plugin)](https://github.com/swartzrock/obsidian-settings-float-plugin/releases) +[![Last commit](https://img.shields.io/github/last-commit/swartzrock/obsidian-settings-float-plugin)](https://github.com/swartzrock/obsidian-settings-float-plugin/commits/main) +[![Issues](https://img.shields.io/github/issues/swartzrock/obsidian-settings-float-plugin)](https://github.com/swartzrock/obsidian-settings-float-plugin/issues) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) + +Settings Float is an Obsidian desktop plugin that makes the core Settings modal and supported catalog dialogs movable and resizable. It is built for appearance-tuning workflows where you want Settings, theme browsing, or plugin browsing open while still seeing the note or workspace behind it. + +## Demo + + + +## Table of Contents + +- [Installation](#installation) +- [Workflow](#workflow) +- [Settings](#settings) +- [Recovery](#recovery) +- [Development](#development) +- [Compatibility Notes](#compatibility-notes) +- [License](#license) + +## Installation + +Settings Float is not listed in Obsidian's community plugin catalog yet. Install it with [BRAT](https://github.com/TfTHacker/obsidian42-brat), from a GitHub release, or from a local build. + +### Install with BRAT + +BRAT is the easiest way to try unreleased Obsidian plugins and keep them updated from GitHub. + +1. In Obsidian, install and enable [BRAT](https://github.com/TfTHacker/obsidian42-brat) from Community plugins. +2. Open BRAT settings and choose `Add Beta plugin`. +3. Paste this repository URL: + + ```text + https://github.com/swartzrock/obsidian-settings-float-plugin + ``` + +4. Choose `Add Plugin`, then enable `Settings Float` from Obsidian's Community plugins list. + +### Install Manually from a Release + +1. Download the latest release from [GitHub Releases](https://github.com/swartzrock/obsidian-settings-float-plugin/releases). +2. In your vault, create this folder if it does not already exist: + + ```text + .obsidian/plugins/settings-float/ + ``` + +3. Copy these release files into that folder: + + ```text + manifest.json + main.js + styles.css + ``` + +4. Restart Obsidian or reload the app. +5. Enable `Settings Float` from `Settings -> Community plugins -> Installed plugins`. + +### Install from a Local Build + +Use this path when testing changes from source. + +1. Clone the repository and install dependencies: + + ```bash + git clone https://github.com/swartzrock/obsidian-settings-float-plugin.git + cd obsidian-settings-float-plugin + bun install + ``` + +2. Build the plugin: + + ```bash + bun run build + ``` + +3. Copy `manifest.json`, `main.js`, and `styles.css` into your vault's `.obsidian/plugins/settings-float/` folder. +4. Reload Obsidian and enable the plugin. + +## Workflow + +Open Obsidian Settings, move the modal by dragging empty space inside the dialog, then resize from the bottom-right corner. Theme and community plugin browser dialogs can also be moved and resized, which makes it easier to preview themes or inspect plugins without covering the whole workspace. + +The plugin enhances the main Settings modal plus Obsidian's theme and community plugin browser dialogs. Other nested dialogs, such as font pickers, are left to Obsidian's default behavior. + +## Settings + +- `Enable movable dialogs`: lets empty dialog space move supported dialogs. +- `Enable resizable dialogs`: shows the bottom-right resize corner on supported dialogs. +- `Remember window geometry`: restores the last valid position and size in this vault. +- `Disable on narrow windows`: keeps behavior conservative on cramped desktop windows. +- `Disable on mobile`: leaves mobile as a no-op for this release. +- `Reset saved geometry`: clears the persisted position and size. + +## Recovery + +If Settings ends up in an awkward position, use the reset button in the plugin settings tab. Saved geometry is validated and clamped to the visible Obsidian window before it is applied. + +## Development + +1. Install dependencies with `bun install`. +2. Run `bun run dev` for watch mode or `bun run build` for a production bundle. +3. Use `bun run typecheck`, `bun run lint`, and `bun run test` before packaging. +4. Release packaging expects `manifest.json`, `main.js`, and `styles.css`; the publish workflow also creates `settings-float-.zip`. + +## Compatibility Notes + +- Desktop is the intended target; mobile behavior is disabled for 0.1. +- The plugin relies on Obsidian's current Settings and catalog dialog DOM shapes. Selectors are centralized so they can be updated if Obsidian changes those structures. +- Custom themes may affect modal spacing, shadows, or scrollbar placement. The plugin uses isolated classes and Obsidian CSS variables where possible. +- Manual validation in Obsidian is still required before publishing a release. + +## License + +Settings Float is released under the [MIT License](LICENSE). diff --git a/docs/release-notes/0.1.0.md b/docs/release-notes/0.1.0.md new file mode 100644 index 0000000..be201bd --- /dev/null +++ b/docs/release-notes/0.1.0.md @@ -0,0 +1,31 @@ +# Settings Float 0.1.0 Release Notes + +Initial desktop release for moving and resizing the core Obsidian Settings modal. + +## Added + +- Detects and enhances only the main Obsidian Settings modal. +- Detects and enhances Obsidian's theme and community plugin browser dialogs. +- Moves Settings by dragging non-interactive empty space inside the dialog. +- Resizes Settings from a bottom-right corner grip. +- Persists valid Settings geometry per vault and restores it on reopen. +- Clamps geometry to the visible Obsidian window on open, resize, and host-window resize. +- Adds a plugin settings tab for movement, resizing, persistence, narrow-window, mobile, and reset controls. +- Leaves nested child dialogs, such as font pickers, untouched. + +## Compatibility + +- Desktop-only release. +- Mobile behavior is disabled. +- Settings modal selectors are centralized for future Obsidian DOM changes. +- Theme compatibility uses isolated CSS classes and Obsidian CSS variables, but custom themes still need manual validation. + +## Release Artifacts + +- `manifest.json` +- `main.js` +- `styles.css` + +## Manual QA Required + +Before publishing, verify Settings movement, resizing, persistence, nested dialogs, and host-window resize behavior in a real Obsidian vault. diff --git a/docs/requirements/movable-resizable-settings-prd.md b/docs/requirements/movable-resizable-settings-prd.md new file mode 100644 index 0000000..50f5102 --- /dev/null +++ b/docs/requirements/movable-resizable-settings-prd.md @@ -0,0 +1,196 @@ +--- +date: 2026-06-12 +topic: movable-resizable-settings +type: product_requirements +--- + +# Product Requirements: Movable and Resizable Obsidian Settings + +## Summary + +Build an Obsidian community plugin, written in TypeScript, that lets users move and resize the core Settings window and supported catalog dialogs. The primary use case is appearance tuning: users should be able to adjust fonts, themes, CSS snippets, zoom, and similar visual settings while still seeing the note or workspace behind Settings. + +The first release should be a focused utility, not a general modal/window manager. It should enhance the existing Settings modal and selected catalog browser dialogs with safe drag and resize behavior while avoiding private Obsidian API patches wherever possible. + +## Problem + +Obsidian Settings opens as a large centered modal that obscures the active workspace. When changing appearance-related settings, users cannot see the effect of their changes in context. They must close Settings, inspect the workspace, remember what changed, reopen Settings, and navigate back to the relevant section. + +This creates two pains: + +- Slow feedback cycles while tuning visual settings. +- Unnecessary memory burden because the setting value and visual result are separated in time. + +The motivating screenshot shows the Appearance settings page and a nested Text font dialog covering the note beneath, which means the plugin must handle both the main Settings modal and child dialogs carefully. + +## Goals + +- Let users move the Settings window so the active note/workspace remains visible. +- Let users resize the Settings window without breaking Settings layout or controls. +- Let users move and resize theme and community plugin browser dialogs so catalog browsing does not obscure live preview context. +- Remember safe window geometry between Settings opens. +- Provide recovery controls when the modal is moved or resized into an undesirable state. +- Avoid affecting unrelated Obsidian modals unless explicitly added in a later release. +- Keep behavior reversible and easy to disable. + +## Non-Goals + +- Do not recreate Obsidian Settings as a custom pane. +- Do not open Settings in a separate native Electron window. +- Do not patch Obsidian internals or monkey-patch Settings constructors for the first release. +- Do not make every Obsidian modal movable and resizable. +- Do not implement a screenshot comparison workflow. +- Do not replace themes, snippets, or Appearance settings themselves. + +## Target Users + +- Users who frequently customize themes, fonts, CSS snippets, and visual settings. +- Theme builders who need to see live visual effects while changing Settings. +- Plugin developers and power users who often move between Settings and live workspace state. +- Users on laptops or single-monitor setups where the Settings modal consumes most of the available screen. + +## Core User Stories + +- As a user changing my text font, I want to move Settings aside so I can see the current note while selecting fonts. +- As a theme customizer, I want to resize Settings so the Appearance controls remain usable while more of the workspace is visible. +- As a user choosing a theme, I want the theme browser dialog to move aside so I can see the theme applied to my workspace. +- As a user with a preferred layout, I want Settings to reopen in the same place and size next time. +- As a user who moves Settings too far, I want a settings-tab reset action to restore the default centered position. +- As a user interacting with a nested font picker, I want child dialogs to keep working normally. + +## Functional Requirements + +- R1. The plugin enhances the core Obsidian Settings modal when it opens. +- R1a. The plugin enhances Obsidian's theme and community plugin browser dialogs when they open. +- R2. The plugin adds a clear drag affordance to the Settings modal, preferably in the header or an inserted handle strip. +- R3. The plugin allows resizing the Settings modal from at least the bottom-right corner. +- R4. The plugin enforces minimum width and height values so Settings remains usable. +- R5. The plugin clamps the modal to the visible Obsidian window after drag, resize, open, and host window resize. +- R6. The plugin persists Settings modal geometry per vault. +- R7. The plugin validates persisted geometry before applying it. +- R8. The plugin exposes a settings-tab action to reset Settings geometry to Obsidian's default centered layout. +- R9. The plugin does not expose command palette actions for positioning in the first release. +- R10. The plugin can be disabled from its own settings tab. +- R11. The plugin does not apply drag or resize behavior to nested child dialogs by default. +- R12. The plugin does not interfere with form controls, sliders, dropdowns, buttons, search inputs, or nested modal focus. +- R13. The plugin cleans up all DOM additions and event listeners on unload. +- R14. The plugin uses isolated CSS class names to avoid theme conflicts. + +## Settings + +The plugin settings should be minimal for the first release: + +- Enable movable Settings: default on. +- Enable resizable Settings: default on. +- Remember last position and size: default on. +- Reset saved geometry: action button. +- Advanced: disable on mobile or narrow windows: default on. + +## Commands + +No command palette actions ship in version 0.1. + +Optional later commands: + +- Reset Settings window position and size. +- Center Settings. +- Dock Settings left. +- Dock Settings right. +- Toggle movable Settings. +- Narrow Settings. +- Widen Settings. +- Move Settings by keyboard increment. + +## Interaction Requirements + +Dragging: + +- Dragging should only start from the handle/header affordance. +- Dragging should not start from setting rows, text inputs, sliders, dropdowns, or buttons. +- Dragging should maintain pointer capture until release. +- Dragging should clamp the modal to the visible workspace. + +Resizing: + +- Resize should be available from an explicit handle. +- Resize should respect minimum dimensions. +- Resize should clamp maximum dimensions to the visible Obsidian window. +- Resize should not introduce scroll traps in the Settings content area. + +Nested dialogs: + +- Child dialogs, such as font selector dialogs, should remain centered or follow Obsidian's default behavior unless a later release intentionally supports positioning them. +- The plugin should avoid attaching handles to child dialogs. +- Theme and community plugin browser dialogs are explicitly supported exceptions because they block appearance/plugin browsing preview workflows. +- The plugin should preserve Escape, close buttons, focus management, and click-outside behavior. + +## Accessibility Requirements + +- Handles must have accessible labels if they are focusable or visible controls. +- The plugin must not make Settings unreachable by keyboard. +- The reset settings-tab action must clear saved geometry even if Settings is currently offscreen or partially clipped. +- Visual handles must have sufficient contrast across light and dark themes. + +## Compatibility Requirements + +- The plugin should target current Obsidian desktop releases and avoid mobile behavior in the first release unless testing proves it safe. +- The plugin should not rely on undocumented class names more than necessary. +- Any DOM selectors used to identify the Settings modal should be centralized and easy to update. +- The plugin should degrade gracefully if the Settings modal DOM shape changes. +- The plugin should use Obsidian CSS variables where possible. + +## Success Metrics + +- A user can change Appearance font settings while keeping at least part of the active note visible. +- A user can browse themes or community plugins while keeping at least part of the active workspace visible. +- A user can move and resize Settings without breaking Settings controls. +- A user can close and reopen Settings and see their preferred geometry restored. +- A user can recover from bad geometry using the plugin settings reset action. +- The plugin does not apply behavior to nested child dialogs in the motivating font-selection workflow. + +## Acceptance Criteria + +- AC1. Opening Settings with the plugin enabled shows a draggable affordance on the Settings modal. +- AC2. Dragging the affordance moves the Settings modal and does not trigger settings controls. +- AC3. Resizing from the resize handle changes modal dimensions and preserves usable Settings content. +- AC4. Closing and reopening Settings restores the last valid geometry when persistence is enabled. +- AC5. If the Obsidian window is resized, the Settings modal remains visible or is clamped back into view. +- AC6. The reset settings-tab action restores the default centered Settings layout on the next open and immediately recenters the current Settings modal if it is open. +- AC7. The plugin does not add center, dock-left, dock-right, reset, or toggle-movable command palette actions in version 0.1. +- AC8. Opening the Text font dialog from Appearance settings does not add drag/resize handles to that child dialog. +- AC8a. Opening the theme or community plugin browser adds drag/resize behavior to that browser dialog. +- AC9. Disabling the plugin removes its handles/classes and returns Settings behavior to normal after reopening Settings. +- AC10. Unloading the plugin removes event listeners and DOM additions without requiring an Obsidian restart. + +## Release Scope + +### Version 0.1 + +- Detect and enhance the core Settings modal. +- Detect and enhance theme and community plugin browser dialogs. +- Add drag handle. +- Add bottom-right resize handle. +- Persist and validate geometry. +- Add minimal plugin settings. +- Disable or no-op on mobile/narrow windows. +- Document limitations and recovery behavior. + +### Later Versions + +- Keyboard increment move/resize controls. +- Named geometry profiles for laptop and external monitor layouts. +- Optional child-dialog positioning. +- Optional visible preset buttons inside the Settings modal. +- Broader support for selected non-Settings modals if users ask for it. + +## Risks and Open Questions + +- Obsidian may change Settings modal DOM structure, requiring selector updates. +- Themes may style modals in ways that conflict with inserted handles. +- Nested modal behavior may vary across Settings sections and community plugin settings tabs. +- The plugin needs real manual testing inside Obsidian because DOM behavior is difficult to validate with unit tests alone. +- It is unclear whether the Settings modal should persist geometry globally or per vault; per vault is the safer first assumption. + +## Source Ideation + +This requirements document is based on the ideation artifact at `docs/ideation/2026-06-12-movable-resizable-obsidian-settings-ideation.html`. diff --git a/todo.md b/todo.md new file mode 100644 index 0000000..bf5fd9d --- /dev/null +++ b/todo.md @@ -0,0 +1,254 @@ +--- +title: "feat: Add movable and resizable Obsidian Settings" +type: feat +date: 2026-06-12 +origin: docs/requirements/movable-resizable-settings-prd.md +--- + +# TODO: Movable and Resizable Obsidian Settings Plugin + +## Summary + +Build a TypeScript Obsidian community plugin that enhances the core Settings modal and selected catalog browser dialogs with safe move, resize, persistence, and recovery controls. The first release should stay focused on Settings plus theme/community plugin browsing so users can preview appearance changes against the active workspace without closing and reopening dialogs. + +## Scope + +In scope: + +- Scaffold a standard Obsidian plugin project. +- Detect the core Settings modal plus theme/community plugin browser dialogs, and enhance only those supported dialogs. +- Add explicit drag and resize affordances. +- Persist safe geometry per vault through plugin data. +- Clamp modal geometry to the visible Obsidian window. +- Provide a settings-tab reset action for saved Settings geometry. +- Add minimal plugin settings and user-facing documentation. +- Add focused automated tests for pure geometry/state logic and manual QA coverage for Obsidian DOM behavior. + +Out of scope: + +- Recreating Obsidian Settings as a custom pane. +- Opening Settings in a separate native Electron window. +- Patching private Obsidian internals or monkey-patching Settings constructors. +- Making every Obsidian modal movable. +- Handling child-dialog positioning beyond avoiding interference. +- Mobile support beyond safe disable/no-op behavior. + + +## Goal Mode Instructions + +If this file is used with `/goal`, the goal for a single run is NOT to finish this whole file. The goal for a single run is: + +> Complete all unchecked tasks in exactly one phase, report manual test instructions, and stop. + +Stopping after one phase is a successful completion of that run. Do not continue just because there are more unchecked phases below. + +## Rules + +1. Only work on ONE incomplete phase at a time. +2. Before editing code, identify the single phase you are about to complete. +3. Complete all unchecked tasks in the selected phase. +4. Do not inspect or implement later phases except to understand dependencies for the selected phase. +5. When the selected phase is fully coded and verified, mark only that phase's completed tasks as [x]. +6. After finishing the selected phase, STOP immediately and report: +- what changed +- tests/build results +- manual Obsidian test instructions +1. Do not commit immediately after finishing a phase. +2. Wait for the user to manually test in Obsidian. +3. Only create a git commit after the user explicitly says `continue`. +4. When the user says `continue`, first commit the completed phase with a descriptive commit message, then begin exactly one next incomplete phase. +5. Never include unfinished phase work in the commit. +6. If there are still incomplete phases after the selected phase is complete, leave them incomplete and stop anyway. +7. Use bun for package management + + + +## Key Technical Decisions + +- KTD1. Use the official Obsidian sample plugin shape: start from a conventional `src/main.ts`, `manifest.json`, `styles.css`, `package.json`, `tsconfig.json`, and build config so the repo follows community plugin expectations. +- KTD2. Attach behavior through DOM observation and public plugin lifecycle hooks: the Obsidian API exposes DOM utilities and `registerDomEvent`, but not a first-class Settings-window API, so all DOM selectors must be centralized and easy to update. +- KTD3. Store plugin configuration and geometry with `loadData` and `saveData`: Obsidian plugin data is written to `data.json` in the plugin folder, which is the right persistence layer for per-vault geometry. +- KTD4. Treat desktop as the first-class surface: use `Platform` checks to no-op on mobile and narrow windows by default. +- KTD5. Keep generic nested modals unmodified: the motivating font-picker workflow has a child dialog, so the modal detector must identify the main Settings modal and selected catalog browser dialogs while avoiding handles on unrelated child dialogs. +- KTD6. Keep geometry logic pure where possible: bounds, presets, clamping, and persistence validation should live in testable modules rather than inside pointer-event handlers. + +## Output Structure + +```text +. +├── manifest.json +├── package.json +├── package-lock.json +├── tsconfig.json +├── esbuild.config.mjs +├── eslint.config.mts +├── styles.css +├── versions.json +├── README.md +├── src/ +│ ├── main.ts +│ ├── settings.ts +│ ├── settings-tab.ts +│ ├── modal-detector.ts +│ ├── modal-enhancer.ts +│ ├── geometry.ts +│ ├── drag-resize.ts +│ ├── commands.ts +│ └── platform.ts +└── tests/ + ├── geometry.test.ts + ├── settings.test.ts + ├── modal-detector.test.ts + └── drag-resize.test.ts +``` + +## Phase 1: Project Scaffold + +- [x] Create the Obsidian plugin scaffold files: `manifest.json`, `package.json`, `tsconfig.json`, `esbuild.config.mjs`, `eslint.config.mts`, `versions.json`, `styles.css`, and `src/main.ts`. +- [x] Choose the plugin id and display name, keeping them stable for `manifest.json`, release assets, and documentation. +- [x] Configure TypeScript and esbuild to compile `src/main.ts` into root `main.js`. +- [x] Add package scripts for development build, production build, linting, type checking, and tests. +- [x] Add test tooling for TypeScript modules that can run outside Obsidian, such as Vitest with a DOM-capable environment. +- [x] Add `.gitignore` entries for build output, dependencies, editor files, and local vault/test artifacts. +- [x] Add a minimal `README.md` with install, development, and current limitation notes. +- [x] Verify the scaffold can build before feature work begins. + +## Phase 2: Settings Model and Persistence + +- [x] Create `src/settings.ts` with typed plugin settings for movable behavior, resizable behavior, persistence, visible preset controls, and mobile/narrow-window disablement. +- [x] Define a persisted geometry model with `x`, `y`, `width`, `height`, schema version, and last-applied bounds metadata. +- [x] Add defaults that match the PRD: movable on, resizable on, remember geometry on, preset controls on if visible controls ship, and mobile/narrow disablement on. +- [x] Implement loading settings through `loadData`, merging missing fields with defaults. +- [x] Implement saving settings through `saveData`, preserving forward-compatible unknown-free typed data. +- [x] Add geometry reset helpers that clear saved geometry without clearing unrelated plugin settings. +- [x] Add migration handling for future schema changes, even if version 0.1 only has one schema. +- [x] Test default merging, persistence round trips, geometry reset, and invalid saved-data recovery in `tests/settings.test.ts`. + +## Phase 3: Settings Modal Detection and Lifecycle + +- [x] Create `src/modal-detector.ts` to identify the core Settings modal through centralized selector and structure checks. +- [x] Prefer robust signals such as Settings-specific modal structure, title/sidebar/content shape, and exclusion of child dialogs over broad `.modal` matching. +- [x] Identify theme and community plugin browser dialogs through conservative catalog-browser structure checks. +- [x] Use a `MutationObserver` or lifecycle polling strategy to notice when Settings opens without patching Obsidian internals. +- [x] Ensure the detector returns no match for nested child dialogs such as font selectors. +- [x] Ensure the detector returns no match for ordinary plugin modals and non-Settings Obsidian dialogs. +- [x] Create an attach/detach lifecycle that enhances each Settings modal once and cleans up when it closes. +- [x] Handle Obsidian popout windows by using the modal's owning `document` and `window`, not only global `document` and `window`. +- [x] Route detector cleanup through plugin unload. +- [x] Test detector behavior with representative DOM fixtures in `tests/modal-detector.test.ts`. + +## Phase 4: Geometry Engine + +- [x] Create `src/geometry.ts` with pure functions for measuring host bounds, validating saved geometry, clamping rectangles, and applying presets. +- [x] Define minimum modal dimensions that keep Settings usable and record the rationale in code comments or docs. +- [x] Clamp `x` and `y` so at least the full modal, or a defined recovery-safe portion, remains inside the visible Obsidian window. +- [x] Clamp `width` and `height` between minimum dimensions and available host bounds. +- [x] Add preset calculations for center, dock left, dock right, and bottom half if bottom half is kept for 0.1. +- [x] Ensure persisted geometry is ignored when dimensions are non-finite, negative, too small, or incompatible with the current host window. +- [x] Recompute geometry on host window resize. +- [x] Preserve current modal size when centering, unless it no longer fits. +- [x] Keep geometry functions independent from Obsidian imports. +- [x] Test clamping, minimums, presets, bad persisted data, host resize, and edge cases in `tests/geometry.test.ts`. + +## Phase 5: Modal Enhancement Shell + +- [x] Create `src/modal-enhancer.ts` to own one enhanced Settings modal instance. +- [x] Add a plugin-specific class to the Settings modal while enhanced. +- [x] Add a drag handle in the modal header or an inserted handle strip. +- [x] Add a resize handle at the bottom-right corner. +- [x] Apply geometry through inline styles or CSS variables in a way that does not fight Obsidian themes. +- [x] Keep Settings content scrollable after resizing. +- [x] Make enhancement idempotent so reopening or DOM mutation does not duplicate handles. +- [x] Detach handles, classes, inline styles, observers, and event listeners on modal close. +- [x] Expose methods for commands to center, reset, dock left, dock right, and toggle behavior. +- [x] Add integration-shaped tests with DOM fixtures where practical, plus manual QA notes for behavior that requires Obsidian. + +## Phase 6: Drag and Resize Interactions + +- [x] Create `src/drag-resize.ts` for pointer-event state machines shared by drag and resize behavior. +- [x] Start drag only from the drag handle, never from setting rows, inputs, sliders, dropdowns, or buttons. +- [x] Start resize only from the resize handle. +- [x] Use pointer capture during active drag/resize when available. +- [x] Disable text selection or accidental interaction only for the duration of active drag/resize. +- [x] Convert pointer deltas into geometry changes through the pure geometry engine. +- [x] Persist geometry after interaction ends when persistence is enabled. +- [x] Ignore secondary mouse buttons and unsupported touch/mobile cases for 0.1. +- [x] Handle cancel paths such as pointer cancellation, modal close, and plugin unload. +- [x] Test pointer state transitions, ignored targets, persistence callbacks, and cancellation in `tests/drag-resize.test.ts`. + +## Phase 7: Commands, Presets, and Settings UI + +- [x] Create `src/commands.ts` to register reset, center, dock-left, dock-right, and toggle commands from the main plugin. +- [x] Ensure reset works even when Settings is closed by clearing saved geometry and applying default behavior next open. +- [x] Ensure commands gracefully no-op or show a notice when Settings is not currently open and the command only affects a live modal. +- [x] Create `src/settings-tab.ts` with toggles for movable, resizable, persistence, preset controls, and mobile/narrow disablement. +- [x] Add a reset saved geometry button to the settings tab. +- [x] Apply setting changes to the currently open Settings modal without requiring an Obsidian restart where feasible. +- [x] Keep visible preset controls optional; if shipped in 0.1, add them as compact controls that do not crowd the Settings UI. +- [x] Test command registration and settings-state behavior with mocks where practical. + +## Phase 8: Styling, Accessibility, and Theme Compatibility + +- [x] Add isolated CSS classes in `styles.css` for the enhanced modal, drag handle, resize handle, active-drag state, and optional preset controls. +- [x] Use Obsidian CSS variables for colors, borders, backgrounds, shadows, and text contrast. +- [x] Ensure handles are visible in light and dark themes. +- [x] Add accessible labels or titles for visible handles and preset controls. +- [x] Preserve Escape, close button, click-outside, and focus behavior for Settings and child dialogs. +- [x] Avoid layout styles that cause nested modals or Settings content to lose scrollability. +- [x] Manually verify common built-in themes and at least one custom theme before release. + +## Phase 9: Obsidian Manual QA + +- [ ] Install the built plugin into a test vault under `.obsidian/plugins//`. +- [ ] Verify Settings opens normally with the plugin disabled. +- [ ] Verify Settings opens with a drag affordance and resize affordance when the plugin is enabled. +- [ ] Verify Appearance font changes can be previewed with part of the active note visible. +- [ ] Verify dragging does not trigger or block Settings controls. +- [ ] Verify resizing keeps Settings content usable and scrollable. +- [ ] Verify geometry persists after closing and reopening Settings. +- [ ] Verify reset, center, dock-left, and dock-right commands. +- [ ] Verify the Text font child dialog does not receive handles and still closes/focuses correctly. +- [ ] Verify host window resize clamps the modal back into a recoverable visible area. + +## Phase 10: Documentation and Release Readiness + +- [ ] Expand `README.md` with screenshots or GIFs showing the appearance-preview workflow. +- [x] Document commands, settings, recovery behavior, and mobile/narrow-window limitations. +- [x] Document known compatibility risks around Obsidian DOM changes and themes. +- [x] Add release notes for version 0.1. +- [x] Confirm release artifacts are `manifest.json`, `main.js`, and `styles.css`. +- [x] Confirm `versions.json` maps the plugin version to the intended minimum Obsidian version. +- [x] Run final build, lint, type-check, and test verification. +- [x] Manually inspect the release bundle before publishing. + +## Acceptance Coverage Map + +| Acceptance criterion | Primary phases | +|---|---| +| AC1. Settings shows a draggable affordance | Phase 3, Phase 5, Phase 9 | +| AC2. Dragging moves Settings without triggering controls | Phase 6, Phase 8, Phase 9 | +| AC3. Resize changes dimensions while preserving content | Phase 4, Phase 5, Phase 9 | +| AC4. Reopen restores valid geometry | Phase 2, Phase 4, Phase 9 | +| AC5. Host resize clamps modal into view | Phase 4, Phase 5, Phase 9 | +| AC6. Reset restores default centered layout | Phase 2, Phase 7, Phase 9 | +| AC7. Dock presets produce predictable layouts | Phase 4, Phase 7, Phase 9 | +| AC8. Child font dialog does not receive handles | Phase 3, Phase 5, Phase 9 | +| AC9. Disabling plugin restores normal behavior | Phase 5, Phase 7, Phase 9 | +| AC10. Plugin unload cleans up listeners and DOM additions | Phase 3, Phase 5, Phase 6 | + +## Risks and Mitigations + +- Obsidian Settings DOM changes: centralize selectors in `src/modal-detector.ts`, keep behavior fail-closed, and document manual QA after Obsidian updates. +- Theme conflicts: use isolated class names and Obsidian CSS variables in `styles.css`. +- Lost modal geometry: validate persisted data, clamp on every open and resize, and ship a settings-tab reset action in 0.1. +- Nested modal interference: explicitly test child dialogs and keep enhancement scoped to the main Settings modal plus selected catalog browser dialogs. +- Pointer-event regressions: keep drag/resize state isolated in `src/drag-resize.ts` and cover state transitions with tests. +- Limited automated coverage inside Obsidian: test pure modules thoroughly and maintain an explicit manual QA checklist for Obsidian-only behavior. + +## Sources and References + +- `docs/requirements/movable-resizable-settings-prd.md` +- `docs/ideation/2026-06-12-movable-resizable-obsidian-settings-ideation.html` +- Obsidian sample plugin: https://github.com/obsidianmd/obsidian-sample-plugin +- Obsidian API definitions: https://raw.githubusercontent.com/obsidianmd/obsidian-api/master/obsidian.d.ts +- Obsidian forum request: https://forum.obsidian.md/t/resizable-settings-window/36628