@blockrun/franklin 3.15.2 → 3.15.3
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/ui/app.js +46 -13
- package/package.json +1 -1
package/dist/ui/app.js
CHANGED
|
@@ -99,6 +99,11 @@ function formatAgentErrorForDisplay(error) {
|
|
|
99
99
|
}
|
|
100
100
|
function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain, startWithPicker, onSubmit, onModelChange, onAbort, onExit, }) {
|
|
101
101
|
const { exit } = useApp();
|
|
102
|
+
// Track terminal rows so we can cap the dynamic-region height. Ink wipes the
|
|
103
|
+
// terminal scrollback (via ansiEscapes.clearTerminal → \x1b[3J) whenever the
|
|
104
|
+
// dynamic output exceeds rows, so any tall live region (streaming text,
|
|
105
|
+
// model picker) must be windowed to preserve "scroll to the start" history.
|
|
106
|
+
const { rows: termRows } = useTerminalSize();
|
|
102
107
|
const [input, setInput] = useState('');
|
|
103
108
|
const [streamText, setStreamText] = useState('');
|
|
104
109
|
const [thinking, setThinking] = useState(false);
|
|
@@ -761,7 +766,7 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
761
766
|
setAskUserRequest(null);
|
|
762
767
|
setAskUserInput('');
|
|
763
768
|
r(answer);
|
|
764
|
-
}, focus: true })] })] })), expandableTool && (() => {
|
|
769
|
+
}, focus: true })] })] })), expandableTool && !permissionRequest && !askUserRequest && (() => {
|
|
765
770
|
const tool = expandableTool;
|
|
766
771
|
const elapsedFmt = tool.elapsed >= 1000
|
|
767
772
|
? `${(tool.elapsed / 1000).toFixed(1)}s`
|
|
@@ -776,18 +781,46 @@ function RunCodeApp({ initialModel, workDir, walletAddress, walletBalance, chain
|
|
|
776
781
|
const lines = thinkingText.split('\n').filter(Boolean).slice(-3);
|
|
777
782
|
return (_jsx(Box, { flexDirection: "column", marginLeft: 2, children: lines.map((line, i) => (_jsxs(Text, { dimColor: true, wrap: "truncate-end", children: ['⎿ ', line.slice(0, 120)] }, i))) }));
|
|
778
783
|
})()] })), waiting && !thinking && tools.size === 0 && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { color: "yellow", children: [_jsx(Spinner, { type: "dots" }), ' ', _jsxs(Text, { dimColor: true, children: [shortModelName(currentModel), completedTools.length > 0 ? ` · step ${completedTools.length + 1}` : ''] })] }) })), streamText && (() => {
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
784
|
+
const maxLines = Math.max(8, termRows - 12);
|
|
785
|
+
const lines = streamText.split('\n');
|
|
786
|
+
const truncated = lines.length > maxLines;
|
|
787
|
+
const visible = truncated ? lines.slice(-maxLines).join('\n') : streamText;
|
|
788
|
+
const { rendered, partial } = renderMarkdownStreaming(visible);
|
|
789
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 0, marginBottom: 0, marginLeft: 2, children: [truncated && (_jsxs(Text, { dimColor: true, children: ["\u2191 ", lines.length - maxLines, " earlier line", lines.length - maxLines === 1 ? '' : 's', " \u2014 full response will appear in scrollback when this turn finishes"] })), _jsxs(Text, { wrap: "wrap", children: [rendered, rendered && partial ? '\n' : '', partial] })] }));
|
|
790
|
+
})(), responsePreview && !streamText && !permissionRequest && !askUserRequest && (_jsx(Box, { flexDirection: "column", marginBottom: 0, marginLeft: 2, children: _jsx(Text, { wrap: "wrap", children: renderMarkdown(responsePreview) }) })), inPicker && (() => {
|
|
791
|
+
const totalModels = PICKER_MODELS_FLAT.length;
|
|
792
|
+
const maxModels = Math.max(6, termRows - 12);
|
|
793
|
+
let start = Math.max(0, pickerIdx - Math.floor(maxModels / 2));
|
|
794
|
+
let end = Math.min(totalModels, start + maxModels);
|
|
795
|
+
// Expand window backward if we hit the bottom of the list, so we
|
|
796
|
+
// always fill `maxModels` rows when the list is long enough.
|
|
797
|
+
if (end - start < maxModels)
|
|
798
|
+
start = Math.max(0, end - maxModels);
|
|
799
|
+
const hiddenAbove = start;
|
|
800
|
+
const hiddenBelow = totalModels - end;
|
|
801
|
+
// Pre-compute each category's base offset into the flat model list so
|
|
802
|
+
// we can map (cat, localIdx) → globalIdx in one pass without re-walking.
|
|
803
|
+
let cursor = 0;
|
|
804
|
+
const catBases = PICKER_CATEGORIES.map((cat) => {
|
|
805
|
+
const base = cursor;
|
|
806
|
+
cursor += cat.models.length;
|
|
807
|
+
return base;
|
|
808
|
+
});
|
|
809
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Box, { marginLeft: 2, children: [_jsx(Text, { bold: true, children: "Select a model " }), _jsx(Text, { dimColor: true, children: "(\u2191\u2193 navigate, Enter select, Esc cancel)" })] }), hiddenAbove > 0 && (_jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["\u2191 ", hiddenAbove, " more above"] }) })), PICKER_CATEGORIES.map((cat, catIdx) => {
|
|
810
|
+
const base = catBases[catIdx];
|
|
811
|
+
const visible = cat.models
|
|
812
|
+
.map((m, localIdx) => ({ m, globalIdx: base + localIdx }))
|
|
813
|
+
.filter(({ globalIdx }) => globalIdx >= start && globalIdx < end);
|
|
814
|
+
if (visible.length === 0)
|
|
815
|
+
return null;
|
|
816
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["\u2500\u2500 ", cat.category, " \u2500\u2500"] }) }), visible.map(({ m, globalIdx }) => {
|
|
817
|
+
const isSelected = globalIdx === pickerIdx;
|
|
818
|
+
const isCurrent = m.id === currentModel;
|
|
819
|
+
const isHighlight = m.highlight === true;
|
|
820
|
+
return (_jsxs(Box, { marginLeft: 2, children: [_jsxs(Text, { inverse: isSelected, color: isSelected ? 'cyan' : isHighlight ? 'yellow' : undefined, bold: isSelected || isHighlight, children: [' ', m.label.padEnd(26), ' '] }), _jsxs(Text, { dimColor: true, children: [" ", m.shortcut.padEnd(14)] }), _jsx(Text, { color: m.price === 'FREE' ? 'green' : isHighlight ? 'yellow' : undefined, dimColor: !isHighlight && m.price !== 'FREE', children: m.price }), isCurrent && _jsx(Text, { color: "green", children: " \u2190" })] }, m.id));
|
|
821
|
+
})] }, cat.category));
|
|
822
|
+
}), hiddenBelow > 0 && (_jsx(Box, { marginLeft: 2, marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["\u2193 ", hiddenBelow, " more below"] }) })), _jsx(Box, { marginTop: 1, marginLeft: 2, children: _jsx(Text, { dimColor: true, children: "Your conversation stays above \u2014 picking a model keeps all history intact." }) })] }));
|
|
823
|
+
})(), !inPicker && !permissionRequest && !askUserRequest && (_jsx(InputBox, { input: input, setInput: setInput, onSubmit: handleSubmit, model: currentModel, balance: liveBalance, chain: chain, walletTail: walletAddress && walletAddress.length >= 4 && !walletAddress.startsWith('not set') ? walletAddress.slice(-4) : undefined, sessionCost: totalCost, queued: queuedInputs[0] || undefined, queuedCount: queuedInputs.length, focused: !permissionRequest && !askUserRequest, busy: !askUserRequest && (waiting || thinking || tools.size > 0), contextPct: contextPct, vimMode: vimEnabled, onVimModeChange: setCurrentVimMode }))] }));
|
|
791
824
|
}
|
|
792
825
|
export function launchInkUI(opts) {
|
|
793
826
|
let resolveInput = null;
|
package/package.json
CHANGED