@intentface/chat 0.1.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 (145) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +108 -0
  3. package/dist/ask-user/ask-user.d.ts +104 -0
  4. package/dist/ask-user/ask-user.js +226 -0
  5. package/dist/ask-user/index.d.ts +3 -0
  6. package/dist/ask-user/index.js +3 -0
  7. package/dist/ask-user/index.parts.d.ts +1 -0
  8. package/dist/ask-user/index.parts.js +5 -0
  9. package/dist/attachments/attachments.d.ts +41 -0
  10. package/dist/attachments/attachments.js +91 -0
  11. package/dist/attachments/index.d.ts +3 -0
  12. package/dist/attachments/index.js +3 -0
  13. package/dist/attachments/index.parts.d.ts +1 -0
  14. package/dist/attachments/index.parts.js +5 -0
  15. package/dist/chip/chip.d.ts +25 -0
  16. package/dist/chip/chip.js +25 -0
  17. package/dist/chip/index.d.ts +2 -0
  18. package/dist/chip/index.js +2 -0
  19. package/dist/chip/index.parts.d.ts +1 -0
  20. package/dist/chip/index.parts.js +5 -0
  21. package/dist/chip-markdown.d.ts +25 -0
  22. package/dist/chip-markdown.js +52 -0
  23. package/dist/composer/actions.d.ts +35 -0
  24. package/dist/composer/actions.js +75 -0
  25. package/dist/composer/ask-user-machine.d.ts +52 -0
  26. package/dist/composer/ask-user-machine.js +164 -0
  27. package/dist/composer/attachments-machine.d.ts +34 -0
  28. package/dist/composer/attachments-machine.js +91 -0
  29. package/dist/composer/attachments.d.ts +16 -0
  30. package/dist/composer/attachments.js +32 -0
  31. package/dist/composer/command-list.d.ts +49 -0
  32. package/dist/composer/command-list.js +357 -0
  33. package/dist/composer/commands.d.ts +28 -0
  34. package/dist/composer/commands.js +56 -0
  35. package/dist/composer/container.d.ts +3 -0
  36. package/dist/composer/container.js +32 -0
  37. package/dist/composer/controller.d.ts +3 -0
  38. package/dist/composer/controller.js +6 -0
  39. package/dist/composer/editor-dom.d.ts +55 -0
  40. package/dist/composer/editor-dom.js +431 -0
  41. package/dist/composer/fuzzy.d.ts +4 -0
  42. package/dist/composer/fuzzy.js +49 -0
  43. package/dist/composer/index.d.ts +25 -0
  44. package/dist/composer/index.js +13 -0
  45. package/dist/composer/index.parts.d.ts +11 -0
  46. package/dist/composer/index.parts.js +15 -0
  47. package/dist/composer/internals.d.ts +29 -0
  48. package/dist/composer/internals.js +102 -0
  49. package/dist/composer/keyboard.d.ts +61 -0
  50. package/dist/composer/keyboard.js +75 -0
  51. package/dist/composer/panel.d.ts +28 -0
  52. package/dist/composer/panel.js +80 -0
  53. package/dist/composer/placeholder.d.ts +10 -0
  54. package/dist/composer/placeholder.js +8 -0
  55. package/dist/composer/popover.d.ts +12 -0
  56. package/dist/composer/popover.js +63 -0
  57. package/dist/composer/prefix-detection.d.ts +23 -0
  58. package/dist/composer/prefix-detection.js +58 -0
  59. package/dist/composer/root.d.ts +21 -0
  60. package/dist/composer/root.js +107 -0
  61. package/dist/composer/segments.d.ts +108 -0
  62. package/dist/composer/segments.js +366 -0
  63. package/dist/composer/store.d.ts +97 -0
  64. package/dist/composer/store.js +451 -0
  65. package/dist/composer/textarea.d.ts +39 -0
  66. package/dist/composer/textarea.js +64 -0
  67. package/dist/composer/trigger-tracker.d.ts +12 -0
  68. package/dist/composer/trigger-tracker.js +90 -0
  69. package/dist/composer/types.d.ts +126 -0
  70. package/dist/composer/types.js +4 -0
  71. package/dist/composer/use-composer-editor.d.ts +43 -0
  72. package/dist/composer/use-composer-editor.js +649 -0
  73. package/dist/internal/collapsible.d.ts +22 -0
  74. package/dist/internal/collapsible.js +90 -0
  75. package/dist/internal/primitive-props.d.ts +4 -0
  76. package/dist/internal/primitive-props.js +4 -0
  77. package/dist/internal/render/baseTypes.d.ts +18 -0
  78. package/dist/internal/render/baseTypes.js +4 -0
  79. package/dist/internal/render/empty.d.ts +3 -0
  80. package/dist/internal/render/empty.js +5 -0
  81. package/dist/internal/render/getReactElementRef.d.ts +5 -0
  82. package/dist/internal/render/getReactElementRef.js +15 -0
  83. package/dist/internal/render/getStateAttributesProps.d.ts +4 -0
  84. package/dist/internal/render/getStateAttributesProps.js +23 -0
  85. package/dist/internal/render/mergeObjects.d.ts +1 -0
  86. package/dist/internal/render/mergeObjects.js +14 -0
  87. package/dist/internal/render/mergeProps.d.ts +55 -0
  88. package/dist/internal/render/mergeProps.js +183 -0
  89. package/dist/internal/render/positioning.d.ts +22 -0
  90. package/dist/internal/render/positioning.js +78 -0
  91. package/dist/internal/render/reactVersion.d.ts +3 -0
  92. package/dist/internal/render/reactVersion.js +7 -0
  93. package/dist/internal/render/resolveClassName.d.ts +8 -0
  94. package/dist/internal/render/resolveClassName.js +12 -0
  95. package/dist/internal/render/resolveStyle.d.ts +8 -0
  96. package/dist/internal/render/resolveStyle.js +12 -0
  97. package/dist/internal/render/transition.d.ts +48 -0
  98. package/dist/internal/render/transition.js +203 -0
  99. package/dist/internal/render/types.d.ts +74 -0
  100. package/dist/internal/render/types.js +3 -0
  101. package/dist/internal/render/useMergedRefs.d.ts +21 -0
  102. package/dist/internal/render/useMergedRefs.js +102 -0
  103. package/dist/internal/render/useRefWithInit.d.ts +10 -0
  104. package/dist/internal/render/useRefWithInit.js +12 -0
  105. package/dist/internal/render/useRender.d.ts +71 -0
  106. package/dist/internal/render/useRender.js +12 -0
  107. package/dist/internal/render/useRenderElement.d.ts +62 -0
  108. package/dist/internal/render/useRenderElement.js +157 -0
  109. package/dist/internal/render/warn.d.ts +1 -0
  110. package/dist/internal/render/warn.js +15 -0
  111. package/dist/internal/state-mappings.d.ts +8 -0
  112. package/dist/internal/state-mappings.js +17 -0
  113. package/dist/message/index.d.ts +3 -0
  114. package/dist/message/index.js +3 -0
  115. package/dist/message/index.parts.d.ts +1 -0
  116. package/dist/message/index.parts.js +5 -0
  117. package/dist/message/message.d.ts +43 -0
  118. package/dist/message/message.js +78 -0
  119. package/dist/message-utils.d.ts +55 -0
  120. package/dist/message-utils.js +79 -0
  121. package/dist/reasoning/index.d.ts +3 -0
  122. package/dist/reasoning/index.js +3 -0
  123. package/dist/reasoning/index.parts.d.ts +1 -0
  124. package/dist/reasoning/index.parts.js +5 -0
  125. package/dist/reasoning/reasoning.d.ts +20 -0
  126. package/dist/reasoning/reasoning.js +49 -0
  127. package/dist/steps/index.d.ts +2 -0
  128. package/dist/steps/index.js +2 -0
  129. package/dist/steps/index.parts.d.ts +1 -0
  130. package/dist/steps/index.parts.js +5 -0
  131. package/dist/steps/steps.d.ts +53 -0
  132. package/dist/steps/steps.js +79 -0
  133. package/dist/thread/geometry.d.ts +18 -0
  134. package/dist/thread/geometry.js +59 -0
  135. package/dist/thread/index.d.ts +4 -0
  136. package/dist/thread/index.js +3 -0
  137. package/dist/thread/index.parts.d.ts +1 -0
  138. package/dist/thread/index.parts.js +5 -0
  139. package/dist/thread/stores.d.ts +21 -0
  140. package/dist/thread/stores.js +63 -0
  141. package/dist/thread/thread.d.ts +66 -0
  142. package/dist/thread/thread.js +639 -0
  143. package/dist/types.d.ts +58 -0
  144. package/dist/types.js +17 -0
  145. package/package.json +106 -0
