lynx-console 0.5.0 → 0.6.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.
package/README.md CHANGED
@@ -182,6 +182,7 @@ You can also integrate it with a back press handler so that the console closes w
182
182
  | `theme` | `"light" \| "dark"` | `"light"` | Console UI theme |
183
183
  | `safeAreaInsetBottom` | `string` | `"50px"` | Bottom safe area inset |
184
184
  | `customTabs` | `CustomTab[]` | `undefined` | Additional custom tabs to display in the console |
185
+ | `initialPosition` | `{ top?: number; left?: number; right?: number; bottom?: number }` | `{ right: 16, bottom: 84 }` | Initial position (px) of the floating button. Each side is independent, so you can anchor it to any corner (e.g. `{ top: 50, left: 16 }`). When both `top` and `bottom` (or both `left` and `right`) are set, `top`/`left` win. Once the user drags the button, the saved position takes precedence. |
185
186
 
186
187
  ### `CustomTab`
187
188
 
package/dist/index.cjs CHANGED
@@ -1368,22 +1368,42 @@ const LONG_PRESS_DURATION = 400;
1368
1368
  const MOVE_THRESHOLD = 5;
1369
1369
  const DEFAULT_RIGHT = 16;
1370
1370
  const DEFAULT_BOTTOM = 84;
