@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.js CHANGED
@@ -46,6 +46,7 @@ __export(src_exports, {
46
46
  module.exports = __toCommonJS(src_exports);
47
47
 
48
48
  // src/components/ai-chat/index.tsx
49
+ var import_react20 = require("react");
49
50
  var import_styled17 = __toESM(require("@emotion/styled"));
50
51
  var import_compass_ui4 = require("@xinghunm/compass-ui");
51
52
 
@@ -129,6 +130,9 @@ var resolveSessionTitleFromMessage = (message) => {
129
130
  if (trimmedContent) {
130
131
  return trimmedContent.slice(0, 30);
131
132
  }
133
+ if (message.skills?.length) {
134
+ return message.skills.join(", ").slice(0, 30);
135
+ }
132
136
  if ((message.attachments?.length ?? 0) > 0) {
133
137
  return IMAGE_MESSAGE_SESSION_TITLE;
134
138
  }
@@ -674,7 +678,7 @@ var createDefaultRequestBody = async ({
674
678
  attachments
675
679
  }) => {
676
680
  const hasAttachments = Boolean(attachments?.length);
677
- const skillPayload = skills?.length ? { skill: skills.length === 1 ? skills[0] : skills } : {};
681
+ const skillPayload = skills?.length ? { skills } : {};
678
682
  if (!hasAttachments) {
679
683
  return {
680
684
  model,
@@ -2866,7 +2870,7 @@ var areMessageBlocksEqual = (previousBlocks, nextBlocks) => {
2866
2870
  }
2867
2871
  });
2868
2872
  };
2869
- 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;
2873
+ 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;
2870
2874
  var ChatMessageItemView = ({
2871
2875
  message,
2872
2876
  mode = "agent",
@@ -2886,6 +2890,7 @@ var ChatMessageItemView = ({
2886
2890
  } = useChatMessageReveal(message);
2887
2891
  const isStoppedAssistant = message.role === "assistant" && message.status === "stopped";
2888
2892
  const attachments = message.attachments ?? [];
2893
+ const skills = message.skills ?? [];
2889
2894
  const blocks = message.blocks ?? [];
2890
2895
  const hasStructuredBlocks = blocks.length > 0;
2891
2896
  const hasMarkdownOnlyBlocks = hasStructuredBlocks && blocks.every((block) => block.type === "markdown");
@@ -2927,6 +2932,7 @@ var ChatMessageItemView = ({
2927
2932
  freshContent,
2928
2933
  settledContent
2929
2934
  });
2935
+ const shouldRenderHeader = isAssistantStreaming || isUserMessageCollapsible || isStoppedAssistant;
2930
2936
  const renderMessageContent = (content) => messageRenderMode === "plain-text" ? renderPlainTextContent(content) : renderMarkdownContent(content);
2931
2937
  const renderChatMessageBlock = (block, index3) => {
2932
2938
  switch (block.type) {
@@ -3076,7 +3082,7 @@ var ChatMessageItemView = ({
3076
3082
  })();
3077
3083
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
3078
3084
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Bubble, { "data-role": message.role, "data-status": message.status ?? "done", children: [
3079
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Header2, { children: [
3085
+ shouldRenderHeader ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Header2, { children: [
3080
3086
  isAssistantStreaming ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
3081
3087
  StreamingIndicator,
3082
3088
  {
@@ -3088,7 +3094,6 @@ var ChatMessageItemView = ({
3088
3094
  ]
3089
3095
  }
3090
3096
  ) : null,
3091
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Role, { children: message.role === "user" ? labels.userRoleLabel : labels.assistantRoleLabel }),
3092
3097
  isUserMessageCollapsible ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3093
3098
  CollapseToggle,
3094
3099
  {
@@ -3101,8 +3106,9 @@ var ChatMessageItemView = ({
3101
3106
  }
3102
3107
  ) : null,
3103
3108
  isStoppedAssistant ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(StatusTag, { "data-testid": "chat-message-stopped-tag", children: labels.stoppedResponse }) : null
3104
- ] }),
3109
+ ] }) : null,
3105
3110
  /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Content, { "data-testid": "chat-message-content", children: [
3111
+ skills.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SkillTagList, { "data-testid": "chat-message-skill-tags", children: skills.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SkillTag, { children: skill }, skill)) }) : null,
3106
3112
  shouldRenderStructuredBlocks || hasTextContent ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
3107
3113
  ContentStack,
3108
3114
  {
@@ -3167,19 +3173,22 @@ var ChatMessageItem = (0, import_react9.memo)(
3167
3173
  var Bubble = import_styled7.default.article`
3168
3174
  width: 100%;
3169
3175
  max-width: 100%;
3170
- padding: 14px 16px;
3171
- border-radius: 22px;
3172
- background: rgba(255, 255, 255, 0.04);
3173
- border: 1px solid rgba(255, 255, 255, 0.07);
3174
- box-shadow:
3175
- inset 0 1px 0 rgba(255, 255, 255, 0.03),
3176
- 0 12px 30px rgba(0, 0, 0, 0.18);
3176
+ padding: 0;
3177
+ background: transparent;
3178
+ border: none;
3179
+ box-shadow: none;
3177
3180
 
3178
3181
  &[data-role='user'] {
3179
3182
  width: auto;
3180
3183
  max-width: min(760px, 100%);
3181
3184
  margin-left: auto;
3185
+ padding: 14px 16px;
3186
+ border-radius: 22px;
3182
3187
  background: linear-gradient(180deg, rgba(59, 59, 63, 0.9) 0%, rgba(42, 43, 46, 0.92) 100%);
3188
+ border: 1px solid rgba(255, 255, 255, 0.07);
3189
+ box-shadow:
3190
+ inset 0 1px 0 rgba(255, 255, 255, 0.03),
3191
+ 0 12px 30px rgba(0, 0, 0, 0.18);
3183
3192
  }
3184
3193
  `;
3185
3194
  var Header2 = import_styled7.default.div`
@@ -3188,12 +3197,6 @@ var Header2 = import_styled7.default.div`
3188
3197
  gap: 10px;
3189
3198
  margin-bottom: 10px;
3190
3199
  `;
3191
- var Role = import_styled7.default.div`
3192
- font-size: 12px;
3193
- color: rgba(255, 255, 255, 0.42);
3194
- text-transform: capitalize;
3195
- letter-spacing: 0.08em;
3196
- `;
3197
3200
  var StatusTag = import_styled7.default.span`
3198
3201
  display: inline-flex;
3199
3202
  align-items: center;
@@ -3300,6 +3303,23 @@ var Content = import_styled7.default.div`
3300
3303
  margin: 0 0 8px;
3301
3304
  }
3302
3305
  `;
3306
+ var SkillTagList = import_styled7.default.div`
3307
+ display: flex;
3308
+ flex-wrap: wrap;
3309
+ gap: 8px;
3310
+ margin-bottom: 12px;
3311
+ `;
3312
+ var SkillTag = import_styled7.default.span`
3313
+ display: inline-flex;
3314
+ align-items: center;
3315
+ max-width: 100%;
3316
+ padding: 10px 18px;
3317
+ border-radius: 999px;
3318
+ background: rgba(65, 65, 63, 0.6);
3319
+ color: #c5c1ba;
3320
+ font-size: 14px;
3321
+ line-height: 1;
3322
+ `;
3303
3323
  var ContentStack = import_styled7.default.div`
3304
3324
  display: flex;
3305
3325
  flex-direction: column;
@@ -6558,6 +6578,7 @@ function useFloating2(options) {
6558
6578
  var createUserMessage = ({
6559
6579
  sessionId,
6560
6580
  content,
6581
+ skills,
6561
6582
  attachments,
6562
6583
  localOnly,
6563
6584
  createdAt,
@@ -6567,6 +6588,7 @@ var createUserMessage = ({
6567
6588
  sessionId,
6568
6589
  role: "user",
6569
6590
  content,
6591
+ skills,
6570
6592
  attachments,
6571
6593
  localOnly,
6572
6594
  createdAt
@@ -6586,16 +6608,20 @@ var createAssistantStreamingMessage = ({
6586
6608
  var canSendChatMessage = ({
6587
6609
  value,
6588
6610
  attachmentCount = 0,
6611
+ skillCount = 0,
6589
6612
  isModelsLoading,
6590
6613
  isModelsError,
6591
6614
  hasModels
6592
6615
  }) => {
6593
6616
  const hasText = Boolean(value.trim());
6594
6617
  const hasAttachments = attachmentCount > 0;
6595
- if (!hasText && !hasAttachments)
6618
+ const hasSkills = skillCount > 0;
6619
+ if (!hasText && !hasAttachments && !hasSkills)
6596
6620
  return false;
6597
6621
  if (!hasText && hasAttachments)
6598
6622
  return true;
6623
+ if (!hasText && !hasAttachments && hasSkills)
6624
+ return !isModelsLoading && !isModelsError && hasModels;
6599
6625
  return !isModelsLoading && !isModelsError && hasModels;
6600
6626
  };
6601
6627
  var shouldSubmitChatComposer = ({
@@ -6891,6 +6917,7 @@ var useChatComposer = () => {
6891
6917
  const abortControllerBySessionRef = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
6892
6918
  const stopRequestBySessionRef = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
6893
6919
  const lastRequestBySessionRef = (0, import_react15.useRef)(/* @__PURE__ */ new Map());
6920
+ const previousActiveSessionIdRef = (0, import_react15.useRef)(activeSessionId);
6894
6921
  (0, import_react15.useEffect)(() => {
6895
6922
  setSelectedModel(
6896
6923
  (current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
@@ -6903,6 +6930,12 @@ var useChatComposer = () => {
6903
6930
  }
6904
6931
  setSelectedModeLocal(preferredMode ?? DEFAULT_CHAT_AGENT_MODE);
6905
6932
  }, [activeSession, preferredMode]);
6933
+ (0, import_react15.useEffect)(() => {
6934
+ if (previousActiveSessionIdRef.current !== activeSessionId) {
6935
+ setSelectedSkills([]);
6936
+ previousActiveSessionIdRef.current = activeSessionId;
6937
+ }
6938
+ }, [activeSessionId]);
6906
6939
  (0, import_react15.useEffect)(() => {
6907
6940
  if (!attachmentNotice)
6908
6941
  return;
@@ -7094,6 +7127,7 @@ var useChatComposer = () => {
7094
7127
  if (!canSendChatMessage({
7095
7128
  value: content,
7096
7129
  attachmentCount: composerAttachmentCount,
7130
+ skillCount: messageSkills?.length ?? 0,
7097
7131
  isModelsLoading,
7098
7132
  isModelsError,
7099
7133
  hasModels
@@ -7121,6 +7155,7 @@ var useChatComposer = () => {
7121
7155
  const userMessage = createUserMessage({
7122
7156
  sessionId: localSessionId,
7123
7157
  content,
7158
+ skills: messageSkills,
7124
7159
  attachments: messageAttachments,
7125
7160
  localOnly: false,
7126
7161
  createdAt: nowIso(),
@@ -7163,6 +7198,19 @@ var useChatComposer = () => {
7163
7198
  store
7164
7199
  ]
7165
7200
  );
7201
+ const openSkillPicker = (0, import_react15.useCallback)(() => {
7202
+ setValue((current) => {
7203
+ const matchedSkillQuery = current.match(/(^|\s)\/([^\s/]*)$/);
7204
+ if (matchedSkillQuery && matchedSkillQuery.index !== void 0) {
7205
+ const queryStart = matchedSkillQuery.index + matchedSkillQuery[1].length;
7206
+ return current.slice(0, queryStart).replace(/\s+$/, "");
7207
+ }
7208
+ if (!current.trim()) {
7209
+ return "/";
7210
+ }
7211
+ return /\s$/.test(current) ? `${current}/` : `${current} /`;
7212
+ });
7213
+ }, []);
7166
7214
  const stopSession = (0, import_react15.useCallback)(
7167
7215
  async (sessionId) => {
7168
7216
  const storeState = store.getState();
@@ -7237,6 +7285,7 @@ var useChatComposer = () => {
7237
7285
  removeSkill: (skill) => {
7238
7286
  setSelectedSkills((current) => current.filter((item) => item !== skill));
7239
7287
  },
7288
+ openSkillPicker,
7240
7289
  setSelectedModel,
7241
7290
  setSelectedMode: (mode) => {
7242
7291
  setSelectedModeLocal(mode);
@@ -7431,6 +7480,33 @@ var CloseGlyph = import_styled10.default.span`
7431
7480
  var import_styled11 = __toESM(require("@emotion/styled"));
7432
7481
  var import_compass_ui = require("@xinghunm/compass-ui");
7433
7482
  var import_jsx_runtime13 = require("@emotion/react/jsx-runtime");
7483
+ var ModelIcon = () => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7484
+ "svg",
7485
+ {
7486
+ "aria-hidden": "true",
7487
+ width: "14",
7488
+ height: "14",
7489
+ viewBox: "0 0 14 14",
7490
+ fill: "none",
7491
+ style: { display: "block" },
7492
+ xmlns: "http://www.w3.org/2000/svg",
7493
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
7494
+ "g",
7495
+ {
7496
+ transform: "translate(1.6545 1.0182)",
7497
+ stroke: "currentColor",
7498
+ strokeLinecap: "round",
7499
+ strokeLinejoin: "round",
7500
+ strokeWidth: "1.22181818",
7501
+ children: [
7502
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("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" }),
7503
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M5.40909091 11.9636364V5.98120474" }),
7504
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("path", { d: "M0.174292929 2.98998893L5.40909091 5.98120474L10.6438889 2.98998893" })
7505
+ ]
7506
+ }
7507
+ )
7508
+ }
7509
+ );
7434
7510
  var ChatModelControl = ({
7435
7511
  selectedModel,
7436
7512
  availableModels,
@@ -7474,22 +7550,20 @@ var ChatModelControl = ({
7474
7550
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: "Loading models..." });
7475
7551
  }
7476
7552
  if (hasModels && selectedModel) {
7477
- if (availableModels.length > 1) {
7478
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7479
- ModelSelect,
7480
- {
7481
- "data-testid": "chat-model-select",
7482
- "aria-label": "Select model",
7483
- value: selectedModel,
7484
- onChange: (value) => onSelectedModelChange(String(value)),
7485
- options: availableModels.map((model) => ({
7486
- label: model.id,
7487
- value: model.id
7488
- }))
7489
- }
7490
- );
7491
- }
7492
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: selectedModel });
7553
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
7554
+ ModelSelect,
7555
+ {
7556
+ "data-testid": "chat-model-select",
7557
+ "aria-label": "Select model",
7558
+ value: selectedModel,
7559
+ onChange: (value) => onSelectedModelChange(String(value)),
7560
+ options: availableModels.map((model) => ({
7561
+ label: model.id,
7562
+ value: model.id
7563
+ })),
7564
+ labelRender: () => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelIcon, {})
7565
+ }
7566
+ );
7493
7567
  }
7494
7568
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ModelBadge, { children: "No model available" });
7495
7569
  };
