@openspecui/web 1.5.0 → 1.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.
Files changed (52) hide show
  1. package/dist/assets/{BufferResource-Bn1UWy0D.js → BufferResource-LpIscPOh.js} +1 -1
  2. package/dist/assets/{CanvasRenderer-D8NiU8la.js → CanvasRenderer-hdBAgk8Z.js} +1 -1
  3. package/dist/assets/{Filter-CRwq487x.js → Filter-DJVBXWdE.js} +1 -1
  4. package/dist/assets/{RenderTargetSystem-CtoB_qTm.js → RenderTargetSystem-CEuUrT8d.js} +1 -1
  5. package/dist/assets/{WebGLRenderer-BgKO8R0a.js → WebGLRenderer-DUSFltAk.js} +1 -1
  6. package/dist/assets/{WebGPURenderer-CQeL2efC.js → WebGPURenderer-DafJULoy.js} +1 -1
  7. package/dist/assets/{browserAll-DP6sOYev.js → browserAll-DYvdsmhE.js} +1 -1
  8. package/dist/assets/{ghostty-web-evxujSxm.js → ghostty-web-BitlvuYh.js} +1 -1
  9. package/dist/assets/{index-BnT52DZ8.js → index-5zLYkWge.js} +1 -1
  10. package/dist/assets/{index-B0IbsqHi.js → index-B8AH-4mO.js} +1 -1
  11. package/dist/assets/{index-D2Tp4F9B.js → index-BXlwoClD.js} +1 -1
  12. package/dist/assets/{index-dSf1u0YV.js → index-BhxOfd1V.js} +1 -1
  13. package/dist/assets/{index-f0QdJSzm.js → index-BwqUcS4o.js} +1 -1
  14. package/dist/assets/{index-DTeOcXKn.js → index-CGTRVPmS.js} +1 -1
  15. package/dist/assets/{index-T8xoxmUb.js → index-CKfGQiMr.js} +236 -205
  16. package/dist/assets/{index-BejnsZfY.js → index-CYTyn82I.js} +1 -1
  17. package/dist/assets/{index-DJqmTRAR.js → index-CjHce-bH.js} +1 -1
  18. package/dist/assets/{index-B147AOgf.js → index-CuiBRRWA.js} +1 -1
  19. package/dist/assets/{index-BPZ3nG0r.js → index-DEgCfC-b.js} +1 -1
  20. package/dist/assets/{index-DcXyAs0z.js → index-DbrGteUX.js} +1 -1
  21. package/dist/assets/{index-BMashGQn.js → index-Dge9ymDE.js} +1 -1
  22. package/dist/assets/{index-CBCPR3Qb.js → index-DtJ1xKq6.js} +1 -1
  23. package/dist/assets/index-USotIqwU.css +1 -0
  24. package/dist/assets/{index-D6ardy54.js → index-gPPYc26D.js} +1 -1
  25. package/dist/assets/{index-4MAU81Qk.js → index-hXwe0Xwc.js} +1 -1
  26. package/dist/assets/{webworkerAll-DA2HufNb.js → webworkerAll-CFbGR7bA.js} +1 -1
  27. package/dist/index.html +2 -2
  28. package/dist-ssg/client/.vite/ssr-manifest.json +25 -23
  29. package/dist-ssg/client/assets/{index-nXK46PJX.js → index-BTlc4gRd.js} +1 -1
  30. package/dist-ssg/client/assets/{index-CkIJT9VM.js → index-BWss1-RY.js} +1 -1
  31. package/dist-ssg/client/assets/{index-neDlRvdc.js → index-BiJDZ6HF.js} +1 -1
  32. package/dist-ssg/client/assets/{index-C1d9Zuhl.js → index-CXPcc7SN.js} +1 -1
  33. package/dist-ssg/client/assets/{index-CsRZVfC5.js → index-CZlM3xpa.js} +1 -1
  34. package/dist-ssg/client/assets/{index-BNvK4AuA.js → index-ChGuHVYh.js} +1 -1
  35. package/dist-ssg/client/assets/{index-DuI28swy.js → index-D05l7owO.js} +1 -1
  36. package/dist-ssg/client/assets/{index-lmUf9UYl.js → index-DDs25DSq.js} +1 -1
  37. package/dist-ssg/client/assets/{index-DaAYjM02.js → index-DGbspcQI.js} +1 -1
  38. package/dist-ssg/client/assets/{index-DoT0nwXT.js → index-DMgvGt5z.js} +1 -1
  39. package/dist-ssg/client/assets/{index-CfKJRsCY.js → index-DRxWBSjo.js} +1 -1
  40. package/dist-ssg/client/assets/{index-Qc9PpyXL.js → index-DbUWuKU6.js} +1 -1
  41. package/dist-ssg/client/assets/index-Di6aqhqv.css +1 -0
  42. package/dist-ssg/client/assets/{index-Bts5PKwE.js → index-Dw5LFumn.js} +1 -1
  43. package/dist-ssg/client/assets/{index-BKuRbAoU.js → index-JFh0HIYc.js} +1 -1
  44. package/dist-ssg/client/assets/{index-BPqe-tty.js → index-LZFdJ_jX.js} +1 -1
  45. package/dist-ssg/client/assets/index.ssg-2R4_pvpx.js +631 -0
  46. package/dist-ssg/client/index.ssg.html +2 -2
  47. package/dist-ssg/server/entry-server.js +829 -321
  48. package/dist-ssg/ssg-cli.mjs +27 -2
  49. package/package.json +2 -2
  50. package/dist/assets/index-Ys2MTD3W.css +0 -1
  51. package/dist-ssg/client/assets/index-42NXIrUL.css +0 -1
  52. package/dist-ssg/client/assets/index.ssg-B7Yw_6vy.js +0 -600
@@ -36333,23 +36333,23 @@ const createLucideIcon = (iconName, iconNode) => {
36333
36333
  Component.displayName = `${iconName}`;
36334
36334
  return Component;
36335
36335
  };
