@xinghunm/ai-chat 1.2.2 → 1.3.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/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/components/ai-chat/index.tsx
2
+ import { useEffect as useEffect9 } from "react";
2
3
  import styled17 from "@emotion/styled";
3
4
  import { ConfigProvider } from "@xinghunm/compass-ui";
4
5
 
@@ -82,6 +83,9 @@ var resolveSessionTitleFromMessage = (message) => {
82
83
  if (trimmedContent) {
83
84
  return trimmedContent.slice(0, 30);
84
85
  }
86
+ if (message.skills?.length) {
87
+ return message.skills.join(", ").slice(0, 30);
88
+ }
85
89
  if ((message.attachments?.length ?? 0) > 0) {
86
90
  return IMAGE_MESSAGE_SESSION_TITLE;
87
91
  }
@@ -627,7 +631,7 @@ var createDefaultRequestBody = async ({
627
631
  attachments
628
632
  }) => {
629
633
  const hasAttachments = Boolean(attachments?.length);
630
- const skillPayload = skills?.length ? { skill: skills.length === 1 ? skills[0] : skills } : {};
634
+ const skillPayload = skills?.length ? { skills } : {};
631
635
  if (!hasAttachments) {
632
636
  return {
633
637
  model,
@@ -2823,7 +2827,7 @@ var areMessageBlocksEqual = (previousBlocks, nextBlocks) => {
2823
2827
  }
2824
2828
  });
2825
2829
  };
2826
- var isSameMessage = (previousMessage, nextMessage, previousMode, nextMode, previousConfirmationSubmit, nextConfirmationSubmit, previousQuestionnaireSubmit, nextQuestionnaireSubmit, previousRenderMessageBlock, nextRenderMessageBlock) => previousMessage.id === nextMessage.id && previousMessage.sessionId === nextMessage.sessionId && previousMessage.role === nextMessage.role && previousMessage.content === nextMessage.content && areMessageBlocksEqual(previousMessage.blocks, nextMessage.blocks) && previousMessage.localOnly === nextMessage.localOnly && areChatAttachmentsEqual(previousMessage.attachments, nextMessage.attachments) && previousMessage.status === nextMessage.status && previousMessage.createdAt === nextMessage.createdAt && previousMode === nextMode && previousConfirmationSubmit === nextConfirmationSubmit && previousQuestionnaireSubmit === nextQuestionnaireSubmit && previousRenderMessageBlock === nextRenderMessageBlock;
2830
+ var isSameMessage = (previousMessage, nextMessage, previousMode, nextMode, previousConfirmationSubmit, nextConfirmationSubmit, previousQuestionnaireSubmit, nextQuestionnaireSubmit, previousRenderMessageBlock, nextRenderMessageBlock) => previousMessage.id === nextMessage.id && previousMessage.sessionId === nextMessage.sessionId && previousMessage.role === nextMessage.role && previousMessage.content === nextMessage.content && areStringArraysEqual(previousMessage.skills ?? [], nextMessage.skills ?? []) && areMessageBlocksEqual(previousMessage.blocks, nextMessage.blocks) && previousMessage.localOnly === nextMessage.localOnly && areChatAttachmentsEqual(previousMessage.attachments, nextMessage.attachments) && previousMessage.status === nextMessage.status && previousMessage.createdAt === nextMessage.createdAt && previousMode === nextMode && previousConfirmationSubmit === nextConfirmationSubmit && previousQuestionnaireSubmit === nextQuestionnaireSubmit && previousRenderMessageBlock === nextRenderMessageBlock;
2827
2831
  var ChatMessageItemView = ({
2828
2832
  message,
2829
2833
  mode = "agent",
@@ -2843,6 +2847,7 @@ var ChatMessageItemView = ({
2843
2847
  } = useChatMessageReveal(message);
2844
2848
  const isStoppedAssistant = message.role === "assistant" && message.status === "stopped";
2845
2849
  const attachments = message.attachments ?? [];
2850
+ const skills = message.skills ?? [];
2846
2851
  const blocks = message.blocks ?? [];
2847
2852
  const hasStructuredBlocks = blocks.length > 0;
2848
2853
  const hasMarkdownOnlyBlocks = hasStructuredBlocks && blocks.every((block) => block.type === "markdown");
@@ -2884,6 +2889,7 @@ var ChatMessageItemView = ({
2884
2889
  freshContent,
2885
2890
  settledContent
2886
2891
  });
2892
+ const shouldRenderHeader = isAssistantStreaming || isUserMessageCollapsible || isStoppedAssistant;
2887
2893
  const renderMessageContent = (content) => messageRenderMode === "plain-text" ? renderPlainTextContent(content) : renderMarkdownContent(content);
2888
2894
  const renderChatMessageBlock = (block, index3) => {
2889
2895
  switch (block.type) {
@@ -3033,7 +3039,7 @@ var ChatMessageItemView = ({
3033
3039
  })();
3034
3040
  return /* @__PURE__ */ jsxs5(Fragment2, { children: [
3035
3041
  /* @__PURE__ */ jsxs5(Bubble, { "data-role": message.role, "data-status": message.status ?? "done", children: [
3036
- /* @__PURE__ */ jsxs5(Header2, { children: [
3042
+ shouldRenderHeader ? /* @__PURE__ */ jsxs5(Header2, { children: [
3037
3043
  isAssistantStreaming ? /* @__PURE__ */ jsxs5(
3038
3044
  StreamingIndicator,
3039
3045
  {
@@ -3045,7 +3051,6 @@ var ChatMessageItemView = ({
3045
3051
  ]
3046
3052
  }
3047
3053
  ) : null,
3048
- /* @__PURE__ */ jsx8(Role, { children: message.role === "user" ? labels.userRoleLabel : labels.assistantRoleLabel }),
3049
3054
  isUserMessageCollapsible ? /* @__PURE__ */ jsx8(
3050
3055
  CollapseToggle,
3051
3056
  {
@@ -3058,8 +3063,9 @@ var ChatMessageItemView = ({
3058
3063
  }
3059
3064
  ) : null,
3060
3065
  isStoppedAssistant ? /* @__PURE__ */ jsx8(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
3061
- ] }),
3066
+ ] }) : null,
3062
3067
  /* @__PURE__ */ jsxs5(Content, { "data-testid": "chat-message-content", children: [
3068
+ skills.length ? /* @__PURE__ */ jsx8(SkillTagList, { "data-testid": "chat-message-skill-tags", children: skills.map((skill) => /* @__PURE__ */ jsx8(SkillTag, { children: skill }, skill)) }) : null,
3063
3069
  shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ jsx8(
3064
3070
  ContentStack,
3065
3071
  {
@@ -3124,19 +3130,22 @@ var ChatMessageItem = memo(
3124
3130
  var Bubble = styled7.article`
3125
3131
  width: 100%;
3126
3132
  max-width: 100%;
3127
- padding: 14px 16px;
3128
- border-radius: 22px;
3129
- background: rgba(255, 255, 255, 0.04);
3130
- border: 1px solid rgba(255, 255, 255, 0.07);
3131
- box-shadow:
3132
- inset 0 1px 0 rgba(255, 255, 255, 0.03),
3133
- 0 12px 30px rgba(0, 0, 0, 0.18);
3133
+ padding: 0;
3134
+ background: transparent;
3135
+ border: none;
3136
+ box-shadow: none;
3134
3137
 
3135
3138
  &[data-role='user'] {
3136
3139
  width: auto;
3137
3140
  max-width: min(760px, 100%);
3138
3141
  margin-left: auto;
3142
+ padding: 14px 16px;
3143
+ border-radius: 22px;
3139
3144
  background: linear-gradient(180deg, rgba(59, 59, 63, 0.9) 0%, rgba(42, 43, 46, 0.92) 100%);
3145
+ border: 1px solid rgba(255, 255, 255, 0.07);
3146
+ box-shadow:
3147
+ inset 0 1px 0 rgba(255, 255, 255, 0.03),
3148
+ 0 12px 30px rgba(0, 0, 0, 0.18);
3140
3149
  }
3141
3150
  `;
3142
3151
  var Header2 = styled7.div`
@@ -3145,12 +3154,6 @@ var Header2 = styled7.div`
3145
3154
  gap: 10px;
3146
3155
  margin-bottom: 10px;
3147
3156
  `;
3148
- var Role = styled7.div`
3149
- font-size: 12px;
3150
- color: rgba(255, 255, 255, 0.42);
3151
- text-transform: capitalize;
3152
- letter-spacing: 0.08em;
3153
- `;
3154
3157
  var StatusTag = styled7.span`
3155
3158
  display: inline-flex;
3156
3159
  align-items: center;
@@ -3257,6 +3260,23 @@ var Content = styled7.div`
3257
3260
  margin: 0 0 8px;
3258
3261
  }
3259
3262
  `;
3263
+ var SkillTagList = styled7.div`
3264
+ display: flex;
3265
+ flex-wrap: wrap;
3266
+ gap: 8px;
3267
+ margin-bottom: 12px;
3268
+ `;
3269
+ var SkillTag = styled7.span`
3270
+ display: inline-flex;
3271
+ align-items: center;
3272
+ max-width: 100%;
3273
+ padding: 10px 18px;
3274
+ border-radius: 999px;
3275
+ background: rgba(65, 65, 63, 0.6);
3276
+ color: #c5c1ba;
3277
+ font-size: 14px;
3278
+ line-height: 1;
3279
+ `;
3260
3280
  var ContentStack = styled7.div`
3261
3281
  display: flex;
3262
3282
  flex-direction: column;
@@ -6515,6 +6535,7 @@ function useFloating2(options) {
6515
6535
  var createUserMessage = ({
6516
6536
  sessionId,
6517
6537
  content,
6538
+ skills,
6518
6539
  attachments,
6519
6540
  localOnly,
6520
6541
  createdAt,
@@ -6524,6 +6545,7 @@ var createUserMessage = ({
6524
6545
  sessionId,
6525
6546
  role: "user",
6526
6547
  content,
6548
+ skills,
6527
6549
  attachments,
6528
6550
  localOnly,
6529
6551
  createdAt
@@ -6543,16 +6565,20 @@ var createAssistantStreamingMessage = ({
6543
6565
  var canSendChatMessage = ({
6544
6566
  value,
6545
6567
  attachmentCount = 0,
6568
+ skillCount = 0,
6546
6569
  isModelsLoading,
6547
6570
  isModelsError,
6548
6571
  hasModels
6549
6572
  }) => {
6550
6573
  const hasText = Boolean(value.trim());
6551
6574
  const hasAttachments = attachmentCount > 0;
6552
- if (!hasText && !hasAttachments)
6575
+ const hasSkills = skillCount > 0;
6576
+ if (!hasText && !hasAttachments && !hasSkills)
6553
6577
  return false;
6554
6578
  if (!hasText && hasAttachments)
6555
6579
  return true;
6580
+ if (!hasText && !hasAttachments && hasSkills)
6581
+ return !isModelsLoading && !isModelsError && hasModels;
6556
6582
  return !isModelsLoading && !isModelsError && hasModels;
6557
6583
  };
6558
6584
  var shouldSubmitChatComposer = ({
@@ -6848,6 +6874,7 @@ var useChatComposer = () => {
6848
6874
  const abortControllerBySessionRef = useRef10(/* @__PURE__ */ new Map());
6849
6875
  const stopRequestBySessionRef = useRef10(/* @__PURE__ */ new Map());
6850
6876
  const lastRequestBySessionRef = useRef10(/* @__PURE__ */ new Map());
6877
+ const previousActiveSessionIdRef = useRef10(activeSessionId);
6851
6878
  useEffect7(() => {
6852
6879
  setSelectedModel(
6853
6880
  (current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
@@ -6860,6 +6887,12 @@ var useChatComposer = () => {
6860
6887
  }
6861
6888
  setSelectedModeLocal(preferredMode ?? DEFAULT_CHAT_AGENT_MODE);
6862
6889
  }, [activeSession, preferredMode]);
6890
+ useEffect7(() => {
6891
+ if (previousActiveSessionIdRef.current !== activeSessionId) {
6892
+ setSelectedSkills([]);
6893
+ previousActiveSessionIdRef.current = activeSessionId;
6894
+ }
6895
+ }, [activeSessionId]);
6863
6896
  useEffect7(() => {
6864
6897
  if (!attachmentNotice)
6865
6898
  return;
@@ -7051,6 +7084,7 @@ var useChatComposer = () => {
7051
7084
  if (!canSendChatMessage({
7052
7085
  value: content,
7053
7086
  attachmentCount: composerAttachmentCount,
7087
+ skillCount: messageSkills?.length ?? 0,
7054
7088
  isModelsLoading,
7055
7089
  isModelsError,
7056
7090
  hasModels
@@ -7078,6 +7112,7 @@ var useChatComposer = () => {
7078
7112
  const userMessage = createUserMessage({
7079
7113
  sessionId: localSessionId,
7080
7114
  content,
7115
+ skills: messageSkills,
7081
7116
  attachments: messageAttachments,
7082
7117
  localOnly: false,
7083
7118
  createdAt: nowIso(),
@@ -7120,6 +7155,19 @@ var useChatComposer = () => {
7120
7155
  store
7121
7156
  ]
7122
7157
  );
7158
+ const openSkillPicker = useCallback7(() => {
7159
+ setValue((current) => {
7160
+ const matchedSkillQuery = current.match(/(^|\s)\/([^\s/]*)$/);
7161
+ if (matchedSkillQuery && matchedSkillQuery.index !== void 0) {
7162
+ const queryStart = matchedSkillQuery.index + matchedSkillQuery[1].length;
7163
+ return current.slice(0, queryStart).replace(/\s+$/, "");
7164
+ }
7165
+ if (!current.trim()) {
7166
+ return "/";
7167
+ }
7168
+ return /\s$/.test(current) ? `${current}/` : `${current} /`;
7169
+ });
7170
+ }, []);
7123
7171
  const stopSession = useCallback7(
7124
7172
  async (sessionId) => {
7125
7173
  const storeState = store.getState();
@@ -7194,6 +7242,7 @@ var useChatComposer = () => {
7194
7242
  removeSkill: (skill) => {
7195
7243
  setSelectedSkills((current) => current.filter((item) => item !== skill));
7196
7244
  },
7245
+ openSkillPicker,
7197
7246
  setSelectedModel,
7198
7247
  setSelectedMode: (mode) => {
7199
7248
  setSelectedModeLocal(mode);
@@ -7388,6 +7437,33 @@ var CloseGlyph = styled10.span`
7388
7437
  import styled11 from "@emotion/styled";
7389
7438
  import { Select } from "@xinghunm/compass-ui";
7390
7439
  import { jsx as jsx13, jsxs as jsxs10 } from "@emotion/react/jsx-runtime";
7440
+ var ModelIcon = () => /* @__PURE__ */ jsx13(
7441
+ "svg",
7442
+ {
7443
+ "aria-hidden": "true",
7444
+ width: "14",
7445
+ height: "14",
7446
+ viewBox: "0 0 14 14",
7447
+ fill: "none",
7448
+ style: { display: "block" },
7449
+ xmlns: "http://www.w3.org/2000/svg",
7450
+ children: /* @__PURE__ */ jsxs10(
7451
+ "g",
7452
+ {
7453
+ transform: "translate(1.6545 1.0182)",
7454
+ stroke: "currentColor",
7455
+ strokeLinecap: "round",
7456
+ strokeLinejoin: "round",
7457
+ strokeWidth: "1.22181818",
7458
+ children: [
7459
+ /* @__PURE__ */ jsx13("path", { d: "M4.80808081 11.8021107C5.17998715 12.0158424 5.63819467 12.0158424 6.01010101 11.8021107L10.2171717 9.40913806C10.588697 9.19562567 10.8177418 8.8012024 10.8181818 8.37417739V3.58823209C10.8177418 3.16120709 10.588697 2.76678381 10.2171717 2.55327142L6.01010101 0.160298772C5.63819467 -0.0534329241 5.17998715 -0.0534329241 4.80808081 0.160298772L0.601010101 2.55327142C0.229484841 2.76678381 0.000440028788 3.16120709 0 3.58823209V8.37417739C0.000440028788 8.8012024 0.229484841 9.19562567 0.601010101 9.40913806L4.80808081 11.8021107Z" }),
7460
+ /* @__PURE__ */ jsx13("path", { d: "M5.40909091 11.9636364V5.98120474" }),
7461
+ /* @__PURE__ */ jsx13("path", { d: "M0.174292929 2.98998893L5.40909091 5.98120474L10.6438889 2.98998893" })
7462
+ ]
7463
+ }
7464
+ )
7465
+ }
7466
+ );
7391
7467
  var ChatModelControl = ({
7392
7468
  selectedModel,
7393
7469
  availableModels,
@@ -7431,22 +7507,20 @@ var ChatModelControl = ({
7431
7507
  return /* @__PURE__ */ jsx13(ModelBadge, { children: "Loading models..." });
7432
7508
  }
7433
7509
  if (hasModels && selectedModel) {
7434
- if (availableModels.length > 1) {
7435
- return /* @__PURE__ */ jsx13(
7436
- ModelSelect,
7437
- {
7438
- "data-testid": "chat-model-select",
7439
- "aria-label": "Select model",
7440
- value: selectedModel,
7441
- onChange: (value) => onSelectedModelChange(String(value)),
7442
- options: availableModels.map((model) => ({
7443
- label: model.id,
7444
- value: model.id
7445
- }))
7446
- }
7447
- );
7448
- }
7449
- return /* @__PURE__ */ jsx13(ModelBadge, { children: selectedModel });
7510
+ return /* @__PURE__ */ jsx13(
7511
+ ModelSelect,
7512
+ {
7513
+ "data-testid": "chat-model-select",
7514
+ "aria-label": "Select model",
7515
+ value: selectedModel,
7516
+ onChange: (value) => onSelectedModelChange(String(value)),
7517
+ options: availableModels.map((model) => ({
7518
+ label: model.id,
7519
+ value: model.id
7520
+ })),
7521
+ labelRender: () => /* @__PURE__ */ jsx13(ModelIcon, {})
7522
+ }
7523
+ );
7450
7524
  }
7451
7525
  return /* @__PURE__ */ jsx13(ModelBadge, { children: "No model available" });
7452
7526
  };
@@ -7489,20 +7563,55 @@ var ReloadIcon = styled11.svg`
7489
7563
  `;
7490
7564
  var ModelSelect = styled11(Select)`
7491
7565
  && {
7492
- width: auto;
7566
+ width: 24px;
7567
+ min-width: 24px;
7568
+ max-width: 24px;
7569
+ flex: 0 0 24px;
7570
+ }
7571
+
7572
+ && .compass-select-selector {
7573
+ justify-content: center;
7574
+ border-radius: 999px;
7575
+ min-height: 24px;
7576
+ height: 24px;
7577
+ width: 24px;
7578
+ padding: 0;
7579
+ background: rgba(255, 255, 255, 0.06);
7580
+ border-color: rgba(255, 255, 255, 0.14);
7581
+ color: #c5c1ba;
7582
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
7583
+ margin-right: 0;
7584
+ }
7585
+
7586
+ && .compass-select-selector:hover {
7587
+ background: rgba(255, 255, 255, 0.1);
7588
+ border-color: rgba(255, 255, 255, 0.2);
7589
+ }
7590
+
7591
+ && .compass-select-selection-item {
7592
+ display: flex;
7593
+ width: 100%;
7594
+ margin-right: 0;
7493
7595
  min-width: 0;
7494
- max-width: 100%;
7596
+ min-height: 24px;
7597
+ align-items: center;
7598
+ justify-content: center;
7599
+ }
7495
7600
 
7496
- .compass-select-selector {
7497
- line-height: 1;
7498
- border-radius: 999px;
7499
- min-height: 24px;
7500
- height: 24px;
7501
- background: rgba(255, 255, 255, 0.04);
7502
- border-color: rgba(255, 255, 255, 0.12);
7503
- color: rgba(255, 255, 255, 0.82);
7504
- padding: 4px 12px;
7505
- }
7601
+ && .compass-select-selector > div:first-of-type {
7602
+ display: flex;
7603
+ width: 100%;
7604
+ align-items: center;
7605
+ justify-content: center;
7606
+ margin-right: 0;
7607
+ }
7608
+
7609
+ && .compass-select-selection-item svg {
7610
+ display: block;
7611
+ }
7612
+
7613
+ && .compass-select-selector > span:last-child {
7614
+ display: none;
7506
7615
  }
7507
7616
  `;
7508
7617
 
@@ -7533,21 +7642,27 @@ var ChatModeControl = ({
7533
7642
  };
7534
7643
  var ModeSelect = styled12(Select2)`
7535
7644
  && {
7536
- flex: 0 1 auto;
7537
- width: auto;
7538
- min-width: 0;
7539
- max-width: 100%;
7645
+ flex: 0 0 auto;
7646
+ width: fit-content;
7647
+ min-width: fit-content;
7648
+ max-width: none;
7649
+ }
7540
7650
 
7541
- .compass-select-selector {
7542
- line-height: 1;
7543
- border-radius: 999px;
7544
- min-height: 24px;
7545
- height: 24px;
7546
- background: rgba(255, 255, 255, 0.04);
7547
- border-color: rgba(255, 255, 255, 0.12);
7548
- color: rgba(255, 255, 255, 0.82);
7549
- padding: 4px 12px;
7550
- }
7651
+ && .compass-select-selector {
7652
+ width: fit-content;
7653
+ line-height: 1;
7654
+ border-radius: 999px;
7655
+ min-height: 24px;
7656
+ height: 24px;
7657
+ background: rgba(255, 255, 255, 0.04);
7658
+ border-color: rgba(255, 255, 255, 0.12);
7659
+ color: rgba(255, 255, 255, 0.82);
7660
+ padding: 4px 12px;
7661
+ }
7662
+
7663
+ && .compass-select-selector > div:first-of-type {
7664
+ width: auto;
7665
+ min-width: max-content;
7551
7666
  }
7552
7667
  `;
7553
7668
 
@@ -7722,6 +7837,31 @@ var PlusIcon = () => /* @__PURE__ */ jsx16(
7722
7837
  children: /* @__PURE__ */ jsx16("path", { d: "M8 3v10M3 8h10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })
7723
7838
  }
7724
7839
  );
7840
+ var SkillIcon = () => /* @__PURE__ */ jsx16(
7841
+ "svg",
7842
+ {
7843
+ "aria-hidden": "true",
7844
+ width: "14",
7845
+ height: "14",
7846
+ viewBox: "0 0 14 14",
7847
+ fill: "none",
7848
+ xmlns: "http://www.w3.org/2000/svg",
7849
+ children: /* @__PURE__ */ jsxs11(
7850
+ "g",
7851
+ {
7852
+ transform: "translate(1.7818 2.2909)",
7853
+ stroke: "currentColor",
7854
+ strokeLinecap: "round",
7855
+ strokeLinejoin: "round",
7856
+ strokeWidth: "1.22181818",
7857
+ children: [
7858
+ /* @__PURE__ */ jsx16("path", { d: "M5.28181818 2.12121212V9.54545455" }),
7859
+ /* @__PURE__ */ jsx16("path", { d: "M0.528181818 7.95454545C0.236475055 7.95454545 0 7.7171207 0 7.42424242V0.53030303C0 0.237424754 0.236475055 0 0.528181818 0H3.16909091C4.33591796 0 5.28181818 0.949699016 5.28181818 2.12121212C5.28181818 0.949699016 6.2277184 0 7.39454545 0H10.0354545C10.3271613 0 10.5636364 0.237424754 10.5636364 0.53030303V7.42424242C10.5636364 7.7171207 10.3271613 7.95454545 10.0354545 7.95454545H6.86636364C5.99124335 7.95454545 5.28181818 8.66681972 5.28181818 9.54545455C5.28181818 8.66681972 4.57239302 7.95454545 3.69727273 7.95454545H0.528181818Z" })
7860
+ ]
7861
+ }
7862
+ )
7863
+ }
7864
+ );
7725
7865
  var ComposerExpandIcon = ({ expanded }) => /* @__PURE__ */ jsx16(
7726
7866
  "svg",
7727
7867
  {
@@ -7805,6 +7945,7 @@ var ChatComposerView = ({
7805
7945
  onSelectedModelChange,
7806
7946
  onSelectedModeChange,
7807
7947
  onReloadModels,
7948
+ onOpenSkillPicker,
7808
7949
  onStop,
7809
7950
  onSend
7810
7951
  }) => {
@@ -7819,6 +7960,7 @@ var ChatComposerView = ({
7819
7960
  const canSend = canSendChatMessage({
7820
7961
  value,
7821
7962
  attachmentCount: attachments.length,
7963
+ skillCount: selectedSkills.length,
7822
7964
  isModelsLoading,
7823
7965
  isModelsError,
7824
7966
  hasModels
@@ -7839,7 +7981,7 @@ var ChatComposerView = ({
7839
7981
  open: showSkillMenu,
7840
7982
  placement: "bottom-start",
7841
7983
  middleware: [
7842
- offset3(8),
7984
+ offset3(3),
7843
7985
  flip3({ padding: 8 }),
7844
7986
  shift3({ padding: 8 }),
7845
7987
  size3({
@@ -7945,6 +8087,12 @@ var ChatComposerView = ({
7945
8087
  event.preventDefault();
7946
8088
  onPasteImages(imageFiles);
7947
8089
  };
8090
+ const handleOpenSkillPicker = () => {
8091
+ onOpenSkillPicker();
8092
+ requestAnimationFrame(() => {
8093
+ inputRef.current?.focus();
8094
+ });
8095
+ };
7948
8096
  return /* @__PURE__ */ jsxs11(Container2, { children: [
7949
8097
  /* @__PURE__ */ jsxs11(Surface, { "data-testid": "chat-composer-surface", children: [
7950
8098
  enableImageAttachments ? /* @__PURE__ */ jsx16(
@@ -7967,7 +8115,7 @@ var ChatComposerView = ({
7967
8115
  }
7968
8116
  ),
7969
8117
  attachmentNotice === "limit_reached" ? /* @__PURE__ */ jsx16(AttachmentNotice, { "data-testid": "chat-composer-attachment-notice", children: attachmentLimitNotice }) : null,
7970
- selectedSkills.length ? /* @__PURE__ */ jsx16(SkillTagList, { "data-testid": "chat-composer-skill-tags", children: selectedSkills.map((skill) => /* @__PURE__ */ jsxs11(SkillTag, { children: [
8118
+ selectedSkills.length ? /* @__PURE__ */ jsx16(SkillTagList2, { "data-testid": "chat-composer-skill-tags", children: selectedSkills.map((skill) => /* @__PURE__ */ jsxs11(SkillTag2, { children: [
7971
8119
  /* @__PURE__ */ jsx16("span", { children: skill }),
7972
8120
  /* @__PURE__ */ jsx16(
7973
8121
  SkillTagRemoveButton,
@@ -8007,17 +8155,27 @@ var ChatComposerView = ({
8007
8155
  )
8008
8156
  ] }),
8009
8157
  /* @__PURE__ */ jsxs11(Footer, { children: [
8010
- /* @__PURE__ */ jsx16(LeadingActions, { "data-testid": "chat-composer-leading-actions", children: enableImageAttachments ? /* @__PURE__ */ jsx16(
8011
- AttachButton,
8012
- {
8013
- type: "button",
8014
- "data-testid": "chat-composer-attach-image",
8015
- "aria-label": "Attach image",
8016
- onClick: () => imageInputRef.current?.click(),
8017
- children: /* @__PURE__ */ jsx16(PlusIcon, {})
8018
- }
8019
- ) : null }),
8020
- /* @__PURE__ */ jsxs11(TrailingActions, { "data-testid": "chat-composer-trailing-actions", children: [
8158
+ /* @__PURE__ */ jsxs11(LeadingActions, { "data-testid": "chat-composer-leading-actions", children: [
8159
+ enableImageAttachments ? /* @__PURE__ */ jsx16(
8160
+ AttachButton,
8161
+ {
8162
+ type: "button",
8163
+ "data-testid": "chat-composer-attach-image",
8164
+ "aria-label": "Attach image",
8165
+ onClick: () => imageInputRef.current?.click(),
8166
+ children: /* @__PURE__ */ jsx16(PlusIcon, {})
8167
+ }
8168
+ ) : null,
8169
+ /* @__PURE__ */ jsx16(
8170
+ SkillButton,
8171
+ {
8172
+ type: "button",
8173
+ "data-testid": "chat-composer-skill-trigger",
8174
+ "aria-label": "Open skill picker",
8175
+ onClick: handleOpenSkillPicker,
8176
+ children: /* @__PURE__ */ jsx16(SkillIcon, {})
8177
+ }
8178
+ ),
8021
8179
  /* @__PURE__ */ jsx16(
8022
8180
  ChatModeControl,
8023
8181
  {
@@ -8026,7 +8184,9 @@ var ChatComposerView = ({
8026
8184
  labels: modeLabels,
8027
8185
  onChange: onSelectedModeChange
8028
8186
  }
8029
- ),
8187
+ )
8188
+ ] }),
8189
+ /* @__PURE__ */ jsxs11(TrailingActions, { "data-testid": "chat-composer-trailing-actions", children: [
8030
8190
  /* @__PURE__ */ jsx16(
8031
8191
  ChatModelControl,
8032
8192
  {
@@ -8128,6 +8288,7 @@ var ChatComposer = () => {
8128
8288
  onSelectedModelChange: actions.setSelectedModel,
8129
8289
  onSelectedModeChange: actions.setSelectedMode,
8130
8290
  onReloadModels: actions.reloadModels,
8291
+ onOpenSkillPicker: actions.openSkillPicker,
8131
8292
  onStop: actions.stop,
8132
8293
  onSend: send
8133
8294
  }
@@ -8166,31 +8327,55 @@ var AttachmentNotice = styled14.div`
8166
8327
  font-size: 12px;
8167
8328
  line-height: 1.4;
8168
8329
  `;
8169
- var SkillTagList = styled14.div`
8330
+ var SkillTagList2 = styled14.div`
8170
8331
  display: flex;
8171
8332
  flex-wrap: wrap;
8172
- gap: 8px;
8173
- padding: 12px 12px 0;
8333
+ gap: 9px;
8334
+ padding: 11px 16px;
8174
8335
  `;
8175
- var SkillTag = styled14.span`
8336
+ var SkillTag2 = styled14.span`
8337
+ position: relative;
8176
8338
  display: inline-flex;
8177
8339
  align-items: center;
8178
- gap: 6px;
8179
8340
  max-width: 100%;
8180
- border-radius: 999px;
8181
- padding: 8px 12px;
8182
- background: rgba(255, 255, 255, 0.06);
8183
- color: var(--text-primary);
8184
- font-size: 13px;
8341
+ padding: 11px 16px;
8342
+ background: rgba(65, 65, 63, 0.35);
8343
+ border-radius: 18px;
8344
+ font-weight: 400;
8345
+ font-size: 14px;
8346
+ color: #c5c1ba;
8185
8347
  line-height: 1;
8348
+
8349
+ :hover {
8350
+ background: #41413f;
8351
+ }
8352
+
8353
+ &:hover > button,
8354
+ &:focus-within > button {
8355
+ opacity: 1;
8356
+ pointer-events: auto;
8357
+ }
8186
8358
  `;
8187
8359
  var SkillTagRemoveButton = styled14.button`
8360
+ position: absolute;
8361
+ top: 0;
8362
+ right: 0;
8363
+ width: 14px;
8364
+ height: 14px;
8365
+ border-radius: 50%;
8366
+ background: #000000;
8367
+ display: grid;
8368
+ place-items: center;
8188
8369
  border: none;
8189
8370
  padding: 0;
8190
- background: transparent;
8191
8371
  color: inherit;
8192
8372
  cursor: pointer;
8193
8373
  line-height: 1;
8374
+ opacity: 0;
8375
+ pointer-events: none;
8376
+ transition:
8377
+ opacity 120ms ease,
8378
+ background-color 120ms ease;
8194
8379
  `;
8195
8380
  var InputArea = styled14.div`
8196
8381
  grid-area: input;
@@ -8251,18 +8436,21 @@ var SkillMenu = styled14.div`
8251
8436
  gap: 4px;
8252
8437
  max-height: 240px;
8253
8438
  overflow-y: auto;
8254
- padding: 8px;
8255
- border: 1px solid rgba(255, 255, 255, 0.12);
8256
- border-radius: 16px;
8439
+ padding: 4px;
8440
+ border-radius: 12px;
8257
8441
  background: rgba(28, 28, 28, 0.98);
8258
8442
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.28);
8259
8443
  z-index: 1000;
8444
+ background: #1c1c1c;
8445
+ box-shadow: 0px 4px 6px 0px rgba(14, 14, 14, 0.3);
8446
+ border-radius: 12px;
8447
+ border: 1px solid #282825;
8260
8448
  `;
8261
8449
  var SkillMenuItem = styled14.button`
8262
8450
  width: 100%;
8263
8451
  border: none;
8264
- border-radius: 12px;
8265
- padding: 12px 14px;
8452
+ border-radius: 8px;
8453
+ padding: 12px;
8266
8454
  background: transparent;
8267
8455
  color: var(--text-primary);
8268
8456
  text-align: left;
@@ -8270,7 +8458,7 @@ var SkillMenuItem = styled14.button`
8270
8458
 
8271
8459
  &[data-active='true'],
8272
8460
  &:hover {
8273
- background: rgba(255, 255, 255, 0.12);
8461
+ background: #41413f;
8274
8462
  }
8275
8463
  `;
8276
8464
  var SkillMenuState = styled14.div`
@@ -8300,9 +8488,8 @@ var ComposerExpandButton = styled14.button`
8300
8488
  `;
8301
8489
  var Footer = styled14.div`
8302
8490
  grid-area: footer;
8303
- display: grid;
8304
- grid-template-columns: auto minmax(0, 1fr);
8305
- align-items: flex-end;
8491
+ display: flex;
8492
+ align-items: center;
8306
8493
  gap: 8px;
8307
8494
  padding: 0 14px 14px;
8308
8495
  `;
@@ -8310,33 +8497,72 @@ var LeadingActions = styled14.div`
8310
8497
  display: flex;
8311
8498
  align-items: center;
8312
8499
  justify-content: flex-start;
8313
- gap: 8px;
8500
+ gap: 4px;
8314
8501
  min-width: 0;
8315
8502
  `;
8316
8503
  var TrailingActions = styled14.div`
8317
8504
  display: flex;
8318
8505
  align-items: center;
8319
- flex-wrap: wrap;
8506
+ flex-wrap: nowrap;
8320
8507
  min-width: 0;
8321
- width: fit-content;
8322
- max-width: 100%;
8323
- justify-self: end;
8508
+ margin-left: auto;
8324
8509
  justify-content: flex-end;
8325
8510
  gap: 8px;
8326
8511
  `;
8327
8512
  var AttachButton = styled14.button`
8328
- width: 28px;
8329
- height: 28px;
8513
+ width: 24px;
8514
+ height: 24px;
8330
8515
  display: grid;
8331
8516
  place-items: center;
8332
- border: none;
8333
8517
  border-radius: 999px;
8334
- background: transparent;
8335
- color: rgba(255, 255, 255, 0.82);
8518
+ border: 1px solid rgba(255, 255, 255, 0.14);
8519
+ background: rgba(255, 255, 255, 0.06);
8520
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
8521
+ padding: 0;
8522
+ color: #c5c1ba;
8336
8523
  cursor: pointer;
8524
+ flex: 0 0 24px;
8525
+
8526
+ transition:
8527
+ background 160ms ease,
8528
+ border-color 160ms ease,
8529
+ color 160ms ease;
8337
8530
 
8338
8531
  &:hover {
8339
- background: rgba(255, 255, 255, 0.08);
8532
+ background: rgba(255, 255, 255, 0.1);
8533
+ border-color: rgba(255, 255, 255, 0.2);
8534
+ }
8535
+
8536
+ svg {
8537
+ display: block;
8538
+ }
8539
+ `;
8540
+ var SkillButton = styled14.button`
8541
+ width: 24px;
8542
+ height: 24px;
8543
+ display: grid;
8544
+ place-items: center;
8545
+ border-radius: 999px;
8546
+ border: 1px solid rgba(255, 255, 255, 0.14);
8547
+ background: rgba(255, 255, 255, 0.06);
8548
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
8549
+ padding: 0;
8550
+ color: #c5c1ba;
8551
+ cursor: pointer;
8552
+ flex: 0 0 24px;
8553
+
8554
+ transition:
8555
+ background 160ms ease,
8556
+ border-color 160ms ease,
8557
+ color 160ms ease;
8558
+
8559
+ &:hover {
8560
+ background: rgba(255, 255, 255, 0.1);
8561
+ border-color: rgba(255, 255, 255, 0.2);
8562
+ }
8563
+
8564
+ svg {
8565
+ display: block;
8340
8566
  }
8341
8567
  `;
8342
8568
 
@@ -8469,6 +8695,76 @@ var List2 = styled16.div`
8469
8695
 
8470
8696
  // src/components/ai-chat/index.tsx
8471
8697
  import { Fragment as Fragment6, jsx as jsx19, jsxs as jsxs14 } from "@emotion/react/jsx-runtime";
8698
+ var NewTalkIcon = () => /* @__PURE__ */ jsx19(
8699
+ "svg",
8700
+ {
8701
+ "aria-hidden": "true",
8702
+ width: "16",
8703
+ height: "16",
8704
+ viewBox: "0 0 16 16",
8705
+ fill: "none",
8706
+ style: { display: "block" },
8707
+ xmlns: "http://www.w3.org/2000/svg",
8708
+ children: /* @__PURE__ */ jsxs14(
8709
+ "g",
8710
+ {
8711
+ transform: "translate(1.8909 2.0364)",
8712
+ stroke: "currentColor",
8713
+ strokeLinecap: "round",
8714
+ strokeLinejoin: "round",
8715
+ strokeWidth: "1.36533333",
8716
+ children: [
8717
+ /* @__PURE__ */ jsxs14("g", { transform: "translate(9.8909 2.3273) rotate(-315) translate(-9.8909 -2.3273) translate(8.2909 0.7273)", children: [
8718
+ /* @__PURE__ */ jsx19("path", { d: "M0 0C0 0 1.06666667 1.06666667 3.2 3.2" }),
8719
+ /* @__PURE__ */ jsx19("path", { d: "M3.2 0C3.2 0 2.13333333 1.06666667 0 3.2" })
8720
+ ] }),
8721
+ /* @__PURE__ */ jsx19("path", { d: "M12.2181818 6.15099432V7.31151515C12.2181818 8.25408112 11.4540811 9.01818182 10.5115152 9.01818182H4.19089868C4.07080994 9.01818182 3.95380126 9.05618401 3.85662972 9.12674601L0.903157852 11.2714364C0.648927541 11.4560481 0.293175841 11.399611 0.10856419 11.1453807C0.0380021898 11.0482092 0 10.9312005 0 10.8111117V1.70666667C0 0.764100694 0.764100694 0 1.70666667 0H6.07111268" })
8722
+ ]
8723
+ }
8724
+ )
8725
+ }
8726
+ );
8727
+ var hasStartedConversation = ({
8728
+ activeSessionId,
8729
+ messagesBySession,
8730
+ streamingMessageBySession,
8731
+ errorBySession
8732
+ }) => {
8733
+ if (!activeSessionId) {
8734
+ return false;
8735
+ }
8736
+ return Boolean(
8737
+ (messagesBySession[activeSessionId]?.length ?? 0) > 0 || streamingMessageBySession[activeSessionId] || errorBySession[activeSessionId]
8738
+ );
8739
+ };
8740
+ var AiChatWorkspaceContent = ({
8741
+ showConversationList,
8742
+ showNewChatButton,
8743
+ renderNewChatTrigger,
8744
+ showComposerOnlyBeforeFirstMessage = false,
8745
+ onConversationStartedChange
8746
+ }) => {
8747
+ const isConversationStarted = useChatStore(
8748
+ (state) => hasStartedConversation({
8749
+ activeSessionId: state.activeSessionId,
8750
+ messagesBySession: state.messagesBySession,
8751
+ streamingMessageBySession: state.streamingMessageBySession,
8752
+ errorBySession: state.errorBySession
8753
+ })
8754
+ );
8755
+ const shouldShowComposerOnly = showComposerOnlyBeforeFirstMessage && !showConversationList && !isConversationStarted;
8756
+ useEffect9(() => {
8757
+ onConversationStartedChange?.(isConversationStarted);
8758
+ }, [isConversationStarted, onConversationStartedChange]);
8759
+ return /* @__PURE__ */ jsxs14(Root, { "data-testid": "ai-chat", children: [
8760
+ showConversationList ? /* @__PURE__ */ jsx19(ChatConversationList, {}) : null,
8761
+ /* @__PURE__ */ jsxs14(Workspace, { children: [
8762
+ showNewChatButton && !showConversationList && !shouldShowComposerOnly ? /* @__PURE__ */ jsx19(QuickActions, { renderNewChatTrigger }) : null,
8763
+ shouldShowComposerOnly ? null : /* @__PURE__ */ jsx19(ChatThread, {}),
8764
+ /* @__PURE__ */ jsx19(ChatComposer, {})
8765
+ ] })
8766
+ ] });
8767
+ };
8472
8768
  var QuickActions = ({ renderNewChatTrigger }) => {
8473
8769
  const { labels, stopRef, store } = useChatContext();
8474
8770
  const startNewChat = useChatStore((state) => state.startNewChat);
@@ -8522,9 +8818,11 @@ var QuickActions = ({ renderNewChatTrigger }) => {
8522
8818
  {
8523
8819
  type: "button",
8524
8820
  "data-testid": "chat-start-new-session",
8821
+ "aria-label": labels.newChat,
8822
+ title: labels.newChat,
8525
8823
  onClick: () => void handleStartNewChat(),
8526
8824
  disabled: isActiveSessionStopping,
8527
- children: labels.newChat
8825
+ children: /* @__PURE__ */ jsx19(NewTalkIcon, {})
8528
8826
  }
8529
8827
  ) });
8530
8828
  };
@@ -8536,6 +8834,8 @@ var AiChat = ({
8536
8834
  showConversationList = false,
8537
8835
  showNewChatButton = false,
8538
8836
  renderNewChatTrigger,
8837
+ showComposerOnlyBeforeFirstMessage = false,
8838
+ onConversationStartedChange,
8539
8839
  ...providerProps
8540
8840
  }) => /* @__PURE__ */ jsx19(
8541
8841
  ConfigProvider,
@@ -8572,14 +8872,16 @@ var AiChat = ({
8572
8872
  }
8573
8873
  }
8574
8874
  },
8575
- children: /* @__PURE__ */ jsx19(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ jsxs14(Root, { "data-testid": "ai-chat", children: [
8576
- showConversationList ? /* @__PURE__ */ jsx19(ChatConversationList, {}) : null,
8577
- /* @__PURE__ */ jsxs14(Workspace, { children: [
8578
- showNewChatButton && !showConversationList ? /* @__PURE__ */ jsx19(QuickActions, { renderNewChatTrigger }) : null,
8579
- /* @__PURE__ */ jsx19(ChatThread, {}),
8580
- /* @__PURE__ */ jsx19(ChatComposer, {})
8581
- ] })
8582
- ] }) })
8875
+ children: /* @__PURE__ */ jsx19(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ jsx19(
8876
+ AiChatWorkspaceContent,
8877
+ {
8878
+ showConversationList,
8879
+ showNewChatButton,
8880
+ renderNewChatTrigger,
8881
+ showComposerOnlyBeforeFirstMessage,
8882
+ onConversationStartedChange
8883
+ }
8884
+ ) })
8583
8885
  }
8584
8886
  );
8585
8887
  var Root = styled17.div`
@@ -8603,12 +8905,26 @@ var QuickActionsRow = styled17.div`
8603
8905
  padding: 12px 12px 0;
8604
8906
  `;
8605
8907
  var QuickActionButton = styled17.button`
8606
- border: none;
8607
- border-radius: 12px;
8608
- padding: 10px 14px;
8908
+ width: 48px;
8909
+ height: 48px;
8910
+ display: grid;
8911
+ place-items: center;
8912
+ padding: 0;
8913
+ border: 1px solid rgba(255, 255, 255, 0.14);
8914
+ border-radius: 999px;
8609
8915
  background: rgba(255, 255, 255, 0.08);
8916
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
8610
8917
  color: var(--text-primary, #fcfbf8);
8611
8918
  cursor: pointer;
8919
+ transition:
8920
+ background 160ms ease,
8921
+ border-color 160ms ease,
8922
+ color 160ms ease;
8923
+
8924
+ &:hover:not(:disabled) {
8925
+ background: rgba(255, 255, 255, 0.12);
8926
+ border-color: rgba(255, 255, 255, 0.2);
8927
+ }
8612
8928
 
8613
8929
  &:disabled {
8614
8930
  opacity: 0.5;