@nick-skriabin/glyph 0.1.37 → 0.1.38

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.
package/dist/index.cjs CHANGED
@@ -2076,6 +2076,18 @@ function render(element, opts = {}) {
2076
2076
  }
2077
2077
  }
2078
2078
  return result;
2079
+ },
2080
+ getActiveElements() {
2081
+ const activeIds = getActiveFocusableIds();
2082
+ const result = [];
2083
+ for (const id of activeIds) {
2084
+ if (skippableIds.has(id)) continue;
2085
+ const node = focusRegistry.get(id);
2086
+ if (node) {
2087
+ result.push({ id, node });
2088
+ }
2089
+ }
2090
+ return result;
2079
2091
  }
2080
2092
  };
2081
2093
  const layoutSubscriptions = /* @__PURE__ */ new Map();
@@ -4347,7 +4359,6 @@ function DialogOverlay({ dialog, onDismiss }) {
4347
4359
  )
4348
4360
  );
4349
4361
  }
4350
- var JumpNavContext = React15.createContext(null);
4351
4362
  function generateHints(count, chars) {
4352
4363
  const hints = [];
4353
4364
  const charList = chars.split("");
@@ -4378,25 +4389,13 @@ function JumpNav({
4378
4389
  };
4379
4390
  const [isActive, setIsActive] = React15.useState(false);
4380
4391
  const [inputBuffer, setInputBuffer] = React15.useState("");
4381
- const [hasChildJumpNav, setHasChildJumpNav] = React15.useState(false);
4382
4392
  const [elements, setElements] = React15.useState([]);
4383
4393
  const inputCtx = React15.useContext(InputContext);
4384
4394
  const focusCtx = React15.useContext(FocusContext);
4385
4395
  const layoutCtx = React15.useContext(LayoutContext);
4386
- const parentJumpNav = React15.useContext(JumpNavContext);
4387
- const wrapperRef = React15.useRef(null);
4388
4396
  React15.useEffect(() => {
4389
- if (parentJumpNav) {
4390
- return parentJumpNav.registerChildJumpNav();
4391
- }
4392
- }, [parentJumpNav]);
4393
- const contextValue = React15.useMemo(() => ({
4394
- isChildActive: hasChildJumpNav,
4395
- registerChildJumpNav: () => {
4396
- setHasChildJumpNav(true);
4397
- return () => setHasChildJumpNav(false);
4398
- }
4399
- }), [hasChildJumpNav]);
4397
+ log("Mounted, inputCtx:", !!inputCtx, "focusCtx:", !!focusCtx, "enabled:", enabled);
4398
+ }, []);
4400
4399
  const parseKey = React15.useCallback((keyStr) => {
4401
4400
  const parts = keyStr.toLowerCase().split("+");
4402
4401
  return {
@@ -4408,45 +4407,34 @@ function JumpNav({
4408
4407
  };
4409
4408
  }, []);
4410
4409
  const activationKeyParsed = parseKey(activationKey);
4411
- const findFocusableDescendants = React15.useCallback((node) => {
4412
- const result = [];
4413
- function walk(n) {
4414
- if (n.focusId) {
4415
- result.push({
4416
- id: n.focusId,
4417
- node: n,
4418
- layout: layoutCtx?.getLayout(n) ?? n.layout
4419
- });
4420
- }
4421
- for (const child of n.children) {
4422
- walk(child);
4423
- }
4424
- }
4425
- walk(node);
4426
- return result;
4427
- }, [layoutCtx]);
4428
4410
  const refreshElements = React15.useCallback(() => {
4429
- if (!wrapperRef.current) {
4430
- log("refreshElements: no wrapper ref");
4411
+ if (!focusCtx?.getActiveElements) {
4412
+ log("refreshElements: no getActiveElements");
4431
4413
  return;
4432
4414
  }
4433
- const descendants = findFocusableDescendants(wrapperRef.current);
4434
- log("Found", descendants.length, "focusable elements");
4435
- descendants.sort((a, b) => {
4415
+ const active = focusCtx.getActiveElements();
4416
+ log("getActiveElements returned", active.length, "elements");
4417
+ const mapped = active.map(({ id, node }) => ({
4418
+ id,
4419
+ node,
4420
+ layout: layoutCtx?.getLayout(node) ?? node.layout
4421
+ }));
4422
+ mapped.sort((a, b) => {
4436
4423
  if (a.layout.y !== b.layout.y) {
4437
4424
  return a.layout.y - b.layout.y;
4438
4425
  }
4439
4426
  return a.layout.x - b.layout.x;
4440
4427
  });
4441
- setElements(descendants);
4442
- }, [findFocusableDescendants, log]);
4428
+ setElements(mapped);
4429
+ }, [focusCtx, layoutCtx, log]);
4443
4430
  const wasActiveRef = React15.useRef(false);
4444
4431
  React15.useEffect(() => {
4445
4432
  if (isActive && !wasActiveRef.current) {
4433
+ log("Activated! Refreshing elements...");
4446
4434
  refreshElements();
4447
4435
  }
4448
4436
  wasActiveRef.current = isActive;
4449
- }, [isActive, refreshElements]);
4437
+ }, [isActive, refreshElements, log]);
4450
4438
  const visibleElements = elements.filter(
4451
4439
  (el) => el.layout.width > 0 && el.layout.height > 0
4452
4440
  );
@@ -4460,19 +4448,19 @@ function JumpNav({
4460
4448
  });
4461
4449
  return map;
4462
4450
  }, [visibleElements, visibleHints]);