36336
- const __iconNode$O = [
36336
+ const __iconNode$P = [
36337
36337
  ["rect", { width: "20", height: "5", x: "2", y: "3", rx: "1", key: "1wp1u1" }],
36338
36338
  ["path", { d: "M4 8v11a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8", key: "1s80jp" }],
36339
36339
  ["path", { d: "M10 12h4", key: "a56b0p" }]
36340
36340
  ];
36341
- const Archive = createLucideIcon("Archive", __iconNode$O);
36342
- const __iconNode$N = [
36341
+ const Archive = createLucideIcon("Archive", __iconNode$P);
36342
+ const __iconNode$O = [
36343
36343
  ["path", { d: "m12 19-7-7 7-7", key: "1l729n" }],
36344
36344
  ["path", { d: "M19 12H5", key: "x3x0zl" }]
36345
36345
  ];
36346
- const ArrowLeft = createLucideIcon("ArrowLeft", __iconNode$N);
36347
- const __iconNode$M = [
36346
+ const ArrowLeft = createLucideIcon("ArrowLeft", __iconNode$O);
36347
+ const __iconNode$N = [
36348
36348
  ["path", { d: "M5 12h14", key: "1ays0h" }],
36349
36349
  ["path", { d: "m12 5 7 7-7 7", key: "xquz4c" }]
36350
36350
  ];
36351
- const ArrowRight = createLucideIcon("ArrowRight", __iconNode$M);
36352
- const __iconNode$L = [
36351
+ const ArrowRight = createLucideIcon("ArrowRight", __iconNode$N);
36352
+ const __iconNode$M = [
36353
36353
  [
36354
36354
  "path",
36355
36355
  {
@@ -36359,63 +36359,63 @@ const __iconNode$L = [
36359
36359
  ],
36360
36360
  ["circle", { cx: "12", cy: "13", r: "3", key: "1vg3eu" }]
36361
36361
  ];
36362
- const Camera = createLucideIcon("Camera", __iconNode$L);
36363
- const __iconNode$K = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
36364
- const Check = createLucideIcon("Check", __iconNode$K);
36365
- const __iconNode$J = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
36366
- const ChevronDown = createLucideIcon("ChevronDown", __iconNode$J);
36367
- const __iconNode$I = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
36368
- const ChevronRight = createLucideIcon("ChevronRight", __iconNode$I);
36369
- const __iconNode$H = [
36362
+ const Camera = createLucideIcon("Camera", __iconNode$M);
36363
+ const __iconNode$L = [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]];
36364
+ const Check = createLucideIcon("Check", __iconNode$L);
36365
+ const __iconNode$K = [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]];
36366
+ const ChevronDown = createLucideIcon("ChevronDown", __iconNode$K);
36367
+ const __iconNode$J = [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]];
36368
+ const ChevronRight = createLucideIcon("ChevronRight", __iconNode$J);
36369
+ const __iconNode$I = [
36370
36370
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
36371
36371
  ["line", { x1: "12", x2: "12", y1: "8", y2: "12", key: "1pkeuh" }],
36372
36372
  ["line", { x1: "12", x2: "12.01", y1: "16", y2: "16", key: "4dfq90" }]
36373
36373
  ];
36374
- const CircleAlert = createLucideIcon("CircleAlert", __iconNode$H);
36375
- const __iconNode$G = [
36374
+ const CircleAlert = createLucideIcon("CircleAlert", __iconNode$I);
36375
+ const __iconNode$H = [
36376
36376
  ["path", { d: "M21.801 10A10 10 0 1 1 17 3.335", key: "yps3ct" }],
36377
36377
  ["path", { d: "m9 11 3 3L22 4", key: "1pflzl" }]
36378
36378
  ];
36379
- const CircleCheckBig = createLucideIcon("CircleCheckBig", __iconNode$G);
36380
- const __iconNode$F = [
36379
+ const CircleCheckBig = createLucideIcon("CircleCheckBig", __iconNode$H);
36380
+ const __iconNode$G = [
36381
36381
  ["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }],
36382
36382
  ["path", { d: "m9 12 2 2 4-4", key: "dzmm74" }]
36383
36383
  ];
36384
- const CircleCheck = createLucideIcon("CircleCheck", __iconNode$F);
36385
- const __iconNode$E = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
36386
- const Circle = createLucideIcon("Circle", __iconNode$E);
36387
- const __iconNode$D = [
36384
+ const CircleCheck = createLucideIcon("CircleCheck", __iconNode$G);
36385
+ const __iconNode$F = [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]];
36386
+ const Circle = createLucideIcon("Circle", __iconNode$F);
36387
+ const __iconNode$E = [
36388
36388
  ["rect", { width: "14", height: "14", x: "8", y: "8", rx: "2", ry: "2", key: "17jyea" }],
36389
36389
  ["path", { d: "M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2", key: "zix9uf" }]
36390
36390
  ];
36391
- const Copy = createLucideIcon("Copy", __iconNode$D);
36392
- const __iconNode$C = [
36391
+ const Copy = createLucideIcon("Copy", __iconNode$E);
36392
+ const __iconNode$D = [
36393
36393
  ["circle", { cx: "12", cy: "12", r: "1", key: "41hilf" }],
36394
36394
  ["circle", { cx: "12", cy: "5", r: "1", key: "gxeob9" }],
36395
36395
  ["circle", { cx: "12", cy: "19", r: "1", key: "lyex9k" }]
36396
36396
  ];
36397
- const EllipsisVertical = createLucideIcon("EllipsisVertical", __iconNode$C);
36398
- const __iconNode$B = [
36397
+ const EllipsisVertical = createLucideIcon("EllipsisVertical", __iconNode$D);
36398
+ const __iconNode$C = [
36399
36399
  ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
36400
36400
  ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
36401
36401
  ["path", { d: "M9 15h6", key: "cctwl0" }],
36402
36402
  ["path", { d: "M12 18v-6", key: "17g6i2" }]
36403
36403
  ];
36404
- const FilePlus = createLucideIcon("FilePlus", __iconNode$B);
36405
- const __iconNode$A = [
36404
+ const FilePlus = createLucideIcon("FilePlus", __iconNode$C);
36405
+ const __iconNode$B = [
36406
36406
  ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
36407
36407
  ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }],
36408
36408
  ["path", { d: "M10 9H8", key: "b1mrlr" }],
36409
36409
  ["path", { d: "M16 13H8", key: "t4e002" }],
36410
36410
  ["path", { d: "M16 17H8", key: "z1uh3a" }]
36411
36411
  ];
36412
- const FileText = createLucideIcon("FileText", __iconNode$A);
36413
- const __iconNode$z = [
36412
+ const FileText = createLucideIcon("FileText", __iconNode$B);
36413
+ const __iconNode$A = [
36414
36414
  ["path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z", key: "1rqfz7" }],
36415
36415
  ["path", { d: "M14 2v4a2 2 0 0 0 2 2h4", key: "tnqrlb" }]
36416
36416
  ];
36417
- const File = createLucideIcon("File", __iconNode$z);
36418
- const __iconNode$y = [
36417
+ const File = createLucideIcon("File", __iconNode$A);
36418
+ const __iconNode$z = [
36419
36419
  [
36420
36420
  "path",
36421
36421
  {
@@ -36424,8 +36424,8 @@ const __iconNode$y = [
36424
36424
  }
36425
36425
  ]
36426
36426
  ];
36427
- const FolderOpen = createLucideIcon("FolderOpen", __iconNode$y);
36428
- const __iconNode$x = [
36427
+ const FolderOpen = createLucideIcon("FolderOpen", __iconNode$z);
36428
+ const __iconNode$y = [
36429
36429
  ["path", { d: "M12 10v6", key: "1bos4e" }],
36430
36430
  ["path", { d: "M9 13h6", key: "1uhe8q" }],
36431
36431
  [
@@ -36436,8 +36436,8 @@ const __iconNode$x = [
36436
36436
  }
36437
36437
  ]
36438
36438
  ];
36439
- const FolderPlus = createLucideIcon("FolderPlus", __iconNode$x);
36440
- const __iconNode$w = [
36439
+ const FolderPlus = createLucideIcon("FolderPlus", __iconNode$y);
36440
+ const __iconNode$x = [
36441
36441
  [
36442
36442
  "path",
36443
36443
  {
@@ -36455,8 +36455,8 @@ const __iconNode$w = [
36455
36455
  ["path", { d: "M3 5a2 2 0 0 0 2 2h3", key: "f2jnh7" }],
36456
36456
  ["path", { d: "M3 3v13a2 2 0 0 0 2 2h3", key: "k8epm1" }]
36457
36457
  ];
36458
- const FolderTree = createLucideIcon("FolderTree", __iconNode$w);
36459
- const __iconNode$v = [
36458
+ const FolderTree = createLucideIcon("FolderTree", __iconNode$x);
36459
+ const __iconNode$w = [
36460
36460
  [
36461
36461
  "path",
36462
36462
  {
@@ -36465,20 +36465,31 @@ const __iconNode$v = [
36465
36465
  }
36466
36466
  ]
36467
36467
  ];
36468
- const Folder = createLucideIcon("Folder", __iconNode$v);
36469
- const __iconNode$u = [
36468
+ const Folder = createLucideIcon("Folder", __iconNode$w);
36469
+ const __iconNode$v = [
36470
36470
  ["line", { x1: "6", x2: "6", y1: "3", y2: "15", key: "17qcm7" }],
36471
36471
  ["circle", { cx: "18", cy: "6", r: "3", key: "1h7g24" }],
36472
36472
  ["circle", { cx: "6", cy: "18", r: "3", key: "fqmcym" }],
36473
36473
  ["path", { d: "M18 9a9 9 0 0 1-9 9", key: "n2h4wq" }]
36474
36474
  ];
36475
- const GitBranch = createLucideIcon("GitBranch", __iconNode$u);
36476
- const __iconNode$t = [
36475
+ const GitBranch = createLucideIcon("GitBranch", __iconNode$v);
36476
+ const __iconNode$u = [
36477
36477
  ["circle", { cx: "12", cy: "12", r: "3", key: "1v7zrd" }],
36478
36478
  ["line", { x1: "3", x2: "9", y1: "12", y2: "12", key: "1dyftd" }],
36479
36479
  ["line", { x1: "15", x2: "21", y1: "12", y2: "12", key: "oup4p8" }]
36480
36480
  ];
36481
- const GitCommitHorizontal = createLucideIcon("GitCommitHorizontal", __iconNode$t);
36481
+ const GitCommitHorizontal = createLucideIcon("GitCommitHorizontal", __iconNode$u);
36482
+ const __iconNode$t = [
36483
+ [
36484
+ "path",
36485
+ {
36486
+ d: "M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5.08-1.25-.27-2.48-1-3.5.28-1.15.28-2.35 0-3.5 0 0-1 0-3 1.5-2.64-.5-5.36-.5-8 0C6 2 5 2 5 2c-.3 1.15-.3 2.35 0 3.5A5.403 5.403 0 0 0 4 9c0 3.5 3 5.5 6 5.5-.39.49-.68 1.05-.85 1.65-.17.6-.22 1.23-.15 1.85v4",
36487
+ key: "tonef"
36488
+ }
36489
+ ],
36490
+ ["path", { d: "M9 18c-4.51 2-5-2-7-2", key: "9comsn" }]
36491
+ ];
36492
+ const Github = createLucideIcon("Github", __iconNode$t);
36482
36493
  const __iconNode$s = [
36483
36494
  ["circle", { cx: "9", cy: "12", r: "1", key: "1vctgf" }],
36484
36495
  ["circle", { cx: "9", cy: "5", r: "1", key: "hp0tcf" }],
@@ -39512,6 +39523,80 @@ Click to copy`,
39512
39523
  }
39513
39524
  );
39514
39525
  }
39526
+ const VIRTUAL_PROJECT_DIRNAME = "project";
39527
+ const WINDOWS_DRIVE_PREFIX = /^[A-Za-z]:\//;
39528
+ function normalizeFsPath$1(path2) {
39529
+ return path2.replace(/\\/g, "/").replace(/\/+$/g, "");
39530
+ }
39531
+ function isAbsolutePath(path2) {
39532
+ return path2.startsWith("/") || WINDOWS_DRIVE_PREFIX.test(path2);
39533
+ }
39534
+ function stripRelativePrefix(path2) {
39535
+ return path2.replace(/^\.?\//, "");
39536
+ }
39537
+ function splitSegments(path2) {
39538
+ return normalizeFsPath$1(path2).split("/").filter(Boolean);
39539
+ }
39540
+ function toNpmSpecifier(path2) {
39541
+ const normalized = normalizeFsPath$1(path2);
39542
+ const match = /(?:^|\/)node_modules\/(?:\.pnpm\/[^/]+\/node_modules\/)?(@[^/]+\/[^/]+|[^/]+)(\/.*)?/.exec(
39543
+ normalized
39544
+ );
39545
+ const pkgName = match?.[1];
39546
+ if (!pkgName) return null;
39547
+ const rest = match[2] ?? "";
39548
+ return `npm:${pkgName}${rest}`;
39549
+ }
39550
+ function toVirtualProjectPath(path2) {
39551
+ const normalized = stripRelativePrefix(path2);
39552
+ return `${VIRTUAL_PROJECT_DIRNAME}:${normalized}`;
39553
+ }
39554
+ function isPathInside$1(root2, target) {
39555
+ const normalizedRoot = normalizeFsPath$1(root2);
39556
+ const normalizedTarget = normalizeFsPath$1(target);
39557
+ const rootLower = normalizedRoot.toLowerCase();
39558
+ const targetLower = normalizedTarget.toLowerCase();
39559
+ return targetLower === rootLower || targetLower.startsWith(`${rootLower}/`);
39560
+ }
39561
+ function toRelativeFromRoot$1(root2, target) {
39562
+ const rootSegments = splitSegments(root2);
39563
+ const targetSegments = splitSegments(target);
39564
+ let index2 = 0;
39565
+ while (index2 < rootSegments.length && index2 < targetSegments.length) {
39566
+ if (rootSegments[index2]?.toLowerCase() !== targetSegments[index2]?.toLowerCase()) {
39567
+ break;
39568
+ }
39569
+ index2 += 1;
39570
+ }
39571
+ return targetSegments.slice(index2).join("/");
39572
+ }
39573
+ function findOpspecSlice(path2) {
39574
+ const segments = splitSegments(path2);
39575
+ const idx = segments.lastIndexOf("openspec");
39576
+ if (idx < 0) return null;
39577
+ return segments.slice(idx).join("/");
39578
+ }
39579
+ function toOpsxDisplayPath(absoluteOrRelativePath, options) {
39580
+ const normalized = normalizeFsPath$1(absoluteOrRelativePath);
39581
+ const npmSpecifier = toNpmSpecifier(normalized);
39582
+ if (options?.source === "package" || npmSpecifier) {
39583
+ if (npmSpecifier) return npmSpecifier;
39584
+ }
39585
+ if (!isAbsolutePath(normalized)) {
39586
+ return toVirtualProjectPath(normalized);
39587
+ }
39588
+ if (options?.projectDir && isPathInside$1(options.projectDir, normalized)) {
39589
+ const relativePath = toRelativeFromRoot$1(options.projectDir, normalized);
39590
+ return toVirtualProjectPath(relativePath);
39591
+ }
39592
+ const openspecSlice = findOpspecSlice(normalized);
39593
+ if (openspecSlice) {
39594
+ return toVirtualProjectPath(openspecSlice);
39595
+ }
39596
+ const segments = splitSegments(normalized);
39597
+ const tail = segments.slice(-4).join("/");
39598
+ return toVirtualProjectPath(tail);
39599
+ }
39515
39600
  var dist$1 = {};
39516
39601
  var composer = {};
39517
39602
  var directives = {};
@@ -39972,7 +40057,7 @@ function requireAnchors() {
39972
40057
  anchors.findNewAnchor = findNewAnchor;
39973
40058
  return anchors;
39974
40059
  }
39975
- var Node = {};
40060
+ var Node$1 = {};
39976
40061
  var applyReviver = {};
39977
40062
  var hasRequiredApplyReviver;
39978
40063
  function requireApplyReviver() {
@@ -40055,7 +40140,7 @@ function requireToJS() {
40055
40140
  }
40056
40141
  var hasRequiredNode;
40057
40142
  function requireNode() {
40058
- if (hasRequiredNode) return Node;
40143
+ if (hasRequiredNode) return Node$1;
40059
40144
  hasRequiredNode = 1;
40060
40145
  var applyReviver2 = requireApplyReviver();
40061
40146
  var identity2 = requireIdentity();
@@ -40090,8 +40175,8 @@ function requireNode() {
40090
40175
  return typeof reviver === "function" ? applyReviver2.applyReviver(reviver, { "": res }, "", res) : res;
40091
40176
  }
40092
40177
  }
40093
- Node.NodeBase = NodeBase;
40094
- return Node;
40178
+ Node$1.NodeBase = NodeBase;
40179
+ return Node$1;
40095
40180
  }
40096
40181
  var hasRequiredAlias;
40097
40182
  function requireAlias() {
@@ -46870,15 +46955,14 @@ function parseDatedIdTimestamp(id2) {
46870
46955
  return Number.isFinite(ts) ? ts : null;
46871
46956
  }
46872
46957
  function normalizeTrendEvents(events, pointLimit) {
46873
- return events.filter((event) => Number.isFinite(event.ts) && event.ts > 0 && Number.isFinite(event.value)).sort((a, b) => a.ts - b.ts).slice(-100);
46874
- }
46875
- function buildBucketedTrend(events, pointLimit, mode) {
46876
- const normalizedEvents = normalizeTrendEvents(events);
46877
- if (normalizedEvents.length === 0) {
46878
- return [];
46879
- }
46880
- const end = normalizedEvents[normalizedEvents.length - 1].ts;
46881
- const probeStart = normalizedEvents[0].ts;
46958
+ return events.filter((event) => Number.isFinite(event.ts) && event.ts > 0 && Number.isFinite(event.value)).sort((a, b) => a.ts - b.ts).slice(-pointLimit);
46959
+ }
46960
+ function buildTimeWindow(probeEvents, rightEdgeTs) {
46961
+ if (probeEvents.length === 0) return null;
46962
+ const probeEnd = probeEvents[probeEvents.length - 1].ts;
46963
+ const hasRightEdge = typeof rightEdgeTs === "number" && Number.isFinite(rightEdgeTs) && rightEdgeTs > 0;
46964
+ const end = hasRightEdge ? Math.max(probeEnd, rightEdgeTs) : probeEnd;
46965
+ const probeStart = probeEvents[0].ts;
46882
46966
  const rangeMs = Math.max(1, end - probeStart);
46883
46967
  const bucketMs = rangeMs >= DAY_MS ? Math.max(DAY_MS, Math.ceil(rangeMs / DASHBOARD_TREND_BAR_COUNT / DAY_MS) * DAY_MS) : Math.max(1, Math.ceil(rangeMs / DASHBOARD_TREND_BAR_COUNT));
46884
46968
  const windowStart = end - bucketMs * DASHBOARD_TREND_BAR_COUNT;
@@ -46886,6 +46970,16 @@ function buildBucketedTrend(events, pointLimit, mode) {
46886
46970
  { length: DASHBOARD_TREND_BAR_COUNT },
46887
46971
  (_, index2) => windowStart + bucketMs * (index2 + 1)
46888
46972
  );
46973
+ return { windowStart, bucketMs, bucketEnds };
46974
+ }
46975
+ function buildBucketedTrend(events, pointLimit, mode, rightEdgeTs) {
46976
+ const normalizedEvents = normalizeTrendEvents(events, pointLimit);
46977
+ if (normalizedEvents.length === 0) {
46978
+ return [];
46979
+ }
46980
+ const timeWindow = buildTimeWindow(normalizedEvents, rightEdgeTs);
46981
+ if (!timeWindow) return [];
46982
+ const { windowStart, bucketMs, bucketEnds } = timeWindow;
46889
46983
  const sums = Array.from({ length: bucketEnds.length }, () => 0);
46890
46984
  for (const event of normalizedEvents) {
46891
46985
  if (event.ts <= windowStart) {
@@ -46899,7 +46993,7 @@ function buildBucketedTrend(events, pointLimit, mode) {
46899
46993
  return { ts, value: sums[index2] };
46900
46994
  });
46901
46995
  }
46902
- function buildStaticObjectiveTrends(snapshot) {
46996
+ function buildStaticObjectiveTrends(snapshot, pointLimit, rightEdgeTs) {
46903
46997
  const trends = createEmptyTrends();
46904
46998
  const requirementEvents = snapshot.specs.flatMap((spec) => {
46905
46999
  const ts = resolveTrendTimestamp(spec.updatedAt, spec.createdAt);
@@ -46909,14 +47003,20 @@ function buildStaticObjectiveTrends(snapshot) {
46909
47003
  snapshot.specs.flatMap((spec) => {
46910
47004
  const ts = resolveTrendTimestamp(spec.createdAt, spec.updatedAt);
46911
47005
  return ts === null ? [] : [{ ts, value: 1 }];
46912
- })
47006
+ }),
47007
+ pointLimit,
47008
+ "sum",
47009
+ rightEdgeTs
46913
47010
  );
46914
- trends.requirements = buildBucketedTrend(requirementEvents);
47011
+ trends.requirements = buildBucketedTrend(requirementEvents, pointLimit, "sum", rightEdgeTs);
46915
47012
  trends.completedChanges = buildBucketedTrend(
46916
47013
  snapshot.archives.flatMap((archive) => {
46917
47014
  const ts = parseDatedIdTimestamp(archive.id) ?? resolveTrendTimestamp(archive.updatedAt, archive.createdAt);
46918
47015
  return ts === null ? [] : [{ ts, value: archive.parsedTasks.length }];
46919
- })
47016
+ }),
47017
+ pointLimit,
47018
+ "sum",
47019
+ rightEdgeTs
46920
47020
  );
46921
47021
  return trends;
46922
47022
  }
@@ -46926,6 +47026,34 @@ function isGlobPattern$1(path2) {
46926
47026
  function normalizePath(path2) {
46927
47027
  return path2.replace(/\\/g, "/").replace(/^\.?\//, "");
46928
47028
  }
47029
+ function normalizeFsPath(path2) {
47030
+ return path2.replace(/\\/g, "/");
47031
+ }
47032
+ function isAbsoluteFsPath(path2) {
47033
+ return /^(?:[A-Za-z]:\/|\/)/.test(path2);
47034
+ }
47035
+ function isPathInside(root2, target) {
47036
+ const normalizedRoot = normalizeFsPath(root2).replace(/\/+$/, "").toLowerCase();
47037
+ const normalizedTarget = normalizeFsPath(target).toLowerCase();
47038
+ return normalizedTarget === normalizedRoot || normalizedTarget.startsWith(`${normalizedRoot}/`);
47039
+ }
47040
+ function toRelativeFromRoot(root2, target) {
47041
+ const normalizedRoot = normalizeFsPath(root2).replace(/\/+$/, "");
47042
+ const normalizedTarget = normalizeFsPath(target);
47043
+ return normalizedTarget.slice(normalizedRoot.length + 1);
47044
+ }
47045
+ function toSchemaRelativePath(inputPath, schemaRoot) {
47046
+ const path2 = normalizeFsPath(inputPath);
47047
+ if (!isAbsoluteFsPath(path2)) return normalizePath(path2);
47048
+ if (schemaRoot && isPathInside(schemaRoot, path2)) {
47049
+ return normalizePath(toRelativeFromRoot(schemaRoot, path2));
47050
+ }
47051
+ const templatesIdx = path2.lastIndexOf("/templates/");
47052
+ if (templatesIdx >= 0) {
47053
+ return normalizePath(path2.slice(templatesIdx + 1));
47054
+ }
47055
+ return getPathBasename(path2);
47056
+ }
46929
47057
  function getPathBasename(path2) {
46930
47058
  const normalized = normalizePath(path2);
46931
47059
  const parts = normalized.split("/");
@@ -47086,6 +47214,48 @@ function buildChangeStatus(snapshot, change, preferredSchema) {
47086
47214
  artifacts
47087
47215
  };
47088
47216
  }
47217
+ function buildStaticGitSnapshot(snapshot) {
47218
+ const defaultBranch = snapshot.git?.defaultBranch || "main";
47219
+ const repositoryUrl = snapshot.git?.repositoryUrl?.trim() || null;
47220
+ const recentCommits = snapshot.git?.recentCommits ?? [];
47221
+ if (recentCommits.length === 0) {
47222
+ return {
47223
+ defaultBranch,
47224
+ worktrees: []
47225
+ };
47226
+ }
47227
+ const commitEntries = recentCommits.slice(0, 5).map((commit) => ({
47228
+ type: "commit",
47229
+ hash: commit.hash,
47230
+ title: commit.title,
47231
+ relatedChanges: commit.relatedChanges,
47232
+ diff: commit.diff
47233
+ }));
47234
+ const aggregateDiff = commitEntries.reduce(
47235
+ (acc, entry) => {
47236
+ acc.files += entry.diff.files;
47237
+ acc.insertions += entry.diff.insertions;
47238
+ acc.deletions += entry.diff.deletions;
47239
+ return acc;
47240
+ },
47241
+ { files: 0, insertions: 0, deletions: 0 }
47242
+ );
47243
+ return {
47244
+ defaultBranch,
47245
+ worktrees: [
47246
+ {
47247
+ path: repositoryUrl ?? "Repository URL unavailable",
47248
+ relativePath: "repo",
47249
+ branchName: "(snapshot)",
47250
+ isCurrent: true,
47251
+ ahead: 0,
47252
+ behind: 0,
47253
+ diff: aggregateDiff,
47254
+ entries: commitEntries
47255
+ }
47256
+ ]
47257
+ };
47258
+ }
47089
47259
  async function loadSnapshot() {
47090
47260
  if (snapshotCache) {
47091
47261
  return snapshotCache;
@@ -47175,6 +47345,14 @@ async function getDashboardOverview() {
47175
47345
  }
47176
47346
  };
47177
47347
  }
47348
+ const trendPointLimit = Math.max(
47349
+ 20,
47350
+ Math.min(
47351
+ 500,
47352
+ Math.trunc(snapshot.config?.dashboard?.trendPointLimit ?? DASHBOARD_TREND_POINT_LIMIT)
47353
+ )
47354
+ );
47355
+ const rightEdgeTs = snapshot.git?.latestCommitTs ?? null;
47178
47356
  const specifications = snapshot.specs.map((spec) => ({
47179
47357
  id: spec.id,
47180
47358
  name: spec.name,
@@ -47198,7 +47376,7 @@ async function getDashboardOverview() {
47198
47376
  const inProgressChanges = activeChanges.filter(
47199
47377
  (change) => change.progress.total > 0 && change.progress.completed < change.progress.total
47200
47378
  ).length;
47201
- const trends = buildStaticObjectiveTrends(snapshot);
47379
+ const trends = buildStaticObjectiveTrends(snapshot, trendPointLimit, rightEdgeTs);
47202
47380
  const hasObjectiveSpecificationTrend = trends.specifications.length > 0 || specifications.length === 0;
47203
47381
  const hasObjectiveRequirementTrend = trends.requirements.length > 0 || requirements === 0;
47204
47382
  const hasObjectiveCompletedTrend = trends.completedChanges.length > 0 || snapshot.archives.length === 0;
@@ -47223,15 +47401,12 @@ async function getDashboardOverview() {
47223
47401
  hasObjectiveCompletedTrend
47224
47402
  }),
47225
47403
  trendMeta: {
47226
- pointLimit: DASHBOARD_TREND_POINT_LIMIT,
47404
+ pointLimit: trendPointLimit,
47227
47405
  lastUpdatedAt: Date.now()
47228
47406
  },
47229
47407
  specifications,
47230
47408
  activeChanges,
47231
- git: {
47232
- defaultBranch: "main",
47233
- worktrees: []
47234
- }
47409
+ git: buildStaticGitSnapshot(snapshot)
47235
47410
  };
47236
47411
  }
47237
47412
  async function getSpec(id2) {
@@ -47345,9 +47520,35 @@ async function getOpsxTemplates(schema2) {
47345
47520
  if (!snapshot?.opsx?.templates) return null;
47346
47521
  if (!schema2) {
47347
47522
  const first = Object.keys(snapshot.opsx.templates)[0];
47348
- return first ? snapshot.opsx.templates[first] : null;
47523
+ if (!first) return null;
47524
+ const templates2 = snapshot.opsx.templates[first];
47525
+ return Object.fromEntries(
47526
+ Object.entries(templates2).map(([artifactId, template]) => [
47527
+ artifactId,
47528
+ {
47529
+ ...template,
47530
+ displayPath: template.displayPath ?? toOpsxDisplayPath(template.path, {
47531
+ source: template.source,
47532
+ projectDir: snapshot.meta.projectDir
47533
+ })
47534
+ }
47535
+ ])
47536
+ );
47349
47537
  }
47350
- return snapshot.opsx.templates[schema2] ?? null;
47538
+ const templates = snapshot.opsx.templates[schema2];
47539
+ if (!templates) return null;
47540
+ return Object.fromEntries(
47541
+ Object.entries(templates).map(([artifactId, template]) => [
47542
+ artifactId,
47543
+ {
47544
+ ...template,
47545
+ displayPath: template.displayPath ?? toOpsxDisplayPath(template.path, {
47546
+ source: template.source,
47547
+ projectDir: snapshot.meta.projectDir
47548
+ })
47549
+ }
47550
+ ])
47551
+ );
47351
47552
  }
47352
47553
  async function getOpsxSchemaFiles(name2) {
47353
47554
  const snapshot = await loadSnapshot();
@@ -47359,6 +47560,9 @@ async function getOpsxSchemaFiles(name2) {
47359
47560
  if (!schemaName) return null;
47360
47561
  const entries = [];
47361
47562
  const seen = /* @__PURE__ */ new Set();
47563
+ const schemaRoot = snapshot.opsx.schemaResolutions?.[schemaName]?.path;
47564
+ const schemaYamlContent = snapshot.opsx.schemaYamls?.[schemaName];
47565
+ const templateContentsByArtifact = snapshot.opsx.templateContents?.[schemaName] ?? {};
47362
47566
  const addEntry = (entry) => {
47363
47567
  if (seen.has(entry.path)) return;
47364
47568
  seen.add(entry.path);
@@ -47372,19 +47576,44 @@ async function getOpsxSchemaFiles(name2) {
47372
47576
  }
47373
47577
  };
47374
47578
  if (snapshot.opsx.schemaDetails?.[schemaName]) {
47375
- addEntry({ path: "schema.yaml", type: "file" });
47579
+ addEntry({ path: "schema.yaml", type: "file", content: schemaYamlContent });
47376
47580
  }
47377
47581
  const templates = snapshot.opsx.templates?.[schemaName];
47378
47582
  if (templates) {
47379
- Object.values(templates).forEach((template) => {
47380
- addDirEntries(template.path);
47381
- addEntry({ path: template.path, type: "file" });
47583
+ Object.entries(templates).forEach(([artifactId, template]) => {
47584
+ const relativePath = toSchemaRelativePath(template.path, schemaRoot);
47585
+ const templateContent = templateContentsByArtifact[artifactId]?.content ?? void 0;
47586
+ addDirEntries(relativePath);
47587
+ addEntry({ path: relativePath, type: "file", content: templateContent });
47382
47588
  });
47383
47589
  }
47384
47590
  return entries;
47385
47591
  }
47386
- async function getOpsxTemplateContents() {
47387
- return null;
47592
+ async function getOpsxTemplateContents(schema2) {
47593
+ const snapshot = await loadSnapshot();
47594
+ if (!snapshot?.opsx) return null;
47595
+ const targetSchema = schema2 ?? snapshot.opsx.schemas?.[0]?.name ?? Object.keys(snapshot.opsx.templates ?? {})[0] ?? null;
47596
+ if (!targetSchema) return null;
47597
+ const templates = snapshot.opsx.templates?.[targetSchema] ?? {};
47598
+ const contents = snapshot.opsx.templateContents?.[targetSchema] ?? {};
47599
+ const merged = Object.fromEntries(
47600
+ Object.entries(templates).map(([artifactId, template]) => {
47601
+ const contentInfo = contents[artifactId];
47602
+ return [
47603
+ artifactId,
47604
+ {
47605
+ content: contentInfo?.content ?? null,
47606
+ path: template.path,
47607
+ displayPath: contentInfo?.displayPath ?? template.displayPath ?? toOpsxDisplayPath(template.path, {
47608
+ source: template.source,
47609
+ projectDir: snapshot.meta.projectDir
47610
+ }),
47611
+ source: template.source
47612
+ }
47613
+ ];
47614
+ })
47615
+ );
47616
+ return merged;
47388
47617
  }
47389
47618
  async function getOpsxChangeList() {
47390
47619
  const snapshot = await loadSnapshot();
@@ -47435,10 +47664,17 @@ async function getOpsxGlobArtifactFiles(changeId, outputPath) {
47435
47664
  }
47436
47665
  function StatusIndicator() {
47437
47666
  if (isStaticMode()) {
47438
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "status-indicator flex items-center gap-1.5 text-xs", children: [
47439
- /* @__PURE__ */ jsxRuntimeExports.jsx(Camera, { className: "h-3.5 w-3.5 text-blue-500" }),
47440
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "status-text text-blue-600", children: "Static" })
47441
- ] });
47667
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
47668
+ "div",
47669
+ {
47670
+ className: "status-indicator flex items-center gap-1.5 text-xs",
47671
+ title: "Live features disabled (no file watching, task toggling, or AI integration)",
47672
+ children: [
47673
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Camera, { className: "h-3.5 w-3.5 text-blue-500" }),
47674
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "status-text text-blue-600", children: "Static" })
47675
+ ]
47676
+ }
47677
+ );
47442
47678
  }
47443
47679
  const status = useServerStatus();
47444
47680
  const reconnect = useManualReconnect();
@@ -47472,7 +47708,7 @@ function StatusIndicator() {
47472
47708
  function DesktopStatusBar() {
47473
47709
  const staticMode = isStaticMode();
47474
47710
  const [generatedAt, setGeneratedAt] = reactExports.useState("Unknown");
47475
- const [snapshotProjectDir, setSnapshotProjectDir] = reactExports.useState(null);
47711
+ const [snapshotRepositoryUrl, setSnapshotRepositoryUrl] = reactExports.useState(null);
47476
47712
  reactExports.useEffect(() => {
47477
47713
  if (!staticMode) return;
47478
47714
  void loadSnapshot().then((snapshot) => {
@@ -47481,35 +47717,32 @@ function DesktopStatusBar() {
47481
47717
  } else {
47482
47718
  setGeneratedAt("Unknown");
47483
47719
  }
47484
- setSnapshotProjectDir(snapshot?.meta?.projectDir ?? null);
47720
+ setSnapshotRepositoryUrl(snapshot?.git?.repositoryUrl ?? null);
47485
47721
  }).catch(() => {
47486
47722
  setGeneratedAt("Unknown");
47487
- setSnapshotProjectDir(null);
47723
+ setSnapshotRepositoryUrl(null);
47488
47724
  });
47489
47725
  }, [staticMode]);
47490
47726
  if (staticMode) {
47491
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "desktop-status border-border bg-muted/30 text-muted-foreground flex h-8 items-center justify-between gap-4 border-t px-4 text-xs", children: [
47492
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-4", children: [
47493
- /* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, {}),
47494
- /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
47495
- "Generated: ",
47496
- generatedAt
47497
- ] }),
47498
- snapshotProjectDir && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
47499
- /* @__PURE__ */ jsxRuntimeExports.jsx(FolderOpen, { className: "h-3.5 w-3.5 shrink-0" }),
47500
- /* @__PURE__ */ jsxRuntimeExports.jsx(
47501
- PathMarquee,
47502
- {
47503
- children: snapshotProjectDir,
47504
- maxWidth: 280,
47505
- duration: 12,
47506
- className: "text-xs"
47507
- }
47508
- )
47509
- ] })
47727
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "desktop-status border-border bg-muted/30 text-muted-foreground flex h-8 items-center justify-between gap-4 border-t px-4 text-xs", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-4", children: [
47728
+ /* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, {}),
47729
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "truncate", children: [
47730
+ "Generated: ",
47731
+ generatedAt
47510
47732
  ] }),
47511
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: "Live features disabled (no file watching, task toggling, or AI integration)" })
47512
- ] });
47733
+ snapshotRepositoryUrl && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-w-0 items-center gap-1.5", children: [
47734
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Github, { className: "h-3.5 w-3.5 shrink-0" }),
47735
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
47736
+ PathMarquee,
47737
+ {
47738
+ children: snapshotRepositoryUrl,
47739
+ maxWidth: 280,
47740
+ duration: 12,
47741
+ className: "text-xs"
47742
+ }
47743
+ )
47744
+ ] })
47745
+ ] }) });
47513
47746
  }
47514
47747
  const status = useServerStatus();
47515
47748
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "desktop-status border-border bg-muted/30 text-muted-foreground flex h-8 items-center justify-between gap-4 border-t px-4 text-xs", children: [
@@ -47766,7 +47999,7 @@ function Dialog({
47766
47999
  "div",
47767
48000
  {
47768
48001
  ref: panelRef,
47769
- className: `bg-background relative flex h-full w-[calc(100%-0.5rem)] max-w-2xl flex-col overflow-hidden rounded-[var(--openspec-dialog-radius,0.75rem)] border shadow-xl ${borderClass} ${className}`,
48002
+ className: `bg-background relative flex h-fit w-[calc(100%-0.5rem)] max-w-2xl flex-col overflow-hidden rounded-[var(--openspec-dialog-radius,0.75rem)] border shadow-xl ${borderClass} ${className}`,
47770
48003
  style: { maxHeight },
47771
48004
  children: [
47772
48005
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-border flex flex-none shrink-0 items-center justify-between border-b px-4 py-3", children: [
@@ -104582,55 +104815,140 @@ function CodeEditor({
104582
104815
  }
104583
104816
  );
104584
104817
  }
104585
- function ContextMenu({ open, items, position: position2, onClose }) {
104818
+ function resolveAnchorPosition(anchor) {
104819
+ if (!anchor) return null;
104820
+ if (anchor.type === "point") return { x: anchor.x, y: anchor.y };
104821
+ const element2 = anchor.element;
104822
+ if (!element2) return null;
104823
+ const rect = element2.getBoundingClientRect();
104824
+ const placement = anchor.placement ?? "bottom-start";
104825
+ switch (placement) {
104826
+ case "bottom-end":
104827
+ return { x: rect.right, y: rect.bottom };
104828
+ case "top-start":
104829
+ return { x: rect.left, y: rect.top };
104830
+ case "top-end":
104831
+ return { x: rect.right, y: rect.top };
104832
+ case "bottom-start":
104833
+ default:
104834
+ return { x: rect.left, y: rect.bottom };
104835
+ }
104836
+ }
104837
+ function clampWithinBounds(position2, menuRect, boundaryRect) {
104838
+ const margin = 12;
104839
+ const left = boundaryRect?.left ?? 0;
104840
+ const top2 = boundaryRect?.top ?? 0;
104841
+ const right = boundaryRect?.right ?? window.innerWidth;
104842
+ const bottom = boundaryRect?.bottom ?? window.innerHeight;
104843
+ const minX = left + margin;
104844
+ const minY = top2 + margin;
104845
+ const maxX = Math.max(minX, right - menuRect.width - margin);
104846
+ const maxY = Math.max(minY, bottom - menuRect.height - margin);
104847
+ return {
104848
+ x: Math.max(minX, Math.min(position2.x, maxX)),
104849
+ y: Math.max(minY, Math.min(position2.y, maxY))
104850
+ };
104851
+ }
104852
+ function toLocalPointStyle(point2, wrapperElement) {
104853
+ if (!wrapperElement) {
104854
+ return { position: "fixed", left: point2.x, top: point2.y };
104855
+ }
104856
+ const rect = wrapperElement.getBoundingClientRect();
104857
+ return {
104858
+ position: "absolute",
104859
+ left: point2.x - rect.left + wrapperElement.scrollLeft,
104860
+ top: point2.y - rect.top + wrapperElement.scrollTop
104861
+ };
104862
+ }
104863
+ function getPlacement(anchor) {
104864
+ if (anchor?.type === "target") return anchor.placement ?? "bottom-start";
104865
+ return "bottom-start";
104866
+ }
104867
+ function ContextMenu({
104868
+ open,
104869
+ items,
104870
+ anchor,
104871
+ wrapperElement,
104872
+ boundaryElement,
104873
+ position: position2,
104874
+ onClose
104875
+ }) {
104586
104876
  const menuRef = reactExports.useRef(null);
104587
- const anchorRef = reactExports.useRef(null);
104877
+ const fallbackAnchor = reactExports.useMemo(
104878
+ () => position2 ? { type: "point", x: position2.x, y: position2.y } : null,
104879
+ [position2]
104880
+ );
104881
+ const activeAnchor = anchor ?? fallbackAnchor;
104882
+ const hostElement = wrapperElement ?? boundaryElement ?? null;
104883
+ const anchorPosition = reactExports.useMemo(() => resolveAnchorPosition(activeAnchor), [activeAnchor]);
104588
104884
  const [adjustedPosition, setAdjustedPosition] = reactExports.useState(null);
104885
+ const menuId = reactExports.useId().replace(/[^a-zA-Z0-9_-]/g, "");
104886
+ const anchorName = `--context-menu-anchor-${menuId}`;
104887
+ const menuDataId = `context-menu-${menuId}`;
104888
+ const placement = getPlacement(activeAnchor);
104589
104889
  const visibleItems = reactExports.useMemo(() => items.filter((item) => item.label.length > 0), [items]);
104590
- const shouldRender = open && !!position2 && visibleItems.length > 0;
104890
+ const shouldRender = open && !!anchorPosition && visibleItems.length > 0;
104591
104891
  reactExports.useLayoutEffect(() => {
104592
- if (!open || !position2) {
104892
+ if (!open || !anchorPosition) {
104593
104893
  setAdjustedPosition(null);
104594
104894
  return;
104595
104895
  }
104596
104896
  const menu = menuRef.current;
104597
104897
  if (!menu) {
104598
- setAdjustedPosition(position2);
104898
+ setAdjustedPosition(anchorPosition);
104599
104899
  return;
104600
104900
  }
104601
- const rect = menu.getBoundingClientRect();
104602
- const margin = 12;
104603
- const maxX = window.innerWidth - rect.width - margin;
104604
- const maxY = window.innerHeight - rect.height - margin;
104605
- const x = Math.max(margin, Math.min(position2.x, maxX));
104606
- const y = Math.max(margin, Math.min(position2.y, maxY));
104607
- setAdjustedPosition({ x, y });
104608
- }, [open, position2]);
104901
+ const boundaryRect = hostElement?.getBoundingClientRect() ?? null;
104902
+ const clamped = clampWithinBounds(anchorPosition, menu.getBoundingClientRect(), boundaryRect);
104903
+ setAdjustedPosition(clamped);
104904
+ }, [anchorPosition, hostElement, open]);
104609
104905
  reactExports.useEffect(() => {
104610
- const menu = menuRef.current;
104611
- if (!menu || !open) return;
104612
- const handleToggle = (event) => {
104613
- if (!(event instanceof Event)) return;
104614
- const target = event.target;
104615
- if (target instanceof HTMLElement && !target.matches(":popover-open")) {
104616
- onClose();
104617
- }
104618
- };
104619
104906
  const handleScroll = () => onClose();
104620
- menu.addEventListener("toggle", handleToggle);
104621
104907
  window.addEventListener("scroll", handleScroll, true);
104622
104908
  window.addEventListener("resize", handleScroll);
104623
104909
  return () => {
104624
- menu.removeEventListener("toggle", handleToggle);
104625
104910
  window.removeEventListener("scroll", handleScroll, true);
104626
104911
  window.removeEventListener("resize", handleScroll);
104627
104912
  };
104628
104913
  }, [open, onClose]);
104629
- const menuStyle = adjustedPosition ?? position2 ?? {
104630
- x: 0,
104631
- y: 0
104632
- };
104633
- const menuStyleValues = { left: menuStyle.x, top: menuStyle.y };
104914
+ reactExports.useEffect(() => {
104915
+ if (!shouldRender) return;
104916
+ const handlePointerDown = (event) => {
104917
+ if (event.button === 2) return;
104918
+ const target = event.target;
104919
+ if (!(target instanceof Node)) return;
104920
+ const menu = menuRef.current;
104921
+ if (menu?.contains(target)) return;
104922
+ if (activeAnchor?.type === "target" && activeAnchor.element?.contains(target)) return;
104923
+ onClose();
104924
+ };
104925
+ const handleKeyDown = (event) => {
104926
+ if (event.key === "Escape") onClose();
104927
+ };
104928
+ window.addEventListener("pointerdown", handlePointerDown, true);
104929
+ window.addEventListener("keydown", handleKeyDown);
104930
+ return () => {
104931
+ window.removeEventListener("pointerdown", handlePointerDown, true);
104932
+ window.removeEventListener("keydown", handleKeyDown);
104933
+ };
104934
+ }, [activeAnchor, onClose, shouldRender]);
104935
+ reactExports.useEffect(() => {
104936
+ if (!open || activeAnchor?.type !== "target" || !activeAnchor.element) return;
104937
+ const target = activeAnchor.element;
104938
+ const previousAnchorName = target.style.getPropertyValue("anchor-name");
104939
+ const previousPriority = target.style.getPropertyPriority("anchor-name");
104940
+ target.style.setProperty("anchor-name", anchorName);
104941
+ return () => {
104942
+ if (previousAnchorName) {
104943
+ target.style.setProperty("anchor-name", previousAnchorName, previousPriority);
104944
+ } else {
104945
+ target.style.removeProperty("anchor-name");
104946
+ }
104947
+ };
104948
+ }, [activeAnchor, anchorName, open]);
104949
+ const resolvedPosition = adjustedPosition ?? anchorPosition;
104950
+ const fallbackStyle = resolvedPosition === null ? { left: 0, top: 0 } : { left: resolvedPosition.x, top: resolvedPosition.y };
104951
+ const syntheticAnchorStyle = activeAnchor?.type === "point" && anchorPosition ? toLocalPointStyle(anchorPosition, hostElement) : null;
104634
104952
  reactExports.useEffect(() => {
104635
104953
  const menu = menuRef.current;
104636
104954
  if (!menu) return;
@@ -104638,44 +104956,83 @@ function ContextMenu({ open, items, position: position2, onClose }) {
104638
104956
  if (!menu.matches(":popover-open")) {
104639
104957
  menu.showPopover?.();
104640
104958
  }
104641
- } else {
104642
- if (menu.matches(":popover-open")) {
104643
- menu.hidePopover?.();
104644
- }
104959
+ } else if (menu.matches(":popover-open")) {
104960
+ menu.hidePopover?.();
104645
104961
  }
104646
- }, [shouldRender, position2, adjustedPosition]);
104647
- const styles = String.raw;
104962
+ }, [shouldRender]);
104648
104963
  if (!shouldRender) return null;
104964
+ const styles = String.raw;
104965
+ const anchorPlacementCSS = placement === "top-start" ? "bottom: anchor(top); left: anchor(left); top: auto; right: auto;" : placement === "top-end" ? "bottom: anchor(top); left: anchor(right); top: auto; right: auto;" : placement === "bottom-end" ? "top: anchor(bottom); left: anchor(right); bottom: auto; right: auto;" : "top: anchor(bottom); left: anchor(left); bottom: auto; right: auto;";
104649
104966
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
104650
104967
  /* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: styles`
104651
- .context-menu-anchor {
104652
- position: fixed;
104968
+ .context-menu-anchor[data-context-menu-id='${menuDataId}'] {
104653
104969
  width: 1px;
104654
104970
  height: 1px;
104655
- anchor-name: --context-menu-anchor;
104656
104971
  pointer-events: none;
104657
104972
  z-index: 50;
104973
+ anchor-name: ${anchorName};
104658
104974
  }
104659
- .context-menu-popover {
104660
- position: fixed;
104975
+ .context-menu-popover[data-context-menu-id='${menuDataId}'] {
104976
+ position: absolute;
104661
104977
  inset: auto;
104662
104978
  }
104663
- @supports (position-anchor: --context-menu-anchor) {
104664
- .context-menu-popover {
104665
- position-anchor: --context-menu-anchor;
104979
+ @supports (position-anchor: ${anchorName}) {
104980
+ .context-menu-popover[data-context-menu-id='${menuDataId}'] {
104981
+ position-anchor: ${anchorName};
104982
+ ${anchorPlacementCSS}
104983
+ }
104984
+ }
104985
+ @supports (position-try-fallbacks: --context-menu-right-bottom) {
104986
+ @position-try --context-menu-right-bottom {
104666
104987
  top: anchor(bottom);
104667
- left: anchor(left);
104988
+ left: anchor(right);
104989
+ right: auto;
104990
+ bottom: auto;
104991
+ }
104992
+ @position-try --context-menu-left-bottom {
104993
+ top: anchor(bottom);
104994
+ right: anchor(left);
104995
+ left: auto;
104996
+ bottom: auto;
104997
+ }
104998
+ @position-try --context-menu-left-top {
104999
+ bottom: anchor(top);
105000
+ right: anchor(left);
105001
+ left: auto;
105002
+ top: auto;
105003
+ }
105004
+ @position-try --context-menu-right-top {
105005
+ bottom: anchor(top);
105006
+ left: anchor(right);
105007
+ right: auto;
105008
+ top: auto;
105009
+ }
105010
+ .context-menu-popover[data-context-menu-id='${menuDataId}'] {
105011
+ position-try-fallbacks:
105012
+ --context-menu-right-bottom,
105013
+ --context-menu-left-bottom,
105014
+ --context-menu-left-top,
105015
+ --context-menu-right-top;
104668
105016
  }
104669
105017
  }
104670
105018
  ` }),
104671
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: anchorRef, className: "context-menu-anchor", style: menuStyleValues, "aria-hidden": true }),
105019
+ syntheticAnchorStyle && /* @__PURE__ */ jsxRuntimeExports.jsx(
105020
+ "div",
105021
+ {
105022
+ "data-context-menu-id": menuDataId,
105023
+ className: "context-menu-anchor",
105024
+ style: syntheticAnchorStyle,
105025
+ "aria-hidden": true
105026
+ }
105027
+ ),
104672
105028
  /* @__PURE__ */ jsxRuntimeExports.jsx(
104673
105029
  "div",
104674
105030
  {
104675
105031
  ref: menuRef,
104676
- popover: "auto",
105032
+ popover: "manual",
105033
+ "data-context-menu-id": menuDataId,
104677
105034
  className: "context-menu-popover border-border bg-card text-foreground z-50 min-w-[180px] rounded-md border p-1 shadow-lg",
104678
- style: menuStyleValues,
105035
+ style: fallbackStyle,
104679
105036
  children: visibleItems.map((item) => {
104680
105037
  const isDisabled = item.disabled;
104681
105038
  const toneClass = item.tone === "destructive" ? "text-destructive hover:bg-destructive/10" : "";
@@ -104702,6 +105059,36 @@ function ContextMenu({ open, items, position: position2, onClose }) {
104702
105059
  )
104703
105060
  ] });
104704
105061
  }
105062
+ const ContextMenuWrapper = reactExports.forwardRef(
105063
+ function ContextMenuWrapper2({ children, className, ...props }, ref) {
105064
+ const mergedClassName = className ? `relative ${className}` : "relative";
105065
+ return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref, "data-context-menu-wrapper": "", className: mergedClassName, ...props, children });
105066
+ }
105067
+ );
105068
+ const ContextMenuTargeter = reactExports.forwardRef(
105069
+ function ContextMenuTargeter2({ children, className, ...props }, ref) {
105070
+ return /* @__PURE__ */ jsxRuntimeExports.jsx(
105071
+ "span",
105072
+ {
105073
+ ref,
105074
+ "data-context-menu-targeter": "",
105075
+ className: className ?? "inline-flex",
105076
+ ...props,
105077
+ children
105078
+ }
105079
+ );
105080
+ }
105081
+ );
105082
+ function toContextMenuItems(items) {
105083
+ return items.map((item) => ({
105084
+ id: item.id,
105085
+ label: item.label,
105086
+ icon: item.icon,
105087
+ disabled: item.disabled,
105088
+ tone: item.tone,
105089
+ onSelect: item.onSelect
105090
+ }));
105091
+ }
104705
105092
  const css = String.raw;
104706
105093
  const layoutStyles = css`
104707
105094
  /* 窄屏:单列布局 */
@@ -104728,7 +105115,7 @@ const layoutStyles = css`
104728
105115
  @container (min-width: 768px) {
104729
105116
  .fev-layout {
104730
105117
  display: grid;
104731
- grid-template-columns: 1fr 240px;
105118
+ grid-template-columns: minmax(0, 1fr) minmax(240px, clamp(240px, 30%, 420px));
104732
105119
  gap: 1rem;
104733
105120
  }
104734
105121
  .fev-sidebar-tabs {
@@ -104776,72 +105163,145 @@ function getParentPath$1(path2) {
104776
105163
  const parts = path2.split("/");
104777
105164
  return parts.slice(0, -1).join("/");
104778
105165
  }
105166
+ function splitBreadcrumbRoot(rootPath) {
105167
+ const normalized = (rootPath ?? "").trim().replace(/\/+$/g, "");
105168
+ if (!normalized) return [];
105169
+ const scopedMatch = /^([A-Za-z][A-Za-z0-9+.-]*:)(.*)$/.exec(normalized);
105170
+ if (!scopedMatch) {
105171
+ return normalized.split("/").filter(Boolean);
105172
+ }
105173
+ const prefix2 = scopedMatch[1];
105174
+ const suffix = scopedMatch[2].replace(/^\/+/g, "");
105175
+ const segments = [prefix2];
105176
+ if (suffix.length > 0) {
105177
+ segments.push(...suffix.split("/").filter(Boolean));
105178
+ }
105179
+ return segments;
105180
+ }
104779
105181
  function Breadcrumb({
104780
105182
  path: path2,
105183
+ rootPath,
104781
105184
  entries,
104782
105185
  onNavigate
104783
105186
  }) {
104784
- const parts = path2.split("/");
105187
+ const rootSegments = splitBreadcrumbRoot(rootPath).map((name2, index2) => ({
105188
+ key: `root-${index2}-${name2}`,
105189
+ kind: "root",
105190
+ name: name2
105191
+ }));
105192
+ const parts = path2.split("/").filter(Boolean);
104785
105193
  const isMarkdown = path2.endsWith(".md");
104786
- const segments = [];
105194
+ const fileSegments = [];
104787
105195
  for (let i = 0; i < parts.length; i++) {
104788
105196
  const segmentPath = parts.slice(0, i + 1).join("/");
104789
105197
  const isFile = i === parts.length - 1;
104790
- segments.push({ name: parts[i], path: segmentPath, isFile });
105198
+ fileSegments.push({ key: `path-${segmentPath}`, name: parts[i], path: segmentPath, isFile });
104791
105199
  }
105200
+ const segments = [...rootSegments, ...fileSegments];
105201
+ const rootCount = rootSegments.length;
104792
105202
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "border-border/50 bg-muted/20 flex items-center gap-1 overflow-x-auto border-b px-3 py-2 text-xs", children: segments.map((segment, i) => {
104793
105203
  const isLast = i === segments.length - 1;
104794
- const canNavigate = !isLast && entries.some((e) => e.type === "file" && e.path.startsWith(segment.path + "/"));
105204
+ const isRootSegment = i < rootCount;
105205
+ const canNavigate = !isRootSegment && !isLast && entries.some(
105206
+ (e) => e.type === "file" && e.path.startsWith(fileSegments[i - rootCount].path + "/")
105207
+ );
104795
105208
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1", children: [
104796
105209
  i > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(ChevronRight, { className: "text-muted-foreground/50 h-3 w-3" }),
104797
- isLast ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-foreground flex items-center gap-1.5", children: [
104798
- segment.isFile ? isMarkdown ? /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(File, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "h-3.5 w-3.5" }),
105210
+ isRootSegment ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground flex items-center gap-1.5", children: [
105211
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "h-3.5 w-3.5" }),
105212
+ segment.name
105213
+ ] }) : isLast ? /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-foreground flex items-center gap-1.5", children: [
105214
+ fileSegments[i - rootCount].isFile ? isMarkdown ? /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(File, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "h-3.5 w-3.5" }),
104799
105215
  segment.name
104800
105216
  ] }) : canNavigate ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
104801
105217
  "button",
104802
105218
  {
104803
105219
  onClick: () => {
104804
105220
  const firstFile = entries.find(
104805
- (e) => e.type === "file" && e.path.startsWith(segment.path + "/")
105221
+ (e) => e.type === "file" && e.path.startsWith(fileSegments[i - rootCount].path + "/")
104806
105222
  );
104807
105223
  if (firstFile) onNavigate(firstFile.path);
104808
105224
  },
104809
105225
  className: "text-muted-foreground hover:text-foreground flex items-center gap-1.5 transition-colors",
104810
105226
  children: [
104811
105227
  /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "h-3.5 w-3.5" }),
104812
- segment.name
105228
+ fileSegments[i - rootCount].name
104813
105229
  ]
104814
105230
  }
104815
105231
  ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground flex items-center gap-1.5", children: [
104816
105232
  /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "h-3.5 w-3.5" }),
104817
- segment.name
105233
+ fileSegments[i - rootCount].name
104818
105234
  ] })
104819
- ] }, segment.path);
105235
+ ] }, segment.key);
104820
105236
  }) });
104821
105237
  }
104822
105238
  function FileTabs({
104823
105239
  entries,
104824
105240
  selectedPath,
104825
- onSelect
105241
+ onSelect,
105242
+ entryActions
104826
105243
  }) {
105244
+ const wrapperRef = reactExports.useRef(null);
105245
+ const [menuAnchor, setMenuAnchor] = reactExports.useState(null);
104827
105246
  const files = entries.filter((e) => e.type === "file");
104828
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "scrollbar-thin scrollbar-track-transparent border-border bg-muted/30 flex gap-1 overflow-x-auto rounded-md border p-1", children: files.map((entry) => {
104829
- const isActive = entry.path === selectedPath;
104830
- const isMarkdown = entry.path.endsWith(".md");
104831
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
104832
- "button",
104833
- {
104834
- onClick: () => onSelect(entry.path),
104835
- title: entry.path,
104836
- className: `flex shrink-0 items-center gap-1.5 rounded px-2.5 py-1.5 text-xs transition-colors ${isActive ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:bg-background/50 hover:text-foreground"}`,
104837
- children: [
104838
- isMarkdown ? /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(File, { className: "h-3.5 w-3.5" }),
104839
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "max-w-[120px] truncate", children: getFileName(entry.path) })
104840
- ]
104841
- },
104842
- entry.path
104843
- );
104844
- }) });
105247
+ const selectedFile = files.find((entry) => entry.path === selectedPath) ?? files[0] ?? null;
105248
+ const selectedActions = selectedFile && entryActions ? toContextMenuItems(entryActions(selectedFile)) : [];
105249
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
105250
+ ContextMenuWrapper,
105251
+ {
105252
+ ref: wrapperRef,
105253
+ className: "border-border bg-muted/30 flex items-stretch overflow-hidden rounded-md border",
105254
+ children: [
105255
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "scrollbar-thin scrollbar-track-transparent flex min-w-0 flex-1 gap-1 overflow-x-auto px-1 py-1", children: files.map((entry) => {
105256
+ const isActive = entry.path === selectedPath;
105257
+ const isMarkdown = entry.path.endsWith(".md");
105258
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
105259
+ "button",
105260
+ {
105261
+ onClick: () => onSelect(entry.path),
105262
+ title: entry.path,
105263
+ className: `flex shrink-0 items-center gap-1.5 rounded px-2.5 py-1.5 text-xs transition-colors ${isActive ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:bg-background/50 hover:text-foreground"}`,
105264
+ children: [
105265
+ isMarkdown ? /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(File, { className: "h-3.5 w-3.5" }),
105266
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "max-w-[120px] truncate", children: getFileName(entry.path) })
105267
+ ]
105268
+ },
105269
+ entry.path
105270
+ );
105271
+ }) }),
105272
+ selectedActions.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuTargeter, { className: "text-muted-foreground inline-flex items-center", children: [
105273
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { "aria-hidden": "true", className: "bg-border/80 block w-px self-stretch" }),
105274
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
105275
+ "button",
105276
+ {
105277
+ type: "button",
105278
+ onClick: (event) => {
105279
+ setMenuAnchor({
105280
+ type: "target",
105281
+ element: event.currentTarget,
105282
+ placement: "bottom-end"
105283
+ });
105284
+ },
105285
+ className: "text-muted-foreground hover:text-foreground hover:bg-background/80 inline-flex h-7 w-7 shrink-0 items-center justify-center rounded",
105286
+ "aria-label": "Current file actions",
105287
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { className: "h-4 w-4" })
105288
+ }
105289
+ )
105290
+ ] }),
105291
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
105292
+ ContextMenu,
105293
+ {
105294
+ open: menuAnchor !== null,
105295
+ items: selectedActions,
105296
+ anchor: menuAnchor,
105297
+ wrapperElement: wrapperRef.current,
105298
+ boundaryElement: wrapperRef.current,
105299
+ onClose: () => setMenuAnchor(null)
105300
+ }
105301
+ )
105302
+ ]
105303
+ }
105304
+ );
104845
105305
  }
104846
105306
  function FileTree({
104847
105307
  entries,
@@ -104852,6 +105312,7 @@ function FileTree({
104852
105312
  entryActions
104853
105313
  }) {
104854
105314
  const [menuState, setMenuState] = reactExports.useState(null);
105315
+ const wrapperRef = reactExports.useRef(null);
104855
105316
  const getIndentLevel = (entry) => {
104856
105317
  const parentPath = getParentPath$1(entry.path);
104857
105318
  if (!parentPath) return 0;
@@ -104862,90 +105323,94 @@ function FileTree({
104862
105323
  }
104863
105324
  return entry.path.split("/").length - 1;
104864
105325
  };
104865
- const openMenu = (entry, position2, items) => {
104866
- const mapped = items.map((item) => ({
104867
- id: item.id,
104868
- label: item.label,
104869
- icon: item.icon,
104870
- disabled: item.disabled,
104871
- tone: item.tone,
104872
- onSelect: item.onSelect
104873
- }));
105326
+ const openMenu = (anchor, items) => {
105327
+ const mapped = toContextMenuItems(items);
104874
105328
  if (mapped.length === 0) return;
104875
- setMenuState({ entry, items: mapped, position: position2 });
105329
+ setMenuState({ anchor, items: mapped });
104876
105330
  };
104877
105331
  const closeMenu = () => setMenuState(null);
104878
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-border bg-muted/30 flex h-full flex-col rounded-md border", children: [
104879
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-border/50 text-muted-foreground flex items-center justify-between border-b px-3 py-2 text-xs font-medium", children: [
104880
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: headerLabel }),
104881
- headerActions
104882
- ] }),
104883
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "scrollbar-thin scrollbar-track-transparent flex-1 overflow-y-auto", children: entries.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground px-3 py-2 text-xs", children: "No files yet." }) : entries.map((entry) => {
104884
- const depth = getIndentLevel(entry);
104885
- const isActive = entry.path === selectedPath;
104886
- const isFile = entry.type === "file";
104887
- const actions = entryActions ? entryActions(entry) : [];
104888
- const showActions = actions.length > 0;
104889
- const icon = isFile ? entry.path.endsWith(".md") ? /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-4 w-4 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(File, { className: "h-4 w-4 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "h-4 w-4 shrink-0" });
104890
- return /* @__PURE__ */ jsxRuntimeExports.jsxs(
104891
- "div",
104892
- {
104893
- className: `group flex w-full items-center gap-2 px-2 py-1 text-sm transition-colors ${isActive ? "bg-primary/10 text-foreground" : isFile ? "text-muted-foreground hover:bg-muted/50 hover:text-foreground" : "text-muted-foreground"}`,
104894
- onContextMenu: (event) => {
104895
- if (!showActions) return;
104896
- event.preventDefault();
104897
- if (isFile) onSelect(entry.path);
104898
- openMenu(entry, { x: event.clientX, y: event.clientY }, actions);
104899
- },
104900
- children: [
104901
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
104902
- "button",
104903
- {
104904
- type: "button",
104905
- disabled: !isFile,
104906
- onClick: () => isFile && onSelect(entry.path),
104907
- className: `flex flex-1 items-center gap-2 text-left ${!isFile ? "cursor-default" : ""}`,
104908
- style: { paddingLeft: 4 + depth * 14 },
104909
- children: [
104910
- icon,
104911
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `truncate ${!isFile ? "text-foreground font-medium" : ""}`, children: getFileName(entry.path) })
104912
- ]
104913
- }
104914
- ),
104915
- showActions && /* @__PURE__ */ jsxRuntimeExports.jsx(
104916
- "button",
104917
- {
104918
- type: "button",
104919
- onClick: (event) => {
104920
- event.stopPropagation();
104921
- const rect = event.currentTarget.getBoundingClientRect();
104922
- openMenu(entry, { x: rect.right, y: rect.bottom }, actions);
104923
- },
104924
- className: "hover:bg-muted text-muted-foreground flex h-7 w-7 items-center justify-center rounded-md",
104925
- "aria-label": "File actions",
104926
- children: /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { className: "h-4 w-4" })
104927
- }
104928
- )
104929
- ]
104930
- },
104931
- entry.path
104932
- );
104933
- }) }),
104934
- /* @__PURE__ */ jsxRuntimeExports.jsx(
104935
- ContextMenu,
104936
- {
104937
- open: !!menuState,
104938
- items: menuState?.items ?? [],
104939
- position: menuState?.position ?? null,
104940
- onClose: closeMenu
104941
- }
104942
- )
104943
- ] });
105332
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
105333
+ ContextMenuWrapper,
105334
+ {
105335
+ ref: wrapperRef,
105336
+ className: "border-border bg-muted/30 flex h-full flex-col rounded-md border",
105337
+ children: [
105338
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-border/50 text-muted-foreground flex items-center justify-between border-b px-3 py-2 text-xs font-medium", children: [
105339
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "min-w-0 truncate", children: headerLabel }),
105340
+ headerActions
105341
+ ] }),
105342
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "scrollbar-thin scrollbar-track-transparent flex-1 overflow-y-auto", children: entries.length === 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground px-3 py-2 text-xs", children: "No files yet." }) : entries.map((entry) => {
105343
+ const depth = getIndentLevel(entry);
105344
+ const isActive = entry.path === selectedPath;
105345
+ const isFile = entry.type === "file";
105346
+ const actions = entryActions ? entryActions(entry) : [];
105347
+ const showActions = actions.length > 0;
105348
+ const icon = isFile ? entry.path.endsWith(".md") ? /* @__PURE__ */ jsxRuntimeExports.jsx(FileText, { className: "h-4 w-4 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(File, { className: "h-4 w-4 shrink-0" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(Folder, { className: "h-4 w-4 shrink-0" });
105349
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs(
105350
+ "div",
105351
+ {
105352
+ className: `group flex w-full items-center gap-2 px-2 py-1 text-sm transition-colors ${isActive ? "bg-primary/10 text-foreground" : isFile ? "text-muted-foreground hover:bg-muted/50 hover:text-foreground" : "text-muted-foreground"}`,
105353
+ onContextMenu: (event) => {
105354
+ if (!showActions) return;
105355
+ event.preventDefault();
105356
+ if (isFile) onSelect(entry.path);
105357
+ openMenu({ type: "point", x: event.clientX, y: event.clientY }, actions);
105358
+ },
105359
+ children: [
105360
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(
105361
+ "button",
105362
+ {
105363
+ type: "button",
105364
+ disabled: !isFile,
105365
+ onClick: () => isFile && onSelect(entry.path),
105366
+ className: `flex flex-1 items-center gap-2 text-left ${!isFile ? "cursor-default" : ""}`,
105367
+ style: { paddingLeft: 4 + depth * 14 },
105368
+ children: [
105369
+ icon,
105370
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `truncate ${!isFile ? "text-foreground font-medium" : ""}`, children: getFileName(entry.path) })
105371
+ ]
105372
+ }
105373
+ ),
105374
+ showActions && /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuTargeter, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
105375
+ "button",
105376
+ {
105377
+ type: "button",
105378
+ onClick: (event) => {
105379
+ event.stopPropagation();
105380
+ openMenu(
105381
+ { type: "target", element: event.currentTarget, placement: "bottom-end" },
105382
+ actions
105383
+ );
105384
+ },
105385
+ className: "hover:bg-muted text-muted-foreground flex h-7 w-7 items-center justify-center rounded-md",
105386
+ "aria-label": "File actions",
105387
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { className: "h-4 w-4" })
105388
+ }
105389
+ ) })
105390
+ ]
105391
+ },
105392
+ entry.path
105393
+ );
105394
+ }) }),
105395
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
105396
+ ContextMenu,
105397
+ {
105398
+ open: !!menuState,
105399
+ items: menuState?.items ?? [],
105400
+ anchor: menuState?.anchor ?? null,
105401
+ boundaryElement: wrapperRef.current,
105402
+ onClose: closeMenu
105403
+ }
105404
+ )
105405
+ ]
105406
+ }
105407
+ );
104944
105408
  }