@@ -0,0 +1,157 @@
1
+ import { createElement as _createElement } from "react";
2
+ // Vendored from @base-ui/react v1.6.0 (MIT) — packages/react/src/internals/useRenderElement.tsx
3
+ // https://github.com/mui/base-ui — import specifiers rewired; one local departure:
4
+ // the `props` config accepts bespoke data-* part attributes (WithDataAttributes).
5
+ import * as React from 'react';
6
+ import { useMergedRefs, useMergedRefsN } from './useMergedRefs';
7
+ import { getReactElementRef } from './getReactElementRef';
8
+ import { mergeObjects } from './mergeObjects';
9
+ import { warn } from './warn';
10
+ import { EMPTY_OBJECT } from './empty';
11
+ import { getStateAttributesProps } from './getStateAttributesProps';
12
+ import { resolveClassName } from './resolveClassName';
13
+ import { resolveStyle } from './resolveStyle';
14
+ import { mergeProps, mergePropsN, mergeClassNames } from './mergeProps';
15
+ /**
16
+ * Renders a Base UI element.
17
+ *
18
+ * @param element The default HTML element to render. Can be overridden by the `render` prop.
19
+ * @param componentProps An object containing the `render` and `className` props to be used for element customization. Other props are ignored.
20
+ * @param params Additional parameters for rendering the element.
21
+ */
22
+ export function useRenderElement(element, componentProps, params = {}) {
23
+ const renderProp = componentProps.render;
24
+ const outProps = useRenderElementProps(componentProps, params);
25
+ if (params.enabled === false) {
26
+ return null;
27
+ }
28
+ const state = params.state ?? EMPTY_OBJECT;
29
+ return evaluateRenderProp(element, renderProp, outProps, state);
30
+ }
31
+ /**
32
+ * Computes render element final props.
33
+ */
34
+ function useRenderElementProps(componentProps, params = {}) {
35
+ const { className: classNameProp, style: styleProp, render: renderProp } = componentProps;
36
+ const { state = EMPTY_OBJECT, ref, props, stateAttributesMapping, enabled = true, } = params;
37
+ const className = enabled ? resolveClassName(classNameProp, state) : undefined;
38
+ const style = enabled ? resolveStyle(styleProp, state) : undefined;
39
+ const stateProps = enabled
40
+ ? getStateAttributesProps(state, stateAttributesMapping)
41
+ : EMPTY_OBJECT;
42
+ const resolvedProps = enabled && props ? resolveRenderFunctionProps(props) : undefined;
43
+ // Ensure outProps is always a new mutable object when enabled, never EMPTY_OBJECT.
44
+ // This prevents potential TypeError when setting ref, className, or style properties,
45
+ // since EMPTY_OBJECT is frozen and mutations would fail in strict mode.
46
+ const outProps = enabled
47
+ ? (mergeObjects(stateProps, resolvedProps) ?? {})
48
+ : EMPTY_OBJECT;
49
+ // SAFETY: The `useMergedRefs` functions use a single hook to store the same value,
50
+ // switching between them at runtime is safe. If this assertion fails, React will
51
+ // throw at runtime anyway.
52
+ // This also skips the `useMergedRefs` call on the server, which is fine because
53
+ // refs are not used on the server side.
54
+ /* eslint-disable react-hooks/rules-of-hooks */
55
+ if (typeof document !== 'undefined') {
56
+ if (!enabled) {
57
+ useMergedRefs(null, null);
58
+ }
59
+ else if (Array.isArray(ref)) {
60
+ outProps.ref = useMergedRefsN([outProps.ref, getReactElementRef(renderProp), ...ref]);
61
+ }
62
+ else {
63
+ outProps.ref = useMergedRefs(outProps.ref, getReactElementRef(renderProp), ref);
64
+ }
65
+ }
66
+ if (!enabled) {
67
+ return EMPTY_OBJECT;
68
+ }
69
+ if (className !== undefined) {
70
+ outProps.className = mergeClassNames(outProps.className, className);
71
+ }
72
+ if (style !== undefined) {
73
+ outProps.style = mergeObjects(outProps.style, style);
74
+ }
75
+ return outProps;
76
+ }
77
+ function resolveRenderFunctionProps(props) {
78
+ if (Array.isArray(props)) {
79
+ return mergePropsN(props);
80
+ }
81
+ return mergeProps(undefined, props);
82
+ }
83
+ // The symbol React uses internally for lazy components
84
+ // https://github.com/facebook/react/blob/a0566250b210499b4c5677f5ac2eedbd71d51a1b/packages/shared/ReactSymbols.js#L31
85
+ //
86
+ // TODO delete once https://github.com/facebook/react/issues/32392 is fixed
87
+ const REACT_LAZY_TYPE = Symbol.for('react.lazy');
88
+ const COMPONENT_IDENTIFIER_PATTERN = /^[A-Z][A-Za-z0-9$]*$/;
89
+ const LOWERCASE_CHARACTER_PATTERN = /[a-z]/;
90
+ function evaluateRenderProp(element, render, props, state) {
91
+ if (render) {
92
+ if (typeof render === 'function') {
93
+ if (process.env.NODE_ENV !== 'production') {
94
+ warnIfRenderPropLooksLikeComponent(render);
95
+ }
96
+ return render(props, state);
97
+ }
98
+ const mergedProps = mergeProps(props, render.props);
99
+ mergedProps.ref = props.ref;
100
+ let newElement = render;
101
+ // Workaround for https://github.com/facebook/react/issues/32392
102
+ // This works because the toArray() logic unwrap lazy element type in
103
+ // https://github.com/facebook/react/blob/a0566250b210499b4c5677f5ac2eedbd71d51a1b/packages/react/src/ReactChildren.js#L186
104
+ if (newElement?.$$typeof === REACT_LAZY_TYPE) {
105
+ const children = React.Children.toArray(render);
106
+ newElement = children[0];
107
+ }
108
+ // There is a high number of indirections, the error message thrown by React.cloneElement() is
109
+ // hard to use for developers, this logic provides a better context.
110
+ //
111
+ // Our general guideline is to never change the control flow depending on the environment.
112
+ // However, React.cloneElement() throws if React.isValidElement() is false,
113
+ // so we can throw before with custom message.
114
+ if (process.env.NODE_ENV !== 'production') {
115
+ if (!React.isValidElement(newElement)) {
116
+ // TODO: fix mui/no-guarded-throw
117
+ // eslint-disable-next-line mui/no-guarded-throw
118
+ throw new Error([
119
+ 'Base UI: The `render` prop was provided an invalid React element as `React.isValidElement(render)` is `false`.',
120
+ 'A valid React element must be provided to the `render` prop because it is cloned with props to replace the default element.',
121
+ 'https://base-ui.com/r/invalid-render-prop',
122
+ ].join('\n'));
123
+ }
124
+ }
125
+ return React.cloneElement(newElement, mergedProps);
126
+ }
127
+ if (element) {
128
+ if (typeof element === 'string') {
129
+ return renderTag(element, props);
130
+ }
131
+ }
132
+ // Unreachable, but the typings on `useRenderElement` need to be reworked
133
+ // to annotate it correctly.
134
+ throw new Error('Base UI: Render element or function are not defined.');
135
+ }
136
+ function warnIfRenderPropLooksLikeComponent(renderFn) {
137
+ const functionName = renderFn.name;
138
+ if (functionName.length === 0) {
139
+ return;
140
+ }
141
+ if (!COMPONENT_IDENTIFIER_PATTERN.test(functionName)) {
142
+ return;
143
+ }
144
+ if (!LOWERCASE_CHARACTER_PATTERN.test(functionName)) {
145
+ return;
146
+ }
147
+ warn(`The \`render\` prop received a function named \`${functionName}\` that starts with an uppercase letter.`, 'This usually means a React component was passed directly as `render={Component}`.', 'Base UI calls `render` as a plain function, which can break the Rules of Hooks during reconciliation.', 'If this is an intentional render callback, rename it to start with a lowercase letter.', 'Use `render={<Component />}` or `render={(props) => <Component {...props} />}` instead.', 'https://base-ui.com/r/invalid-render-prop');
148
+ }
149
+ function renderTag(Tag, props) {
150
+ if (Tag === 'button') {
151
+ return _createElement("button", { type: "button", ...props, key: props.key });
152
+ }
153
+ if (Tag === 'img') {
154
+ return _createElement("img", { alt: "", ...props, key: props.key });
155
+ }
156
+ return React.createElement(Tag, props);
157
+ }
@@ -0,0 +1 @@
1
+ export declare function warn(...messages: string[]): void;
@@ -0,0 +1,15 @@
1
+ // Vendored from @base-ui/utils v1.6.0 (MIT) — packages/utils/src/warn.ts
2
+ // https://github.com/mui/base-ui — exact copy.
3
+ let set;
4
+ if (process.env.NODE_ENV !== 'production') {
5
+ set = new Set();
6
+ }
7
+ export function warn(...messages) {
8
+ if (process.env.NODE_ENV !== 'production') {
9
+ const messageKey = messages.join(' ');
10
+ if (!set.has(messageKey)) {
11
+ set.add(messageKey);
12
+ console.warn(`Base UI: ${messageKey}`);
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,8 @@
1
+ import type { StateAttributesMapping } from "./render/getStateAttributesProps.js";
2
+ import type { TransitionStatus } from "./render/transition.js";
3
+ export declare const openStateMapping: StateAttributesMapping<{
4
+ open: boolean;
5
+ }>;
6
+ export declare const transitionStatusMapping: StateAttributesMapping<{
7
+ transitionStatus: TransitionStatus;
8
+ }>;
@@ -0,0 +1,17 @@
1
+ // Shared state → data-attribute mappings for useRenderElement. Base UI
2
+ // convention: `open` produces a data-open / data-closed presence pair so both
3
+ // directions are styleable (exit transitions target data-closed).
4
+ export const openStateMapping = {
5
+ open: (value) => (value ? { "data-open": "" } : { "data-closed": "" }),
6
+ };
7
+ // Base UI convention: the transitional frames of an open/close animation surface as
8
+ // data-starting-style (entering) / data-ending-style (exiting); settled states emit nothing.
9
+ export const transitionStatusMapping = {
10
+ transitionStatus: (value) => {
11
+ if (value === "starting")
12
+ return { "data-starting-style": "" };
13
+ if (value === "ending")
14
+ return { "data-ending-style": "" };
15
+ return null;
16
+ },
17
+ };
@@ -0,0 +1,3 @@
1
+ export * as Message from "./index.parts.js";
2
+ export type { MessageChipSegment, MessageRootProps, MessageSelection, MessageState, MessageTextProps, MessageTurnProps, } from "./message.js";
3
+ export { readMessageSelection, useMessageSelection, useMessageSelectionScope, } from "./message.js";
@@ -0,0 +1,3 @@
1
+ // Public surface of @intentface/chat/message. No directive — see index.parts.ts.
2
+ export * as Message from "./index.parts.js";
3
+ export { readMessageSelection, useMessageSelection, useMessageSelectionScope, } from "./message.js";
@@ -0,0 +1 @@
1
+ export { MessageRoot as Root, MessageText as Text, MessageTurn as Turn, } from "./message.js";
@@ -0,0 +1,5 @@
1
+ // Part names for the `Message` namespace. Deliberately carries no
2
+ // "use client" directive: this module and index.ts must stay server-resolvable
3
+ // so a React Server Component can reach Message.Root and its siblings
4
+ // through them. The directive lives one level down, on message.tsx.
5
+ export { MessageRoot as Root, MessageText as Text, MessageTurn as Turn, } from "./message.js";
@@ -0,0 +1,43 @@
1
+ import { type ReactNode } from "react";
2
+ import { type ChipSegment } from "../chip-markdown.js";
3
+ import type { PrimitiveProps } from "../internal/primitive-props.js";
4
+ export type MessageState = {
5
+ /** Opaque role string; the consumer owns the concrete set. Surfaced as data-role. */
6
+ role: string;
7
+ /** Present as data-error when true. */
8
+ error: boolean;
9
+ /** Present as data-last when true. */
10
+ last: boolean;
11
+ };
12
+ export type MessageRootProps = Omit<PrimitiveProps<"div", MessageState>, "role"> & {
13
+ role: string;
14
+ isLast?: boolean;
15
+ isError?: boolean;
16
+ };
17
+ export declare const MessageRoot: ({ role, isLast, isError, className, render, style, ...elementProps }: MessageRootProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
18
+ export type MessageTurnProps = PrimitiveProps<"div">;
19
+ export declare const MessageTurn: ({ className, render, style, ...elementProps }: MessageTurnProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
20
+ export type MessageChipSegment = Extract<ChipSegment, {
21
+ type: "chip";
22
+ }>;
23
+ export type MessageTextProps = Omit<PrimitiveProps<"span">, "children"> & {
24
+ children: string;
25
+ renderText?: (text: string, index: number) => ReactNode;
26
+ renderChip?: (chip: MessageChipSegment, index: number) => ReactNode;
27
+ };
28
+ export declare const MessageText: ({ children, className, render, style, renderText, renderChip, ...elementProps }: MessageTextProps) => import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
29
+ export type MessageSelection = {
30
+ text: string;
31
+ range: Range;
32
+ };
33
+ export declare const readMessageSelection: (scope: HTMLElement) => MessageSelection | null;
34
+ export declare const useMessageSelection: (scope: HTMLElement | null) => MessageSelection | null;
35
+ /**
36
+ * Resolves the owning message's content element from an invisible anchor node
37
+ * rendered anywhere inside the message. Scoping to message-content keeps the
38
+ * toolbar off markers, sources, and actions elsewhere in the message.
39
+ */
40
+ export declare const useMessageSelectionScope: () => {
41
+ anchorRef: (node: HTMLElement | null) => void;
42
+ contentElement: HTMLElement | null;
43
+ };
@@ -0,0 +1,78 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ // Headless message parts. Owns the role/error/last data-attribute contract,
4
+ // chip-segment text parsing (with render inversion for markdown/styled chips),
5
+ // timestamp formatting, and the text-selection subsystem. Entrance animation,
6
+ // popovers, tooltips, icons, and thumbnails belong to the styled layer.
7
+ //
8
+ // Every part supports the Base UI render prop: pass a ReactElement to swap the
9
+ // underlying element (props are merged onto it), or a function `(props, state)`
10
+ // for full control. The root's data attributes (data-role, data-error,
11
+ // data-last) are generated from its state object, so the render callback and
12
+ // CSS consumers see the same state.
13
+ import { Fragment, useCallback, useEffect, useMemo, useState } from "react";
14
+ import { parseChipSegments } from "../chip-markdown.js";
15
+ import { useRenderElement } from "../internal/render/useRenderElement.js";
16
+ export const MessageRoot = ({ role, isLast = false, isError = false, className, render, style, ...elementProps }) => {
17
+ const state = useMemo(() => ({ role, error: isError, last: isLast }), [role, isError, isLast]);
18
+ return useRenderElement("div", { className, render, style }, { state, props: [{ "data-message": "" }, elementProps] });
19
+ };
20
+ export const MessageTurn = ({ className, render, style, ...elementProps }) => useRenderElement("div", { className, render, style }, { props: [{ "data-message-turn": "" }, elementProps] });
21
+ export const MessageText = ({ children, className, render, style, renderText, renderChip, ...elementProps }) => {
22
+ const segments = parseChipSegments(children);
23
+ const segmentNodes = segments.map((segment, index) => segment.type === "text" ? (_jsx(Fragment, { children: renderText ? renderText(segment.text, index) : segment.text }, index)) : (_jsx(Fragment, { children: renderChip ? (renderChip(segment, index)) : (_jsx("span", { "data-message-chip": "", children: segment.label })) }, index)));
24
+ return useRenderElement("span", { className, render, style }, { props: [{ "data-message-text": "", children: segmentNodes }, elementProps] });
25
+ };
26
+ export const readMessageSelection = (scope) => {
27
+ const selection = window.getSelection();
28
+ if (!selection || selection.isCollapsed || selection.rangeCount === 0)
29
+ return null;
30
+ const text = selection.toString();
31
+ if (text.trim().length === 0)
32
+ return null;
33
+ // Both endpoints must sit inside this message — a cross-message selection
34
+ // has its common ancestor outside the scope and resolves to null.
35
+ const range = selection.getRangeAt(0);
36
+ if (!scope.contains(range.commonAncestorContainer))
37
+ return null;
38
+ // Clone so later mutations of the live selection don't move our anchor.
39
+ return { text, range: range.cloneRange() };
40
+ };
41
+ // Subscribe to the document selection, scoped to `scope`. Samples when the
42
+ // gesture settles (mouseup / keyup) instead of on every selectionchange drag
43
+ // tick; selectionchange only clears the value once the selection collapses.
44
+ export const useMessageSelection = (scope) => {
45
+ const [selection, setSelection] = useState(null);
46
+ useEffect(() => {
47
+ if (!scope)
48
+ return;
49
+ const readSelection = () => setSelection(readMessageSelection(scope));
50
+ const hideWhenCollapsed = () => {
51
+ const current = window.getSelection();
52
+ if (!current || current.isCollapsed)
53
+ setSelection(null);
54
+ };
55
+ document.addEventListener("mouseup", readSelection);
56
+ document.addEventListener("keyup", readSelection);
57
+ document.addEventListener("selectionchange", hideWhenCollapsed);
58
+ return () => {
59
+ document.removeEventListener("mouseup", readSelection);
60
+ document.removeEventListener("keyup", readSelection);
61
+ document.removeEventListener("selectionchange", hideWhenCollapsed);
62
+ };
63
+ }, [scope]);
64
+ return selection;
65
+ };
66
+ /**
67
+ * Resolves the owning message's content element from an invisible anchor node
68
+ * rendered anywhere inside the message. Scoping to message-content keeps the
69
+ * toolbar off markers, sources, and actions elsewhere in the message.
70
+ */
71
+ export const useMessageSelectionScope = () => {
72
+ const [contentElement, setContentElement] = useState(null);
73
+ const anchorRef = useCallback((node) => {
74
+ const messageRoot = node?.closest("[data-message]");
75
+ setContentElement(messageRoot?.querySelector("[data-message-content]") ?? null);
76
+ }, []);
77
+ return { anchorRef, contentElement };
78
+ };
@@ -0,0 +1,55 @@
1
+ import { type ChatMessage, type FilePart, type ReasoningPart, type TextPart, type ToolPart, type UnknownPart } from "./types.js";
2
+ /** Classifies message parts into coarse groups for chronological rendering. */
3
+ export type SegmentType = "reasoning" | "tool" | "text" | "file";
4
+ /**
5
+ * A contiguous run of same-typed message parts. Used by message renderers to
6
+ * group reasoning blocks, tool calls, text chunks, etc.
7
+ */
8
+ export type MessageSegment = {
9
+ type: "reasoning";
10
+ parts: ReasoningPart[];
11
+ } | {
12
+ type: "tool";
13
+ parts: ToolPart[];
14
+ } | {
15
+ type: "text";
16
+ parts: TextPart[];
17
+ } | {
18
+ type: "file";
19
+ parts: FilePart[];
20
+ };
21
+ /**
22
+ * Maps a single message part to its segment type.
23
+ * Returns `null` for parts that are not rendered chronologically.
24
+ */
25
+ export declare const partSegmentType: (part: UnknownPart) => SegmentType | null;
26
+ /**
27
+ * Groups an array of message parts into contiguous segments of the same type.
28
+ * Adjacent parts with the same type are merged into a single segment. This
29
+ * preserves chronological order for interleaved rendering (e.g. reasoning
30
+ * interspersed with tool calls).
31
+ */
32
+ export declare const getSegmentedParts: (parts: readonly UnknownPart[]) => MessageSegment[];
33
+ /** A conversational turn: a user message plus its trailing assistant/tool replies. */
34
+ export type Turn<Message extends ChatMessage<unknown, UnknownPart> = ChatMessage> = {
35
+ key: string;
36
+ messages: Message[];
37
+ };
38
+ /**
39
+ * Groups messages into turns. A new turn starts at every user message;
40
+ * non-user messages attach to the current turn. A conversation that opens with
41
+ * a non-user message still gets a leading turn so nothing is dropped. The turn
42
+ * key is the first message's id — stable while the trailing assistant message
43
+ * streams, so React keys and entrance animations stay put.
44
+ */
45
+ export declare const groupTurns: <Message extends ChatMessage<unknown, UnknownPart>>(messages: readonly Message[], turnStartRole?: string) => Turn<Message>[];
46
+ /** Aggregated text content from all text segments in a message. */
47
+ export type TextInfo = {
48
+ parts: TextPart[];
49
+ /** Concatenated text content of all text parts. */
50
+ text: string;
51
+ };
52
+ /** Extracts all text parts from a message and concatenates their content. */
53
+ export declare const getTextInfo: (segments: readonly MessageSegment[]) => TextInfo;
54
+ /** Extracts all file attachment parts from segments. */
55
+ export declare const getFileParts: (segments: readonly MessageSegment[]) => FilePart[];
@@ -0,0 +1,79 @@
1
+ // Derivation helpers over the structural message contract in types.ts.
2
+ // Pure functions — no React, no styling. Renderers and panel-state hooks share
3
+ // these so they stay decoupled from concrete part shapes.
4
+ import { isFilePart, isReasoningPart, isTextPart, isToolPart, } from "./types.js";
5
+ /**
6
+ * Maps a single message part to its segment type.
7
+ * Returns `null` for parts that are not rendered chronologically.
8
+ */
9
+ export const partSegmentType = (part) => {
10
+ if (isReasoningPart(part))
11
+ return "reasoning";
12
+ if (isTextPart(part))
13
+ return "text";
14
+ if (isFilePart(part))
15
+ return "file";
16
+ if (isToolPart(part))
17
+ return "tool";
18
+ return null;
19
+ };
20
+ /**
21
+ * Groups an array of message parts into contiguous segments of the same type.
22
+ * Adjacent parts with the same type are merged into a single segment. This
23
+ * preserves chronological order for interleaved rendering (e.g. reasoning
24
+ * interspersed with tool calls).
25
+ */
26
+ export const getSegmentedParts = (parts) => {
27
+ const segments = [];
28
+ for (const part of parts) {
29
+ const segType = partSegmentType(part);
30
+ if (!segType)
31
+ continue;
32
+ const last = segments.at(-1);
33
+ if (last && last.type === segType) {
34
+ last.parts.push(part);
35
+ }
36
+ else {
37
+ segments.push({ type: segType, parts: [part] });
38
+ }
39
+ }
40
+ return segments;
41
+ };
42
+ /**
43
+ * Groups messages into turns. A new turn starts at every user message;
44
+ * non-user messages attach to the current turn. A conversation that opens with
45
+ * a non-user message still gets a leading turn so nothing is dropped. The turn
46
+ * key is the first message's id — stable while the trailing assistant message
47
+ * streams, so React keys and entrance animations stay put.
48
+ */
49
+ export const groupTurns = (messages,
50
+ // The role that starts a new turn — opaque, so consumers with a different
51
+ // role set can group correctly.
52
+ turnStartRole = "user") => {
53
+ const turns = [];
54
+ for (const message of messages) {
55
+ const currentTurn = turns.at(-1);
56
+ if (message.role === turnStartRole || !currentTurn) {
57
+ turns.push({ key: message.id, messages: [message] });
58
+ }
59
+ else {
60
+ currentTurn.messages.push(message);
61
+ }
62
+ }
63
+ return turns;
64
+ };
65
+ /** Extracts all text parts from a message and concatenates their content. */
66
+ export const getTextInfo = (segments) => {
67
+ const parts = segments
68
+ .filter((s) => s.type === "text")
69
+ .flatMap((s) => s.parts);
70
+ const text = parts.map((p) => p.text).join("");
71
+ return { parts, text };
72
+ };
73
+ /** Extracts all file attachment parts from segments. */
74
+ export const getFileParts = (segments) => segments
75
+ .filter((s) => s.type === "file")
76
+ .flatMap((s) => s.parts);
77
+ // Chain/reasoning/source extraction (and ask-user extraction) is the consuming
78
+ // app's concern — grouping conventions, header formats, and dedup rules are
79
+ // product policy. This package ships only the generic segmentation above.
@@ -0,0 +1,3 @@
1
+ export * as Reasoning from "./index.parts.js";
2
+ export type { ReasoningContentProps, ReasoningRootProps, ReasoningTriggerProps, } from "./reasoning.js";
3
+ export { useReasoning } from "./reasoning.js";
@@ -0,0 +1,3 @@
1
+ // Public surface of @intentface/chat/reasoning. No directive — see index.parts.ts.
2
+ export * as Reasoning from "./index.parts.js";
3
+ export { useReasoning } from "./reasoning.js";
@@ -0,0 +1 @@
1
+ export { ReasoningContent as Content, ReasoningRoot as Root, ReasoningTrigger as Trigger, } from "./reasoning.js";
@@ -0,0 +1,5 @@
1
+ // Part names for the `Reasoning` namespace. Deliberately carries no
2
+ // "use client" directive: this module and index.ts must stay server-resolvable
3
+ // so a React Server Component can reach Reasoning.Root and its siblings
4
+ // through them. The directive lives one level down, on reasoning.tsx.
5
+ export { ReasoningContent as Content, ReasoningRoot as Root, ReasoningTrigger as Trigger, } from "./reasoning.js";
@@ -0,0 +1,20 @@
1
+ import { type ComponentProps } from "react";
2
+ import { Collapsible } from "../internal/collapsible.js";
3
+ type ReasoningContextValue = {
4
+ isStreaming: boolean;
5
+ isOpen: boolean;
6
+ setIsOpen: (open: boolean) => void;
7
+ duration: number | undefined;
8
+ };
9
+ export declare const useReasoning: () => ReasoningContextValue;
10
+ export type ReasoningRootProps = Omit<ComponentProps<typeof Collapsible>, "onOpenChange"> & {
11
+ isStreaming?: boolean;
12
+ duration?: number;
13
+ onOpenChange?: (open: boolean) => void;
14
+ };
15
+ export declare const ReasoningRoot: import("react").MemoExoticComponent<({ isStreaming, open: controlledOpen, defaultOpen, onOpenChange, duration: durationProp, children, ...props }: ReasoningRootProps) => import("react").JSX.Element>;
16
+ export type ReasoningTriggerProps = ComponentProps<typeof Collapsible.Trigger>;
17
+ export declare const ReasoningTrigger: (props: ReasoningTriggerProps) => import("react").JSX.Element;
18
+ export type ReasoningContentProps = ComponentProps<typeof Collapsible.Panel>;
19
+ export declare const ReasoningContent: (props: ReasoningContentProps) => import("react").JSX.Element;
20
+ export {};
@@ -0,0 +1,49 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ // Headless reasoning disclosure. Owns the open state and the streaming
4
+ // duration tracking; exposes them via useReasoning() so the styled layer can
5
+ // render its own trigger label (shimmer, crossfade) and markdown content.
6
+ import { createContext, memo, use, useCallback, useEffect, useMemo, useRef, useState, } from "react";
7
+ import { Collapsible } from "../internal/collapsible.js";
8
+ const MS_IN_S = 1000;
9
+ const ReasoningContext = createContext(null);
10
+ export const useReasoning = () => {
11
+ const context = use(ReasoningContext);
12
+ if (!context) {
13
+ throw new Error("Reasoning components must be used within Reasoning");
14
+ }
15
+ return context;
16
+ };
17
+ export const ReasoningRoot = memo(({ isStreaming = false, open: controlledOpen, defaultOpen, onOpenChange, duration: durationProp, children, ...props }) => {
18
+ const resolvedDefaultOpen = defaultOpen ?? false;
19
+ const [internalOpen, setInternalOpen] = useState(resolvedDefaultOpen);
20
+ const isControlled = controlledOpen !== undefined;
21
+ const isOpen = isControlled ? controlledOpen : internalOpen;
22
+ const [duration, setDuration] = useState(durationProp);
23
+ const hasEverStreamedRef = useRef(isStreaming);
24
+ const startTimeRef = useRef(null);
25
+ const setIsOpen = useCallback((value) => {
26
+ if (!isControlled) {
27
+ setInternalOpen(value);
28
+ }
29
+ onOpenChange?.(value);
30
+ }, [isControlled, onOpenChange]);
31
+ // Track when streaming starts and compute duration
32
+ useEffect(() => {
33
+ if (isStreaming) {
34
+ hasEverStreamedRef.current = true;
35
+ if (startTimeRef.current === null) {
36
+ startTimeRef.current = Date.now();
37
+ }
38
+ }
39
+ else if (startTimeRef.current !== null) {
40
+ setDuration(Math.ceil((Date.now() - startTimeRef.current) / MS_IN_S));
41
+ startTimeRef.current = null;
42
+ }
43
+ }, [isStreaming]);
44
+ const contextValue = useMemo(() => ({ duration, isOpen, isStreaming, setIsOpen }), [duration, isOpen, isStreaming, setIsOpen]);
45
+ return (_jsx(ReasoningContext, { value: contextValue, children: _jsx(Collapsible, { open: isOpen, onOpenChange: (open) => setIsOpen(open), "aria-busy": isStreaming || undefined, "data-reasoning": "", "data-streaming": isStreaming ? "" : undefined, ...props, children: children }) }));
46
+ });
47
+ ReasoningRoot.displayName = "Reasoning";
48
+ export const ReasoningTrigger = (props) => (_jsx(Collapsible.Trigger, { "data-reasoning-trigger": "", ...props }));
49
+ export const ReasoningContent = (props) => (_jsx(Collapsible.Panel, { "data-reasoning-content": "", ...props }));
@@ -0,0 +1,2 @@
1
+ export * as Steps from "./index.parts.js";
2
+ export type { StepStatus, StepsIconProps, StepsItemProps, StepsLabelProps, StepsPanelProps, StepsRootProps, StepsStatusProps, StepsTriggerProps, } from "./steps.js";
@@ -0,0 +1,2 @@
1
+ // Public surface of @intentface/chat/steps. No directive — see index.parts.ts.
2
+ export * as Steps from "./index.parts.js";
@@ -0,0 +1 @@
1
+ export { StepsIcon as Icon, StepsItem as Item, StepsLabel as Label, StepsPanel as Panel, StepsRoot as Root, StepsStatus as Status, StepsTrigger as Trigger, } from "./steps.js";
@@ -0,0 +1,5 @@
1
+ // Part names for the `Steps` namespace. Deliberately carries no
2
+ // "use client" directive: this module and index.ts must stay server-resolvable
3
+ // so a React Server Component can reach Steps.Root and its siblings
4
+ // through them. The directive lives one level down, on steps.tsx.
5
+ export { StepsIcon as Icon, StepsItem as Item, StepsLabel as Label, StepsPanel as Panel, StepsRoot as Root, StepsStatus as Status, StepsTrigger as Trigger, } from "./steps.js";
@@ -0,0 +1,53 @@
1
+ import { type ComponentProps } from "react";
2
+ import { Collapsible } from "../internal/collapsible.js";
3
+ import type { PrimitiveProps } from "../internal/primitive-props.js";
4
+ export type StepsRootProps = PrimitiveProps<"div">;
5
+ export declare const StepsRoot: {
6
+ ({ className, render, style, ...elementProps }: StepsRootProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
7
+ displayName: string;
8
+ };
9
+ export type StepStatus = string;
10
+ export type StepsItemProps = ComponentProps<typeof Collapsible> & {
11
+ status?: StepStatus;
12
+ };
13
+ export declare const StepsItem: {
14
+ ({ status, defaultOpen, ...props }: StepsItemProps): import("react").JSX.Element;
15
+ displayName: string;
16
+ };
17
+ export type StepsTriggerProps = ComponentProps<typeof Collapsible.Trigger>;
18
+ export declare const StepsTrigger: {
19
+ (props: StepsTriggerProps): import("react").JSX.Element;
20
+ displayName: string;
21
+ };
22
+ export type StepsPanelProps = ComponentProps<typeof Collapsible.Panel>;
23
+ export declare const StepsPanel: {
24
+ (props: StepsPanelProps): import("react").JSX.Element;
25
+ displayName: string;
26
+ };
27
+ export type StepsIconProps = PrimitiveProps<"span", {
28
+ status: StepStatus;
29
+ }> & {
30
+ status?: StepStatus;
31
+ };
32
+ export declare const StepsIcon: {
33
+ ({ status, className, render, style, ...elementProps }: StepsIconProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
34
+ displayName: string;
35
+ };
36
+ export type StepsLabelProps = PrimitiveProps<"span", {
37
+ status: StepStatus;
38
+ }> & {
39
+ status?: StepStatus;
40
+ };
41
+ export declare const StepsLabel: {
42
+ ({ status, className, render, style, ...elementProps }: StepsLabelProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
43
+ displayName: string;
44
+ };
45
+ export type StepsStatusProps = PrimitiveProps<"span", {
46
+ status: StepStatus;
47
+ }> & {
48
+ status?: StepStatus;
49
+ };
50
+ export declare const StepsStatus: {
51
+ ({ status, children, className, render, style, ...elementProps }: StepsStatusProps): import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>;
52
+ displayName: string;
53
+ };