From 50b1f7db12fce10eeab7a83d25a9abe423f21809 Mon Sep 17 00:00:00 2001
From: Yidadaa <yidadaa@qq.com>
Date: Sat, 24 Jun 2023 22:32:46 +0800
Subject: [PATCH 1/4] feat: right-click to copy message to user input

---
 app/components/chat.tsx | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index 35f280466..d4e1dcd84 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -605,6 +605,10 @@ export function Chat() {
   const onRightClick = (e: any, message: ChatMessage) => {
     // copy to clipboard
     if (selectOrCopy(e.currentTarget, message.content)) {
+      if (userInput.length === 0) {
+        setUserInput(message.content);
+      }
+
       e.preventDefault();
     }
   };

From 5d06fa217cfde63439ef2c5b09cc71703e7d7f90 Mon Sep 17 00:00:00 2001
From: Yidadaa <yidadaa@qq.com>
Date: Sat, 24 Jun 2023 22:41:13 +0800
Subject: [PATCH 2/4] feat: close #2013 add switch model button to chat actions

---
 app/components/chat.tsx | 21 +++++++++++++++++++++
 app/icons/robot.svg     |  1 +
 2 files changed, 22 insertions(+)
 create mode 100644 app/icons/robot.svg

diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index d4e1dcd84..505af77da 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -27,6 +27,7 @@ import DarkIcon from "../icons/dark.svg";
 import AutoIcon from "../icons/auto.svg";
 import BottomIcon from "../icons/bottom.svg";
 import StopIcon from "../icons/pause.svg";
+import RobotIcon from "../icons/robot.svg";
 
 import {
   ChatMessage,
@@ -38,6 +39,7 @@ import {
   Theme,
   useAppConfig,
   DEFAULT_TOPIC,
+  ALL_MODELS,
 } from "../store";
 
 import {
@@ -385,6 +387,19 @@ export function ChatActions(props: {
   const couldStop = ChatControllerPool.hasPending();
   const stopAll = () => ChatControllerPool.stopAll();
 
+  // switch model
+  const currentModel = chatStore.currentSession().mask.modelConfig.model;
+  function nextModel() {
+    const models = ALL_MODELS.filter((m) => m.available).map((m) => m.name);
+    const modelIndex = models.indexOf(currentModel);
+    const nextIndex = (modelIndex + 1) % models.length;
+    const nextModel = models[nextIndex];
+    chatStore.updateCurrentSession((session) => {
+      session.mask.modelConfig.model = nextModel;
+      session.mask.syncGlobalConfig = false;
+    });
+  }
+
   return (
     <div className={chatStyle["chat-input-actions"]}>
       {couldStop && (
@@ -453,6 +468,12 @@ export function ChatActions(props: {
           });
         }}
       />
+
+      <ChatAction
+        onClick={nextModel}
+        text={currentModel}
+        icon={<RobotIcon />}
+      />
     </div>
   );
 }
diff --git a/app/icons/robot.svg b/app/icons/robot.svg
new file mode 100644
index 000000000..62dd9dc8d
--- /dev/null
+++ b/app/icons/robot.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16" viewBox="0 0 16 16" fill="none"><g opacity="1"  transform="translate(0 0)  rotate(0)"><mask id="bg-mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#bg-mask-0)" ><path  id="路径 1" style="fill:#333333; opacity:1;" d="M3.67,13.67h8.66c0.0022,0 0.0033,-0.00113 0.0033,-0.0034v-7.32997c0,-0.00091 -0.00033,-0.00169 -0.001,-0.00235c-0.0006,-0.00066 -0.00137,-0.00099 -0.0023,-0.00099h-8.66c-0.00222,0 -0.00333,0.00111 -0.00333,0.00334v7.32997c0,0.00227 0.00111,0.0034 0.00333,0.0034zM3.67,15.0033c-0.36909,0 -0.68414,-0.1305 -0.94515,-0.3915c-0.26101,-0.261 -0.39152,-0.57607 -0.39152,-0.9452v-7.32997c0,-0.3691 0.13051,-0.68415 0.39152,-0.94516c0.26101,-0.26101 0.57606,-0.39151 0.94515,-0.39151h8.66c0.36907,0 0.68413,0.1305 0.9452,0.39151c0.261,0.26101 0.3915,0.57606 0.3915,0.94516v7.32997c0,0.36913 -0.1305,0.6842 -0.3915,0.9452c-0.26107,0.261 -0.57613,0.3915 -0.9452,0.3915z"></path><path  id="路径 2" style="fill:#333333; opacity:1;" d="M9.81876,6.127c-0.03001,0.03187 -0.06284,0.0605 -0.09849,0.08591c-0.03565,0.02541 -0.07343,0.04709 -0.11335,0.06505c-0.03992,0.01796 -0.08121,0.03185 -0.12386,0.04168c-0.04265,0.00983 -0.08586,0.01541 -0.12962,0.01672c-0.04375,0.00132 -0.08721,-0.00164 -0.13038,-0.00888c-0.04317,-0.00725 -0.08522,-0.01863 -0.12615,-0.03416c-0.04093,-0.01553 -0.07995,-0.0349 -0.11706,-0.05811c-0.07495,-0.04688 -0.13808,-0.10632 -0.18939,-0.17831c-0.05131,-0.07199 -0.08688,-0.15106 -0.10673,-0.23721c-0.01985,-0.08615 -0.02246,-0.17282 -0.00783,-0.26c0.01463,-0.08719 0.04538,-0.16826 0.09226,-0.24321l1.67004,-2.67c0.0232,-0.03711 0.0498,-0.07161 0.0798,-0.10348c0.03,-0.03187 0.06283,-0.0605 0.0985,-0.08591c0.0356,-0.02541 0.07337,-0.04709 0.1133,-0.06505c0.03993,-0.01796 0.08123,-0.03185 0.1239,-0.04168c0.04267,-0.00983 0.08587,-0.01541 0.1296,-0.01672c0.04373,-0.00132 0.0872,0.00164 0.1304,0.00888c0.04313,0.00725 0.08517,0.01863 0.1261,0.03416c0.04093,0.01553 0.07997,0.0349 0.1171,0.05811c0.03713,0.02321 0.07163,0.04982 0.1035,0.07983c0.03187,0.03001 0.0605,0.06283 0.0859,0.09848c0.0254,0.03565 0.04707,0.07343 0.065,0.11335c0.018,0.03992 0.0319,0.08121 0.0417,0.12386c0.00987,0.04266 0.01543,0.08587 0.0167,0.12962c0.00133,0.04375 -0.00163,0.08721 -0.0089,0.13038c-0.0072,0.04317 -0.01857,0.08522 -0.0341,0.12615c-0.01553,0.04093 -0.0349,0.07995 -0.0581,0.11706l-1.67001,2.67c-0.02321,0.03711 -0.04982,0.07161 -0.07983,0.10348z"></path><path  id="路径 3" style="fill:#333333; opacity:1;" d="M7.29332,5.43354c0.01553,0.04093 0.02691,0.08297 0.03416,0.12614c0.00724,0.04317 0.0102,0.08664 0.00888,0.13039c-0.00131,0.04375 -0.00689,0.08696 -0.01672,0.12962c-0.00983,0.04265 -0.02372,0.08394 -0.04168,0.12386c-0.01796,0.03992 -0.03964,0.0777 -0.06505,0.11335c-0.02541,0.03565 -0.05404,0.06847 -0.08591,0.09848c-0.03187,0.03001 -0.06637,0.05662 -0.10348,0.07983c-0.03711,0.02321 -0.07613,0.04258 -0.11706,0.05811c-0.04093,0.01553 -0.08298,0.02691 -0.12615,0.03416c-0.04317,0.00724 -0.08663,0.0102 -0.13038,0.00888c-0.04375,-0.00131 -0.08696,-0.00689 -0.12962,-0.01672c-0.04265,-0.00983 -0.08394,-0.02372 -0.12386,-0.04168c-0.03992,-0.01796 -0.0777,-0.03964 -0.11335,-0.06505c-0.03565,-0.02541 -0.06847,-0.05404 -0.09848,-0.08591c-0.03001,-0.03187 -0.05662,-0.06637 -0.07983,-0.10348l-1.67,-2.67c-0.02321,-0.03711 -0.04258,-0.07613 -0.05811,-0.11706c-0.01553,-0.04093 -0.02691,-0.08298 -0.03416,-0.12615c-0.00724,-0.04317 -0.0102,-0.08663 -0.00888,-0.13038c0.00131,-0.04375 0.00689,-0.08696 0.01672,-0.12962c0.00983,-0.04265 0.02372,-0.08394 0.04168,-0.12386c0.01796,-0.03992 0.03964,-0.0777 0.06505,-0.11335c0.02541,-0.03565 0.05404,-0.06847 0.08591,-0.09848c0.03187,-0.03001 0.06637,-0.05662 0.10348,-0.07983c0.03711,-0.02321 0.07613,-0.04258 0.11706,-0.05811c0.04093,-0.01553 0.08297,-0.02691 0.12614,-0.03416c0.04317,-0.00724 0.08664,-0.0102 0.13039,-0.00888c0.04375,0.00131 0.08696,0.00689 0.12962,0.01672c0.04265,0.00983 0.08394,0.02372 0.12386,0.04168c0.03992,0.01796 0.0777,0.03964 0.11335,0.06505c0.03565,0.02541 0.06847,0.05404 0.09848,0.08591c0.03001,0.03187 0.05662,0.06637 0.07983,0.10348l1.67,2.67c0.02321,0.03711 0.04258,0.07613 0.05811,0.11706z"></path><path  id="路径 4" style="fill:#333333; opacity:1;" d="M11.3366,2.33329c-0.0022,0 -0.0033,0.00111 -0.0033,0.00334c0,-0.00493 -0.0005,-0.00788 -0.0015,-0.00884c0.00147,0.00145 0.00307,0.00217 0.0048,0.00217c-0.00407,0 -0.0062,0.00009 -0.0064,0.00026c-0.00013,0.00017 -0.0002,0.00231 -0.0002,0.00641c0,-0.00173 -0.00073,-0.00333 -0.0022,-0.0048c0.00093,0.00097 0.00387,0.00146 0.0088,0.00146zM11.3366,0.99996c0.366,0 0.67897,0.13132 0.9389,0.39396c0.25853,0.26125 0.3878,0.57548 0.3878,0.94271c0,0.36409 -0.1301,0.67623 -0.3903,0.9364c-0.26013,0.26017 -0.57227,0.39026 -0.9364,0.39026c-0.3672,0 -0.68143,-0.12927 -0.9427,-0.38782c-0.26263,-0.25993 -0.39394,-0.57288 -0.39394,-0.93884c0,-0.3691 0.13051,-0.68415 0.39154,-0.94516c0.261,-0.26101 0.57603,-0.39151 0.9451,-0.39151z"></path><path  id="路径 5" style="fill:#333333; opacity:1;" d="M4.67,2.33329c-0.00222,0 -0.00333,0.00111 -0.00333,0.00334c0,-0.00493 -0.00049,-0.00788 -0.00146,-0.00884c0.00146,0.00145 0.00306,0.00217 0.00479,0.00217c-0.00409,0 -0.00623,0.00009 -0.0064,0.00026c-0.00018,0.00017 -0.00027,0.00231 -0.00027,0.00641c0,-0.00173 -0.00072,-0.00333 -0.00217,-0.0048c0.00097,0.00097 0.00391,0.00146 0.00884,0.00146zM4.67,0.99996c0.36596,0 0.67891,0.13132 0.93884,0.39396c0.25855,0.26125 0.38783,0.57548 0.38783,0.94271c0,0.36409 -0.13009,0.67623 -0.39027,0.9364c-0.26017,0.26017 -0.57231,0.39026 -0.9364,0.39026c-0.36723,0 -0.68146,-0.12927 -0.94271,-0.38782c-0.26264,-0.25993 -0.39396,-0.57288 -0.39396,-0.93884c0,-0.3691 0.13051,-0.68415 0.39152,-0.94516c0.26101,-0.26101 0.57606,-0.39151 0.94515,-0.39151z"></path><path  id="路径 6" style="fill:#333333; opacity:1;" d="M6.66337,10.0033h2.67c0.18392,0 0.34014,-0.06492 0.46866,-0.19476c0.12979,-0.13113 0.19468,-0.29065 0.19468,-0.47854c0,-0.18091 -0.06533,-0.33669 -0.19599,-0.46735c-0.13066,-0.13065 -0.28644,-0.19598 -0.46735,-0.19598h-2.67c-0.1809,0 -0.33668,0.06533 -0.46734,0.19598c-0.13066,0.13066 -0.19599,0.28644 -0.19599,0.46735c0,0.18789 0.06489,0.34741 0.19467,0.47854c0.12853,0.12984 0.28475,0.19476 0.46866,0.19476zM6.66337,11.3367c-0.27228,0 -0.53262,-0.05287 -0.78101,-0.1586c-0.23965,-0.10193 -0.45143,-0.2458 -0.63533,-0.4316c-0.18317,-0.18513 -0.3249,-0.3979 -0.42518,-0.6383c-0.10343,-0.24791 -0.15514,-0.50731 -0.15514,-0.7782c0,-0.26987 0.05281,-0.52885 0.15843,-0.77694c0.1014,-0.23816 0.24409,-0.44923 0.42808,-0.63321c0.18398,-0.18399 0.39505,-0.32668 0.63322,-0.42808c0.24808,-0.10563 0.50706,-0.15844 0.77693,-0.15844h2.67c0.26987,0 0.52885,0.05281 0.77693,0.15844c0.2382,0.1014 0.44927,0.24409 0.6332,0.42808c0.184,0.18398 0.3267,0.39505 0.4281,0.63321c0.1056,0.24809 0.1584,0.50707 0.1584,0.77694c0,0.27089 -0.0517,0.53029 -0.1551,0.7782c-0.10027,0.2404 -0.242,0.45317 -0.4252,0.6383c-0.18387,0.1858 -0.39563,0.32967 -0.6353,0.4316c-0.2484,0.10573 -0.50874,0.1586 -0.78103,0.1586z"></path><path  id="路径 7" style="fill:#333333; opacity:1;" d="M2.00004,10.6633h1.00333c0.04377,0 0.08713,0.00427 0.13006,0.0128c0.04293,0.0086 0.08462,0.02127 0.12507,0.038c0.04044,0.01673 0.07886,0.03727 0.11525,0.0616c0.0364,0.02433 0.07008,0.05197 0.10103,0.0829c0.03095,0.03093 0.05859,0.0646 0.08291,0.101c0.02432,0.0364 0.04485,0.07483 0.0616,0.1153c0.01675,0.0404 0.0294,0.08207 0.03794,0.125c0.00854,0.04293 0.01281,0.0863 0.01281,0.1301c0,0.0438 -0.00427,0.08717 -0.01281,0.1301c-0.00854,0.04293 -0.02119,0.0846 -0.03794,0.125c-0.01675,0.04047 -0.03728,0.0789 -0.0616,0.1153c-0.02432,0.0364 -0.05196,0.07007 -0.08291,0.101c-0.03095,0.03093 -0.06463,0.05857 -0.10103,0.0829c-0.03639,0.02433 -0.07481,0.04487 -0.11525,0.0616c-0.04045,0.01673 -0.08214,0.0294 -0.12507,0.038c-0.04293,0.00853 -0.08629,0.0128 -0.13006,0.0128h-1.67c-0.04377,0 -0.08713,-0.00427 -0.13006,-0.0128c-0.04293,-0.0086 -0.08462,-0.02127 -0.12506,-0.038c-0.04044,-0.01673 -0.07886,-0.03727 -0.11526,-0.0616c-0.0364,-0.02433 -0.07007,-0.05197 -0.10103,-0.0829c-0.03095,-0.03093 -0.05859,-0.0646 -0.08291,-0.101c-0.02432,-0.0364 -0.04485,-0.07483 -0.06161,-0.1153c-0.01675,-0.0404 -0.0294,-0.08207 -0.03794,-0.125c-0.00854,-0.04293 -0.01281,-0.0863 -0.01281,-0.1301v-3.33c0,-0.04377 0.00427,-0.08713 0.01281,-0.13006c0.00854,-0.04293 0.02119,-0.08462 0.03794,-0.12506c0.01675,-0.04045 0.03729,-0.07887 0.06161,-0.11526c0.02432,-0.0364 0.05196,-0.07008 0.08291,-0.10103c0.03095,-0.03095 0.06463,-0.05858 0.10103,-0.0829c0.0364,-0.02432 0.07482,-0.04486 0.11526,-0.06161c0.04044,-0.01675 0.08213,-0.0294 0.12506,-0.03794c0.04293,-0.00854 0.08629,-0.01281 0.13006,-0.01281h1.67c0.04377,0 0.08713,0.00427 0.13006,0.01281c0.04293,0.00854 0.08462,0.02119 0.12507,0.03794c0.04044,0.01675 0.07886,0.03729 0.11525,0.06161c0.0364,0.02432 0.07008,0.05195 0.10103,0.0829c0.03095,0.03095 0.05859,0.06463 0.08291,0.10103c0.02432,0.03639 0.04485,0.07481 0.0616,0.11526c0.01675,0.04044 0.0294,0.08213 0.03794,0.12506c0.00854,0.04293 0.01281,0.08629 0.01281,0.13006c0,0.04377 -0.00427,0.08713 -0.01281,0.13006c-0.00854,0.04293 -0.02119,0.08462 -0.03794,0.12506c-0.01675,0.04044 -0.03728,0.07886 -0.0616,0.11526c-0.02432,0.03639 -0.05196,0.07007 -0.08291,0.10102c-0.03095,0.03095 -0.06463,0.05859 -0.10103,0.08291c-0.03639,0.02432 -0.07481,0.04486 -0.11525,0.06161c-0.04045,0.01675 -0.08214,0.0294 -0.12507,0.03794c-0.04293,0.00854 -0.08629,0.01281 -0.13006,0.01281h-1.00333z"></path><path  id="路径 8" style="fill:#333333; opacity:1;" d="M14.8001,7.34614c0.04293,0.00854 0.0846,0.02119 0.125,0.03794c0.04047,0.01675 0.0789,0.03729 0.1153,0.06161c0.0364,0.02432 0.07007,0.05195 0.101,0.0829c0.03093,0.03095 0.05857,0.06463 0.0829,0.10103c0.02433,0.03639 0.04487,0.07481 0.0616,0.11526c0.01673,0.04044 0.0294,0.08213 0.038,0.12506c0.00853,0.04293 0.0128,0.08629 0.0128,0.13006v3.33c0,0.0438 -0.00427,0.08717 -0.0128,0.1301c-0.0086,0.04293 -0.02127,0.0846 -0.038,0.125c-0.01673,0.04047 -0.03727,0.0789 -0.0616,0.1153c-0.02433,0.0364 -0.05197,0.07007 -0.0829,0.101c-0.03093,0.03093 -0.0646,0.05857 -0.101,0.0829c-0.0364,0.02433 -0.07483,0.04487 -0.1153,0.0616c-0.0404,0.01673 -0.08207,0.0294 -0.125,0.038c-0.04293,0.00853 -0.0863,0.0128 -0.1301,0.0128h-1.67c-0.0438,0 -0.08717,-0.00427 -0.1301,-0.0128c-0.04293,-0.0086 -0.0846,-0.02127 -0.125,-0.038c-0.04047,-0.01673 -0.0789,-0.03727 -0.1153,-0.0616c-0.0364,-0.02433 -0.07007,-0.05197 -0.101,-0.0829c-0.03093,-0.03093 -0.05857,-0.0646 -0.0829,-0.101c-0.02433,-0.0364 -0.04487,-0.07483 -0.0616,-0.1153c-0.01673,-0.0404 -0.0294,-0.08207 -0.038,-0.125c-0.00853,-0.04293 -0.0128,-0.0863 -0.0128,-0.1301c0,-0.0438 0.00427,-0.08717 0.0128,-0.1301c0.0086,-0.04293 0.02127,-0.0846 0.038,-0.125c0.01673,-0.04047 0.03727,-0.0789 0.0616,-0.1153c0.02433,-0.0364 0.05197,-0.07007 0.0829,-0.101c0.03093,-0.03093 0.0646,-0.05857 0.101,-0.0829c0.0364,-0.02433 0.07483,-0.04487 0.1153,-0.0616c0.0404,-0.01673 0.08207,-0.0294 0.125,-0.038c0.04293,-0.00853 0.0863,-0.0128 0.1301,-0.0128h1.0033v-1.99663h-1.0033c-0.0438,0 -0.08717,-0.00427 -0.1301,-0.01281c-0.04293,-0.00854 -0.0846,-0.02119 -0.125,-0.03794c-0.04047,-0.01675 -0.0789,-0.03729 -0.1153,-0.06161c-0.0364,-0.02432 -0.07007,-0.05196 -0.101,-0.08291c-0.03093,-0.03095 -0.05857,-0.06463 -0.0829,-0.10102c-0.02433,-0.0364 -0.04487,-0.07482 -0.0616,-0.11526c-0.01673,-0.04044 -0.0294,-0.08213 -0.038,-0.12506c-0.00853,-0.04293 -0.0128,-0.08629 -0.0128,-0.13006c0,-0.04377 0.00427,-0.08713 0.0128,-0.13006c0.0086,-0.04293 0.02127,-0.08462 0.038,-0.12506c0.01673,-0.04045 0.03727,-0.07887 0.0616,-0.11526c0.02433,-0.0364 0.05197,-0.07008 0.0829,-0.10103c0.03093,-0.03095 0.0646,-0.05858 0.101,-0.0829c0.0364,-0.02432 0.07483,-0.04486 0.1153,-0.06161c0.0404,-0.01675 0.08207,-0.0294 0.125,-0.03794c0.04293,-0.00854 0.0863,-0.01281 0.1301,-0.01281h1.67c0.0438,0 0.08717,0.00427 0.1301,0.01281z"></path></g></g><defs><rect id="path_0" x="0" y="0" width="16" height="16" /></defs></svg>

From ae1ef3215b45ae373044f0cba370307279d1ff7a Mon Sep 17 00:00:00 2001
From: Yidadaa <yidadaa@qq.com>
Date: Sat, 24 Jun 2023 23:38:11 +0800
Subject: [PATCH 3/4] feat: close #628 add chat commands

---
 app/command.ts             | 43 ++++++++++++++++++++++++++++++++++++++
 app/components/chat.tsx    | 38 ++++++++++++++++++++++++++-------
 app/components/sidebar.tsx |  7 ++-----
 app/locales/cn.ts          | 10 ++++++++-
 app/locales/en.ts          | 10 ++++++++-
 app/store/chat.ts          |  8 +++++++
 6 files changed, 102 insertions(+), 14 deletions(-)

diff --git a/app/command.ts b/app/command.ts
index 40bad92b3..ba3bb6538 100644
--- a/app/command.ts
+++ b/app/command.ts
@@ -1,4 +1,5 @@
 import { useSearchParams } from "react-router-dom";
+import Locale from "./locales";
 
 type Command = (param: string) => void;
 interface Commands {
@@ -26,3 +27,45 @@ export function useCommand(commands: Commands = {}) {
     setSearchParams(searchParams);
   }
 }
+
+interface ChatCommands {
+  new?: Command;
+  newm?: Command;
+  next?: Command;
+  prev?: Command;
+  clear?: Command;
+  del?: Command;
+}
+
+export const ChatCommandPrefix = ":";
+
+export function useChatCommand(commands: ChatCommands = {}) {
+  function extract(userInput: string) {
+    return (
+      userInput.startsWith(ChatCommandPrefix) ? userInput.slice(1) : userInput
+    ) as keyof ChatCommands;
+  }
+
+  function search(userInput: string) {
+    const input = extract(userInput);
+    const desc = Locale.Chat.Commands;
+    return Object.keys(commands)
+      .filter((c) => c.startsWith(input))
+      .map((c) => ({
+        title: desc[c as keyof ChatCommands],
+        content: ChatCommandPrefix + c,
+      }));
+  }
+
+  function match(userInput: string) {
+    const command = extract(userInput);
+    const matched = typeof commands[command] === "function";
+
+    return {
+      matched,
+      invoke: () => matched && commands[command]!(userInput),
+    };
+  }
+
+  return { match, search };
+}
diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index 505af77da..e1011e422 100644
--- a/app/components/chat.tsx
+++ b/app/components/chat.tsx
@@ -66,7 +66,7 @@ import { LAST_INPUT_KEY, Path, REQUEST_TIMEOUT_MS } from "../constant";
 import { Avatar } from "./emoji";
 import { MaskAvatar, MaskConfig } from "./mask";
 import { useMaskStore } from "../store/mask";
-import { useCommand } from "../command";
+import { ChatCommandPrefix, useChatCommand, useCommand } from "../command";
 import { prettyObject } from "../utils/format";
 import { ExportMessageModal } from "./exporter";
 import { getClientConfig } from "../config/client";
@@ -208,8 +208,7 @@ export function PromptHints(props: {
 
   useEffect(() => {
     const onKeyDown = (e: KeyboardEvent) => {
-      if (noPrompts) return;
-      if (e.metaKey || e.altKey || e.ctrlKey) {
+      if (noPrompts || e.metaKey || e.altKey || e.ctrlKey) {
         return;
       }
       // arrow up / down to select prompt
@@ -510,16 +509,19 @@ export function Chat() {
   const [promptHints, setPromptHints] = useState<Prompt[]>([]);
   const onSearch = useDebouncedCallback(
     (text: string) => {
-      setPromptHints(promptStore.search(text));
+      const matchedPrompts = promptStore.search(text);
+      setPromptHints(matchedPrompts);
     },
     100,
     { leading: true, trailing: true },
   );
 
   const onPromptSelect = (prompt: Prompt) => {
-    setPromptHints([]);
-    inputRef.current?.focus();
-    setTimeout(() => setUserInput(prompt.content), 60);
+    setTimeout(() => {
+      setPromptHints([]);
+      setUserInput(prompt.content);
+      inputRef.current?.focus();
+    }, 30);
   };
 
   // auto grow input
@@ -543,6 +545,19 @@ export function Chat() {
   // eslint-disable-next-line react-hooks/exhaustive-deps
   useEffect(measure, [userInput]);
 
+  // chat commands shortcuts
+  const chatCommands = useChatCommand({
+    new: () => chatStore.newSession(),
+    newm: () => navigate(Path.NewChat),
+    prev: () => chatStore.nextSession(-1),
+    next: () => chatStore.nextSession(1),
+    clear: () =>
+      chatStore.updateCurrentSession(
+        (session) => (session.clearContextIndex = session.messages.length),
+      ),
+    del: () => chatStore.deleteSession(chatStore.currentSessionIndex),
+  });
+
   // only search prompts when user input is short
   const SEARCH_TEXT_LIMIT = 30;
   const onInput = (text: string) => {
@@ -552,6 +567,8 @@ export function Chat() {
     // clear search results
     if (n === 0) {
       setPromptHints([]);
+    } else if (text.startsWith(ChatCommandPrefix)) {
+      setPromptHints(chatCommands.search(text));
     } else if (!config.disablePromptHint && n < SEARCH_TEXT_LIMIT) {
       // check if need to trigger auto completion
       if (text.startsWith("/")) {
@@ -563,6 +580,13 @@ export function Chat() {
 
   const doSubmit = (userInput: string) => {
     if (userInput.trim() === "") return;
+    const matchCommand = chatCommands.match(userInput);
+    if (matchCommand.matched) {
+      setUserInput("");
+      setPromptHints([]);
+      matchCommand.invoke();
+      return;
+    }
     setIsLoading(true);
     chatStore.onUserInput(userInput).then(() => setIsLoading(false));
     localStorage.setItem(LAST_INPUT_KEY, userInput);
diff --git a/app/components/sidebar.tsx b/app/components/sidebar.tsx
index a43daedef..6392b962a 100644
--- a/app/components/sidebar.tsx
+++ b/app/components/sidebar.tsx
@@ -38,13 +38,10 @@ function useHotKey() {
   useEffect(() => {
     const onKeyDown = (e: KeyboardEvent) => {
       if (e.metaKey || e.altKey || e.ctrlKey) {
-        const n = chatStore.sessions.length;
-        const limit = (x: number) => (x + n) % n;
-        const i = chatStore.currentSessionIndex;
         if (e.key === "ArrowUp") {
-          chatStore.selectSession(limit(i - 1));
+          chatStore.nextSession(-1);
         } else if (e.key === "ArrowDown") {
-          chatStore.selectSession(limit(i + 1));
+          chatStore.nextSession(1);
         }
       }
     };
diff --git a/app/locales/cn.ts b/app/locales/cn.ts
index e94c0332e..14ee7ec99 100644
--- a/app/locales/cn.ts
+++ b/app/locales/cn.ts
@@ -27,6 +27,14 @@ const cn = {
       Retry: "重试",
       Delete: "删除",
     },
+    Commands: {
+      new: "新建聊天",
+      newm: "从面具新建聊天",
+      next: "下一个聊天",
+      prev: "上一个聊天",
+      clear: "清除上下文",
+      del: "删除聊天",
+    },
     InputActions: {
       Stop: "停止响应",
       ToBottom: "滚到最新",
@@ -47,7 +55,7 @@ const cn = {
       if (submitKey === String(SubmitKey.Enter)) {
         inputHints += ",Shift + Enter 换行";
       }
-      return inputHints + ",/ 触发补全";
+      return inputHints + ",/ 触发补全,: 触发命令";
     },
     Send: "发送",
     Config: {
diff --git a/app/locales/en.ts b/app/locales/en.ts
index 8e56147c8..1659ddb1f 100644
--- a/app/locales/en.ts
+++ b/app/locales/en.ts
@@ -28,6 +28,14 @@ const en: LocaleType = {
       Retry: "Retry",
       Delete: "Delete",
     },
+    Commands: {
+      new: "Start a new chat",
+      newm: "Start a new chat with mask",
+      next: "Next Chat",
+      prev: "Previous Chat",
+      clear: "Clear Context",
+      del: "Delete Chat",
+    },
     InputActions: {
       Stop: "Stop",
       ToBottom: "To Latest",
@@ -48,7 +56,7 @@ const en: LocaleType = {
       if (submitKey === String(SubmitKey.Enter)) {
         inputHints += ", Shift + Enter to wrap";
       }
-      return inputHints + ", / to search prompts";
+      return inputHints + ", / to search prompts, : to use commands";
     },
     Send: "Send",
     Config: {
diff --git a/app/store/chat.ts b/app/store/chat.ts
index d42031005..fa6296811 100644
--- a/app/store/chat.ts
+++ b/app/store/chat.ts
@@ -85,6 +85,7 @@ interface ChatStore {
   newSession: (mask?: Mask) => void;
   deleteSession: (index: number) => void;
   currentSession: () => ChatSession;
+  nextSession: (delta: number) => void;
   onNewMessage: (message: ChatMessage) => void;
   onUserInput: (content: string) => Promise<void>;
   summarizeSession: () => void;
@@ -200,6 +201,13 @@ export const useChatStore = create<ChatStore>()(
         }));
       },
 
+      nextSession(delta) {
+        const n = get().sessions.length;
+        const limit = (x: number) => (x + n) % n;
+        const i = get().currentSessionIndex;
+        get().selectSession(limit(i + delta));
+      },
+
       deleteSession(index) {
         const deletingLastSession = get().sessions.length === 1;
         const deletedSession = get().sessions.at(index);

From 8915af9b6d7150f128e9fb99052300e20f9af311 Mon Sep 17 00:00:00 2001
From: Yidadaa <yidadaa@qq.com>
Date: Sat, 24 Jun 2023 23:39:38 +0800
Subject: [PATCH 4/4] fixup

---
 app/locales/ar.ts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/app/locales/ar.ts b/app/locales/ar.ts
index 70bfb0ceb..7a3eaa2b2 100644
--- a/app/locales/ar.ts
+++ b/app/locales/ar.ts
@@ -1,7 +1,7 @@
 import { SubmitKey } from "../store/config";
-import { LocaleType } from "./index";
+import type { PartialLocaleType } from "./index";
 
-const ar: LocaleType = {
+const ar: PartialLocaleType = {
   WIP: "قريبًا...",
   Error: {
     Unauthorized: