fluxflow-cli 2.13.1 → 2.13.2

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 (2) hide show
  1. package/dist/fluxflow.js +376 -185
  2. package/package.json +4 -3
package/dist/fluxflow.js CHANGED
@@ -2227,12 +2227,21 @@ var init_MultilineInput = __esm({
2227
2227
  const visibleLines = useMemo(() => {
2228
2228
  return visualLines.slice(newScrollOffset, newScrollOffset + visibleRows);
2229
2229
  }, [visualLines, newScrollOffset, visibleRows]);
2230
+ const [blink, setBlink] = useState2(true);
2231
+ useEffect2(() => {
2232
+ setBlink(true);
2233
+ if (!focus || !showCursor) return;
2234
+ const timer = setInterval(() => {
2235
+ setBlink((prev) => !prev);
2236
+ }, 530);
2237
+ return () => clearInterval(timer);
2238
+ }, [focus, showCursor, value, cursorIndex]);
2230
2239
  const cursorStyle = useMemo(() => ({
2231
2240
  ...textStyle,
2232
- color: showCursor && focus ? "white" : void 0,
2233
- bold: showCursor && focus,
2234
- inverse: showCursor && focus
2235
- }), [textStyle, showCursor, focus]);
2241
+ color: showCursor && focus && blink ? "white" : void 0,
2242
+ bold: showCursor && focus && blink,
2243
+ inverse: showCursor && focus && blink
2244
+ }), [textStyle, showCursor, focus, blink]);
2236
2245
  return /* @__PURE__ */ React2.createElement(Box, { height: visibleRows, width: wrapWidth, overflow: "hidden", flexDirection: "column", flexGrow: 0, flexShrink: 0 }, visibleLines.map((lineObj, idx) => {
2237
2246
  const globalLineIdx = newScrollOffset + idx;
2238
2247
  const isCursorLine = globalLineIdx === cursorLine && focus && showCursor;
@@ -2316,6 +2325,16 @@ var init_MultilineInput = __esm({
2316
2325
  cursorIndexRef.current = newIndex;
2317
2326
  setCursorIndex(newIndex);
2318
2327
  setPasteLength(0);
2328
+ } else if (key.upArrow && cursorLine === 0) {
2329
+ cursorIndexRef.current = 0;
2330
+ setCursorIndex(0);
2331
+ setPasteLength(0);
2332
+ } else if (key.downArrow && cursorLine === visualLines.length - 1) {
2333
+ const lastLineObj = visualLines[visualLines.length - 1];
2334
+ const newIndex = lastLineObj.globalStart + lastLineObj.text.length;
2335
+ cursorIndexRef.current = newIndex;
2336
+ setCursorIndex(newIndex);
2337
+ setPasteLength(0);
2319
2338
  }
2320
2339
  }
2321
2340
  } else if (key.leftArrow) {
@@ -2346,6 +2365,22 @@ var init_MultilineInput = __esm({
2346
2365
  onChange(newValue);
2347
2366
  setPasteLength(0);
2348
2367
  }
2368
+ } else if (key.home || key.end) {
2369
+ if (showCursor) {
2370
+ const { visualLines, cursorLine } = computeVisualMatrix(val, curIdx, wrapWidth, identity);
2371
+ const currentLineObj = visualLines[cursorLine];
2372
+ if (currentLineObj) {
2373
+ let newIndex;
2374
+ if (key.home) {
2375
+ newIndex = currentLineObj.globalStart;
2376
+ } else if (key.end) {
2377
+ newIndex = currentLineObj.globalStart + currentLineObj.text.length;
2378
+ }
2379
+ cursorIndexRef.current = newIndex;
2380
+ setCursorIndex(newIndex);
2381
+ setPasteLength(0);
2382
+ }
2383
+ }
2349
2384
  } else {
2350
2385
  if (input) {
2351
2386
  const newValue = val.slice(0, curIdx) + input + val.slice(curIdx);
@@ -2741,6 +2776,7 @@ var init_text = __esm({
2741
2776
  text: line,
2742
2777
  gutterWidth,
2743
2778
  lineNum: idx + 1,
2779
+ isFirstLine: idx === 0,
2744
2780
  isLastLine: isLast
2745
2781
  };
2746
2782
  if (isLast && msg.isStreaming) {
@@ -2758,61 +2794,6 @@ var init_text = __esm({
2758
2794
  const match = text.match(/\[DIFF_START\]([\s\S]*?)(?:\[DIFF_END\]|$)/);
2759
2795
  const diffBody = match ? match[1].trim() : "";
2760
2796
  const diffLines = diffBody.split("\n").map((l) => l.replace(/\r$/, ""));
2761
- const highlightInfos = Array(diffLines.length).fill(null);
2762
- let idx = 0;
2763
- while (idx < diffLines.length) {
2764
- const removals = [];
2765
- const additions = [];
2766
- while (idx < diffLines.length) {
2767
- const line = diffLines[idx];
2768
- const cleanLine = line.replace("[UI_CONTEXT]", "");
2769
- if (cleanLine.startsWith("-")) {
2770
- removals.push({ idx, line: cleanLine });
2771
- idx++;
2772
- } else {
2773
- break;
2774
- }
2775
- }
2776
- while (idx < diffLines.length) {
2777
- const line = diffLines[idx];
2778
- const cleanLine = line.replace("[UI_CONTEXT]", "");
2779
- if (cleanLine.startsWith("+")) {
2780
- additions.push({ idx, line: cleanLine });
2781
- idx++;
2782
- } else {
2783
- break;
2784
- }
2785
- }
2786
- if (removals.length > 0 && additions.length > 0) {
2787
- const pairCount = Math.min(removals.length, additions.length);
2788
- for (let k = 0; k < pairCount; k++) {
2789
- const r = removals[k];
2790
- const a = additions[k];
2791
- const rRest = r.line.substring(1);
2792
- const rSplit = rRest.indexOf("|");
2793
- const rContent = rSplit !== -1 ? rRest.substring(rSplit + 1) : rRest;
2794
- const aRest = a.line.substring(1);
2795
- const aSplit = aRest.indexOf("|");
2796
- const aContent = aSplit !== -1 ? aRest.substring(aSplit + 1) : aRest;
2797
- let prefixLen = 0;
2798
- while (prefixLen < rContent.length && prefixLen < aContent.length && rContent[prefixLen] === aContent[prefixLen]) {
2799
- prefixLen++;
2800
- }
2801
- let suffixLen = 0;
2802
- const maxSuffix = Math.min(rContent.length - prefixLen, aContent.length - prefixLen);
2803
- while (suffixLen < maxSuffix && rContent[rContent.length - 1 - suffixLen] === aContent[aContent.length - 1 - suffixLen]) {
2804
- suffixLen++;
2805
- }
2806
- if (prefixLen > 0 || suffixLen > 0) {
2807
- highlightInfos[r.idx] = { prefixLen, suffixLen };
2808
- highlightInfos[a.idx] = { prefixLen, suffixLen };
2809
- }
2810
- }
2811
- }
2812
- if (removals.length === 0 && additions.length === 0) {
2813
- idx++;
2814
- }
2815
- }
2816
2797
  const completedBlocks2 = [];
2817
2798
  let activeBlock2 = null;
2818
2799
  diffLines.forEach((line, i) => {
@@ -2822,7 +2803,8 @@ var init_text = __esm({
2822
2803
  msg,
2823
2804
  type: "diff-line",
2824
2805
  text: line,
2825
- highlightInfo: highlightInfos[i]
2806
+ isFirstLine: i === 0,
2807
+ isLastLine: isLast
2826
2808
  };
2827
2809
  if (isLast && msg.isStreaming) {
2828
2810
  activeBlock2 = block;
@@ -2865,6 +2847,7 @@ var init_text = __esm({
2865
2847
  text: line
2866
2848
  };
2867
2849
  if (isLast && msg.isStreaming) {
2850
+ block.isActiveBlock = true;
2868
2851
  activeBlock = block;
2869
2852
  } else {
2870
2853
  completedBlocks.push(block);
@@ -2900,6 +2883,7 @@ var init_text = __esm({
2900
2883
  text: codeLines.join("\n")
2901
2884
  };
2902
2885
  if (isLast && msg.isStreaming && inCodeBlock) {
2886
+ block.isActiveBlock = true;
2903
2887
  activeBlock = block;
2904
2888
  } else {
2905
2889
  completedBlocks.push(block);
@@ -2918,6 +2902,7 @@ var init_text = __esm({
2918
2902
  text: codeLines.join("\n")
2919
2903
  };
2920
2904
  if (msg.isStreaming) {
2905
+ block.isActiveBlock = true;
2921
2906
  activeBlock = block;
2922
2907
  } else {
2923
2908
  completedBlocks.push(block);
@@ -2933,7 +2918,8 @@ var init_text = __esm({
2933
2918
  msg,
2934
2919
  type: "table",
2935
2920
  text: tableLines.join("\n"),
2936
- isStreaming: true
2921
+ isStreaming: true,
2922
+ isActiveBlock: true
2937
2923
  };
2938
2924
  } else {
2939
2925
  completedBlocks.push({
@@ -2964,6 +2950,7 @@ var init_text = __esm({
2964
2950
  text: line
2965
2951
  };
2966
2952
  if (isLast && msg.isStreaming) {
2953
+ block.isActiveBlock = true;
2967
2954
  activeBlock = block;
2968
2955
  } else {
2969
2956
  completedBlocks.push(block);
@@ -3497,12 +3484,77 @@ ${coloredArt[7]}`;
3497
3484
  // src/components/ChatLayout.jsx
3498
3485
  import React4, { useState as useState3, useEffect as useEffect3, useRef as useRef2 } from "react";
3499
3486
  import { Box as Box3, Text as Text4 } from "ink";
3500
- var formatThinkText, parseMathSymbols, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
3487
+ import { diffWordsWithSpace } from "diff";
3488
+ var useStreamingText, formatThinkText, parseMathSymbols, renderLatexText, InlineMarkdown, TableRenderer, MarkdownText, parseLineInfo, DiffLine, DiffBlock, CodeRenderer, formatThinkingDuration, MessageItem, BlockItem, ChatLayout;
3501
3489
  var init_ChatLayout = __esm({
3502
3490
  "src/components/ChatLayout.jsx"() {
3503
3491
  init_TerminalBox();
3504
3492
  init_text();
3505
3493
  init_terminal();
3494
+ useStreamingText = (targetText, isStreaming, isActiveBlock) => {
3495
+ const [displayedText, setDisplayedText] = useState3(isActiveBlock && isStreaming ? "" : targetText);
3496
+ const targetTextRef = useRef2(targetText);
3497
+ useEffect3(() => {
3498
+ targetTextRef.current = targetText;
3499
+ }, [targetText]);
3500
+ useEffect3(() => {
3501
+ if (!isActiveBlock) {
3502
+ setDisplayedText(targetText);
3503
+ return;
3504
+ }
3505
+ if (!isStreaming && displayedText === targetText) {
3506
+ return;
3507
+ }
3508
+ const interval = setInterval(() => {
3509
+ setDisplayedText((current) => {
3510
+ const target = targetTextRef.current;
3511
+ if (current.length >= target.length) {
3512
+ if (!isStreaming) {
3513
+ clearInterval(interval);
3514
+ }
3515
+ return current;
3516
+ }
3517
+ if (!target.startsWith(current)) {
3518
+ return target;
3519
+ }
3520
+ const remaining = target.substring(current.length);
3521
+ const words = remaining.split(/(\s+)/);
3522
+ if (words.length <= 1) {
3523
+ if (!isStreaming) {
3524
+ clearInterval(interval);
3525
+ }
3526
+ return target;
3527
+ }
3528
+ const currentWordsCount = current.split(/\s+/).filter(Boolean).length;
3529
+ const targetWordsCount = target.split(/\s+/).filter(Boolean).length;
3530
+ const diff = targetWordsCount - currentWordsCount;
3531
+ let wordsToAdd = 1;
3532
+ if (diff > 15) {
3533
+ wordsToAdd = 4;
3534
+ } else if (diff > 8) {
3535
+ wordsToAdd = 3;
3536
+ } else if (diff > 3) {
3537
+ wordsToAdd = 2;
3538
+ }
3539
+ let addedText = "";
3540
+ let wordCount = 0;
3541
+ for (let i = 0; i < words.length; i++) {
3542
+ const w = words[i];
3543
+ addedText += w;
3544
+ if (/\S/.test(w)) {
3545
+ wordCount++;
3546
+ if (wordCount >= wordsToAdd) {
3547
+ break;
3548
+ }
3549
+ }
3550
+ }
3551
+ return current + addedText;
3552
+ });
3553
+ }, 100);
3554
+ return () => clearInterval(interval);
3555
+ }, [isStreaming, isActiveBlock, targetText, displayedText]);
3556
+ return displayedText;
3557
+ };
3506
3558
  formatThinkText = (cleaned, columns = 80) => {
3507
3559
  if (!cleaned) return null;
3508
3560
  const availableWidth = columns - 10;
@@ -3686,97 +3738,95 @@ var init_ChatLayout = __esm({
3686
3738
  flushBuffers("final");
3687
3739
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 2 }, result);
3688
3740
  });
3689
- DiffLine = React4.memo(({ line, columns = 80, highlightInfo }) => {
3741
+ parseLineInfo = (l) => {
3742
+ if (!l) return null;
3743
+ const clean = l.replace("[UI_CONTEXT]", "").replace(/\r/g, "");
3744
+ const isR = clean.startsWith("-");
3745
+ const isA = clean.startsWith("+");
3746
+ let rest = isR || isA ? clean.substring(1) : clean;
3747
+ rest = rest.trim();
3748
+ const splitIdx = rest.indexOf("|");
3749
+ const num = splitIdx !== -1 ? rest.substring(0, splitIdx).trim() : "";
3750
+ const content = splitIdx !== -1 ? rest.substring(splitIdx + 1) : rest;
3751
+ return { isR, isA, num, content };
3752
+ };
3753
+ DiffLine = React4.memo(({ line, pairContent, parentText, columns = 80 }) => {
3690
3754
  const isContext = line.includes("[UI_CONTEXT]");
3691
3755
  const cleanLine = line.replace("[UI_CONTEXT]", "");
3692
3756
  if (isContext && cleanLine.includes("\u2550")) {
3693
3757
  return /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2550".repeat(Math.max(10, columns - 4))));
3694
3758
  }
3695
- const isRemoval = cleanLine.startsWith("-");
3696
- const isAddition = cleanLine.startsWith("+");
3697
- const prefixChar = cleanLine[0];
3698
- const rest = cleanLine.substring(1);
3699
- const splitIdx = rest.indexOf("|");
3700
- const lineNum = splitIdx !== -1 ? rest.substring(0, splitIdx).trim() : "";
3701
- const content = splitIdx !== -1 ? rest.substring(splitIdx + 1) : rest;
3702
- const bgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : "#1a1a1a";
3703
- const textColor = isRemoval ? "#ff4d4d" : isAddition ? "#4dff88" : isContext ? "white" : "white";
3704
- const numColor = isRemoval ? "#cf3a3a" : isAddition ? "#3acf65" : "gray";
3705
- const hasHighlight = highlightInfo && (highlightInfo.prefixLen > 0 || highlightInfo.suffixLen > 0);
3706
- const rowBgColor = hasHighlight ? "#1a1a1a" : bgColor;
3707
- if (hasHighlight) {
3708
- const prefixLen = highlightInfo.prefixLen;
3709
- const suffixLen = highlightInfo.suffixLen;
3710
- const prefix = content.substring(0, prefixLen);
3711
- const delta = content.substring(prefixLen, content.length - suffixLen);
3712
- const suffix = content.substring(content.length - suffixLen);
3713
- return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: rowBgColor, paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0, justifyContent: "flex-end" }, /* @__PURE__ */ React4.createElement(Text4, { color: numColor, dimColor: isContext }, lineNum)), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, bold: true }, isRemoval ? "-" : isAddition ? "+" : " ")), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, dimColor: isContext }, prefix, /* @__PURE__ */ React4.createElement(Text4, { backgroundColor: bgColor }, delta), suffix)));
3714
- }
3715
- return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: rowBgColor, paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0, justifyContent: "flex-end" }, /* @__PURE__ */ React4.createElement(Text4, { color: numColor, dimColor: isContext }, lineNum)), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, bold: true }, isRemoval ? "-" : isAddition ? "+" : " ")), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: textColor, dimColor: isContext }, wrapText(content, columns - 14))));
3759
+ const parsedCurrent = parseLineInfo(line);
3760
+ if (!parsedCurrent) {
3761
+ return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, wrapText(cleanLine, columns - 14))));
3762
+ }
3763
+ const { isR: isRemoval, isA: isAddition, num: lineNum, content } = parsedCurrent;
3764
+ const innerBgColor = isRemoval ? "#3a0c0c" : isAddition ? "#0c3a1a" : void 0;
3765
+ let finalPairContent = pairContent;
3766
+ if (!finalPairContent && parentText && (isRemoval || isAddition)) {
3767
+ const cleanParent = parentText.replace(/\[DIFF_START\]|\[DIFF_END\]/g, "").trim();
3768
+ const diffLines = cleanParent.split("\n");
3769
+ const pairLine = diffLines.find((l) => {
3770
+ const p = parseLineInfo(l);
3771
+ return p && p.num === lineNum && p.isR !== isRemoval;
3772
+ });
3773
+ if (pairLine) {
3774
+ finalPairContent = parseLineInfo(pairLine).content;
3775
+ }
3776
+ }
3777
+ let words = [];
3778
+ if (finalPairContent !== void 0 && finalPairContent !== null) {
3779
+ const oldStr = isRemoval ? content : finalPairContent;
3780
+ const newStr = isRemoval ? finalPairContent : content;
3781
+ try {
3782
+ words = diffWordsWithSpace(oldStr, newStr);
3783
+ } catch (e) {
3784
+ words = [];
3785
+ }
3786
+ }
3787
+ const hasInlineChange = words.some((part) => isRemoval && part.removed || isAddition && part.added);
3788
+ const isPureUnpairedBlock = !finalPairContent && (isRemoval || isAddition);
3789
+ const hasRealChange = hasInlineChange || isPureUnpairedBlock;
3790
+ const finalNumColor = isRemoval || isAddition ? isRemoval ? "#d96868" : "#68d98c" : "gray";
3791
+ const finalPrefixColor = isRemoval ? "#ff4d4d" : "#4dff88";
3792
+ const displayPrefix = isRemoval ? "-" : isAddition ? "+" : " ";
3793
+ const renderInlineDiff = () => {
3794
+ if (isPureUnpairedBlock) {
3795
+ const blockColor = isRemoval ? "#ff3333" : "#33ff66";
3796
+ return /* @__PURE__ */ React4.createElement(Text4, { color: blockColor }, wrapText(content, columns - 14));
3797
+ }
3798
+ if (!(isRemoval || isAddition) || words.length === 0 || !hasInlineChange) {
3799
+ const textColor = isRemoval ? "#b34d4d" : isAddition ? "#4db36b" : "gray";
3800
+ return /* @__PURE__ */ React4.createElement(Text4, { color: textColor }, wrapText(content, columns - 14));
3801
+ }
3802
+ return /* @__PURE__ */ React4.createElement(Text4, { wrap: "anywhere" }, words.map((part, idx) => {
3803
+ const isWhitespace = /^\s+$/.test(part.value);
3804
+ if (isRemoval) {
3805
+ const isSurroundedByRemoval = words[idx - 1]?.removed || words[idx + 1]?.removed;
3806
+ if (part.removed || isWhitespace && isSurroundedByRemoval) {
3807
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#ff3333" }, part.value);
3808
+ }
3809
+ if (part.added) return null;
3810
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#b34d4d" }, part.value);
3811
+ }
3812
+ if (isAddition) {
3813
+ const isSurroundedByAddition = words[idx - 1]?.added || words[idx + 1]?.added;
3814
+ if (part.added || isWhitespace && isSurroundedByAddition) {
3815
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#33ff66" }, part.value);
3816
+ }
3817
+ if (part.removed) return null;
3818
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "#4db36b" }, part.value);
3819
+ }
3820
+ return /* @__PURE__ */ React4.createElement(Text4, { key: idx, color: "gray" }, part.value);
3821
+ }));
3822
+ };
3823
+ return /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: columns }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0, justifyContent: "flex-end" }, /* @__PURE__ */ React4.createElement(Text4, { color: finalNumColor }, lineNum)), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: finalPrefixColor }, displayPrefix)), /* @__PURE__ */ React4.createElement(Box3, { marginLeft: 1, backgroundColor: innerBgColor, flexShrink: 1 }, renderInlineDiff()));
3716
3824
  });
3717
3825
  DiffBlock = React4.memo(({ text, columns = 80 }) => {
3718
3826
  const match = text.match(/\[DIFF_START\]([\s\S]*?)\[DIFF_END\]/);
3719
3827
  const diffBody = match ? match[1].trim() : "";
3720
3828
  const diffLines = diffBody.split("\n");
3721
- const highlightInfos = React4.useMemo(() => {
3722
- const infos = Array(diffLines.length).fill(null);
3723
- let idx = 0;
3724
- while (idx < diffLines.length) {
3725
- const removals = [];
3726
- const additions = [];
3727
- while (idx < diffLines.length) {
3728
- const line = diffLines[idx];
3729
- const cleanLine = line.replace("[UI_CONTEXT]", "");
3730
- if (cleanLine.startsWith("-")) {
3731
- removals.push({ idx, line: cleanLine });
3732
- idx++;
3733
- } else {
3734
- break;
3735
- }
3736
- }
3737
- while (idx < diffLines.length) {
3738
- const line = diffLines[idx];
3739
- const cleanLine = line.replace("[UI_CONTEXT]", "");
3740
- if (cleanLine.startsWith("+")) {
3741
- additions.push({ idx, line: cleanLine });
3742
- idx++;
3743
- } else {
3744
- break;
3745
- }
3746
- }
3747
- if (removals.length > 0 && additions.length > 0) {
3748
- const pairCount = Math.min(removals.length, additions.length);
3749
- for (let k = 0; k < pairCount; k++) {
3750
- const r = removals[k];
3751
- const a = additions[k];
3752
- const rRest = r.line.substring(1);
3753
- const rSplit = rRest.indexOf("|");
3754
- const rContent = rSplit !== -1 ? rRest.substring(rSplit + 1) : rRest;
3755
- const aRest = a.line.substring(1);
3756
- const aSplit = aRest.indexOf("|");
3757
- const aContent = aSplit !== -1 ? aRest.substring(aSplit + 1) : aRest;
3758
- let prefixLen = 0;
3759
- while (prefixLen < rContent.length && prefixLen < aContent.length && rContent[prefixLen] === aContent[prefixLen]) {
3760
- prefixLen++;
3761
- }
3762
- let suffixLen = 0;
3763
- const maxSuffix = Math.min(rContent.length - prefixLen, aContent.length - prefixLen);
3764
- while (suffixLen < maxSuffix && rContent[rContent.length - 1 - suffixLen] === aContent[aContent.length - 1 - suffixLen]) {
3765
- suffixLen++;
3766
- }
3767
- if (prefixLen > 0 || suffixLen > 0) {
3768
- infos[r.idx] = { prefixLen, suffixLen };
3769
- infos[a.idx] = { prefixLen, suffixLen };
3770
- }
3771
- }
3772
- }
3773
- if (removals.length === 0 && additions.length === 0) {
3774
- idx++;
3775
- }
3776
- }
3777
- return infos;
3778
- }, [diffLines]);
3779
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 3, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingY: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 5, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 2, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " "))), diffLines.map((line, i) => /* @__PURE__ */ React4.createElement(DiffLine, { key: i, line, columns: columns - 3, highlightInfo: highlightInfos[i] })), /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 5, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 2, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " ")))));
3829
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", width: columns - 3, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingY: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " "))), diffLines.map((line, i) => /* @__PURE__ */ React4.createElement(DiffLine, { key: i, line, parentText: text, columns: columns - 3 })), /* @__PURE__ */ React4.createElement(Box3, { backgroundColor: "#1a1a1a", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { width: 3, flexShrink: 0 }), /* @__PURE__ */ React4.createElement(Box3, { width: 1, flexShrink: 0, marginLeft: 1 }), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, " ")))));
3780
3830
  });
3781
3831
  CodeRenderer = React4.memo(({ text, columns = 80 }) => {
3782
3832
  if (!text) return null;
@@ -3895,13 +3945,70 @@ var init_ChatLayout = __esm({
3895
3945
  const selectionMatch = msg.text.match(/Selection: (.*)/);
3896
3946
  const selection = selectionMatch ? selectionMatch[1] : "No selection";
3897
3947
  const s = emojiSpace(2);
3898
- return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "AGENT REQUEST: RESOLVED")), /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white" }, "Selection: ", /* @__PURE__ */ React4.createElement(Text4, { color: "grey", bold: true }, selection)))));
3948
+ return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(
3949
+ Box3,
3950
+ {
3951
+ flexDirection: "column",
3952
+ borderStyle: "single",
3953
+ borderLeft: true,
3954
+ borderRight: false,
3955
+ borderTop: false,
3956
+ borderBottom: false,
3957
+ borderColor: "#444444",
3958
+ paddingLeft: 2,
3959
+ paddingRight: 0,
3960
+ paddingTop: 1,
3961
+ paddingBottom: 1,
3962
+ backgroundColor: "#1a1a1a",
3963
+ width: "100%"
3964
+ },
3965
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "AGENT REQUEST: RESOLVED")),
3966
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white" }, "Selection: ", /* @__PURE__ */ React4.createElement(Text4, { color: "grey", bold: true }, selection)))
3967
+ ));
3899
3968
  }
3900
3969
  if (msg.isAboutRecord) {
3901
- return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "ABOUT FLUX FLOW")), /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, msg.text))));
3970
+ return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 0, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(
3971
+ Box3,
3972
+ {
3973
+ flexDirection: "column",
3974
+ borderStyle: "single",
3975
+ borderLeft: true,
3976
+ borderRight: false,
3977
+ borderTop: false,
3978
+ borderBottom: false,
3979
+ borderColor: "#444444",
3980
+ paddingLeft: 2,
3981
+ paddingRight: 0,
3982
+ paddingTop: 1,
3983
+ paddingBottom: 1,
3984
+ backgroundColor: "#1a1a1a",
3985
+ width: "100%"
3986
+ },
3987
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "ABOUT FLUX FLOW")),
3988
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(Text4, null, msg.text))
3989
+ ));
3902
3990
  }
3903
3991
  if (msg.isUpdateNotification) {
3904
- return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "UPDATE AVAILABLE")), /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text: msg.text, columns }))));
3992
+ return /* @__PURE__ */ React4.createElement(Box3, { marginBottom: 1, paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(
3993
+ Box3,
3994
+ {
3995
+ flexDirection: "column",
3996
+ borderStyle: "single",
3997
+ borderLeft: true,
3998
+ borderRight: false,
3999
+ borderTop: false,
4000
+ borderBottom: false,
4001
+ borderColor: "#444444",
4002
+ paddingLeft: 2,
4003
+ paddingRight: 0,
4004
+ paddingTop: 1,
4005
+ paddingBottom: 1,
4006
+ backgroundColor: "#1a1a1a",
4007
+ width: "100%"
4008
+ },
4009
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "white", bold: true }, "UPDATE AVAILABLE")),
4010
+ /* @__PURE__ */ React4.createElement(Box3, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text: msg.text, columns }))
4011
+ ));
3905
4012
  }
3906
4013
  if (msg.isHelpRecord) {
3907
4014
  const commandList = [
@@ -3994,17 +4101,18 @@ var init_ChatLayout = __esm({
3994
4101
  if (!text || text.trim() === "") {
3995
4102
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "row", width: "100%", paddingX: 1 }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2502 "));
3996
4103
  }
3997
- const trimmed = text.trim();
4104
+ const animatedText = useStreamingText(text, msg.isStreaming, block.isActiveBlock);
4105
+ const trimmed = animatedText.trim();
3998
4106
  const isUnordered = /^[\*\-\+]\s/.test(trimmed);
3999
4107
  const isOrdered = /^\d+\.\s/.test(trimmed);
4000
- let content = text;
4108
+ let content = animatedText;
4001
4109
  if (isUnordered || isOrdered) {
4002
4110
  const bullet = isUnordered ? " \u2022 " : trimmed.match(/^\d+\.\s/)[0];
4003
4111
  const indent = " ".repeat(bullet.length);
4004
4112
  const wrappedPart = wrapText(trimmed.replace(/^[\*\-\d+\.]+\s/, ""), columns - (bullet.length + 10));
4005
4113
  content = bullet + wrappedPart.split("\n").join("\n" + indent);
4006
4114
  } else {
4007
- content = wrapText(text, columns - 10);
4115
+ content = wrapText(animatedText, columns - 10);
4008
4116
  }
4009
4117
  const wrappedLines = content.split("\n");
4010
4118
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, wrappedLines.map((wLine, idx) => /* @__PURE__ */ React4.createElement(Box3, { key: idx, flexDirection: "row", width: "100%" }, /* @__PURE__ */ React4.createElement(Text4, { color: "gray" }, "\u2502 "), /* @__PURE__ */ React4.createElement(Box3, { flexGrow: 1, marginLeft: 1 }, /* @__PURE__ */ React4.createElement(InlineMarkdown, { text: wLine, color: "gray", italic: true })))));
@@ -4017,7 +4125,8 @@ var init_ChatLayout = __esm({
4017
4125
  if (!text || text.trim() === "") {
4018
4126
  return /* @__PURE__ */ React4.createElement(Box3, { height: 1 });
4019
4127
  }
4020
- return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text, columns }));
4128
+ const animatedText = useStreamingText(text, msg.isStreaming, block.isActiveBlock);
4129
+ return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(CodeRenderer, { text: animatedText, columns }));
4021
4130
  }
4022
4131
  if (type === "table") {
4023
4132
  return /* @__PURE__ */ React4.createElement(Box3, { flexDirection: "column", paddingX: 1, width: "100%" }, /* @__PURE__ */ React4.createElement(TableRenderer, { buffer: text.split("\n"), terminalWidth: columns }));
@@ -4029,8 +4138,9 @@ var init_ChatLayout = __esm({
4029
4138
  DiffLine,
4030
4139
  {
4031
4140
  line: text,
4032
- columns,
4033
- highlightInfo: block.highlightInfo
4141
+ pairContent: block.pairContent,
4142
+ parentText: msg?.text,
4143
+ columns
4034
4144
  }
4035
4145
  ), isLastLine && renderPaddingLine(true));
4036
4146
  }
@@ -5435,31 +5545,73 @@ var init_AskUserModal = __esm({
5435
5545
  });
5436
5546
  const s = emojiSpace(2);
5437
5547
  if (isSuggestingElse) {
5438
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4AC} SUGGEST SOMETHING ELSE")), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { italic: true, color: "gray" }, "Replying to: ", question)), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, flexDirection: "row" }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4A0} "), /* @__PURE__ */ React9.createElement(
5439
- TextInput3,
5440
- {
5441
- value: customInput,
5442
- onChange: setCustomInput,
5443
- onSubmit: () => onResolve(customInput)
5444
- }
5445
- )), /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Press Enter to send)")));
5446
- }
5447
- return /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", borderStyle: "round", borderColor: "gray", padding: 0, width: "100%" }, /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "AGENT REQUEST: ACTION REQUIRED")), /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: "white" }, question)), /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", width: "100%" }, allOptions.map((opt, idx) => {
5448
- const isSelected = idx === selectedIndex;
5449
5548
  return /* @__PURE__ */ React9.createElement(
5450
5549
  Box8,
5451
5550
  {
5452
- key: opt.id,
5453
5551
  flexDirection: "column",
5454
- width: "100%",
5455
- backgroundColor: isSelected ? "#2a2a2a" : void 0,
5456
- paddingX: 1,
5457
- marginBottom: idx === allOptions.length - 1 ? 0 : 1
5552
+ borderStyle: "single",
5553
+ borderLeft: true,
5554
+ borderRight: false,
5555
+ borderTop: false,
5556
+ borderBottom: false,
5557
+ borderColor: "#444444",
5558
+ paddingLeft: 2,
5559
+ paddingRight: 0,
5560
+ paddingTop: 1,
5561
+ paddingBottom: 1,
5562
+ backgroundColor: "#1a1a1a",
5563
+ width: "100%"
5458
5564
  },
5459
- /* @__PURE__ */ React9.createElement(Text9, { color: isSelected ? "white" : "grey", bold: isSelected }, isSelected ? "\u276F " : " ", opt.label),
5460
- opt.description && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 4 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, opt.description))
5565
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4AC} SUGGEST SOMETHING ELSE")),
5566
+ /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1 }, /* @__PURE__ */ React9.createElement(Text9, { italic: true, color: "gray" }, "Replying to: ", question)),
5567
+ /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, flexDirection: "row" }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "\u{1F4A0} "), /* @__PURE__ */ React9.createElement(
5568
+ TextInput3,
5569
+ {
5570
+ value: customInput,
5571
+ onChange: setCustomInput,
5572
+ onSubmit: () => onResolve(customInput)
5573
+ }
5574
+ )),
5575
+ /* @__PURE__ */ React9.createElement(Box8, { marginTop: 1, paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Press Enter to send)"))
5461
5576
  );
5462
- })), /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Use Arrows to navigate, Enter to confirm)")));
5577
+ }
5578
+ return /* @__PURE__ */ React9.createElement(
5579
+ Box8,
5580
+ {
5581
+ flexDirection: "column",
5582
+ borderStyle: "single",
5583
+ borderLeft: true,
5584
+ borderRight: false,
5585
+ borderTop: false,
5586
+ borderBottom: false,
5587
+ borderColor: "#444444",
5588
+ paddingLeft: 2,
5589
+ paddingRight: 0,
5590
+ paddingTop: 1,
5591
+ paddingBottom: 1,
5592
+ backgroundColor: "#1a1a1a",
5593
+ width: "100%"
5594
+ },
5595
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "white", bold: true }, "AGENT REQUEST: ACTION REQUIRED")),
5596
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { bold: true, color: "white" }, question)),
5597
+ /* @__PURE__ */ React9.createElement(Box8, { flexDirection: "column", width: "100%" }, allOptions.map((opt, idx) => {
5598
+ const isSelected = idx === selectedIndex;
5599
+ return /* @__PURE__ */ React9.createElement(
5600
+ Box8,
5601
+ {
5602
+ key: opt.id,
5603
+ flexDirection: "column",
5604
+ width: "100%",
5605
+ backgroundColor: isSelected ? "#2a2a2a" : void 0,
5606
+ paddingX: 1,
5607
+ marginBottom: idx === allOptions.length - 1 ? 0 : 1
5608
+ },
5609
+ /* @__PURE__ */ React9.createElement(Text9, { color: isSelected ? "white" : "grey", bold: isSelected }, isSelected ? "\u276F " : " ", opt.label),
5610
+ opt.description && /* @__PURE__ */ React9.createElement(Box8, { marginLeft: 4 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, opt.description))
5611
+ );
5612
+ })),
5613
+ /* @__PURE__ */ React9.createElement(Box8, { paddingX: 1, marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React9.createElement(Text9, { color: "gray", italic: true }, "(Use Arrows to navigate, Enter to confirm)"))
5614
+ );
5463
5615
  };
5464
5616
  AskUserModal_default = AskUserModal;
5465
5617
  }
@@ -13915,18 +14067,50 @@ function App({ args = [] }) {
13915
14067
  const completed = [];
13916
14068
  const active = [];
13917
14069
  const columns = terminalSize.columns || 80;
13918
- messages.slice(0, completedIndex).forEach((msg) => {
14070
+ const completedMsgs = messages.slice(0, completedIndex);
14071
+ const activeMsgs = messages.slice(completedIndex);
14072
+ const seenAskSelections = /* @__PURE__ */ new Set();
14073
+ const filterDuplicates = (msgList) => {
14074
+ return msgList.filter((msg) => {
14075
+ if (msg.isAskRecord) {
14076
+ const selectionMatch = msg.text?.match(/Selection: (.*)/);
14077
+ const selection = selectionMatch ? selectionMatch[1].trim() : "";
14078
+ if (selection) {
14079
+ if (seenAskSelections.has(selection)) {
14080
+ return false;
14081
+ }
14082
+ seenAskSelections.add(selection);
14083
+ }
14084
+ }
14085
+ return true;
14086
+ });
14087
+ };
14088
+ const uniqueCompleted = filterDuplicates(completedMsgs);
14089
+ const uniqueActive = filterDuplicates(activeMsgs);
14090
+ uniqueCompleted.forEach((msg) => {
13919
14091
  const parsed = parseMessageToBlocks(msg, columns);
13920
14092
  completed.push(...parsed.completed);
13921
14093
  completed.push(...parsed.active);
13922
14094
  });
13923
- messages.slice(completedIndex).forEach((msg) => {
14095
+ uniqueActive.forEach((msg) => {
13924
14096
  const parsed = parseMessageToBlocks(msg, columns);
13925
14097
  completed.push(...parsed.completed);
13926
14098
  active.push(...parsed.active);
13927
14099
  });
13928
- const MAX_BLOCKS = 1e3;
14100
+ const MAX_BLOCKS = 5e9;
13929
14101
  const slicedCompleted = completed.slice(Math.max(0, completed.length - MAX_BLOCKS));
14102
+ if (slicedCompleted.length >= 75e3) {
14103
+ slicedCompleted.push({
14104
+ key: "memory-warning-block",
14105
+ msg: {
14106
+ role: "system",
14107
+ text: `\u26A0\uFE0F MEMORY WARNING: CHAT IS GETTING VERY LONG`,
14108
+ subText: `This session has reached ${slicedCompleted.length} blocks. To maintain optimal performance and prevent high memory usage, it is highly recommended to save and start a clean chat with /clear.`,
14109
+ isHomeWarning: true
14110
+ },
14111
+ type: "full-message"
14112
+ });
14113
+ }
13930
14114
  return {
13931
14115
  completed: slicedCompleted,
13932
14116
  active
@@ -14459,9 +14643,9 @@ function App({ args = [] }) {
14459
14643
  { cmd: "/quit", desc: "Exit and shutdown Flux" },
14460
14644
  { cmd: "/help", desc: "Show all available commands" },
14461
14645
  ...parsedArgs.playground ? [{ cmd: "/move", desc: "Move playground directory to original CWD/playground-export" }] : [],
14646
+ { cmd: "/resume", desc: "Load previous session" },
14462
14647
  { cmd: "/compress", desc: "Summarize and compress chat history" },
14463
14648
  { cmd: "/clear", desc: "Clear terminal screen" },
14464
- { cmd: "/resume", desc: "Load previous session" },
14465
14649
  { cmd: "/revert", desc: "Revert codebase back to a checkpoint" },
14466
14650
  { cmd: "/gemini", desc: "Get a happy message from Gemini CLI" },
14467
14651
  { cmd: "/save", desc: "Force save current chat" },
@@ -15691,20 +15875,27 @@ ${timestamp}` };
15691
15875
  },
15692
15876
  onAskUser: async (question, options) => {
15693
15877
  return new Promise((resolve) => {
15878
+ let resolvedFlag = false;
15694
15879
  setPendingAsk({
15695
15880
  question,
15696
15881
  options,
15697
15882
  resolve: (val) => {
15698
- setMessages((prev) => [
15699
- ...prev,
15700
- {
15701
- id: "ask-" + Date.now(),
15702
- role: "system",
15703
- text: `\u{1F4AC} **Ask User**
15883
+ if (resolvedFlag) return;
15884
+ resolvedFlag = true;
15885
+ setMessages((prev) => {
15886
+ const hasAskRecord = prev.some((m) => m.isAskRecord && m.text?.includes(`Selection: ${val}`));
15887
+ if (hasAskRecord) return prev;
15888
+ return [
15889
+ ...prev,
15890
+ {
15891
+ id: "ask-" + Date.now(),
15892
+ role: "system",
15893
+ text: `\u{1F4AC} **Ask User**
15704
15894
  Selection: ${val}`,
15705
- isAskRecord: true
15706
- }
15707
- ]);
15895
+ isAskRecord: true
15896
+ }
15897
+ ];
15898
+ });
15708
15899
  resolve(val);
15709
15900
  }
15710
15901
  });
@@ -17350,7 +17541,7 @@ var init_app = __esm({
17350
17541
  // src/cli.jsx
17351
17542
  import { spawn as spawn3 } from "child_process";
17352
17543
  import { fileURLToPath as fileURLToPath2 } from "url";
17353
- var HEAP_LIMIT = 4096;
17544
+ var HEAP_LIMIT = 6144;
17354
17545
  var isBundled = fileURLToPath2(import.meta.url).endsWith(".js");
17355
17546
  if (isBundled && !process.execArgv.some((arg) => arg.includes("max-old-space-size"))) {
17356
17547
  const cp = spawn3(process.execPath, [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fluxflow-cli",
3
- "version": "2.13.1",
4
- "date": "2026-06-26",
3
+ "version": "2.13.2",
4
+ "date": "2026-06-27",
5
5
  "description": "A high-fidelity agentic terminal assistant for the Flux Era.",
6
6
  "keywords": [
7
7
  "ai",
@@ -37,11 +37,12 @@
37
37
  },
38
38
  "scripts": {
39
39
  "start": "tsx ./src/cli.jsx",
40
- "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:@google/genai --external:zod --external:nanoid --external:puppeteer --external:pdf-lib --external:node-pty --external:html-to-docx --external:typescript --external:ws --external:web-tree-sitter"
40
+ "build": "esbuild ./src/cli.jsx --bundle --platform=node --format=esm --outfile=./dist/fluxflow.js --external:react --external:ink --external:chalk --external:fs-extra --external:gradient-string --external:ink-text-input --external:ink-select-input --external:@google/genai --external:zod --external:nanoid --external:puppeteer --external:pdf-lib --external:node-pty --external:html-to-docx --external:typescript --external:ws --external:web-tree-sitter --external:diff"
41
41
  },
42
42
  "dependencies": {
43
43
  "@google/genai": "^1.52.0",
44
44
  "chalk": "^5.6.2",
45
+ "diff": "^9.0.0",
45
46
  "fs-extra": "^11.3.4",
46
47
  "gradient-string": "^3.0.0",
47
48
  "html-to-docx": "^1.8.0",