@@ -7532,20 +7606,55 @@ var ReloadIcon = import_styled11.default.svg`
7532
7606
  `;
7533
7607
  var ModelSelect = (0, import_styled11.default)(import_compass_ui.Select)`
7534
7608
  && {
7535
- width: auto;
7609
+ width: 24px;
7610
+ min-width: 24px;
7611
+ max-width: 24px;
7612
+ flex: 0 0 24px;
7613
+ }
7614
+
7615
+ && .compass-select-selector {
7616
+ justify-content: center;
7617
+ border-radius: 999px;
7618
+ min-height: 24px;
7619
+ height: 24px;
7620
+ width: 24px;
7621
+ padding: 0;
7622
+ background: rgba(255, 255, 255, 0.06);
7623
+ border-color: rgba(255, 255, 255, 0.14);
7624
+ color: #c5c1ba;
7625
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
7626
+ margin-right: 0;
7627
+ }
7628
+
7629
+ && .compass-select-selector:hover {
7630
+ background: rgba(255, 255, 255, 0.1);
7631
+ border-color: rgba(255, 255, 255, 0.2);
7632
+ }
7633
+
7634
+ && .compass-select-selection-item {
7635
+ display: flex;
7636
+ width: 100%;
7637
+ margin-right: 0;
7536
7638
  min-width: 0;
7537
- max-width: 100%;
7639
+ min-height: 24px;
7640
+ align-items: center;
7641
+ justify-content: center;
7642
+ }
7538
7643
 
7539
- .compass-select-selector {
7540
- line-height: 1;
7541
- border-radius: 999px;
7542
- min-height: 24px;
7543
- height: 24px;
7544
- background: rgba(255, 255, 255, 0.04);
7545
- border-color: rgba(255, 255, 255, 0.12);
7546
- color: rgba(255, 255, 255, 0.82);
7547
- padding: 4px 12px;
7548
- }
7644
+ && .compass-select-selector > div:first-of-type {
7645
+ display: flex;
7646
+ width: 100%;
7647
+ align-items: center;
7648
+ justify-content: center;
7649
+ margin-right: 0;
7650
+ }
7651
+
7652
+ && .compass-select-selection-item svg {
7653
+ display: block;
7654
+ }
7655
+
7656
+ && .compass-select-selector > span:last-child {
7657
+ display: none;
7549
7658
  }
7550
7659
  `;
