glassbox 0.4.3 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -4332,6 +4332,9 @@ function jsx(tag, props) {
4332
4332
  const childStr = children != null ? renderChildren(children) : "";
4333
4333
  return new SafeHtml(`<${tag}${attrStr}>${childStr}</${tag}>`);
4334
4334
  }
4335
+ function Fragment({ children }) {
4336
+ return new SafeHtml(children != null ? renderChildren(children) : "");
4337
+ }
4335
4338
 
4336
4339
  // src/icons.tsx
4337
4340
  var S14 = { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" };
@@ -4392,6 +4395,64 @@ function IconActualSize() {
4392
4395
  ] });
4393
4396
  }
4394
4397
 
4398
+ // src/utils/charDiff.ts
4399
+ function charDiff(oldStr, newStr) {
4400
+ if (!oldStr && !newStr) return null;
4401
+ if (oldStr === newStr) return null;
4402
+ const lcs = lcsTable(oldStr, newStr);
4403
+ const lcsLen = lcs[oldStr.length][newStr.length];
4404
+ const maxLen = Math.max(oldStr.length, newStr.length);
4405
+ if (maxLen === 0) return null;
4406
+ if (lcsLen / maxLen < 0.2) return null;
4407
+ const oldCommon = /* @__PURE__ */ new Set();
4408
+ const newCommon = /* @__PURE__ */ new Set();
4409
+ let i = oldStr.length, j = newStr.length;
4410
+ while (i > 0 && j > 0) {
4411
+ if (oldStr[i - 1] === newStr[j - 1]) {
4412
+ oldCommon.add(i - 1);
4413
+ newCommon.add(j - 1);
4414
+ i--;
4415
+ j--;
4416
+ } else if (lcs[i - 1][j] > lcs[i][j - 1]) {
4417
+ i--;
4418
+ } else {
4419
+ j--;
4420
+ }
4421
+ }
4422
+ return {
4423
+ oldSegments: buildSegments(oldStr, oldCommon),
4424
+ newSegments: buildSegments(newStr, newCommon)
4425
+ };
4426
+ }
4427
+ function lcsTable(a, b) {
4428
+ const m = a.length, n = b.length;
4429
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
4430
+ for (let i = 1; i <= m; i++) {
4431
+ for (let j = 1; j <= n; j++) {
4432
+ dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] + 1 : Math.max(dp[i - 1][j], dp[i][j - 1]);
4433
+ }
4434
+ }
4435
+ return dp;
4436
+ }
4437
+ function buildSegments(str, commonPositions) {
4438
+ const segments = [];
4439
+ let current = "";
4440
+ let currentChanged = false;
4441
+ for (let i = 0; i < str.length; i++) {
4442
+ const changed = !commonPositions.has(i);
4443
+ if (changed !== currentChanged && current.length > 0) {
4444
+ segments.push({ text: current, changed: currentChanged });
4445
+ current = "";
4446
+ }
4447
+ currentChanged = changed;
4448
+ current += str[i];
4449
+ }
4450
+ if (current.length > 0) {
4451
+ segments.push({ text: current, changed: currentChanged });
4452
+ }
4453
+ return segments;
4454
+ }
4455
+
4395
4456
  // src/components/imageDiff.tsx
4396
4457
  function ImageDiff({ file, diff, fontWarning, baseWidth, baseHeight }) {
4397
4458
  const fileId = file.id;
@@ -4519,7 +4580,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4519
4580
  "data-new-line": group.pair.left?.newNum ?? group.pair.right?.newNum ?? "",
4520
4581
  children: [
4521
4582
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": group.pair.left?.oldNum ?? "" }),
4522
- /* @__PURE__ */ jsx("span", { className: "code", children: group.pair.left?.content ?? "" })
4583
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(group.pair, "left") })
4523
4584
  ]
4524
4585
  }
4525
4586
  ),
@@ -4531,7 +4592,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4531
4592
  "data-side": "new",
4532
4593
  children: [
4533
4594
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": group.pair.right?.newNum ?? "" }),
4534
- /* @__PURE__ */ jsx("span", { className: "code", children: group.pair.right?.content ?? "" })
4595
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(group.pair, "right") })
4535
4596
  ]
4536
4597
  }
4537
4598
  )
@@ -4581,7 +4642,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4581
4642
  "data-new-line": pair.left?.newNum ?? pair.right?.newNum ?? "",