104945
105409
  function FileExplorer({
104946
105410
  entries,
104947
105411
  selectedPath,
104948
105412
  onSelect,
105413
+ breadcrumbRoot,
104949
105414
  headerLabel = "Files",
104950
105415
  headerActions,
104951
105416
  entryActions,
@@ -104960,7 +105425,15 @@ function FileExplorer({
104960
105425
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "@container-[size] h-full", children: [
104961
105426
  /* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: layoutStyles }),
104962
105427
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "fev-layout", children: [
104963
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fev-sidebar-tabs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(FileTabs, { entries: sortedEntries, selectedPath, onSelect }) }),
105428
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fev-sidebar-tabs", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
105429
+ FileTabs,
105430
+ {
105431
+ entries: sortedEntries,
105432
+ selectedPath,
105433
+ onSelect,
105434
+ entryActions
105435
+ }
105436
+ ) }),
104964
105437
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fev-sidebar-tree", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
104965
105438
  FileTree,
104966
105439
  {
@@ -104973,7 +105446,15 @@ function FileExplorer({
104973
105446
  }
104974
105447
  ) }),
104975
105448
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "fev-editor-wrapper border-border bg-background overflow-hidden rounded-md border shadow-sm", children: activeFile ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
104976
- /* @__PURE__ */ jsxRuntimeExports.jsx(Breadcrumb, { path: activeFile.path, entries: sortedEntries, onNavigate: onSelect }),
105449
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
105450
+ Breadcrumb,
105451
+ {
105452
+ path: activeFile.path,
105453
+ rootPath: breadcrumbRoot,
105454
+ entries: sortedEntries,
105455
+ onNavigate: onSelect
105456
+ }
105457
+ ),
104977
105458
  renderEditor(activeFile)
104978
105459
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground flex h-full items-center justify-center", children: sortedEntries.length > 0 ? "Select a file to view" : emptyState ?? "No files found." }) })
104979
105460
  ] })
@@ -105421,7 +105902,7 @@ function useOpsxTemplateContentsSubscription(schema2) {
105421
105902
  );
105422
105903
  return useSubscription(
105423
105904
  subscribe2,
105424
- () => getOpsxTemplateContents(),
105905
+ () => getOpsxTemplateContents(schema2),
105425
105906
  [schema2],
105426
105907
  `opsx.subscribeTemplateContents:${schema2 ?? ""}`
105427
105908
  );
@@ -105962,11 +106443,10 @@ function Config() {
105962
106443
  const [createEntryParent, setCreateEntryParent] = reactExports.useState(null);
105963
106444
  const [createEntryName, setCreateEntryName] = reactExports.useState("");
105964
106445
  const [activeEntry, setActiveEntry] = reactExports.useState(null);
105965
- const [headerMenuPosition, setHeaderMenuPosition] = reactExports.useState(
105966
- null
105967
- );
105968
- const [fileMenuPosition, setFileMenuPosition] = reactExports.useState(null);
105969
- const [viewMenuPosition, setViewMenuPosition] = reactExports.useState(null);
106446
+ const [headerMenuAnchor, setHeaderMenuAnchor] = reactExports.useState(null);
106447
+ const [fileMenuAnchor, setFileMenuAnchor] = reactExports.useState(null);
106448
+ const [viewMenuAnchor, setViewMenuAnchor] = reactExports.useState(null);
106449
+ const schemaMenuWrapperRef = reactExports.useRef(null);
105970
106450
  const [schemaEditorWrap, setSchemaEditorWrap] = reactExports.useState(true);
105971
106451
  const [newSchemaName, setNewSchemaName] = reactExports.useState("");
105972
106452
  const [newSchemaMode, setNewSchemaMode] = reactExports.useState("init");
@@ -106047,9 +106527,9 @@ function Config() {
106047
106527
  setDirtyFiles({});
106048
106528
  setSchemaEntryError(null);
106049
106529
  setActiveEntry(null);
106050
- setHeaderMenuPosition(null);
106051
- setFileMenuPosition(null);
106052
- setViewMenuPosition(null);
106530
+ setHeaderMenuAnchor(null);
106531
+ setFileMenuAnchor(null);
106532
+ setViewMenuAnchor(null);
106053
106533
  }, [selectedSchema]);
106054
106534
  reactExports.useEffect(() => {
106055
106535
  if (!schemaFiles || schemaFiles.length === 0) {
@@ -106142,14 +106622,20 @@ function Config() {
106142
106622
  const isRoot = activeEntry.path === "/";
106143
106623
  const childCount = activeEntry.type === "directory" ? isRoot ? schemaEntries.length : schemaEntries.filter((entry) => entry.path.startsWith(activeEntry.path + "/")).length : void 0;
106144
106624
  return {
106145
- path: isRoot ? schemaResolution?.path ?? "/" : activeEntry.path,
106625
+ path: isRoot ? schemaResolution?.displayPath ?? schemaResolution?.path ?? "/" : activeEntry.path,
106146
106626
  type: activeEntry.type,
106147
106627
  source: schemaResolution?.source ?? "unknown",
106148
106628
  sizeBytes,
106149
106629
  childCount
106150
106630
  };
106151
106631
  }, [activeEntry, schemaEntries, schemaResolution]);
106152
- const schemaRootLabel = schemaResolution?.path ?? "Schema root";
106632
+ const schemaRootLabel = reactExports.useMemo(() => {
106633
+ if (schemaResolution?.displayPath) return schemaResolution.displayPath;
106634
+ if (schemaResolution?.path) {
106635
+ return toOpsxDisplayPath(schemaResolution.path, { source: schemaResolution.source });
106636
+ }
106637
+ return "project:openspec/schemas";
106638
+ }, [schemaResolution]);
106153
106639
  const schemaRootEntry = reactExports.useMemo(() => ({ path: "/", type: "directory" }), []);
106154
106640
  const saveConfigMutation = useMutation({
106155
106641
  mutationFn: async () => {
@@ -106553,11 +107039,12 @@ function Config() {
106553
107039
  ] }),
106554
107040
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "truncate", children: [
106555
107041
  "Path: ",
106556
- schemaResolution.path
107042
+ schemaResolution.displayPath ?? schemaResolution.path
106557
107043
  ] }),
106558
107044
  schemaResolution.shadows.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
106559
- "Shadows: ",
106560
- schemaResolution.shadows.map((s) => s.source).join(", ")
107045
+ "Shadows:",
107046
+ " ",
107047
+ schemaResolution.shadows.map((s) => `${s.source}(${s.displayPath ?? s.path})`).join(", ")
106561
107048
  ] })
