@lattice-php/lattice 0.51.0 → 0.52.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.d.ts +0 -4
- package/dist/index.js +1 -3
- package/dist/registry.d.ts +3 -3
- package/dist/registry.js +1 -2
- package/dist/registry.js.map +1 -1
- package/dist/runtime.d.ts +2 -0
- package/dist/runtime.js +3 -3
- package/dist/types/generated.d.ts +3 -42
- package/dist-standalone/chunks/{chart-view-7uSaRy0z.js → chart-view-R1KaYiz7.js} +6 -6
- package/dist-standalone/chunks/{date-picker-field-BhEHDCJ1.js → date-picker-field-DMXrmpRX.js} +1 -1
- package/dist-standalone/chunks/{field-HgRGTmPZ.js → field-CWEzgjj4.js} +1 -1
- package/dist-standalone/chunks/{rich-editor-field-D76kQ6Fe.js → rich-editor-field-Byr29WU6.js} +2 -2
- package/dist-standalone/chunks/{runtime-BxB5Ladg.js → runtime-C4ZndQk5.js} +8 -9
- package/dist-standalone/chunks/{subscriptions-Cr5l3mYJ.js → subscriptions-CEmSNfBN.js} +1 -1
- package/dist-standalone/lattice.js +1 -1
- package/dist-standalone/manifest.json +1 -1
- package/dist-standalone/runtime.js +1 -1
- package/package.json +6 -10
- package/dist/chat/components/chat-box.d.ts +0 -3
- package/dist/chat/components/chat-box.js +0 -72
- package/dist/chat/components/chat-box.js.map +0 -1
- package/dist/chat/components/message-list.d.ts +0 -5
- package/dist/chat/components/message-list.js +0 -22
- package/dist/chat/components/message-list.js.map +0 -1
- package/dist/chat/components/message.d.ts +0 -5
- package/dist/chat/components/message.js +0 -20
- package/dist/chat/components/message.js.map +0 -1
- package/dist/chat/components/prompt-input.d.ts +0 -8
- package/dist/chat/components/prompt-input.js +0 -49
- package/dist/chat/components/prompt-input.js.map +0 -1
- package/dist/chat/hooks/use-chat.d.ts +0 -15
- package/dist/chat/hooks/use-chat.js +0 -162
- package/dist/chat/hooks/use-chat.js.map +0 -1
- package/dist/chat/index.d.ts +0 -4
- package/dist/chat/index.js +0 -3
- package/dist/chat/lib/transport.d.ts +0 -4
- package/dist/chat/lib/transport.js +0 -66
- package/dist/chat/lib/transport.js.map +0 -1
- package/dist/chat/parts/text.d.ts +0 -3
- package/dist/chat/parts/text.js +0 -12
- package/dist/chat/parts/text.js.map +0 -1
- package/dist/chat/parts/tool-call.d.ts +0 -3
- package/dist/chat/parts/tool-call.js +0 -20
- package/dist/chat/parts/tool-call.js.map +0 -1
- package/dist/chat/plugin.d.ts +0 -2
- package/dist/chat/plugin.js +0 -17
- package/dist/chat/plugin.js.map +0 -1
- package/dist/chat/types.d.ts +0 -31
- package/dist/chat/types.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{t}from"./react-CwJFpaho.js";import{G as n,W as r,
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{t}from"./react-CwJFpaho.js";import{G as n,W as r,_n as i}from"./runtime-C4ZndQk5.js";import{t as a}from"./jsx-runtime-NZYk81nU.js";import{c as o}from"./instance-D6cXaqIT.js";import{useEcho as s,useEchoPresence as c,useEchoPublic as l}from"./dist-D2qBbka0.js";var u=e(t(),1);function d(e,t,n){return e.map(e=>f(e,t,n))}function f(e,t,i){if(e.type!==`toast`)return e;let a=e.props;if(typeof a!=`object`||!a)return e;let o=a.message;return r(o)?{...e,props:{...a,message:n(o,t,i)}}:e}var p=e(a(),1);function m(e){let{t}=o(`lattice`),n=i();return(0,u.useCallback)(r=>{let i=typeof r==`object`&&r?r:{};n(d(e.effects,i,t))},[n,e,t])}function h({listener:e}){return l(e.channel,e.events,m(e)),null}function g({listener:e}){return s(e.channel,e.events,m(e)),null}function _({listener:e}){return c(e.channel,e.events,m(e)),null}function v({listener:e}){switch(e.visibility){case`private`:return(0,p.jsx)(g,{listener:e});case`presence`:return(0,p.jsx)(_,{listener:e});default:return(0,p.jsx)(h,{listener:e})}}function y({listeners:e}){return(0,p.jsx)(p.Fragment,{children:e.map((e,t)=>(0,p.jsx)(v,{listener:e},`${e.visibility}:${e.channel}:${t}`))})}export{y as default};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./chunks/dist-D2qBbka0.js","./chunks/rolldown-runtime-hePW80VL.js","./chunks/react-CwJFpaho.js"])))=>i.map(i=>d[i]);
|
|
2
|
-
import{$
|
|
2
|
+
import{$n as e,Qn as t,Zt as n,m as r,y as i}from"./chunks/runtime-C4ZndQk5.js";function a(){return console.warn(`[lattice] The data-lattice-config script holds invalid JSON; booting with defaults.`),{}}function o(e){let t=e.querySelector(`script[data-lattice-config]`);if(!t?.textContent)return{};try{let e=JSON.parse(t.textContent);return typeof e!=`object`||!e||Array.isArray(e)?a():e}catch{return a()}}var s=o(document);async function c(){let a=await t(s.plugins??[]);if(s.refreshRefUrl&&n(s.refreshRefUrl),s.echo)try{let{configureEcho:t}=await e(async()=>{let{configureEcho:e}=await import(`./chunks/dist-D2qBbka0.js`);return{configureEcho:e}},__vite__mapDeps([0,1,2]),import.meta.url);t(s.echo)}catch(e){console.warn(`[lattice] Failed to load the realtime chunk; continuing without Echo.`,e)}r({plugins:a,...s.spriteUrl?{sprite:{href:s.spriteUrl}}:{},defaults:{visitOptions:i}})}var l=c();export{l as booted};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{$ as e,$
|
|
1
|
+
import{$ as e,$t as t,A as n,An as r,At as i,B as a,Bt as o,C as s,Cn as c,Ct as l,D as u,Dn as d,Dt as f,En as p,Et as m,F as h,Ft as g,Gt as _,H as v,Ht as y,I as b,It as x,J as S,Jn as C,Jt as w,Kt as T,L as E,Lt as D,M as O,Mt as k,N as A,Nt as j,O as M,On as N,Ot as P,P as F,Pt as I,Q as L,Qn as R,Qt as z,R as B,Rt as V,S as H,Sn as U,St as W,T as G,Tn as K,Tt as q,U as J,Ut as Y,V as X,Vt as Z,Wt as Q,X as $,Xn as ee,Xt as te,Y as ne,Yn as re,Yt as ie,Z as ae,Zn as oe,Zt as se,_ as ce,_n as le,a as ue,an as de,at as fe,b as pe,bn as me,bt as he,c as ge,cn as _e,ct as ve,dn as ye,dt as be,en as xe,et as Se,fn as Ce,ft as we,g as Te,gn as Ee,h as De,hn as Oe,i as ke,in as Ae,it as je,j as Me,jn as Ne,jt as Pe,kn as Fe,kt as Ie,ln as Le,m as Re,mn as ze,n as Be,nn as Ve,nt as He,o as Ue,on as We,ot as Ge,p as Ke,pn as qe,qt as Je,r as Ye,rn as Xe,rt as Ze,s as Qe,sn as $e,st as et,t as tt,tn as nt,tt as rt,un as it,ut as at,v as ot,vn as st,w as ct,wn as lt,wt as ut,x as dt,xn as ft,xt as pt,y as mt,yn as ht,z as gt,zt as _t}from"./chunks/runtime-C4ZndQk5.js";import{a as vt,n as yt,r as bt,s as xt,t as St}from"./chunks/locale-BZOrJtMq.js";import{i as Ct,r as wt}from"./chunks/registry-context-Q5KtSmdi.js";import{_ as Tt,h as Et,m as Dt}from"./chunks/floating-ui.dom-DRSJZ5ii.js";import{a as Ot,c as kt,n as At,s as jt}from"./chunks/instance-D6cXaqIT.js";import{i as Mt,n as Nt,r as Pt,t as Ft}from"./chunks/timezone-Ckl1gbqB.js";import{t as It}from"./chunks/input-C-Wm1w0g.js";import{i as Lt,t as Rt}from"./chunks/color-CXV-Y5Qv.js";export{et as AddRowMenu,Me as Badge,c as Button,a as Callouts,Ze as Checkbox,M as CodeBlock,ae as Combobox,Ve as ConfirmDialog,S as CopyButton,$e as DateTime,Xe as Dialog,Ae as DialogContent,de as DialogHeader,De as EventBridge,f as FieldScopeProvider,at as FormFieldFrame,Dt as Icon,L as IconButton,lt as IconRenderer,K as IconRendererProvider,we as InfoTooltip,It as Input,xt as LATTICE_EVENT,xe as LATTICE_REF_HEADER,be as Label,ye as LocaleReload,F as NativeSelect,ze as NavigationProvider,gt as OutletContext,n as PreviewableImage,Ke as Provider,ge as RICH_EDITOR_EXTENSION,Ie as ROW_ID_KEY,pe as RealtimeListeners,Q as RenderNode,_ as Renderer,Ge as RowKeyInputs,dt as SchemaLayout,He as SegmentedPills,T as SidebarCollapsedContext,rt as SimpleField,We as Spinner,Et as SpriteProvider,$ as Textarea,X as Toaster,Qe as ToolbarIconButton,At as UI_NAMESPACE,i as addRow,tt as announce,ie as apiFetch,te as apiJson,D as appendPath,Be as attachTreeItemInstruction,N as builtinEffectHandlers,Tt as cn,Rt as coerceColor,O as columnCell,Ue as combine,Ce as configureI18n,_e as configureI18nFromPageProps,ne as copyToClipboard,Re as createLatticeApp,Te as createLayoutResolver,ce as createPageResolver,C as createRegistry,St as currentLocale,Ft as currentTimezone,Oe as defaultNavigation,st as dispatchActionError,ht as dispatchEffects,ke as draggable,ue as dropTargetForElements,Pe as duplicateRow,re as eagerComponent,Fe as effectHandler,qe as enableBackend,k as ensureRowIds,ee as extendRegistry,Ye as extractTreeItemInstruction,Z as fieldProps,Mt as formatDateValue,me as getActionEffects,h as getBulkActions,V as getPath,Ot as i18n,Le as i18nConfigFromPageProps,p as inertiaNavigation,H as initializeAppearance,ft as isActionEffect,B as layoutComponents,oe as lazyComponent,R as loadPluginModules,yt as localeHeader,r as mergeEffectHandlers,j as moveRow,d as navigationPlugin,U as nodeIdentity,v as onCallout,J as onToast,ot as pageComponentName,G as registry,I as removeRow,e as requestSignedUpload,Ne as router,pt as rowSchemaFor,W as rowTemplatesOf,w as runAction,g as seedRows,bt as setLocale,_t as setPath,se as setRefRefreshEndpoint,Nt as setTimezone,o as toHtmlName,Lt as toneProps,jt as translate,s as updateAppearance,ct as useAppearance,A as useColumnRegistry,wt as useComponentRegistry,he as useDebouncedCallback,ve as useDependentField,le as useEffectDispatcher,Ct as useExtensionRegistry,l as useFieldCommit,P as useFieldScope,Y as useFormContext,ut as useFormValue,q as useFormValues,u as useFormatContext,vt as useLocale,it as useLocaleOptions,Ee as useNavigation,fe as usePersistentState,je as useRowCollection,m as useSetFormValue,Je as useSidebarCollapsed,kt as useT,E as useTable,b as useTableSelection,Pt as useTimezone,y as walkFields,t as withHeaders,nt as withRefHeader,x as withRowId,mt as withVisitHeaders,Se as xhrTransfer,z as xsrfToken};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lattice-php/lattice",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "Server-driven React components for Laravel and Inertia.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Manuel Christlieb <manuel@christlieb.eu>",
|
|
@@ -36,10 +36,6 @@
|
|
|
36
36
|
"types": "./dist/appearance/index.d.ts",
|
|
37
37
|
"import": "./dist/appearance/index.js"
|
|
38
38
|
},
|
|
39
|
-
"./chat": {
|
|
40
|
-
"types": "./dist/chat/index.d.ts",
|
|
41
|
-
"import": "./dist/chat/index.js"
|
|
42
|
-
},
|
|
43
39
|
"./dnd": {
|
|
44
40
|
"types": "./dist/dnd/index.d.ts",
|
|
45
41
|
"import": "./dist/dnd/index.js"
|
|
@@ -117,11 +113,11 @@
|
|
|
117
113
|
"typecheck": "tsc --noEmit"
|
|
118
114
|
},
|
|
119
115
|
"dependencies": {
|
|
120
|
-
"@lattice-php/action": "0.
|
|
121
|
-
"@lattice-php/core": "0.
|
|
122
|
-
"@lattice-php/form": "0.
|
|
123
|
-
"@lattice-php/table": "0.
|
|
124
|
-
"@lattice-php/ui": "0.
|
|
116
|
+
"@lattice-php/action": "0.52.0",
|
|
117
|
+
"@lattice-php/core": "0.52.0",
|
|
118
|
+
"@lattice-php/form": "0.52.0",
|
|
119
|
+
"@lattice-php/table": "0.52.0",
|
|
120
|
+
"@lattice-php/ui": "0.52.0",
|
|
125
121
|
"@lattice-php/vite-svg-sprite": "^0.3.0",
|
|
126
122
|
"@atlaskit/pragmatic-drag-and-drop": "^2.0.2",
|
|
127
123
|
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "^3.0.0",
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { createRemoteNdjsonChatTransport, ndjsonChatTransport } from "../lib/transport.js";
|
|
2
|
-
import { useChat } from "../hooks/use-chat.js";
|
|
3
|
-
import { MessageList } from "./message-list.js";
|
|
4
|
-
import { PromptInput } from "./prompt-input.js";
|
|
5
|
-
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
6
|
-
import { apiFetch, remoteJson } from "@lattice-php/core/api";
|
|
7
|
-
import { cn } from "@lattice-php/ui/lib/utils";
|
|
8
|
-
import { useT } from "@lattice-php/ui/i18n";
|
|
9
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
-
import { testIdentity } from "@lattice-php/core/test-id";
|
|
11
|
-
//#region resources/js/chat/components/chat-box.tsx
|
|
12
|
-
var ChatBox = ({ node }) => {
|
|
13
|
-
const props = node.props;
|
|
14
|
-
const { t } = useT("lattice");
|
|
15
|
-
const transport = useMemo(() => props.remote ? createRemoteNdjsonChatTransport(props.remote) : ndjsonChatTransport, [props.remote]);
|
|
16
|
-
const { messages, status, error, sendMessage, setMessages } = useChat({
|
|
17
|
-
endpoint: props.streamEndpoint ?? "",
|
|
18
|
-
transport
|
|
19
|
-
});
|
|
20
|
-
const seededRef = useRef(false);
|
|
21
|
-
const seedHistory = useCallback(async () => {
|
|
22
|
-
if (seededRef.current || !props.historyEndpoint) return;
|
|
23
|
-
seededRef.current = true;
|
|
24
|
-
if (props.remote) {
|
|
25
|
-
const payload = await remoteJson(props.historyEndpoint, { remote: props.remote });
|
|
26
|
-
setMessages(payload.messages);
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
const response = await apiFetch(props.historyEndpoint, { throwOnError: false });
|
|
30
|
-
if (!response.ok) return;
|
|
31
|
-
const payload = await response.json();
|
|
32
|
-
setMessages(payload.messages);
|
|
33
|
-
}, [
|
|
34
|
-
props.historyEndpoint,
|
|
35
|
-
props.remote,
|
|
36
|
-
setMessages
|
|
37
|
-
]);
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
seedHistory().catch(() => {});
|
|
40
|
-
}, [seedHistory]);
|
|
41
|
-
const disabled = status === "submitted" || status === "streaming" || !props.streamEndpoint;
|
|
42
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
43
|
-
className: cn("flex flex-col overflow-hidden border border-lt-border bg-lt-bg", props.fill ? "sticky top-0 h-full min-h-[28rem] w-full" : "h-[28rem] w-80 rounded-lt shadow-lt-lg"),
|
|
44
|
-
"data-test": testIdentity("chat-box"),
|
|
45
|
-
children: [
|
|
46
|
-
/* @__PURE__ */ jsx("div", {
|
|
47
|
-
className: "flex items-center border-b border-lt-border px-3 py-2",
|
|
48
|
-
children: /* @__PURE__ */ jsx("span", {
|
|
49
|
-
className: "text-sm font-medium text-lt-fg",
|
|
50
|
-
children: props.title ?? t("common.chat.title", "Chat")
|
|
51
|
-
})
|
|
52
|
-
}),
|
|
53
|
-
/* @__PURE__ */ jsx("div", {
|
|
54
|
-
className: "flex-1 overflow-y-auto",
|
|
55
|
-
children: /* @__PURE__ */ jsx(MessageList, { messages })
|
|
56
|
-
}),
|
|
57
|
-
error ? /* @__PURE__ */ jsx("div", {
|
|
58
|
-
className: "border-t border-lt-danger/40 px-3 py-2 text-xs text-lt-danger",
|
|
59
|
-
children: error
|
|
60
|
-
}) : null,
|
|
61
|
-
/* @__PURE__ */ jsx(PromptInput, {
|
|
62
|
-
disabled,
|
|
63
|
-
onSubmit: sendMessage,
|
|
64
|
-
placeholder: props.placeholder ?? void 0
|
|
65
|
-
})
|
|
66
|
-
]
|
|
67
|
-
});
|
|
68
|
-
};
|
|
69
|
-
//#endregion
|
|
70
|
-
export { ChatBox, ChatBox as default };
|
|
71
|
-
|
|
72
|
-
//# sourceMappingURL=chat-box.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"chat-box.js","names":[],"sources":["../../../resources/js/chat/components/chat-box.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { apiFetch, remoteJson } from \"@lattice-php/core/api\";\nimport { testIdentity } from \"@lattice-php/core/test-id\";\nimport type { RendererComponent } from \"@lattice-php/core/types\";\nimport type { ChatBox as ChatBoxProps } from \"@lattice-php/lattice/types/generated\";\nimport { useT } from \"@lattice-php/ui/i18n\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport {\n createRemoteNdjsonChatTransport,\n ndjsonChatTransport,\n} from \"@lattice-php/lattice/chat/lib/transport\";\nimport { useChat } from \"@lattice-php/lattice/chat/hooks/use-chat\";\nimport type { ChatMessage } from \"@lattice-php/lattice/chat/types\";\nimport { MessageList } from \"./message-list\";\nimport { PromptInput } from \"./prompt-input\";\n\ntype ChatHistoryResponse = { messages: ChatMessage[] };\n\nexport const ChatBox: RendererComponent<\"chat.box\"> = ({ node }) => {\n const props: ChatBoxProps = node.props;\n const { t } = useT(\"lattice\");\n const transport = useMemo(\n () => (props.remote ? createRemoteNdjsonChatTransport(props.remote) : ndjsonChatTransport),\n [props.remote],\n );\n const { messages, status, error, sendMessage, setMessages } = useChat({\n endpoint: props.streamEndpoint ?? \"\",\n transport,\n });\n\n const seededRef = useRef(false);\n\n const seedHistory = useCallback(async (): Promise<void> => {\n if (seededRef.current || !props.historyEndpoint) {\n return;\n }\n\n seededRef.current = true;\n\n if (props.remote) {\n const payload = await remoteJson<ChatHistoryResponse>(props.historyEndpoint, {\n remote: props.remote,\n });\n setMessages(payload.messages);\n\n return;\n }\n\n const response = await apiFetch(props.historyEndpoint, { throwOnError: false });\n\n if (!response.ok) {\n return;\n }\n\n const payload = (await response.json()) as ChatHistoryResponse;\n setMessages(payload.messages);\n }, [props.historyEndpoint, props.remote, setMessages]);\n\n useEffect(() => {\n void seedHistory().catch(() => {});\n }, [seedHistory]);\n\n const busy = status === \"submitted\" || status === \"streaming\";\n const disabled = busy || !props.streamEndpoint;\n\n return (\n <div\n className={cn(\n \"flex flex-col overflow-hidden border border-lt-border bg-lt-bg\",\n props.fill\n ? \"sticky top-0 h-full min-h-[28rem] w-full\"\n : \"h-[28rem] w-80 rounded-lt shadow-lt-lg\",\n )}\n data-test={testIdentity(\"chat-box\")}\n >\n <div className=\"flex items-center border-b border-lt-border px-3 py-2\">\n <span className=\"text-sm font-medium text-lt-fg\">\n {props.title ?? t(\"common.chat.title\", \"Chat\")}\n </span>\n </div>\n <div className=\"flex-1 overflow-y-auto\">\n <MessageList messages={messages} />\n </div>\n {error ? (\n <div className=\"border-t border-lt-danger/40 px-3 py-2 text-xs text-lt-danger\">{error}</div>\n ) : null}\n <PromptInput\n disabled={disabled}\n onSubmit={sendMessage}\n placeholder={props.placeholder ?? undefined}\n />\n </div>\n );\n};\n\nexport default ChatBox;\n"],"mappings":";;;;;;;;;;;AAkBA,IAAa,WAA0C,EAAE,WAAW;CAClE,MAAM,QAAsB,KAAK;CACjC,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,YAAY,cACT,MAAM,SAAS,gCAAgC,MAAM,MAAM,IAAI,qBACtE,CAAC,MAAM,MAAM,CACf;CACA,MAAM,EAAE,UAAU,QAAQ,OAAO,aAAa,gBAAgB,QAAQ;EACpE,UAAU,MAAM,kBAAkB;EAClC;CACF,CAAC;CAED,MAAM,YAAY,OAAO,KAAK;CAE9B,MAAM,cAAc,YAAY,YAA2B;EACzD,IAAI,UAAU,WAAW,CAAC,MAAM,iBAC9B;EAGF,UAAU,UAAU;EAEpB,IAAI,MAAM,QAAQ;GAChB,MAAM,UAAU,MAAM,WAAgC,MAAM,iBAAiB,EAC3E,QAAQ,MAAM,OAChB,CAAC;GACD,YAAY,QAAQ,QAAQ;GAE5B;EACF;EAEA,MAAM,WAAW,MAAM,SAAS,MAAM,iBAAiB,EAAE,cAAc,MAAM,CAAC;EAE9E,IAAI,CAAC,SAAS,IACZ;EAGF,MAAM,UAAW,MAAM,SAAS,KAAK;EACrC,YAAY,QAAQ,QAAQ;CAC9B,GAAG;EAAC,MAAM;EAAiB,MAAM;EAAQ;CAAW,CAAC;CAErD,gBAAgB;EACd,YAAiB,CAAC,CAAC,YAAY,CAAC,CAAC;CACnC,GAAG,CAAC,WAAW,CAAC;CAGhB,MAAM,WADO,WAAW,eAAe,WAAW,eACzB,CAAC,MAAM;CAEhC,OACE,qBAAC,OAAD;EACE,WAAW,GACT,kEACA,MAAM,OACF,6CACA,wCACN;EACA,aAAW,aAAa,UAAU;EAPpC,UAAA;GASE,oBAAC,OAAD;IAAK,WAAU;IACb,UAAA,oBAAC,QAAD;KAAM,WAAU;KACb,UAAA,MAAM,SAAS,EAAE,qBAAqB,MAAM;IACzC,CAAA;GACH,CAAA;GACL,oBAAC,OAAD;IAAK,WAAU;IACb,UAAA,oBAAC,aAAD,EAAuB,SAAW,CAAA;GAC/B,CAAA;GACJ,QACC,oBAAC,OAAD;IAAK,WAAU;IAAiE,UAAA;GAAW,CAAA,IACzF;GACJ,oBAAC,aAAD;IACY;IACV,UAAU;IACV,aAAa,MAAM,eAAe,KAAA;GACnC,CAAA;EACE;;AAET"}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { Message } from "./message.js";
|
|
2
|
-
import { useEffect, useRef } from "react";
|
|
3
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
-
import { testIdentity } from "@lattice-php/core/test-id";
|
|
5
|
-
//#region resources/js/chat/components/message-list.tsx
|
|
6
|
-
function MessageList({ messages }) {
|
|
7
|
-
const bottomRef = useRef(null);
|
|
8
|
-
const lastMessage = messages.at(-1);
|
|
9
|
-
const lastPartLength = lastMessage?.parts.at(-1) !== void 0 ? JSON.stringify(lastMessage.parts.at(-1)).length : 0;
|
|
10
|
-
useEffect(() => {
|
|
11
|
-
if (bottomRef.current && typeof bottomRef.current.scrollIntoView === "function") bottomRef.current.scrollIntoView({ behavior: "smooth" });
|
|
12
|
-
}, [messages.length, lastPartLength]);
|
|
13
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
14
|
-
className: "flex flex-col gap-2 overflow-y-auto p-3",
|
|
15
|
-
"data-test": testIdentity("chat-messages"),
|
|
16
|
-
children: [messages.map((message) => /* @__PURE__ */ jsx(Message, { message }, message.id)), /* @__PURE__ */ jsx("div", { ref: bottomRef })]
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { MessageList };
|
|
21
|
-
|
|
22
|
-
//# sourceMappingURL=message-list.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message-list.js","names":[],"sources":["../../../resources/js/chat/components/message-list.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { testIdentity } from \"@lattice-php/core/test-id\";\nimport type { ChatMessage } from \"@lattice-php/lattice/chat/types\";\nimport { Message } from \"./message\";\n\nexport function MessageList({ messages }: { messages: ChatMessage[] }): ReactNode {\n const bottomRef = useRef<HTMLDivElement>(null);\n const lastMessage = messages.at(-1);\n const lastPartLength =\n lastMessage?.parts.at(-1) !== undefined ? JSON.stringify(lastMessage.parts.at(-1)).length : 0;\n\n useEffect(() => {\n if (bottomRef.current && typeof bottomRef.current.scrollIntoView === \"function\") {\n bottomRef.current.scrollIntoView({ behavior: \"smooth\" });\n }\n }, [messages.length, lastPartLength]);\n\n return (\n <div\n className=\"flex flex-col gap-2 overflow-y-auto p-3\"\n data-test={testIdentity(\"chat-messages\")}\n >\n {messages.map((message) => (\n <Message key={message.id} message={message} />\n ))}\n <div ref={bottomRef} />\n </div>\n );\n}\n"],"mappings":";;;;;AAMA,SAAgB,YAAY,EAAE,YAAoD;CAChF,MAAM,YAAY,OAAuB,IAAI;CAC7C,MAAM,cAAc,SAAS,GAAG,EAAE;CAClC,MAAM,iBACJ,aAAa,MAAM,GAAG,EAAE,MAAM,KAAA,IAAY,KAAK,UAAU,YAAY,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;CAE9F,gBAAgB;EACd,IAAI,UAAU,WAAW,OAAO,UAAU,QAAQ,mBAAmB,YACnE,UAAU,QAAQ,eAAe,EAAE,UAAU,SAAS,CAAC;CAE3D,GAAG,CAAC,SAAS,QAAQ,cAAc,CAAC;CAEpC,OACE,qBAAC,OAAD;EACE,WAAU;EACV,aAAW,aAAa,eAAe;EAFzC,UAAA,CAIG,SAAS,KAAK,YACb,oBAAC,SAAD,EAAmC,QAAU,GAA/B,QAAQ,EAAuB,CAC9C,GACD,oBAAC,OAAD,EAAK,KAAK,UAAY,CAAA,CACnB;;AAET"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { RenderNode } from "@lattice-php/core/renderer";
|
|
2
|
-
import { cn } from "@lattice-php/ui/lib/utils";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import { testIdentity } from "@lattice-php/core/test-id";
|
|
5
|
-
//#region resources/js/chat/components/message.tsx
|
|
6
|
-
function Message({ message }) {
|
|
7
|
-
const isUser = message.role === "user";
|
|
8
|
-
return /* @__PURE__ */ jsx("div", {
|
|
9
|
-
className: cn("flex flex-col gap-1", isUser ? "items-end" : "items-start"),
|
|
10
|
-
"data-test": testIdentity(`chat-message-${message.role}`),
|
|
11
|
-
children: /* @__PURE__ */ jsx("div", {
|
|
12
|
-
className: cn("max-w-[80%] rounded-lt px-3 py-2 text-sm", isUser ? "bg-lt-primary text-lt-primary-fg" : "bg-lt-muted text-lt-fg"),
|
|
13
|
-
children: message.parts.map((part, index) => /* @__PURE__ */ jsx(RenderNode, { node: part }, part.key ?? index))
|
|
14
|
-
})
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
//#endregion
|
|
18
|
-
export { Message };
|
|
19
|
-
|
|
20
|
-
//# sourceMappingURL=message.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"message.js","names":[],"sources":["../../../resources/js/chat/components/message.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport { testIdentity } from \"@lattice-php/core/test-id\";\nimport { RenderNode } from \"@lattice-php/core/renderer\";\nimport type { ChatMessage } from \"@lattice-php/lattice/chat/types\";\n\nexport function Message({ message }: { message: ChatMessage }): ReactNode {\n const isUser = message.role === \"user\";\n\n return (\n <div\n className={cn(\"flex flex-col gap-1\", isUser ? \"items-end\" : \"items-start\")}\n data-test={testIdentity(`chat-message-${message.role}`)}\n >\n <div\n className={cn(\n \"max-w-[80%] rounded-lt px-3 py-2 text-sm\",\n isUser ? \"bg-lt-primary text-lt-primary-fg\" : \"bg-lt-muted text-lt-fg\",\n )}\n >\n {message.parts.map((part, index) => (\n <RenderNode key={part.key ?? index} node={part} />\n ))}\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;AAMA,SAAgB,QAAQ,EAAE,WAAgD;CACxE,MAAM,SAAS,QAAQ,SAAS;CAEhC,OACE,oBAAC,OAAD;EACE,WAAW,GAAG,uBAAuB,SAAS,cAAc,aAAa;EACzE,aAAW,aAAa,gBAAgB,QAAQ,MAAM;EAEtD,UAAA,oBAAC,OAAD;GACE,WAAW,GACT,4CACA,SAAS,qCAAqC,wBAChD;GAEC,UAAA,QAAQ,MAAM,KAAK,MAAM,UACxB,oBAAC,YAAD,EAAoC,MAAM,KAAO,GAAhC,KAAK,OAAO,KAAoB,CAClD;EACE,CAAA;CACF,CAAA;AAET"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
2
|
-
type PromptInputProps = {
|
|
3
|
-
onSubmit: (text: string) => void;
|
|
4
|
-
disabled?: boolean;
|
|
5
|
-
placeholder?: string;
|
|
6
|
-
};
|
|
7
|
-
export declare function PromptInput({ onSubmit, disabled, placeholder, }: PromptInputProps): ReactNode;
|
|
8
|
-
export {};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import { cn } from "@lattice-php/ui/lib/utils";
|
|
3
|
-
import { useT } from "@lattice-php/ui/i18n";
|
|
4
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
-
import { testIdentity } from "@lattice-php/core/test-id";
|
|
6
|
-
import { Button } from "@lattice-php/ui/components/button";
|
|
7
|
-
//#region resources/js/chat/components/prompt-input.tsx
|
|
8
|
-
function PromptInput({ onSubmit, disabled = false, placeholder }) {
|
|
9
|
-
const { t } = useT("lattice");
|
|
10
|
-
const [value, setValue] = useState("");
|
|
11
|
-
function submit() {
|
|
12
|
-
const trimmed = value.trim();
|
|
13
|
-
if (trimmed === "" || disabled) return;
|
|
14
|
-
onSubmit(trimmed);
|
|
15
|
-
setValue("");
|
|
16
|
-
}
|
|
17
|
-
function handleKeyDown(event) {
|
|
18
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
19
|
-
event.preventDefault();
|
|
20
|
-
submit();
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return /* @__PURE__ */ jsxs("div", {
|
|
24
|
-
className: "flex items-end gap-2 border-t border-lt-border p-3",
|
|
25
|
-
children: [/* @__PURE__ */ jsx("textarea", {
|
|
26
|
-
"aria-label": t("common.chat.input-label", "Message input"),
|
|
27
|
-
className: cn("min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-[length:var(--lt-ring-width)] focus:ring-lt-ring/50 disabled:bg-lt-disabled disabled:text-lt-disabled-fg"),
|
|
28
|
-
"data-slot": "prompt-input",
|
|
29
|
-
"data-test": testIdentity("chat-input"),
|
|
30
|
-
disabled,
|
|
31
|
-
onChange: (e) => setValue(e.target.value),
|
|
32
|
-
onKeyDown: handleKeyDown,
|
|
33
|
-
placeholder,
|
|
34
|
-
rows: 1,
|
|
35
|
-
value
|
|
36
|
-
}), /* @__PURE__ */ jsx(Button, {
|
|
37
|
-
"data-test": testIdentity("chat-send"),
|
|
38
|
-
disabled,
|
|
39
|
-
onClick: submit,
|
|
40
|
-
size: "sm",
|
|
41
|
-
type: "button",
|
|
42
|
-
children: t("common.chat.send", "Send")
|
|
43
|
-
})]
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
//#endregion
|
|
47
|
-
export { PromptInput };
|
|
48
|
-
|
|
49
|
-
//# sourceMappingURL=prompt-input.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-input.js","names":[],"sources":["../../../resources/js/chat/components/prompt-input.tsx"],"sourcesContent":["import { useState } from \"react\";\nimport type { KeyboardEvent, ReactNode } from \"react\";\nimport { cn } from \"@lattice-php/ui/lib/utils\";\nimport { testIdentity } from \"@lattice-php/core/test-id\";\nimport { Button } from \"@lattice-php/ui/components/button\";\nimport { useT } from \"@lattice-php/ui/i18n\";\n\ntype PromptInputProps = {\n onSubmit: (text: string) => void;\n disabled?: boolean;\n placeholder?: string;\n};\n\nexport function PromptInput({\n onSubmit,\n disabled = false,\n placeholder,\n}: PromptInputProps): ReactNode {\n const { t } = useT(\"lattice\");\n const [value, setValue] = useState(\"\");\n\n function submit(): void {\n const trimmed = value.trim();\n if (trimmed === \"\" || disabled) {\n return;\n }\n\n onSubmit(trimmed);\n setValue(\"\");\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>): void {\n if (event.key === \"Enter\" && !event.shiftKey) {\n event.preventDefault();\n submit();\n }\n }\n\n return (\n <div className=\"flex items-end gap-2 border-t border-lt-border p-3\">\n <textarea\n aria-label={t(\"common.chat.input-label\", \"Message input\")}\n className={cn(\n \"min-h-[2.5rem] flex-1 resize-none rounded-lt-sm border border-lt-input bg-lt-bg px-3 py-2 text-sm text-lt-fg placeholder:text-lt-muted-fg focus:outline-none focus:ring-[length:var(--lt-ring-width)] focus:ring-lt-ring/50 disabled:bg-lt-disabled disabled:text-lt-disabled-fg\",\n )}\n data-slot=\"prompt-input\"\n data-test={testIdentity(\"chat-input\")}\n disabled={disabled}\n onChange={(e) => setValue(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={placeholder}\n rows={1}\n value={value}\n />\n <Button\n data-test={testIdentity(\"chat-send\")}\n disabled={disabled}\n onClick={submit}\n size=\"sm\"\n type=\"button\"\n >\n {t(\"common.chat.send\", \"Send\")}\n </Button>\n </div>\n );\n}\n"],"mappings":";;;;;;;AAaA,SAAgB,YAAY,EAC1B,UACA,WAAW,OACX,eAC8B;CAC9B,MAAM,EAAE,MAAM,KAAK,SAAS;CAC5B,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CAErC,SAAS,SAAe;EACtB,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,YAAY,MAAM,UACpB;EAGF,SAAS,OAAO;EAChB,SAAS,EAAE;CACb;CAEA,SAAS,cAAc,OAAiD;EACtE,IAAI,MAAM,QAAQ,WAAW,CAAC,MAAM,UAAU;GAC5C,MAAM,eAAe;GACrB,OAAO;EACT;CACF;CAEA,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,oBAAC,YAAD;GACE,cAAY,EAAE,2BAA2B,eAAe;GACxD,WAAW,GACT,kRACF;GACA,aAAU;GACV,aAAW,aAAa,YAAY;GAC1B;GACV,WAAW,MAAM,SAAS,EAAE,OAAO,KAAK;GACxC,WAAW;GACE;GACb,MAAM;GACC;EACR,CAAA,GACD,oBAAC,QAAD;GACE,aAAW,aAAa,WAAW;GACzB;GACV,SAAS;GACT,MAAK;GACL,MAAK;GAEJ,UAAA,EAAE,oBAAoB,MAAM;EACvB,CAAA,CACL;;AAET"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { ChatFrame, ChatMessage, ChatTransport, UseChatReturn } from '../types.js';
|
|
2
|
-
export type UseChatOptions = {
|
|
3
|
-
endpoint: string;
|
|
4
|
-
transport?: ChatTransport;
|
|
5
|
-
initialMessages?: ChatMessage[];
|
|
6
|
-
generateId?: () => string;
|
|
7
|
-
};
|
|
8
|
-
/**
|
|
9
|
-
* Pure reducer for a single streamed frame: a `text` frame appends to the open
|
|
10
|
-
* assistant text part (opening one if the last part is not text); a `part` frame
|
|
11
|
-
* pushes a complete part, which closes the open text part. Operates immutably on
|
|
12
|
-
* the last assistant message.
|
|
13
|
-
*/
|
|
14
|
-
export declare function foldFrame(messages: ChatMessage[], frame: ChatFrame): ChatMessage[];
|
|
15
|
-
export declare function useChat({ endpoint, transport, initialMessages, generateId, }: UseChatOptions): UseChatReturn;
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { ndjsonChatTransport } from "../lib/transport.js";
|
|
2
|
-
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
|
-
//#region resources/js/chat/hooks/use-chat.ts
|
|
4
|
-
var idCounter = 0;
|
|
5
|
-
function defaultGenerateId() {
|
|
6
|
-
idCounter += 1;
|
|
7
|
-
return `chat-${idCounter}`;
|
|
8
|
-
}
|
|
9
|
-
function isAbortError(error) {
|
|
10
|
-
return error instanceof DOMException && error.name === "AbortError";
|
|
11
|
-
}
|
|
12
|
-
function errorMessage(error) {
|
|
13
|
-
return error instanceof Error ? error.message : String(error);
|
|
14
|
-
}
|
|
15
|
-
function lastIsAssistant(messages) {
|
|
16
|
-
const last = messages.at(-1);
|
|
17
|
-
return last !== void 0 && last.role === "assistant";
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Pure reducer for a single streamed frame: a `text` frame appends to the open
|
|
21
|
-
* assistant text part (opening one if the last part is not text); a `part` frame
|
|
22
|
-
* pushes a complete part, which closes the open text part. Operates immutably on
|
|
23
|
-
* the last assistant message.
|
|
24
|
-
*/
|
|
25
|
-
function foldFrame(messages, frame) {
|
|
26
|
-
if (!lastIsAssistant(messages)) return messages;
|
|
27
|
-
const index = messages.length - 1;
|
|
28
|
-
const target = messages[index];
|
|
29
|
-
if (frame.type === "text") {
|
|
30
|
-
const parts = [...target.parts];
|
|
31
|
-
const openIndex = parts.length - 1;
|
|
32
|
-
const open = parts[openIndex];
|
|
33
|
-
if (open !== void 0 && open.type === "chat.part.text") parts[openIndex] = {
|
|
34
|
-
type: "chat.part.text",
|
|
35
|
-
props: { text: `${open.props.text}${frame.value}` }
|
|
36
|
-
};
|
|
37
|
-
else parts.push({
|
|
38
|
-
type: "chat.part.text",
|
|
39
|
-
props: { text: frame.value }
|
|
40
|
-
});
|
|
41
|
-
const next = [...messages];
|
|
42
|
-
next[index] = {
|
|
43
|
-
...target,
|
|
44
|
-
parts
|
|
45
|
-
};
|
|
46
|
-
return next;
|
|
47
|
-
}
|
|
48
|
-
if (frame.type === "part") {
|
|
49
|
-
const next = [...messages];
|
|
50
|
-
next[index] = {
|
|
51
|
-
...target,
|
|
52
|
-
parts: [...target.parts, frame.part]
|
|
53
|
-
};
|
|
54
|
-
return next;
|
|
55
|
-
}
|
|
56
|
-
return messages;
|
|
57
|
-
}
|
|
58
|
-
function useChat({ endpoint, transport = ndjsonChatTransport, initialMessages = [], generateId = defaultGenerateId }) {
|
|
59
|
-
const [messages, setMessagesState] = useState(initialMessages);
|
|
60
|
-
const [status, setStatus] = useState("idle");
|
|
61
|
-
const [error, setError] = useState(null);
|
|
62
|
-
const abortRef = useRef(null);
|
|
63
|
-
const lastUserTextRef = useRef(null);
|
|
64
|
-
const messagesRef = useRef(messages);
|
|
65
|
-
messagesRef.current = messages;
|
|
66
|
-
const commitMessages = useCallback((next) => {
|
|
67
|
-
messagesRef.current = next;
|
|
68
|
-
setMessagesState(next);
|
|
69
|
-
}, []);
|
|
70
|
-
const runTurn = useCallback(async (history, userText) => {
|
|
71
|
-
abortRef.current?.abort();
|
|
72
|
-
const controller = new AbortController();
|
|
73
|
-
abortRef.current = controller;
|
|
74
|
-
setError(null);
|
|
75
|
-
setStatus("submitted");
|
|
76
|
-
const userMessage = {
|
|
77
|
-
id: generateId(),
|
|
78
|
-
role: "user",
|
|
79
|
-
parts: [{
|
|
80
|
-
type: "chat.part.text",
|
|
81
|
-
props: { text: userText }
|
|
82
|
-
}]
|
|
83
|
-
};
|
|
84
|
-
const assistantMessage = {
|
|
85
|
-
id: generateId(),
|
|
86
|
-
role: "assistant",
|
|
87
|
-
parts: []
|
|
88
|
-
};
|
|
89
|
-
commitMessages([
|
|
90
|
-
...history,
|
|
91
|
-
userMessage,
|
|
92
|
-
assistantMessage
|
|
93
|
-
]);
|
|
94
|
-
setStatus("streaming");
|
|
95
|
-
try {
|
|
96
|
-
for await (const frame of transport({
|
|
97
|
-
url: endpoint,
|
|
98
|
-
body: { message: userText },
|
|
99
|
-
signal: controller.signal
|
|
100
|
-
})) {
|
|
101
|
-
if (frame.type === "done") {
|
|
102
|
-
setStatus("idle");
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
if (frame.type === "error") {
|
|
106
|
-
setError(frame.message ?? "Chat failed");
|
|
107
|
-
setStatus("error");
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
commitMessages(foldFrame(messagesRef.current, frame));
|
|
111
|
-
}
|
|
112
|
-
setStatus((current) => current === "streaming" || current === "submitted" ? "idle" : current);
|
|
113
|
-
} catch (caught) {
|
|
114
|
-
if (isAbortError(caught)) {
|
|
115
|
-
setStatus("idle");
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
setError(errorMessage(caught));
|
|
119
|
-
setStatus("error");
|
|
120
|
-
}
|
|
121
|
-
}, [
|
|
122
|
-
commitMessages,
|
|
123
|
-
endpoint,
|
|
124
|
-
generateId,
|
|
125
|
-
transport
|
|
126
|
-
]);
|
|
127
|
-
const sendMessage = useCallback((text) => {
|
|
128
|
-
const trimmed = text.trim();
|
|
129
|
-
if (trimmed === "") return;
|
|
130
|
-
lastUserTextRef.current = trimmed;
|
|
131
|
-
runTurn(messagesRef.current, trimmed);
|
|
132
|
-
}, [runTurn]);
|
|
133
|
-
const stop = useCallback(() => {
|
|
134
|
-
abortRef.current?.abort();
|
|
135
|
-
setStatus("idle");
|
|
136
|
-
}, []);
|
|
137
|
-
const regenerate = useCallback(() => {
|
|
138
|
-
const userText = lastUserTextRef.current;
|
|
139
|
-
if (userText === null) return;
|
|
140
|
-
const current = messagesRef.current;
|
|
141
|
-
const history = lastIsAssistant(current) ? current.slice(0, -2) : current;
|
|
142
|
-
runTurn(history, userText);
|
|
143
|
-
}, [runTurn]);
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
return () => {
|
|
146
|
-
abortRef.current?.abort();
|
|
147
|
-
};
|
|
148
|
-
}, []);
|
|
149
|
-
return {
|
|
150
|
-
messages,
|
|
151
|
-
status,
|
|
152
|
-
error,
|
|
153
|
-
sendMessage,
|
|
154
|
-
setMessages: commitMessages,
|
|
155
|
-
stop,
|
|
156
|
-
regenerate
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
//#endregion
|
|
160
|
-
export { foldFrame, useChat };
|
|
161
|
-
|
|
162
|
-
//# sourceMappingURL=use-chat.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-chat.js","names":[],"sources":["../../../resources/js/chat/hooks/use-chat.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { ndjsonChatTransport } from \"@lattice-php/lattice/chat/lib/transport\";\nimport type {\n ChatFrame,\n ChatMessage,\n ChatStatus,\n ChatTransport,\n UseChatReturn,\n} from \"@lattice-php/lattice/chat/types\";\n\nexport type UseChatOptions = {\n endpoint: string;\n transport?: ChatTransport;\n initialMessages?: ChatMessage[];\n generateId?: () => string;\n};\n\nlet idCounter = 0;\n\nfunction defaultGenerateId(): string {\n idCounter += 1;\n\n return `chat-${idCounter}`;\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof DOMException && error.name === \"AbortError\";\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction lastIsAssistant(messages: ChatMessage[]): boolean {\n const last = messages.at(-1);\n\n return last !== undefined && last.role === \"assistant\";\n}\n\n/**\n * Pure reducer for a single streamed frame: a `text` frame appends to the open\n * assistant text part (opening one if the last part is not text); a `part` frame\n * pushes a complete part, which closes the open text part. Operates immutably on\n * the last assistant message.\n */\nexport function foldFrame(messages: ChatMessage[], frame: ChatFrame): ChatMessage[] {\n if (!lastIsAssistant(messages)) {\n return messages;\n }\n\n const index = messages.length - 1;\n const target = messages[index];\n\n if (frame.type === \"text\") {\n const parts = [...target.parts];\n const openIndex = parts.length - 1;\n const open = parts[openIndex];\n\n if (open !== undefined && open.type === \"chat.part.text\") {\n const openText = (open.props as { text: string }).text;\n parts[openIndex] = { type: \"chat.part.text\", props: { text: `${openText}${frame.value}` } };\n } else {\n parts.push({ type: \"chat.part.text\", props: { text: frame.value } });\n }\n\n const next = [...messages];\n next[index] = { ...target, parts };\n\n return next;\n }\n\n if (frame.type === \"part\") {\n const next = [...messages];\n next[index] = { ...target, parts: [...target.parts, frame.part] };\n\n return next;\n }\n\n return messages;\n}\n\nexport function useChat({\n endpoint,\n transport = ndjsonChatTransport,\n initialMessages = [],\n generateId = defaultGenerateId,\n}: UseChatOptions): UseChatReturn {\n const [messages, setMessagesState] = useState<ChatMessage[]>(initialMessages);\n const [status, setStatus] = useState<ChatStatus>(\"idle\");\n const [error, setError] = useState<string | null>(null);\n\n const abortRef = useRef<AbortController | null>(null);\n const lastUserTextRef = useRef<string | null>(null);\n const messagesRef = useRef<ChatMessage[]>(messages);\n messagesRef.current = messages;\n\n const commitMessages = useCallback((next: ChatMessage[]): void => {\n messagesRef.current = next;\n setMessagesState(next);\n }, []);\n\n const runTurn = useCallback(\n async (history: ChatMessage[], userText: string): Promise<void> => {\n abortRef.current?.abort();\n const controller = new AbortController();\n abortRef.current = controller;\n\n setError(null);\n setStatus(\"submitted\");\n\n const userMessage: ChatMessage = {\n id: generateId(),\n role: \"user\",\n parts: [{ type: \"chat.part.text\", props: { text: userText } }],\n };\n const assistantMessage: ChatMessage = { id: generateId(), role: \"assistant\", parts: [] };\n commitMessages([...history, userMessage, assistantMessage]);\n setStatus(\"streaming\");\n\n try {\n for await (const frame of transport({\n url: endpoint,\n body: { message: userText },\n signal: controller.signal,\n })) {\n if (frame.type === \"done\") {\n setStatus(\"idle\");\n continue;\n }\n\n if (frame.type === \"error\") {\n setError(frame.message ?? \"Chat failed\");\n setStatus(\"error\");\n continue;\n }\n\n commitMessages(foldFrame(messagesRef.current, frame));\n }\n\n setStatus((current) =>\n current === \"streaming\" || current === \"submitted\" ? \"idle\" : current,\n );\n } catch (caught) {\n if (isAbortError(caught)) {\n setStatus(\"idle\");\n\n return;\n }\n\n setError(errorMessage(caught));\n setStatus(\"error\");\n }\n },\n [commitMessages, endpoint, generateId, transport],\n );\n\n const sendMessage = useCallback(\n (text: string): void => {\n const trimmed = text.trim();\n if (trimmed === \"\") {\n return;\n }\n\n lastUserTextRef.current = trimmed;\n void runTurn(messagesRef.current, trimmed);\n },\n [runTurn],\n );\n\n const stop = useCallback((): void => {\n abortRef.current?.abort();\n setStatus(\"idle\");\n }, []);\n\n const regenerate = useCallback((): void => {\n const userText = lastUserTextRef.current;\n if (userText === null) {\n return;\n }\n\n const current = messagesRef.current;\n const history = lastIsAssistant(current) ? current.slice(0, -2) : current;\n void runTurn(history, userText);\n }, [runTurn]);\n\n useEffect(() => {\n return () => {\n abortRef.current?.abort();\n };\n }, []);\n\n return { messages, status, error, sendMessage, setMessages: commitMessages, stop, regenerate };\n}\n"],"mappings":"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"}
|
package/dist/chat/index.d.ts
DELETED
package/dist/chat/index.js
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { RemoteAccess } from '@lattice-php/core/api';
|
|
2
|
-
import { ChatFrame, ChatTransportRequest } from '../types.js';
|
|
3
|
-
export declare const ndjsonChatTransport: (request: ChatTransportRequest) => AsyncGenerator<ChatFrame>;
|
|
4
|
-
export declare function createRemoteNdjsonChatTransport(remote: RemoteAccess): (request: ChatTransportRequest) => AsyncGenerator<ChatFrame>;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { apiFetch, remoteFetch } from "@lattice-php/core/api";
|
|
2
|
-
//#region resources/js/chat/lib/transport.ts
|
|
3
|
-
function parseFrame(line) {
|
|
4
|
-
try {
|
|
5
|
-
return JSON.parse(line);
|
|
6
|
-
} catch {
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
function ndjsonTransport(fetchResponse) {
|
|
11
|
-
return async function* transport(request) {
|
|
12
|
-
const res = await fetchResponse(request);
|
|
13
|
-
if (!res.ok || !res.body) throw new Error(`Chat stream failed (${res.status})`);
|
|
14
|
-
yield* readNdjsonFrames(res.body);
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
var ndjsonChatTransport = ndjsonTransport(({ url, body, signal }) => apiFetch(url, {
|
|
18
|
-
method: "POST",
|
|
19
|
-
signal,
|
|
20
|
-
headers: { Accept: "application/x-ndjson" },
|
|
21
|
-
body: JSON.stringify(body),
|
|
22
|
-
throwOnError: false
|
|
23
|
-
}));
|
|
24
|
-
function createRemoteNdjsonChatTransport(remote) {
|
|
25
|
-
return ndjsonTransport(({ url, body, signal }) => remoteFetch(url, {
|
|
26
|
-
remote,
|
|
27
|
-
method: "POST",
|
|
28
|
-
signal,
|
|
29
|
-
headers: {
|
|
30
|
-
Accept: "application/x-ndjson",
|
|
31
|
-
"Content-Type": "application/json"
|
|
32
|
-
},
|
|
33
|
-
body: JSON.stringify(body),
|
|
34
|
-
throwOnError: false
|
|
35
|
-
}));
|
|
36
|
-
}
|
|
37
|
-
async function* readNdjsonFrames(body) {
|
|
38
|
-
const reader = body.getReader();
|
|
39
|
-
const decoder = new TextDecoder();
|
|
40
|
-
let buffer = "";
|
|
41
|
-
try {
|
|
42
|
-
for (;;) {
|
|
43
|
-
const { done, value } = await reader.read();
|
|
44
|
-
if (done) break;
|
|
45
|
-
buffer += decoder.decode(value, { stream: true });
|
|
46
|
-
const lines = buffer.split("\n");
|
|
47
|
-
buffer = lines.pop() ?? "";
|
|
48
|
-
for (const line of lines) {
|
|
49
|
-
if (line.trim() === "") continue;
|
|
50
|
-
const frame = parseFrame(line);
|
|
51
|
-
if (frame) yield frame;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
buffer += decoder.decode();
|
|
55
|
-
if (buffer.trim() !== "") {
|
|
56
|
-
const frame = parseFrame(buffer);
|
|
57
|
-
if (frame) yield frame;
|
|
58
|
-
}
|
|
59
|
-
} finally {
|
|
60
|
-
await reader.cancel().catch(() => {});
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
//#endregion
|
|
64
|
-
export { createRemoteNdjsonChatTransport, ndjsonChatTransport };
|
|
65
|
-
|
|
66
|
-
//# sourceMappingURL=transport.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transport.js","names":[],"sources":["../../../resources/js/chat/lib/transport.ts"],"sourcesContent":["import { apiFetch, remoteFetch, type RemoteAccess } from \"@lattice-php/core/api\";\nimport type { ChatFrame, ChatTransportRequest } from \"@lattice-php/lattice/chat/types\";\n\nfunction parseFrame(line: string): ChatFrame | null {\n try {\n return JSON.parse(line) as ChatFrame;\n } catch {\n return null;\n }\n}\n\nfunction ndjsonTransport(fetchResponse: (request: ChatTransportRequest) => Promise<Response>) {\n return async function* transport(request: ChatTransportRequest): AsyncGenerator<ChatFrame> {\n const res = await fetchResponse(request);\n\n if (!res.ok || !res.body) {\n throw new Error(`Chat stream failed (${res.status})`);\n }\n\n yield* readNdjsonFrames(res.body);\n };\n}\n\nexport const ndjsonChatTransport = ndjsonTransport(({ url, body, signal }) =>\n apiFetch(url, {\n method: \"POST\",\n signal,\n headers: { Accept: \"application/x-ndjson\" },\n body: JSON.stringify(body),\n throwOnError: false,\n }),\n);\n\nexport function createRemoteNdjsonChatTransport(remote: RemoteAccess) {\n return ndjsonTransport(({ url, body, signal }) =>\n remoteFetch(url, {\n remote,\n method: \"POST\",\n signal,\n headers: {\n Accept: \"application/x-ndjson\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n throwOnError: false,\n }),\n );\n}\n\nasync function* readNdjsonFrames(body: ReadableStream<Uint8Array>): AsyncGenerator<ChatFrame> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n for (;;) {\n const { done, value } = await reader.read();\n if (done) {\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line.trim() === \"\") {\n continue;\n }\n\n const frame = parseFrame(line);\n if (frame) {\n yield frame;\n }\n }\n }\n\n buffer += decoder.decode();\n if (buffer.trim() !== \"\") {\n const frame = parseFrame(buffer);\n if (frame) {\n yield frame;\n }\n }\n } finally {\n await reader.cancel().catch(() => {});\n }\n}\n"],"mappings":";;AAGA,SAAS,WAAW,MAAgC;CAClD,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,gBAAgB,eAAqE;CAC5F,OAAO,gBAAgB,UAAU,SAA0D;EACzF,MAAM,MAAM,MAAM,cAAc,OAAO;EAEvC,IAAI,CAAC,IAAI,MAAM,CAAC,IAAI,MAClB,MAAM,IAAI,MAAM,uBAAuB,IAAI,OAAO,EAAE;EAGtD,OAAO,iBAAiB,IAAI,IAAI;CAClC;AACF;AAEA,IAAa,sBAAsB,iBAAiB,EAAE,KAAK,MAAM,aAC/D,SAAS,KAAK;CACZ,QAAQ;CACR;CACA,SAAS,EAAE,QAAQ,uBAAuB;CAC1C,MAAM,KAAK,UAAU,IAAI;CACzB,cAAc;AAChB,CAAC,CACH;AAEA,SAAgB,gCAAgC,QAAsB;CACpE,OAAO,iBAAiB,EAAE,KAAK,MAAM,aACnC,YAAY,KAAK;EACf;EACA,QAAQ;EACR;EACA,SAAS;GACP,QAAQ;GACR,gBAAgB;EAClB;EACA,MAAM,KAAK,UAAU,IAAI;EACzB,cAAc;CAChB,CAAC,CACH;AACF;AAEA,gBAAgB,iBAAiB,MAA6D;CAC5F,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,SAAS;CAEb,IAAI;EACF,SAAS;GACP,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,KAAK;GAC1C,IAAI,MACF;GAGF,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;GAChD,MAAM,QAAQ,OAAO,MAAM,IAAI;GAC/B,SAAS,MAAM,IAAI,KAAK;GAExB,KAAK,MAAM,QAAQ,OAAO;IACxB,IAAI,KAAK,KAAK,MAAM,IAClB;IAGF,MAAM,QAAQ,WAAW,IAAI;IAC7B,IAAI,OACF,MAAM;GAEV;EACF;EAEA,UAAU,QAAQ,OAAO;EACzB,IAAI,OAAO,KAAK,MAAM,IAAI;GACxB,MAAM,QAAQ,WAAW,MAAM;GAC/B,IAAI,OACF,MAAM;EAEV;CACF,UAAU;EACR,MAAM,OAAO,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;CACtC;AACF"}
|
package/dist/chat/parts/text.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
//#region resources/js/chat/parts/text.tsx
|
|
3
|
-
var TextPart = ({ node }) => {
|
|
4
|
-
return /* @__PURE__ */ jsx("div", {
|
|
5
|
-
className: "whitespace-pre-wrap text-sm",
|
|
6
|
-
children: node.props.text
|
|
7
|
-
});
|
|
8
|
-
};
|
|
9
|
-
//#endregion
|
|
10
|
-
export { TextPart, TextPart as default };
|
|
11
|
-
|
|
12
|
-
//# sourceMappingURL=text.js.map
|