rx-hotkeys 3.1.1 → 4.0.0

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.
@@ -2,7 +2,7 @@ import { describe, it, before, beforeEach, afterEach, mock } from "node:test";
2
2
  import assert from "node:assert";
3
3
  import { Hotkeys, ShortcutTypes } from "./hotkeys.js";
4
4
  import { Keys } from "./keys.js";
5
- import { fromEvent, BehaviorSubject, Observable, EMPTY } from "rxjs";
5
+ import { fromEvent, BehaviorSubject, Observable, EMPTY, firstValueFrom } from "rxjs";
6
6
  import { createMockFn, dispatchKeyEvent } from "./testutils.js";
7
7
  import { JSDOM } from "jsdom";
8
8
  // --- JSDOM and RxJS setup for Node.js tests ---
@@ -79,99 +79,54 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
79
79
  }
80
80
  });
81
81
  // ... (Initialization and Basic Context tests remain the same) ...
82
- describe("Initialization and Context Management", () => {
83
- it("should initialize without errors", () => {
82
+ describe("Initialization and Context Stack Management", () => {
83
+ it("should initialize with a base context (null by default)", () => {
84
84
  assert(keyManager instanceof Hotkeys);
85
- assert.strictEqual(keyManager.getContext(), null); // Default initial context
85
+ assert.strictEqual(keyManager.getActiveContext(), null);
86
86
  });
87
- it("should initialize with a null context by default", () => {
88
- assert.strictEqual(keyManager.getContext(), null);
89
- });
90
- it("should initialize with a given initial context (debug off)", () => {
87
+ it("should initialize with a given initial context", () => {
91
88
  const manager = new Hotkeys("editor", false);
92
- assert.strictEqual(manager.getContext(), "editor");
93
- manager.destroy();
94
- });
95
- it("should log library initialization with context in debug mode", () => {
96
- const consoleLogMock = mock.method(console, "log");
97
- const manager = new Hotkeys("debugInitCtx", true);
98
- const initLog = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`Library initialized. Initial context: "debugInitCtx"`));
99
- assert.ok(initLog, "Library initialization log not found or incorrect.");
89
+ assert.strictEqual(manager.getActiveContext(), "editor");
100
90
  manager.destroy();
101
- consoleLogMock.mock.restore();
102
- });
103
- it("should set and get context correctly", () => {
104
- keyManager.setContext("modal");
105
- assert.strictEqual(keyManager.getContext(), "modal");
106
- keyManager.setContext(null);
107
- assert.strictEqual(keyManager.getContext(), null);
108
- });
109
- it("should log context change correctly when context is different (debug mode on)", () => {
110
- const consoleLogMock = mock.method(console, "log");
111
- keyManager.setDebugMode(true);
112
- // Initial context is null for keyManager
113
- consoleLogMock.mock.resetCalls(); // Clear "Debug mode enabled" log
114
- keyManager.setContext("debug_test");
115
- assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Context changed from "null" to "debug_test"`)), "Log for context change from null incorrect.");
116
- assert.strictEqual(keyManager.getContext(), "debug_test");
117
- consoleLogMock.mock.resetCalls();
118
- keyManager.setContext("another_test");
119
- assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Context changed from "debug_test" to "another_test"`)), "Log for context change between non-null incorrect.");
120
- assert.strictEqual(keyManager.getContext(), "another_test");
121
- consoleLogMock.mock.restore();
122
91
  });
123
- it("should log context change from non-null to null (debug mode on)", () => {
92
+ it("should allow entering a new context, making it active", () => {
93
+ assert.strictEqual(keyManager.getActiveContext(), null);
94
+ keyManager.enterContext("modal");
95
+ assert.strictEqual(keyManager.getActiveContext(), "modal");
96
+ });
97
+ it("should allow leaving a context, restoring the previous one and returning the one that was left", () => {
98
+ keyManager.enterContext("modal");
99
+ keyManager.enterContext("dropdown");
100
+ assert.strictEqual(keyManager.getActiveContext(), "dropdown");
101
+ const leftDropdown = keyManager.leaveContext();
102
+ assert.strictEqual(leftDropdown, "dropdown", `Should return "dropdown"`);
103
+ assert.strictEqual(keyManager.getActiveContext(), "modal");
104
+ const leftModal = keyManager.leaveContext();
105
+ assert.strictEqual(leftModal, "modal", `Should return "modal"`);
106
+ assert.strictEqual(keyManager.getActiveContext(), null);
107
+ });
108
+ it("should not allow leaving the base context and should return undefined", () => {
109
+ assert.strictEqual(keyManager.getActiveContext(), null);
110
+ const leftResult1 = keyManager.leaveContext(); // Attempt to leave
111
+ assert.strictEqual(leftResult1, undefined, "Should return undefined when leaving the base");
112
+ assert.strictEqual(keyManager.getActiveContext(), null);
113
+ keyManager.enterContext("modal");
114
+ keyManager.leaveContext();
115
+ const leftResult2 = keyManager.leaveContext(); // Second attempt
116
+ assert.strictEqual(leftResult2, undefined, "Should return undefined on second attempt");
117
+ assert.strictEqual(keyManager.getActiveContext(), null);
118
+ });
119
+ it("should log context changes in debug mode", () => {
124
120
  const consoleLogMock = mock.method(console, "log");
125
- keyManager.setContext("fromCtx"); // Initial context
126
- keyManager.setDebugMode(true);
121
+ const manager = new Hotkeys("base", true); // Debug on
122
+ consoleLogMock.mock.resetCalls(); // Clear init logs
123
+ manager.enterContext("modal");
124
+ assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Entering context: "modal"`)));
125
+ assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Active context changed to: modal`)));
127
126
  consoleLogMock.mock.resetCalls();
128
- keyManager.setContext(null);
129
- const logCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`Context changed from "fromCtx" to "null"`));
130
- assert.ok(logCall, "Context change to null log not found or incorrect.");
131
- assert.strictEqual(keyManager.getContext(), null);
132
- consoleLogMock.mock.restore();
133
- });
134
- it(`should not call activeContext$.next and log "no change" if context is set to the same value (debug mode on)`, () => {
135
- keyManager.setContext("sameCtx"); // Set initial context
136
- const consoleLogMock = mock.method(console, "log");
137
- keyManager.setDebugMode(true); // Enable debug for this test
138
- consoleLogMock.mock.resetCalls(); // Clear "Debug mode enabled" log
139
- // @ts-ignore: Accessing private member for test
140
- const activeContextNextSpy = mock.method(keyManager["activeContext$"], "next");
141
- keyManager.setContext("sameCtx"); // Attempt to set the same context
142
- const noChangeLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`setContext called with the same context "sameCtx". No change made.`));
143
- assert.ok(noChangeLogCall, "No-change log not found or incorrect for same context.");
144
- const changedLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`Context changed from`));
145
- assert.strictEqual(changedLogCall, undefined, "Context changed log should not appear for same context.");
146
- assert.strictEqual(keyManager.getContext(), "sameCtx");
147
- assert.strictEqual(activeContextNextSpy.mock.callCount(), 0, "activeContext$.next should not have been called.");
148
- activeContextNextSpy.mock.restore();
149
- consoleLogMock.mock.restore();
150
- });
151
- it("should not log or call next if context is set to the same value (debug mode off)", () => {
152
- keyManager.setContext("sameCtxNoDebug"); // Set initial context
153
- keyManager.setDebugMode(false);
154
- // Ensure debug is off
155
- const consoleLogMock = mock.method(console, "log");
156
- // @ts-ignore: Accessing private member for test
157
- const activeContextNextSpy = mock.method(keyManager["activeContext$"], "next");
158
- keyManager.setContext("sameCtxNoDebug"); // Attempt to set the same context
159
- assert.strictEqual(consoleLogMock.mock.callCount(), 0, "Console.log should not have been called with debug mode off.");
160
- assert.strictEqual(activeContextNextSpy.mock.callCount(), 0, "activeContext$.next should not have been called.");
161
- assert.strictEqual(keyManager.getContext(), "sameCtxNoDebug");
162
- activeContextNextSpy.mock.restore();
163
- consoleLogMock.mock.restore();
164
- });
165
- it(`should log "no change" if context is set to the same value (debug mode on)`, () => {
166
- keyManager.setContext("sameCtx"); // Set initial context
167
- const consoleLogMock = mock.method(console, "log");
168
- keyManager.setDebugMode(true);
169
- consoleLogMock.mock.resetCalls(); // Clear previous logs
170
- keyManager.setContext("sameCtx"); // Attempt to set the same context
171
- const noChangeLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes(`setContext called with the same context "sameCtx". No change made.`));
172
- assert.ok(noChangeLogCall, "No-change log not found or incorrect for same context.");
173
- const changedLogCall = consoleLogMock.mock.calls.find(call => call.arguments[0].includes("Context changed from"));
174
- assert.strictEqual(changedLogCall, undefined, "Context changed log should not appear for same context.");
127
+ manager.leaveContext();
128
+ assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Leaving context: "modal"`)));
129
+ assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes(`Active context changed to: base`)));
175
130
  consoleLogMock.mock.restore();
176
131
  });
177
132
  it("should toggle debug mode and log its state", () => {
@@ -183,73 +138,171 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
183
138
  assert.ok(consoleLogMock.mock.calls.some(call => call.arguments[0].includes("Debug mode disabled")));
184
139
  consoleLogMock.mock.restore();
185
140
  });
186
- describe("setContext return value", () => {
187
- it("should return true when context is changed from null to a new value", () => {
188
- assert.strictEqual(keyManager.getContext(), null, "Initial context should be null");
189
- const result = keyManager.setContext("newContext");
190
- assert.strictEqual(result, true, "setContext should return true for a change from null");
191
- assert.strictEqual(keyManager.getContext(), "newContext");
192
- });
193
- it("should return true when context is changed from one value to another", () => {
194
- keyManager.setContext("initialContext"); // Set a non-null initial context
195
- const result = keyManager.setContext("changedContext");
196
- assert.strictEqual(result, true, "setContext should return true for a value-to-value change");
197
- assert.strictEqual(keyManager.getContext(), "changedContext");
198
- });
199
- it("should return false when context is set to the same value (non-null)", () => {
200
- keyManager.setContext("sameContext");
201
- const result = keyManager.setContext("sameContext");
202
- assert.strictEqual(result, false, "setContext should return false for no change (non-null)");
203
- assert.strictEqual(keyManager.getContext(), "sameContext");
204
- });
205
- it("should return true when context is changed from a value to null", () => {
206
- keyManager.setContext("initialContext");
207
- const result = keyManager.setContext(null);
208
- assert.strictEqual(result, true, "setContext should return true for a change to null");
209
- assert.strictEqual(keyManager.getContext(), null);
210
- });
211
- it("should return false when context is set to null and already null", () => {
212
- keyManager.setContext(null); // Ensure context is null
213
- const result = keyManager.setContext(null);
214
- assert.strictEqual(result, false, "setContext should return false for no change (already null)");
215
- assert.strictEqual(keyManager.getContext(), null);
216
- });
217
- });
218
- describe("onContextChange$ observable", () => {
219
- it("should emit initial context to new subscriber", () => {
220
- const manager = new Hotkeys("initial", false);
221
- const spy = createMockFn();
222
- const sub = manager.onContextChange$.subscribe(spy);
223
- assert.strictEqual(spy.calledCount, 1);
224
- assert.strictEqual(spy.lastArgs[0], "initial");
225
- sub.unsubscribe();
141
+ describe("activeContext$ observable", () => {
142
+ it("should emit the initial context to a new subscriber", async () => {
143
+ const manager = new Hotkeys("initial");
144
+ const activeContext = await firstValueFrom(manager.onContextChange$);
145
+ assert.strictEqual(activeContext, "initial");
226
146
  manager.destroy();
227
147
  });
228
- it("should emit when context changes", () => {
148
+ it("should emit when the active context changes via enter/leave", () => {
229
149
  const spy = createMockFn();
230
- const sub = keyManager.onContextChange$.subscribe(spy); // Subscribes, gets initial null
231
- spy.mockClear(); // Clear initial emission
232
- assert.ok(keyManager.setContext("newContext"), "setContext should have returned true for change");
233
- assert.strictEqual(spy.calledCount, 1, "onContextChange$ did not emit on first change");
150
+ const sub = keyManager.onContextChange$.subscribe(spy);
151
+ assert.strictEqual(spy.calledCount, 1, "Did not emit initial context");
152
+ assert.strictEqual(spy.lastArgs[0], null);
153
+ keyManager.enterContext("newContext");
154
+ assert.strictEqual(spy.calledCount, 2, "Did not emit on enterContext");
234
155
  assert.strictEqual(spy.lastArgs[0], "newContext");
235
- assert.ok(keyManager.setContext("anotherContext"), "setContext should have returned true for second change");
236
- assert.strictEqual(spy.calledCount, 2, "onContextChange$ did not emit on second change");
156
+ keyManager.enterContext("anotherContext");
157
+ assert.strictEqual(spy.calledCount, 3, "Did not emit on second enterContext");
237
158
  assert.strictEqual(spy.lastArgs[0], "anotherContext");
238
- assert.ok(keyManager.setContext(null), "setContext should have returned true for change to null");
239
- assert.strictEqual(spy.calledCount, 3, "onContextChange$ did not emit on change to null");
159
+ keyManager.leaveContext();
160
+ assert.strictEqual(spy.calledCount, 4, "Did not emit on leaveContext");
161
+ assert.strictEqual(spy.lastArgs[0], "newContext");
162
+ keyManager.leaveContext();
163
+ assert.strictEqual(spy.calledCount, 5, "Did not emit on final leaveContext");
240
164
  assert.strictEqual(spy.lastArgs[0], null);
241
165
  sub.unsubscribe();
242
166
  });
243
- it("should not emit if context is set to the same value", () => {
244
- keyManager.setContext("testContext");
167
+ it("should not emit if the active context does not change (e.g. attempting to leave base context)", () => {
245
168
  const spy = createMockFn();
246
- const sub = keyManager.onContextChange$.subscribe(spy); // Subscribes, gets "testContext"
169
+ const sub = keyManager.onContextChange$.subscribe(spy); // Emits initial `null`
247
170
  spy.mockClear(); // Clear initial emission
248
- assert.strictEqual(keyManager.setContext("testContext"), false, "setContext should have returned false for no change"); // Set same context
249
- assert.strictEqual(spy.calledCount, 0, "Observable should not emit if context value is the same.");
171
+ keyManager.leaveContext();
172
+ assert.strictEqual(spy.calledCount, 0, "Should not emit when leaving base context");
250
173
  sub.unsubscribe();
251
174
  });
252
175
  });
176
+ describe("Deprecated context methods", () => {
177
+ it("getContext should warn and return active context", () => {
178
+ keyManager.enterContext("test");
179
+ const ctx = keyManager.getContext();
180
+ assert.strictEqual(ctx, "test");
181
+ assert.ok(consoleWarnMock.mock.calls.some(c => c.arguments[0].includes(`"getContext" is deprecated`)));
182
+ });
183
+ });
184
+ });
185
+ describe("Context Management (Stack and Override)", () => {
186
+ describe("Context Stack (enter/leave)", () => {
187
+ it("should enter and leave contexts on the stack correctly", () => {
188
+ assert.strictEqual(keyManager.getActiveContext(), null);
189
+ keyManager.enterContext("modal");
190
+ assert.strictEqual(keyManager.getActiveContext(), "modal");
191
+ keyManager.enterContext("dropdown");
192
+ assert.strictEqual(keyManager.getActiveContext(), "dropdown");
193
+ const left1 = keyManager.leaveContext();
194
+ assert.strictEqual(left1, "dropdown");
195
+ assert.strictEqual(keyManager.getActiveContext(), "modal");
196
+ const left2 = keyManager.leaveContext();
197
+ assert.strictEqual(left2, "modal");
198
+ assert.strictEqual(keyManager.getActiveContext(), null);
199
+ const left3 = keyManager.leaveContext();
200
+ assert.strictEqual(left3, undefined);
201
+ });
202
+ });
203
+ describe("Context Override (setContext/restore)", () => {
204
+ it("setContext should set an override context and return a restore function", () => {
205
+ keyManager.enterContext("parent");
206
+ assert.strictEqual(keyManager.getActiveContext(), "parent");
207
+ const restore = keyManager.setContext("override");
208
+ assert.strictEqual(keyManager.getActiveContext(), "override");
209
+ restore();
210
+ assert.strictEqual(keyManager.getActiveContext(), "parent");
211
+ });
212
+ it("the override context should take precedence over the stack context", () => {
213
+ keyManager.enterContext("parent");
214
+ keyManager.setContext("override");
215
+ // Even if we change the stack, the override should remain active
216
+ keyManager.enterContext("child");
217
+ assert.strictEqual(keyManager.getActiveContext(), "override");
218
+ keyManager.leaveContext(); // leaves "child"
219
+ assert.strictEqual(keyManager.getActiveContext(), "override");
220
+ });
221
+ it("restoring the override should reveal the correct, current stack context", () => {
222
+ keyManager.enterContext("parent");
223
+ const restore = keyManager.setContext("override");
224
+ keyManager.enterContext("child");
225
+ assert.strictEqual(keyManager.getActiveContext(), "override");
226
+ restore();
227
+ assert.strictEqual(keyManager.getActiveContext(), "child", `Should reveal "child" which is top of the stack`);
228
+ });
229
+ it("should distinguish between restoring an override and setting an override to null", () => {
230
+ keyManager.enterContext("parent");
231
+ assert.strictEqual(keyManager.getActiveContext(), "parent");
232
+ // 1. Set an override to "null". The active context should be null.
233
+ const restoreNull = keyManager.setContext(null);
234
+ assert.strictEqual(keyManager.getActiveContext(), null, "Active context should be null due to override.");
235
+ // Leaving the stack context should have no effect on the active context, since the override is active.
236
+ keyManager.leaveContext(); // Tries to leave "parent"
237
+ assert.strictEqual(keyManager.getActiveContext(), null, "Active context should still be null after stack change.");
238
+ // Restore from the null override. Active context should revert to the top of the stack.
239
+ restoreNull();
240
+ assert.strictEqual(keyManager.getActiveContext(), null, `Active context should now be from the stack, which is "null".`);
241
+ // 2. Test restore from a non-null override
242
+ keyManager.enterContext("parent"); // Stack is [null, parent]
243
+ const restoreEditor = keyManager.setContext("editor");
244
+ assert.strictEqual(keyManager.getActiveContext(), "editor");
245
+ restoreEditor();
246
+ assert.strictEqual(keyManager.getActiveContext(), "parent");
247
+ });
248
+ });
249
+ describe("Active Context Logic and Shortcuts", () => {
250
+ it("should trigger shortcuts based on the override context", () => {
251
+ const overrideCb = createMockFn();
252
+ keyManager.addCombination({ id: "o", keys: "o", context: "override" }).subscribe(overrideCb);
253
+ const parentCb = createMockFn();
254
+ keyManager.addCombination({ id: "p", keys: "p", context: "parent" }).subscribe(parentCb);
255
+ keyManager.enterContext("parent");
256
+ keyManager.setContext("override");
257
+ dispatchKeyEvent(document, "p");
258
+ assert.strictEqual(parentCb.calledCount, 0);
259
+ dispatchKeyEvent(document, "o");
260
+ assert.strictEqual(overrideCb.calledCount, 1);
261
+ });
262
+ it("should trigger stack context shortcuts after override is restored", () => {
263
+ const overrideCb = createMockFn();
264
+ keyManager.addCombination({ id: "o", keys: "o", context: "override" }).subscribe(overrideCb);
265
+ const parentCb = createMockFn();
266
+ keyManager.addCombination({ id: "p", keys: "p", context: "parent" }).subscribe(parentCb);
267
+ keyManager.enterContext("parent");
268
+ const restore = keyManager.setContext("override");
269
+ dispatchKeyEvent(document, "p");
270
+ assert.strictEqual(parentCb.calledCount, 0);
271
+ restore(); // Restore context back to "parent"
272
+ dispatchKeyEvent(document, "p");
273
+ assert.strictEqual(parentCb.calledCount, 1);
274
+ dispatchKeyEvent(document, "o");
275
+ assert.strictEqual(overrideCb.calledCount, 0);
276
+ });
277
+ });
278
+ });
279
+ describe("Contextual Shortcut Triggering", () => {
280
+ it("should only trigger shortcut in its active context", () => {
281
+ keyManager.addCombination({ id: "test", keys: "a", context: "editor" }).subscribe(mockCallback);
282
+ // 1. Wrong context
283
+ keyManager.enterContext("modal");
284
+ dispatchKeyEvent(document, "a");
285
+ assert.strictEqual(mockCallback.calledCount, 0, `Should not trigger in "modal" context`);
286
+ // 2. No context (should not trigger)
287
+ keyManager.leaveContext(); // back to "null"
288
+ dispatchKeyEvent(document, "a");
289
+ assert.strictEqual(mockCallback.calledCount, 0, `Should not trigger in "null" context`);
290
+ // 3. Correct context
291
+ keyManager.enterContext("editor");
292
+ dispatchKeyEvent(document, "a");
293
+ assert.strictEqual(mockCallback.calledCount, 1, `Should trigger in "editor" context`);
294
+ });
295
+ it("global shortcut should trigger if no specific context is active", () => {
296
+ keyManager.addCombination({ id: "global", keys: "g" }).subscribe(mockCallback);
297
+ dispatchKeyEvent(document, "g");
298
+ assert.strictEqual(mockCallback.calledCount, 1, `Global shortcut should trigger in "null" context`);
299
+ });
300
+ it("global shortcut should trigger if a different specific context is active", () => {
301
+ keyManager.addCombination({ id: "global", keys: "g" }).subscribe(mockCallback);
302
+ keyManager.enterContext("editor");
303
+ dispatchKeyEvent(document, "g");
304
+ assert.strictEqual(mockCallback.calledCount, 1, "Global shortcut should trigger in any active context");
305
+ });
253
306
  });
254
307
  describe("addCombination", () => {
255
308
  it(`should return an Observable that emits on a simple key combination (e.g., "A")`, () => {
@@ -637,18 +690,20 @@ describe("Hotkeys Library (Node.js Test Runner)", () => {
637
690
  }).subscribe(specificCallback);
638
691
  });
639
692
  it("should only trigger specific context callback when specific context is active", () => {
640
- keyManager.setContext("editor");
693
+ keyManager.enterContext("editor");
641
694
  dispatchKeyEvent(document, Keys.G, "keydown", { ctrlKey: true });
642
695
  assert.strictEqual(specificCallback.calledCount, 1, "Specific callback should have been called");
643
696
  assert.strictEqual(globalCallback.calledCount, 0, "Global callback should NOT have been called");
644
697
  });
645
- it("should only trigger global callback when no specific context is active (or context doesn't match)", () => {
646
- keyManager.setContext(null); // No specific context
698
+ it("should only trigger global callback when no specific context is active", () => {
699
+ // Initial context is null
647
700
  dispatchKeyEvent(document, Keys.G, "keydown", { ctrlKey: true });
648
701
  assert.strictEqual(specificCallback.calledCount, 0, "Specific callback should NOT have been called");
649
702
  assert.strictEqual(globalCallback.calledCount, 1, "Global callback should have been called");
703
+ });
704
+ it("should trigger global callback when a non-matching specific context is active", () => {
650
705
  globalCallback.mockClear();
651
- keyManager.setContext("anotherContext"); // Different specific context
706
+ keyManager.enterContext("anotherContext"); // Different specific context
652
707
  dispatchKeyEvent(document, Keys.G, "keydown", { ctrlKey: true });
653
708
  assert.strictEqual(specificCallback.calledCount, 0, `Specific callback should NOT have been called for "anotherContext"`);
654
709
  assert.strictEqual(globalCallback.calledCount, 1, `Global callback should have been called when in "anotherContext"`);