@neuronection/assistant-ui 0.23.0 → 0.24.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/CHANGELOG.md +75 -0
- package/README.md +9 -0
- package/dist/about.js +1 -1
- package/dist/ai-magic-fill.js +1 -1
- package/dist/chat-branch-tree.d.ts +45 -0
- package/dist/chat-branch-tree.js +3 -0
- package/dist/chat-branch-tree.js.map +1 -0
- package/dist/chat-composer.d.ts +50 -0
- package/dist/chat-composer.js +3 -0
- package/dist/chat-composer.js.map +1 -0
- package/dist/chat-core.d.ts +154 -0
- package/dist/chat-core.js +4 -0
- package/dist/chat-core.js.map +1 -0
- package/dist/chat-drawer.d.ts +32 -0
- package/dist/chat-drawer.js +3 -0
- package/dist/chat-drawer.js.map +1 -0
- package/dist/chat-launcher.d.ts +28 -0
- package/dist/chat-launcher.js +3 -0
- package/dist/chat-launcher.js.map +1 -0
- package/dist/chat-markdown.d.ts +64 -0
- package/dist/chat-markdown.js +3 -0
- package/dist/chat-markdown.js.map +1 -0
- package/dist/chat-message.d.ts +108 -0
- package/dist/chat-message.js +3 -0
- package/dist/chat-message.js.map +1 -0
- package/dist/chat-panel.d.ts +28 -0
- package/dist/chat-panel.js +3 -0
- package/dist/chat-panel.js.map +1 -0
- package/dist/chat-reasoning.d.ts +27 -0
- package/dist/chat-reasoning.js +3 -0
- package/dist/chat-reasoning.js.map +1 -0
- package/dist/chat-session-list.d.ts +51 -0
- package/dist/chat-session-list.js +3 -0
- package/dist/chat-session-list.js.map +1 -0
- package/dist/chat-tool-card.d.ts +37 -0
- package/dist/chat-tool-card.js +3 -0
- package/dist/chat-tool-card.js.map +1 -0
- package/dist/chat-transcript.d.ts +33 -0
- package/dist/chat-transcript.js +3 -0
- package/dist/chat-transcript.js.map +1 -0
- package/dist/{chunk-CZ2BL2ZL.js → chunk-3JJ2WCPN.js} +2 -2
- package/dist/{chunk-CZ2BL2ZL.js.map → chunk-3JJ2WCPN.js.map} +1 -1
- package/dist/chunk-5IOLDMDY.js +3 -0
- package/dist/chunk-5IOLDMDY.js.map +1 -0
- package/dist/{chunk-EODV6JK7.js → chunk-5QTHDB3D.js} +3 -3
- package/dist/{chunk-EODV6JK7.js.map → chunk-5QTHDB3D.js.map} +1 -1
- package/dist/chunk-6E6RRLKU.js +3 -0
- package/dist/chunk-6E6RRLKU.js.map +1 -0
- package/dist/chunk-6GNLY2EB.js +272 -0
- package/dist/chunk-6GNLY2EB.js.map +1 -0
- package/dist/chunk-743FJDRA.js +184 -0
- package/dist/chunk-743FJDRA.js.map +1 -0
- package/dist/chunk-BSIWLVDX.js +217 -0
- package/dist/chunk-BSIWLVDX.js.map +1 -0
- package/dist/chunk-BVJT67B5.js +134 -0
- package/dist/chunk-BVJT67B5.js.map +1 -0
- package/dist/chunk-DZBTJZZV.js +201 -0
- package/dist/chunk-DZBTJZZV.js.map +1 -0
- package/dist/chunk-HQIYSW2D.js +131 -0
- package/dist/chunk-HQIYSW2D.js.map +1 -0
- package/dist/chunk-JPDD5AFN.js +119 -0
- package/dist/chunk-JPDD5AFN.js.map +1 -0
- package/dist/chunk-LSCWBNG3.js +3 -0
- package/dist/chunk-LSCWBNG3.js.map +1 -0
- package/dist/chunk-NKYPNEOX.js +198 -0
- package/dist/chunk-NKYPNEOX.js.map +1 -0
- package/dist/{chunk-INMAUGBK.js → chunk-OH7AJ3WY.js} +3 -3
- package/dist/{chunk-INMAUGBK.js.map → chunk-OH7AJ3WY.js.map} +1 -1
- package/dist/chunk-PAQ6YQ7Y.js +3 -0
- package/dist/chunk-PAQ6YQ7Y.js.map +1 -0
- package/dist/chunk-QVDCFZSE.js +72 -0
- package/dist/chunk-QVDCFZSE.js.map +1 -0
- package/dist/chunk-RPLEI6LQ.js +102 -0
- package/dist/chunk-RPLEI6LQ.js.map +1 -0
- package/dist/chunk-S62VJVWB.js +36 -0
- package/dist/chunk-S62VJVWB.js.map +1 -0
- package/dist/chunk-X3C2FBMR.js +141 -0
- package/dist/chunk-X3C2FBMR.js.map +1 -0
- package/dist/{chunk-7YLR4AGA.js → chunk-XZWLTTHA.js} +2 -2
- package/dist/{chunk-7YLR4AGA.js.map → chunk-XZWLTTHA.js.map} +1 -1
- package/dist/chunk-Z4IBQ53C.js +75 -0
- package/dist/chunk-Z4IBQ53C.js.map +1 -0
- package/dist/{chunk-7KD4MG75.js → chunk-ZK2V2LPA.js} +3 -3
- package/dist/chunk-ZK2V2LPA.js.map +1 -0
- package/dist/copy-button.js +1 -1
- package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +39 -26
- package/dist/index.js.map +1 -1
- package/dist/portal.js +1 -1
- package/dist/settings-shell.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tree-YaIz73vo.d.ts +54 -0
- package/dist/types-DZwzmyKE.d.ts +44 -0
- package/package.json +56 -2
- package/dist/chunk-7KD4MG75.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-reasoning/ChatReasoning.tsx"],"names":["ChatReasoning"],"mappings":";;;;;;AA8BO,IAAM,aAAA,GAAsB,KAAA,CAAA,UAAA;AAAA,EACjC,SAASA,cAAAA,CACP,EAAE,IAAA,EAAM,SAAA,GAAY,OAAO,IAAA,EAAM,QAAA,EAAU,WAAA,GAAc,IAAA,EAAM,cAAc,MAAA,EAAQ,IAAA,EAAM,OAAO,KAAA,EAAO,SAAA,IACzG,GAAA,EACA;AACA,IAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAU,eAAS,WAAW,CAAA;AAC1E,IAAA,MAAM,eAAe,QAAA,KAAa,MAAA;AAClC,IAAA,MAAM,IAAA,GAAO,eAAe,QAAA,GAAW,gBAAA;AACvC,IAAA,MAAM,WAAiB,KAAA,CAAA,KAAA,EAAM;AAE7B,IAAA,MAAM,SAAS,MAAM;AACnB,MAAA,MAAM,OAAO,CAAC,IAAA;AACd,MAAA,IAAI,CAAC,YAAA,EAAc;AACjB,QAAA,mBAAA,CAAoB,IAAI,CAAA;AAAA,MAC1B;AACA,MAAA,YAAA,GAAe,IAAI,CAAA;AAAA,IACrB,CAAA;AAEA,IAAA,MAAM,SAAA,GAA2D,CAAC,KAAA,KAAU;AAC1E,MAAA,IAAI,KAAA,CAAM,GAAA,KAAQ,OAAA,IAAW,KAAA,CAAM,QAAQ,GAAA,EAAK;AAC9C,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,MAAA,EAAO;AAAA,MACT;AAAA,IACF,CAAA;AAEA,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,gBAAA;AAAA,QACR,WAAA,EAAW,IAAA;AAAA,QACX,SAAA,EAAW,EAAA;AAAA,UACT,sHAAA;AAAA,UACA;AAAA,SACF;AAAA,QAEA,QAAA,EAAA;AAAA,0BAAA,IAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,eAAA,EAAe,IAAA;AAAA,cACf,eAAA,EAAe,QAAA;AAAA,cACf,OAAA,EAAS,MAAA;AAAA,cACT,SAAA;AAAA,cACA,SAAA,EAAU,8OAAA;AAAA,cAEV,QAAA,EAAA;AAAA,gCAAA,GAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,mBAAA,EAAoB,aAAA,EAAW,IAAA,EAAC,CAAA;AAAA,oCAC/C,MAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAU,QAAA,EAAA,MAAA,EAAQ,SAAS,UAAA,EAAW,CAAA;AAAA,gBACrD,4BACC,IAAA,CAAC,MAAA,EAAA,EAAK,IAAA,EAAK,QAAA,EAAS,WAAU,uCAAA,EAC5B,QAAA,EAAA;AAAA,kCAAA,GAAA,CAAC,OAAA,EAAA,EAAQ,IAAA,EAAK,IAAA,EAAK,SAAA,EAAU,2BAAA,EAA4B,CAAA;AAAA,sCACxD,MAAA,EAAA,EAAK,SAAA,EAAU,SAAA,EAAW,QAAA,EAAA,MAAA,EAAQ,aAAa,gBAAA,EAAY;AAAA,iBAAA,EAC9D,CAAA,GACE,IAAA;AAAA,gCACJ,GAAA;AAAA,kBAAC,WAAA;AAAA,kBAAA;AAAA,oBACC,SAAA,EAAW,EAAA,CAAG,wCAAA,EAA0C,IAAA,IAAQ,YAAY,CAAA;AAAA,oBAC5E,aAAA,EAAW;AAAA;AAAA;AACb;AAAA;AAAA,WACF;AAAA,UACC,IAAA,mBACC,GAAA,CAAC,KAAA,EAAA,EAAI,EAAA,EAAI,QAAA,EAAU,SAAA,EAAU,gDAAA,EAC3B,QAAA,kBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,iDAAA,EAAmD,QAAA,EAAA,IAAA,EAAK,GACvE,CAAA,GACE;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACF","file":"chunk-QVDCFZSE.js","sourcesContent":["import * as React from 'react'\nimport { Brain, ChevronDown } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Spinner } from '../spinner/Spinner'\n\nexport interface ChatReasoningLabels {\n title: string\n streaming: string\n}\n\nexport interface ChatReasoningProps {\n /** Accumulated reasoning text (family `delta kind=\"reasoning\"`). */\n text: string\n /** Live reasoning stream — spinner + streaming label while on. */\n streaming?: boolean\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n labels?: Partial<ChatReasoningLabels>\n icon?: LucideIcon\n className?: string\n}\n\n/**\n * Collapsible thinking block for reasoning models (study's\n * `ReasoningBubble`, family-standard): streams live while the model\n * reasons, collapses to a summary row when done. Controlled-first.\n */\nexport const ChatReasoning = React.forwardRef<HTMLDivElement, ChatReasoningProps>(\n function ChatReasoning(\n { text, streaming = false, open: openProp, defaultOpen = true, onOpenChange, labels, icon: Icon = Brain, className },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const regionId = React.useId()\n\n const toggle = () => {\n const next = !open\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLButtonElement> = (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault()\n toggle()\n }\n }\n\n return (\n <div\n ref={ref}\n data-as=\"chat-reasoning\"\n data-open={open}\n className={cn(\n 'rounded-[var(--as-radius)] border border-[var(--as-border)] bg-[var(--as-surface)] text-xs text-[var(--as-muted-fg)]',\n className,\n )}\n >\n <button\n type=\"button\"\n aria-expanded={open}\n aria-controls={regionId}\n onClick={toggle}\n onKeyDown={onKeyDown}\n className=\"flex w-full items-center gap-2 px-2.5 py-1.5 text-left font-medium text-[var(--as-muted-fg)] transition-colors hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <Icon className=\"size-3.5 shrink-0\" aria-hidden />\n <span className=\"flex-1\">{labels?.title ?? 'Thinking'}</span>\n {streaming ? (\n <span role=\"status\" className=\"flex items-center gap-1.5 font-normal\">\n <Spinner size=\"sm\" className=\"text-[var(--as-muted-fg)]\" />\n <span className=\"sr-only\">{labels?.streaming ?? 'Thinking…'}</span>\n </span>\n ) : null}\n <ChevronDown\n className={cn('size-3.5 shrink-0 transition-transform', open && 'rotate-180')}\n aria-hidden\n />\n </button>\n {open ? (\n <div id={regionId} className=\"border-t border-[var(--as-border)] px-2.5 py-2\">\n <p className=\"whitespace-pre-wrap break-words leading-relaxed\">{text}</p>\n </div>\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Portal } from './chunk-XZWLTTHA.js';
|
|
2
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { X, MessageCircle } from 'lucide-react';
|
|
5
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
var ChatLauncher = React.forwardRef(
|
|
8
|
+
function ChatLauncher2({
|
|
9
|
+
panel,
|
|
10
|
+
open: openProp,
|
|
11
|
+
defaultOpen = false,
|
|
12
|
+
onOpenChange,
|
|
13
|
+
position = "bottom-right",
|
|
14
|
+
label = "Chat",
|
|
15
|
+
closeLabel = "Close chat",
|
|
16
|
+
icon: Icon = MessageCircle,
|
|
17
|
+
closeIcon: CloseIcon = X,
|
|
18
|
+
badge,
|
|
19
|
+
container,
|
|
20
|
+
panelClassName,
|
|
21
|
+
className
|
|
22
|
+
}, ref) {
|
|
23
|
+
const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen);
|
|
24
|
+
const isControlled = openProp !== void 0;
|
|
25
|
+
const open = isControlled ? openProp : uncontrolledOpen;
|
|
26
|
+
const panelRef = React.useRef(null);
|
|
27
|
+
const setOpen = (next) => {
|
|
28
|
+
if (!isControlled) {
|
|
29
|
+
setUncontrolledOpen(next);
|
|
30
|
+
}
|
|
31
|
+
onOpenChange?.(next);
|
|
32
|
+
};
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
if (!open) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const onKeyDown = (event) => {
|
|
38
|
+
if (event.key === "Escape" && panelRef.current?.contains(document.activeElement)) {
|
|
39
|
+
setOpen(false);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
document.addEventListener("keydown", onKeyDown);
|
|
43
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
44
|
+
});
|
|
45
|
+
const showBadge = badge !== void 0 && badge !== 0 && badge !== "";
|
|
46
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
47
|
+
open ? /* @__PURE__ */ jsx(Portal, { container, children: /* @__PURE__ */ jsxs(
|
|
48
|
+
"div",
|
|
49
|
+
{
|
|
50
|
+
ref: panelRef,
|
|
51
|
+
"data-as": "chat-launcher-panel",
|
|
52
|
+
className: cn(
|
|
53
|
+
"as-anim-pop fixed bottom-20 z-[var(--as-z-modal)] flex h-[min(36rem,calc(100dvh-11rem))] w-[min(26rem,calc(100vw-2rem))] flex-col overflow-hidden rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)]",
|
|
54
|
+
position === "bottom-right" ? "right-4" : "left-4",
|
|
55
|
+
panelClassName
|
|
56
|
+
),
|
|
57
|
+
role: "complementary",
|
|
58
|
+
"aria-label": label,
|
|
59
|
+
children: [
|
|
60
|
+
panel,
|
|
61
|
+
/* @__PURE__ */ jsx(
|
|
62
|
+
"button",
|
|
63
|
+
{
|
|
64
|
+
type: "button",
|
|
65
|
+
"aria-label": closeLabel,
|
|
66
|
+
onClick: () => setOpen(false),
|
|
67
|
+
className: "absolute right-2 top-2 z-10 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
|
|
68
|
+
children: /* @__PURE__ */ jsx(CloseIcon, { "aria-hidden": true, className: "size-4" })
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
) }) : null,
|
|
74
|
+
/* @__PURE__ */ jsxs(
|
|
75
|
+
"button",
|
|
76
|
+
{
|
|
77
|
+
ref,
|
|
78
|
+
type: "button",
|
|
79
|
+
"aria-expanded": open,
|
|
80
|
+
"aria-label": open ? closeLabel : label,
|
|
81
|
+
title: open ? closeLabel : label,
|
|
82
|
+
onClick: () => setOpen(!open),
|
|
83
|
+
"data-as": "chat-launcher",
|
|
84
|
+
"data-open": open || void 0,
|
|
85
|
+
className: cn(
|
|
86
|
+
"fixed bottom-4 z-[var(--as-z-popover)] flex size-12 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] shadow-[var(--as-shadow-2)] transition-transform hover:scale-105 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
|
|
87
|
+
position === "bottom-right" ? "right-4" : "left-4",
|
|
88
|
+
className
|
|
89
|
+
),
|
|
90
|
+
children: [
|
|
91
|
+
/* @__PURE__ */ jsx(Icon, { className: "size-5", "aria-hidden": true }),
|
|
92
|
+
showBadge ? /* @__PURE__ */ jsx("span", { className: "absolute -right-0.5 -top-0.5 flex h-5 min-w-5 items-center justify-center rounded-full border-2 border-[var(--as-surface)] bg-[var(--as-danger)] px-1 text-[10px] font-semibold text-[var(--as-danger-fg)]", children: badge }) : null
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
] });
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
export { ChatLauncher };
|
|
101
|
+
//# sourceMappingURL=chunk-RPLEI6LQ.js.map
|
|
102
|
+
//# sourceMappingURL=chunk-RPLEI6LQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-launcher/ChatLauncher.tsx"],"names":["ChatLauncher"],"mappings":"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","file":"chunk-RPLEI6LQ.js","sourcesContent":["import * as React from 'react'\nimport { MessageCircle, X } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\nimport { Portal } from '../portal/Portal'\n\nexport interface ChatLauncherProps {\n /** The anchored panel body — usually `ChatPanel variant=\"bubble\"`. */\n panel: React.ReactNode\n open?: boolean\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n position?: 'bottom-right' | 'bottom-left'\n label?: string\n closeLabel?: string\n icon?: LucideIcon\n closeIcon?: LucideIcon\n /** Unread / activity badge. */\n badge?: number | string\n container?: HTMLElement | null\n panelClassName?: string\n className?: string\n}\n\n/**\n * The floating chat bubble (career's `ChatWidget` launcher,\n * family-standard): fixed launcher button opening an anchored, non-modal\n * panel. `aria-expanded` wiring, Escape closes while focus is inside.\n */\nexport const ChatLauncher = React.forwardRef<HTMLButtonElement, ChatLauncherProps>(\n function ChatLauncher(\n {\n panel,\n open: openProp,\n defaultOpen = false,\n onOpenChange,\n position = 'bottom-right',\n label = 'Chat',\n closeLabel = 'Close chat',\n icon: Icon = MessageCircle,\n closeIcon: CloseIcon = X,\n badge,\n container,\n panelClassName,\n className,\n },\n ref,\n ) {\n const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n const isControlled = openProp !== undefined\n const open = isControlled ? openProp : uncontrolledOpen\n const panelRef = React.useRef<HTMLDivElement | null>(null)\n\n const setOpen = (next: boolean) => {\n if (!isControlled) {\n setUncontrolledOpen(next)\n }\n onOpenChange?.(next)\n }\n\n React.useEffect(() => {\n if (!open) {\n return\n }\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === 'Escape' && panelRef.current?.contains(document.activeElement)) {\n setOpen(false)\n }\n }\n document.addEventListener('keydown', onKeyDown)\n return () => document.removeEventListener('keydown', onKeyDown)\n })\n\n const showBadge = badge !== undefined && badge !== 0 && badge !== ''\n\n return (\n <>\n {open ? (\n <Portal container={container}>\n <div\n ref={panelRef}\n data-as=\"chat-launcher-panel\"\n className={cn(\n 'as-anim-pop fixed bottom-20 z-[var(--as-z-modal)] flex h-[min(36rem,calc(100dvh-11rem))] w-[min(26rem,calc(100vw-2rem))] flex-col overflow-hidden rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)]',\n position === 'bottom-right' ? 'right-4' : 'left-4',\n panelClassName,\n )}\n role=\"complementary\"\n aria-label={label}\n >\n {panel}\n <button\n type=\"button\"\n aria-label={closeLabel}\n onClick={() => setOpen(false)}\n className=\"absolute right-2 top-2 z-10 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <CloseIcon aria-hidden className=\"size-4\" />\n </button>\n </div>\n </Portal>\n ) : null}\n <button\n ref={ref}\n type=\"button\"\n aria-expanded={open}\n aria-label={open ? closeLabel : label}\n title={open ? closeLabel : label}\n onClick={() => setOpen(!open)}\n data-as=\"chat-launcher\"\n data-open={open || undefined}\n className={cn(\n 'fixed bottom-4 z-[var(--as-z-popover)] flex size-12 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] shadow-[var(--as-shadow-2)] transition-transform hover:scale-105 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]',\n position === 'bottom-right' ? 'right-4' : 'left-4',\n className,\n )}\n >\n <Icon className=\"size-5\" aria-hidden />\n {showBadge ? (\n <span className=\"absolute -right-0.5 -top-0.5 flex h-5 min-w-5 items-center justify-center rounded-full border-2 border-[var(--as-surface)] bg-[var(--as-danger)] px-1 text-[10px] font-semibold text-[var(--as-danger-fg)]\">\n {badge}\n </span>\n ) : null}\n </button>\n </>\n )\n },\n)\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
var ChatPanel = React.forwardRef(
|
|
6
|
+
function ChatPanel2({ variant, title, actions, banner, transcript, composer, footer, className }, ref) {
|
|
7
|
+
return /* @__PURE__ */ jsxs(
|
|
8
|
+
"div",
|
|
9
|
+
{
|
|
10
|
+
ref,
|
|
11
|
+
"data-as": "chat-panel",
|
|
12
|
+
"data-variant": variant,
|
|
13
|
+
className: cn(
|
|
14
|
+
"flex h-full min-h-0 w-full flex-col bg-[var(--as-surface)] text-[var(--as-fg)]",
|
|
15
|
+
variant === "page" && "mx-auto w-full max-w-3xl",
|
|
16
|
+
variant === "bubble" && "overflow-hidden",
|
|
17
|
+
className
|
|
18
|
+
),
|
|
19
|
+
children: [
|
|
20
|
+
title !== void 0 || actions !== void 0 ? /* @__PURE__ */ jsxs("header", { className: "flex shrink-0 items-center gap-2 border-b border-[var(--as-border)] px-3 py-2", children: [
|
|
21
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 truncate text-sm font-semibold", children: title }),
|
|
22
|
+
actions ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5", children: actions }) : null
|
|
23
|
+
] }) : null,
|
|
24
|
+
banner ? /* @__PURE__ */ jsx("div", { "data-as": "chat-panel-banner", className: "shrink-0 px-3 py-1.5 text-xs text-[var(--as-muted-fg)]", children: banner }) : null,
|
|
25
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: transcript }),
|
|
26
|
+
composer ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-[var(--as-border)] p-2", children: composer }) : null,
|
|
27
|
+
footer ? /* @__PURE__ */ jsx("div", { className: "shrink-0 px-3 pb-2 pt-1 text-center text-[10px] text-[var(--as-muted-fg)]", children: footer }) : null
|
|
28
|
+
]
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export { ChatPanel };
|
|
35
|
+
//# sourceMappingURL=chunk-S62VJVWB.js.map
|
|
36
|
+
//# sourceMappingURL=chunk-S62VJVWB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-panel/ChatPanel.tsx"],"names":["ChatPanel"],"mappings":";;;;AA6BO,IAAM,SAAA,GAAkB,KAAA,CAAA,UAAA;AAAA,EAC7B,SAASA,UAAAA,CAAU,EAAE,OAAA,EAAS,KAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAA,EAAU,MAAA,EAAQ,SAAA,EAAU,EAAG,GAAA,EAAK;AACpG,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,YAAA;AAAA,QACR,cAAA,EAAc,OAAA;AAAA,QACd,SAAA,EAAW,EAAA;AAAA,UACT,gFAAA;AAAA,UACA,YAAY,MAAA,IAAU,0BAAA;AAAA,UACtB,YAAY,QAAA,IAAY,iBAAA;AAAA,UACxB;AAAA,SACF;AAAA,QAEC,QAAA,EAAA;AAAA,UAAA,KAAA,KAAU,UAAa,OAAA,KAAY,MAAA,mBAClC,IAAA,CAAC,QAAA,EAAA,EAAO,WAAU,+EAAA,EAChB,QAAA,EAAA;AAAA,4BAAA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,+CAAA,EAAiD,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,YACrE,0BAAU,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,oCAAA,EAAsC,mBAAQ,CAAA,GAAS;AAAA,WAAA,EACnF,CAAA,GACE,IAAA;AAAA,UACH,MAAA,uBACE,KAAA,EAAA,EAAI,SAAA,EAAQ,qBAAoB,SAAA,EAAU,wDAAA,EACxC,kBACH,CAAA,GACE,IAAA;AAAA,0BACJ,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8BAAA,EAAgC,QAAA,EAAA,UAAA,EAAW,CAAA;AAAA,UACzD,2BAAW,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,iDAAA,EAAmD,oBAAS,CAAA,GAAS,IAAA;AAAA,UAC/F,yBACC,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,2EAAA,EAA6E,kBAAO,CAAA,GACjG;AAAA;AAAA;AAAA,KACN;AAAA,EAEJ;AACF","file":"chunk-S62VJVWB.js","sourcesContent":["import * as React from 'react'\n\nimport { cn } from '../../lib/utils'\n\nexport type ChatPanelVariant = 'page' | 'sidebar' | 'bubble'\n\nexport interface ChatPanelProps {\n /** Layout tier: full page (centered, wide), sidebar (dense column), bubble (compact widget body). */\n variant: ChatPanelVariant\n /** Header row: title node + action nodes (session list, branch tree, expand, close). */\n title?: React.ReactNode\n actions?: React.ReactNode\n /** Status strip under the header (connection warnings, context summary). */\n banner?: React.ReactNode\n /** Usually `<ChatTranscript …/>`. */\n transcript: React.ReactNode\n /** Usually `<ChatComposer …/>`. */\n composer?: React.ReactNode\n /** Disclaimer row under the composer (health guidance, etc.). */\n footer?: React.ReactNode\n className?: string\n}\n\n/**\n * The composed chat host — the one uniform surface assembly behind the\n * family's three shapes (full page / sidepanel / bubble). Owns layout\n * only; transcript, composer and every feature slot stay app-composed\n * (ADR-006 tier 3, `RichTextEditor` precedent).\n */\nexport const ChatPanel = React.forwardRef<HTMLDivElement, ChatPanelProps>(\n function ChatPanel({ variant, title, actions, banner, transcript, composer, footer, className }, ref) {\n return (\n <div\n ref={ref}\n data-as=\"chat-panel\"\n data-variant={variant}\n className={cn(\n 'flex h-full min-h-0 w-full flex-col bg-[var(--as-surface)] text-[var(--as-fg)]',\n variant === 'page' && 'mx-auto w-full max-w-3xl',\n variant === 'bubble' && 'overflow-hidden',\n className,\n )}\n >\n {title !== undefined || actions !== undefined ? (\n <header className=\"flex shrink-0 items-center gap-2 border-b border-[var(--as-border)] px-3 py-2\">\n <div className=\"min-w-0 flex-1 truncate text-sm font-semibold\">{title}</div>\n {actions ? <div className=\"flex shrink-0 items-center gap-0.5\">{actions}</div> : null}\n </header>\n ) : null}\n {banner ? (\n <div data-as=\"chat-panel-banner\" className=\"shrink-0 px-3 py-1.5 text-xs text-[var(--as-muted-fg)]\">\n {banner}\n </div>\n ) : null}\n <div className=\"flex min-h-0 flex-1 flex-col\">{transcript}</div>\n {composer ? <div className=\"shrink-0 border-t border-[var(--as-border)] p-2\">{composer}</div> : null}\n {footer ? (\n <div className=\"shrink-0 px-3 pb-2 pt-1 text-center text-[10px] text-[var(--as-muted-fg)]\">{footer}</div>\n ) : null}\n </div>\n )\n },\n)\n"]}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
4
|
+
import { X } from 'lucide-react';
|
|
5
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function useIsNarrow(breakpointPx) {
|
|
8
|
+
const [narrow, setNarrow] = React.useState(
|
|
9
|
+
() => typeof window !== "undefined" && window.matchMedia !== void 0 ? window.matchMedia(`(max-width: ${breakpointPx - 1}px)`).matches : false
|
|
10
|
+
);
|
|
11
|
+
React.useEffect(() => {
|
|
12
|
+
if (typeof window === "undefined" || window.matchMedia === void 0) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const query = window.matchMedia(`(max-width: ${breakpointPx - 1}px)`);
|
|
16
|
+
const listener = (event) => setNarrow(event.matches);
|
|
17
|
+
query.addEventListener("change", listener);
|
|
18
|
+
setNarrow(query.matches);
|
|
19
|
+
return () => query.removeEventListener("change", listener);
|
|
20
|
+
}, [breakpointPx]);
|
|
21
|
+
return narrow;
|
|
22
|
+
}
|
|
23
|
+
var ChatDrawer = React.forwardRef(
|
|
24
|
+
function ChatDrawer2({
|
|
25
|
+
open,
|
|
26
|
+
onOpenChange,
|
|
27
|
+
panel,
|
|
28
|
+
side = "right",
|
|
29
|
+
width = 480,
|
|
30
|
+
onWidthChange,
|
|
31
|
+
minWidth = 320,
|
|
32
|
+
maxWidth = 860,
|
|
33
|
+
resizable = true,
|
|
34
|
+
closeLabel = "Close chat",
|
|
35
|
+
resizeLabel = "Resize chat panel",
|
|
36
|
+
title = "Chat",
|
|
37
|
+
fullScreenBreakpointPx = 768,
|
|
38
|
+
container,
|
|
39
|
+
overlayClassName,
|
|
40
|
+
className
|
|
41
|
+
}) {
|
|
42
|
+
const narrow = useIsNarrow(fullScreenBreakpointPx);
|
|
43
|
+
const draggingRef = React.useRef(false);
|
|
44
|
+
const clamp = (value) => Math.min(maxWidth, Math.max(minWidth, value));
|
|
45
|
+
const onHandleKeyDown = (event) => {
|
|
46
|
+
if (!resizable || narrow) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const step = event.shiftKey ? 48 : 16;
|
|
50
|
+
if (event.key === (side === "right" ? "ArrowLeft" : "ArrowRight")) {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
onWidthChange?.(clamp(width + step));
|
|
53
|
+
} else if (event.key === (side === "right" ? "ArrowRight" : "ArrowLeft")) {
|
|
54
|
+
event.preventDefault();
|
|
55
|
+
onWidthChange?.(clamp(width - step));
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const onHandlePointerDown = (event) => {
|
|
59
|
+
if (!resizable || narrow || event.button !== 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
draggingRef.current = true;
|
|
64
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
65
|
+
};
|
|
66
|
+
const onHandlePointerMove = (event) => {
|
|
67
|
+
if (!draggingRef.current) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const delta = side === "right" ? window.innerWidth - event.clientX : event.clientX;
|
|
71
|
+
onWidthChange?.(clamp(delta));
|
|
72
|
+
};
|
|
73
|
+
const onHandlePointerUp = (event) => {
|
|
74
|
+
if (!draggingRef.current) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
draggingRef.current = false;
|
|
78
|
+
if (event.currentTarget.hasPointerCapture(event.pointerId)) {
|
|
79
|
+
event.currentTarget.releasePointerCapture(event.pointerId);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogPrimitive.Portal, { container, children: [
|
|
83
|
+
/* @__PURE__ */ jsx(
|
|
84
|
+
DialogPrimitive.Overlay,
|
|
85
|
+
{
|
|
86
|
+
"data-as": "chat-drawer-overlay",
|
|
87
|
+
className: cn("as-anim-fade fixed inset-0 z-[var(--as-z-modal)] bg-[var(--as-overlay)]", overlayClassName)
|
|
88
|
+
}
|
|
89
|
+
),
|
|
90
|
+
/* @__PURE__ */ jsxs(
|
|
91
|
+
DialogPrimitive.Content,
|
|
92
|
+
{
|
|
93
|
+
"data-as": "chat-drawer",
|
|
94
|
+
"data-side": side,
|
|
95
|
+
"data-full-screen": narrow || void 0,
|
|
96
|
+
style: narrow ? void 0 : { width: `${width}px`, maxWidth: "100vw" },
|
|
97
|
+
className: cn(
|
|
98
|
+
"as-anim-drawer fixed top-0 z-[var(--as-z-modal)] flex h-dvh flex-col border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)] focus:outline-none",
|
|
99
|
+
narrow ? "inset-x-0 bottom-0 border-t" : side === "right" ? "bottom-0 right-0 border-l" : "bottom-0 left-0 border-r",
|
|
100
|
+
className
|
|
101
|
+
),
|
|
102
|
+
children: [
|
|
103
|
+
resizable && !narrow ? /* @__PURE__ */ jsx(
|
|
104
|
+
"div",
|
|
105
|
+
{
|
|
106
|
+
role: "separator",
|
|
107
|
+
"aria-orientation": "vertical",
|
|
108
|
+
"aria-label": resizeLabel,
|
|
109
|
+
tabIndex: 0,
|
|
110
|
+
onKeyDown: onHandleKeyDown,
|
|
111
|
+
onPointerDown: onHandlePointerDown,
|
|
112
|
+
onPointerMove: onHandlePointerMove,
|
|
113
|
+
onPointerUp: onHandlePointerUp,
|
|
114
|
+
onPointerCancel: onHandlePointerUp,
|
|
115
|
+
className: cn(
|
|
116
|
+
"group/handle absolute top-0 z-10 h-dvh w-1.5 cursor-col-resize focus-visible:outline-2 focus-visible:outline-[var(--as-focus-ring)]",
|
|
117
|
+
side === "right" ? "-left-1" : "-right-1"
|
|
118
|
+
),
|
|
119
|
+
children: /* @__PURE__ */ jsx("span", { className: "mx-auto block h-full w-px bg-transparent transition-colors group-hover/handle:bg-[var(--as-border)]" })
|
|
120
|
+
}
|
|
121
|
+
) : null,
|
|
122
|
+
/* @__PURE__ */ jsx(DialogPrimitive.Title, { className: "sr-only", children: title }),
|
|
123
|
+
/* @__PURE__ */ jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: panel }),
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
DialogPrimitive.Close,
|
|
126
|
+
{
|
|
127
|
+
"aria-label": closeLabel,
|
|
128
|
+
className: "absolute right-3 top-2.5 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]",
|
|
129
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": true, className: "size-4" })
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
] }) });
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
export { ChatDrawer };
|
|
140
|
+
//# sourceMappingURL=chunk-X3C2FBMR.js.map
|
|
141
|
+
//# sourceMappingURL=chunk-X3C2FBMR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-drawer/ChatDrawer.tsx"],"names":["ChatDrawer"],"mappings":";;;;;;AA6BA,SAAS,YAAY,YAAA,EAA+B;AAClD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAU,KAAA,CAAA,QAAA;AAAA,IAAS,MACzC,OAAO,MAAA,KAAW,WAAA,IAAe,OAAO,UAAA,KAAe,MAAA,GACnD,MAAA,CAAO,UAAA,CAAW,CAAA,YAAA,EAAe,YAAA,GAAe,CAAC,CAAA,GAAA,CAAK,EAAE,OAAA,GACxD;AAAA,GACN;AACA,EAAM,gBAAU,MAAM;AACpB,IAAA,IAAI,OAAO,MAAA,KAAW,WAAA,IAAe,MAAA,CAAO,eAAe,MAAA,EAAW;AACpE,MAAA;AAAA,IACF;AACA,IAAA,MAAM,QAAQ,MAAA,CAAO,UAAA,CAAW,CAAA,YAAA,EAAe,YAAA,GAAe,CAAC,CAAA,GAAA,CAAK,CAAA;AACpE,IAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAA+B,SAAA,CAAU,MAAM,OAAO,CAAA;AACxE,IAAA,KAAA,CAAM,gBAAA,CAAiB,UAAU,QAAQ,CAAA;AACzC,IAAA,SAAA,CAAU,MAAM,OAAO,CAAA;AACvB,IAAA,OAAO,MAAM,KAAA,CAAM,mBAAA,CAAoB,QAAA,EAAU,QAAQ,CAAA;AAAA,EAC3D,CAAA,EAAG,CAAC,YAAY,CAAC,CAAA;AACjB,EAAA,OAAO,MAAA;AACT;AAOO,IAAM,UAAA,GAAmB,KAAA,CAAA,UAAA;AAAA,EAC9B,SAASA,WAAAA,CAAW;AAAA,IAChB,IAAA;AAAA,IACA,YAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA,GAAO,OAAA;AAAA,IACP,KAAA,GAAQ,GAAA;AAAA,IACR,aAAA;AAAA,IACA,QAAA,GAAW,GAAA;AAAA,IACX,QAAA,GAAW,GAAA;AAAA,IACX,SAAA,GAAY,IAAA;AAAA,IACZ,UAAA,GAAa,YAAA;AAAA,IACb,WAAA,GAAc,mBAAA;AAAA,IACd,KAAA,GAAQ,MAAA;AAAA,IACR,sBAAA,GAAyB,GAAA;AAAA,IACzB,SAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACF,EAAG;AACH,IAAA,MAAM,MAAA,GAAS,YAAY,sBAAsB,CAAA;AACjD,IAAA,MAAM,WAAA,GAAoB,aAAO,KAAK,CAAA;AAEtC,IAAA,MAAM,KAAA,GAAQ,CAAC,KAAA,KAAkB,IAAA,CAAK,GAAA,CAAI,UAAU,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,KAAK,CAAC,CAAA;AAE7E,IAAA,MAAM,eAAA,GAAkB,CAAC,KAAA,KAA+B;AACtD,MAAA,IAAI,CAAC,aAAa,MAAA,EAAQ;AACxB,QAAA;AAAA,MACF;AACA,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,QAAA,GAAW,EAAA,GAAK,EAAA;AACnC,MAAA,IAAI,KAAA,CAAM,GAAA,MAAS,IAAA,KAAS,OAAA,GAAU,cAAc,YAAA,CAAA,EAAe;AACjE,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,aAAA,GAAgB,KAAA,CAAM,KAAA,GAAQ,IAAI,CAAC,CAAA;AAAA,MACrC,WAAW,KAAA,CAAM,GAAA,MAAS,IAAA,KAAS,OAAA,GAAU,eAAe,WAAA,CAAA,EAAc;AACxE,QAAA,KAAA,CAAM,cAAA,EAAe;AACrB,QAAA,aAAA,GAAgB,KAAA,CAAM,KAAA,GAAQ,IAAI,CAAC,CAAA;AAAA,MACrC;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,mBAAA,GAAsB,CAAC,KAAA,KAA8C;AACzE,MAAA,IAAI,CAAC,SAAA,IAAa,MAAA,IAAU,KAAA,CAAM,WAAW,CAAA,EAAG;AAC9C,QAAA;AAAA,MACF;AACA,MAAA,KAAA,CAAM,cAAA,EAAe;AACrB,MAAA,WAAA,CAAY,OAAA,GAAU,IAAA;AACtB,MAAA,KAAA,CAAM,aAAA,CAAc,iBAAA,CAAkB,KAAA,CAAM,SAAS,CAAA;AAAA,IACvD,CAAA;AAEA,IAAA,MAAM,mBAAA,GAAsB,CAAC,KAAA,KAA8C;AACzE,MAAA,IAAI,CAAC,YAAY,OAAA,EAAS;AACxB,QAAA;AAAA,MACF;AACA,MAAA,MAAM,QAAQ,IAAA,KAAS,OAAA,GAAU,OAAO,UAAA,GAAa,KAAA,CAAM,UAAU,KAAA,CAAM,OAAA;AAC3E,MAAA,aAAA,GAAgB,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,IAC9B,CAAA;AAEA,IAAA,MAAM,iBAAA,GAAoB,CAAC,KAAA,KAA8C;AACvE,MAAA,IAAI,CAAC,YAAY,OAAA,EAAS;AACxB,QAAA;AAAA,MACF;AACA,MAAA,WAAA,CAAY,OAAA,GAAU,KAAA;AACtB,MAAA,IAAI,KAAA,CAAM,aAAA,CAAc,iBAAA,CAAkB,KAAA,CAAM,SAAS,CAAA,EAAG;AAC1D,QAAA,KAAA,CAAM,aAAA,CAAc,qBAAA,CAAsB,KAAA,CAAM,SAAS,CAAA;AAAA,MAC3D;AAAA,IACF,CAAA;AAEA,IAAA,uBACE,GAAA,CAAiB,sBAAhB,EAAqB,IAAA,EAAY,cAChC,QAAA,kBAAA,IAAA,CAAiB,eAAA,CAAA,MAAA,EAAhB,EAAuB,SAAA,EACtB,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAiB,eAAA,CAAA,OAAA;AAAA,QAAhB;AAAA,UACC,SAAA,EAAQ,qBAAA;AAAA,UACR,SAAA,EAAW,EAAA,CAAG,yEAAA,EAA2E,gBAAgB;AAAA;AAAA,OAC3G;AAAA,sBACA,IAAA;AAAA,QAAiB,eAAA,CAAA,OAAA;AAAA,QAAhB;AAAA,UACC,SAAA,EAAQ,aAAA;AAAA,UACR,WAAA,EAAW,IAAA;AAAA,UACX,oBAAkB,MAAA,IAAU,MAAA;AAAA,UAC5B,KAAA,EAAO,SAAS,MAAA,GAAY,EAAE,OAAO,CAAA,EAAG,KAAK,CAAA,EAAA,CAAA,EAAM,QAAA,EAAU,OAAA,EAAQ;AAAA,UACrE,SAAA,EAAW,EAAA;AAAA,YACT,0LAAA;AAAA,YACA,MAAA,GACI,6BAAA,GACA,IAAA,KAAS,OAAA,GACP,2BAAA,GACA,0BAAA;AAAA,YACN;AAAA,WACF;AAAA,UAEC,QAAA,EAAA;AAAA,YAAA,SAAA,IAAa,CAAC,MAAA,mBACb,GAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,IAAA,EAAK,WAAA;AAAA,gBACL,kBAAA,EAAiB,UAAA;AAAA,gBACjB,YAAA,EAAY,WAAA;AAAA,gBACZ,QAAA,EAAU,CAAA;AAAA,gBACV,SAAA,EAAW,eAAA;AAAA,gBACX,aAAA,EAAe,mBAAA;AAAA,gBACf,aAAA,EAAe,mBAAA;AAAA,gBACf,WAAA,EAAa,iBAAA;AAAA,gBACb,eAAA,EAAiB,iBAAA;AAAA,gBACjB,SAAA,EAAW,EAAA;AAAA,kBACT,qIAAA;AAAA,kBACA,IAAA,KAAS,UAAU,SAAA,GAAY;AAAA,iBACjC;AAAA,gBAEA,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,qGAAA,EAAsG;AAAA;AAAA,aACxH,GACE,IAAA;AAAA,4BACJ,GAAA,CAAiB,eAAA,CAAA,KAAA,EAAhB,EAAsB,SAAA,EAAU,WAAW,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,4BAClD,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,8BAAA,EAAgC,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,4BACrD,GAAA;AAAA,cAAiB,eAAA,CAAA,KAAA;AAAA,cAAhB;AAAA,gBACC,YAAA,EAAY,UAAA;AAAA,gBACZ,SAAA,EAAU,qQAAA;AAAA,gBAEV,QAAA,kBAAA,GAAA,CAAC,CAAA,EAAA,EAAE,aAAA,EAAW,IAAA,EAAC,WAAU,QAAA,EAAS;AAAA;AAAA;AACpC;AAAA;AAAA;AACF,KAAA,EACF,CAAA,EACF,CAAA;AAAA,EAEJ;AACF","file":"chunk-X3C2FBMR.js","sourcesContent":["import * as React from 'react'\nimport * as DialogPrimitive from '@radix-ui/react-dialog'\nimport { X } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatDrawerProps {\n open: boolean\n onOpenChange: (open: boolean) => void\n /** The sidepanel content — usually `ChatPanel variant=\"sidebar\"`. */\n panel: React.ReactNode\n side?: 'right' | 'left'\n /** Controlled width in px (persist app-side). Default 480. */\n width?: number\n onWidthChange?: (width: number) => void\n minWidth?: number\n maxWidth?: number\n resizable?: boolean\n closeLabel?: string\n resizeLabel?: string\n /** Accessible dialog name (visually hidden). Default \"Chat\". */\n title?: string\n /** Below this viewport width the drawer goes full-screen. Default 768. */\n fullScreenBreakpointPx?: number\n container?: HTMLElement | null\n overlayClassName?: string\n className?: string\n}\n\nfunction useIsNarrow(breakpointPx: number): boolean {\n const [narrow, setNarrow] = React.useState(() =>\n typeof window !== 'undefined' && window.matchMedia !== undefined\n ? window.matchMedia(`(max-width: ${breakpointPx - 1}px)`).matches\n : false,\n )\n React.useEffect(() => {\n if (typeof window === 'undefined' || window.matchMedia === undefined) {\n return\n }\n const query = window.matchMedia(`(max-width: ${breakpointPx - 1}px)`)\n const listener = (event: MediaQueryListEvent) => setNarrow(event.matches)\n query.addEventListener('change', listener)\n setNarrow(query.matches)\n return () => query.removeEventListener('change', listener)\n }, [breakpointPx])\n return narrow\n}\n\n/**\n * The resizable chat sidepanel (health's `AIDrawer` + study's resizable\n * sidebar, family-standard): portal overlay on Radix Dialog (focus trap,\n * Escape), drag + keyboard resize, full-screen below the breakpoint.\n */\nexport const ChatDrawer = React.forwardRef<HTMLDivElement, ChatDrawerProps>(\n function ChatDrawer({\n open,\n onOpenChange,\n panel,\n side = 'right',\n width = 480,\n onWidthChange,\n minWidth = 320,\n maxWidth = 860,\n resizable = true,\n closeLabel = 'Close chat',\n resizeLabel = 'Resize chat panel',\n title = 'Chat',\n fullScreenBreakpointPx = 768,\n container,\n overlayClassName,\n className,\n }) {\n const narrow = useIsNarrow(fullScreenBreakpointPx)\n const draggingRef = React.useRef(false)\n\n const clamp = (value: number) => Math.min(maxWidth, Math.max(minWidth, value))\n\n const onHandleKeyDown = (event: React.KeyboardEvent) => {\n if (!resizable || narrow) {\n return\n }\n const step = event.shiftKey ? 48 : 16\n if (event.key === (side === 'right' ? 'ArrowLeft' : 'ArrowRight')) {\n event.preventDefault()\n onWidthChange?.(clamp(width + step))\n } else if (event.key === (side === 'right' ? 'ArrowRight' : 'ArrowLeft')) {\n event.preventDefault()\n onWidthChange?.(clamp(width - step))\n }\n }\n\n const onHandlePointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!resizable || narrow || event.button !== 0) {\n return\n }\n event.preventDefault()\n draggingRef.current = true\n event.currentTarget.setPointerCapture(event.pointerId)\n }\n\n const onHandlePointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return\n }\n const delta = side === 'right' ? window.innerWidth - event.clientX : event.clientX\n onWidthChange?.(clamp(delta))\n }\n\n const onHandlePointerUp = (event: React.PointerEvent<HTMLDivElement>) => {\n if (!draggingRef.current) {\n return\n }\n draggingRef.current = false\n if (event.currentTarget.hasPointerCapture(event.pointerId)) {\n event.currentTarget.releasePointerCapture(event.pointerId)\n }\n }\n\n return (\n <DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>\n <DialogPrimitive.Portal container={container}>\n <DialogPrimitive.Overlay\n data-as=\"chat-drawer-overlay\"\n className={cn('as-anim-fade fixed inset-0 z-[var(--as-z-modal)] bg-[var(--as-overlay)]', overlayClassName)}\n />\n <DialogPrimitive.Content\n data-as=\"chat-drawer\"\n data-side={side}\n data-full-screen={narrow || undefined}\n style={narrow ? undefined : { width: `${width}px`, maxWidth: '100vw' }}\n className={cn(\n 'as-anim-drawer fixed top-0 z-[var(--as-z-modal)] flex h-dvh flex-col border-[var(--as-border)] bg-[var(--as-surface)] text-[var(--as-fg)] shadow-[var(--as-shadow-3)] focus:outline-none',\n narrow\n ? 'inset-x-0 bottom-0 border-t'\n : side === 'right'\n ? 'bottom-0 right-0 border-l'\n : 'bottom-0 left-0 border-r',\n className,\n )}\n >\n {resizable && !narrow ? (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={resizeLabel}\n tabIndex={0}\n onKeyDown={onHandleKeyDown}\n onPointerDown={onHandlePointerDown}\n onPointerMove={onHandlePointerMove}\n onPointerUp={onHandlePointerUp}\n onPointerCancel={onHandlePointerUp}\n className={cn(\n 'group/handle absolute top-0 z-10 h-dvh w-1.5 cursor-col-resize focus-visible:outline-2 focus-visible:outline-[var(--as-focus-ring)]',\n side === 'right' ? '-left-1' : '-right-1',\n )}\n >\n <span className=\"mx-auto block h-full w-px bg-transparent transition-colors group-hover/handle:bg-[var(--as-border)]\" />\n </div>\n ) : null}\n <DialogPrimitive.Title className=\"sr-only\">{title}</DialogPrimitive.Title>\n <div className=\"flex min-h-0 flex-1 flex-col\">{panel}</div>\n <DialogPrimitive.Close\n aria-label={closeLabel}\n className=\"absolute right-3 top-2.5 rounded-[var(--as-radius-sm)] p-1 text-[var(--as-muted-fg)] transition-colors hover:bg-[var(--as-secondary)] hover:text-[var(--as-fg)] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <X aria-hidden className=\"size-4\" />\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPrimitive.Portal>\n </DialogPrimitive.Root>\n )\n },\n)\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/portal/Portal.tsx"],"names":[],"mappings":";;;AAQO,SAAS,MAAA,CAAO,EAAE,QAAA,EAAU,SAAA,EAAU,EAAgB;AAC3D,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,IAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,aAAa,QAAA,CAAS,IAAA;AACpC,EAAA,OAAO,YAAA,CAAa,UAAU,KAAK,CAAA;AACrC","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/components/portal/Portal.tsx"],"names":[],"mappings":";;;AAQO,SAAS,MAAA,CAAO,EAAE,QAAA,EAAU,SAAA,EAAU,EAAgB;AAC3D,EAAA,IAAI,OAAO,QAAA,KAAa,WAAA,EAAa,OAAO,IAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,aAAa,QAAA,CAAS,IAAA;AACpC,EAAA,OAAO,YAAA,CAAa,UAAU,KAAK,CAAA;AACrC","file":"chunk-XZWLTTHA.js","sourcesContent":["import * as React from 'react'\nimport { createPortal } from 'react-dom'\n\nexport interface PortalProps {\n children: React.ReactNode\n container?: HTMLElement | null\n}\n\nexport function Portal({ children, container }: PortalProps) {\n if (typeof document === 'undefined') return null\n const mount = container ?? document.body\n return createPortal(children, mount)\n}\n"]}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// src/components/chat-core/tree.ts
|
|
2
|
+
function buildBranchTree(input) {
|
|
3
|
+
const nodes = {};
|
|
4
|
+
for (const node of input.nodes) {
|
|
5
|
+
nodes[node.id] = { ...node, childIds: [] };
|
|
6
|
+
}
|
|
7
|
+
for (const node of Object.values(nodes)) {
|
|
8
|
+
if (node.parentId !== null && nodes[node.parentId] !== void 0) {
|
|
9
|
+
nodes[node.parentId].childIds.push(node.id);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
let activeRootId = input.activeRootId ?? null;
|
|
13
|
+
if (activeRootId === null || nodes[activeRootId] === void 0) {
|
|
14
|
+
const roots = Object.values(nodes).filter((node) => node.parentId === null);
|
|
15
|
+
activeRootId = roots.length > 0 ? roots[roots.length - 1].id : null;
|
|
16
|
+
}
|
|
17
|
+
return { activeRootId, nodes };
|
|
18
|
+
}
|
|
19
|
+
function linearTree(items) {
|
|
20
|
+
const nodes = {};
|
|
21
|
+
let previousId = null;
|
|
22
|
+
for (const item of items) {
|
|
23
|
+
nodes[item.id] = {
|
|
24
|
+
id: item.id,
|
|
25
|
+
role: item.role,
|
|
26
|
+
excerpt: item.excerpt,
|
|
27
|
+
parentId: previousId,
|
|
28
|
+
activeChildId: null,
|
|
29
|
+
childIds: []
|
|
30
|
+
};
|
|
31
|
+
if (previousId !== null) {
|
|
32
|
+
nodes[previousId].activeChildId = item.id;
|
|
33
|
+
nodes[previousId].childIds.push(item.id);
|
|
34
|
+
}
|
|
35
|
+
previousId = item.id;
|
|
36
|
+
}
|
|
37
|
+
return { activeRootId: items.length > 0 ? items[0].id : null, nodes };
|
|
38
|
+
}
|
|
39
|
+
function walkActivePath(tree) {
|
|
40
|
+
const path = [];
|
|
41
|
+
const visited = /* @__PURE__ */ new Set();
|
|
42
|
+
let currentId = tree.activeRootId;
|
|
43
|
+
while (currentId !== null && !visited.has(currentId)) {
|
|
44
|
+
const node = tree.nodes[currentId];
|
|
45
|
+
if (node === void 0) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
visited.add(currentId);
|
|
49
|
+
path.push(currentId);
|
|
50
|
+
const fallback = node.childIds.length > 0 ? node.childIds[node.childIds.length - 1] ?? null : null;
|
|
51
|
+
const nextId = node.activeChildId ?? fallback;
|
|
52
|
+
currentId = nextId !== null && tree.nodes[nextId] !== void 0 ? nextId : null;
|
|
53
|
+
}
|
|
54
|
+
return path;
|
|
55
|
+
}
|
|
56
|
+
function activePathSet(tree) {
|
|
57
|
+
return new Set(walkActivePath(tree));
|
|
58
|
+
}
|
|
59
|
+
function variantInfo(tree, nodeId) {
|
|
60
|
+
const node = tree.nodes[nodeId];
|
|
61
|
+
if (node === void 0) {
|
|
62
|
+
return { index: 1, count: 1, siblingIds: [nodeId] };
|
|
63
|
+
}
|
|
64
|
+
const siblingIds = node.parentId !== null && tree.nodes[node.parentId] !== void 0 ? tree.nodes[node.parentId].childIds : Object.values(tree.nodes).filter((candidate) => candidate.parentId === null).map((candidate) => candidate.id);
|
|
65
|
+
const index = siblingIds.indexOf(nodeId);
|
|
66
|
+
return {
|
|
67
|
+
index: index === -1 ? 1 : index + 1,
|
|
68
|
+
count: siblingIds.length,
|
|
69
|
+
siblingIds
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export { activePathSet, buildBranchTree, linearTree, variantInfo, walkActivePath };
|
|
74
|
+
//# sourceMappingURL=chunk-Z4IBQ53C.js.map
|
|
75
|
+
//# sourceMappingURL=chunk-Z4IBQ53C.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-core/tree.ts"],"names":[],"mappings":"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","file":"chunk-Z4IBQ53C.js","sourcesContent":["import type { ChatRole } from './types'\n\nexport interface BranchNode {\n id: string\n role: ChatRole\n excerpt?: string\n parentId: string | null\n activeChildId: string | null\n /** In insertion (sibling-order) order; the last entry is the newest sibling. */\n childIds: string[]\n}\n\nexport interface BranchTree {\n activeRootId: string | null\n nodes: Record<string, BranchNode>\n}\n\nexport type BranchNodeInput = Omit<BranchNode, 'childIds'>\n\n/**\n * Index the server's branch-tree projection (family contract: the read-only\n * `GET …/tree` shape) and compute child lists. Insertion order defines\n * sibling order — the last child is the newest variant (fallback when a\n * parent has no `activeChildId`).\n */\nexport function buildBranchTree(input: { activeRootId?: string | null; nodes: BranchNodeInput[] }): BranchTree {\n const nodes: Record<string, BranchNode> = {}\n for (const node of input.nodes) {\n nodes[node.id] = { ...node, childIds: [] }\n }\n for (const node of Object.values(nodes)) {\n if (node.parentId !== null && nodes[node.parentId] !== undefined) {\n nodes[node.parentId]!.childIds.push(node.id)\n }\n }\n let activeRootId = input.activeRootId ?? null\n if (activeRootId === null || nodes[activeRootId] === undefined) {\n const roots = Object.values(nodes).filter((node) => node.parentId === null)\n activeRootId = roots.length > 0 ? roots[roots.length - 1]!.id : null\n }\n return { activeRootId, nodes }\n}\n\n/**\n * Degenerate single-path tree from a visible (already active-path) message\n * list — for apps/backends without branching yet. Every message parents to\n * the previous one; the tree renders identically to a branched one.\n */\nexport function linearTree(items: { id: string; role: ChatRole; excerpt?: string }[]): BranchTree {\n const nodes: Record<string, BranchNode> = {}\n let previousId: string | null = null\n for (const item of items) {\n nodes[item.id] = {\n id: item.id,\n role: item.role,\n excerpt: item.excerpt,\n parentId: previousId,\n activeChildId: null,\n childIds: [],\n }\n if (previousId !== null) {\n nodes[previousId]!.activeChildId = item.id\n nodes[previousId]!.childIds.push(item.id)\n }\n previousId = item.id\n }\n return { activeRootId: items.length > 0 ? items[0]!.id : null, nodes }\n}\n\n/**\n * The visible conversation: pointer walk from the active root, following\n * each node's `activeChildId` (fallback: newest sibling). Cycle-safe.\n */\nexport function walkActivePath(tree: BranchTree): string[] {\n const path: string[] = []\n const visited = new Set<string>()\n let currentId = tree.activeRootId\n while (currentId !== null && !visited.has(currentId)) {\n const node = tree.nodes[currentId]\n if (node === undefined) {\n break\n }\n visited.add(currentId)\n path.push(currentId)\n const fallback = node.childIds.length > 0 ? node.childIds[node.childIds.length - 1] ?? null : null\n const nextId = node.activeChildId ?? fallback\n currentId = nextId !== null && tree.nodes[nextId] !== undefined ? nextId : null\n }\n return path\n}\n\nexport function activePathSet(tree: BranchTree): Set<string> {\n return new Set(walkActivePath(tree))\n}\n\n/**\n * Sibling variant info for the inline `‹ n/N ›` switcher: the 1-based index\n * of `nodeId` among its siblings (roots are siblings of each other) and the\n * ordered sibling ids.\n */\nexport function variantInfo(\n tree: BranchTree,\n nodeId: string,\n): { index: number; count: number; siblingIds: string[] } {\n const node = tree.nodes[nodeId]\n if (node === undefined) {\n return { index: 1, count: 1, siblingIds: [nodeId] }\n }\n const siblingIds =\n node.parentId !== null && tree.nodes[node.parentId] !== undefined\n ? tree.nodes[node.parentId]!.childIds\n : Object.values(tree.nodes)\n .filter((candidate) => candidate.parentId === null)\n .map((candidate) => candidate.id)\n const index = siblingIds.indexOf(nodeId)\n return {\n index: index === -1 ? 1 : index + 1,\n count: siblingIds.length,\n siblingIds,\n }\n}\n"]}
|
|
@@ -10,7 +10,7 @@ var SettingsShell = React.forwardRef(
|
|
|
10
10
|
{
|
|
11
11
|
ref,
|
|
12
12
|
"data-as": "settings-shell",
|
|
13
|
-
className: cn("grid
|
|
13
|
+
className: cn("grid gap-8", className),
|
|
14
14
|
children: [
|
|
15
15
|
/* @__PURE__ */ jsx(
|
|
16
16
|
"nav",
|
|
@@ -79,5 +79,5 @@ var SettingsShell = React.forwardRef(
|
|
|
79
79
|
);
|
|
80
80
|
|
|
81
81
|
export { SettingsShell };
|
|
82
|
-
//# sourceMappingURL=chunk-
|
|
83
|
-
//# sourceMappingURL=chunk-
|
|
82
|
+
//# sourceMappingURL=chunk-ZK2V2LPA.js.map
|
|
83
|
+
//# sourceMappingURL=chunk-ZK2V2LPA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/settings-shell/SettingsShell.tsx"],"names":["SettingsShell"],"mappings":";;;;AAyBO,IAAM,aAAA,GAAsB,KAAA,CAAA,UAAA;AAAA,EACjC,SAASA,cAAAA,CACP,EAAE,GAAA,EAAK,MAAA,EAAQ,UAAA,EAAY,MAAA,EAAQ,QAAA,EAAU,SAAA,EAAW,YAAA,EAAc,SAAA,EAAU,EAChF,GAAA,EACA;AACA,IAAA,MAAM,aAAa,MAAA,EAAQ,IAAA;AAC3B,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,SAAA,EAAQ,gBAAA;AAAA,QACR,SAAA,EAAW,EAAA,CAAG,YAAA,EAAc,SAAS,CAAA;AAAA,QAErC,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,KAAA;AAAA,YAAA;AAAA,cACC,SAAA,EAAU,eAAA;AAAA,cACV,YAAA,EAAW,mBAAA;AAAA,cACX,aAAA,EAAa,SAAA;AAAA,cAEb,QAAA,kBAAA,IAAA;AAAA,gBAAC,KAAA;AAAA,gBAAA;AAAA,kBACC,SAAA,EAAW,EAAA;AAAA,oBACT,gIAAA;AAAA,oBACA;AAAA,mBACF;AAAA,kBAEC,QAAA,EAAA;AAAA,oBAAA,MAAA,mBACC,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,6EAAA,EACZ,QAAA,EAAA;AAAA,sBAAA,UAAA,mBACC,GAAA;AAAA,wBAAC,UAAA;AAAA,wBAAA;AAAA,0BACC,SAAA,EAAU,0CAAA;AAAA,0BACV,aAAA,EAAW;AAAA;AAAA,uBACb,GACE,IAAA;AAAA,sCACJ,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,gDAAA,EACb,iBAAO,KAAA,EACV;AAAA,qBAAA,EACF,CAAA,GACE,IAAA;AAAA,oBACH,GAAA,CAAI,GAAA,CAAI,CAAC,EAAE,EAAA,EAAI,OAAO,WAAA,EAAa,IAAA,EAAM,IAAA,EAAM,QAAA,EAAS,KAAM;AAC7D,sBAAA,MAAM,WAAW,EAAA,KAAO,MAAA;AACxB,sBAAA,uBACE,IAAA;AAAA,wBAAC,QAAA;AAAA,wBAAA;AAAA,0BAEC,IAAA,EAAK,QAAA;AAAA,0BACL,cAAA,EAAc,WAAW,MAAA,GAAS,MAAA;AAAA,0BAClC,OAAA,EAAS,MAAM,UAAA,CAAW,EAAE,CAAA;AAAA,0BAC5B,SAAA,EAAW,EAAA;AAAA,4BACT,uNAAA;AAAA,4BACA,WACI,8FAAA,GACA;AAAA,2BACN;AAAA,0BAEC,QAAA,EAAA;AAAA,4BAAA,IAAA,mBACC,GAAA;AAAA,8BAAC,IAAA;AAAA,8BAAA;AAAA,gCACC,SAAA,EAAU,6BAAA;AAAA,gCACV,aAAA,EAAW;AAAA;AAAA,6BACb,GACE,IAAA;AAAA,4CACJ,IAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,uBAAA,EACd,QAAA,EAAA;AAAA,8CAAA,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,UAAA,EAAY,QAAA,EAAA,KAAA,EAAM,CAAA;AAAA,8BACjC,8BACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mDAAA,EACb,uBACH,CAAA,GACE;AAAA,6BAAA,EACN,CAAA;AAAA,4BACC,2BACC,GAAA,CAAC,MAAA,EAAA,EAAK,SAAA,EAAU,4CAAA,EACb,oBACH,CAAA,GACE;AAAA;AAAA,yBAAA;AAAA,wBA7BC;AAAA,uBA8BP;AAAA,oBAEJ,CAAC;AAAA;AAAA;AAAA;AACH;AAAA,WACF;AAAA,0BACA,GAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,uBAAA,EAAyB,QAAA,EAAS;AAAA;AAAA;AAAA,KACnD;AAAA,EAEJ;AACF","file":"chunk-ZK2V2LPA.js","sourcesContent":["import * as React from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport { cn } from '../../lib/utils'\n\nexport interface SettingsNavItem {\n id: string\n label: string\n description?: string\n icon?: LucideIcon\n /** Optional trailing node (status dot, count, badge) at the row edge. */\n trailing?: React.ReactNode\n}\n\nexport interface SettingsShellProps {\n nav: SettingsNavItem[]\n active: string\n onNavigate: (id: string) => void\n header?: { icon?: LucideIcon; title: string }\n children: React.ReactNode\n className?: string\n navClassName?: string\n /** Test hook: data-testid for the `<nav>` element. */\n navTestId?: string\n}\n\nexport const SettingsShell = React.forwardRef<HTMLDivElement, SettingsShellProps>(\n function SettingsShell(\n { nav, active, onNavigate, header, children, className, navClassName, navTestId },\n ref,\n ) {\n const HeaderIcon = header?.icon\n return (\n <div\n ref={ref}\n data-as=\"settings-shell\"\n className={cn('grid gap-8', className)}\n >\n <nav\n className=\"lg:col-span-1\"\n aria-label=\"Settings sections\"\n data-testid={navTestId}\n >\n <div\n className={cn(\n 'space-y-1 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface-raised)] p-3 lg:sticky lg:top-24',\n navClassName,\n )}\n >\n {header ? (\n <div className=\"mb-2 flex items-center gap-2.5 border-b border-[var(--as-border)] px-3 py-2\">\n {HeaderIcon ? (\n <HeaderIcon\n className=\"size-5 shrink-0 text-[var(--as-primary)]\"\n aria-hidden\n />\n ) : null}\n <span className=\"truncate text-sm font-bold text-[var(--as-fg)]\">\n {header.title}\n </span>\n </div>\n ) : null}\n {nav.map(({ id, label, description, icon: Icon, trailing }) => {\n const isActive = id === active\n return (\n <button\n key={id}\n type=\"button\"\n aria-current={isActive ? 'page' : undefined}\n onClick={() => onNavigate(id)}\n className={cn(\n 'flex w-full cursor-pointer items-start rounded-[var(--as-radius)] px-3 py-2.5 text-left text-sm transition-colors focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]',\n isActive\n ? 'bg-[color-mix(in_srgb,var(--as-primary)_12%,transparent)] font-bold text-[var(--as-primary)]'\n : 'font-medium text-[var(--as-fg)] hover:bg-[var(--as-muted)]',\n )}\n >\n {Icon ? (\n <Icon\n className=\"mr-3 mt-0.5 size-4 shrink-0\"\n aria-hidden\n />\n ) : null}\n <span className=\"flex min-w-0 flex-col\">\n <span className=\"truncate\">{label}</span>\n {description ? (\n <span className=\"text-[11px] font-normal text-[var(--as-muted-fg)]\">\n {description}\n </span>\n ) : null}\n </span>\n {trailing ? (\n <span className=\"ml-auto flex items-center self-center pl-2\">\n {trailing}\n </span>\n ) : null}\n </button>\n )\n })}\n </div>\n </nav>\n <div className=\"min-w-0 lg:col-span-3\">{children}</div>\n </div>\n )\n },\n)\n"]}
|
package/dist/copy-button.js
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|