4582
4643
  children: [
4583
4644
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": pair.left?.oldNum ?? "" }),
4584
- /* @__PURE__ */ jsx("span", { className: "code", children: pair.left?.content ?? "" })
4645
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(pair, "left") })
4585
4646
  ]
4586
4647
  }
4587
4648
  );
@@ -4626,7 +4687,7 @@ function SplitDiff({ hunks, annotationsByLine }) {
4626
4687
  "data-side": "new",
4627
4688
  children: [
4628
4689
  /* @__PURE__ */ jsx("span", { className: "gutter", "data-line-number": pair.right?.newNum ?? "" }),
4629
- /* @__PURE__ */ jsx("span", { className: "code", children: pair.right?.content ?? "" })
4690
+ /* @__PURE__ */ jsx("span", { className: "code", children: renderPairContent(pair, "right") })
4630
4691
  ]
4631
4692
  }
4632
4693
  );
@@ -4634,6 +4695,20 @@ function SplitDiff({ hunks, annotationsByLine }) {
4634
4695
  ] });
4635
4696
  }) });
4636
4697
  }
4698
+ function renderSegments(segments) {
4699
+ return /* @__PURE__ */ jsx(Fragment, { children: segments.map((s) => s.changed ? /* @__PURE__ */ jsx("span", { className: "char-change", children: s.text }) : /* @__PURE__ */ jsx(Fragment, { children: s.text })) });
4700
+ }
4701
+ function renderPairContent(pair, side) {
4702
+ const line = side === "left" ? pair.left : pair.right;
4703
+ if (!line) return "";
4704
+ if (pair.left && pair.right && pair.left.type === "remove" && pair.right.type === "add") {
4705
+ const diff = charDiff(pair.left.content, pair.right.content);
4706
+ if (diff) {
4707
+ return renderSegments(side === "left" ? diff.oldSegments : diff.newSegments);
4708
+ }
4709
+ }
4710
+ return line.content;
4711
+ }
4637
4712
  function pairLines(lines) {
4638
4713
  const pairs = [];
4639
4714
  let i = 0;
@@ -4667,55 +4742,88 @@ function pairLines(lines) {
4667
4742
  }
4668
4743
  return pairs;
4669
4744
  }