106562
107049
  ] })
106563
107050
  ] }),
@@ -106574,6 +107061,7 @@ function Config() {
106574
107061
  const rawArtifact = rawArtifactMap.get(artifact.id);
106575
107062
  const templateInfo = templateContents?.[artifact.id] ?? (templates?.[artifact.id] ? { ...templates[artifact.id], content: null } : null);
106576
107063
  const templatePath = templateInfo?.path ?? (typeof rawArtifact?.template === "string" ? rawArtifact.template : void 0);
107064
+ const templateDisplayPath = templateInfo?.displayPath ?? templatePath ?? null;
106577
107065
  const draftTemplateContent = templatePath !== void 0 ? draftByPath.get(templatePath) : void 0;
106578
107066
  const templateBody = draftTemplateContent !== void 0 ? draftTemplateContent : templateInfo ? templateInfo.content : null;
106579
107067
  const rawKnownFields = [
@@ -106632,7 +107120,7 @@ function Config() {
106632
107120
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground text-xs font-semibold uppercase tracking-wide", children: "Template" }),
106633
107121
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-muted-foreground pl-4 text-xs", children: [
106634
107122
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "mr-1", children: "Template:" }),
106635
- /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "bg-muted rounded px-1", children: templatePath }),
107123
+ /* @__PURE__ */ jsxRuntimeExports.jsx("code", { className: "bg-muted rounded px-1", children: templateDisplayPath }),
106636
107124
  templateInfo?.source ? ` (${templateInfo.source})` : null