7551
7660
 
@@ -7576,21 +7685,27 @@ var ChatModeControl = ({
7576
7685
  };
7577
7686
  var ModeSelect = (0, import_styled12.default)(import_compass_ui2.Select)`
7578
7687
  && {
7579
- flex: 0 1 auto;
7580
- width: auto;
7581
- min-width: 0;
7582
- max-width: 100%;
7688
+ flex: 0 0 auto;
7689
+ width: fit-content;
7690
+ min-width: fit-content;
7691
+ max-width: none;
7692
+ }
7583
7693
 
7584
- .compass-select-selector {
7585
- line-height: 1;
7586
- border-radius: 999px;
7587
- min-height: 24px;
7588
- height: 24px;
7589
- background: rgba(255, 255, 255, 0.04);
7590
- border-color: rgba(255, 255, 255, 0.12);
7591
- color: rgba(255, 255, 255, 0.82);
7592
- padding: 4px 12px;
7593
- }
7694
+ && .compass-select-selector {
7695
+ width: fit-content;
7696
+ line-height: 1;
7697
+ border-radius: 999px;
7698
+ min-height: 24px;
7699
+ height: 24px;
7700
+ background: rgba(255, 255, 255, 0.04);
7701
+ border-color: rgba(255, 255, 255, 0.12);
7702
+ color: rgba(255, 255, 255, 0.82);
7703
+ padding: 4px 12px;
7704
+ }
7705
+
7706
+ && .compass-select-selector > div:first-of-type {
7707
+ width: auto;
7708
+ min-width: max-content;
7594
7709
  }
7595
7710
  `;
7596
7711
 
@@ -7765,6 +7880,31 @@ var PlusIcon = () => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7765
7880
  children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M8 3v10M3 8h10", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" })
