ink-cartridge 3.7.0 → 3.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -137,9 +137,7 @@ export default function GlobalKeyDisplayBox({ top: initialTop, left }) {
137
137
  React.createElement(Box, { paddingTop: 1 },
138
138
  React.createElement(Text, { dimColor: true }, expandedEntry
139
139
  ? 'Enter: collapse | Esc: close'
140
- : 'Tab: switch focus | Enter: details | Esc: close')),
141
- !expandedEntry && (React.createElement(Box, null,
142
- React.createElement(Text, { dimColor: true }, "After collapsing from detail, press Tab to restore the selection highlight.")))));
140
+ : 'Tab: switch focus | Enter: details | Esc: close'))));
143
141
  }
144
142
  registerComponent(GlobalKeyDisplayBox, {
145
143
  top: 0,
@@ -145,8 +145,32 @@ export function KeyboardProvider({ children }) {
145
145
  const pushOwner = useCallback((owner) => {
146
146
  ownerStackRef.current = [...ownerStackRef.current, owner];
147
147
  }, []);
148
+ // fix: _popOwner used `filter(o => o !== owner)` which deletes EVERY
149
+ // occurrence of owner from the stack. When multiple components inside
150
+ // the same modal/overlay each call `useKeyboard()`, they all push the
151
+ // same owner ID — e.g. GlobalKeyDisplayBox, SelectInput, and
152
+ // useFocusState() each push `modalId`, creating [modalId, modalId, modalId].
153
+ //
154
+ // When a nested component unmounts, its _popOwner cleanup would wipe
155
+ // ALL three entries, emptying the stack while the outer component is
156
+ // still mounted. This makes `getCurrentOwner()` return null, causing:
157
+ // - focusUnregister() to silently no-op (stale focus state remains)
158
+ // - focusCurrent/focusSet/focusNext/focusPrev all blind to the layer
159
+ // - re-mounting components (e.g. SelectInput after collapsing a
160
+ // detail view) unable to restore keyboard focus
161
+ //
162
+ // Switch to `lastIndexOf` + slice so only the LAST occurrence (the
163
+ // caller's own push) is removed — proper LIFO stack behaviour.
164
+ // @2026-06-25 v3.7.0
148
165
  const popOwner = useCallback((owner) => {
149
- ownerStackRef.current = ownerStackRef.current.filter(o => o !== owner);
166
+ const stack = ownerStackRef.current;
167
+ const idx = stack.lastIndexOf(owner);
168
+ if (idx !== -1) {
169
+ ownerStackRef.current = [
170
+ ...stack.slice(0, idx),
171
+ ...stack.slice(idx + 1),
172
+ ];
173
+ }
150
174
  }, []);
151
175
  const enableWildcardPriority = useCallback(() => {
152
176
  wildcardPriorityCountRef.current += 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ink-cartridge",
3
- "version": "3.7.0",
3
+ "version": "3.7.1",
4
4
  "description": "Ready-to-use Ink components and screen management system for building terminal UIs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",