lovecode-ai 0.1.2 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +168 -103
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6406,12 +6406,12 @@ import { Command as Command13 } from "commander";
6406
6406
  import chalk37 from "chalk";
6407
6407
 
6408
6408
  // src/tui/index.ts
6409
- import React3 from "react";
6409
+ import React4 from "react";
6410
6410
  import { render } from "ink";
6411
6411
 
6412
6412
  // src/tui/App.tsx
6413
- import { useState as useState2, useCallback as useCallback2 } from "react";
6414
- import { Box as Box2 } from "ink";
6413
+ import { useState as useState3, useCallback as useCallback2 } from "react";
6414
+ import { Box as Box3 } from "ink";
6415
6415
 
6416
6416
  // src/tui/components.tsx
6417
6417
  import React, { useEffect as useEffect2 } from "react";
@@ -6571,19 +6571,6 @@ function Pane({ title, children, focused = false, borderColor, height }) {
6571
6571
  }
6572
6572
  );
6573
6573
  }
6574
- function ChatMessage({ role, content, streaming, streamedContent }) {
6575
- const theme = getTheme();
6576
- const displayContent = streaming && streamedContent !== void 0 ? streamedContent : content;
6577
- const label = role === "user" ? "You" : role === "assistant" ? "LoveCode" : "System";
6578
- const labelColor = role === "user" ? theme.colors.success : role === "assistant" ? theme.colors.primary : theme.colors.warning;
6579
- return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
6580
- /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { bold: true, color: labelColor, children: label }) }),
6581
- /* @__PURE__ */ jsxs(Box, { children: [
6582
- /* @__PURE__ */ jsx(Text, { color: theme.colors.text, children: displayContent }),
6583
- streaming && /* @__PURE__ */ jsx(Text, { color: theme.colors.primary, children: "\u2588" })
6584
- ] })
6585
- ] });
6586
- }
6587
6574
  function RepoContextPane({ projectName, branch, fileCount, language, framework, status, focused }) {
6588
6575
  const theme = getTheme();
6589
6576
  return /* @__PURE__ */ jsx(Pane, { title: "Repo Context", focused, height: 7, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "row", gap: 2, children: [
@@ -6623,25 +6610,6 @@ function RepoContextPane({ projectName, branch, fileCount, language, framework,
6623
6610
  ] })
6624
6611
  ] }) });
6625
6612
  }
6626
- function ChatPane({ messages, streaming, streamedIndex, streamedContent, focused }) {
6627
- const scroll = useScroll();
6628
- useEffect2(() => {
6629
- scroll.scrollToBottom();
6630
- }, [messages.length]);
6631
- return /* @__PURE__ */ jsx(Pane, { title: "Chat", focused, height: "100%", children: /* @__PURE__ */ jsxs(Box, { flexDirection: "column", overflowY: "hidden", overflowX: "hidden", children: [
6632
- messages.length === 0 && /* @__PURE__ */ jsx(Text, { color: getTheme().colors.textDim, children: "Start typing to chat with LoveCode AI" }),
6633
- messages.map((msg, i) => /* @__PURE__ */ jsx(
6634
- ChatMessage,
6635
- {
6636
- role: msg.role,
6637
- content: streaming && i === streamedIndex && streamedContent !== void 0 ? streamedContent : msg.content,
6638
- streaming: streaming && i === streamedIndex,
6639
- streamedContent: streaming && i === streamedIndex ? streamedContent : void 0
6640
- },
6641
- i
6642
- ))
6643
- ] }) });
6644
- }
6645
6613
  function CommandPane({ commands, focused }) {
6646
6614
  const spinner = useSpinner();
6647
6615
  const theme = getTheme();
@@ -6664,16 +6632,6 @@ function CommandPane({ commands, focused }) {
6664
6632
  ] }, i))
6665
6633
  ] }) });
6666
6634
  }
6667
- function InputPane({ value, placeholder, focused, mode, vimMode }) {
6668
- const theme = getTheme();
6669
- const cursor = vimMode ? mode === "normal" ? "\u258C" : "\u2588" : "\u2588";
6670
- const modeIndicator = vimMode ? mode === "normal" ? " NORMAL " : " INSERT " : "";
6671
- return /* @__PURE__ */ jsx(Pane, { title: `Input${modeIndicator}`, focused, height: 4, children: /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
6672
- /* @__PURE__ */ jsx(Text, { bold: true, color: theme.colors.success, children: " > " }),
6673
- /* @__PURE__ */ jsx(Text, { color: theme.colors.text, children: value || (placeholder ? /* @__PURE__ */ jsx(Text, { color: theme.colors.textDim, children: placeholder }) : null) }),
6674
- focused && /* @__PURE__ */ jsx(Text, { color: theme.colors.primary, children: cursor })
6675
- ] }) });
6676
- }
6677
6635
  function StatusBar({ mode, theme: themeName, focus, vimMode, messages }) {
6678
6636
  const theme = getTheme();
6679
6637
  return /* @__PURE__ */ jsxs(
@@ -6710,8 +6668,153 @@ function StatusBar({ mode, theme: themeName, focus, vimMode, messages }) {
6710
6668
  );
6711
6669
  }
6712
6670
 
6713
- // src/tui/App.tsx
6671
+ // src/tui/ChatBox.tsx
6672
+ import { useState as useState2, useEffect as useEffect3, useRef as useRef2 } from "react";
6673
+ import { Box as Box2, Text as Text2, useInput as useInput2 } from "ink";
6714
6674
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
6675
+ function MessageRow({ msg, isStreaming, streamContent }) {
6676
+ const theme = getTheme();
6677
+ const displayContent = isStreaming && streamContent !== void 0 ? streamContent : msg.content;
6678
+ const label = msg.role === "user" ? "You" : msg.role === "assistant" ? "LoveCode" : "System";
6679
+ const labelColor = msg.role === "user" ? theme.colors.success : msg.role === "assistant" ? theme.colors.primary : theme.colors.warning;
6680
+ const formatted = msg.role === "assistant" || msg.role === "system" ? renderMarkdown(displayContent) : displayContent;
6681
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", marginBottom: 1, children: [
6682
+ /* @__PURE__ */ jsx2(Box2, { children: /* @__PURE__ */ jsx2(Text2, { bold: true, color: labelColor, children: label }) }),
6683
+ /* @__PURE__ */ jsxs2(Box2, { children: [
6684
+ /* @__PURE__ */ jsx2(Text2, { color: theme.colors.text, children: formatted }),
6685
+ isStreaming && /* @__PURE__ */ jsx2(Text2, { color: theme.colors.primary, children: "\u2588" })
6686
+ ] })
6687
+ ] });
6688
+ }
6689
+ function ChatBox({ messages, onSend, streaming, streamedContent, focused, placeholder = "Type a message..." }) {
6690
+ const [input, setInput] = useState2("");
6691
+ const [vimMode, setVimMode] = useState2("insert");
6692
+ const scroll = useScroll();
6693
+ const inputRef = useRef2(input);
6694
+ inputRef.current = input;
6695
+ useEffect3(() => {
6696
+ scroll.scrollToBottom();
6697
+ }, [messages.length, streamedContent]);
6698
+ useInput2((text, key) => {
6699
+ if (!focused) return;
6700
+ if (vimMode === "normal") {
6701
+ if (text === "i") {
6702
+ setVimMode("insert");
6703
+ return;
6704
+ }
6705
+ if (text === "a") {
6706
+ setVimMode("insert");
6707
+ setInput((prev) => prev + " ");
6708
+ return;
6709
+ }
6710
+ if (text === "j") {
6711
+ scroll.scrollDown();
6712
+ return;
6713
+ }
6714
+ if (text === "k") {
6715
+ scroll.scrollUp();
6716
+ return;
6717
+ }
6718
+ if (text === "g") {
6719
+ scroll.scrollToTop();
6720
+ return;
6721
+ }
6722
+ if (text === "G") {
6723
+ scroll.scrollToBottom();
6724
+ return;
6725
+ }
6726
+ return;
6727
+ }
6728
+ if (key.escape) {
6729
+ setVimMode("normal");
6730
+ return;
6731
+ }
6732
+ if (key.return && input.trim()) {
6733
+ onSend(input);
6734
+ setInput("");
6735
+ return;
6736
+ }
6737
+ if (key.backspace || key.delete) {
6738
+ setInput((prev) => prev.slice(0, -1));
6739
+ return;
6740
+ }
6741
+ if (key.upArrow) {
6742
+ scroll.scrollUp();
6743
+ return;
6744
+ }
6745
+ if (key.downArrow) {
6746
+ scroll.scrollDown();
6747
+ return;
6748
+ }
6749
+ if (key.pageUp) {
6750
+ for (let i = 0; i < 10; i++) scroll.scrollUp();
6751
+ return;
6752
+ }
6753
+ if (key.pageDown) {
6754
+ for (let i = 0; i < 10; i++) scroll.scrollDown();
6755
+ return;
6756
+ }
6757
+ if (text && text.length === 1 && !key.ctrl && !key.meta) {
6758
+ setInput((prev) => prev + text);
6759
+ }
6760
+ });
6761
+ const theme = getTheme();
6762
+ const cursor = vimMode === "normal" ? "\u258C" : "\u2588";
6763
+ const modeIndicator = vimMode === "normal" ? " NORMAL " : " INSERT ";
6764
+ const maxVisible = 10;
6765
+ return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", height: "100%", width: "100%", children: [
6766
+ /* @__PURE__ */ jsxs2(
6767
+ Box2,
6768
+ {
6769
+ flexDirection: "column",
6770
+ flexGrow: 1,
6771
+ overflowY: "hidden",
6772
+ overflowX: "hidden",
6773
+ paddingX: 1,
6774
+ paddingY: 0,
6775
+ children: [
6776
+ messages.length === 0 && /* @__PURE__ */ jsx2(Text2, { color: theme.colors.textDim, children: "Start typing to chat with LoveCode AI" }),
6777
+ messages.map((msg, i) => /* @__PURE__ */ jsx2(
6778
+ MessageRow,
6779
+ {
6780
+ msg,
6781
+ isStreaming: streaming && i === messages.length - 1,
6782
+ streamContent: streaming && i === messages.length - 1 ? streamedContent : void 0
6783
+ },
6784
+ i
6785
+ )),
6786
+ messages.length > maxVisible && /* @__PURE__ */ jsxs2(Text2, { color: theme.colors.textDim, children: [
6787
+ "-- Press j/k to scroll (",
6788
+ scroll.offset + 1,
6789
+ "-",
6790
+ Math.min(scroll.offset + maxVisible, messages.length),
6791
+ " of ",
6792
+ messages.length,
6793
+ ") --"
6794
+ ] })
6795
+ ]
6796
+ }
6797
+ ),
6798
+ /* @__PURE__ */ jsxs2(
6799
+ Box2,
6800
+ {
6801
+ borderStyle: "round",
6802
+ borderColor: focused ? theme.colors.primary : theme.colors.border,
6803
+ marginX: 0,
6804
+ paddingX: 1,
6805
+ children: [
6806
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.colors.warning, children: modeIndicator }),
6807
+ /* @__PURE__ */ jsx2(Text2, { bold: true, color: theme.colors.success, children: " > " }),
6808
+ /* @__PURE__ */ jsx2(Text2, { color: theme.colors.text, children: input || /* @__PURE__ */ jsx2(Text2, { color: theme.colors.textDim, children: placeholder }) }),
6809
+ focused && vimMode === "insert" && /* @__PURE__ */ jsx2(Text2, { color: theme.colors.primary, children: cursor })
6810
+ ]
6811
+ }
6812
+ )
6813
+ ] });
6814
+ }
6815
+
6816
+ // src/tui/App.tsx
6817
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
6715
6818
  function App({
6716
6819
  initialMessages = [],
6717
6820
  projectName: _pn,
@@ -6723,57 +6826,31 @@ function App({
6723
6826
  onSendMessage,
6724
6827
  onRunCommand
6725
6828
  }) {
6726
- const [messages, setMessages] = useState2(initialMessages);
6727
- const [commands, setCommands] = useState2([]);
6728
- const [input, setInput] = useState2("");
6729
- const [streaming, setStreaming] = useState2(false);
6730
- const [streamedContent, setStreamedContent] = useState2("");
6731
- const [streamedIndex, setStreamedIndex] = useState2(0);
6732
- const [vimMode, setVimMode] = useState2("insert");
6829
+ const [messages, setMessages] = useState3(initialMessages);
6830
+ const [commands, setCommands] = useState3([]);
6831
+ const [streaming, setStreaming] = useState3(false);
6832
+ const [streamedContent, setStreamedContent] = useState3("");
6733
6833
  const [focus, setFocus] = useFocus();
6734
- const [currentTheme, setCurrentThemeState] = useState2("default");
6834
+ const [currentTheme, setCurrentThemeState] = useState3("default");
6735
6835
  useTerminalSize();
6736
6836
  const setCurrentTheme = useCallback2((name) => {
6737
6837
  setTheme(name);
6738
6838
  setCurrentThemeState(name);
6739
6839
  }, []);
6740
6840
  useKeyboard({
6741
- onKey: (key) => {
6742
- if (focus === "input") {
6743
- if (vimMode === "normal") {
6744
- if (key === "i") setVimMode("insert");
6745
- else if (key === "h") {
6746
- setFocus("chat");
6747
- } else if (key === "l") {
6748
- setFocus("command");
6749
- }
6750
- }
6751
- }
6752
- },
6753
- onEnter: () => {
6754
- if (focus === "input" && input.trim() && vimMode === "insert") {
6755
- handleSend(input);
6756
- }
6757
- },
6758
- onEscape: () => {
6759
- if (vimMode === "insert") setVimMode("normal");
6760
- },
6761
6841
  onTab: () => {
6762
- setFocus(focus === "input" ? "chat" : focus === "chat" ? "command" : focus === "command" ? "repo" : "input");
6842
+ setFocus(focus === "chat" ? "command" : focus === "command" ? "repo" : focus === "repo" ? "chat" : "chat");
6763
6843
  }
6764
6844
  });
6765
6845
  async function handleSend(text) {
6766
6846
  const userMsg = { role: "user", content: text };
6767
6847
  setMessages((prev) => [...prev, userMsg]);
6768
- setInput("");
6769
6848
  if (text.startsWith("/")) {
6770
6849
  handleCommand(text);
6771
6850
  return;
6772
6851
  }
6773
6852
  if (onSendMessage) {
6774
6853
  setStreaming(true);
6775
- const idx = messages.length + 1;
6776
- setStreamedIndex(idx);
6777
6854
  setStreamedContent("");
6778
6855
  try {
6779
6856
  const response = await onSendMessage(text);
@@ -6802,8 +6879,7 @@ function App({
6802
6879
  setMessages((prev) => [...prev, { role: "system", content: `Unknown theme: ${name}. Available: ${names.join(", ")}` }]);
6803
6880
  }
6804
6881
  } else if (cmd === "vim") {
6805
- setVimMode(vimMode === "normal" ? "insert" : "normal");
6806
- setMessages((prev) => [...prev, { role: "system", content: `Vim mode: ${vimMode === "normal" ? "insert" : "normal"}` }]);
6882
+ setMessages((prev) => [...prev, { role: "system", content: "Vim mode is built into the chat box (Esc = normal, i = insert)" }]);
6807
6883
  } else if (cmd === "exit" || cmd === "quit") {
6808
6884
  process.exit(0);
6809
6885
  } else if (cmd.startsWith("!")) {
@@ -6834,9 +6910,9 @@ function App({
6834
6910
  }
6835
6911
  }
6836
6912
  }
6837
- return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width: "100%", height: "100%", children: [
6838
- /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", flexGrow: 1, paddingX: 0, children: /* @__PURE__ */ jsxs2(SplitPane, { direction: "vertical", sizes: [15, 55, 20, 10], children: [
6839
- /* @__PURE__ */ jsx2(
6913
+ return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", width: "100%", height: "100%", children: [
6914
+ /* @__PURE__ */ jsx3(Box3, { flexDirection: "column", flexGrow: 1, paddingX: 0, children: /* @__PURE__ */ jsxs3(SplitPane, { direction: "vertical", sizes: [15, 60, 25], children: [
6915
+ /* @__PURE__ */ jsx3(
6840
6916
  RepoContextPane,
6841
6917
  {
6842
6918
  projectName: _pn || "LoveCode",
@@ -6848,43 +6924,32 @@ function App({
6848
6924
  focused: focus === "repo"
6849
6925
  }
6850
6926
  ),
6851
- /* @__PURE__ */ jsx2(
6852
- ChatPane,
6927
+ /* @__PURE__ */ jsx3(
6928
+ ChatBox,
6853
6929
  {
6854
6930
  messages,
6931
+ onSend: handleSend,
6855
6932
  streaming,
6856
- streamedIndex,
6857
6933
  streamedContent,
6858
- focused: focus === "chat"
6934
+ focused: focus === "chat",
6935
+ placeholder: "Type a message or /help..."
6859
6936
  }
6860
6937
  ),
6861
- /* @__PURE__ */ jsx2(
6938
+ /* @__PURE__ */ jsx3(
6862
6939
  CommandPane,
6863
6940
  {
6864
6941
  commands,
6865
6942
  focused: focus === "command"
6866
6943
  }
6867
- ),
6868
- /* @__PURE__ */ jsx2(
6869
- InputPane,
6870
- {
6871
- value: input,
6872
- onChange: setInput,
6873
- onSubmit: handleSend,
6874
- placeholder: "Type a message or /help...",
6875
- focused: focus === "input",
6876
- mode: vimMode,
6877
- vimMode: true
6878
- }
6879
6944
  )
6880
6945
  ] }) }),
6881
- /* @__PURE__ */ jsx2(
6946
+ /* @__PURE__ */ jsx3(
6882
6947
  StatusBar,
6883
6948
  {
6884
6949
  mode: "chat",
6885
6950
  theme: currentTheme,
6886
6951
  focus,
6887
- vimMode: vimMode === "normal" ? "NORMAL" : "INSERT",
6952
+ vimMode: "INSERT",
6888
6953
  messages: messages.length
6889
6954
  }
6890
6955
  )
@@ -6893,7 +6958,7 @@ function App({
6893
6958
 
6894
6959
  // src/tui/index.ts
6895
6960
  function startTUI(props) {
6896
- const { waitUntilExit } = render(React3.createElement(App, props));
6961
+ const { waitUntilExit } = render(React4.createElement(App, props));
6897
6962
  waitUntilExit().then(() => process.exit(0));
6898
6963
  }
6899
6964
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lovecode-ai",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Terminal-native autonomous coding agent powered by free AI models",
5
5
  "keywords": [
6
6
  "ai",