7766
7881
  }
7767
7882
  );
7883
+ var SkillIcon = () => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7884
+ "svg",
7885
+ {
7886
+ "aria-hidden": "true",
7887
+ width: "14",
7888
+ height: "14",
7889
+ viewBox: "0 0 14 14",
7890
+ fill: "none",
7891
+ xmlns: "http://www.w3.org/2000/svg",
7892
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(
7893
+ "g",
7894
+ {
7895
+ transform: "translate(1.7818 2.2909)",
7896
+ stroke: "currentColor",
7897
+ strokeLinecap: "round",
7898
+ strokeLinejoin: "round",
7899
+ strokeWidth: "1.22181818",
7900
+ children: [
7901
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("path", { d: "M5.28181818 2.12121212V9.54545455" }),
7902
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("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" })
7903
+ ]
7904
+ }
7905
+ )
7906
+ }
7907
+ );
7768
7908
  var ComposerExpandIcon = ({ expanded }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
7769
7909
  "svg",
7770
7910
  {
@@ -7848,6 +7988,7 @@ var ChatComposerView = ({
7848
7988
  onSelectedModelChange,
7849
7989
  onSelectedModeChange,
7850
7990
  onReloadModels,
7991
+ onOpenSkillPicker,
7851
7992
  onStop,
7852
7993
  onSend
7853
7994
  }) => {
@@ -7862,6 +8003,7 @@ var ChatComposerView = ({
7862
8003
  const canSend = canSendChatMessage({
7863
8004
  value,
7864
8005
  attachmentCount: attachments.length,
8006
+ skillCount: selectedSkills.length,
7865
8007
  isModelsLoading,
7866
8008
  isModelsError,
7867
8009
  hasModels
@@ -7882,7 +8024,7 @@ var ChatComposerView = ({
7882
8024
  open: showSkillMenu,
7883
8025
  placement: "bottom-start",
7884
8026
  middleware: [
7885
- offset3(8),
8027
+ offset3(3),
7886
8028
  flip3({ padding: 8 }),
7887
8029
  shift3({ padding: 8 }),
7888
8030
  size3({
@@ -7988,6 +8130,12 @@ var ChatComposerView = ({
7988
8130
  event.preventDefault();
7989
8131
  onPasteImages(imageFiles);
7990
8132
  };
8133
+ const handleOpenSkillPicker = () => {
8134
+ onOpenSkillPicker();
8135
+ requestAnimationFrame(() => {
8136
+ inputRef.current?.focus();
8137
+ });
8138
+ };
7991
8139
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Container2, { children: [
7992
8140
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Surface, { "data-testid": "chat-composer-surface", children: [
7993
8141
  enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
@@ -8010,7 +8158,7 @@ var ChatComposerView = ({
8010
8158
  }
8011
8159
  ),
8012
8160
  attachmentNotice === "limit_reached" ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AttachmentNotice, { "data-testid": "chat-composer-attachment-notice", children: attachmentLimitNotice }) : null,
8013
- selectedSkills.length ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SkillTagList, { "data-testid": "chat-composer-skill-tags", children: selectedSkills.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(SkillTag, { children: [
8161
+ selectedSkills.length ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SkillTagList2, { "data-testid": "chat-composer-skill-tags", children: selectedSkills.map((skill) => /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(SkillTag2, { children: [
8014
8162
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { children: skill }),
8015
8163
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8016
8164
  SkillTagRemoveButton,
@@ -8050,17 +8198,27 @@ var ChatComposerView = ({
8050
8198
  )
8051
8199
  ] }),
8052
8200
  /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(Footer, { children: [
8053
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(LeadingActions, { "data-testid": "chat-composer-leading-actions", children: enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8054
- AttachButton,
8055
- {
8056
- type: "button",
8057
- "data-testid": "chat-composer-attach-image",
8058
- "aria-label": "Attach image",
8059
- onClick: () => imageInputRef.current?.click(),
8060
- children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PlusIcon, {})
8061
- }
8062
- ) : null }),
8063
- /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(TrailingActions, { "data-testid": "chat-composer-trailing-actions", children: [
8201
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(LeadingActions, { "data-testid": "chat-composer-leading-actions", children: [
8202
+ enableImageAttachments ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8203
+ AttachButton,
8204
+ {
8205
+ type: "button",
8206
+ "data-testid": "chat-composer-attach-image",
8207
+ "aria-label": "Attach image",
8208
+ onClick: () => imageInputRef.current?.click(),
8209
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(PlusIcon, {})
8210
+ }
8211
+ ) : null,
8212
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8213
+ SkillButton,
8214
+ {
8215
+ type: "button",
8216
+ "data-testid": "chat-composer-skill-trigger",
8217
+ "aria-label": "Open skill picker",
8218
+ onClick: handleOpenSkillPicker,
8219
+ children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(SkillIcon, {})
8220
+ }
8221
+ ),
8064
8222
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8065
8223
  ChatModeControl,
8066
8224
  {
@@ -8069,7 +8227,9 @@ var ChatComposerView = ({
8069
8227
  labels: modeLabels,
8070
8228
  onChange: onSelectedModeChange
8071
8229
  }
8072
- ),
8230
+ )
8231
+ ] }),
8232
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(TrailingActions, { "data-testid": "chat-composer-trailing-actions", children: [
8073
8233
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
8074
8234
  ChatModelControl,
8075
8235
  {
@@ -8171,6 +8331,7 @@ var ChatComposer = () => {
8171
8331
  onSelectedModelChange: actions.setSelectedModel,
8172
8332
  onSelectedModeChange: actions.setSelectedMode,
8173
8333
  onReloadModels: actions.reloadModels,
8334
+ onOpenSkillPicker: actions.openSkillPicker,
8174
8335
  onStop: actions.stop,
8175
8336
  onSend: send
8176
8337
  }
@@ -8209,31 +8370,55 @@ var AttachmentNotice = import_styled14.default.div`
8209
8370
  font-size: 12px;
8210
8371
  line-height: 1.4;
8211
8372
  `;
8212
- var SkillTagList = import_styled14.default.div`
8373
+ var SkillTagList2 = import_styled14.default.div`
8213
8374
  display: flex;
8214
8375
  flex-wrap: wrap;
8215
- gap: 8px;
8216
- padding: 12px 12px 0;
8376
+ gap: 9px;
8377
+ padding: 11px 16px;
8217
8378
  `;
8218
- var SkillTag = import_styled14.default.span`
8379
+ var SkillTag2 = import_styled14.default.span`
8380
+ position: relative;
8219
8381
  display: inline-flex;
8220
8382
  align-items: center;
8221
- gap: 6px;
8222
8383
  max-width: 100%;
8223
- border-radius: 999px;
8224
- padding: 8px 12px;
8225
- background: rgba(255, 255, 255, 0.06);
8226
- color: var(--text-primary);
8227
- font-size: 13px;
8384
+ padding: 11px 16px;
8385
+ background: rgba(65, 65, 63, 0.35);
8386
+ border-radius: 18px;
8387
+ font-weight: 400;
8388
+ font-size: 14px;
8389
+ color: #c5c1ba;
8228
8390
  line-height: 1;
8391
+
8392
+ :hover {
8393
+ background: #41413f;
8394
+ }
8395
+
8396
+ &:hover > button,
8397
+ &:focus-within > button {
8398
+ opacity: 1;
8399
+ pointer-events: auto;
8400
+ }
8229
8401
  `;
8230
8402
  var SkillTagRemoveButton = import_styled14.default.button`
8403
+ position: absolute;
8404
+ top: 0;
8405
+ right: 0;
8406
+ width: 14px;
8407
+ height: 14px;
8408
+ border-radius: 50%;
8409
+ background: #000000;
8410
+ display: grid;
8411
+ place-items: center;
8231
8412
  border: none;
8232
8413
  padding: 0;
8233
- background: transparent;
8234
8414
  color: inherit;
8235
8415
  cursor: pointer;
8236
8416
  line-height: 1;
8417
+ opacity: 0;
8418
+ pointer-events: none;
8419
+ transition:
8420
+ opacity 120ms ease,
8421
+ background-color 120ms ease;
8237
8422
  `;
8238
8423
  var InputArea = import_styled14.default.div`
8239
8424
  grid-area: input;
@@ -8294,18 +8479,21 @@ var SkillMenu = import_styled14.default.div`
8294
8479
  gap: 4px;
8295
8480
  max-height: 240px;
8296
8481
  overflow-y: auto;
8297
- padding: 8px;
8298
- border: 1px solid rgba(255, 255, 255, 0.12);
8299
- border-radius: 16px;
8482
+ padding: 4px;
8483
+ border-radius: 12px;
8300
8484
  background: rgba(28, 28, 28, 0.98);
8301
8485
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.28);
8302
8486
  z-index: 1000;
8487
+ background: #1c1c1c;
8488
+ box-shadow: 0px 4px 6px 0px rgba(14, 14, 14, 0.3);
8489
+ border-radius: 12px;
8490
+ border: 1px solid #282825;
8303
8491
  `;
8304
8492
  var SkillMenuItem = import_styled14.default.button`
8305
8493
  width: 100%;
8306
8494
  border: none;
8307
- border-radius: 12px;
8308
- padding: 12px 14px;
8495
+ border-radius: 8px;
8496
+ padding: 12px;
8309
8497
  background: transparent;
8310
8498
  color: var(--text-primary);
8311
8499
  text-align: left;
@@ -8313,7 +8501,7 @@ var SkillMenuItem = import_styled14.default.button`
8313
8501
 
8314
8502
  &[data-active='true'],
8315
8503
  &:hover {
8316
- background: rgba(255, 255, 255, 0.12);
8504
+ background: #41413f;
8317
8505
  }
8318
8506
  `;
8319
8507
  var SkillMenuState = import_styled14.default.div`
@@ -8343,9 +8531,8 @@ var ComposerExpandButton = import_styled14.default.button`
8343
8531
  `;
8344
8532
  var Footer = import_styled14.default.div`
8345
8533
  grid-area: footer;
8346
- display: grid;
8347
- grid-template-columns: auto minmax(0, 1fr);
8348
- align-items: flex-end;
8534
+ display: flex;
8535
+ align-items: center;
8349
8536
  gap: 8px;
8350
8537
  padding: 0 14px 14px;
8351
8538
  `;
@@ -8353,33 +8540,72 @@ var LeadingActions = import_styled14.default.div`
8353
8540
  display: flex;
8354
8541
  align-items: center;
8355
8542
  justify-content: flex-start;
8356
- gap: 8px;
8543
+ gap: 4px;
8357
8544
  min-width: 0;
8358
8545
  `;
8359
8546
  var TrailingActions = import_styled14.default.div`
8360
8547
  display: flex;
8361
8548
  align-items: center;
8362
- flex-wrap: wrap;
8549
+ flex-wrap: nowrap;
8363
8550
  min-width: 0;
8364
- width: fit-content;
8365
- max-width: 100%;
8366
- justify-self: end;
8551
+ margin-left: auto;
8367
8552
  justify-content: flex-end;
8368
8553
  gap: 8px;
8369
8554
  `;
8370
8555
  var AttachButton = import_styled14.default.button`
8371
- width: 28px;
8372
- height: 28px;
8556
+ width: 24px;
8557
+ height: 24px;
8373
8558
  display: grid;
8374
8559
  place-items: center;
8375
- border: none;
8376
8560
  border-radius: 999px;
8377
- background: transparent;
8378
- color: rgba(255, 255, 255, 0.82);
8561
+ border: 1px solid rgba(255, 255, 255, 0.14);
8562
+ background: rgba(255, 255, 255, 0.06);
8563
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
8564
+ padding: 0;
8565
+ color: #c5c1ba;
8379
8566
  cursor: pointer;
8567
+ flex: 0 0 24px;
8568
+
8569
+ transition:
8570
+ background 160ms ease,
8571
+ border-color 160ms ease,
8572
+ color 160ms ease;
8380
8573
 
8381
8574
  &:hover {
8382
- background: rgba(255, 255, 255, 0.08);
8575
+ background: rgba(255, 255, 255, 0.1);
8576
+ border-color: rgba(255, 255, 255, 0.2);
8577
+ }
8578
+
8579
+ svg {
8580
+ display: block;
8581
+ }
8582
+ `;
8583
+ var SkillButton = import_styled14.default.button`
8584
+ width: 24px;
8585
+ height: 24px;
8586
+ display: grid;
8587
+ place-items: center;
8588
+ border-radius: 999px;
8589
+ border: 1px solid rgba(255, 255, 255, 0.14);
8590
+ background: rgba(255, 255, 255, 0.06);
8591
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
8592
+ padding: 0;
8593
+ color: #c5c1ba;
8594
+ cursor: pointer;
8595
+ flex: 0 0 24px;
8596
+
8597
+ transition:
8598
+ background 160ms ease,
8599
+ border-color 160ms ease,
8600
+ color 160ms ease;
8601
+
8602
+ &:hover {
8603
+ background: rgba(255, 255, 255, 0.1);
8604
+ border-color: rgba(255, 255, 255, 0.2);
8605
+ }
8606
+
8607
+ svg {
8608
+ display: block;
8383
8609
  }
8384
8610
  `;
8385
8611
 
@@ -8512,6 +8738,76 @@ var List2 = import_styled16.default.div`
8512
8738
 
8513
8739
  // src/components/ai-chat/index.tsx
8514
8740
  var import_jsx_runtime19 = require("@emotion/react/jsx-runtime");
8741
+ var NewTalkIcon = () => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8742
+ "svg",
8743
+ {
8744
+ "aria-hidden": "true",
8745
+ width: "16",
8746
+ height: "16",
8747
+ viewBox: "0 0 16 16",
8748
+ fill: "none",
8749
+ style: { display: "block" },
8750
+ xmlns: "http://www.w3.org/2000/svg",
8751
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
8752
+ "g",
8753
+ {
8754
+ transform: "translate(1.8909 2.0364)",
8755
+ stroke: "currentColor",
8756
+ strokeLinecap: "round",
8757
+ strokeLinejoin: "round",
8758
+ strokeWidth: "1.36533333",
8759
+ children: [
8760
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("g", { transform: "translate(9.8909 2.3273) rotate(-315) translate(-9.8909 -2.3273) translate(8.2909 0.7273)", children: [
8761
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M0 0C0 0 1.06666667 1.06666667 3.2 3.2" }),
8762
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("path", { d: "M3.2 0C3.2 0 2.13333333 1.06666667 0 3.2" })
8763
+ ] }),
8764
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("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" })
8765
+ ]
8766
+ }
8767
+ )
8768
+ }
8769
+ );
8770
+ var hasStartedConversation = ({
8771
+ activeSessionId,
8772
+ messagesBySession,
8773
+ streamingMessageBySession,
8774
+ errorBySession
8775
+ }) => {
8776
+ if (!activeSessionId) {
8777
+ return false;
8778
+ }
8779
+ return Boolean(
8780
+ (messagesBySession[activeSessionId]?.length ?? 0) > 0 || streamingMessageBySession[activeSessionId] || errorBySession[activeSessionId]
8781
+ );
8782
+ };
8783
+ var AiChatWorkspaceContent = ({
8784
+ showConversationList,
8785
+ showNewChatButton,
8786
+ renderNewChatTrigger,
8787
+ showComposerOnlyBeforeFirstMessage = false,
8788
+ onConversationStartedChange
8789
+ }) => {
8790
+ const isConversationStarted = useChatStore(
8791
+ (state) => hasStartedConversation({
8792
+ activeSessionId: state.activeSessionId,
8793
+ messagesBySession: state.messagesBySession,
8794
+ streamingMessageBySession: state.streamingMessageBySession,
8795
+ errorBySession: state.errorBySession
8796
+ })
8797
+ );
8798
+ const shouldShowComposerOnly = showComposerOnlyBeforeFirstMessage && !showConversationList && !isConversationStarted;
8799
+ (0, import_react20.useEffect)(() => {
8800
+ onConversationStartedChange?.(isConversationStarted);
8801
+ }, [isConversationStarted, onConversationStartedChange]);
8802
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Root, { "data-testid": "ai-chat", children: [
8803
+ showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatConversationList, {}) : null,
8804
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Workspace, { children: [
8805
+ showNewChatButton && !showConversationList && !shouldShowComposerOnly ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(QuickActions, { renderNewChatTrigger }) : null,
8806
+ shouldShowComposerOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatThread, {}),
8807
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatComposer, {})
8808
+ ] })
8809
+ ] });
8810
+ };
8515
8811
  var QuickActions = ({ renderNewChatTrigger }) => {
8516
8812
  const { labels, stopRef, store } = useChatContext();
8517
8813
  const startNewChat = useChatStore((state) => state.startNewChat);
@@ -8565,9 +8861,11 @@ var QuickActions = ({ renderNewChatTrigger }) => {
8565
8861
  {
8566
8862
  type: "button",
8567
8863
  "data-testid": "chat-start-new-session",
8864
+ "aria-label": labels.newChat,
8865
+ title: labels.newChat,
8568
8866
  onClick: () => void handleStartNewChat(),
8569
8867
  disabled: isActiveSessionStopping,
8570
- children: labels.newChat
8868
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(NewTalkIcon, {})
8571
8869
  }
8572
8870
  ) });
8573
8871
  };
@@ -8579,6 +8877,8 @@ var AiChat = ({
8579
8877
  showConversationList = false,
8580
8878
  showNewChatButton = false,
8581
8879
  renderNewChatTrigger,
8880
+ showComposerOnlyBeforeFirstMessage = false,
8881
+ onConversationStartedChange,
8582
8882
  ...providerProps
8583
8883
  }) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8584
8884
  import_compass_ui4.ConfigProvider,
@@ -8615,14 +8915,16 @@ var AiChat = ({
8615
8915
  }
8616
8916
  }
8617
8917
  },
8618
- children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Root, { "data-testid": "ai-chat", children: [
8619
- showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatConversationList, {}) : null,
8620
- /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Workspace, { children: [
8621
- showNewChatButton && !showConversationList ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(QuickActions, { renderNewChatTrigger }) : null,
8622
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatThread, {}),
8623
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChatComposer, {})
8624
- ] })
8625
- ] }) })
8918
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(AiChatProvider, { ...providerProps, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
8919
+ AiChatWorkspaceContent,
8920
+ {
8921
+ showConversationList,
8922
+ showNewChatButton,
8923
+ renderNewChatTrigger,
8924
+ showComposerOnlyBeforeFirstMessage,
8925
+ onConversationStartedChange
8926
+ }
8927
+ ) })
8626
8928
  }
8627
8929
  );
8628
8930
  var Root = import_styled17.default.div`
@@ -8646,12 +8948,26 @@ var QuickActionsRow = import_styled17.default.div`
8646
8948
  padding: 12px 12px 0;
8647
8949
  `;
8648
8950
  var QuickActionButton = import_styled17.default.button`
8649
- border: none;
8650
- border-radius: 12px;
8651
- padding: 10px 14px;
8951
+ width: 48px;
8952
+ height: 48px;
8953
+ display: grid;
8954
+ place-items: center;
8955
+ padding: 0;
8956
+ border: 1px solid rgba(255, 255, 255, 0.14);
8957
+ border-radius: 999px;
8652
8958
  background: rgba(255, 255, 255, 0.08);
8959
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
8653
8960
  color: var(--text-primary, #fcfbf8);
8654
8961
  cursor: pointer;
8962
+ transition:
8963
+ background 160ms ease,
8964
+ border-color 160ms ease,
8965
+ color 160ms ease;
8966
+
8967
+ &:hover:not(:disabled) {
8968
+ background: rgba(255, 255, 255, 0.12);
8969
+ border-color: rgba(255, 255, 255, 0.2);
8970
+ }
8655
8971
 
8656
8972
  &:disabled {
8657
8973
  opacity: 0.5;