106637
107125
  ] }),
106638
107126
  templateBody !== null && templateBody !== void 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-4", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "bg-muted/30 rounded-lg p-4 [zoom:0.86]", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -106662,7 +107150,7 @@ function Config() {
106662
107150
  ] });
106663
107151
  }
106664
107152
  }
106665
- ) : /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "h-full space-y-4", children: [
107153
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsxs(ContextMenuWrapper, { ref: schemaMenuWrapperRef, className: "h-full space-y-4", children: [
106666
107154
  schemaFilesError && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-destructive text-xs", children: [
106667
107155
  "Failed to load schema files: ",
106668
107156
  schemaFilesError.message
@@ -106673,6 +107161,7 @@ function Config() {
106673
107161
  entries: schemaEntries,
106674
107162
  selectedPath: selectedSchemaPath,
106675
107163
  onSelect: setSelectedSchemaPath,
107164
+ breadcrumbRoot: schemaRootLabel,
106676
107165
  headerLabel: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex min-w-0 items-center gap-2", children: [
106677
107166
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "uppercase tracking-wide", children: "Files" }),
106678
107167
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -106684,42 +107173,50 @@ function Config() {
106684
107173
  }
106685
107174
  )
106686
107175
  ] }),
106687
- headerActions: headerMenuItems.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(
107176
+ headerActions: headerMenuItems.length > 0 ? /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuTargeter, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
106688
107177
  "button",
106689
107178
  {
106690
107179
  type: "button",
106691
107180
  onClick: (event) => {
106692
- const rect = event.currentTarget.getBoundingClientRect();
106693
- setFileMenuPosition(null);
106694
- setViewMenuPosition(null);
106695
- setHeaderMenuPosition({ x: rect.right, y: rect.bottom });
107181
+ setFileMenuAnchor(null);
107182
+ setViewMenuAnchor(null);
107183
+ setHeaderMenuAnchor({
107184
+ type: "target",
107185
+ element: event.currentTarget,
107186
+ placement: "bottom-end"
107187
+ });
106696
107188
  },
106697
107189
  className: "hover:bg-muted rounded-md p-1",
106698
107190
  "aria-label": "Schema menu",
106699
107191
  children: /* @__PURE__ */ jsxRuntimeExports.jsx(EllipsisVertical, { className: "h-4 w-4" })
106700
107192
  }
106701
- ) : void 0,
106702
- entryActions: schemaMode === "edit" && canManageEntries ? (entry) => {
107193
+ ) }) : void 0,
107194
+ entryActions: (entry) => {
107195
+ const propertiesAction = {
107196
+ id: "properties",
107197
+ label: "Properties",
107198
+ icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Info$1, { className: "h-3.5 w-3.5" }),
107199
+ onSelect: () => handleOpenEntryInfo(entry)
107200
+ };
107201
+ if (schemaMode !== "edit" || !canManageEntries) {
107202
+ return [propertiesAction];
107203
+ }
106703
107204
  const parent = entry.type === "directory" ? entry.path : getParentPath(entry.path);
