@agents24/chat-react 0.4.0 → 0.5.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.
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var templates_exports = {};
32
32
  __export(templates_exports, {
33
33
  AgentChatApp: () => AgentChatApp,
34
+ AgentChatAppearanceControl: () => AgentChatAppearanceControl,
34
35
  AgentChatLayout: () => AgentChatLayout,
35
36
  AgentChatShell: () => AgentChatShell,
36
37
  AgentChatStatePanel: () => AgentChatStatePanel,
@@ -1449,7 +1450,7 @@ var MessageResponse = React5.memo(
1449
1450
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1450
1451
  import_streamdown.Streamdown,
1451
1452
  {
1452
- className: cn("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className),
1453
+ className: cn("a24-message-response", className),
1453
1454
  components: streamdownComponents,
1454
1455
  rehypePlugins: rehypePlugins || MESSAGE_RESPONSE_REHYPE_PLUGINS,
1455
1456
  ...props,
@@ -1927,8 +1928,84 @@ function connectionLabel(state) {
1927
1928
  function AgentAvatar({ agent }) {
1928
1929
  return agent.avatarUrl ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { alt: "", className: "a24-avatar", height: 36, src: agent.avatarUrl, width: 36 }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { "aria-hidden": "true", className: "a24-avatar a24-avatar--fallback", children: agent.name.trim().slice(0, 1).toUpperCase() || "A" });
1929
1930
  }
1931
+ function AgentTargetSelector({ agent, selector }) {
1932
+ const rootRef = React8.useRef(null);
1933
+ const triggerRef = React8.useRef(null);
1934
+ const inputId = React8.useId();
1935
+ const [draft, setDraft] = React8.useState(selector.value);
1936
+ const [error, setError] = React8.useState(null);
1937
+ const [open, setOpen] = React8.useState(false);
1938
+ const [submitting, setSubmitting] = React8.useState(false);
1939
+ React8.useEffect(() => setDraft(selector.value), [selector.value]);
1940
+ React8.useEffect(() => {
1941
+ if (!open) return void 0;
1942
+ const closeOnPointerDown = (event) => {
1943
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
1944
+ };
1945
+ const closeOnEscape = (event) => {
1946
+ if (event.key !== "Escape") return;
1947
+ event.preventDefault();
1948
+ setOpen(false);
1949
+ triggerRef.current?.focus();
1950
+ };
1951
+ document.addEventListener("pointerdown", closeOnPointerDown);
1952
+ document.addEventListener("keydown", closeOnEscape);
1953
+ return () => {
1954
+ document.removeEventListener("pointerdown", closeOnPointerDown);
1955
+ document.removeEventListener("keydown", closeOnEscape);
1956
+ };
1957
+ }, [open]);
1958
+ const apply = async (event) => {
1959
+ event.preventDefault();
1960
+ const value = draft.trim();
1961
+ if (!value) {
1962
+ setError(`${selector.label} is required.`);
1963
+ return;
1964
+ }
1965
+ setSubmitting(true);
1966
+ setError(null);
1967
+ try {
1968
+ await selector.onApply(value);
1969
+ setOpen(false);
1970
+ } catch (nextError) {
1971
+ setError(nextError instanceof Error ? nextError.message : "The target could not be changed.");
1972
+ } finally {
1973
+ setSubmitting(false);
1974
+ }
1975
+ };
1976
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-target-selector", "data-open": open || void 0, ref: rootRef, children: [
1977
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1978
+ "button",
1979
+ {
1980
+ "aria-expanded": open,
1981
+ "aria-haspopup": "dialog",
1982
+ "aria-label": `Change ${selector.label}`,
1983
+ className: "a24-target-selector__trigger",
1984
+ onClick: () => setOpen((value) => !value),
1985
+ ref: triggerRef,
1986
+ type: "button",
1987
+ children: [
1988
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentAvatar, { agent }),
1989
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("span", { className: "a24-target-selector__copy", children: [
1990
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("strong", { children: agent.name }),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: agent.description || "Click to change target" })
1992
+ ] }),
1993
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react5.ChevronDown, { "aria-hidden": "true" })
1994
+ ]
1995
+ }
1996
+ ),
1997
+ open ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("form", { "aria-label": `Change ${selector.label}`, className: "a24-target-selector__panel", onSubmit: (event) => void apply(event), children: [
1998
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("label", { htmlFor: inputId, children: selector.label }),
1999
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("input", { autoComplete: "off", id: inputId, onChange: (event) => setDraft(event.target.value), placeholder: selector.placeholder, spellCheck: false, value: draft }),
2000
+ selector.helperText ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { children: selector.helperText }) : null,
2001
+ error ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "a24-target-selector__error", role: "alert", children: error }) : null,
2002
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { className: "a24-button a24-target-selector__apply", disabled: submitting || !draft.trim(), type: "submit", children: submitting ? "Switching\u2026" : "Use target" })
2003
+ ] }) : null
2004
+ ] });
2005
+ }
1930
2006
  function ThreadNavigation({
1931
2007
  activeThreadId,
2008
+ agent,
1932
2009
  close,
1933
2010
  hasMoreThreads,
1934
2011
  isDeletingThread,
@@ -1938,12 +2015,14 @@ function ThreadNavigation({
1938
2015
  onNewThread,
1939
2016
  onOpenThread,
1940
2017
  slots,
2018
+ targetSelector,
1941
2019
  threads
1942
2020
  }) {
1943
2021
  const [query, setQuery] = React8.useState("");
1944
2022
  const normalizedQuery = query.trim().toLocaleLowerCase();
1945
2023
  const visibleThreads = normalizedQuery ? threads.filter((thread) => String(thread.title || "New chat").toLocaleLowerCase().includes(normalizedQuery)) : threads;
1946
2024
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-sidebar__inner", children: [
2025
+ targetSelector ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentTargetSelector, { agent, selector: targetSelector }) : null,
1947
2026
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "a24-sidebar__top", children: [
1948
2027
  /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
1949
2028
  "button",
@@ -2050,6 +2129,7 @@ function AgentChatLayout({
2050
2129
  slots,
2051
2130
  state = "ready",
2052
2131
  streamingMessageId,
2132
+ targetSelector,
2053
2133
  threads,
2054
2134
  title
2055
2135
  }) {
@@ -2094,6 +2174,7 @@ function AgentChatLayout({
2094
2174
  ThreadNavigation,
2095
2175
  {
2096
2176
  activeThreadId,
2177
+ agent,
2097
2178
  close: closeSidebar,
2098
2179
  hasMoreThreads,
2099
2180
  isDeletingThread,
@@ -2103,6 +2184,7 @@ function AgentChatLayout({
2103
2184
  onNewThread,
2104
2185
  onOpenThread,
2105
2186
  slots,
2187
+ targetSelector,
2106
2188
  threads
2107
2189
  }
2108
2190
  ) }),
@@ -2464,9 +2546,51 @@ function AgentChatApp({
2464
2546
  }
2465
2547
  );
2466
2548
  }
2549
+
2550
+ // src/templates/appearance-control.tsx
2551
+ var import_lucide_react6 = require("lucide-react");
2552
+ var React10 = __toESM(require("react"), 1);
2553
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2554
+ var MODES = ["system", "light", "dark"];
2555
+ function storedMode(storageKey) {
2556
+ if (typeof window === "undefined") return "system";
2557
+ const value = window.localStorage.getItem(storageKey);
2558
+ return value === "light" || value === "dark" ? value : "system";
2559
+ }
2560
+ function applyMode(mode) {
2561
+ if (typeof document === "undefined") return;
2562
+ document.documentElement.dataset.agents24ColorMode = mode;
2563
+ }
2564
+ function AgentChatAppearanceControl({
2565
+ storageKey = "agents24-color-mode"
2566
+ }) {
2567
+ const [mode, setMode] = React10.useState(() => storedMode(storageKey));
2568
+ React10.useEffect(() => applyMode(mode), [mode]);
2569
+ const label = `Appearance: ${mode[0]?.toUpperCase()}${mode.slice(1)}`;
2570
+ const Icon = mode === "light" ? import_lucide_react6.Sun : mode === "dark" ? import_lucide_react6.Moon : import_lucide_react6.Monitor;
2571
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
2572
+ "button",
2573
+ {
2574
+ "aria-label": `${label}. Activate to change.`,
2575
+ className: "a24-icon-button a24-appearance",
2576
+ onClick: () => {
2577
+ const next = MODES[(MODES.indexOf(mode) + 1) % MODES.length] || "system";
2578
+ window.localStorage.setItem(storageKey, next);
2579
+ setMode(next);
2580
+ },
2581
+ title: label,
2582
+ type: "button",
2583
+ children: [
2584
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Icon, { "aria-hidden": "true" }),
2585
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "a24-sr-only", children: label })
2586
+ ]
2587
+ }
2588
+ );
2589
+ }
2467
2590
  // Annotate the CommonJS export names for ESM import in node:
2468
2591
  0 && (module.exports = {
2469
2592
  AgentChatApp,
2593
+ AgentChatAppearanceControl,
2470
2594
  AgentChatLayout,
2471
2595
  AgentChatShell,
2472
2596
  AgentChatStatePanel,