1371
- let savedRight = DEFAULT_RIGHT;
1372
- let savedBottom = DEFAULT_BOTTOM;
1373
- function useLongPressDrag(onTap) {
1374
- const [right, setRight] = (0, _lynx_js_react.useState)(savedRight);
1375
- const [bottom, setBottom] = (0, _lynx_js_react.useState)(savedBottom);
1371
+ function resolveAnchors(initial) {
1372
+ const vertical = initial?.top !== void 0 ? "top" : "bottom";
1373
+ const horizontal = initial?.left !== void 0 ? "left" : "right";
1374
+ const y = vertical === "top" ? initial?.top ?? 0 : initial?.bottom ?? DEFAULT_BOTTOM;
1375
+ return {
1376
+ vertical,
1377
+ horizontal,
1378
+ x: horizontal === "left" ? initial?.left ?? 0 : initial?.right ?? DEFAULT_RIGHT,
1379
+ y
1380
+ };
1381
+ }
1382
+ let saved = null;
1383
+ function useLongPressDrag(onTap, options) {
1384
+ const anchors = resolveAnchors(options?.initialPosition);
1385
+ const snapshot = saved;
1386
+ let initX = anchors.x;
1387
+ let initY = anchors.y;
1388
+ if (snapshot !== null && snapshot.vertical === anchors.vertical && snapshot.horizontal === anchors.horizontal) {
1389
+ initX = snapshot.x;
1390
+ initY = snapshot.y;
1391
+ }
1392
+ const [x, setX] = (0, _lynx_js_react.useState)(initX);
1393
+ const [y, setY] = (0, _lynx_js_react.useState)(initY);
1376
1394
  const [phase, setPhase] = (0, _lynx_js_react.useState)("idle");
1377
- const [tempRight, setTempRight] = (0, _lynx_js_react.useState)(savedRight);
1378
- const [tempBottom, setTempBottom] = (0, _lynx_js_react.useState)(savedBottom);
1395
+ const [tempX, setTempX] = (0, _lynx_js_react.useState)(initX);
1396
+ const [tempY, setTempY] = (0, _lynx_js_react.useState)(initY);
1379
1397
  const timerRef = (0, _lynx_js_react.useRef)(null);
1380
1398
  const draggingRef = (0, _lynx_js_react.useRef)(false);
1381
1399
  const startRef = (0, _lynx_js_react.useRef)({
1382
1400
  x: 0,
1383
1401
  y: 0,
1384
- r: 0,
1385
- b: 0
1402
+ ax: 0,
1403
+ ay: 0
1386
1404
  });
1405
+ const xSign = anchors.horizontal === "right" ? -1 : 1;
1406
+ const ySign = anchors.vertical === "bottom" ? -1 : 1;
1387
1407
  const clearTimer = () => {
1388
1408
  if (timerRef.current) {
1389
1409
  clearTimeout(timerRef.current);
@@ -1394,15 +1414,15 @@ function useLongPressDrag(onTap) {
1394
1414
  startRef.current = {
1395
1415
  x: e.detail.x,
1396
1416
  y: e.detail.y,
1397
- r: right,
1398
- b: bottom
1417
+ ax: x,
1418
+ ay: y
1399
1419
  };
1400
1420
  draggingRef.current = false;
1401
1421
  timerRef.current = setTimeout(() => {
1402
1422
  draggingRef.current = true;
1403
1423
  setPhase("dragging");
1404
- setTempRight(right);
1405
- setTempBottom(bottom);
1424
+ setTempX(x);
1425
+ setTempY(y);
1406
1426
  }, LONG_PRESS_DURATION);
1407
1427
  };
1408
1428
  const handleTouchMove = (e) => {
@@ -1410,26 +1430,34 @@ function useLongPressDrag(onTap) {
1410
1430
  const dy = e.detail.y - startRef.current.y;
1411
1431
  if (!draggingRef.current && (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)) clearTimer();
1412
1432
  if (!draggingRef.current) return;
1413
- setTempRight(startRef.current.r - dx);
1414
- setTempBottom(startRef.current.b - dy);
1433
+ setTempX(startRef.current.ax + xSign * dx);
1434
+ setTempY(startRef.current.ay + ySign * dy);
1415
1435
  };
1416
1436
  const handleTouchEnd = () => {
1417
1437
  clearTimer();
1418
1438
  if (draggingRef.current) {
1419
- setRight(tempRight);
1420
- setBottom(tempBottom);
1421
- savedRight = tempRight;
1422
- savedBottom = tempBottom;
1439
+ setX(tempX);
1440
+ setY(tempY);
1441
+ saved = {
1442
+ vertical: anchors.vertical,
1443
+ horizontal: anchors.horizontal,
1444
+ x: tempX,
1445
+ y: tempY
1446
+ };
1423
1447
  setPhase("releasing");
1424
1448
  draggingRef.current = false;
1425
1449
  setTimeout(() => setPhase("idle"), 300);
1426
1450
  } else onTap();
1427
1451
  };
1428
1452
  const isDragging = phase === "dragging";
1453
+ const currentX = isDragging ? tempX : x;
1454
+ const currentY = isDragging ? tempY : y;
1429
1455
  return {
1430
1456
  phase,
1431
- right: isDragging ? tempRight : right,
1432
- bottom: isDragging ? tempBottom : bottom,
1457
+ positionStyle: {
1458
+ [anchors.horizontal]: `${currentX}px`,
1459
+ [anchors.vertical]: `${currentY}px`
1460
+ },
1433
1461
  clearTimer,
1434
1462
  handlers: {
1435
1463
  catchtouchstart: handleTouchStart,
@@ -1457,9 +1485,9 @@ const SHINE_STYLES = {
1457
1485
  transition: "opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)"
1458
1486
  }
1459
1487
  };
1460
- const FloatingButton = ({ bindtap, children }) => {
1488
+ const FloatingButton = ({ bindtap, children, initialPosition }) => {
1461
1489
  const colors = useThemeColors();
1462
- const { phase, right, bottom, clearTimer, handlers } = useLongPressDrag(bindtap);
1490
+ const { phase, positionStyle, clearTimer, handlers } = useLongPressDrag(bindtap, { initialPosition });
1463
1491
  const handleReload = () => {
1464
1492
  try {
1465
1493
  lynx.reload({}, () => {
@@ -1471,8 +1499,7 @@ const FloatingButton = ({ bindtap, children }) => {
1471
1499
  };
1472
1500
  const isDragging = phase === "dragging";
1473
1501
  return <view className={"fb-wrapper"} consume-slide-event={[[-180, 180]]} style={{
1474
- right: `${right}px`,
1475
- bottom: `${bottom}px`,
1502
+ ...positionStyle,
1476
1503
  transform: isDragging ? "scale(1.05)" : "scale(1)",
1477
1504
  transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`
1478
1505
  }} {...handlers}>
@@ -1490,7 +1517,7 @@ const FloatingButton = ({ bindtap, children }) => {
1490
1517
 
1491
1518
  //#endregion
1492
1519
  //#region src/index.tsx
1493
- const LynxConsole = (0, _lynx_js_react.forwardRef)(({ theme = "light", safeAreaInsetBottom = "50px", customTabs }, ref) => {
1520
+ const LynxConsole = (0, _lynx_js_react.forwardRef)(({ theme = "light", safeAreaInsetBottom = "50px", customTabs, initialPosition }, ref) => {
1494
1521
  const [isOpen, setIsOpen] = (0, _lynx_js_react.useState)(false);
1495
1522
  const [shouldClose, setShouldClose] = (0, _lynx_js_react.useState)(false);
1496
1523
  const { performances } = usePerformance();
@@ -1528,7 +1555,7 @@ const LynxConsole = (0, _lynx_js_react.forwardRef)(({ theme = "light", safeAreaI
1528
1555
  backgroundColor: colors.bg.layerDefault,
1529
1556
  color: colors.fg.neutral
1530
1557
  }}>
1531
- <FloatingButton bindtap={handleOpenBottomSheet}>
1558
+ <FloatingButton bindtap={handleOpenBottomSheet} initialPosition={initialPosition}>
1532
1559
  <text className="fb-title t4" style={{
1533
1560
  fontWeight: "400",
1534
1561
  color: colors.palette.staticWhite
package/dist/index.d.cts CHANGED
@@ -88,6 +88,12 @@ interface LynxConsoleProps {
88
88
  theme?: "light" | "dark";
89
89
  safeAreaInsetBottom?: string;
90
90
  customTabs?: CustomTab[];
91
+ initialPosition?: {
92
+ top?: number;
93
+ left?: number;
94
+ right?: number;
95
+ bottom?: number;
96
+ };
91
97
  }
92
98
  declare const LynxConsole: react.ForwardRefExoticComponent<LynxConsoleProps & react.RefAttributes<LynxConsoleHandle>>;
93
99
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;AAAA;AAAA,cAcT,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;EACb,eAAA;IACE,GAAA;IACA,IAAA;IACA,KAAA;IACA,MAAA;EAAA;AAAA;AAAA,cAeE,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
package/dist/index.d.mts CHANGED
@@ -88,6 +88,12 @@ interface LynxConsoleProps {
88
88
  theme?: "light" | "dark";
89
89
  safeAreaInsetBottom?: string;
90
90
  customTabs?: CustomTab[];
91
+ initialPosition?: {
92
+ top?: number;
93
+ left?: number;
94
+ right?: number;
95
+ bottom?: number;
96
+ };
91
97
  }
92
98
  declare const LynxConsole: react.ForwardRefExoticComponent<LynxConsoleProps & react.RefAttributes<LynxConsoleHandle>>;
93
99
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;AAAA;AAAA,cAcT,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/index.tsx"],"mappings":";;;;;UAIiB,SAAA;EACf,GAAA;EACA,KAAA;EACA,aAAA,QAAqB,SAAA;AAAA;AAAA,KAKX,QAAA;AAAA,UAEK,QAAA;EACf,EAAA;EACA,KAAA,EAAO,QAAA;EACP,OAAA;EACA,SAAA;EACA,IAAA;AAAA;AAAA,KAIU,aAAA;AAAA,UAEK,YAAA;EACf,EAAA;EACA,GAAA;EACA,MAAA;EACA,MAAA,EAAQ,aAAA;EACR,UAAA;EACA,UAAA;EACA,SAAA;EACA,OAAA;EACA,QAAA;EACA,cAAA,GAAiB,MAAA;EACjB,WAAA;EACA,eAAA,GAAkB,MAAA;EAClB,YAAA;EACA,KAAA;AAAA;AAAA,KAIU,oBAAA;AAAA,UAEK,iBAAA;EACf,IAAA;EACA,QAAA;EACA,kBAAA;EACA,cAAA;EACA,gBAAA;EACA,YAAA;AAAA;AAAA,UAGe,oBAAA;EACf,EAAA;EACA,SAAA,EAAW,oBAAA;EACX,IAAA;EACA,SAAA;EACA,OAAA,GAAU,iBAAA;EACV,QAAA;AAAA;AAAA,QAIM,MAAA;EAAA,IACF,aAAA;IACF,KAAA;IAAA,CACC,GAAA;EAAA;EAAA,IAGC,gBAAA;IAGE,eAAA;MACE,GAAA,MAAS,IAAA;MACT,IAAA,MAAU,IAAA;MACV,KAAA,MAAW,IAAA;MACX,IAAA,MAAU,IAAA;IAAA;IAIZ,KAAA;MACE,IAAA,GAAO,QAAA;MACP,YAAA,GAAe,GAAA,EAAK,KAAA,EAAO,QAAA;MAC3B,YAAA,IAAgB,QAAA,GAAW,KAAA,EAAO,QAAA;MAElC,QAAA,GAAW,YAAA;MACX,WAAA,GAAc,GAAA,SAAY,YAAA;MAC1B,gBAAA,GAAmB,GAAA,EAAK,KAAA,EAAO,YAAA;MAC/B,gBAAA,IACE,QAAA,GAAW,KAAA,EAAO,YAAA;MAGpB,YAAA,GAAe,oBAAA;MACf,oBAAA,GAAuB,GAAA,EAAK,KAAA,EAAO,oBAAA;MACnC,oBAAA,IACE,QAAA,GAAW,KAAA,EAAO,oBAAA;IAAA;IAItB,qBAAA;EAAA;AAAA;;;UCnFS,iBAAA;EACf,IAAA;EACA,KAAA;EACA,MAAA;AAAA;AAAA,UAGe,gBAAA;EACf,KAAA;EACA,mBAAA;EACA,UAAA,GAAa,SAAA;EACb,eAAA;IACE,GAAA;IACA,IAAA;IACA,KAAA;IACA,MAAA;EAAA;AAAA;AAAA,cAeE,WAAA,EAAW,KAAA,CAAA,yBAAA,CAAA,gBAAA,GAAA,KAAA,CAAA,aAAA,CAAA,iBAAA"}
package/dist/index.mjs CHANGED
@@ -1368,22 +1368,42 @@ const LONG_PRESS_DURATION = 400;
1368
1368
  const MOVE_THRESHOLD = 5;
1369
1369
  const DEFAULT_RIGHT = 16;
1370
1370
  const DEFAULT_BOTTOM = 84;
1371
- let savedRight = DEFAULT_RIGHT;
1372
- let savedBottom = DEFAULT_BOTTOM;
1373
- function useLongPressDrag(onTap) {
1374
- const [right, setRight] = useState(savedRight);
1375
- const [bottom, setBottom] = useState(savedBottom);
1371
+ function resolveAnchors(initial) {
1372
+ const vertical = initial?.top !== void 0 ? "top" : "bottom";
1373
+ const horizontal = initial?.left !== void 0 ? "left" : "right";
1374
+ const y = vertical === "top" ? initial?.top ?? 0 : initial?.bottom ?? DEFAULT_BOTTOM;
1375
+ return {
1376
+ vertical,
1377
+ horizontal,
1378
+ x: horizontal === "left" ? initial?.left ?? 0 : initial?.right ?? DEFAULT_RIGHT,
1379
+ y
1380
+ };
1381
+ }
1382
+ let saved = null;
1383
+ function useLongPressDrag(onTap, options) {
1384
+ const anchors = resolveAnchors(options?.initialPosition);
1385
+ const snapshot = saved;
1386
+ let initX = anchors.x;
1387
+ let initY = anchors.y;
1388
+ if (snapshot !== null && snapshot.vertical === anchors.vertical && snapshot.horizontal === anchors.horizontal) {
1389
+ initX = snapshot.x;
1390
+ initY = snapshot.y;
1391
+ }
1392
+ const [x, setX] = useState(initX);
1393
+ const [y, setY] = useState(initY);
1376
1394
  const [phase, setPhase] = useState("idle");
1377
- const [tempRight, setTempRight] = useState(savedRight);
1378
- const [tempBottom, setTempBottom] = useState(savedBottom);
1395
+ const [tempX, setTempX] = useState(initX);
1396
+ const [tempY, setTempY] = useState(initY);
1379
1397
  const timerRef = useRef(null);
1380
1398
  const draggingRef = useRef(false);
1381
1399
  const startRef = useRef({
1382
1400
  x: 0,
1383
1401
  y: 0,
1384
- r: 0,
1385
- b: 0
1402
+ ax: 0,
1403
+ ay: 0
1386
1404
  });
1405
+ const xSign = anchors.horizontal === "right" ? -1 : 1;
1406
+ const ySign = anchors.vertical === "bottom" ? -1 : 1;
1387
1407
  const clearTimer = () => {
1388
1408
  if (timerRef.current) {
1389
1409
  clearTimeout(timerRef.current);
@@ -1394,15 +1414,15 @@ function useLongPressDrag(onTap) {
1394
1414
  startRef.current = {
1395
1415
  x: e.detail.x,
1396
1416
  y: e.detail.y,
1397
- r: right,
1398
- b: bottom
1417
+ ax: x,
1418
+ ay: y
1399
1419
  };
1400
1420
  draggingRef.current = false;
1401
1421
  timerRef.current = setTimeout(() => {
1402
1422
  draggingRef.current = true;
1403
1423
  setPhase("dragging");
1404
- setTempRight(right);
1405
- setTempBottom(bottom);
1424
+ setTempX(x);
1425
+ setTempY(y);
1406
1426
  }, LONG_PRESS_DURATION);
1407
1427
  };
1408
1428
  const handleTouchMove = (e) => {
@@ -1410,26 +1430,34 @@ function useLongPressDrag(onTap) {
1410
1430
  const dy = e.detail.y - startRef.current.y;
1411
1431
  if (!draggingRef.current && (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)) clearTimer();
1412
1432
  if (!draggingRef.current) return;
1413
- setTempRight(startRef.current.r - dx);
1414
- setTempBottom(startRef.current.b - dy);
1433
+ setTempX(startRef.current.ax + xSign * dx);
1434
+ setTempY(startRef.current.ay + ySign * dy);
1415
1435
  };
1416
1436
  const handleTouchEnd = () => {
1417
1437
  clearTimer();
1418
1438
  if (draggingRef.current) {
1419
- setRight(tempRight);
1420
- setBottom(tempBottom);
1421
- savedRight = tempRight;
1422
- savedBottom = tempBottom;
1439
+ setX(tempX);
1440
+ setY(tempY);
1441
+ saved = {
1442
+ vertical: anchors.vertical,
1443
+ horizontal: anchors.horizontal,
1444
+ x: tempX,
1445
+ y: tempY
1446
+ };
1423
1447
  setPhase("releasing");
1424
1448
  draggingRef.current = false;
1425
1449
  setTimeout(() => setPhase("idle"), 300);
1426
1450
  } else onTap();
1427
1451
  };
1428
1452
  const isDragging = phase === "dragging";
1453
+ const currentX = isDragging ? tempX : x;
1454
+ const currentY = isDragging ? tempY : y;
1429
1455
  return {
1430
1456
  phase,
1431
- right: isDragging ? tempRight : right,
1432
- bottom: isDragging ? tempBottom : bottom,
1457
+ positionStyle: {
1458
+ [anchors.horizontal]: `${currentX}px`,
1459
+ [anchors.vertical]: `${currentY}px`
1460
+ },
1433
1461
  clearTimer,
1434
1462
  handlers: {
1435
1463
  catchtouchstart: handleTouchStart,
@@ -1457,9 +1485,9 @@ const SHINE_STYLES = {
1457
1485
  transition: "opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)"
1458
1486
  }
1459
1487
  };
1460
- const FloatingButton = ({ bindtap, children }) => {
1488
+ const FloatingButton = ({ bindtap, children, initialPosition }) => {
1461
1489
  const colors = useThemeColors();
1462
- const { phase, right, bottom, clearTimer, handlers } = useLongPressDrag(bindtap);
1490
+ const { phase, positionStyle, clearTimer, handlers } = useLongPressDrag(bindtap, { initialPosition });
1463
1491
  const handleReload = () => {
1464
1492
  try {
1465
1493
  lynx.reload({}, () => {
@@ -1471,8 +1499,7 @@ const FloatingButton = ({ bindtap, children }) => {
1471
1499
  };
1472
1500
  const isDragging = phase === "dragging";
1473
1501
  return <view className={"fb-wrapper"} consume-slide-event={[[-180, 180]]} style={{
1474
- right: `${right}px`,
1475
- bottom: `${bottom}px`,
1502
+ ...positionStyle,
1476
1503
  transform: isDragging ? "scale(1.05)" : "scale(1)",
1477
1504
  transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`
1478
1505
  }} {...handlers}>
@@ -1490,7 +1517,7 @@ const FloatingButton = ({ bindtap, children }) => {
1490
1517
 
1491
1518
  //#endregion
1492
1519
  //#region src/index.tsx
1493
- const LynxConsole = forwardRef(({ theme = "light", safeAreaInsetBottom = "50px", customTabs }, ref) => {
1520
+ const LynxConsole = forwardRef(({ theme = "light", safeAreaInsetBottom = "50px", customTabs, initialPosition }, ref) => {
1494
1521
  const [isOpen, setIsOpen] = useState(false);
1495
1522
  const [shouldClose, setShouldClose] = useState(false);
1496
1523
  const { performances } = usePerformance();
@@ -1528,7 +1555,7 @@ const LynxConsole = forwardRef(({ theme = "light", safeAreaInsetBottom = "50px",
1528
1555
  backgroundColor: colors.bg.layerDefault,
1529
1556
  color: colors.fg.neutral
1530
1557
  }}>
1531
- <FloatingButton bindtap={handleOpenBottomSheet}>
1558
+ <FloatingButton bindtap={handleOpenBottomSheet} initialPosition={initialPosition}>
1532
1559
  <text className="fb-title t4" style={{
1533
1560
  fontWeight: "400",
1534
1561
  color: colors.palette.staticWhite
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/hooks/useKeyboardHeight.ts","../src/styles/theme.ts","../src/styles/ThemeContext.ts","../src/components/BottomSheet.tsx","../src/hooks/useConsole.ts","../src/hooks/useNetwork.ts","../src/hooks/usePerformance.ts","../src/utils/parseCssStyle.ts","../src/utils/parseFormat.ts","../src/components/LogPanel.tsx","../src/components/NetworkDetailSection.tsx","../src/components/NetworkPanel.tsx","../src/components/PerformancePanel.tsx","../src/components/Tabs.tsx","../src/components/ConsolePanel.tsx","../src/hooks/useLongPressDrag.ts","../src/components/FloatingButton.tsx","../src/index.tsx"],"sourcesContent":["import { useLynxGlobalEventListener, useState } from \"@lynx-js/react\";\n\nexport function useKeyboardHeight() {\n const [keyboardHeight, setKeyboardHeight] = useState(0);\n\n useLynxGlobalEventListener(\n \"keyboardstatuschanged\",\n (status: \"on\" | \"off\", height: number) => {\n setKeyboardHeight(status === \"on\" ? height : 0);\n },\n );\n\n return keyboardHeight;\n}\n","export type Theme = \"light\" | \"dark\";\n\nconst lightColors = {\n palette: {\n blue100: \"#eff6ff\",\n blue600: \"#5e98fe\",\n gray100: \"#f7f8f9\",\n gray400: \"#dcdee3\",\n green100: \"#edfaf6\",\n green600: \"#10ab7d\",\n purple100: \"#f5f3fe\",\n purple600: \"#9f84fb\",\n red100: \"#fdf0f0\",\n red600: \"#fc6a66\",\n red900: \"#921708\",\n staticWhite: \"#ffffff\",\n yellow100: \"#fff7de\",\n yellow600: \"#c49725\",\n yellow900: \"#4f3e1f\",\n },\n fg: {\n neutral: \"#1a1c20\",\n placeholder: \"#b0b3ba\",\n disabled: \"#d1d3d8\",\n neutralMuted: \"#555d6d\",\n neutralSubtle: \"#868b94\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#ffffff\",\n layerFloating: \"#ffffff\",\n neutralWeak: \"#f3f4f5\",\n },\n stroke: {\n neutralSubtle: \"#0000000c\",\n neutralWeak: \"#dcdee3\",\n },\n} as const;\n\nconst darkColors = {\n palette: {\n blue100: \"#202742\",\n blue600: \"#1e82eb\",\n gray100: \"#16171b\",\n gray400: \"#393d46\",\n green100: \"#202926\",\n green600: \"#1b946d\",\n purple100: \"#28213b\",\n purple600: \"#8e6bee\",\n red100: \"#322323\",\n red600: \"#f73526\",\n red900: \"#f8c5c3\",\n staticWhite: \"#ffffff\",\n yellow100: \"#302819\",\n yellow600: \"#b6720d\",\n yellow900: \"#e5d49b\",\n },\n fg: {\n neutral: \"#f3f4f5\",\n placeholder: \"#868b94\",\n disabled: \"#5b606a\",\n neutralMuted: \"#dcdee3\",\n neutralSubtle: \"#b0b3ba\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#16171b\",\n layerFloating: \"#1d2025\",\n neutralWeak: \"#2b2e35\",\n },\n stroke: {\n neutralSubtle: \"#ffffff0d\",\n neutralWeak: \"#393d46\",\n },\n} as const;\n\nconst colorMap = {\n light: lightColors,\n dark: darkColors,\n} as const;\n\nexport const fontWeight = {\n regular: \"400\",\n medium: \"500\",\n bold: \"700\",\n} as const;\n\nexport const duration = {\n d4: \"200ms\",\n d6: \"300ms\",\n} as const;\n\nexport function getColors(theme: Theme) {\n return colorMap[theme];\n}\n\nexport type ThemeColors = ReturnType<typeof getColors>;\n","import { createContext, useContext } from \"@lynx-js/react\";\nimport { getColors, type ThemeColors } from \"./theme\";\n\nconst ThemeContext = createContext<ThemeColors>(getColors(\"light\"));\n\nexport const ThemeProvider = ThemeContext.Provider;\n\nexport function useThemeColors(): ThemeColors {\n return useContext(ThemeContext);\n}\n","import { type ReactNode, useEffect, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\nimport { useKeyboardHeight } from \"../hooks/useKeyboardHeight\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration, fontWeight } from \"../styles/theme\";\nimport \"./BottomSheet.css\";\n\ninterface BottomSheetProps {\n children: ReactNode;\n title?: string;\n footer?: ReactNode;\n onClose: () => void;\n isOpen: boolean;\n shouldClose?: boolean;\n safeAreaInsetBottom?: string;\n}\n\nconst MIN_HEIGHT = 200;\nconst MAX_HEIGHT = 700;\nconst DEFAULT_HEIGHT = 500;\nconst CLOSE_DRAG_THRESHOLD = 30; // 30px 이상 아래로 드래그하면 닫힘\n\n// 마지막 높이 저장\nlet savedHeight: number | null = null;\n\nexport default function BottomSheet({\n children,\n title,\n footer,\n onClose,\n isOpen,\n shouldClose = false,\n safeAreaInsetBottom = \"25px\",\n}: BottomSheetProps) {\n const colors = useThemeColors();\n const [sheetHeight, setSheetHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [tempHeight, setTempHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [isDragging, setIsDragging] = useState(false);\n const [dragStartY, setDragStartY] = useState(0);\n const [dragStartHeight, setDragStartHeight] = useState(\n savedHeight ?? DEFAULT_HEIGHT,\n );\n const [isOpening, setIsOpening] = useState(true);\n const [isClosing, setIsClosing] = useState(false);\n const keyboardHeight = useKeyboardHeight();\n\n // 닫기 애니메이션 처리\n const handleClose = () => {\n setIsClosing(true);\n setTimeout(() => {\n onClose();\n }, 300);\n };\n\n // 아래에서 올라오는 애니메이션\n useEffect(() => {\n requestAnimationFrame(() => {\n setIsOpening(false);\n });\n }, []);\n\n // 외부에서 닫기 요청 시 애니메이션 처리\n useEffect(() => {\n if (shouldClose && !isClosing) {\n handleClose();\n }\n }, [shouldClose, isClosing]);\n\n // 높이 변경 시 저장\n useEffect(() => {\n savedHeight = sheetHeight;\n }, [sheetHeight]);\n\n if (!isOpen) return null;\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n setIsDragging(true);\n setDragStartY(e.detail.y);\n setDragStartHeight(sheetHeight);\n setTempHeight(sheetHeight);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n if (!isDragging) return;\n const deltaY = dragStartY - e.detail.y;\n const newHeight = Math.min(\n Math.max(dragStartHeight + deltaY, MIN_HEIGHT),\n MAX_HEIGHT,\n );\n setTempHeight(newHeight);\n };\n\n const handleTouchEnd = () => {\n setIsDragging(false);\n\n // 아래로 일정 30px 이상 드래그하면 닫기\n const dragDistance = dragStartHeight - tempHeight;\n setSheetHeight(tempHeight);\n if (dragDistance > CLOSE_DRAG_THRESHOLD) {\n handleClose();\n }\n };\n\n return (\n <scroll-view\n className=\"bs-backdrop\"\n style={{\n background: colors.bg.overlay,\n opacity: isOpening || isClosing ? 0 : 1,\n transition: `opacity ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n <view className=\"bs-overlay\" bindtap={handleClose}>\n <view\n className=\"bs-content\"\n catchtap={() => {}}\n style={{\n background: colors.bg.layerFloating,\n height: `${\n keyboardHeight > 0\n ? Math.min(\n MAX_HEIGHT,\n (isDragging ? tempHeight : sheetHeight) + keyboardHeight,\n )\n : isDragging\n ? tempHeight\n : sheetHeight\n }px`,\n transform:\n isOpening || isClosing ? \"translateY(100%)\" : \"translateY(0)\",\n transition: isDragging\n ? \"none\"\n : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1), height ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n {/* catchtap: 이벤트 버블링 차단 */}\n <view\n className=\"bs-handleContainer\"\n bindtouchstart={handleTouchStart}\n bindtouchmove={handleTouchMove}\n bindtouchend={handleTouchEnd}\n >\n <view\n className=\"bs-handle\"\n style={{ backgroundColor: colors.palette.gray400 }}\n />\n </view>\n <view className=\"bs-header\">\n {title && (\n <text\n className=\"bs-title t7\"\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n {title}\n </text>\n )}\n </view>\n <view\n className=\"bs-body\"\n style={{\n paddingBottom:\n keyboardHeight > 0\n ? `${keyboardHeight}px`\n : safeAreaInsetBottom,\n }}\n >\n {children}\n </view>\n {footer && <view className=\"bs-footer\">{footer}</view>}\n </view>\n </view>\n </scroll-view>\n );\n}\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { LogEntry } from \"../types\";\n\nexport const useConsole = () => {\n const [logs, setLogs] = useState<LogEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs === \"undefined\") {\n console.warn(\"[LynxConsole] Log monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setLogs([...(state.logs ?? [])]);\n\n const updateLogs = (_entry: LogEntry) => {\n setLogs([...(state.logs ?? [])]);\n };\n\n const unsubscribe = state.logSubscribe?.(updateLogs);\n\n return unsubscribe;\n }, []);\n\n const clearLogs = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.logs = [];\n setLogs([]);\n }\n };\n\n return { logs, clearLogs };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { NetworkEntry } from \"../types\";\n\nexport const useNetwork = () => {\n const [networks, setNetworks] = useState<NetworkEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks === \"undefined\") {\n console.warn(\"[LynxConsole] Network monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setNetworks([...(state.networks ?? [])]);\n\n const updateNetworks = (_entry: NetworkEntry) => {\n setNetworks([...(state.networks ?? [])]);\n };\n\n const unsubscribe = state.subscribeNetwork?.(updateNetworks);\n\n return unsubscribe;\n }, []);\n\n const clearNetworks = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.networks = [];\n state.networksMap?.clear();\n setNetworks([]);\n }\n };\n\n return { networks, clearNetworks };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { PerformanceEntryData } from \"../types\";\n\nexport const usePerformance = () => {\n const [performances, setPerformances] = useState<PerformanceEntryData[]>([]);\n\n useEffect(() => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances === \"undefined\"\n ) {\n console.warn(\"[LynxConsole] Performance monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setPerformances([...(state.performances ?? [])]);\n\n const updatePerformances = (_entry: PerformanceEntryData) => {\n setPerformances([...(state.performances ?? [])]);\n };\n\n const unsubscribe = state.subscribePerformance?.(updatePerformances);\n\n return unsubscribe;\n }, []);\n\n const clearPerformances = () => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances !== \"undefined\"\n ) {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.performances = [];\n setPerformances([]);\n }\n };\n\n return { performances, clearPerformances };\n};\n","export type StyleObject = Record<string, string>;\n\nconst DANGEROUS_VALUE = /url\\s*\\(|expression\\s*\\(|@import/i;\n\nconst toCamelCase = (name: string): string => {\n if (name.startsWith(\"--\")) return name;\n return name.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n};\n\nexport const parseCssString = (css: string): StyleObject => {\n const result: StyleObject = {};\n for (const raw of css.split(\";\")) {\n const decl = raw.trim();\n if (!decl) continue;\n const colon = decl.indexOf(\":\");\n if (colon <= 0) continue;\n const name = decl.slice(0, colon).trim().toLowerCase();\n const value = decl.slice(colon + 1).trim();\n if (!name || !value) continue;\n if (DANGEROUS_VALUE.test(value)) continue;\n result[toCamelCase(name)] = value;\n }\n return result;\n};\n","import { parseCssString, type StyleObject } from \"./parseCssStyle\";\n\nexport type LogSegment =\n | { type: \"text\"; text: string; style?: StyleObject }\n | { type: \"arg\"; value: unknown };\n\nexport interface ParsedLogArgs {\n segments: LogSegment[];\n rest: unknown[];\n}\n\nconst HAS_FORMAT = /%[csdifoO%]/;\n\nconst formatNumber = (v: unknown, int: boolean): string => {\n const n =\n typeof v === \"number\"\n ? int\n ? Math.trunc(v)\n : v\n : typeof v === \"symbol\"\n ? Number.NaN\n : int\n ? parseInt(String(v), 10)\n : parseFloat(String(v));\n return Number.isNaN(n) ? \"NaN\" : String(n);\n};\n\nexport const parseConsoleArgs = (args: unknown[]): ParsedLogArgs => {\n const first = args[0];\n if (typeof first !== \"string\" || !HAS_FORMAT.test(first)) {\n return { segments: [], rest: args };\n }\n\n const segments: LogSegment[] = [];\n let currentText = \"\";\n let currentStyle: StyleObject | undefined;\n let argIndex = 1;\n let lastIndex = 0;\n\n const flushText = () => {\n if (currentText) {\n segments.push({ type: \"text\", text: currentText, style: currentStyle });\n currentText = \"\";\n }\n };\n\n const re = /%([csdifoO%])/g;\n let match: RegExpExecArray | null = re.exec(first);\n while (match !== null) {\n currentText += first.slice(lastIndex, match.index);\n lastIndex = re.lastIndex;\n const spec = match[1];\n\n if (spec === \"%\") {\n currentText += \"%\";\n } else if (argIndex >= args.length) {\n currentText += match[0];\n } else {\n const arg = args[argIndex++];\n switch (spec) {\n case \"c\":\n flushText();\n currentStyle =\n typeof arg === \"string\" ? parseCssString(arg) : undefined;\n break;\n case \"s\":\n currentText += String(arg);\n break;\n case \"d\":\n case \"i\":\n currentText += formatNumber(arg, true);\n break;\n case \"f\":\n currentText += formatNumber(arg, false);\n break;\n case \"o\":\n case \"O\":\n flushText();\n segments.push({ type: \"arg\", value: arg });\n break;\n }\n }\n\n match = re.exec(first);\n }\n currentText += first.slice(lastIndex);\n flushText();\n\n return { segments, rest: args.slice(argIndex) };\n};\n","import { useEffect, useMemo, useRef, useState } from \"@lynx-js/react\";\nimport type { BaseEvent, InputInputEvent, NodesRef } from \"@lynx-js/types\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { LogEntry, LogLevel } from \"../types\";\nimport { parseConsoleArgs } from \"../utils/parseFormat\";\nimport \"./ConsolePanel.css\";\n\nconst LOG_LEVELS: LogLevel[] = [\"log\", \"info\", \"warn\", \"error\"];\n\nlet savedEnabledLevels: Set<LogLevel> | null = null;\nlet savedSearchQuery = \"\";\nlet closeFilterDropdown: (() => void) | null = null;\n\nexport const dismissFilterDropdown = () => closeFilterDropdown?.();\n\ninterface LogPanelProps {\n logs: LogEntry[];\n clearLogs: () => void;\n}\n\nconst runCode = (code: string) => {\n try {\n // biome-ignore lint: intentional REPL tool\n const result = eval(code);\n if (result instanceof Promise) {\n result.then((r) => console.log(r)).catch((e) => console.error(e));\n } else {\n console.log(result);\n }\n } catch (e) {\n console.error(e);\n }\n};\n\nfunction getLevelColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"log\":\n return colors.palette.green600;\n case \"info\":\n return colors.palette.blue600;\n case \"warn\":\n return colors.palette.yellow600;\n case \"error\":\n return colors.palette.red600;\n }\n}\n\nfunction getLogItemBg(\n colors: ThemeColors,\n level: LogLevel,\n): string | undefined {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nfunction getStringColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.fg.neutral;\n }\n}\n\nfunction getPrimitiveColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.palette.blue600;\n }\n}\n\nexport const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {\n const colors = useThemeColors();\n const [expandedArgs, setExpandedArgs] = useState(new Set());\n const [code, setCode] = useState(\"\");\n const [enabledLevels, setEnabledLevels] = useState<Set<LogLevel>>(\n () => savedEnabledLevels ?? new Set(LOG_LEVELS),\n );\n const [filterOpen, setFilterOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState(savedSearchQuery);\n const inputRef = useRef<NodesRef>(null);\n const searchInputRef = useRef<NodesRef>(null);\n const listRef = useRef<NodesRef>(null);\n\n useEffect(() => {\n savedEnabledLevels = enabledLevels;\n }, [enabledLevels]);\n\n useEffect(() => {\n savedSearchQuery = searchQuery;\n }, [searchQuery]);\n\n useEffect(() => {\n if (savedSearchQuery) {\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: savedSearchQuery } })\n .exec();\n }\n }, []);\n\n useEffect(() => {\n closeFilterDropdown = () => setFilterOpen(false);\n return () => {\n closeFilterDropdown = null;\n };\n }, []);\n\n const filteredLogs = useMemo(\n () =>\n logs.filter((log) => {\n if (!enabledLevels.has(log.level)) return false;\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return log.args.some((arg) =>\n String(arg).toLowerCase().includes(query),\n );\n }\n return true;\n }),\n [logs, enabledLevels, searchQuery],\n );\n const logsRef = useRef(filteredLogs);\n logsRef.current = filteredLogs;\n\n const toggleLevel = (level: LogLevel) => {\n setEnabledLevels((prev) => {\n const next = new Set(prev);\n if (next.has(level)) {\n next.delete(level);\n } else {\n next.add(level);\n }\n return next;\n });\n };\n\n const scrollToBottom = (smooth: boolean) => {\n if (logsRef.current.length === 0) return;\n listRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: { position: logsRef.current.length - 1, smooth },\n })\n .exec();\n };\n\n useEffect(() => {\n scrollToBottom(true);\n }, [filteredLogs]);\n\n const toggleArg = (key: string) => {\n setExpandedArgs((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n };\n\n const handleRun = () => {\n const trimmed = code.trim();\n if (!trimmed) return;\n\n setCode(\"\");\n inputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n runCode(trimmed);\n };\n\n const renderArg = (\n arg: unknown,\n parentKey: string,\n level: \"log\" | \"info\" | \"warn\" | \"error\",\n ): React.ReactNode => {\n const key = parentKey;\n const isExpanded = expandedArgs.has(key);\n\n if (arg === null) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n null\n </text>\n );\n }\n\n if (arg === undefined) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n undefined\n </text>\n );\n }\n\n if (typeof arg === \"string\") {\n const MAX_LENGTH = 80;\n const shouldTruncate = arg.length > MAX_LENGTH;\n const strColor = getStringColor(colors, level);\n\n if (!shouldTruncate) {\n return (\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {arg}\n </text>\n );\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {isExpanded ? arg : `${arg.slice(0, MAX_LENGTH)}...`}\n </text>\n </view>\n </view>\n );\n }\n\n if (typeof arg === \"number\" || typeof arg === \"boolean\") {\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n }\n\n if (typeof arg === \"object\") {\n let preview = \"Object\";\n if (Array.isArray(arg)) {\n preview = `Array(${arg.length})`;\n } else if (arg instanceof Map) {\n preview = `Map(${arg.size})`;\n } else if (arg instanceof Set) {\n preview = `Set(${arg.size})`;\n } else if (arg instanceof Date) {\n preview = `Date`;\n } else if (arg instanceof RegExp) {\n preview = `RegExp`;\n } else if (arg instanceof Error) {\n preview = `${arg.constructor.name}`;\n } else if (arg?.constructor?.name && arg.constructor.name !== \"Object\") {\n preview = arg.constructor.name;\n }\n\n let jsonString: string;\n if (arg instanceof Map) {\n const entries = Array.from(arg.entries()).map(\n ([k, v]) => ` [${stringify(k)}, ${stringify(v)}]`,\n );\n jsonString = `{\\n${entries.join(\",\\n\")}\\n}`;\n } else if (arg instanceof Set) {\n const values = Array.from(arg.values()).map((v) => stringify(v));\n jsonString = `{\\n${values.join(\", \")}\\n}`;\n } else {\n jsonString =\n stringify(arg, null, 2, { references: true }) ?? String(arg);\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argObjectPreview t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {preview}\n </text>\n </view>\n {isExpanded && (\n <view className={\"cp-argObjectContent\"}>\n <text\n className={\"cp-argObjectJson t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {jsonString}\n </text>\n </view>\n )}\n </view>\n );\n }\n\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n };\n\n return (\n <view\n className={\"cp-logContainer\"}\n bindtap={() => {\n if (filterOpen) setFilterOpen(false);\n }}\n >\n <view className={\"cp-logHeader\"}>\n <view className={\"cp-filterWrapper\"}>\n <view\n className={\"cp-filterButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n catchtap={() => setFilterOpen((v) => !v)}\n >\n <text\n className={\"cp-filterButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n Filter ▼\n </text>\n </view>\n {filterOpen && (\n <view\n className={\"cp-filterDropdown\"}\n style={{\n backgroundColor: colors.bg.layerFloating,\n borderColor: colors.stroke.neutralSubtle,\n }}\n catchtap={() => {}}\n >\n {LOG_LEVELS.map((level) => (\n <view\n key={level}\n className={\"cp-filterOption\"}\n bindtap={() => toggleLevel(level)}\n >\n <text\n className={\"cp-filterCheckbox t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {enabledLevels.has(level) ? \"✅\" : \"⬜\"}\n </text>\n <text\n className={\"cp-filterLabel t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {level.toUpperCase()}\n </text>\n </view>\n ))}\n </view>\n )}\n </view>\n <view\n className={\"cp-searchWrapper\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"cp-searchPrompt t6\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={searchInputRef}\n className={\"cp-searchInput t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"Search logs...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setSearchQuery(e.detail.value)\n }\n />\n {searchQuery.length > 0 && (\n <view\n className={\"cp-searchClear\"}\n bindtap={() => {\n setSearchQuery(\"\");\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n }}\n >\n <text\n className={\"cp-searchClearText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n ✕\n </text>\n </view>\n )}\n </view>\n <view style={{ display: \"flex\", flexDirection: \"row\", gap: 8 }}>\n <view\n className={\"cp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearLogs}\n >\n <text\n className={\"cp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n </view>\n <list\n ref={listRef}\n scroll-orientation=\"vertical\"\n className={\"cp-logList\"}\n preload-buffer-count={10}\n initial-scroll-index={Math.max(0, filteredLogs.length - 1)}\n >\n {filteredLogs.length === 0 ? (\n <list-item item-key=\"empty-state\">\n <view className={\"cp-placeholder\"}>\n <text\n className={\"cp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No logs yet. Try console.log(\"Hello!\")\n </text>\n </view>\n </list-item>\n ) : (\n filteredLogs.map((log) => {\n return (\n <list-item key={log.id} item-key={log.id}>\n <view\n className={\"cp-logItem\"}\n style={{\n backgroundColor: getLogItemBg(colors, log.level),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view className={\"cp-logItemHeader\"}>\n <text\n className={\"cp-logLevel t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getLevelColor(colors, log.level),\n }}\n >\n {log.level.toUpperCase()}\n </text>\n <text\n className={\"cp-logTime t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(log.timestamp).toISOString()}\n </text>\n </view>\n <view className={\"cp-logArgsContainer\"}>\n {(() => {\n const { segments, rest } = parseConsoleArgs(log.args);\n const baseTextStyle = {\n color: getStringColor(colors, log.level),\n fontWeight: fontWeight.regular,\n };\n const wrap = (key: string, content: React.ReactNode) => (\n <view\n key={key}\n className={\"cp-logArgItem\"}\n style={{ fontWeight: fontWeight.regular }}\n >\n {content}\n </view>\n );\n return (\n <>\n {segments.map((seg, index) => {\n const key = `${log.id}-seg-${index.toString()}`;\n return wrap(\n key,\n seg.type === \"text\" ? (\n <text\n className={\"cp-argString t3\"}\n style={{ ...baseTextStyle, ...seg.style }}\n >\n {seg.text}\n </text>\n ) : (\n renderArg(seg.value, key, log.level)\n ),\n );\n })}\n {rest.map((arg, index) => {\n const key = `${log.id}-rest-${index.toString()}`;\n return wrap(key, renderArg(arg, key, log.level));\n })}\n </>\n );\n })()}\n </view>\n </view>\n </list-item>\n );\n })\n )}\n </list>\n <view className={\"cp-replInputRow\"}>\n <text\n className={\"cp-replPrompt t10\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={inputRef}\n className={\"cp-replInput t5\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"enter code...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setCode(e.detail.value)\n }\n bindconfirm={handleRun}\n />\n <view\n className={\"cp-replRunButton\"}\n style={{ backgroundColor: colors.palette.green100 }}\n bindtap={handleRun}\n >\n <text\n className={\"cp-replRunButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.palette.green600,\n }}\n >\n Run\n </text>\n </view>\n </view>\n </view>\n );\n};\n","import { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkDetailSectionProps {\n headers?: Record<string, string> | undefined;\n body?: string | undefined;\n error?: string | undefined;\n}\n\nexport const NetworkDetailSection = ({\n headers = {},\n body = \"\",\n error = \"\",\n}: NetworkDetailSectionProps) => {\n const colors = useThemeColors();\n\n return (\n <>\n {/* Headers */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Headers\n </text>\n {headers && Object.keys(headers).length > 0 ? (\n <view className={\"np-table\"}>\n {Object.entries(headers).map(([key, value]) => (\n <view\n key={key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {value}\n </text>\n </view>\n ))}\n </view>\n ) : (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No headers\n </text>\n )}\n </view>\n\n {/* Body */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Body\n </text>\n {error && (\n <text\n className={\"np-errorText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n }}\n >\n {error}\n </text>\n )}\n {body && (\n <text\n className={\"np-bodyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n }}\n >\n {body}\n </text>\n )}\n {!error && !body && (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No body\n </text>\n )}\n </view>\n </>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { NetworkEntry } from \"../types\";\nimport { NetworkDetailSection } from \"./NetworkDetailSection\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkPanelProps {\n networks: NetworkEntry[];\n clearNetworks: () => void;\n}\n\ntype TabType = \"general\" | \"request\" | \"response\";\n\nfunction getMethodColors(colors: ThemeColors, method: string) {\n switch (method) {\n case \"GET\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"POST\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"PUT\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n case \"PATCH\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"DELETE\":\n return {\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nfunction getStatusCodeColor(\n colors: ThemeColors,\n variant: \"success\" | \"error\" | \"pending\",\n): string {\n switch (variant) {\n case \"success\":\n return colors.palette.green600;\n case \"error\":\n return colors.palette.red600;\n case \"pending\":\n return colors.fg.neutralSubtle;\n }\n}\n\nfunction getItemBg(colors: ThemeColors, status: string): string | undefined {\n switch (status) {\n case \"pending\":\n return colors.palette.gray100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nexport const NetworkPanel = ({\n networks,\n clearNetworks,\n}: NetworkPanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<TabType>(\"general\");\n const formatDuration = (duration?: number): string => {\n if (!duration) return \"-\";\n if (duration < 1000) return `${duration}ms`;\n return `${(duration / 1000).toFixed(2)}s`;\n };\n\n const extractPath = (url: string): string => {\n const pathMatch = url.match(/^https?:\\/\\/[^/]+(.*)$/);\n if (pathMatch?.[1]) {\n return pathMatch[1].startsWith(\"/\")\n ? pathMatch[1].slice(1)\n : pathMatch[1];\n }\n return url;\n };\n\n const getGeneralInfo = (network: NetworkEntry) => {\n return [\n { key: \"URL\", value: network.url },\n { key: \"Method\", value: network.method },\n network.statusCode\n ? { key: \"Status\", value: String(network.statusCode) }\n : null,\n {\n key: \"Request Time\",\n value: new Date(network.startTime).toISOString(),\n },\n network.endTime\n ? {\n key: \"Response Time\",\n value: new Date(network.endTime).toISOString(),\n }\n : null,\n network.duration\n ? { key: \"Duration\", value: formatDuration(network.duration) }\n : null,\n ].filter((item) => item !== null);\n };\n\n const getStatusCodeVariant = (\n status: string,\n statusCode?: number,\n ): \"success\" | \"error\" | \"pending\" => {\n if (status === \"pending\") return \"pending\";\n if (status === \"error\") return \"error\";\n if (statusCode && statusCode >= 200 && statusCode < 300) return \"success\";\n return \"error\";\n };\n\n return (\n <view className={\"np-container\"}>\n <view className={\"np-header\"}>\n <text\n className={\"np-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Total: {networks.length} requests\n </text>\n <view\n className={\"np-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearNetworks}\n >\n <text\n className={\"np-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n {networks.length === 0 ? (\n <view className={\"np-placeholder\"}>\n <text\n className={\"np-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No network requests yet\n </text>\n </view>\n ) : (\n <list scroll-orientation=\"vertical\" className={\"np-list\"}>\n {networks.map((network) => (\n <list-item key={network.id} item-key={network.id}>\n <view\n className={\"np-item\"}\n style={{\n backgroundColor: getItemBg(colors, network.status),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view\n className={\"np-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n <text\n className={\"np-method t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getMethodColors(colors, network.method),\n }}\n >\n {network.method}\n </text>\n {network.statusCode && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getStatusCodeColor(\n colors,\n getStatusCodeVariant(\n network.status,\n network.statusCode,\n ),\n ),\n }}\n >\n {network.statusCode}\n </text>\n )}\n {network.status === \"pending\" && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n Pending...\n </text>\n )}\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {formatDuration(network.duration)}\n </text>\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(network.startTime).toISOString()}\n </text>\n </view>\n\n <text\n className={\"np-path t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n {extractPath(network.url)}\n </text>\n\n {selectedId === network.id && (\n <view\n className={\"np-detailsContainer\"}\n style={{ borderTopColor: colors.stroke.neutralSubtle }}\n >\n {/* Tabs */}\n <view className={\"np-tabs\"}>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"general\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"general\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"general\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n General\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"request\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"request\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"request\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Request\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"response\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"response\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"response\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Response\n </text>\n </view>\n </view>\n\n {/* Tab Content */}\n <view className={\"np-tabContent\"}>\n {activeTab === \"general\" && (\n <view className={\"np-table\"}>\n {getGeneralInfo(network).map((item) => (\n <view\n key={item.key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {item.key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {item.value}\n </text>\n </view>\n ))}\n </view>\n )}\n\n {activeTab === \"request\" && (\n <NetworkDetailSection\n headers={network.requestHeaders}\n body={network.requestBody}\n />\n )}\n\n {activeTab === \"response\" && (\n <NetworkDetailSection\n headers={network.responseHeaders}\n body={network.responseBody}\n error={network.error}\n />\n )}\n </view>\n </view>\n )}\n </view>\n </list-item>\n ))}\n </list>\n )}\n </view>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { PerformanceEntryData } from \"../types\";\nimport \"./PerformancePanel.css\";\n\ninterface PerformancePanelProps {\n performances: PerformanceEntryData[];\n clearPerformances: () => void;\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst isMetricFcpEntry = (entry: PerformanceEntryData): boolean => {\n return entry.entryType === \"metric\" && entry.name === \"fcp\";\n};\n\nconst extractFcpMetrics = (entry: PerformanceEntryData) => {\n if (!isMetricFcpEntry(entry) || !entry.rawEntry) {\n return null;\n }\n\n const metricEntry = entry.rawEntry as MetricFcpEntry;\n\n return {\n totalFcp: metricEntry.totalFcp ?? undefined,\n lynxFcp: metricEntry.lynxFcp ?? undefined,\n fcp: metricEntry.fcp ?? undefined,\n };\n};\n\nconst formatDuration = (ms?: number): string => {\n if (ms === undefined) return \"-\";\n return `${ms.toFixed(2)}ms`;\n};\n\nconst getPrimaryFcpLabel = (entry: PerformanceEntryData): string => {\n const fcpMetrics = extractFcpMetrics(entry);\n if (!fcpMetrics) return \"\";\n\n const { totalFcp, lynxFcp, fcp } = fcpMetrics;\n\n if (totalFcp?.duration !== undefined) {\n return `totalFcp: ${formatDuration(totalFcp.duration)}`;\n }\n if (lynxFcp?.duration !== undefined) {\n return `lynxFcp: ${formatDuration(lynxFcp.duration)}`;\n }\n if (fcp?.duration !== undefined) {\n return `fcp: ${formatDuration(fcp.duration)}`;\n }\n return \"\";\n};\n\nfunction getEntryTypeColors(colors: ThemeColors, entryType: string) {\n switch (entryType) {\n case \"init\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"metric\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"pipeline\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"resource\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nexport const PerformancePanel = ({\n performances,\n clearPerformances,\n}: PerformancePanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n if (performances.length === 0) {\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n 0 entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n <view className={\"pp-placeholder\"}>\n <text\n className={\"pp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No performance data yet...\n </text>\n </view>\n </view>\n );\n }\n\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {performances.length} entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n <list scroll-orientation=\"vertical\" className={\"pp-list\"}>\n {performances.map((perf) => {\n const isMetricFcp = isMetricFcpEntry(perf);\n const fcpMetrics = extractFcpMetrics(perf);\n const primaryFcp = getPrimaryFcpLabel(perf);\n const { totalFcp, lynxFcp, fcp } = fcpMetrics ?? {};\n\n return (\n <list-item key={perf.id} item-key={perf.id}>\n <view\n className={\"pp-item\"}\n style={{ borderBottomColor: colors.stroke.neutralWeak }}\n >\n <view\n className={\"pp-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n <text\n className={\"pp-entryType t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getEntryTypeColors(colors, perf.entryType),\n }}\n >\n {perf.entryType}\n </text>\n <text\n className={\"pp-entryName t2\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {perf.name}\n </text>\n <text\n className={\"pp-timestamp t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(perf.timestamp).toISOString()}\n </text>\n </view>\n\n <view\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n {isMetricFcp && primaryFcp && (\n <text\n className={\"pp-fcpHighlight t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n }}\n >\n {primaryFcp}\n </text>\n )}\n </view>\n\n {selectedId === perf.id && (\n <view className={\"pp-detailsContainer\"}>\n {isMetricFcp && fcpMetrics && (\n <view className={\"pp-fcpSection\"}>\n {totalFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 전체 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(totalFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n PrepareTemplate Start부터 Paint End 까지 걸리는\n 시간\n </text>\n </view>\n )}\n\n {lynxFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n LynxFCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(lynxFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Bundle Load 시작부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n\n {fcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 렌더링 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(fcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n TemplateBundle 준비부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n </view>\n )}\n\n {!!perf.rawEntry && (\n <view\n className={\"pp-rawEntrySection\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-detailTitle t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n Raw Entry\n </text>\n <text\n className={\"pp-rawEntry t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {String(\n stringify(perf.rawEntry, null, 2, {\n references: true,\n }),\n )}\n </text>\n </view>\n )}\n </view>\n )}\n </view>\n </list-item>\n );\n })}\n </list>\n </view>\n );\n};\n","import { type ReactNode, useRef, useState } from \"@lynx-js/react\";\nimport type { ListSnapEvent, NodesRef } from \"@lynx-js/types\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./Tabs.css\";\n\ntype TabsProps = {\n items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }>;\n onTabChange?: () => void;\n};\n\nexport default function Tabs(props: TabsProps) {\n const colors = useThemeColors();\n const tabContentsRef = useRef<NodesRef>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const tabSize =\n props.items.length < 4\n ? undefined\n : `t${Math.max(1, 5 - (props.items.length - 3))}`;\n\n return (\n <view className={\"tabs-root\"}>\n <view\n className={\"tabs-header\"}\n style={{\n boxShadow: `inset 0 -1px 0 0 ${colors.stroke.neutralSubtle}`,\n }}\n >\n {props.items.map((item, i) => (\n <view\n key={item.key}\n className={\"tabs-triggerButton\"}\n bindtap={() => {\n setActiveIndex(i);\n props.onTabChange?.();\n\n tabContentsRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: {\n position: i,\n smooth: true,\n },\n })\n .exec();\n }}\n >\n <text\n className={`tabs-triggerButtonText${tabSize ? ` ${tabSize}` : \"\"}`}\n style={{\n fontWeight: fontWeight.bold,\n color:\n i === activeIndex\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n {item.label}\n </text>\n {i === 0 && (\n <view\n className={\"tabs-triggerIndicator\"}\n style={{ transform: `translateX(${activeIndex * 100}%)` }}\n >\n <view\n className={\"tabs-triggerIndicatorLine\"}\n style={{ backgroundColor: colors.fg.neutral }}\n />\n </view>\n )}\n </view>\n ))}\n </view>\n\n <list\n ref={tabContentsRef}\n className={\"tabs-contents\"}\n scroll-orientation=\"horizontal\"\n item-snap={{ factor: 0, offset: 0 }}\n bindscroll={() => props.onTabChange?.()}\n bindsnap={(e: ListSnapEvent) => {\n setActiveIndex(e.detail.position);\n }}\n bounces={false}\n preload-buffer-count={props.items.length}\n >\n {props.items.map((item) => (\n <list-item\n key={item.key}\n item-key={item.key}\n recyclable={false}\n className={\"tabs-content\"}\n >\n {item.renderContent()}\n </list-item>\n ))}\n </list>\n </view>\n );\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport { useConsole, useNetwork, usePerformance } from \"../hooks\";\nimport type { CustomTab } from \"../types\";\nimport \"./ConsolePanel.css\";\nimport { dismissFilterDropdown, LogPanel } from \"./LogPanel\";\nimport { NetworkPanel } from \"./NetworkPanel\";\nimport { PerformancePanel } from \"./PerformancePanel\";\nimport Tabs from \"./Tabs\";\n\ninterface ConsolePanelProps {\n customTabs?: CustomTab[];\n}\n\nexport const ConsolePanel = ({ customTabs }: ConsolePanelProps) => {\n const { logs, clearLogs } = useConsole();\n const { networks, clearNetworks } = useNetwork();\n const { performances, clearPerformances } = usePerformance();\n\n const state = globalThis.__LYNX_CONSOLE__?.state;\n\n const items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }> = [];\n\n if (state?.logs) {\n items.push({\n key: \"log\",\n label: \"Log\",\n renderContent: () => <LogPanel logs={logs} clearLogs={clearLogs} />,\n });\n }\n\n if (state?.networks) {\n items.push({\n key: \"network\",\n label: \"Network\",\n renderContent: () => (\n <NetworkPanel networks={networks} clearNetworks={clearNetworks} />\n ),\n });\n }\n\n if (state?.performances) {\n items.push({\n key: \"performance\",\n label: \"Perf\",\n renderContent: () => (\n <PerformancePanel\n performances={performances}\n clearPerformances={clearPerformances}\n />\n ),\n });\n }\n\n if (customTabs) {\n for (const tab of customTabs) {\n items.push({\n key: tab.key,\n label: tab.label,\n renderContent: tab.renderContent,\n });\n }\n }\n\n if (items.length === 0) {\n return null;\n }\n\n return (\n <view className=\"cp-container\">\n <Tabs onTabChange={dismissFilterDropdown} items={items} />\n </view>\n );\n};\n","import { useRef, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\n\nconst LONG_PRESS_DURATION = 400;\nconst MOVE_THRESHOLD = 5;\n\nconst DEFAULT_RIGHT = 16;\nconst DEFAULT_BOTTOM = 84;\n\nlet savedRight = DEFAULT_RIGHT;\nlet savedBottom = DEFAULT_BOTTOM;\n\nexport function useLongPressDrag(onTap: () => void) {\n const [right, setRight] = useState(savedRight);\n const [bottom, setBottom] = useState(savedBottom);\n const [phase, setPhase] = useState<\"idle\" | \"dragging\" | \"releasing\">(\"idle\");\n const [tempRight, setTempRight] = useState(savedRight);\n const [tempBottom, setTempBottom] = useState(savedBottom);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const draggingRef = useRef(false);\n const startRef = useRef({ x: 0, y: 0, r: 0, b: 0 });\n\n const clearTimer = () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n startRef.current = {\n x: e.detail.x,\n y: e.detail.y,\n r: right,\n b: bottom,\n };\n draggingRef.current = false;\n\n timerRef.current = setTimeout(() => {\n draggingRef.current = true;\n setPhase(\"dragging\");\n setTempRight(right);\n setTempBottom(bottom);\n }, LONG_PRESS_DURATION);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n const dx = e.detail.x - startRef.current.x;\n const dy = e.detail.y - startRef.current.y;\n\n if (\n !draggingRef.current &&\n (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)\n ) {\n clearTimer();\n }\n\n if (!draggingRef.current) return;\n\n // right/bottom 기준이므로 방향 반전\n setTempRight(startRef.current.r - dx);\n setTempBottom(startRef.current.b - dy);\n };\n\n const handleTouchEnd = () => {\n clearTimer();\n\n if (draggingRef.current) {\n setRight(tempRight);\n setBottom(tempBottom);\n savedRight = tempRight;\n savedBottom = tempBottom;\n setPhase(\"releasing\");\n draggingRef.current = false;\n setTimeout(() => setPhase(\"idle\"), 300);\n } else {\n onTap();\n }\n };\n\n const isDragging = phase === \"dragging\";\n\n return {\n phase,\n right: isDragging ? tempRight : right,\n bottom: isDragging ? tempBottom : bottom,\n clearTimer,\n handlers: {\n catchtouchstart: handleTouchStart,\n catchtouchmove: handleTouchMove,\n catchtouchend: handleTouchEnd,\n },\n };\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport { useLongPressDrag } from \"../hooks/useLongPressDrag\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration } from \"../styles/theme\";\nimport \"./FloatingButton.css\";\n\ninterface FloatingButtonProps {\n bindtap: () => void;\n children: ReactNode;\n}\n\nconst SHINE_STYLES = {\n idle: {\n transform: \"scale(0)\",\n opacity: 0,\n },\n dragging: {\n transform: \"scale(1)\",\n opacity: 1,\n transition: \"transform 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n releasing: {\n transform: \"scale(1)\",\n opacity: 0,\n transition: \"opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n} as const;\n\nexport const FloatingButton = ({ bindtap, children }: FloatingButtonProps) => {\n const colors = useThemeColors();\n const { phase, right, bottom, clearTimer, handlers } =\n useLongPressDrag(bindtap);\n\n const handleReload = () => {\n try {\n lynx.reload({}, () => {\n console.log(\"reloaded!\");\n });\n } catch (e) {\n console.error(\"[LynxConsole] reload failed:\", e);\n }\n };\n\n const isDragging = phase === \"dragging\";\n\n return (\n <view\n className={\"fb-wrapper\"}\n consume-slide-event={[[-180, 180]]}\n style={{\n right: `${right}px`,\n bottom: `${bottom}px`,\n transform: isDragging ? \"scale(1.05)\" : \"scale(1)\",\n transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n {...handlers}\n >\n <view\n className={\"fb-button\"}\n style={{ backgroundColor: colors.palette.green600 }}\n >\n {children}\n <view className={\"fb-shineOverlay\"} style={SHINE_STYLES[phase]} />\n </view>\n <view\n className={\"fb-reloadButton\"}\n style={{ backgroundColor: colors.palette.green600 }}\n catchtouchstart={() => clearTimer()}\n bindtap={handleReload}\n >\n <text\n className={\"fb-reloadIcon\"}\n style={{ color: colors.palette.staticWhite }}\n >\n {\"\\u21BB\"}\n </text>\n </view>\n </view>\n );\n};\n","import {\n type ForwardedRef,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useState,\n} from \"@lynx-js/react\";\nimport BottomSheet from \"./components/BottomSheet.jsx\";\nimport { ConsolePanel } from \"./components/ConsolePanel.jsx\";\nimport \"./components/FloatingButton.css\";\nimport \"./styles/tokens.css\";\nimport { FloatingButton } from \"./components/FloatingButton.jsx\";\nimport { usePerformance } from \"./hooks/usePerformance\";\nimport { ThemeProvider } from \"./styles/ThemeContext\";\nimport { getColors } from \"./styles/theme\";\nimport type { CustomTab } from \"./types\";\n\nexport interface LynxConsoleHandle {\n open: () => void;\n close: () => void;\n isOpen: () => boolean;\n}\n\nexport interface LynxConsoleProps {\n theme?: \"light\" | \"dark\";\n safeAreaInsetBottom?: string;\n customTabs?: CustomTab[];\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst LynxConsole = forwardRef<LynxConsoleHandle, LynxConsoleProps>(\n (\n { theme = \"light\", safeAreaInsetBottom = \"50px\", customTabs },\n ref: ForwardedRef<LynxConsoleHandle>,\n ) => {\n const [isOpen, setIsOpen] = useState(false);\n const [shouldClose, setShouldClose] = useState(false);\n const { performances } = usePerformance();\n const colors = useMemo(() => getColors(theme), [theme]);\n\n const latestFcp = useMemo(() => {\n for (let i = performances.length - 1; i >= 0; i--) {\n const perf = performances[i];\n if (perf && perf.entryType === \"metric\" && perf.name === \"fcp\") {\n const metricEntry = perf.rawEntry as MetricFcpEntry | undefined;\n // totalFcp를 먼저 시도하고, 없으면 lynxFcp 반환\n if (metricEntry?.totalFcp?.duration !== undefined) {\n return metricEntry.totalFcp;\n }\n if (metricEntry?.lynxFcp?.duration !== undefined) {\n return metricEntry.lynxFcp;\n }\n }\n }\n return undefined;\n }, [performances]);\n\n useImperativeHandle(ref, () => ({\n open: () => {\n setIsOpen(true);\n setShouldClose(false);\n },\n close: () => {\n setShouldClose(true);\n },\n isOpen: () => isOpen,\n }));\n\n const handleOpenBottomSheet = () => {\n setIsOpen(true);\n setShouldClose(false);\n };\n\n const handleCloseBottomSheet = () => {\n setIsOpen(false);\n setShouldClose(false);\n };\n\n return (\n <ThemeProvider value={colors}>\n <view\n style={{\n backgroundColor: colors.bg.layerDefault,\n color: colors.fg.neutral,\n }}\n >\n <FloatingButton bindtap={handleOpenBottomSheet}>\n <text\n className=\"fb-title t4\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n LynxConsole\n </text>\n <text\n className=\"fb-subtitle t3\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n {`${latestFcp?.name ?? \"FCP\"}: ${latestFcp?.duration ? latestFcp.duration.toFixed(2) : \"--\"}ms`}\n </text>\n </FloatingButton>\n {isOpen && (\n <BottomSheet\n isOpen={isOpen}\n shouldClose={shouldClose}\n onClose={handleCloseBottomSheet}\n title=\"Lynx Console\"\n safeAreaInsetBottom={safeAreaInsetBottom}\n >\n <ConsolePanel customTabs={customTabs} />\n </BottomSheet>\n )}\n </view>\n </ThemeProvider>\n );\n },\n);\n\nexport type { CustomTab } from \"./types\";\nexport default LynxConsole;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/hooks/useKeyboardHeight.ts","../src/styles/theme.ts","../src/styles/ThemeContext.ts","../src/components/BottomSheet.tsx","../src/hooks/useConsole.ts","../src/hooks/useNetwork.ts","../src/hooks/usePerformance.ts","../src/utils/parseCssStyle.ts","../src/utils/parseFormat.ts","../src/components/LogPanel.tsx","../src/components/NetworkDetailSection.tsx","../src/components/NetworkPanel.tsx","../src/components/PerformancePanel.tsx","../src/components/Tabs.tsx","../src/components/ConsolePanel.tsx","../src/hooks/useLongPressDrag.ts","../src/components/FloatingButton.tsx","../src/index.tsx"],"sourcesContent":["import { useLynxGlobalEventListener, useState } from \"@lynx-js/react\";\n\nexport function useKeyboardHeight() {\n const [keyboardHeight, setKeyboardHeight] = useState(0);\n\n useLynxGlobalEventListener(\n \"keyboardstatuschanged\",\n (status: \"on\" | \"off\", height: number) => {\n setKeyboardHeight(status === \"on\" ? height : 0);\n },\n );\n\n return keyboardHeight;\n}\n","export type Theme = \"light\" | \"dark\";\n\nconst lightColors = {\n palette: {\n blue100: \"#eff6ff\",\n blue600: \"#5e98fe\",\n gray100: \"#f7f8f9\",\n gray400: \"#dcdee3\",\n green100: \"#edfaf6\",\n green600: \"#10ab7d\",\n purple100: \"#f5f3fe\",\n purple600: \"#9f84fb\",\n red100: \"#fdf0f0\",\n red600: \"#fc6a66\",\n red900: \"#921708\",\n staticWhite: \"#ffffff\",\n yellow100: \"#fff7de\",\n yellow600: \"#c49725\",\n yellow900: \"#4f3e1f\",\n },\n fg: {\n neutral: \"#1a1c20\",\n placeholder: \"#b0b3ba\",\n disabled: \"#d1d3d8\",\n neutralMuted: \"#555d6d\",\n neutralSubtle: \"#868b94\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#ffffff\",\n layerFloating: \"#ffffff\",\n neutralWeak: \"#f3f4f5\",\n },\n stroke: {\n neutralSubtle: \"#0000000c\",\n neutralWeak: \"#dcdee3\",\n },\n} as const;\n\nconst darkColors = {\n palette: {\n blue100: \"#202742\",\n blue600: \"#1e82eb\",\n gray100: \"#16171b\",\n gray400: \"#393d46\",\n green100: \"#202926\",\n green600: \"#1b946d\",\n purple100: \"#28213b\",\n purple600: \"#8e6bee\",\n red100: \"#322323\",\n red600: \"#f73526\",\n red900: \"#f8c5c3\",\n staticWhite: \"#ffffff\",\n yellow100: \"#302819\",\n yellow600: \"#b6720d\",\n yellow900: \"#e5d49b\",\n },\n fg: {\n neutral: \"#f3f4f5\",\n placeholder: \"#868b94\",\n disabled: \"#5b606a\",\n neutralMuted: \"#dcdee3\",\n neutralSubtle: \"#b0b3ba\",\n },\n bg: {\n overlay: \"#00000074\",\n layerDefault: \"#16171b\",\n layerFloating: \"#1d2025\",\n neutralWeak: \"#2b2e35\",\n },\n stroke: {\n neutralSubtle: \"#ffffff0d\",\n neutralWeak: \"#393d46\",\n },\n} as const;\n\nconst colorMap = {\n light: lightColors,\n dark: darkColors,\n} as const;\n\nexport const fontWeight = {\n regular: \"400\",\n medium: \"500\",\n bold: \"700\",\n} as const;\n\nexport const duration = {\n d4: \"200ms\",\n d6: \"300ms\",\n} as const;\n\nexport function getColors(theme: Theme) {\n return colorMap[theme];\n}\n\nexport type ThemeColors = ReturnType<typeof getColors>;\n","import { createContext, useContext } from \"@lynx-js/react\";\nimport { getColors, type ThemeColors } from \"./theme\";\n\nconst ThemeContext = createContext<ThemeColors>(getColors(\"light\"));\n\nexport const ThemeProvider = ThemeContext.Provider;\n\nexport function useThemeColors(): ThemeColors {\n return useContext(ThemeContext);\n}\n","import { type ReactNode, useEffect, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\nimport { useKeyboardHeight } from \"../hooks/useKeyboardHeight\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration, fontWeight } from \"../styles/theme\";\nimport \"./BottomSheet.css\";\n\ninterface BottomSheetProps {\n children: ReactNode;\n title?: string;\n footer?: ReactNode;\n onClose: () => void;\n isOpen: boolean;\n shouldClose?: boolean;\n safeAreaInsetBottom?: string;\n}\n\nconst MIN_HEIGHT = 200;\nconst MAX_HEIGHT = 700;\nconst DEFAULT_HEIGHT = 500;\nconst CLOSE_DRAG_THRESHOLD = 30; // 30px 이상 아래로 드래그하면 닫힘\n\n// 마지막 높이 저장\nlet savedHeight: number | null = null;\n\nexport default function BottomSheet({\n children,\n title,\n footer,\n onClose,\n isOpen,\n shouldClose = false,\n safeAreaInsetBottom = \"25px\",\n}: BottomSheetProps) {\n const colors = useThemeColors();\n const [sheetHeight, setSheetHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [tempHeight, setTempHeight] = useState(savedHeight ?? DEFAULT_HEIGHT);\n const [isDragging, setIsDragging] = useState(false);\n const [dragStartY, setDragStartY] = useState(0);\n const [dragStartHeight, setDragStartHeight] = useState(\n savedHeight ?? DEFAULT_HEIGHT,\n );\n const [isOpening, setIsOpening] = useState(true);\n const [isClosing, setIsClosing] = useState(false);\n const keyboardHeight = useKeyboardHeight();\n\n // 닫기 애니메이션 처리\n const handleClose = () => {\n setIsClosing(true);\n setTimeout(() => {\n onClose();\n }, 300);\n };\n\n // 아래에서 올라오는 애니메이션\n useEffect(() => {\n requestAnimationFrame(() => {\n setIsOpening(false);\n });\n }, []);\n\n // 외부에서 닫기 요청 시 애니메이션 처리\n useEffect(() => {\n if (shouldClose && !isClosing) {\n handleClose();\n }\n }, [shouldClose, isClosing]);\n\n // 높이 변경 시 저장\n useEffect(() => {\n savedHeight = sheetHeight;\n }, [sheetHeight]);\n\n if (!isOpen) return null;\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n setIsDragging(true);\n setDragStartY(e.detail.y);\n setDragStartHeight(sheetHeight);\n setTempHeight(sheetHeight);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n if (!isDragging) return;\n const deltaY = dragStartY - e.detail.y;\n const newHeight = Math.min(\n Math.max(dragStartHeight + deltaY, MIN_HEIGHT),\n MAX_HEIGHT,\n );\n setTempHeight(newHeight);\n };\n\n const handleTouchEnd = () => {\n setIsDragging(false);\n\n // 아래로 일정 30px 이상 드래그하면 닫기\n const dragDistance = dragStartHeight - tempHeight;\n setSheetHeight(tempHeight);\n if (dragDistance > CLOSE_DRAG_THRESHOLD) {\n handleClose();\n }\n };\n\n return (\n <scroll-view\n className=\"bs-backdrop\"\n style={{\n background: colors.bg.overlay,\n opacity: isOpening || isClosing ? 0 : 1,\n transition: `opacity ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n <view className=\"bs-overlay\" bindtap={handleClose}>\n <view\n className=\"bs-content\"\n catchtap={() => {}}\n style={{\n background: colors.bg.layerFloating,\n height: `${\n keyboardHeight > 0\n ? Math.min(\n MAX_HEIGHT,\n (isDragging ? tempHeight : sheetHeight) + keyboardHeight,\n )\n : isDragging\n ? tempHeight\n : sheetHeight\n }px`,\n transform:\n isOpening || isClosing ? \"translateY(100%)\" : \"translateY(0)\",\n transition: isDragging\n ? \"none\"\n : `transform ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1), height ${duration.d6} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n >\n {/* catchtap: 이벤트 버블링 차단 */}\n <view\n className=\"bs-handleContainer\"\n bindtouchstart={handleTouchStart}\n bindtouchmove={handleTouchMove}\n bindtouchend={handleTouchEnd}\n >\n <view\n className=\"bs-handle\"\n style={{ backgroundColor: colors.palette.gray400 }}\n />\n </view>\n <view className=\"bs-header\">\n {title && (\n <text\n className=\"bs-title t7\"\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n {title}\n </text>\n )}\n </view>\n <view\n className=\"bs-body\"\n style={{\n paddingBottom:\n keyboardHeight > 0\n ? `${keyboardHeight}px`\n : safeAreaInsetBottom,\n }}\n >\n {children}\n </view>\n {footer && <view className=\"bs-footer\">{footer}</view>}\n </view>\n </view>\n </scroll-view>\n );\n}\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { LogEntry } from \"../types\";\n\nexport const useConsole = () => {\n const [logs, setLogs] = useState<LogEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs === \"undefined\") {\n console.warn(\"[LynxConsole] Log monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setLogs([...(state.logs ?? [])]);\n\n const updateLogs = (_entry: LogEntry) => {\n setLogs([...(state.logs ?? [])]);\n };\n\n const unsubscribe = state.logSubscribe?.(updateLogs);\n\n return unsubscribe;\n }, []);\n\n const clearLogs = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.logs !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.logs = [];\n setLogs([]);\n }\n };\n\n return { logs, clearLogs };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { NetworkEntry } from \"../types\";\n\nexport const useNetwork = () => {\n const [networks, setNetworks] = useState<NetworkEntry[]>([]);\n\n useEffect(() => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks === \"undefined\") {\n console.warn(\"[LynxConsole] Network monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setNetworks([...(state.networks ?? [])]);\n\n const updateNetworks = (_entry: NetworkEntry) => {\n setNetworks([...(state.networks ?? [])]);\n };\n\n const unsubscribe = state.subscribeNetwork?.(updateNetworks);\n\n return unsubscribe;\n }, []);\n\n const clearNetworks = () => {\n if (typeof globalThis.__LYNX_CONSOLE__?.state?.networks !== \"undefined\") {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.networks = [];\n state.networksMap?.clear();\n setNetworks([]);\n }\n };\n\n return { networks, clearNetworks };\n};\n","import { useEffect, useState } from \"@lynx-js/react\";\nimport type { PerformanceEntryData } from \"../types\";\n\nexport const usePerformance = () => {\n const [performances, setPerformances] = useState<PerformanceEntryData[]>([]);\n\n useEffect(() => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances === \"undefined\"\n ) {\n console.warn(\"[LynxConsole] Performance monitoring not initialized\");\n return;\n }\n\n const state = globalThis.__LYNX_CONSOLE__.state;\n\n setPerformances([...(state.performances ?? [])]);\n\n const updatePerformances = (_entry: PerformanceEntryData) => {\n setPerformances([...(state.performances ?? [])]);\n };\n\n const unsubscribe = state.subscribePerformance?.(updatePerformances);\n\n return unsubscribe;\n }, []);\n\n const clearPerformances = () => {\n if (\n typeof globalThis.__LYNX_CONSOLE__?.state?.performances !== \"undefined\"\n ) {\n const state = globalThis.__LYNX_CONSOLE__.state;\n state.performances = [];\n setPerformances([]);\n }\n };\n\n return { performances, clearPerformances };\n};\n","export type StyleObject = Record<string, string>;\n\nconst DANGEROUS_VALUE = /url\\s*\\(|expression\\s*\\(|@import/i;\n\nconst toCamelCase = (name: string): string => {\n if (name.startsWith(\"--\")) return name;\n return name.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n};\n\nexport const parseCssString = (css: string): StyleObject => {\n const result: StyleObject = {};\n for (const raw of css.split(\";\")) {\n const decl = raw.trim();\n if (!decl) continue;\n const colon = decl.indexOf(\":\");\n if (colon <= 0) continue;\n const name = decl.slice(0, colon).trim().toLowerCase();\n const value = decl.slice(colon + 1).trim();\n if (!name || !value) continue;\n if (DANGEROUS_VALUE.test(value)) continue;\n result[toCamelCase(name)] = value;\n }\n return result;\n};\n","import { parseCssString, type StyleObject } from \"./parseCssStyle\";\n\nexport type LogSegment =\n | { type: \"text\"; text: string; style?: StyleObject }\n | { type: \"arg\"; value: unknown };\n\nexport interface ParsedLogArgs {\n segments: LogSegment[];\n rest: unknown[];\n}\n\nconst HAS_FORMAT = /%[csdifoO%]/;\n\nconst formatNumber = (v: unknown, int: boolean): string => {\n const n =\n typeof v === \"number\"\n ? int\n ? Math.trunc(v)\n : v\n : typeof v === \"symbol\"\n ? Number.NaN\n : int\n ? parseInt(String(v), 10)\n : parseFloat(String(v));\n return Number.isNaN(n) ? \"NaN\" : String(n);\n};\n\nexport const parseConsoleArgs = (args: unknown[]): ParsedLogArgs => {\n const first = args[0];\n if (typeof first !== \"string\" || !HAS_FORMAT.test(first)) {\n return { segments: [], rest: args };\n }\n\n const segments: LogSegment[] = [];\n let currentText = \"\";\n let currentStyle: StyleObject | undefined;\n let argIndex = 1;\n let lastIndex = 0;\n\n const flushText = () => {\n if (currentText) {\n segments.push({ type: \"text\", text: currentText, style: currentStyle });\n currentText = \"\";\n }\n };\n\n const re = /%([csdifoO%])/g;\n let match: RegExpExecArray | null = re.exec(first);\n while (match !== null) {\n currentText += first.slice(lastIndex, match.index);\n lastIndex = re.lastIndex;\n const spec = match[1];\n\n if (spec === \"%\") {\n currentText += \"%\";\n } else if (argIndex >= args.length) {\n currentText += match[0];\n } else {\n const arg = args[argIndex++];\n switch (spec) {\n case \"c\":\n flushText();\n currentStyle =\n typeof arg === \"string\" ? parseCssString(arg) : undefined;\n break;\n case \"s\":\n currentText += String(arg);\n break;\n case \"d\":\n case \"i\":\n currentText += formatNumber(arg, true);\n break;\n case \"f\":\n currentText += formatNumber(arg, false);\n break;\n case \"o\":\n case \"O\":\n flushText();\n segments.push({ type: \"arg\", value: arg });\n break;\n }\n }\n\n match = re.exec(first);\n }\n currentText += first.slice(lastIndex);\n flushText();\n\n return { segments, rest: args.slice(argIndex) };\n};\n","import { useEffect, useMemo, useRef, useState } from \"@lynx-js/react\";\nimport type { BaseEvent, InputInputEvent, NodesRef } from \"@lynx-js/types\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { LogEntry, LogLevel } from \"../types\";\nimport { parseConsoleArgs } from \"../utils/parseFormat\";\nimport \"./ConsolePanel.css\";\n\nconst LOG_LEVELS: LogLevel[] = [\"log\", \"info\", \"warn\", \"error\"];\n\nlet savedEnabledLevels: Set<LogLevel> | null = null;\nlet savedSearchQuery = \"\";\nlet closeFilterDropdown: (() => void) | null = null;\n\nexport const dismissFilterDropdown = () => closeFilterDropdown?.();\n\ninterface LogPanelProps {\n logs: LogEntry[];\n clearLogs: () => void;\n}\n\nconst runCode = (code: string) => {\n try {\n // biome-ignore lint: intentional REPL tool\n const result = eval(code);\n if (result instanceof Promise) {\n result.then((r) => console.log(r)).catch((e) => console.error(e));\n } else {\n console.log(result);\n }\n } catch (e) {\n console.error(e);\n }\n};\n\nfunction getLevelColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"log\":\n return colors.palette.green600;\n case \"info\":\n return colors.palette.blue600;\n case \"warn\":\n return colors.palette.yellow600;\n case \"error\":\n return colors.palette.red600;\n }\n}\n\nfunction getLogItemBg(\n colors: ThemeColors,\n level: LogLevel,\n): string | undefined {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nfunction getStringColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.fg.neutral;\n }\n}\n\nfunction getPrimitiveColor(colors: ThemeColors, level: LogLevel): string {\n switch (level) {\n case \"warn\":\n return colors.palette.yellow900;\n case \"error\":\n return colors.palette.red900;\n default:\n return colors.palette.blue600;\n }\n}\n\nexport const LogPanel = ({ logs, clearLogs }: LogPanelProps) => {\n const colors = useThemeColors();\n const [expandedArgs, setExpandedArgs] = useState(new Set());\n const [code, setCode] = useState(\"\");\n const [enabledLevels, setEnabledLevels] = useState<Set<LogLevel>>(\n () => savedEnabledLevels ?? new Set(LOG_LEVELS),\n );\n const [filterOpen, setFilterOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState(savedSearchQuery);\n const inputRef = useRef<NodesRef>(null);\n const searchInputRef = useRef<NodesRef>(null);\n const listRef = useRef<NodesRef>(null);\n\n useEffect(() => {\n savedEnabledLevels = enabledLevels;\n }, [enabledLevels]);\n\n useEffect(() => {\n savedSearchQuery = searchQuery;\n }, [searchQuery]);\n\n useEffect(() => {\n if (savedSearchQuery) {\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: savedSearchQuery } })\n .exec();\n }\n }, []);\n\n useEffect(() => {\n closeFilterDropdown = () => setFilterOpen(false);\n return () => {\n closeFilterDropdown = null;\n };\n }, []);\n\n const filteredLogs = useMemo(\n () =>\n logs.filter((log) => {\n if (!enabledLevels.has(log.level)) return false;\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n return log.args.some((arg) =>\n String(arg).toLowerCase().includes(query),\n );\n }\n return true;\n }),\n [logs, enabledLevels, searchQuery],\n );\n const logsRef = useRef(filteredLogs);\n logsRef.current = filteredLogs;\n\n const toggleLevel = (level: LogLevel) => {\n setEnabledLevels((prev) => {\n const next = new Set(prev);\n if (next.has(level)) {\n next.delete(level);\n } else {\n next.add(level);\n }\n return next;\n });\n };\n\n const scrollToBottom = (smooth: boolean) => {\n if (logsRef.current.length === 0) return;\n listRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: { position: logsRef.current.length - 1, smooth },\n })\n .exec();\n };\n\n useEffect(() => {\n scrollToBottom(true);\n }, [filteredLogs]);\n\n const toggleArg = (key: string) => {\n setExpandedArgs((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n };\n\n const handleRun = () => {\n const trimmed = code.trim();\n if (!trimmed) return;\n\n setCode(\"\");\n inputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n runCode(trimmed);\n };\n\n const renderArg = (\n arg: unknown,\n parentKey: string,\n level: \"log\" | \"info\" | \"warn\" | \"error\",\n ): React.ReactNode => {\n const key = parentKey;\n const isExpanded = expandedArgs.has(key);\n\n if (arg === null) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n null\n </text>\n );\n }\n\n if (arg === undefined) {\n return (\n <text\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n undefined\n </text>\n );\n }\n\n if (typeof arg === \"string\") {\n const MAX_LENGTH = 80;\n const shouldTruncate = arg.length > MAX_LENGTH;\n const strColor = getStringColor(colors, level);\n\n if (!shouldTruncate) {\n return (\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {arg}\n </text>\n );\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argString t3\"}\n style={{ color: strColor, fontWeight: fontWeight.regular }}\n >\n {isExpanded ? arg : `${arg.slice(0, MAX_LENGTH)}...`}\n </text>\n </view>\n </view>\n );\n }\n\n if (typeof arg === \"number\" || typeof arg === \"boolean\") {\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n }\n\n if (typeof arg === \"object\") {\n let preview = \"Object\";\n if (Array.isArray(arg)) {\n preview = `Array(${arg.length})`;\n } else if (arg instanceof Map) {\n preview = `Map(${arg.size})`;\n } else if (arg instanceof Set) {\n preview = `Set(${arg.size})`;\n } else if (arg instanceof Date) {\n preview = `Date`;\n } else if (arg instanceof RegExp) {\n preview = `RegExp`;\n } else if (arg instanceof Error) {\n preview = `${arg.constructor.name}`;\n } else if (arg?.constructor?.name && arg.constructor.name !== \"Object\") {\n preview = arg.constructor.name;\n }\n\n let jsonString: string;\n if (arg instanceof Map) {\n const entries = Array.from(arg.entries()).map(\n ([k, v]) => ` [${stringify(k)}, ${stringify(v)}]`,\n );\n jsonString = `{\\n${entries.join(\",\\n\")}\\n}`;\n } else if (arg instanceof Set) {\n const values = Array.from(arg.values()).map((v) => stringify(v));\n jsonString = `{\\n${values.join(\", \")}\\n}`;\n } else {\n jsonString =\n stringify(arg, null, 2, { references: true }) ?? String(arg);\n }\n\n return (\n <view className={\"cp-argObject\"}>\n <view className={\"cp-argObjectHeader\"} bindtap={() => toggleArg(key)}>\n <text\n className={\"cp-toggleIndicator t2\"}\n style={{\n color: colors.fg.neutralSubtle,\n fontWeight: fontWeight.regular,\n }}\n >\n {isExpanded ? \"▼\" : \"▶\"}\n </text>\n <text\n className={\"cp-argObjectPreview t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {preview}\n </text>\n </view>\n {isExpanded && (\n <view className={\"cp-argObjectContent\"}>\n <text\n className={\"cp-argObjectJson t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {jsonString}\n </text>\n </view>\n )}\n </view>\n );\n }\n\n return (\n <text\n className={\"cp-argPrimitive t3\"}\n style={{\n color: getPrimitiveColor(colors, level),\n fontWeight: fontWeight.regular,\n }}\n >\n {String(arg)}\n </text>\n );\n };\n\n return (\n <view\n className={\"cp-logContainer\"}\n bindtap={() => {\n if (filterOpen) setFilterOpen(false);\n }}\n >\n <view className={\"cp-logHeader\"}>\n <view className={\"cp-filterWrapper\"}>\n <view\n className={\"cp-filterButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n catchtap={() => setFilterOpen((v) => !v)}\n >\n <text\n className={\"cp-filterButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n Filter ▼\n </text>\n </view>\n {filterOpen && (\n <view\n className={\"cp-filterDropdown\"}\n style={{\n backgroundColor: colors.bg.layerFloating,\n borderColor: colors.stroke.neutralSubtle,\n }}\n catchtap={() => {}}\n >\n {LOG_LEVELS.map((level) => (\n <view\n key={level}\n className={\"cp-filterOption\"}\n bindtap={() => toggleLevel(level)}\n >\n <text\n className={\"cp-filterCheckbox t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {enabledLevels.has(level) ? \"✅\" : \"⬜\"}\n </text>\n <text\n className={\"cp-filterLabel t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: getLevelColor(colors, level),\n }}\n >\n {level.toUpperCase()}\n </text>\n </view>\n ))}\n </view>\n )}\n </view>\n <view\n className={\"cp-searchWrapper\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"cp-searchPrompt t6\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={searchInputRef}\n className={\"cp-searchInput t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"Search logs...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setSearchQuery(e.detail.value)\n }\n />\n {searchQuery.length > 0 && (\n <view\n className={\"cp-searchClear\"}\n bindtap={() => {\n setSearchQuery(\"\");\n searchInputRef.current\n ?.invoke({ method: \"setValue\", params: { value: \"\" } })\n .exec();\n }}\n >\n <text\n className={\"cp-searchClearText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n ✕\n </text>\n </view>\n )}\n </view>\n <view style={{ display: \"flex\", flexDirection: \"row\", gap: 8 }}>\n <view\n className={\"cp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearLogs}\n >\n <text\n className={\"cp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n </view>\n <list\n ref={listRef}\n scroll-orientation=\"vertical\"\n className={\"cp-logList\"}\n preload-buffer-count={10}\n initial-scroll-index={Math.max(0, filteredLogs.length - 1)}\n >\n {filteredLogs.length === 0 ? (\n <list-item item-key=\"empty-state\">\n <view className={\"cp-placeholder\"}>\n <text\n className={\"cp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No logs yet. Try console.log(\"Hello!\")\n </text>\n </view>\n </list-item>\n ) : (\n filteredLogs.map((log) => {\n return (\n <list-item key={log.id} item-key={log.id}>\n <view\n className={\"cp-logItem\"}\n style={{\n backgroundColor: getLogItemBg(colors, log.level),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view className={\"cp-logItemHeader\"}>\n <text\n className={\"cp-logLevel t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getLevelColor(colors, log.level),\n }}\n >\n {log.level.toUpperCase()}\n </text>\n <text\n className={\"cp-logTime t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(log.timestamp).toISOString()}\n </text>\n </view>\n <view className={\"cp-logArgsContainer\"}>\n {(() => {\n const { segments, rest } = parseConsoleArgs(log.args);\n const baseTextStyle = {\n color: getStringColor(colors, log.level),\n fontWeight: fontWeight.regular,\n };\n const wrap = (key: string, content: React.ReactNode) => (\n <view\n key={key}\n className={\"cp-logArgItem\"}\n style={{ fontWeight: fontWeight.regular }}\n >\n {content}\n </view>\n );\n return (\n <>\n {segments.map((seg, index) => {\n const key = `${log.id}-seg-${index.toString()}`;\n return wrap(\n key,\n seg.type === \"text\" ? (\n <text\n className={\"cp-argString t3\"}\n style={{ ...baseTextStyle, ...seg.style }}\n >\n {seg.text}\n </text>\n ) : (\n renderArg(seg.value, key, log.level)\n ),\n );\n })}\n {rest.map((arg, index) => {\n const key = `${log.id}-rest-${index.toString()}`;\n return wrap(key, renderArg(arg, key, log.level));\n })}\n </>\n );\n })()}\n </view>\n </view>\n </list-item>\n );\n })\n )}\n </list>\n <view className={\"cp-replInputRow\"}>\n <text\n className={\"cp-replPrompt t10\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.placeholder,\n }}\n >\n {\"›\"}\n </text>\n <input\n ref={inputRef}\n className={\"cp-replInput t5\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n caretColor: colors.palette.green600,\n }}\n placeholder=\"enter code...\"\n bindinput={(e: BaseEvent<\"bindinput\", InputInputEvent>) =>\n setCode(e.detail.value)\n }\n bindconfirm={handleRun}\n />\n <view\n className={\"cp-replRunButton\"}\n style={{ backgroundColor: colors.palette.green100 }}\n bindtap={handleRun}\n >\n <text\n className={\"cp-replRunButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.palette.green600,\n }}\n >\n Run\n </text>\n </view>\n </view>\n </view>\n );\n};\n","import { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkDetailSectionProps {\n headers?: Record<string, string> | undefined;\n body?: string | undefined;\n error?: string | undefined;\n}\n\nexport const NetworkDetailSection = ({\n headers = {},\n body = \"\",\n error = \"\",\n}: NetworkDetailSectionProps) => {\n const colors = useThemeColors();\n\n return (\n <>\n {/* Headers */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Headers\n </text>\n {headers && Object.keys(headers).length > 0 ? (\n <view className={\"np-table\"}>\n {Object.entries(headers).map(([key, value]) => (\n <view\n key={key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {value}\n </text>\n </view>\n ))}\n </view>\n ) : (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No headers\n </text>\n )}\n </view>\n\n {/* Body */}\n <view className={\"np-detailSection\"}>\n <text\n className={\"np-detailSectionTitle t3\"}\n style={{ fontWeight: fontWeight.bold, color: colors.fg.neutral }}\n >\n Body\n </text>\n {error && (\n <text\n className={\"np-errorText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n }}\n >\n {error}\n </text>\n )}\n {body && (\n <text\n className={\"np-bodyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n }}\n >\n {body}\n </text>\n )}\n {!error && !body && (\n <text\n className={\"np-emptyText t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No body\n </text>\n )}\n </view>\n </>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { NetworkEntry } from \"../types\";\nimport { NetworkDetailSection } from \"./NetworkDetailSection\";\nimport \"./NetworkPanel.css\";\n\ninterface NetworkPanelProps {\n networks: NetworkEntry[];\n clearNetworks: () => void;\n}\n\ntype TabType = \"general\" | \"request\" | \"response\";\n\nfunction getMethodColors(colors: ThemeColors, method: string) {\n switch (method) {\n case \"GET\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"POST\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"PUT\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n case \"PATCH\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"DELETE\":\n return {\n color: colors.palette.red600,\n backgroundColor: colors.palette.red100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nfunction getStatusCodeColor(\n colors: ThemeColors,\n variant: \"success\" | \"error\" | \"pending\",\n): string {\n switch (variant) {\n case \"success\":\n return colors.palette.green600;\n case \"error\":\n return colors.palette.red600;\n case \"pending\":\n return colors.fg.neutralSubtle;\n }\n}\n\nfunction getItemBg(colors: ThemeColors, status: string): string | undefined {\n switch (status) {\n case \"pending\":\n return colors.palette.gray100;\n case \"error\":\n return colors.palette.red100;\n default:\n return undefined;\n }\n}\n\nexport const NetworkPanel = ({\n networks,\n clearNetworks,\n}: NetworkPanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [activeTab, setActiveTab] = useState<TabType>(\"general\");\n const formatDuration = (duration?: number): string => {\n if (!duration) return \"-\";\n if (duration < 1000) return `${duration}ms`;\n return `${(duration / 1000).toFixed(2)}s`;\n };\n\n const extractPath = (url: string): string => {\n const pathMatch = url.match(/^https?:\\/\\/[^/]+(.*)$/);\n if (pathMatch?.[1]) {\n return pathMatch[1].startsWith(\"/\")\n ? pathMatch[1].slice(1)\n : pathMatch[1];\n }\n return url;\n };\n\n const getGeneralInfo = (network: NetworkEntry) => {\n return [\n { key: \"URL\", value: network.url },\n { key: \"Method\", value: network.method },\n network.statusCode\n ? { key: \"Status\", value: String(network.statusCode) }\n : null,\n {\n key: \"Request Time\",\n value: new Date(network.startTime).toISOString(),\n },\n network.endTime\n ? {\n key: \"Response Time\",\n value: new Date(network.endTime).toISOString(),\n }\n : null,\n network.duration\n ? { key: \"Duration\", value: formatDuration(network.duration) }\n : null,\n ].filter((item) => item !== null);\n };\n\n const getStatusCodeVariant = (\n status: string,\n statusCode?: number,\n ): \"success\" | \"error\" | \"pending\" => {\n if (status === \"pending\") return \"pending\";\n if (status === \"error\") return \"error\";\n if (statusCode && statusCode >= 200 && statusCode < 300) return \"success\";\n return \"error\";\n };\n\n return (\n <view className={\"np-container\"}>\n <view className={\"np-header\"}>\n <text\n className={\"np-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Total: {networks.length} requests\n </text>\n <view\n className={\"np-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n bindtap={clearNetworks}\n >\n <text\n className={\"np-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n {networks.length === 0 ? (\n <view className={\"np-placeholder\"}>\n <text\n className={\"np-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No network requests yet\n </text>\n </view>\n ) : (\n <list scroll-orientation=\"vertical\" className={\"np-list\"}>\n {networks.map((network) => (\n <list-item key={network.id} item-key={network.id}>\n <view\n className={\"np-item\"}\n style={{\n backgroundColor: getItemBg(colors, network.status),\n borderBottomColor: colors.stroke.neutralWeak,\n }}\n >\n <view\n className={\"np-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n <text\n className={\"np-method t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getMethodColors(colors, network.method),\n }}\n >\n {network.method}\n </text>\n {network.statusCode && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: getStatusCodeColor(\n colors,\n getStatusCodeVariant(\n network.status,\n network.statusCode,\n ),\n ),\n }}\n >\n {network.statusCode}\n </text>\n )}\n {network.status === \"pending\" && (\n <text\n className={\"np-statusCode t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n Pending...\n </text>\n )}\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {formatDuration(network.duration)}\n </text>\n <text\n className={\"np-time t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(network.startTime).toISOString()}\n </text>\n </view>\n\n <text\n className={\"np-path t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n bindtap={() =>\n setSelectedId(selectedId === network.id ? null : network.id)\n }\n >\n {extractPath(network.url)}\n </text>\n\n {selectedId === network.id && (\n <view\n className={\"np-detailsContainer\"}\n style={{ borderTopColor: colors.stroke.neutralSubtle }}\n >\n {/* Tabs */}\n <view className={\"np-tabs\"}>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"general\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"general\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"general\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n General\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"request\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"request\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"request\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Request\n </text>\n </view>\n <view\n className={\"np-tab\"}\n style={{\n backgroundColor:\n activeTab === \"response\"\n ? colors.bg.neutralWeak\n : undefined,\n }}\n bindtap={() => setActiveTab(\"response\")}\n >\n <text\n className={\"np-tabText t4\"}\n style={{\n fontWeight: fontWeight.medium,\n color:\n activeTab === \"response\"\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n Response\n </text>\n </view>\n </view>\n\n {/* Tab Content */}\n <view className={\"np-tabContent\"}>\n {activeTab === \"general\" && (\n <view className={\"np-table\"}>\n {getGeneralInfo(network).map((item) => (\n <view\n key={item.key}\n className={\"np-tableRow\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"np-tableKey t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutralSubtle,\n }}\n >\n {item.key}\n </text>\n <text\n className={\"np-tableValue t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutral,\n }}\n >\n {item.value}\n </text>\n </view>\n ))}\n </view>\n )}\n\n {activeTab === \"request\" && (\n <NetworkDetailSection\n headers={network.requestHeaders}\n body={network.requestBody}\n />\n )}\n\n {activeTab === \"response\" && (\n <NetworkDetailSection\n headers={network.responseHeaders}\n body={network.responseBody}\n error={network.error}\n />\n )}\n </view>\n </view>\n )}\n </view>\n </list-item>\n ))}\n </list>\n )}\n </view>\n );\n};\n","import { useState } from \"@lynx-js/react\";\nimport { stringify } from \"javascript-stringify\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight, type ThemeColors } from \"../styles/theme\";\nimport type { PerformanceEntryData } from \"../types\";\nimport \"./PerformancePanel.css\";\n\ninterface PerformancePanelProps {\n performances: PerformanceEntryData[];\n clearPerformances: () => void;\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst isMetricFcpEntry = (entry: PerformanceEntryData): boolean => {\n return entry.entryType === \"metric\" && entry.name === \"fcp\";\n};\n\nconst extractFcpMetrics = (entry: PerformanceEntryData) => {\n if (!isMetricFcpEntry(entry) || !entry.rawEntry) {\n return null;\n }\n\n const metricEntry = entry.rawEntry as MetricFcpEntry;\n\n return {\n totalFcp: metricEntry.totalFcp ?? undefined,\n lynxFcp: metricEntry.lynxFcp ?? undefined,\n fcp: metricEntry.fcp ?? undefined,\n };\n};\n\nconst formatDuration = (ms?: number): string => {\n if (ms === undefined) return \"-\";\n return `${ms.toFixed(2)}ms`;\n};\n\nconst getPrimaryFcpLabel = (entry: PerformanceEntryData): string => {\n const fcpMetrics = extractFcpMetrics(entry);\n if (!fcpMetrics) return \"\";\n\n const { totalFcp, lynxFcp, fcp } = fcpMetrics;\n\n if (totalFcp?.duration !== undefined) {\n return `totalFcp: ${formatDuration(totalFcp.duration)}`;\n }\n if (lynxFcp?.duration !== undefined) {\n return `lynxFcp: ${formatDuration(lynxFcp.duration)}`;\n }\n if (fcp?.duration !== undefined) {\n return `fcp: ${formatDuration(fcp.duration)}`;\n }\n return \"\";\n};\n\nfunction getEntryTypeColors(colors: ThemeColors, entryType: string) {\n switch (entryType) {\n case \"init\":\n return {\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n };\n case \"metric\":\n return {\n color: colors.palette.green600,\n backgroundColor: colors.palette.green100,\n };\n case \"pipeline\":\n return {\n color: colors.palette.purple600,\n backgroundColor: colors.palette.purple100,\n };\n case \"resource\":\n return {\n color: colors.palette.yellow600,\n backgroundColor: colors.palette.yellow100,\n };\n default:\n return {\n color: colors.fg.neutral,\n backgroundColor: colors.bg.neutralWeak,\n };\n }\n}\n\nexport const PerformancePanel = ({\n performances,\n clearPerformances,\n}: PerformancePanelProps) => {\n const colors = useThemeColors();\n const [selectedId, setSelectedId] = useState<string | null>(null);\n if (performances.length === 0) {\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n 0 entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n <view className={\"pp-placeholder\"}>\n <text\n className={\"pp-placeholderText t4\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.disabled,\n }}\n >\n No performance data yet...\n </text>\n </view>\n </view>\n );\n }\n\n return (\n <view className={\"pp-container\"}>\n <view\n className={\"pp-header\"}\n style={{ borderBottomColor: colors.stroke.neutralSubtle }}\n >\n <text\n className={\"pp-count t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {performances.length} entries\n </text>\n <view\n bindtap={clearPerformances}\n className={\"pp-clearButton\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-clearButtonText t3\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutralMuted,\n }}\n >\n 🗑\n </text>\n </view>\n </view>\n\n <list scroll-orientation=\"vertical\" className={\"pp-list\"}>\n {performances.map((perf) => {\n const isMetricFcp = isMetricFcpEntry(perf);\n const fcpMetrics = extractFcpMetrics(perf);\n const primaryFcp = getPrimaryFcpLabel(perf);\n const { totalFcp, lynxFcp, fcp } = fcpMetrics ?? {};\n\n return (\n <list-item key={perf.id} item-key={perf.id}>\n <view\n className={\"pp-item\"}\n style={{ borderBottomColor: colors.stroke.neutralWeak }}\n >\n <view\n className={\"pp-itemHeader\"}\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n <text\n className={\"pp-entryType t2\"}\n style={{\n fontWeight: fontWeight.bold,\n ...getEntryTypeColors(colors, perf.entryType),\n }}\n >\n {perf.entryType}\n </text>\n <text\n className={\"pp-entryName t2\"}\n style={{\n fontWeight: fontWeight.medium,\n color: colors.fg.neutral,\n }}\n >\n {perf.name}\n </text>\n <text\n className={\"pp-timestamp t2\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {new Date(perf.timestamp).toISOString()}\n </text>\n </view>\n\n <view\n bindtap={() =>\n setSelectedId(selectedId === perf.id ? null : perf.id)\n }\n >\n {isMetricFcp && primaryFcp && (\n <text\n className={\"pp-fcpHighlight t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n backgroundColor: colors.palette.blue100,\n }}\n >\n {primaryFcp}\n </text>\n )}\n </view>\n\n {selectedId === perf.id && (\n <view className={\"pp-detailsContainer\"}>\n {isMetricFcp && fcpMetrics && (\n <view className={\"pp-fcpSection\"}>\n {totalFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 전체 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(totalFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n PrepareTemplate Start부터 Paint End 까지 걸리는\n 시간\n </text>\n </view>\n )}\n\n {lynxFcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n LynxFCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(lynxFcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n Bundle Load 시작부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n\n {fcp !== undefined && (\n <view\n className={\"pp-fcpMetric\"}\n style={{ backgroundColor: colors.bg.layerDefault }}\n >\n <view className={\"pp-fcpMetricHeader\"}>\n <text\n className={\"pp-fcpMetricName t2\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n 렌더링 FCP\n </text>\n <text\n className={\"pp-fcpMetricValue t1\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.palette.blue600,\n }}\n >\n {formatDuration(fcp.duration)}\n </text>\n </view>\n <text\n className={\"pp-fcpMetricDescription t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n TemplateBundle 준비부터 Paint End 까지 걸리는 시간\n </text>\n </view>\n )}\n </view>\n )}\n\n {!!perf.rawEntry && (\n <view\n className={\"pp-rawEntrySection\"}\n style={{ backgroundColor: colors.bg.neutralWeak }}\n >\n <text\n className={\"pp-detailTitle t3\"}\n style={{\n fontWeight: fontWeight.bold,\n color: colors.fg.neutral,\n }}\n >\n Raw Entry\n </text>\n <text\n className={\"pp-rawEntry t3\"}\n style={{\n fontWeight: fontWeight.regular,\n color: colors.fg.neutralSubtle,\n }}\n >\n {String(\n stringify(perf.rawEntry, null, 2, {\n references: true,\n }),\n )}\n </text>\n </view>\n )}\n </view>\n )}\n </view>\n </list-item>\n );\n })}\n </list>\n </view>\n );\n};\n","import { type ReactNode, useRef, useState } from \"@lynx-js/react\";\nimport type { ListSnapEvent, NodesRef } from \"@lynx-js/types\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { fontWeight } from \"../styles/theme\";\nimport \"./Tabs.css\";\n\ntype TabsProps = {\n items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }>;\n onTabChange?: () => void;\n};\n\nexport default function Tabs(props: TabsProps) {\n const colors = useThemeColors();\n const tabContentsRef = useRef<NodesRef>(null);\n const [activeIndex, setActiveIndex] = useState(0);\n const tabSize =\n props.items.length < 4\n ? undefined\n : `t${Math.max(1, 5 - (props.items.length - 3))}`;\n\n return (\n <view className={\"tabs-root\"}>\n <view\n className={\"tabs-header\"}\n style={{\n boxShadow: `inset 0 -1px 0 0 ${colors.stroke.neutralSubtle}`,\n }}\n >\n {props.items.map((item, i) => (\n <view\n key={item.key}\n className={\"tabs-triggerButton\"}\n bindtap={() => {\n setActiveIndex(i);\n props.onTabChange?.();\n\n tabContentsRef.current\n ?.invoke({\n method: \"scrollToPosition\",\n params: {\n position: i,\n smooth: true,\n },\n })\n .exec();\n }}\n >\n <text\n className={`tabs-triggerButtonText${tabSize ? ` ${tabSize}` : \"\"}`}\n style={{\n fontWeight: fontWeight.bold,\n color:\n i === activeIndex\n ? colors.fg.neutral\n : colors.fg.neutralSubtle,\n }}\n >\n {item.label}\n </text>\n {i === 0 && (\n <view\n className={\"tabs-triggerIndicator\"}\n style={{ transform: `translateX(${activeIndex * 100}%)` }}\n >\n <view\n className={\"tabs-triggerIndicatorLine\"}\n style={{ backgroundColor: colors.fg.neutral }}\n />\n </view>\n )}\n </view>\n ))}\n </view>\n\n <list\n ref={tabContentsRef}\n className={\"tabs-contents\"}\n scroll-orientation=\"horizontal\"\n item-snap={{ factor: 0, offset: 0 }}\n bindscroll={() => props.onTabChange?.()}\n bindsnap={(e: ListSnapEvent) => {\n setActiveIndex(e.detail.position);\n }}\n bounces={false}\n preload-buffer-count={props.items.length}\n >\n {props.items.map((item) => (\n <list-item\n key={item.key}\n item-key={item.key}\n recyclable={false}\n className={\"tabs-content\"}\n >\n {item.renderContent()}\n </list-item>\n ))}\n </list>\n </view>\n );\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport { useConsole, useNetwork, usePerformance } from \"../hooks\";\nimport type { CustomTab } from \"../types\";\nimport \"./ConsolePanel.css\";\nimport { dismissFilterDropdown, LogPanel } from \"./LogPanel\";\nimport { NetworkPanel } from \"./NetworkPanel\";\nimport { PerformancePanel } from \"./PerformancePanel\";\nimport Tabs from \"./Tabs\";\n\ninterface ConsolePanelProps {\n customTabs?: CustomTab[];\n}\n\nexport const ConsolePanel = ({ customTabs }: ConsolePanelProps) => {\n const { logs, clearLogs } = useConsole();\n const { networks, clearNetworks } = useNetwork();\n const { performances, clearPerformances } = usePerformance();\n\n const state = globalThis.__LYNX_CONSOLE__?.state;\n\n const items: Array<{\n key: string;\n label: string;\n renderContent: () => ReactNode;\n }> = [];\n\n if (state?.logs) {\n items.push({\n key: \"log\",\n label: \"Log\",\n renderContent: () => <LogPanel logs={logs} clearLogs={clearLogs} />,\n });\n }\n\n if (state?.networks) {\n items.push({\n key: \"network\",\n label: \"Network\",\n renderContent: () => (\n <NetworkPanel networks={networks} clearNetworks={clearNetworks} />\n ),\n });\n }\n\n if (state?.performances) {\n items.push({\n key: \"performance\",\n label: \"Perf\",\n renderContent: () => (\n <PerformancePanel\n performances={performances}\n clearPerformances={clearPerformances}\n />\n ),\n });\n }\n\n if (customTabs) {\n for (const tab of customTabs) {\n items.push({\n key: tab.key,\n label: tab.label,\n renderContent: tab.renderContent,\n });\n }\n }\n\n if (items.length === 0) {\n return null;\n }\n\n return (\n <view className=\"cp-container\">\n <Tabs onTabChange={dismissFilterDropdown} items={items} />\n </view>\n );\n};\n","import { useRef, useState } from \"@lynx-js/react\";\nimport type { BaseTouchEvent, Target } from \"@lynx-js/types\";\n\nconst LONG_PRESS_DURATION = 400;\nconst MOVE_THRESHOLD = 5;\n\nconst DEFAULT_RIGHT = 16;\nconst DEFAULT_BOTTOM = 84;\n\ntype VerticalAxis = \"top\" | \"bottom\";\ntype HorizontalAxis = \"left\" | \"right\";\n\nexport interface InitialPosition {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n}\n\ninterface ResolvedAnchors {\n vertical: VerticalAxis;\n horizontal: HorizontalAxis;\n x: number;\n y: number;\n}\n\nfunction resolveAnchors(initial?: InitialPosition): ResolvedAnchors {\n // top/left이 명시되면 그것이 anchor가 돼요. 둘 다 명시되면 top/left가 이겨요.\n const vertical: VerticalAxis = initial?.top !== undefined ? \"top\" : \"bottom\";\n const horizontal: HorizontalAxis =\n initial?.left !== undefined ? \"left\" : \"right\";\n\n const y =\n vertical === \"top\"\n ? (initial?.top ?? 0)\n : (initial?.bottom ?? DEFAULT_BOTTOM);\n const x =\n horizontal === \"left\"\n ? (initial?.left ?? 0)\n : (initial?.right ?? DEFAULT_RIGHT);\n\n return { vertical, horizontal, x, y };\n}\n\ninterface SavedState {\n vertical: VerticalAxis;\n horizontal: HorizontalAxis;\n x: number;\n y: number;\n}\n\nlet saved: SavedState | null = null;\n\ninterface UseLongPressDragOptions {\n initialPosition?: InitialPosition;\n}\n\nexport function useLongPressDrag(\n onTap: () => void,\n options?: UseLongPressDragOptions,\n) {\n const anchors = resolveAnchors(options?.initialPosition);\n\n // 저장된 위치는 anchor 조합이 동일할 때만 복원해요.\n const snapshot = saved;\n let initX = anchors.x;\n let initY = anchors.y;\n if (\n snapshot !== null &&\n snapshot.vertical === anchors.vertical &&\n snapshot.horizontal === anchors.horizontal\n ) {\n initX = snapshot.x;\n initY = snapshot.y;\n }\n\n const [x, setX] = useState(initX);\n const [y, setY] = useState(initY);\n const [phase, setPhase] = useState<\"idle\" | \"dragging\" | \"releasing\">(\"idle\");\n const [tempX, setTempX] = useState(initX);\n const [tempY, setTempY] = useState(initY);\n\n const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const draggingRef = useRef(false);\n const startRef = useRef({ x: 0, y: 0, ax: 0, ay: 0 });\n\n // anchor 방향에 따라 드래그 부호 결정. right/bottom anchor면 드래그 방향과 값 변화가 반대.\n const xSign = anchors.horizontal === \"right\" ? -1 : 1;\n const ySign = anchors.vertical === \"bottom\" ? -1 : 1;\n\n const clearTimer = () => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const handleTouchStart = (e: BaseTouchEvent<Target>) => {\n startRef.current = {\n x: e.detail.x,\n y: e.detail.y,\n ax: x,\n ay: y,\n };\n draggingRef.current = false;\n\n timerRef.current = setTimeout(() => {\n draggingRef.current = true;\n setPhase(\"dragging\");\n setTempX(x);\n setTempY(y);\n }, LONG_PRESS_DURATION);\n };\n\n const handleTouchMove = (e: BaseTouchEvent<Target>) => {\n const dx = e.detail.x - startRef.current.x;\n const dy = e.detail.y - startRef.current.y;\n\n if (\n !draggingRef.current &&\n (Math.abs(dx) > MOVE_THRESHOLD || Math.abs(dy) > MOVE_THRESHOLD)\n ) {\n clearTimer();\n }\n\n if (!draggingRef.current) return;\n\n setTempX(startRef.current.ax + xSign * dx);\n setTempY(startRef.current.ay + ySign * dy);\n };\n\n const handleTouchEnd = () => {\n clearTimer();\n\n if (draggingRef.current) {\n setX(tempX);\n setY(tempY);\n saved = {\n vertical: anchors.vertical,\n horizontal: anchors.horizontal,\n x: tempX,\n y: tempY,\n };\n setPhase(\"releasing\");\n draggingRef.current = false;\n setTimeout(() => setPhase(\"idle\"), 300);\n } else {\n onTap();\n }\n };\n\n const isDragging = phase === \"dragging\";\n const currentX = isDragging ? tempX : x;\n const currentY = isDragging ? tempY : y;\n\n const positionStyle = {\n [anchors.horizontal]: `${currentX}px`,\n [anchors.vertical]: `${currentY}px`,\n } as { top?: string; left?: string; right?: string; bottom?: string };\n\n return {\n phase,\n positionStyle,\n clearTimer,\n handlers: {\n catchtouchstart: handleTouchStart,\n catchtouchmove: handleTouchMove,\n catchtouchend: handleTouchEnd,\n },\n };\n}\n","import type { ReactNode } from \"@lynx-js/react\";\nimport {\n type InitialPosition,\n useLongPressDrag,\n} from \"../hooks/useLongPressDrag\";\nimport { useThemeColors } from \"../styles/ThemeContext\";\nimport { duration } from \"../styles/theme\";\nimport \"./FloatingButton.css\";\n\ninterface FloatingButtonProps {\n bindtap: () => void;\n children: ReactNode;\n initialPosition?: InitialPosition;\n}\n\nconst SHINE_STYLES = {\n idle: {\n transform: \"scale(0)\",\n opacity: 0,\n },\n dragging: {\n transform: \"scale(1)\",\n opacity: 1,\n transition: \"transform 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n releasing: {\n transform: \"scale(1)\",\n opacity: 0,\n transition: \"opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)\",\n },\n} as const;\n\nexport const FloatingButton = ({\n bindtap,\n children,\n initialPosition,\n}: FloatingButtonProps) => {\n const colors = useThemeColors();\n const { phase, positionStyle, clearTimer, handlers } = useLongPressDrag(\n bindtap,\n { initialPosition },\n );\n\n const handleReload = () => {\n try {\n lynx.reload({}, () => {\n console.log(\"reloaded!\");\n });\n } catch (e) {\n console.error(\"[LynxConsole] reload failed:\", e);\n }\n };\n\n const isDragging = phase === \"dragging\";\n\n return (\n <view\n className={\"fb-wrapper\"}\n consume-slide-event={[[-180, 180]]}\n style={{\n ...positionStyle,\n transform: isDragging ? \"scale(1.05)\" : \"scale(1)\",\n transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`,\n }}\n {...handlers}\n >\n <view\n className={\"fb-button\"}\n style={{ backgroundColor: colors.palette.green600 }}\n >\n {children}\n <view className={\"fb-shineOverlay\"} style={SHINE_STYLES[phase]} />\n </view>\n <view\n className={\"fb-reloadButton\"}\n style={{ backgroundColor: colors.palette.green600 }}\n catchtouchstart={() => clearTimer()}\n bindtap={handleReload}\n >\n <text\n className={\"fb-reloadIcon\"}\n style={{ color: colors.palette.staticWhite }}\n >\n {\"\\u21BB\"}\n </text>\n </view>\n </view>\n );\n};\n","import {\n type ForwardedRef,\n forwardRef,\n useImperativeHandle,\n useMemo,\n useState,\n} from \"@lynx-js/react\";\nimport BottomSheet from \"./components/BottomSheet.jsx\";\nimport { ConsolePanel } from \"./components/ConsolePanel.jsx\";\nimport \"./components/FloatingButton.css\";\nimport \"./styles/tokens.css\";\nimport { FloatingButton } from \"./components/FloatingButton.jsx\";\nimport { usePerformance } from \"./hooks/usePerformance\";\nimport { ThemeProvider } from \"./styles/ThemeContext\";\nimport { getColors } from \"./styles/theme\";\nimport type { CustomTab } from \"./types\";\n\nexport interface LynxConsoleHandle {\n open: () => void;\n close: () => void;\n isOpen: () => boolean;\n}\n\nexport interface LynxConsoleProps {\n theme?: \"light\" | \"dark\";\n safeAreaInsetBottom?: string;\n customTabs?: CustomTab[];\n initialPosition?: {\n top?: number;\n left?: number;\n right?: number;\n bottom?: number;\n };\n}\n\ninterface FcpMetric {\n name: string;\n duration: number;\n}\n\ninterface MetricFcpEntry {\n totalFcp?: FcpMetric;\n lynxFcp?: FcpMetric;\n fcp?: FcpMetric;\n}\n\nconst LynxConsole = forwardRef<LynxConsoleHandle, LynxConsoleProps>(\n (\n {\n theme = \"light\",\n safeAreaInsetBottom = \"50px\",\n customTabs,\n initialPosition,\n },\n ref: ForwardedRef<LynxConsoleHandle>,\n ) => {\n const [isOpen, setIsOpen] = useState(false);\n const [shouldClose, setShouldClose] = useState(false);\n const { performances } = usePerformance();\n const colors = useMemo(() => getColors(theme), [theme]);\n\n const latestFcp = useMemo(() => {\n for (let i = performances.length - 1; i >= 0; i--) {\n const perf = performances[i];\n if (perf && perf.entryType === \"metric\" && perf.name === \"fcp\") {\n const metricEntry = perf.rawEntry as MetricFcpEntry | undefined;\n // totalFcp를 먼저 시도하고, 없으면 lynxFcp 반환\n if (metricEntry?.totalFcp?.duration !== undefined) {\n return metricEntry.totalFcp;\n }\n if (metricEntry?.lynxFcp?.duration !== undefined) {\n return metricEntry.lynxFcp;\n }\n }\n }\n return undefined;\n }, [performances]);\n\n useImperativeHandle(ref, () => ({\n open: () => {\n setIsOpen(true);\n setShouldClose(false);\n },\n close: () => {\n setShouldClose(true);\n },\n isOpen: () => isOpen,\n }));\n\n const handleOpenBottomSheet = () => {\n setIsOpen(true);\n setShouldClose(false);\n };\n\n const handleCloseBottomSheet = () => {\n setIsOpen(false);\n setShouldClose(false);\n };\n\n return (\n <ThemeProvider value={colors}>\n <view\n style={{\n backgroundColor: colors.bg.layerDefault,\n color: colors.fg.neutral,\n }}\n >\n <FloatingButton\n bindtap={handleOpenBottomSheet}\n initialPosition={initialPosition}\n >\n <text\n className=\"fb-title t4\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n LynxConsole\n </text>\n <text\n className=\"fb-subtitle t3\"\n style={{ fontWeight: \"400\", color: colors.palette.staticWhite }}\n >\n {`${latestFcp?.name ?? \"FCP\"}: ${latestFcp?.duration ? latestFcp.duration.toFixed(2) : \"--\"}ms`}\n </text>\n </FloatingButton>\n {isOpen && (\n <BottomSheet\n isOpen={isOpen}\n shouldClose={shouldClose}\n onClose={handleCloseBottomSheet}\n title=\"Lynx Console\"\n safeAreaInsetBottom={safeAreaInsetBottom}\n >\n <ConsolePanel customTabs={customTabs} />\n </BottomSheet>\n )}\n </view>\n </ThemeProvider>\n );\n },\n);\n\nexport type { CustomTab } from \"./types\";\nexport default LynxConsole;\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lynx-console",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -1,5 +1,8 @@
1
1
  import type { ReactNode } from "@lynx-js/react";
2
- import { useLongPressDrag } from "../hooks/useLongPressDrag";
2
+ import {
3
+ type InitialPosition,
4
+ useLongPressDrag,
5
+ } from "../hooks/useLongPressDrag";
3
6
  import { useThemeColors } from "../styles/ThemeContext";
4
7
  import { duration } from "../styles/theme";
5
8
  import "./FloatingButton.css";
@@ -7,6 +10,7 @@ import "./FloatingButton.css";
7
10
  interface FloatingButtonProps {
8
11
  bindtap: () => void;
9
12
  children: ReactNode;
13
+ initialPosition?: InitialPosition;
10
14
  }
11
15
 
12
16
  const SHINE_STYLES = {
@@ -26,10 +30,16 @@ const SHINE_STYLES = {
26
30
  },
27
31
  } as const;
28
32
 
29
- export const FloatingButton = ({ bindtap, children }: FloatingButtonProps) => {
33
+ export const FloatingButton = ({
34
+ bindtap,
35
+ children,
36
+ initialPosition,
37
+ }: FloatingButtonProps) => {
30
38
  const colors = useThemeColors();
31
- const { phase, right, bottom, clearTimer, handlers } =
32
- useLongPressDrag(bindtap);
39
+ const { phase, positionStyle, clearTimer, handlers } = useLongPressDrag(
40
+ bindtap,
41
+ { initialPosition },
42
+ );
33
43
 
34
44
  const handleReload = () => {
35
45
  try {
@@ -48,8 +58,7 @@ export const FloatingButton = ({ bindtap, children }: FloatingButtonProps) => {
48
58
  className={"fb-wrapper"}
49
59
  consume-slide-event={[[-180, 180]]}
50
60
  style={{
51
- right: `${right}px`,
52
- bottom: `${bottom}px`,
61
+ ...positionStyle,
53
62
  transform: isDragging ? "scale(1.05)" : "scale(1)",
54
63
  transition: `transform ${duration.d4} cubic-bezier(0.4, 0, 0.2, 1)`,
55
64
  }}
@@ -7,19 +7,86 @@ const MOVE_THRESHOLD = 5;
7
7
  const DEFAULT_RIGHT = 16;
8
8
  const DEFAULT_BOTTOM = 84;
9
9
 
10
- let savedRight = DEFAULT_RIGHT;
11
- let savedBottom = DEFAULT_BOTTOM;
10
+ type VerticalAxis = "top" | "bottom";
11
+ type HorizontalAxis = "left" | "right";
12
+
13
+ export interface InitialPosition {
14
+ top?: number;
15
+ left?: number;
16
+ right?: number;
17
+ bottom?: number;
18
+ }
19
+
20
+ interface ResolvedAnchors {
21
+ vertical: VerticalAxis;
22
+ horizontal: HorizontalAxis;
23
+ x: number;
24
+ y: number;
25
+ }
26
+
27
+ function resolveAnchors(initial?: InitialPosition): ResolvedAnchors {
28
+ // top/left이 명시되면 그것이 anchor가 돼요. 둘 다 명시되면 top/left가 이겨요.
29
+ const vertical: VerticalAxis = initial?.top !== undefined ? "top" : "bottom";
30
+ const horizontal: HorizontalAxis =
31
+ initial?.left !== undefined ? "left" : "right";
32
+
33
+ const y =
34
+ vertical === "top"
35
+ ? (initial?.top ?? 0)
36
+ : (initial?.bottom ?? DEFAULT_BOTTOM);
37
+ const x =
38
+ horizontal === "left"
39
+ ? (initial?.left ?? 0)
40
+ : (initial?.right ?? DEFAULT_RIGHT);
41
+
42
+ return { vertical, horizontal, x, y };
43
+ }
44
+
45
+ interface SavedState {
46
+ vertical: VerticalAxis;
47
+ horizontal: HorizontalAxis;
48
+ x: number;
49
+ y: number;
50
+ }
51
+
52
+ let saved: SavedState | null = null;
53
+
54
+ interface UseLongPressDragOptions {
55
+ initialPosition?: InitialPosition;
56
+ }
12
57
 
13
- export function useLongPressDrag(onTap: () => void) {
14
- const [right, setRight] = useState(savedRight);
15
- const [bottom, setBottom] = useState(savedBottom);
58
+ export function useLongPressDrag(
59
+ onTap: () => void,
60
+ options?: UseLongPressDragOptions,
61
+ ) {
62
+ const anchors = resolveAnchors(options?.initialPosition);
63
+
64
+ // 저장된 위치는 anchor 조합이 동일할 때만 복원해요.
65
+ const snapshot = saved;
66
+ let initX = anchors.x;
67
+ let initY = anchors.y;
68
+ if (
69
+ snapshot !== null &&
70
+ snapshot.vertical === anchors.vertical &&
71
+ snapshot.horizontal === anchors.horizontal
72
+ ) {
73
+ initX = snapshot.x;
74
+ initY = snapshot.y;
75
+ }
76
+
77
+ const [x, setX] = useState(initX);
78
+ const [y, setY] = useState(initY);
16
79
  const [phase, setPhase] = useState<"idle" | "dragging" | "releasing">("idle");
17
- const [tempRight, setTempRight] = useState(savedRight);
18
- const [tempBottom, setTempBottom] = useState(savedBottom);
80
+ const [tempX, setTempX] = useState(initX);
81
+ const [tempY, setTempY] = useState(initY);
19
82
 
20
83
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
21
84
  const draggingRef = useRef(false);
22
- const startRef = useRef({ x: 0, y: 0, r: 0, b: 0 });
85
+ const startRef = useRef({ x: 0, y: 0, ax: 0, ay: 0 });
86
+
87
+ // anchor 방향에 따라 드래그 부호 결정. right/bottom anchor면 드래그 방향과 값 변화가 반대.
88
+ const xSign = anchors.horizontal === "right" ? -1 : 1;
89
+ const ySign = anchors.vertical === "bottom" ? -1 : 1;
23
90
 
24
91
  const clearTimer = () => {
25
92
  if (timerRef.current) {
@@ -32,16 +99,16 @@ export function useLongPressDrag(onTap: () => void) {
32
99
  startRef.current = {
33
100
  x: e.detail.x,
34
101
  y: e.detail.y,
35
- r: right,
36
- b: bottom,
102
+ ax: x,
103
+ ay: y,
37
104
  };
38
105
  draggingRef.current = false;
39
106
 
40
107
  timerRef.current = setTimeout(() => {
41
108
  draggingRef.current = true;
42
109
  setPhase("dragging");
43
- setTempRight(right);
44
- setTempBottom(bottom);
110
+ setTempX(x);
111
+ setTempY(y);
45
112
  }, LONG_PRESS_DURATION);
46
113
  };
47
114
 
@@ -58,19 +125,22 @@ export function useLongPressDrag(onTap: () => void) {
58
125
 
59
126
  if (!draggingRef.current) return;
60
127
 
61
- // right/bottom 기준이므로 방향 반전
62
- setTempRight(startRef.current.r - dx);
63
- setTempBottom(startRef.current.b - dy);
128
+ setTempX(startRef.current.ax + xSign * dx);
129
+ setTempY(startRef.current.ay + ySign * dy);
64
130
  };
65
131
 
66
132
  const handleTouchEnd = () => {
67
133
  clearTimer();
68
134
 
69
135
  if (draggingRef.current) {
70
- setRight(tempRight);
71
- setBottom(tempBottom);
72
- savedRight = tempRight;
73
- savedBottom = tempBottom;
136
+ setX(tempX);
137
+ setY(tempY);
138
+ saved = {
139
+ vertical: anchors.vertical,
140
+ horizontal: anchors.horizontal,
141
+ x: tempX,
142
+ y: tempY,
143
+ };
74
144
  setPhase("releasing");
75
145
  draggingRef.current = false;
76
146
  setTimeout(() => setPhase("idle"), 300);
@@ -80,11 +150,17 @@ export function useLongPressDrag(onTap: () => void) {
80
150
  };
81
151
 
82
152
  const isDragging = phase === "dragging";
153
+ const currentX = isDragging ? tempX : x;
154
+ const currentY = isDragging ? tempY : y;
155
+
156
+ const positionStyle = {
157
+ [anchors.horizontal]: `${currentX}px`,
158
+ [anchors.vertical]: `${currentY}px`,
159
+ } as { top?: string; left?: string; right?: string; bottom?: string };
83
160
 
84
161
  return {
85
162
  phase,
86
- right: isDragging ? tempRight : right,
87
- bottom: isDragging ? tempBottom : bottom,
163
+ positionStyle,
88
164
  clearTimer,
89
165
  handlers: {
90
166
  catchtouchstart: handleTouchStart,
package/src/index.tsx CHANGED
@@ -25,6 +25,12 @@ export interface LynxConsoleProps {
25
25
  theme?: "light" | "dark";
26
26
  safeAreaInsetBottom?: string;
27
27
  customTabs?: CustomTab[];
28
+ initialPosition?: {
29
+ top?: number;
30
+ left?: number;
31
+ right?: number;
32
+ bottom?: number;
33
+ };
28
34
  }
29
35
 
30
36
  interface FcpMetric {
@@ -40,7 +46,12 @@ interface MetricFcpEntry {
40
46
 
41
47
  const LynxConsole = forwardRef<LynxConsoleHandle, LynxConsoleProps>(
42
48
  (
43
- { theme = "light", safeAreaInsetBottom = "50px", customTabs },
49
+ {
50
+ theme = "light",
51
+ safeAreaInsetBottom = "50px",
52
+ customTabs,
53
+ initialPosition,
54
+ },
44
55
  ref: ForwardedRef<LynxConsoleHandle>,
45
56
  ) => {
46
57
  const [isOpen, setIsOpen] = useState(false);
@@ -94,7 +105,10 @@ const LynxConsole = forwardRef<LynxConsoleHandle, LynxConsoleProps>(
94
105
  color: colors.fg.neutral,
95
106
  }}
96
107
  >
97
- <FloatingButton bindtap={handleOpenBottomSheet}>
108
+ <FloatingButton
109
+ bindtap={handleOpenBottomSheet}
110
+ initialPosition={initialPosition}
111
+ >
98
112
  <text
99
113
  className="fb-title t4"
100
114
  style={{ fontWeight: "400", color: colors.palette.staticWhite }}