107205
+ const isDirectory = entry.type === "directory";
106704
107206
  return [
106705
107207
  {
106706
107208
  id: "new-file",
106707
- label: "New file",
107209
+ label: isDirectory ? "New file inside" : "New sibling file",
106708
107210
  icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FilePlus, { className: "h-3.5 w-3.5" }),
106709
107211
  onSelect: () => handleOpenCreateEntry("file", parent)
106710
107212
  },
106711
107213
  {
106712
107214
  id: "new-folder",
106713
- label: "New folder",
107215
+ label: isDirectory ? "New folder inside" : "New sibling folder",
106714
107216
  icon: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderPlus, { className: "h-3.5 w-3.5" }),
106715
107217
  onSelect: () => handleOpenCreateEntry("directory", parent)
106716
107218
  },
106717
- {
106718
- id: "properties",
106719
- label: "Properties",
106720
- icon: /* @__PURE__ */ jsxRuntimeExports.jsx(Info$1, { className: "h-3.5 w-3.5" }),
106721
- onSelect: () => handleOpenEntryInfo(entry)
106722
- },
107219
+ propertiesAction,
106723
107220
  {
106724
107221
  id: "delete",
106725
107222
  label: "Delete",
@@ -106728,39 +107225,45 @@ function Config() {
106728
107225
  onSelect: () => handleOpenDeleteEntry(entry)
106729
107226
  }
106730
107227
  ];
