@lattice-php/lattice 0.50.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.
Files changed (49) hide show
  1. package/dist/index.d.ts +0 -4
  2. package/dist/index.js +1 -3
  3. package/dist/registry.d.ts +3 -3
  4. package/dist/registry.js +1 -2
  5. package/dist/registry.js.map +1 -1
  6. package/dist/runtime.d.ts +2 -0
  7. package/dist/runtime.js +3 -3
  8. package/dist/types/generated.d.ts +3 -42
  9. package/dist-standalone/chunks/{chart-view-7uSaRy0z.js → chart-view-R1KaYiz7.js} +6 -6
  10. package/dist-standalone/chunks/{date-picker-field-BhEHDCJ1.js → date-picker-field-DMXrmpRX.js} +1 -1
  11. package/dist-standalone/chunks/{field-HgRGTmPZ.js → field-CWEzgjj4.js} +1 -1
  12. package/dist-standalone/chunks/{rich-editor-field-D76kQ6Fe.js → rich-editor-field-Byr29WU6.js} +2 -2
  13. package/dist-standalone/chunks/{runtime-BxB5Ladg.js → runtime-C4ZndQk5.js} +8 -9
  14. package/dist-standalone/chunks/{subscriptions-Cr5l3mYJ.js → subscriptions-CEmSNfBN.js} +1 -1
  15. package/dist-standalone/lattice.js +1 -1
  16. package/dist-standalone/manifest.json +1 -1
  17. package/dist-standalone/runtime.js +1 -1
  18. package/package.json +6 -10
  19. package/dist/chat/components/chat-box.d.ts +0 -3
  20. package/dist/chat/components/chat-box.js +0 -72
  21. package/dist/chat/components/chat-box.js.map +0 -1
  22. package/dist/chat/components/message-list.d.ts +0 -5
  23. package/dist/chat/components/message-list.js +0 -22
  24. package/dist/chat/components/message-list.js.map +0 -1
  25. package/dist/chat/components/message.d.ts +0 -5
  26. package/dist/chat/components/message.js +0 -20
  27. package/dist/chat/components/message.js.map +0 -1
  28. package/dist/chat/components/prompt-input.d.ts +0 -8
  29. package/dist/chat/components/prompt-input.js +0 -49
  30. package/dist/chat/components/prompt-input.js.map +0 -1
  31. package/dist/chat/hooks/use-chat.d.ts +0 -15
  32. package/dist/chat/hooks/use-chat.js +0 -162
  33. package/dist/chat/hooks/use-chat.js.map +0 -1
  34. package/dist/chat/index.d.ts +0 -4
  35. package/dist/chat/index.js +0 -3
  36. package/dist/chat/lib/transport.d.ts +0 -4
  37. package/dist/chat/lib/transport.js +0 -66
  38. package/dist/chat/lib/transport.js.map +0 -1
  39. package/dist/chat/parts/text.d.ts +0 -3
  40. package/dist/chat/parts/text.js +0 -12
  41. package/dist/chat/parts/text.js.map +0 -1
  42. package/dist/chat/parts/tool-call.d.ts +0 -3
  43. package/dist/chat/parts/tool-call.js +0 -20
  44. package/dist/chat/parts/tool-call.js.map +0 -1
  45. package/dist/chat/plugin.d.ts +0 -2
  46. package/dist/chat/plugin.js +0 -17
  47. package/dist/chat/plugin.js.map +0 -1
  48. package/dist/chat/types.d.ts +0 -31
  49. 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,yn as i}from"./runtime-BxB5Ladg.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
+ 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{$t as e,er as t,m as n,tr as r,y as i}from"./chunks/runtime-BxB5Ladg.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&&e(s.refreshRefUrl),s.echo)try{let{configureEcho:e}=await r(async()=>{let{configureEcho:e}=await import(`./chunks/dist-D2qBbka0.js`);return{configureEcho:e}},__vite__mapDeps([0,1,2]),import.meta.url);e(s.echo)}catch(e){console.warn(`[lattice] Failed to load the realtime chunk; continuing without Echo.`,e)}n({plugins:a,...s.spriteUrl?{sprite:{href:s.spriteUrl}}:{},defaults:{visitOptions:i}})}var l=c();export{l as booted};
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,3 +1,3 @@
1
1
  {
2
- "version": "0.50.0"
2
+ "version": "0.52.0"
3
3
  }
