Skip to content

PadIO: context_modes resolution (context token to mode) - #15

Merged
vgreg merged 1 commit into
mainfrom
9-context-modes
Aug 1, 2026
Merged

PadIO: context_modes resolution (context token to mode)#15
vgreg merged 1 commit into
mainfrom
9-context-modes

Conversation

@vgreg

@vgreg vgreg commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Implements #9. Builds on #8.

Maps the context token published by ContextObserver to a mode, per profile. All policy lives in PadIO so the bridge (#12) stays dumb and just reports a raw process name.

Config format

New optional key on ProfileConfig:

"ghostty": {
  "apps": ["com.mitchellh.ghostty"],
  "default_mode": "herdr-nav",
  "context_modes": { "claude": "agent", "nvim": "vim", "zsh": "shell" },
  "modes": { "herdr-nav": {}, "agent": {}, "vim": {}, "shell": {} }
}

context_modes is [String: String] (token → mode), decoded with decodeIfPresent(...) ?? [:], so every existing config keeps parsing unchanged. Matching is exact on the token.

Resolution rules (applyContext)

  1. Fires only on token changeContextObserver publishes only when the token actually changes, so an unchanged token never re-applies a mode.
  2. No match leaves the mode alone — a token with no context_modes entry (or a nil token) keeps the active mode; it does not fall back to default_mode. This is what makes many app-specific modes safe.
  3. Manual override sticks — because we only act on token changes, a hand-picked mode survives until the token changes.
  4. Unknown mode name is logged and ignored, mirroring the existing .setMode handling.
  5. Only the active profile is affected.

Profile-switch interaction

refreshActiveProfile now restores the context-implied mode when a profile becomes active again (alt-tab away from Ghostty and back), instead of snapping to default_mode. When it applies a context mode it sets both activeModeName and profileModes[name], so it no longer widens the pre-existing gap where only activeModeName was set.

Verified

  • Builds clean via BuildProject.
  • Behaviour with no context_modes anywhere is unchanged (empty dict default).

Runtime check (writing claude / nvim / an unmapped token to the context file and watching the mode HUD) is easiest to do end-to-end once the bridge in #12 exists, or by hand with printf ... > tmp && mv tmp ~/.config/padio/context.

🤖 Generated with Claude Code

Map the external context token from ContextObserver to a mode, per
profile. Policy lives here so the bridge stays dumb: ProfileConfig gains
an optional context_modes ([String: String], token -> mode name),
decoded with decodeIfPresent ?? [:] so existing configs keep parsing.

Resolution rules, all deliberate:
- fires only on token change (ContextObserver publishes only on change)
- no match (or nil token) leaves the mode alone, never falls back to
  default_mode
- a mode picked by hand sticks until the token changes
- an unknown mode name is logged and ignored, mirroring setMode
- only the active profile is affected

refreshActiveProfile now restores the context-implied mode on return to
a profile instead of snapping to default_mode, and sets both
activeModeName and profileModes[name] to keep them consistent.

Closes #9

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vgreg
vgreg merged commit 8b33c93 into main Aug 1, 2026
@vgreg
vgreg deleted the 9-context-modes branch August 1, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant