code-ollama 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{tui-CCHcdC7V.js → tui-4cX-bKvd.js} +678 -104
- package/dist/cli.js +46 -7
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as SYSTEM, B as REJECT, C as resetSystemMessage, D as LIST$1, E as WARNING, F as AUTO, H as LIST, I as LABEL, L as PLAN, M as PLAN_GENERATION_INSTRUCTION, N as BACK, O as getTheme, P as CATALOG, R as SAFE, S as saveConfig, T as HEADER_PREFIX, V as VERSION, _ as deleteModel, a as color, b as streamChat, c as createSession, d as listSessions, f as loadSession, g as setClearHandler, h as reset, i as WRITE_TOOLS, j as USER, k as ASSISTANT, l as deleteSession, m as clear, n as READ_TOOLS, o as write, p as updateSessionModel, r as TOOLS, s as appendMessage, t as executeTool, u as deleteSessionIfEmpty, v as listModels, w as withSystemMessage, x as loadConfig, y as pullModel, z as APPROVE } from "../cli.js";
|
|
2
2
|
import { readdirSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join, relative } from "node:path";
|
|
@@ -6,7 +6,7 @@ import { exec } from "node:child_process";
|
|
|
6
6
|
import { Box, Static, Text, render, useApp, useInput, useStdout } from "ink";
|
|
7
7
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
|
-
import { Select, Spinner } from "@inkjs/ui";
|
|
9
|
+
import { ProgressBar, Select, Spinner } from "@inkjs/ui";
|
|
10
10
|
import { Marked } from "marked";
|
|
11
11
|
import { markedTerminal } from "marked-terminal";
|
|
12
12
|
//#region src/components/CodeBlock/CodeBlock.tsx
|
|
@@ -630,9 +630,13 @@ function ToolApproval({ toolCall, onDecision, theme = getTheme() }) {
|
|
|
630
630
|
onChange: handleChange,
|
|
631
631
|
onCancel: handleEscape,
|
|
632
632
|
children: [
|
|
633
|
-
/* @__PURE__ */
|
|
633
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
634
634
|
color: theme.colors.warning,
|
|
635
|
-
children:
|
|
635
|
+
children: [
|
|
636
|
+
"Tool requires approval ",
|
|
637
|
+
WARNING,
|
|
638
|
+
" "
|
|
639
|
+
]
|
|
636
640
|
}),
|
|
637
641
|
/* @__PURE__ */ jsxs(Box, {
|
|
638
642
|
flexDirection: "column",
|
|
@@ -817,8 +821,57 @@ function CommandMenu({ input, onSubmit }) {
|
|
|
817
821
|
});
|
|
818
822
|
}
|
|
819
823
|
//#endregion
|
|
824
|
+
//#region src/components/Suggestions.tsx
|
|
825
|
+
var DEFAULT_MAX_VISIBLE_OPTIONS = 5;
|
|
826
|
+
function Suggestions({ options, isDisabled = false, maxVisibleOptions = DEFAULT_MAX_VISIBLE_OPTIONS, resetKey, onHighlight, onSelect }) {
|
|
827
|
+
const [focusedIndex, setFocusedIndex] = useState(0);
|
|
828
|
+
useEffect(() => {
|
|
829
|
+
setFocusedIndex(0);
|
|
830
|
+
}, [resetKey]);
|
|
831
|
+
useEffect(() => {
|
|
832
|
+
if (!options.length) {
|
|
833
|
+
setFocusedIndex(0);
|
|
834
|
+
onHighlight?.(null);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
setFocusedIndex((currentIndex) => Math.min(currentIndex, options.length - 1));
|
|
838
|
+
}, [onHighlight, options]);
|
|
839
|
+
useEffect(() => {
|
|
840
|
+
onHighlight?.(options[focusedIndex] ?? null);
|
|
841
|
+
}, [
|
|
842
|
+
focusedIndex,
|
|
843
|
+
onHighlight,
|
|
844
|
+
options
|
|
845
|
+
]);
|
|
846
|
+
useInput((_, key) => {
|
|
847
|
+
if (isDisabled || !options.length) return;
|
|
848
|
+
if (key.downArrow) {
|
|
849
|
+
setFocusedIndex((currentIndex) => Math.min(currentIndex + 1, options.length - 1));
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (key.upArrow) {
|
|
853
|
+
setFocusedIndex((currentIndex) => Math.max(currentIndex - 1, 0));
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
if (key.tab || key.return) onSelect(options[focusedIndex]);
|
|
857
|
+
});
|
|
858
|
+
if (!options.length) return null;
|
|
859
|
+
const visibleStart = Math.min(Math.max(0, focusedIndex - maxVisibleOptions + 1), Math.max(0, options.length - maxVisibleOptions));
|
|
860
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
861
|
+
flexDirection: "column",
|
|
862
|
+
children: options.slice(visibleStart, visibleStart + maxVisibleOptions).map((option, index) => {
|
|
863
|
+
return /* @__PURE__ */ jsx(Box, {
|
|
864
|
+
marginLeft: 2,
|
|
865
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
866
|
+
color: visibleStart + index === focusedIndex ? "cyan" : void 0,
|
|
867
|
+
children: option.label
|
|
868
|
+
})
|
|
869
|
+
}, option.label);
|
|
870
|
+
})
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
//#endregion
|
|
820
874
|
//#region src/components/Chat/FileSuggestions.tsx
|
|
821
|
-
var MAX_VISIBLE_OPTIONS = 5;
|
|
822
875
|
var MENTION_PATTERN = /(^|.)@(\S+)/;
|
|
823
876
|
var RIPGREP_MAX_BUFFER = 10 * 1024 * 1024;
|
|
824
877
|
function normalizePath(filePath) {
|
|
@@ -897,7 +950,6 @@ async function listProjectFiles(rootDir) {
|
|
|
897
950
|
}
|
|
898
951
|
function FileSuggestions({ input, isDisabled = false, onChange, onSelect }) {
|
|
899
952
|
const [filePaths, setFilePaths] = useState([]);
|
|
900
|
-
const [focusedIndex, setFocusedIndex] = useState(0);
|
|
901
953
|
useEffect(() => {
|
|
902
954
|
async function loadProjectFiles() {
|
|
903
955
|
setFilePaths(await listProjectFiles(process.cwd()));
|
|
@@ -910,55 +962,29 @@ function FileSuggestions({ input, isDisabled = false, onChange, onSelect }) {
|
|
|
910
962
|
const normalizedQuery = mentionMatch.query.toLowerCase();
|
|
911
963
|
return filePaths.filter((filePath) => filePath.toLowerCase().includes(normalizedQuery));
|
|
912
964
|
}, [filePaths, mentionMatch]);
|
|
913
|
-
useEffect(() => {
|
|
914
|
-
setFocusedIndex(0);
|
|
915
|
-
}, [input]);
|
|
916
|
-
useEffect(() => {
|
|
917
|
-
if (!options.length) {
|
|
918
|
-
setFocusedIndex(0);
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
setFocusedIndex((currentIndex) => Math.min(currentIndex, options.length - 1));
|
|
922
|
-
}, [options]);
|
|
923
965
|
useEffect(() => {
|
|
924
966
|
if (!onChange) return;
|
|
925
|
-
if (!mentionMatch || !options.length)
|
|
926
|
-
onChange(null);
|
|
927
|
-
return;
|
|
928
|
-
}
|
|
929
|
-
onChange(buildNextInput(input, options[focusedIndex]).value);
|
|
967
|
+
if (!mentionMatch || !options.length) onChange(null);
|
|
930
968
|
}, [
|
|
931
|
-
focusedIndex,
|
|
932
|
-
input,
|
|
933
969
|
mentionMatch,
|
|
934
970
|
onChange,
|
|
935
971
|
options
|
|
936
972
|
]);
|
|
937
|
-
useInput((_, key) => {
|
|
938
|
-
if (isDisabled || !options.length) return;
|
|
939
|
-
if (key.downArrow) {
|
|
940
|
-
setFocusedIndex((currentIndex) => Math.min(currentIndex + 1, options.length - 1));
|
|
941
|
-
return;
|
|
942
|
-
}
|
|
943
|
-
if (key.upArrow) {
|
|
944
|
-
setFocusedIndex((currentIndex) => Math.max(currentIndex - 1, 0));
|
|
945
|
-
return;
|
|
946
|
-
}
|
|
947
|
-
if (key.tab || key.return) onSelect(buildNextInput(input, options[focusedIndex]));
|
|
948
|
-
});
|
|
949
973
|
if (!mentionMatch || !options.length) return null;
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
974
|
+
return /* @__PURE__ */ jsx(Suggestions, {
|
|
975
|
+
isDisabled,
|
|
976
|
+
options: options.map((option) => ({
|
|
977
|
+
label: option,
|
|
978
|
+
value: option
|
|
979
|
+
})),
|
|
980
|
+
resetKey: input,
|
|
981
|
+
onHighlight: (option) => {
|
|
982
|
+
// v8 ignore next
|
|
983
|
+
onChange?.(option ? buildNextInput(input, option.value).value : null);
|
|
984
|
+
},
|
|
985
|
+
onSelect: (option) => {
|
|
986
|
+
onSelect(buildNextInput(input, option.value));
|
|
987
|
+
}
|
|
962
988
|
});
|
|
963
989
|
}
|
|
964
990
|
//#endregion
|
|
@@ -1485,7 +1511,7 @@ function Chat({ initialMessages, model, onCommand, onMessagesChange, mode, onMod
|
|
|
1485
1511
|
marginBottom: 1,
|
|
1486
1512
|
children: /* @__PURE__ */ jsx(Text, {
|
|
1487
1513
|
color: theme.colors.error,
|
|
1488
|
-
children: interruptReason === INTERRUPT_REASON.REJECTED ?
|
|
1514
|
+
children: interruptReason === INTERRUPT_REASON.REJECTED ? `❗ Tool call rejected.` : `❗ Execution interrupted.`
|
|
1489
1515
|
})
|
|
1490
1516
|
}),
|
|
1491
1517
|
!pendingPlan && !pendingToolCall && /* @__PURE__ */ jsx(Box, {
|
|
@@ -1588,7 +1614,7 @@ function Header({ model, onLoad, theme = getTheme() }) {
|
|
|
1588
1614
|
/* @__PURE__ */ jsxs(Text, {
|
|
1589
1615
|
color: theme.colors.secondary,
|
|
1590
1616
|
dimColor: true,
|
|
1591
|
-
children: [" ", "to
|
|
1617
|
+
children: [" ", "to manage"]
|
|
1592
1618
|
})
|
|
1593
1619
|
]
|
|
1594
1620
|
}),
|
|
@@ -1602,48 +1628,608 @@ function Header({ model, onLoad, theme = getTheme() }) {
|
|
|
1602
1628
|
});
|
|
1603
1629
|
}
|
|
1604
1630
|
//#endregion
|
|
1605
|
-
//#region src/components/
|
|
1606
|
-
function
|
|
1607
|
-
const
|
|
1608
|
-
const
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1631
|
+
//#region src/components/ModelManager/ModelSuggestions.tsx
|
|
1632
|
+
function rankCatalogMatch(entry, normalizedInput) {
|
|
1633
|
+
const normalizedValue = entry.value.toLowerCase();
|
|
1634
|
+
const normalizedLabel = entry.label.toLowerCase();
|
|
1635
|
+
// v8 ignore start
|
|
1636
|
+
switch (true) {
|
|
1637
|
+
case normalizedValue.startsWith(normalizedInput): return 0;
|
|
1638
|
+
case normalizedLabel.startsWith(normalizedInput): return 1;
|
|
1639
|
+
case normalizedValue.includes(normalizedInput): return 2;
|
|
1640
|
+
case normalizedLabel.includes(normalizedInput): return 3;
|
|
1641
|
+
default: return Number.MAX_SAFE_INTEGER;
|
|
1642
|
+
}
|
|
1643
|
+
// v8 ignore stop
|
|
1644
|
+
}
|
|
1645
|
+
function ModelSuggestions({ catalog, input, isDisabled = false, onHighlight, onSelect }) {
|
|
1646
|
+
const normalizedInput = input.trim().toLowerCase();
|
|
1647
|
+
return /* @__PURE__ */ jsx(Suggestions, {
|
|
1648
|
+
isDisabled,
|
|
1649
|
+
options: useMemo(() => {
|
|
1650
|
+
if (!normalizedInput) return [];
|
|
1651
|
+
return catalog.filter((entry) => entry.value.toLowerCase().includes(normalizedInput) || entry.label.toLowerCase().includes(normalizedInput)).sort((left, right) => rankCatalogMatch(left, normalizedInput) - rankCatalogMatch(right, normalizedInput) || left.label.localeCompare(right.label)).map((entry) => ({
|
|
1652
|
+
label: entry.value,
|
|
1653
|
+
value: entry.value
|
|
1654
|
+
}));
|
|
1655
|
+
}, [catalog, normalizedInput]),
|
|
1656
|
+
resetKey: input,
|
|
1657
|
+
/* v8 ignore next */
|
|
1658
|
+
onHighlight: (option) => onHighlight?.(option?.value ?? null),
|
|
1659
|
+
onSelect: (option) => {
|
|
1660
|
+
onSelect(option.value);
|
|
1661
|
+
}
|
|
1662
|
+
});
|
|
1663
|
+
}
|
|
1664
|
+
//#endregion
|
|
1665
|
+
//#region src/components/ModelManager/types.ts
|
|
1666
|
+
var View = /* @__PURE__ */ function(View) {
|
|
1667
|
+
View["Menu"] = "menu";
|
|
1668
|
+
View["Switch"] = "switch";
|
|
1669
|
+
View["Download"] = "download";
|
|
1670
|
+
View["CustomDownload"] = "custom-download";
|
|
1671
|
+
View["Downloading"] = "downloading";
|
|
1672
|
+
View["Delete"] = "delete";
|
|
1673
|
+
View["DeleteConfirm"] = "delete-confirm";
|
|
1674
|
+
return View;
|
|
1675
|
+
}({});
|
|
1676
|
+
var MenuAction = /* @__PURE__ */ function(MenuAction) {
|
|
1677
|
+
MenuAction["Switch"] = "switch";
|
|
1678
|
+
MenuAction["Download"] = "download";
|
|
1679
|
+
MenuAction["Delete"] = "delete";
|
|
1680
|
+
MenuAction["Cancel"] = "cancel";
|
|
1681
|
+
return MenuAction;
|
|
1682
|
+
}({});
|
|
1683
|
+
var DownloadAction = /* @__PURE__ */ function(DownloadAction) {
|
|
1684
|
+
DownloadAction["Custom"] = "custom";
|
|
1685
|
+
return DownloadAction;
|
|
1686
|
+
}({});
|
|
1687
|
+
var ConfirmDeleteAction = /* @__PURE__ */ function(ConfirmDeleteAction) {
|
|
1688
|
+
ConfirmDeleteAction["Delete"] = "delete";
|
|
1689
|
+
return ConfirmDeleteAction;
|
|
1690
|
+
}({});
|
|
1691
|
+
//#endregion
|
|
1692
|
+
//#region src/components/ModelManager/utils.ts
|
|
1693
|
+
function buildMenuOptions() {
|
|
1694
|
+
return [
|
|
1695
|
+
{
|
|
1696
|
+
label: "Switch model",
|
|
1697
|
+
value: MenuAction.Switch
|
|
1698
|
+
},
|
|
1699
|
+
{
|
|
1700
|
+
label: "Download model",
|
|
1701
|
+
value: MenuAction.Download
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
label: "Delete model",
|
|
1705
|
+
value: MenuAction.Delete
|
|
1706
|
+
},
|
|
1707
|
+
{
|
|
1708
|
+
label: "Cancel",
|
|
1709
|
+
value: MenuAction.Cancel
|
|
1616
1710
|
}
|
|
1711
|
+
];
|
|
1712
|
+
}
|
|
1713
|
+
function buildInstalledModelOptions(models, currentModel) {
|
|
1714
|
+
const nextModels = [...models];
|
|
1715
|
+
if (nextModels.includes(currentModel)) {
|
|
1716
|
+
nextModels.splice(nextModels.indexOf(currentModel), 1);
|
|
1717
|
+
nextModels.unshift(currentModel);
|
|
1718
|
+
}
|
|
1719
|
+
return nextModels.map((model) => ({
|
|
1720
|
+
label: model === currentModel ? `${model} (current model)` : model,
|
|
1721
|
+
value: model
|
|
1722
|
+
}));
|
|
1723
|
+
}
|
|
1724
|
+
function buildDownloadOptions(installedModels) {
|
|
1725
|
+
const installedModelSet = new Set(installedModels);
|
|
1726
|
+
const availableCatalog = CATALOG.filter(({ value, alias }) => !installedModelSet.has(value) && !(alias && installedModelSet.has(alias)));
|
|
1727
|
+
return [
|
|
1728
|
+
{
|
|
1729
|
+
label: "Enter custom model...",
|
|
1730
|
+
value: DownloadAction.Custom
|
|
1731
|
+
},
|
|
1732
|
+
...availableCatalog.map(({ label, value }) => ({
|
|
1733
|
+
label,
|
|
1734
|
+
value
|
|
1735
|
+
})),
|
|
1736
|
+
BACK
|
|
1737
|
+
];
|
|
1738
|
+
}
|
|
1739
|
+
function getNoticeColor(tone, theme) {
|
|
1740
|
+
switch (tone) {
|
|
1741
|
+
case "error": return theme.colors.error;
|
|
1742
|
+
case "success": return theme.colors.status;
|
|
1743
|
+
default: return theme.colors.secondary;
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
function formatBytes(bytes) {
|
|
1747
|
+
if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
|
|
1748
|
+
const units = [
|
|
1749
|
+
"B",
|
|
1750
|
+
"KB",
|
|
1751
|
+
"MB",
|
|
1752
|
+
"GB",
|
|
1753
|
+
"TB"
|
|
1754
|
+
];
|
|
1755
|
+
let value = bytes;
|
|
1756
|
+
let index = 0;
|
|
1757
|
+
while (value >= 1024 && index < units.length - 1) {
|
|
1758
|
+
value /= 1024;
|
|
1759
|
+
index += 1;
|
|
1760
|
+
}
|
|
1761
|
+
const fractionDigits = value >= 10 || index === 0 ? 0 : 1;
|
|
1762
|
+
return `${value.toFixed(fractionDigits)} ${units[index]}`;
|
|
1763
|
+
}
|
|
1764
|
+
function isAbortError(error) {
|
|
1765
|
+
return error instanceof Error && error.name === "AbortError";
|
|
1766
|
+
}
|
|
1767
|
+
function mergeDownloadProgress(previous, model, status, completed, total) {
|
|
1768
|
+
const nextCompleted = typeof completed === "number" && Number.isFinite(completed) && completed >= 0 ? completed : null;
|
|
1769
|
+
const nextTotal = typeof total === "number" && Number.isFinite(total) && total > 0 ? total : null;
|
|
1770
|
+
if (nextTotal !== null && nextCompleted !== null) return {
|
|
1771
|
+
model,
|
|
1772
|
+
status,
|
|
1773
|
+
completed: nextCompleted,
|
|
1774
|
+
total: nextTotal
|
|
1775
|
+
};
|
|
1776
|
+
const hasPreviousProgress = previous?.model === model && previous.total > 0;
|
|
1777
|
+
return {
|
|
1778
|
+
model,
|
|
1779
|
+
status,
|
|
1780
|
+
completed: hasPreviousProgress ? previous.completed : 0,
|
|
1781
|
+
total: hasPreviousProgress ? previous.total : 0
|
|
1782
|
+
};
|
|
1783
|
+
}
|
|
1784
|
+
//#endregion
|
|
1785
|
+
//#region src/components/ModelManager/ModelCustomDownloadView.tsx
|
|
1786
|
+
function ModelCustomDownloadView({ downloadDraft, notice, theme, onDraftChange, onHighlight, onSelectSuggestion, onSubmit }) {
|
|
1787
|
+
const renderNotice = () => notice ? /* @__PURE__ */ jsx(Text, {
|
|
1788
|
+
color: getNoticeColor(notice.tone, theme),
|
|
1789
|
+
children: notice.text
|
|
1790
|
+
}) : null;
|
|
1791
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1792
|
+
flexDirection: "column",
|
|
1793
|
+
children: [
|
|
1794
|
+
/* @__PURE__ */ jsx(Text, { children: "Enter an Ollama model name to download." }),
|
|
1795
|
+
/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, { children: "> " }), /* @__PURE__ */ jsx(TextInput, {
|
|
1796
|
+
value: downloadDraft,
|
|
1797
|
+
placeholder: "name:tag",
|
|
1798
|
+
wrapIndent: 2,
|
|
1799
|
+
onChange: onDraftChange,
|
|
1800
|
+
onSubmit
|
|
1801
|
+
})] }),
|
|
1802
|
+
/* @__PURE__ */ jsx(ModelSuggestions, {
|
|
1803
|
+
catalog: [],
|
|
1804
|
+
input: downloadDraft,
|
|
1805
|
+
onHighlight,
|
|
1806
|
+
onSelect: onSelectSuggestion
|
|
1807
|
+
}),
|
|
1808
|
+
renderNotice(),
|
|
1809
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1810
|
+
color: theme.colors.secondary,
|
|
1811
|
+
dimColor: true,
|
|
1812
|
+
children: "Press Enter to download, Esc or Ctrl+C to go back."
|
|
1813
|
+
})
|
|
1814
|
+
]
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1817
|
+
//#endregion
|
|
1818
|
+
//#region src/components/ModelManager/ModelDeleteConfirmView.tsx
|
|
1819
|
+
function ModelDeleteConfirmView({ deleteCandidate, isDeleting, notice, theme, onCancel, onConfirm }) {
|
|
1820
|
+
if (isDeleting) return /* @__PURE__ */ jsx(Spinner, { label: `Deleting model ${deleteCandidate}...` });
|
|
1821
|
+
const renderNotice = () => notice ? /* @__PURE__ */ jsx(Text, {
|
|
1822
|
+
color: getNoticeColor(notice.tone, theme),
|
|
1823
|
+
children: notice.text
|
|
1824
|
+
}) : null;
|
|
1825
|
+
return /* @__PURE__ */ jsxs(SelectPrompt, {
|
|
1826
|
+
options: [{
|
|
1827
|
+
label: `Yes, delete ${deleteCandidate}`,
|
|
1828
|
+
value: ConfirmDeleteAction.Delete
|
|
1829
|
+
}, {
|
|
1830
|
+
...BACK,
|
|
1831
|
+
label: "No"
|
|
1832
|
+
}],
|
|
1833
|
+
onCancel,
|
|
1834
|
+
onChange: onConfirm,
|
|
1835
|
+
children: [
|
|
1836
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1837
|
+
WARNING,
|
|
1838
|
+
" Delete model",
|
|
1839
|
+
" ",
|
|
1840
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1841
|
+
color: theme.colors.model,
|
|
1842
|
+
children: deleteCandidate
|
|
1843
|
+
}),
|
|
1844
|
+
"?"
|
|
1845
|
+
] }),
|
|
1846
|
+
renderNotice(),
|
|
1847
|
+
/* @__PURE__ */ jsx(SelectPromptHint, { message: "This action cannot be undone" })
|
|
1848
|
+
]
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1851
|
+
//#endregion
|
|
1852
|
+
//#region src/components/ModelManager/ModelDeleteView.tsx
|
|
1853
|
+
function ModelDeleteView({ currentModel, installedModels, isLoading, notice, theme, onCancel, onSelect }) {
|
|
1854
|
+
if (isLoading) return /* @__PURE__ */ jsx(Spinner, { label: "Loading models..." });
|
|
1855
|
+
return /* @__PURE__ */ jsxs(SelectPrompt, {
|
|
1856
|
+
options: [...buildInstalledModelOptions(installedModels.filter((model) => model !== currentModel), currentModel), BACK],
|
|
1857
|
+
onCancel,
|
|
1858
|
+
onChange: (value) => {
|
|
1859
|
+
if (value === BACK.value) onCancel();
|
|
1860
|
+
else onSelect(value);
|
|
1861
|
+
},
|
|
1862
|
+
children: [
|
|
1863
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1864
|
+
"Delete an installed model (current model",
|
|
1865
|
+
" ",
|
|
1866
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1867
|
+
color: theme.colors.model,
|
|
1868
|
+
children: currentModel
|
|
1869
|
+
}),
|
|
1870
|
+
" cannot be deleted)."
|
|
1871
|
+
] }),
|
|
1872
|
+
notice && /* @__PURE__ */ jsx(Text, {
|
|
1873
|
+
color: getNoticeColor(notice.tone, theme),
|
|
1874
|
+
children: notice.text
|
|
1875
|
+
}),
|
|
1876
|
+
/* @__PURE__ */ jsx(SelectPromptHint, { message: "Delete models" })
|
|
1877
|
+
]
|
|
1617
1878
|
});
|
|
1879
|
+
}
|
|
1880
|
+
//#endregion
|
|
1881
|
+
//#region src/components/ModelManager/ModelDownloadingView.tsx
|
|
1882
|
+
function ModelDownloadingView({ progress, theme, onCancel }) {
|
|
1883
|
+
const percent = progress.total > 0 && Number.isFinite(progress.completed) && Number.isFinite(progress.total) ? Math.round(progress.completed / progress.total * 100) : null;
|
|
1884
|
+
return /* @__PURE__ */ jsxs(Box, {
|
|
1885
|
+
flexDirection: "column",
|
|
1886
|
+
children: [
|
|
1887
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
1888
|
+
"Downloading model:",
|
|
1889
|
+
" ",
|
|
1890
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1891
|
+
color: theme.colors.model,
|
|
1892
|
+
children: progress.model
|
|
1893
|
+
})
|
|
1894
|
+
] }),
|
|
1895
|
+
/* @__PURE__ */ jsx(Text, { children: progress.status }),
|
|
1896
|
+
percent !== null ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(Text, { children: [
|
|
1897
|
+
percent,
|
|
1898
|
+
"% (",
|
|
1899
|
+
formatBytes(progress.completed),
|
|
1900
|
+
" /",
|
|
1901
|
+
" ",
|
|
1902
|
+
formatBytes(progress.total),
|
|
1903
|
+
")"
|
|
1904
|
+
] }), /* @__PURE__ */ jsx(ProgressBar, { value: Math.max(0, Math.min(100, percent)) })] }) : /* @__PURE__ */ jsx(Text, {
|
|
1905
|
+
color: theme.colors.secondary,
|
|
1906
|
+
dimColor: true,
|
|
1907
|
+
children: "Progress details unavailable. Waiting for Ollama updates..."
|
|
1908
|
+
}),
|
|
1909
|
+
/* @__PURE__ */ jsx(SelectPrompt, {
|
|
1910
|
+
options: [{
|
|
1911
|
+
label: "Cancel download",
|
|
1912
|
+
value: "cancel-download"
|
|
1913
|
+
}],
|
|
1914
|
+
onCancel,
|
|
1915
|
+
onChange: onCancel,
|
|
1916
|
+
children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Press Enter, Esc, or Ctrl+C to cancel" })
|
|
1917
|
+
})
|
|
1918
|
+
]
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1921
|
+
//#endregion
|
|
1922
|
+
//#region src/components/ModelManager/ModelDownloadView.tsx
|
|
1923
|
+
function ModelDownloadView({ installedModels, notice, theme, onCancel, onChange }) {
|
|
1924
|
+
return /* @__PURE__ */ jsxs(SelectPrompt, {
|
|
1925
|
+
options: buildDownloadOptions(installedModels),
|
|
1926
|
+
onCancel,
|
|
1927
|
+
onChange: (value) => {
|
|
1928
|
+
switch (value) {
|
|
1929
|
+
case "custom":
|
|
1930
|
+
onChange("custom");
|
|
1931
|
+
break;
|
|
1932
|
+
case "back":
|
|
1933
|
+
onCancel();
|
|
1934
|
+
break;
|
|
1935
|
+
default:
|
|
1936
|
+
onChange(value);
|
|
1937
|
+
break;
|
|
1938
|
+
}
|
|
1939
|
+
},
|
|
1940
|
+
children: [
|
|
1941
|
+
/* @__PURE__ */ jsx(Text, { children: "Choose a model to download or use a custom model name." }),
|
|
1942
|
+
notice && /* @__PURE__ */ jsx(Text, {
|
|
1943
|
+
color: getNoticeColor(notice.tone, theme),
|
|
1944
|
+
children: notice.text
|
|
1945
|
+
}),
|
|
1946
|
+
/* @__PURE__ */ jsx(SelectPromptHint, { message: "Download models" })
|
|
1947
|
+
]
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
//#endregion
|
|
1951
|
+
//#region src/components/ModelManager/ModelSwitchView.tsx
|
|
1952
|
+
function ModelSwitchView({ currentModel, installedModels, isLoading, onCancel, onSelect }) {
|
|
1953
|
+
if (isLoading) return /* @__PURE__ */ jsx(Spinner, { label: "Loading models..." });
|
|
1954
|
+
return /* @__PURE__ */ jsx(SelectPrompt, {
|
|
1955
|
+
options: [...buildInstalledModelOptions(installedModels, currentModel), BACK],
|
|
1956
|
+
onCancel,
|
|
1957
|
+
onChange: (value) => {
|
|
1958
|
+
if (value === BACK.value) onCancel();
|
|
1959
|
+
else onSelect(value);
|
|
1960
|
+
},
|
|
1961
|
+
children: /* @__PURE__ */ jsx(SelectPromptHint, { message: "Switch models" })
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
//#endregion
|
|
1965
|
+
//#region src/components/ModelManager/ModelManager.tsx
|
|
1966
|
+
function ModelManager({ currentModel, onSelect, onClose, theme = getTheme() }) {
|
|
1967
|
+
const [view, setView] = useState(View.Menu);
|
|
1968
|
+
const [installedModels, setInstalledModels] = useState([]);
|
|
1969
|
+
const [isLoadingModels, setIsLoadingModels] = useState(true);
|
|
1970
|
+
const [loadError, setLoadError] = useState(null);
|
|
1971
|
+
const [notice, setNotice] = useState(null);
|
|
1972
|
+
const [downloadDraft, setDownloadDraft] = useState("");
|
|
1973
|
+
const [highlightedSuggestion, setHighlightedSuggestion] = useState(null);
|
|
1974
|
+
const [downloadProgress, setDownloadProgress] = useState(null);
|
|
1975
|
+
const [deleteCandidate, setDeleteCandidate] = useState(null);
|
|
1976
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
1977
|
+
const isDeletingRef = useRef(false);
|
|
1978
|
+
const pullRef = useRef(null);
|
|
1979
|
+
const loadInstalledModels = useCallback(async () => {
|
|
1980
|
+
setIsLoadingModels(true);
|
|
1981
|
+
setLoadError(null);
|
|
1982
|
+
try {
|
|
1983
|
+
setInstalledModels(await listModels());
|
|
1984
|
+
} catch (error) {
|
|
1985
|
+
setLoadError(error instanceof Error ? error.message : /* v8 ignore next */ String(error));
|
|
1986
|
+
} finally {
|
|
1987
|
+
setIsLoadingModels(false);
|
|
1988
|
+
}
|
|
1989
|
+
}, []);
|
|
1618
1990
|
useEffect(() => {
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1991
|
+
loadInstalledModels();
|
|
1992
|
+
}, [loadInstalledModels]);
|
|
1993
|
+
const resetDownloadState = useCallback(() => {
|
|
1994
|
+
setDownloadDraft("");
|
|
1995
|
+
setHighlightedSuggestion(null);
|
|
1996
|
+
setDownloadProgress(null);
|
|
1997
|
+
pullRef.current = null;
|
|
1998
|
+
}, []);
|
|
1999
|
+
const handleBackToMenu = useCallback(() => {
|
|
2000
|
+
setNotice(null);
|
|
2001
|
+
setDeleteCandidate(null);
|
|
2002
|
+
setIsDeleting(false);
|
|
2003
|
+
isDeletingRef.current = false;
|
|
2004
|
+
resetDownloadState();
|
|
2005
|
+
setView(View.Menu);
|
|
2006
|
+
}, [resetDownloadState]);
|
|
2007
|
+
const cancelActivePull = useCallback(() => {
|
|
2008
|
+
pullRef.current?.abort();
|
|
2009
|
+
}, []);
|
|
2010
|
+
useInput((input, key) => {
|
|
2011
|
+
if (view === View.CustomDownload && (key.escape || key.ctrl && input === "c")) {
|
|
2012
|
+
setNotice(null);
|
|
2013
|
+
setHighlightedSuggestion(null);
|
|
2014
|
+
setView(View.Download);
|
|
2015
|
+
return;
|
|
2016
|
+
}
|
|
2017
|
+
if (view === View.Downloading && (key.escape || key.ctrl && input === "c")) cancelActivePull();
|
|
2018
|
+
});
|
|
2019
|
+
const handleMenuChange = useCallback((value) => {
|
|
2020
|
+
setNotice(null);
|
|
2021
|
+
switch (value) {
|
|
2022
|
+
case "switch":
|
|
2023
|
+
setView(View.Switch);
|
|
2024
|
+
break;
|
|
2025
|
+
case "download":
|
|
2026
|
+
setView(View.Download);
|
|
2027
|
+
break;
|
|
2028
|
+
case "delete":
|
|
2029
|
+
setView(View.Delete);
|
|
2030
|
+
break;
|
|
2031
|
+
default: onClose();
|
|
2032
|
+
}
|
|
2033
|
+
}, [onClose]);
|
|
2034
|
+
const handleSwitchChange = useCallback((model) => {
|
|
2035
|
+
onSelect({ model });
|
|
2036
|
+
}, [onSelect]);
|
|
2037
|
+
const startPull = useCallback(async (model) => {
|
|
2038
|
+
const normalizedModel = model.trim();
|
|
2039
|
+
if (!normalizedModel) {
|
|
2040
|
+
setNotice({
|
|
2041
|
+
tone: "error",
|
|
2042
|
+
text: `❗ Enter a model name to download`
|
|
2043
|
+
});
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
if (installedModels.includes(normalizedModel)) {
|
|
2047
|
+
setNotice({
|
|
2048
|
+
tone: "info",
|
|
2049
|
+
text: `${normalizedModel} is already installed`
|
|
2050
|
+
});
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
setNotice(null);
|
|
2054
|
+
setDownloadProgress({
|
|
2055
|
+
model: normalizedModel,
|
|
2056
|
+
status: "Starting download...",
|
|
2057
|
+
completed: 0,
|
|
2058
|
+
total: 0
|
|
2059
|
+
});
|
|
2060
|
+
setView(View.Downloading);
|
|
2061
|
+
try {
|
|
2062
|
+
const pull = await pullModel(normalizedModel);
|
|
2063
|
+
pullRef.current = pull;
|
|
2064
|
+
for await (const update of pull) setDownloadProgress((previous) => {
|
|
2065
|
+
return mergeDownloadProgress(previous, normalizedModel, update.status, update.completed, update.total);
|
|
2066
|
+
});
|
|
2067
|
+
pullRef.current = null;
|
|
2068
|
+
resetDownloadState();
|
|
2069
|
+
await loadInstalledModels();
|
|
2070
|
+
setNotice({
|
|
2071
|
+
tone: "success",
|
|
2072
|
+
text: `✅ ${normalizedModel} downloaded successfully`
|
|
2073
|
+
});
|
|
2074
|
+
setView(View.Menu);
|
|
2075
|
+
} catch (error) {
|
|
2076
|
+
pullRef.current = null;
|
|
2077
|
+
if (isAbortError(error)) {
|
|
2078
|
+
setNotice({
|
|
2079
|
+
tone: "error",
|
|
2080
|
+
text: `❌ Download canceled for ${normalizedModel}`
|
|
2081
|
+
});
|
|
2082
|
+
setDownloadProgress(null);
|
|
2083
|
+
setView(View.Download);
|
|
2084
|
+
return;
|
|
1632
2085
|
}
|
|
2086
|
+
setNotice({
|
|
2087
|
+
tone: "error",
|
|
2088
|
+
text: `❗ Error downloading model: ${error instanceof Error ? error.message : /* v8 ignore next */ String(error)}`
|
|
2089
|
+
});
|
|
2090
|
+
setDownloadProgress(null);
|
|
2091
|
+
setView(View.CustomDownload);
|
|
1633
2092
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
2093
|
+
}, [
|
|
2094
|
+
installedModels,
|
|
2095
|
+
loadInstalledModels,
|
|
2096
|
+
resetDownloadState
|
|
2097
|
+
]);
|
|
2098
|
+
const handleDownloadChange = useCallback((value) => {
|
|
2099
|
+
if (value === "custom") {
|
|
2100
|
+
setNotice(null);
|
|
2101
|
+
setView(View.CustomDownload);
|
|
2102
|
+
return;
|
|
2103
|
+
}
|
|
2104
|
+
// v8 ignore next 3
|
|
2105
|
+
if (value === "back") {
|
|
2106
|
+
handleBackToMenu();
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
setDownloadDraft(value);
|
|
2110
|
+
startPull(value);
|
|
2111
|
+
}, [handleBackToMenu, startPull]);
|
|
2112
|
+
const handleCustomDownloadSubmit = useCallback((value) => {
|
|
2113
|
+
const nextValue = highlightedSuggestion ?? value.trim();
|
|
2114
|
+
setDownloadDraft(nextValue);
|
|
2115
|
+
startPull(nextValue);
|
|
2116
|
+
}, [highlightedSuggestion, startPull]);
|
|
2117
|
+
const handleDeleteChange = useCallback((model) => {
|
|
2118
|
+
setNotice(null);
|
|
2119
|
+
setDeleteCandidate(model);
|
|
2120
|
+
setView(View.DeleteConfirm);
|
|
2121
|
+
}, []);
|
|
2122
|
+
const handleDeleteConfirm = useCallback(async (value) => {
|
|
2123
|
+
if (isDeletingRef.current) return;
|
|
2124
|
+
if (value === "back") {
|
|
2125
|
+
setView(View.Delete);
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
// v8 ignore next 3
|
|
2129
|
+
if (!deleteCandidate) {
|
|
2130
|
+
setView(View.Delete);
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
try {
|
|
2134
|
+
isDeletingRef.current = true;
|
|
2135
|
+
setIsDeleting(true);
|
|
2136
|
+
await deleteModel(deleteCandidate);
|
|
2137
|
+
await loadInstalledModels();
|
|
2138
|
+
setNotice({
|
|
2139
|
+
tone: "success",
|
|
2140
|
+
text: `✅ ${deleteCandidate} deleted successfully`
|
|
2141
|
+
});
|
|
2142
|
+
isDeletingRef.current = false;
|
|
2143
|
+
setIsDeleting(false);
|
|
2144
|
+
setDeleteCandidate(null);
|
|
2145
|
+
setView(View.Delete);
|
|
2146
|
+
} catch (error) {
|
|
2147
|
+
isDeletingRef.current = false;
|
|
2148
|
+
setIsDeleting(false);
|
|
2149
|
+
setNotice({
|
|
2150
|
+
tone: "error",
|
|
2151
|
+
text: `❗ Error deleting model: ${error instanceof Error ? error.message : /* v8 ignore next */ String(error)}`
|
|
2152
|
+
});
|
|
2153
|
+
setView(View.Delete);
|
|
2154
|
+
}
|
|
2155
|
+
}, [deleteCandidate, loadInstalledModels]);
|
|
2156
|
+
const renderNotice = () => notice ? /* @__PURE__ */ jsx(Text, {
|
|
2157
|
+
color: notice.tone === "error" ? theme.colors.error : notice.tone === "success" ? theme.colors.status : theme.colors.secondary,
|
|
2158
|
+
children: notice.text
|
|
2159
|
+
}) : null;
|
|
2160
|
+
if (loadError && view !== View.Menu) return /* @__PURE__ */ jsxs(Box, {
|
|
2161
|
+
flexDirection: "column",
|
|
2162
|
+
children: [/* @__PURE__ */ jsxs(Text, {
|
|
2163
|
+
color: theme.colors.error,
|
|
2164
|
+
children: ["Error loading models: ", loadError]
|
|
2165
|
+
}), /* @__PURE__ */ jsx(Text, {
|
|
2166
|
+
color: theme.colors.secondary,
|
|
2167
|
+
dimColor: true,
|
|
2168
|
+
children: "Press Esc to go back."
|
|
2169
|
+
})]
|
|
1639
2170
|
});
|
|
1640
|
-
if (
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
2171
|
+
if (view === View.Downloading && downloadProgress) return /* @__PURE__ */ jsx(ModelDownloadingView, {
|
|
2172
|
+
progress: downloadProgress,
|
|
2173
|
+
theme,
|
|
2174
|
+
onCancel: cancelActivePull
|
|
2175
|
+
});
|
|
2176
|
+
if (view === View.CustomDownload) return /* @__PURE__ */ jsx(ModelCustomDownloadView, {
|
|
2177
|
+
downloadDraft,
|
|
2178
|
+
notice,
|
|
2179
|
+
theme,
|
|
2180
|
+
onDraftChange: setDownloadDraft,
|
|
2181
|
+
onHighlight: setHighlightedSuggestion,
|
|
2182
|
+
onSelectSuggestion: (value) => {
|
|
2183
|
+
setDownloadDraft(value);
|
|
2184
|
+
setHighlightedSuggestion(value);
|
|
2185
|
+
},
|
|
2186
|
+
onSubmit: handleCustomDownloadSubmit
|
|
2187
|
+
});
|
|
2188
|
+
if (view === View.Switch) return /* @__PURE__ */ jsx(ModelSwitchView, {
|
|
2189
|
+
currentModel,
|
|
2190
|
+
installedModels,
|
|
2191
|
+
isLoading: isLoadingModels,
|
|
2192
|
+
onCancel: handleBackToMenu,
|
|
2193
|
+
onSelect: handleSwitchChange
|
|
2194
|
+
});
|
|
2195
|
+
if (view === View.Download) return /* @__PURE__ */ jsx(ModelDownloadView, {
|
|
2196
|
+
installedModels,
|
|
2197
|
+
notice,
|
|
2198
|
+
theme,
|
|
2199
|
+
onCancel: handleBackToMenu,
|
|
2200
|
+
onChange: handleDownloadChange
|
|
2201
|
+
});
|
|
2202
|
+
if (view === View.Delete) return /* @__PURE__ */ jsx(ModelDeleteView, {
|
|
2203
|
+
currentModel,
|
|
2204
|
+
installedModels,
|
|
2205
|
+
isLoading: isLoadingModels,
|
|
2206
|
+
notice,
|
|
2207
|
+
theme,
|
|
2208
|
+
onCancel: handleBackToMenu,
|
|
2209
|
+
onSelect: handleDeleteChange
|
|
2210
|
+
});
|
|
2211
|
+
if (view === View.DeleteConfirm && deleteCandidate) return /* @__PURE__ */ jsx(ModelDeleteConfirmView, {
|
|
2212
|
+
deleteCandidate,
|
|
2213
|
+
isDeleting,
|
|
2214
|
+
notice,
|
|
2215
|
+
theme,
|
|
2216
|
+
onCancel: () => {
|
|
2217
|
+
setView(View.Delete);
|
|
2218
|
+
},
|
|
2219
|
+
onConfirm: handleDeleteConfirm
|
|
2220
|
+
});
|
|
2221
|
+
return /* @__PURE__ */ jsxs(SelectPrompt, {
|
|
2222
|
+
options: buildMenuOptions(),
|
|
1645
2223
|
onCancel: onClose,
|
|
1646
|
-
|
|
2224
|
+
onChange: handleMenuChange,
|
|
2225
|
+
children: [
|
|
2226
|
+
/* @__PURE__ */ jsxs(Text, { children: ["Current model: ", /* @__PURE__ */ jsx(Text, {
|
|
2227
|
+
color: theme.colors.model,
|
|
2228
|
+
children: currentModel
|
|
2229
|
+
})] }),
|
|
2230
|
+
renderNotice(),
|
|
2231
|
+
/* @__PURE__ */ jsx(SelectPromptHint, { message: "Manage models" })
|
|
2232
|
+
]
|
|
1647
2233
|
});
|
|
1648
2234
|
}
|
|
1649
2235
|
//#endregion
|
|
@@ -1751,7 +2337,6 @@ function SearchSettings({ currentUrl, onClose, onSave, theme = getTheme() }) {
|
|
|
1751
2337
|
//#endregion
|
|
1752
2338
|
//#region src/components/SessionManager.tsx
|
|
1753
2339
|
var ACTION = {
|
|
1754
|
-
BACK: "back",
|
|
1755
2340
|
CLOSE: "close",
|
|
1756
2341
|
DELETE_MENU: "delete-menu",
|
|
1757
2342
|
DELETE_PREFIX: "delete:",
|
|
@@ -1779,16 +2364,10 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen, th
|
|
|
1779
2364
|
const options = view === "open" ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
|
|
1780
2365
|
label: formatSessionLabel(session, maxLabelWidth),
|
|
1781
2366
|
value: `${ACTION.OPEN_PREFIX}${session.id}`
|
|
1782
|
-
})), {
|
|
1783
|
-
label: "Back",
|
|
1784
|
-
value: ACTION.BACK
|
|
1785
|
-
}] : view === "delete" ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
|
|
2367
|
+
})), BACK] : view === "delete" ? [...sessions.filter(({ id }) => id !== currentSessionId).map((session) => ({
|
|
1786
2368
|
label: formatSessionLabel(session, maxLabelWidth, "Delete "),
|
|
1787
2369
|
value: `${ACTION.DELETE_PREFIX}${session.id}`
|
|
1788
|
-
})),
|
|
1789
|
-
label: "Back",
|
|
1790
|
-
value: ACTION.BACK
|
|
1791
|
-
}] : [
|
|
2370
|
+
})), BACK] : [
|
|
1792
2371
|
{
|
|
1793
2372
|
label: "New session",
|
|
1794
2373
|
value: ACTION.NEW
|
|
@@ -1820,7 +2399,7 @@ function SessionManager({ currentSessionId, onClose, onDelete, onNew, onOpen, th
|
|
|
1820
2399
|
case value === ACTION.OPEN_MENU:
|
|
1821
2400
|
setView("open");
|
|
1822
2401
|
break;
|
|
1823
|
-
case value ===
|
|
2402
|
+
case value === BACK.value:
|
|
1824
2403
|
setView("main");
|
|
1825
2404
|
break;
|
|
1826
2405
|
case value.startsWith(ACTION.DELETE_PREFIX):
|
|
@@ -1972,7 +2551,7 @@ function ThemeSettings({ currentTheme, onClose, onPreview, onSave }) {
|
|
|
1972
2551
|
//#region src/components/App/constants.ts
|
|
1973
2552
|
var SCREEN = /* @__PURE__ */ function(SCREEN) {
|
|
1974
2553
|
SCREEN["CHAT"] = "chat";
|
|
1975
|
-
SCREEN["
|
|
2554
|
+
SCREEN["MODEL_MANAGER"] = "model-manager";
|
|
1976
2555
|
SCREEN["SEARCH_SETTINGS"] = "search-settings";
|
|
1977
2556
|
SCREEN["SESSION_MANAGER"] = "session-manager";
|
|
1978
2557
|
SCREEN["THEME_SETTINGS"] = "theme-settings";
|
|
@@ -1996,7 +2575,7 @@ function useScreenRouter() {
|
|
|
1996
2575
|
setScreen(SCREEN.SESSION_MANAGER);
|
|
1997
2576
|
break;
|
|
1998
2577
|
case "/model":
|
|
1999
|
-
setScreen(SCREEN.
|
|
2578
|
+
setScreen(SCREEN.MODEL_MANAGER);
|
|
2000
2579
|
break;
|
|
2001
2580
|
case "/search":
|
|
2002
2581
|
setScreen(SCREEN.SEARCH_SETTINGS);
|
|
@@ -2049,8 +2628,6 @@ function useSessionManager({ sessionId, model, commandColor }) {
|
|
|
2049
2628
|
}, []);
|
|
2050
2629
|
return {
|
|
2051
2630
|
activeSession,
|
|
2052
|
-
sessionRef,
|
|
2053
|
-
setActiveSession,
|
|
2054
2631
|
setSession,
|
|
2055
2632
|
handleCreateSession: useCallback(() => {
|
|
2056
2633
|
const nextSession = createSession(modelRef.current);
|
|
@@ -2089,14 +2666,12 @@ function useSessionManager({ sessionId, model, commandColor }) {
|
|
|
2089
2666
|
//#region src/components/App/hooks/useThemeSettings.ts
|
|
2090
2667
|
function useThemeSettings({ currentTheme, onUpdateConfig, setScreen }) {
|
|
2091
2668
|
const [previewThemeId, setPreviewThemeId] = useState(null);
|
|
2092
|
-
const
|
|
2093
|
-
const activeTheme = getTheme(activeThemeId);
|
|
2669
|
+
const activeTheme = getTheme(previewThemeId ?? currentTheme);
|
|
2094
2670
|
const handleThemePreview = useCallback((themeId) => {
|
|
2095
2671
|
setPreviewThemeId(themeId);
|
|
2096
2672
|
}, []);
|
|
2097
2673
|
return {
|
|
2098
2674
|
activeTheme,
|
|
2099
|
-
activeThemeId,
|
|
2100
2675
|
handleThemeClose: useCallback(() => {
|
|
2101
2676
|
setPreviewThemeId(null);
|
|
2102
2677
|
setScreen(SCREEN.CHAT);
|
|
@@ -2106,7 +2681,6 @@ function useThemeSettings({ currentTheme, onUpdateConfig, setScreen }) {
|
|
|
2106
2681
|
setPreviewThemeId(null);
|
|
2107
2682
|
onUpdateConfig({ theme: themeId });
|
|
2108
2683
|
}, [onUpdateConfig]),
|
|
2109
|
-
previewThemeId,
|
|
2110
2684
|
setPreviewThemeId
|
|
2111
2685
|
};
|
|
2112
2686
|
}
|
|
@@ -2181,8 +2755,8 @@ function App({ sessionId }) {
|
|
|
2181
2755
|
}, [handleCreateSession, setScreen]);
|
|
2182
2756
|
let screenContent;
|
|
2183
2757
|
switch (currentScreen) {
|
|
2184
|
-
case SCREEN.
|
|
2185
|
-
screenContent = /* @__PURE__ */ jsx(
|
|
2758
|
+
case SCREEN.MODEL_MANAGER:
|
|
2759
|
+
screenContent = /* @__PURE__ */ jsx(ModelManager, {
|
|
2186
2760
|
currentModel: appConfig.model,
|
|
2187
2761
|
onSelect: handleUpdateConfig,
|
|
2188
2762
|
onClose: handleClose,
|
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@ var LIST$1 = [
|
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
name: "/model",
|
|
22
|
-
description: "
|
|
22
|
+
description: "manage Ollama models"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
name: "/theme",
|
|
@@ -37,7 +37,7 @@ var LIST$1 = [
|
|
|
37
37
|
//#endregion
|
|
38
38
|
//#region package.json
|
|
39
39
|
var name = "code-ollama";
|
|
40
|
-
var version = "0.
|
|
40
|
+
var version = "0.18.0";
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/constants/package.ts
|
|
43
43
|
var NAME = name;
|
|
@@ -60,6 +60,38 @@ var LABEL = {
|
|
|
60
60
|
plan: "Plan"
|
|
61
61
|
};
|
|
62
62
|
//#endregion
|
|
63
|
+
//#region src/constants/models.ts
|
|
64
|
+
/**
|
|
65
|
+
* @see https://ollama.com/library
|
|
66
|
+
*/
|
|
67
|
+
var CATALOG = [
|
|
68
|
+
{
|
|
69
|
+
label: "Gemma 4 (gemma4:latest)",
|
|
70
|
+
value: "gemma4:latest",
|
|
71
|
+
alias: "gemma4:e4b"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: "Granite 4 (granite4.1:8b)",
|
|
75
|
+
value: "granite4.1:8b"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
label: "Qwen 2.5 Coder (qwen2.5-coder:latest)",
|
|
79
|
+
value: "qwen2.5-coder:latest",
|
|
80
|
+
alias: "qwen2.5-coder:7b"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
label: "DeepSeek Coder V2 (deepseek-coder-v2:latest)",
|
|
84
|
+
value: "deepseek-coder-v2:latest",
|
|
85
|
+
alias: "deepseek-coder-v2:16b"
|
|
86
|
+
}
|
|
87
|
+
];
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/constants/option.ts
|
|
90
|
+
var BACK = {
|
|
91
|
+
label: "Back",
|
|
92
|
+
value: "back"
|
|
93
|
+
};
|
|
94
|
+
//#endregion
|
|
63
95
|
//#region src/constants/prompt.ts
|
|
64
96
|
var BASE_SYSTEM_PROMPT = `You are a coding assistant that helps users write, edit, and understand code. You have access to tools for reading files, writing files, running shell commands, searching code, and searching the web
|
|
65
97
|
|
|
@@ -259,6 +291,7 @@ var WEB_FETCH = "web_fetch";
|
|
|
259
291
|
//#endregion
|
|
260
292
|
//#region src/constants/ui.ts
|
|
261
293
|
var HEADER_PREFIX = "🦙 ";
|
|
294
|
+
var WARNING = "⚠️";
|
|
262
295
|
//#endregion
|
|
263
296
|
//#region src/utils/agents.ts
|
|
264
297
|
var AGENTS_FILE = "AGENTS.md";
|
|
@@ -358,6 +391,15 @@ async function listModels() {
|
|
|
358
391
|
const { models } = await client.list();
|
|
359
392
|
return models.map(({ name }) => name);
|
|
360
393
|
}
|
|
394
|
+
async function pullModel(model) {
|
|
395
|
+
return client.pull({
|
|
396
|
+
model,
|
|
397
|
+
stream: true
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
function deleteModel(model) {
|
|
401
|
+
return client.delete({ model });
|
|
402
|
+
}
|
|
361
403
|
//#endregion
|
|
362
404
|
//#region src/utils/screen.ts
|
|
363
405
|
var clearHandler = null;
|
|
@@ -536,9 +578,6 @@ function writeError(text) {
|
|
|
536
578
|
process.stderr.write(text);
|
|
537
579
|
}
|
|
538
580
|
//#endregion
|
|
539
|
-
//#region src/utils/time.ts
|
|
540
|
-
var tick = (ms = 0) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
541
|
-
//#endregion
|
|
542
581
|
//#region src/utils/tools/definitions.ts
|
|
543
582
|
/**
|
|
544
583
|
* Helper to define tool parameters
|
|
@@ -1085,7 +1124,7 @@ async function main(args = process.argv.slice(2)) {
|
|
|
1085
1124
|
else await launchTui();
|
|
1086
1125
|
}
|
|
1087
1126
|
async function launchTui(sessionId) {
|
|
1088
|
-
const { renderApp } = await import("./assets/tui-
|
|
1127
|
+
const { renderApp } = await import("./assets/tui-4cX-bKvd.js");
|
|
1089
1128
|
reset();
|
|
1090
1129
|
renderApp(sessionId);
|
|
1091
1130
|
}
|
|
@@ -1101,4 +1140,4 @@ function isEntrypoint(argv1 = process.argv[1]) {
|
|
|
1101
1140
|
if (isEntrypoint()) main();
|
|
1102
1141
|
// v8 ignore stop
|
|
1103
1142
|
//#endregion
|
|
1104
|
-
export {
|
|
1143
|
+
export { SYSTEM as A, REJECT as B, resetSystemMessage as C, LIST as D, WARNING as E, AUTO as F, LIST$1 as H, LABEL as I, PLAN as L, PLAN_GENERATION_INSTRUCTION as M, BACK as N, getTheme as O, CATALOG as P, SAFE as R, saveConfig as S, HEADER_PREFIX as T, VERSION as V, deleteModel as _, color as a, streamChat as b, createSession as c, listSessions as d, loadSession as f, setClearHandler as g, reset as h, WRITE_TOOLS as i, USER as j, ASSISTANT as k, deleteSession as l, clear as m, main, READ_TOOLS as n, write as o, updateSessionModel as p, TOOLS as r, appendMessage as s, executeTool as t, deleteSessionIfEmpty as u, listModels as v, withSystemMessage as w, loadConfig as x, pullModel as y, APPROVE as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-ollama",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Ollama coding agent that runs in your terminal",
|
|
5
5
|
"author": "Mark <mark@remarkablemark.org> (https://remarkablemark.org)",
|
|
6
6
|
"type": "module",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"lint-staged": "17.0.5",
|
|
66
66
|
"prettier": "3.8.3",
|
|
67
67
|
"publint": "0.3.21",
|
|
68
|
-
"tsx": "4.22.
|
|
68
|
+
"tsx": "4.22.1",
|
|
69
69
|
"typescript": "6.0.3",
|
|
70
70
|
"typescript-eslint": "8.59.3",
|
|
71
71
|
"vite": "8.0.13",
|