@@ -1 +1 @@
1
- import{$ as e,$n as t,$t as n,A as r,An as i,At as a,B as o,Bt as s,C as c,Cn as l,Ct as u,D as d,Dn as f,Dt as p,En as m,Et as h,F as g,Ft as _,Gt as v,H as y,Ht as b,I as x,It as S,J as C,Jt as w,Kt as T,L as E,Lt as D,M as O,Mn as k,Mt as A,N as j,Nn as M,Nt as N,O as P,On as F,Ot as I,P as L,Pt as R,Q as z,Qn as B,Qt as V,R as H,Rt as U,S as W,Sn as G,St as K,T as q,Tn as J,Tt as Y,U as X,Ut as Z,V as Q,Vt as $,Wt as ee,X as te,Xn as ne,Xt as re,Y as ie,Yt as ae,Z as oe,Zn as se,Zt as ce,_ as le,_n as ue,a as de,an as fe,at as pe,b as me,bn as he,c as ge,cn as _e,ct as ve,dn as ye,dt as be,en as xe,er as Se,et as Ce,fn as we,ft as Te,g as Ee,gn as De,h as Oe,hn as ke,i as Ae,in as je,it as Me,j as Ne,jn as Pe,jt as Fe,kn as Ie,kt as Le,ln as Re,m as ze,mn as Be,mt as Ve,n as He,nn as Ue,nt as We,o as Ge,on as Ke,ot as qe,p as Je,pn as Ye,pt as Xe,qt as Ze,r as Qe,rn as $e,rt as et,s as tt,sn as nt,st as rt,t as it,tn as at,tt as ot,un as st,ut as ct,v as lt,vn as ut,w as dt,wn as ft,wt as pt,x as mt,xn as ht,y as gt,yn as _t,z as vt,zt as yt}from"./chunks/runtime-BxB5Ladg.js";import{a as bt,n as xt,r as St,s as Ct,t as wt}from"./chunks/locale-BZOrJtMq.js";import{r as Tt}from"./chunks/registry-context-Q5KtSmdi.js";import{_ as Et,h as Dt,m as Ot}from"./chunks/floating-ui.dom-DRSJZ5ii.js";import{a as kt,c as At,n as jt,s as Mt}from"./chunks/instance-D6cXaqIT.js";import{i as Nt,n as Pt,r as Ft,t as It}from"./chunks/timezone-Ckl1gbqB.js";import{t as Lt}from"./chunks/input-C-Wm1w0g.js";export{rt as AddRowMenu,Ne as Badge,J as Button,o as Callouts,et as Checkbox,P as CodeBlock,oe as Combobox,je as ConfirmDialog,C as CopyButton,Re as DateTime,fe as Dialog,Ke as DialogContent,nt as DialogHeader,Oe as EventBridge,Le as FieldScopeProvider,ct as FormFieldFrame,Ot as Icon,z as IconButton,m as IconRenderer,f as IconRendererProvider,Te as InfoTooltip,Lt as Input,Ct as LATTICE_EVENT,Ue as LATTICE_REF_HEADER,be as Label,Ye as LocaleReload,L as NativeSelect,De as NavigationProvider,vt as OutletContext,r as PreviewableImage,Je as Provider,ge as RICH_EDITOR_EXTENSION,Fe as ROW_ID_KEY,me as RealtimeListeners,T as RenderNode,Ze as Renderer,qe as RowKeyInputs,mt as SchemaLayout,We as SegmentedPills,w as SidebarCollapsedContext,ot as SimpleField,_e as Spinner,Dt as SpriteProvider,te as Textarea,Q as Toaster,tt as ToolbarIconButton,jt as UI_NAMESPACE,A as addRow,it as announce,ce as apiFetch,V as apiJson,yt as appendPath,He as attachTreeItemInstruction,i as builtinEffectHandlers,Xe as chatComponents,Et as cn,O as columnCell,Ge as combine,Be as configureI18n,st as configureI18nFromPageProps,ie as copyToClipboard,ze as createLatticeApp,Ee as createLayoutResolver,le as createPageResolver,ne as createRegistry,wt as currentLocale,It as currentTimezone,ue as defaultNavigation,he as dispatchActionError,ht as dispatchEffects,Ae as draggable,de as dropTargetForElements,N as duplicateRow,se as eagerComponent,Pe as effectHandler,ke as enableBackend,R as ensureRowIds,B as extendRegistry,Qe as extractTreeItemInstruction,Z as fieldProps,Nt as formatDateValue,G as getActionEffects,g as getBulkActions,s as getPath,kt as i18n,ye as i18nConfigFromPageProps,F as inertiaNavigation,W as initializeAppearance,l as isActionEffect,H as layoutComponents,t as lazyComponent,Se as loadPluginModules,xt as localeHeader,k as mergeEffectHandlers,_ as moveRow,Ie as navigationPlugin,ft as nodeIdentity,y as onCallout,X as onToast,lt as pageComponentName,q as registry,S as removeRow,e as requestSignedUpload,M as router,u as rowSchemaFor,pt as rowTemplatesOf,re as runAction,D as seedRows,St as setLocale,$ as setPath,n as setRefRefreshEndpoint,Pt as setTimezone,b as toHtmlName,Mt as translate,c as updateAppearance,dt as useAppearance,Ve as useChat,j as useColumnRegistry,Tt as useComponentRegistry,K as useDebouncedCallback,ve as useDependentField,_t as useEffectDispatcher,Y as useFieldCommit,a as useFieldScope,v as useFormContext,h as useFormValue,p as useFormValues,d as useFormatContext,bt as useLocale,we as useLocaleOptions,ut as useNavigation,pe as usePersistentState,Me as useRowCollection,I as useSetFormValue,ae as useSidebarCollapsed,At as useT,E as useTable,x as useTableSelection,Ft as useTimezone,ee as walkFields,at as withHeaders,$e as withRefHeader,U as withRowId,gt as withVisitHeaders,Ce as xhrTransfer,xe as xsrfToken};
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.50.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.50.0",
121
- "@lattice-php/core": "0.50.0",
122
- "@lattice-php/form": "0.50.0",
123
- "@lattice-php/table": "0.50.0",
124
- "@lattice-php/ui": "0.50.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,3 +0,0 @@
1
- import { RendererComponent } from '@lattice-php/core/types';
2
- export declare const ChatBox: RendererComponent<"chat.box">;
3
- export default ChatBox;
@@ -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,5 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { ChatMessage } from '../types.js';
3
- export declare function MessageList({ messages }: {
4
- messages: ChatMessage[];
5
- }): ReactNode;
@@ -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,5 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { ChatMessage } from '../types.js';
3
- export declare function Message({ message }: {
4
- message: ChatMessage;
5
- }): ReactNode;
@@ -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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"}
@@ -1,4 +0,0 @@
1
- export { useChat } from './hooks/use-chat.js';
2
- export type { UseChatOptions } from './hooks/use-chat.js';
3
- export { chatComponents } from './plugin.js';
4
- export type * from './types.js';
@@ -1,3 +0,0 @@
1
- import { useChat } from "./hooks/use-chat.js";
2
- import { chatComponents } from "./plugin.js";
3
- export { chatComponents, useChat };
@@ -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"}
@@ -1,3 +0,0 @@
1
- import { RendererComponent } from '@lattice-php/core/types';
2
- export declare const TextPart: RendererComponent<"chat.part.text">;
3
- export default TextPart;
@@ -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