106731
- } : void 0,
107228
+ },
106732
107229
  emptyState: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "No files found for this schema." }),
106733
107230
  renderEditor: (activeFile) => activeFile ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [
106734
107231
  schemaMode === "edit" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-border/50 flex items-center justify-between border-b px-3 py-2 text-xs", children: [
106735
107232
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
106736
- /* @__PURE__ */ jsxRuntimeExports.jsx(
107233
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuTargeter, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
106737
107234
  "button",
106738
107235
  {
106739
107236
  type: "button",
106740
107237
  onClick: (event) => {
106741
- const rect = event.currentTarget.getBoundingClientRect();
106742
- setHeaderMenuPosition(null);
106743
- setViewMenuPosition(null);
106744
- setFileMenuPosition({ x: rect.left, y: rect.bottom });
107238
+ setHeaderMenuAnchor(null);
107239
+ setViewMenuAnchor(null);
107240
+ setFileMenuAnchor({
107241
+ type: "target",
107242
+ element: event.currentTarget,
107243
+ placement: "bottom-start"
107244
+ });
106745
107245
  },
106746
107246
  className: "hover:bg-muted rounded-md px-2 py-1 text-xs font-semibold",
106747
107247
  children: "File"
106748
107248
  }
106749
- ),
106750
- /* @__PURE__ */ jsxRuntimeExports.jsx(
107249
+ ) }),
107250
+ /* @__PURE__ */ jsxRuntimeExports.jsx(ContextMenuTargeter, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
106751
107251
  "button",
106752
107252
  {
106753
107253
  type: "button",
106754
107254
  onClick: (event) => {
106755
- const rect = event.currentTarget.getBoundingClientRect();
106756
- setHeaderMenuPosition(null);
106757
- setFileMenuPosition(null);
106758
- setViewMenuPosition({ x: rect.left, y: rect.bottom });
107255
+ setHeaderMenuAnchor(null);
107256
+ setFileMenuAnchor(null);
107257
+ setViewMenuAnchor({
107258
+ type: "target",
107259
+ element: event.currentTarget,
107260
+ placement: "bottom-start"
107261
+ });
106759
107262
  },
106760
107263
  className: "hover:bg-muted rounded-md px-2 py-1 text-xs font-semibold",
106761
107264
  children: "View"
106762
107265
  }
106763
- )
107266
+ ) })
106764
107267
  ] }),
106765
107268
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
106766
107269
  /* @__PURE__ */ jsxRuntimeExports.jsxs(
@@ -106807,28 +107310,31 @@ function Config() {
106807
107310
  /* @__PURE__ */ jsxRuntimeExports.jsx(
106808
107311
  ContextMenu,
106809
107312
  {
106810
- open: !!headerMenuPosition,
107313
+ open: !!headerMenuAnchor,
106811
107314
  items: headerMenuItems,
106812
- position: headerMenuPosition,
106813
- onClose: () => setHeaderMenuPosition(null)
107315
+ anchor: headerMenuAnchor,
107316
+ boundaryElement: schemaMenuWrapperRef.current,
107317
+ onClose: () => setHeaderMenuAnchor(null)
106814
107318
  }
106815
107319
  ),
106816
107320
  /* @__PURE__ */ jsxRuntimeExports.jsx(
106817
107321
  ContextMenu,
106818
107322
  {
106819
- open: !!fileMenuPosition,
107323
+ open: !!fileMenuAnchor,
106820
107324
  items: fileMenuItems,
106821
- position: fileMenuPosition,
106822
- onClose: () => setFileMenuPosition(null)
107325
+ anchor: fileMenuAnchor,
107326
+ boundaryElement: schemaMenuWrapperRef.current,
107327
+ onClose: () => setFileMenuAnchor(null)
106823
107328
  }
106824
107329
  ),
106825
107330
  /* @__PURE__ */ jsxRuntimeExports.jsx(
106826
107331
  ContextMenu,
106827
107332
  {
106828
- open: !!viewMenuPosition,
107333
+ open: !!viewMenuAnchor,
106829
107334
  items: viewMenuItems,
106830
- position: viewMenuPosition,
106831
- onClose: () => setViewMenuPosition(null)
107335
+ anchor: viewMenuAnchor,
107336
+ boundaryElement: schemaMenuWrapperRef.current,
107337
+ onClose: () => setViewMenuAnchor(null)
106832
107338
  }
106833
107339
  )
106834
107340
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground text-sm", children: "Select a schema to view details." })
@@ -107552,6 +108058,9 @@ function formatArtifactLabel(id2) {
107552
108058
  if (!id2) return "Unknown";
107553
108059
  return id2.split(/[-_]/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
107554
108060
  }
108061
+ function isHttpUrl(value) {
108062
+ return /^https?:\/\//.test(value);
108063
+ }
107555
108064
  function sortArtifactIdsForSchema(schemaName, artifactIds) {
107556
108065
  if (schemaName !== "spec-driven") return artifactIds;
107557
108066
  const rank = /* @__PURE__ */ new Map();
@@ -107761,6 +108270,8 @@ function Dashboard() {
107761
108270
  defaultBranch: "main",
107762
108271
  worktrees: []
107763
108272
  };
108273
+ const staticMode = isStaticMode();
108274
+ const showGitSnapshot = !staticMode || git.worktrees.some((worktree) => worktree.entries.length > 0);
107764
108275
  const hasChanges = activeChanges.length > 0;
107765
108276
  const currentWorktree = git.worktrees.find((worktree) => worktree.isCurrent) ?? null;
107766
108277
  const otherWorktrees = git.worktrees.filter((worktree) => !worktree.isCurrent);
@@ -107792,7 +108303,7 @@ function Dashboard() {
107792
108303
  }
107793
108304
  )
107794
108305
  ] });
107795
- const renderExecutionSnapshot = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid min-w-0 gap-3 xl:grid-cols-2", children: [
108306
+ const renderExecutionSnapshot = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `grid min-w-0 gap-3 ${showGitSnapshot ? "xl:grid-cols-2" : "xl:grid-cols-1"}`, children: [
107796
108307
  /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "@container min-w-0 space-y-2", children: [
107797
108308
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
107798
108309
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "font-medium", children: "Workflow Progress" }),
@@ -107860,7 +108371,7 @@ function Dashboard() {
107860
108371
  schema2.schemaName
107861
108372
  )) })
107862
108373
  ] }),
107863
- /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "min-w-0 space-y-2", children: [
108374
+ showGitSnapshot ? /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "min-w-0 space-y-2", children: [
107864
108375
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
107865
108376
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "min-w-0", children: [
107866
108377
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "font-medium", children: "Git Snapshot" }),
@@ -107869,7 +108380,7 @@ function Dashboard() {
107869
108380
  git.defaultBranch
107870
108381
  ] })
107871
108382
  ] }),
107872
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
108383
+ !staticMode ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
107873
108384
  "button",
107874
108385
  {
107875
108386
  type: "button",
@@ -107888,7 +108399,7 @@ function Dashboard() {
107888
108399
  "Refresh"
107889
108400
  ]
107890
108401
  }
107891
- )
108402
+ ) : null
107892
108403
  ] }),
107893
108404
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-border/80 min-w-0 rounded-lg border p-3", children: [
107894
108405
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-2 flex items-center gap-1.5", children: [
@@ -107913,7 +108424,7 @@ function Dashboard() {
107913
108424
  otherWorktrees.map((worktree) => /* @__PURE__ */ jsxRuntimeExports.jsx(WorktreeRow, { worktree, emphasize: false }, worktree.path))
107914
108425
  ] })
107915
108426
  ] })
107916
- ] })
108427
+ ] }) : null
107917
108428
  ] });
107918
108429
  const renderSpecificationsSection = () => /* @__PURE__ */ jsxRuntimeExports.jsxs("section", { className: "border-border min-w-0 rounded-lg border", children: [
107919
108430
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border-border flex flex-wrap items-center justify-between gap-1.5 border-b px-4 py-3", children: [
@@ -108085,6 +108596,7 @@ function WorktreeRow({
108085
108596
  worktree,
108086
108597
  emphasize
108087
108598
  }) {
108599
+ const pathLabel = isHttpUrl(worktree.path) ? worktree.path : `${worktree.relativePath} | ${worktree.path}`;
108088
108600
  return /* @__PURE__ */ jsxRuntimeExports.jsx(
108089
108601
  "div",
108090
108602
  {
@@ -108095,11 +108607,7 @@ function WorktreeRow({
108095
108607
  /* @__PURE__ */ jsxRuntimeExports.jsx(GitBranch, { className: "h-3.5 w-3.5" }),
108096
108608
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "truncate", children: worktree.branchName })
108097
108609
  ] }),
108098
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "text-muted-foreground truncate text-xs", children: [
108099
- worktree.relativePath,
108100
- " | ",
108101
- worktree.path
108102
- ] })
108610
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground truncate text-xs", children: pathLabel })
108103
108611
  ] }),
108104
108612
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "shrink-0 text-right", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-end gap-1", children: [
108105
108613
  /* @__PURE__ */ jsxRuntimeExports.jsx(GitAheadBehindBadge, { ahead: worktree.ahead, behind: worktree.behind }),