4745
+ function buildUnifiedCharDiffs(lines) {
4746
+ const result = /* @__PURE__ */ new Map();
4747
+ let i = 0;
4748
+ while (i < lines.length) {
4749
+ if (lines[i].type === "remove") {
4750
+ const removes = [];
4751
+ while (i < lines.length && lines[i].type === "remove") {
4752
+ removes.push(lines[i]);
4753
+ i++;
4754
+ }
4755
+ const adds = [];
4756
+ while (i < lines.length && lines[i].type === "add") {
4757
+ adds.push(lines[i]);
4758
+ i++;
4759
+ }
4760
+ const pairCount = Math.min(removes.length, adds.length);
4761
+ for (let j = 0; j < pairCount; j++) {
4762
+ const diff = charDiff(removes[j].content, adds[j].content);
4763
+ if (diff) {
4764
+ result.set(removes[j], diff.oldSegments);
4765
+ result.set(adds[j], diff.newSegments);
4766
+ }
4767
+ }
4768
+ } else {
4769
+ i++;
4770
+ }
4771
+ }
4772
+ return result;
4773
+ }
4670
4774
  function UnifiedDiff({ hunks, annotationsByLine }) {
4671
4775
  const lastHunk = hunks[hunks.length - 1];
4672
4776
  const tailStart = lastHunk ? lastHunk.newStart + lastHunk.newCount : 1;
4673
4777
  return /* @__PURE__ */ jsx("div", { className: "diff-table-unified", children: [
4674
- hunks.map((hunk, hunkIdx) => /* @__PURE__ */ jsx("div", { className: "hunk-block", children: [
4675
- /* @__PURE__ */ jsx(
4676
- "div",
4677
- {
4678
- className: "hunk-separator",
4679
- "data-hunk-idx": hunkIdx,
4680
- "data-old-start": hunk.oldStart,
4681
- "data-old-count": hunk.oldCount,
4682
- "data-new-start": hunk.newStart,
4683
- "data-new-count": hunk.newCount,
4684
- children: [
4685
- "@@ -",
4686
- hunk.oldStart,
4687
- ",",
4688
- hunk.oldCount,
4689
- " +",
4690
- hunk.newStart,
4691
- ",",
4692
- hunk.newCount,
4693
- " @@"
4694
- ]
4695
- }
4696
- ),
4697
- hunk.lines.map((line) => {
4698
- const lineNum = line.type === "remove" ? line.oldNum : line.newNum;
4699
- const side = line.type === "remove" ? "old" : "new";
4700
- const anns = annotationsByLine[`${lineNum}:${side}`] ?? [];
4701
- return /* @__PURE__ */ jsx("div", { children: [
4702
- /* @__PURE__ */ jsx(
4703
- "div",
4704
- {
4705
- className: `diff-line ${line.type}${anns.length ? " has-annotation" : ""}`,
4706
- "data-line": lineNum,
4707
- "data-side": side,
4708
- children: [
4709
- /* @__PURE__ */ jsx("span", { className: "gutter-old", "data-line-number": line.oldNum ?? "" }),
4710
- /* @__PURE__ */ jsx("span", { className: "gutter-new", "data-line-number": line.newNum ?? "" }),
4711
- /* @__PURE__ */ jsx("span", { className: "code", children: line.content })
4712
- ]
4713
- }
4714
- ),
4715
- anns.length > 0 ? /* @__PURE__ */ jsx(AnnotationRows, { annotations: anns }) : null
4716
- ] });
4717
- })
4718
- ] })),
4778
+ hunks.map((hunk, hunkIdx) => {
4779
+ const charDiffs = buildUnifiedCharDiffs(hunk.lines);
4780
+ return /* @__PURE__ */ jsx("div", { className: "hunk-block", children: [
4781
+ /* @__PURE__ */ jsx(
4782
+ "div",
4783
+ {
4784
+ className: "hunk-separator",
4785
+ "data-hunk-idx": hunkIdx,
4786
+ "data-old-start": hunk.oldStart,
4787
+ "data-old-count": hunk.oldCount,
4788
+ "data-new-start": hunk.newStart,
4789
+ "data-new-count": hunk.newCount,
4790
+ children: [
4791
+ "@@ -",
4792
+ hunk.oldStart,
4793
+ ",",
4794
+ hunk.oldCount,
4795
+ " +",
4796
+ hunk.newStart,
4797
+ ",",
4798
+ hunk.newCount,
4799
+ " @@"
4800
+ ]
4801
+ }
4802
+ ),
4803
+ hunk.lines.map((line) => {
4804
+ const lineNum = line.type === "remove" ? line.oldNum : line.newNum;
4805
+ const side = line.type === "remove" ? "old" : "new";
4806
+ const anns = annotationsByLine[`${lineNum}:${side}`] ?? [];
4807
+ const segments = charDiffs.get(line);
4808
+ return /* @__PURE__ */ jsx("div", { children: [
4809
+ /* @__PURE__ */ jsx(
4810
+ "div",
4811
+ {
4812
+ className: `diff-line ${line.type}${anns.length ? " has-annotation" : ""}`,
4813
+ "data-line": lineNum,
4814
+ "data-side": side,
4815
+ children: [
4816
+ /* @__PURE__ */ jsx("span", { className: "gutter-old", "data-line-number": line.oldNum ?? "" }),
4817
+ /* @__PURE__ */ jsx("span", { className: "gutter-new", "data-line-number": line.newNum ?? "" }),
4818
+ /* @__PURE__ */ jsx("span", { className: "code", children: segments ? renderSegments(segments) : line.content })
4819
+ ]
4820
+ }
4821
+ ),
4822
+ anns.length > 0 ? /* @__PURE__ */ jsx(AnnotationRows, { annotations: anns }) : null
4823
+ ] });
4824
+ })
4825
+ ] });
4826
+ }),
4719
4827
  /* @__PURE__ */ jsx("div", { className: "hunk-separator hunk-expander-tail", "data-start": tailStart, children: "\u2195 Show remaining lines" })
4720
4828
  ] });
4721
4829
  }
@@ -5589,7 +5697,7 @@ async function main() {
5589
5697
  console.log("AI service test mode enabled \u2014 using mock AI responses");
5590
5698
  }
5591
5699
  if (debug) {
5592
- console.log(`[debug] Build timestamp: ${"2026-03-21T03:42:45.606Z"}`);
5700
+ console.log(`[debug] Build timestamp: ${"2026-03-21T23:17:12.455Z"}`);
5593
5701
  }
5594
5702
  if (projectDir) {
5595
5703
  process.chdir(projectDir);