@nextclaw/agent-chat-ui 0.3.10 → 0.3.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +187 -28
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -17,6 +17,19 @@ import { $applyNodeReplacement, $createLineBreakNode, $createParagraphNode, $cre
17
17
  import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
18
18
  import ReactMarkdown from "react-markdown";
19
19
  import remarkGfm from "remark-gfm";
20
+ import hljs from "highlight.js/lib/core";
21
+ import bash from "highlight.js/lib/languages/bash";
22
+ import css from "highlight.js/lib/languages/css";
23
+ import diff from "highlight.js/lib/languages/diff";
24
+ import javascript from "highlight.js/lib/languages/javascript";
25
+ import json from "highlight.js/lib/languages/json";
26
+ import markdown from "highlight.js/lib/languages/markdown";
27
+ import python from "highlight.js/lib/languages/python";
28
+ import shell from "highlight.js/lib/languages/shell";
29
+ import sql from "highlight.js/lib/languages/sql";
30
+ import typescript from "highlight.js/lib/languages/typescript";
31
+ import xml from "highlight.js/lib/languages/xml";
32
+ import yaml from "highlight.js/lib/languages/yaml";
20
33
  //#region src/components/chat/hooks/use-active-item-scroll.ts
21
34
  const defaultGetItemSelector = (index) => `[data-item-index="${index}"]`;
22
35
  function useActiveItemScroll(params) {
@@ -632,24 +645,23 @@ function ChatInputBarSkillPicker(props) {
632
645
  //#endregion
633
646
  //#region src/components/chat/ui/chat-input-bar/chat-input-bar-toolbar.tsx
634
647
  function ToolbarIcon({ icon }) {
635
- if (icon === "sparkles") return /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 shrink-0 text-primary" });
636
- if (icon === "brain") return /* @__PURE__ */ jsx(Brain, { className: "h-3.5 w-3.5 shrink-0 text-gray-500" });
637
- return null;
648
+ return icon === "sparkles" ? /* @__PURE__ */ jsx(Sparkles, { className: "h-3.5 w-3.5 shrink-0 text-primary" }) : icon === "brain" ? /* @__PURE__ */ jsx(Brain, { className: "h-3.5 w-3.5 shrink-0 text-gray-500" }) : null;
638
649
  }
639
650
  function AccessoryIcon({ icon }) {
640
- if (icon === "paperclip") return /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4" });
641
- return /* @__PURE__ */ jsx(ToolbarIcon, { icon });
642
- }
643
- function resolveTriggerWidth(key) {
644
- if (key === "model") return "min-w-0 flex-1";
645
- if (key === "session-type" || key === "thinking") return "shrink-0";
646
- return "";
651
+ return icon === "paperclip" ? /* @__PURE__ */ jsx(Paperclip, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(ToolbarIcon, { icon });
647
652
  }
648
- function resolveContentWidth(key) {
649
- if (key === "model") return "w-[320px]";
650
- if (key === "session-type") return "w-[220px]";
651
- if (key === "thinking") return "w-[180px]";
652
- return "";
653
+ const TRIGGER_WIDTH_BY_KEY = {
654
+ model: "min-w-0 flex-1 sm:min-w-[220px] sm:flex-none",
655
+ "session-type": "shrink-0",
656
+ thinking: "shrink-0"
657
+ };
658
+ const CONTENT_WIDTH_BY_KEY = {
659
+ model: "w-[min(18rem,calc(100vw-1rem))] sm:w-[320px]",
660
+ "session-type": "w-[220px]",
661
+ thinking: "w-[180px]"
662
+ };
663
+ function resolveMobileSelectedLabel(item) {
664
+ return item.key === "model" && item.selectedLabel ? item.selectedLabel.split("/").slice(1).join("/").trim() || item.selectedLabel : item.selectedLabel;
653
665
  }
654
666
  function ToolbarSelect({ item }) {
655
667
  const { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue } = ChatUiPrimitives;
@@ -658,21 +670,29 @@ function ToolbarSelect({ item }) {
658
670
  options: item.options
659
671
  }] : []);
660
672
  const hasOptions = groups.some((group) => group.options.length > 0);
673
+ const mobileSelectedLabel = resolveMobileSelectedLabel(item);
661
674
  return /* @__PURE__ */ jsxs(Select, {
662
675
  value: item.value,
663
676
  onValueChange: item.onValueChange,
664
677
  disabled: item.disabled,
665
678
  children: [/* @__PURE__ */ jsx(SelectTrigger, {
666
- className: `h-8 w-auto rounded-lg border-0 bg-transparent px-2 text-xs font-medium text-gray-600 shadow-none hover:bg-gray-100 focus:ring-0 sm:px-3 ${resolveTriggerWidth(item.key)}`,
679
+ className: `h-8 w-auto rounded-lg border-0 bg-transparent px-2 text-xs font-medium text-gray-600 shadow-none hover:bg-gray-100 focus:ring-0 sm:px-3 ${TRIGGER_WIDTH_BY_KEY[item.key] ?? ""}`,
667
680
  children: item.selectedLabel ? /* @__PURE__ */ jsxs("div", {
668
681
  className: "flex min-w-0 items-center gap-2 text-left",
669
- children: [/* @__PURE__ */ jsx(ToolbarIcon, { icon: item.icon }), /* @__PURE__ */ jsx("span", {
670
- className: "truncate text-xs font-semibold text-gray-700",
671
- children: item.selectedLabel
672
- })]
682
+ children: [
683
+ /* @__PURE__ */ jsx(ToolbarIcon, { icon: item.icon }),
684
+ /* @__PURE__ */ jsx("span", {
685
+ className: "truncate text-xs font-semibold text-gray-700 sm:hidden",
686
+ children: mobileSelectedLabel
687
+ }),
688
+ /* @__PURE__ */ jsx("span", {
689
+ className: "hidden truncate text-xs font-semibold text-gray-700 sm:inline",
690
+ children: item.selectedLabel
691
+ })
692
+ ]
673
693
  }) : item.loading ? /* @__PURE__ */ jsx("div", { className: "h-3 w-24 animate-pulse rounded bg-gray-200" }) : /* @__PURE__ */ jsx(SelectValue, { placeholder: item.placeholder })
674
694
  }), /* @__PURE__ */ jsxs(SelectContent, {
675
- className: resolveContentWidth(item.key),
695
+ className: CONTENT_WIDTH_BY_KEY[item.key] ?? "",
676
696
  children: [!hasOptions ? item.loading ? /* @__PURE__ */ jsxs("div", {
677
697
  className: "space-y-2 px-3 py-2",
678
698
  children: [
@@ -2018,6 +2038,139 @@ function useCopyFeedback(params) {
2018
2038
  };
2019
2039
  }
2020
2040
  //#endregion
2041
+ //#region src/components/chat/ui/chat-message-list/code-block/chat-code-syntax-highlighter.ts
2042
+ const PLAIN_TEXT_LANGUAGES = new Set([
2043
+ "text",
2044
+ "txt",
2045
+ "plain",
2046
+ "plaintext",
2047
+ "none",
2048
+ "output"
2049
+ ]);
2050
+ const AUTO_DETECT_LANGUAGES = [
2051
+ "typescript",
2052
+ "javascript",
2053
+ "json",
2054
+ "bash",
2055
+ "shell",
2056
+ "python",
2057
+ "css",
2058
+ "xml",
2059
+ "markdown",
2060
+ "yaml",
2061
+ "sql",
2062
+ "diff"
2063
+ ];
2064
+ const AUTO_DETECT_MIN_RELEVANCE = 4;
2065
+ const REGISTERED_LANGUAGES = [
2066
+ {
2067
+ name: "typescript",
2068
+ language: typescript,
2069
+ aliases: ["ts", "tsx"]
2070
+ },
2071
+ {
2072
+ name: "javascript",
2073
+ language: javascript,
2074
+ aliases: [
2075
+ "js",
2076
+ "jsx",
2077
+ "mjs",
2078
+ "cjs"
2079
+ ]
2080
+ },
2081
+ {
2082
+ name: "json",
2083
+ language: json,
2084
+ aliases: ["jsonc"]
2085
+ },
2086
+ {
2087
+ name: "bash",
2088
+ language: bash,
2089
+ aliases: ["sh", "zsh"]
2090
+ },
2091
+ {
2092
+ name: "shell",
2093
+ language: shell,
2094
+ aliases: ["console", "terminal"]
2095
+ },
2096
+ {
2097
+ name: "python",
2098
+ language: python,
2099
+ aliases: ["py"]
2100
+ },
2101
+ {
2102
+ name: "css",
2103
+ language: css
2104
+ },
2105
+ {
2106
+ name: "xml",
2107
+ language: xml,
2108
+ aliases: ["html", "svg"]
2109
+ },
2110
+ {
2111
+ name: "markdown",
2112
+ language: markdown,
2113
+ aliases: ["md", "mdx"]
2114
+ },
2115
+ {
2116
+ name: "yaml",
2117
+ language: yaml,
2118
+ aliases: ["yml"]
2119
+ },
2120
+ {
2121
+ name: "sql",
2122
+ language: sql
2123
+ },
2124
+ {
2125
+ name: "diff",
2126
+ language: diff,
2127
+ aliases: ["patch"]
2128
+ }
2129
+ ];
2130
+ function escapeHtml(value) {
2131
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2132
+ }
2133
+ function normalizeLanguage(value) {
2134
+ return value.trim().toLowerCase();
2135
+ }
2136
+ var ChatCodeSyntaxHighlighter = class {
2137
+ constructor() {
2138
+ this.highlight = (code, language) => {
2139
+ const normalizedLanguage = normalizeLanguage(language);
2140
+ if (!code || PLAIN_TEXT_LANGUAGES.has(normalizedLanguage)) return this.createPlainTextResult(code, normalizedLanguage || "text");
2141
+ if (this.engine.getLanguage(normalizedLanguage)) {
2142
+ const result = this.engine.highlight(code, {
2143
+ language: normalizedLanguage,
2144
+ ignoreIllegals: true
2145
+ });
2146
+ return {
2147
+ html: result.value,
2148
+ language: result.language ?? normalizedLanguage,
2149
+ highlighted: true
2150
+ };
2151
+ }
2152
+ const autoDetected = this.engine.highlightAuto(code, AUTO_DETECT_LANGUAGES);
2153
+ if (autoDetected.language && autoDetected.relevance >= AUTO_DETECT_MIN_RELEVANCE) return {
2154
+ html: autoDetected.value,
2155
+ language: autoDetected.language,
2156
+ highlighted: true
2157
+ };
2158
+ return this.createPlainTextResult(code, normalizedLanguage || "text");
2159
+ };
2160
+ this.createPlainTextResult = (code, language) => ({
2161
+ html: escapeHtml(code),
2162
+ language,
2163
+ highlighted: false
2164
+ });
2165
+ this.engine = hljs.newInstance();
2166
+ REGISTERED_LANGUAGES.forEach((entry) => {
2167
+ this.engine.registerLanguage(entry.name, entry.language);
2168
+ if (entry.aliases) this.engine.registerAliases(entry.aliases, { languageName: entry.name });
2169
+ });
2170
+ }
2171
+ };
2172
+ const chatCodeSyntaxHighlighter = new ChatCodeSyntaxHighlighter();
2173
+ //#endregion
2021
2174
  //#region src/components/chat/ui/chat-message-list/chat-code-block.tsx
2022
2175
  const CODE_LANGUAGE_REGEX = /language-([a-z0-9-]+)/i;
2023
2176
  function flattenNodeText(value) {
@@ -2032,9 +2185,14 @@ function normalizeCodeText(value) {
2032
2185
  function resolveCodeLanguage(className) {
2033
2186
  return (className ? CODE_LANGUAGE_REGEX.exec(className) : null)?.[1]?.toLowerCase() || "text";
2034
2187
  }
2035
- function ChatCodeBlock(props) {
2036
- const language = useMemo(() => resolveCodeLanguage(props.className), [props.className]);
2037
- const codeText = useMemo(() => normalizeCodeText(props.children), [props.children]);
2188
+ function ChatCodeBlock({ className, children, texts }) {
2189
+ const language = useMemo(() => resolveCodeLanguage(className), [className]);
2190
+ const codeText = useMemo(() => normalizeCodeText(children), [children]);
2191
+ const highlightedCode = useMemo(() => chatCodeSyntaxHighlighter.highlight(codeText, language), [codeText, language]);
2192
+ const codeClassName = useMemo(() => {
2193
+ const highlightedLanguageClass = `language-${highlightedCode.language}`;
2194
+ return cn("hljs", className, className?.includes("language-") ? void 0 : highlightedLanguageClass);
2195
+ }, [className, highlightedCode.language]);
2038
2196
  const { copied, copy } = useCopyFeedback({ text: codeText });
2039
2197
  return /* @__PURE__ */ jsxs("div", {
2040
2198
  className: "chat-codeblock",
@@ -2047,12 +2205,13 @@ function ChatCodeBlock(props) {
2047
2205
  type: "button",
2048
2206
  className: "chat-codeblock-copy",
2049
2207
  onClick: () => void copy(),
2050
- "aria-label": copied ? props.texts.copiedCodeLabel : props.texts.copyCodeLabel,
2051
- children: [copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ jsx("span", { children: copied ? props.texts.copiedCodeLabel : props.texts.copyCodeLabel })]
2208
+ "aria-label": copied ? texts.copiedCodeLabel : texts.copyCodeLabel,
2209
+ children: [copied ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" }), /* @__PURE__ */ jsx("span", { children: copied ? texts.copiedCodeLabel : texts.copyCodeLabel })]
2052
2210
  })]
2053
2211
  }), /* @__PURE__ */ jsx("pre", { children: /* @__PURE__ */ jsx("code", {
2054
- className: props.className,
2055
- children: codeText
2212
+ className: codeClassName,
2213
+ "data-highlighted": highlightedCode.highlighted ? "true" : "false",
2214
+ dangerouslySetInnerHTML: { __html: highlightedCode.html }
2056
2215
  }) })]
2057
2216
  });
2058
2217
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/agent-chat-ui",
3
- "version": "0.3.10",
3
+ "version": "0.3.11",
4
4
  "private": false,
5
5
  "description": "Reusable Nextclaw agent chat UI primitives and default skin.",
6
6
  "type": "module",
@@ -39,6 +39,7 @@
39
39
  "@radix-ui/react-tooltip": "^1.1.11",
40
40
  "class-variance-authority": "^0.7.1",
41
41
  "clsx": "^2.1.1",
42
+ "highlight.js": "^11.11.1",
42
43
  "lexical": "^0.43.0",
43
44
  "lucide-react": "^0.462.0",
44
45
  "react-markdown": "^10.1.0",