4463
- React15.useEffect(() => {
4464
- log("Mounted, inputCtx:", !!inputCtx, "enabled:", enabled, "activationKey:", activationKey);
4465
- log("Parsed key:", activationKeyParsed);
4466
- }, []);
4467
4451
  React15.useEffect(() => {
4468
4452
  if (!inputCtx || !enabled) {
4469
4453
  log("Not subscribing - inputCtx:", !!inputCtx, "enabled:", enabled);
4470
4454
  return;
4471
4455
  }
4472
- log("Subscribing to priority input");
4456
+ log("Subscribing to priority input, activation key:", activationKey);
4473
4457
  const handler = (key) => {
4474
- log("Key received:", key.name, "ctrl:", key.ctrl, "isActive:", isActive, "hasChild:", hasChildJumpNav);
4475
- if (!isActive && !hasChildJumpNav && key.name === activationKeyParsed.name && !!key.ctrl === activationKeyParsed.ctrl && !!key.alt === activationKeyParsed.alt && !!key.shift === activationKeyParsed.shift && !!key.meta === activationKeyParsed.meta) {
4458
+ const nameMatch = key.name === activationKeyParsed.name;
4459
+ const ctrlMatch = !!key.ctrl === activationKeyParsed.ctrl;
4460
+ const altMatch = !!key.alt === activationKeyParsed.alt;
4461
+ const shiftMatch = !!key.shift === activationKeyParsed.shift;
4462
+ const metaMatch = !!key.meta === activationKeyParsed.meta;
4463
+ if (!isActive && nameMatch && ctrlMatch && altMatch && shiftMatch && metaMatch) {
4476
4464
  log("Activation key matched! Activating...");
4477
4465
  setIsActive(true);
4478
4466
  setInputBuffer("");
@@ -4480,6 +4468,7 @@ function JumpNav({
4480
4468
  }
4481
4469
  if (isActive) {
4482
4470
  if (key.name === "escape") {
4471
+ log("Escape pressed, deactivating");
4483
4472
  setIsActive(false);
4484
4473
  setInputBuffer("");
4485
4474
  return true;
@@ -4490,8 +4479,10 @@ function JumpNav({
4490
4479
  }
4491
4480
  if (key.sequence && key.sequence.length === 1 && /[a-z]/i.test(key.sequence)) {
4492
4481
  const newBuffer = inputBuffer + key.sequence.toLowerCase();
4482
+ log("Buffer:", newBuffer);
4493
4483
  const targetId = visibleHintMap.get(newBuffer);
4494
4484
  if (targetId) {
4485
+ log("Jumping to", targetId);
4495
4486
  focusCtx?.requestFocus(targetId);
4496
4487
  setIsActive(false);
4497
4488
  setInputBuffer("");
@@ -4510,10 +4501,20 @@ function JumpNav({
4510
4501
  return false;
4511
4502
  };
4512
4503
  return inputCtx.subscribePriority(handler);
4513
- }, [inputCtx, enabled, isActive, activationKeyParsed, inputBuffer, visibleHintMap, focusCtx, hasChildJumpNav]);
4504
+ }, [inputCtx, enabled, isActive, activationKeyParsed, inputBuffer, visibleHintMap, focusCtx, activationKey, log]);
4514
4505
  const hintsOverlay = isActive ? React15__default.default.createElement(
4515
- React15__default.default.Fragment,
4516
- null,
4506
+ "box",
4507
+ {
4508
+ // Portal-like wrapper - fullscreen absolute overlay
4509
+ style: {
4510
+ position: "absolute",
4511
+ top: 0,
4512
+ left: 0,
4513
+ width: "100%",
4514
+ height: "100%",
4515
+ zIndex: 99998
4516
+ }
4517
+ },
4517
4518
  ...visibleElements.map((el, i) => {
4518
4519
  const hint = visibleHints[i];
4519
4520
  if (!hint) return null;
@@ -4558,24 +4559,10 @@ function JumpNav({
4558
4559
  }, inputBuffer ? `Jump: ${inputBuffer}_` : "Press a key to jump \u2022 ESC to cancel")
4559
4560
  )
4560
4561
  ) : null;
4561
- const wrappedChildren = React15__default.default.createElement(
4562
- "box",
4563
- {
4564
- ref: (node) => {
4565
- wrapperRef.current = node;
4566
- },
4567
- style: {
4568
- // Invisible wrapper - takes full size of parent
4569
- flexGrow: 1,
4570
- flexDirection: "column"
4571
- }
4572
- },
4573
- children
4574
- );
4575
4562
  return React15__default.default.createElement(
4576
- JumpNavContext.Provider,
4577
- { value: contextValue },
4578
- wrappedChildren,
4563
+ React15__default.default.Fragment,
4564
+ null,
4565
+ children,
4579
4566
  hintsOverlay
4580
4567
  );
4581
4568
  }
@@ -4685,7 +4672,7 @@ function useFocusRegistry() {
4685
4672
  });
4686
4673
  const updateElements = React15.useCallback(() => {
4687
4674
  if (!focusCtx) return;
4688
- const registered = focusCtx.getRegisteredElements?.() ?? [];
4675
+ const registered = focusCtx.getActiveElements?.() ?? focusCtx.getRegisteredElements?.() ?? [];
4689
4676
  const mapped = registered.map(({ id, node }) => ({
4690
4677
  id,
4691
4678
  node,