prompt-area 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.
- package/LICENSE +21 -0
- package/README.md +120 -0
- package/dist/action-bar/index.d.ts +60 -0
- package/dist/action-bar/index.js +5 -0
- package/dist/action-bar/index.js.map +1 -0
- package/dist/chat-prompt-layout/index.d.ts +53 -0
- package/dist/chat-prompt-layout/index.js +5 -0
- package/dist/chat-prompt-layout/index.js.map +1 -0
- package/dist/chunk-ANZZEZP2.js +38 -0
- package/dist/chunk-ANZZEZP2.js.map +1 -0
- package/dist/chunk-BPJO4DGM.js +198 -0
- package/dist/chunk-BPJO4DGM.js.map +1 -0
- package/dist/chunk-BWVBDP7C.js +38 -0
- package/dist/chunk-BWVBDP7C.js.map +1 -0
- package/dist/chunk-E7HUXORB.js +2692 -0
- package/dist/chunk-E7HUXORB.js.map +1 -0
- package/dist/chunk-NF2LHZIE.js +12 -0
- package/dist/chunk-NF2LHZIE.js.map +1 -0
- package/dist/chunk-UBBCAMJA.js +116 -0
- package/dist/chunk-UBBCAMJA.js.map +1 -0
- package/dist/chunk-XDKRP7UE.js +125 -0
- package/dist/chunk-XDKRP7UE.js.map +1 -0
- package/dist/compact-prompt-area/index.d.ts +86 -0
- package/dist/compact-prompt-area/index.js +6 -0
- package/dist/compact-prompt-area/index.js.map +1 -0
- package/dist/helpers/index.d.ts +374 -0
- package/dist/helpers/index.js +291 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/prompt-area/index.d.ts +327 -0
- package/dist/prompt-area/index.js +6 -0
- package/dist/prompt-area/index.js.map +1 -0
- package/dist/status-bar/index.d.ts +50 -0
- package/dist/status-bar/index.js +5 -0
- package/dist/status-bar/index.js.map +1 -0
- package/dist/styles.css +2 -0
- package/dist/tailwind.css +181 -0
- package/dist/types-C4BgDEpe.d.ts +271 -0
- package/package.json +102 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { segmentsToPlainText } from './chunk-E7HUXORB.js';
|
|
3
|
+
import { useState, useRef, useMemo, useCallback } from 'react';
|
|
4
|
+
|
|
5
|
+
function usePromptAreaState(options = {}) {
|
|
6
|
+
const { initialValue = [] } = options;
|
|
7
|
+
const [value, setValue] = useState(initialValue);
|
|
8
|
+
const ref = useRef(null);
|
|
9
|
+
const plainText = useMemo(() => segmentsToPlainText(value), [value]);
|
|
10
|
+
const isEmpty = useMemo(() => {
|
|
11
|
+
if (value.length === 0) return true;
|
|
12
|
+
return value.every((seg) => seg.type === "text" && seg.text.trim() === "");
|
|
13
|
+
}, [value]);
|
|
14
|
+
const hasChips2 = useMemo(() => value.some((seg) => seg.type === "chip"), [value]);
|
|
15
|
+
const chips = useMemo(
|
|
16
|
+
() => value.filter((seg) => seg.type === "chip"),
|
|
17
|
+
[value]
|
|
18
|
+
);
|
|
19
|
+
const bind = useMemo(() => ({ ref, value, onChange: setValue }), [value]);
|
|
20
|
+
const clear = useCallback(() => {
|
|
21
|
+
if (ref.current) {
|
|
22
|
+
ref.current.clear();
|
|
23
|
+
} else {
|
|
24
|
+
setValue([]);
|
|
25
|
+
}
|
|
26
|
+
}, []);
|
|
27
|
+
const focus = useCallback(() => ref.current?.focus(), []);
|
|
28
|
+
const blur = useCallback(() => ref.current?.blur(), []);
|
|
29
|
+
const insertChip = useCallback(
|
|
30
|
+
(chip2) => ref.current?.insertChip(chip2),
|
|
31
|
+
[]
|
|
32
|
+
);
|
|
33
|
+
return {
|
|
34
|
+
bind,
|
|
35
|
+
plainText,
|
|
36
|
+
isEmpty,
|
|
37
|
+
hasChips: hasChips2,
|
|
38
|
+
chips,
|
|
39
|
+
clear,
|
|
40
|
+
focus,
|
|
41
|
+
blur,
|
|
42
|
+
insertChip
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/prompt-area/segment-helpers.ts
|
|
47
|
+
function text(value) {
|
|
48
|
+
return { type: "text", text: value };
|
|
49
|
+
}
|
|
50
|
+
function chip(opts) {
|
|
51
|
+
return { type: "chip", ...opts };
|
|
52
|
+
}
|
|
53
|
+
function isSegmentsEmpty(segments) {
|
|
54
|
+
if (segments.length === 0) return true;
|
|
55
|
+
return segments.every((seg) => seg.type === "text" && seg.text.trim() === "");
|
|
56
|
+
}
|
|
57
|
+
function hasChips(segments) {
|
|
58
|
+
return segments.some((seg) => seg.type === "chip");
|
|
59
|
+
}
|
|
60
|
+
function getChips(segments) {
|
|
61
|
+
return segments.filter((seg) => seg.type === "chip");
|
|
62
|
+
}
|
|
63
|
+
function getChipsByTrigger(segments, trigger) {
|
|
64
|
+
return segments.filter(
|
|
65
|
+
(seg) => seg.type === "chip" && seg.trigger === trigger
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/prompt-area/trigger-presets.ts
|
|
70
|
+
function mentionTrigger(opts = {}) {
|
|
71
|
+
const { char = "@", ...rest } = opts;
|
|
72
|
+
return {
|
|
73
|
+
char,
|
|
74
|
+
position: "any",
|
|
75
|
+
mode: "dropdown",
|
|
76
|
+
chipStyle: "pill",
|
|
77
|
+
accessibilityLabel: "mention",
|
|
78
|
+
...rest
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function commandTrigger(opts = {}) {
|
|
82
|
+
const { char = "/", ...rest } = opts;
|
|
83
|
+
return {
|
|
84
|
+
char,
|
|
85
|
+
position: "start",
|
|
86
|
+
mode: "dropdown",
|
|
87
|
+
chipStyle: "inline",
|
|
88
|
+
accessibilityLabel: "command",
|
|
89
|
+
...rest
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function hashtagTrigger(opts = {}) {
|
|
93
|
+
const { char = "#", ...rest } = opts;
|
|
94
|
+
return {
|
|
95
|
+
char,
|
|
96
|
+
position: "any",
|
|
97
|
+
mode: "dropdown",
|
|
98
|
+
chipStyle: "pill",
|
|
99
|
+
resolveOnSpace: true,
|
|
100
|
+
accessibilityLabel: "tag",
|
|
101
|
+
...rest
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function callbackTrigger(opts) {
|
|
105
|
+
const { char, ...rest } = opts;
|
|
106
|
+
return {
|
|
107
|
+
char,
|
|
108
|
+
position: "start",
|
|
109
|
+
mode: "callback",
|
|
110
|
+
...rest
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export { callbackTrigger, chip, commandTrigger, getChips, getChipsByTrigger, hasChips, hashtagTrigger, isSegmentsEmpty, mentionTrigger, text, usePromptAreaState };
|
|
115
|
+
//# sourceMappingURL=chunk-UBBCAMJA.js.map
|
|
116
|
+
//# sourceMappingURL=chunk-UBBCAMJA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/prompt-area/use-prompt-area-state.ts","../src/prompt-area/segment-helpers.ts","../src/prompt-area/trigger-presets.ts"],"names":["hasChips","chip"],"mappings":"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","file":"chunk-UBBCAMJA.js","sourcesContent":["/**\n * Convenience hook that wires up all the boilerplate state for a PromptArea.\n *\n * Instead of manually managing `useState<Segment[]>`, `useRef<PromptAreaHandle>`,\n * and computing derived values, call `usePromptAreaState()` once and spread\n * `bind` into your `<PromptArea>`.\n *\n * @example\n * ```tsx\n * function ChatInput() {\n * const { bind, plainText, isEmpty, chips, clear, focus } = usePromptAreaState()\n *\n * return (\n * <PromptArea\n * {...bind}\n * onSubmit={() => {\n * sendMessage(plainText)\n * clear()\n * }}\n * />\n * )\n * }\n * ```\n */\n\n'use client'\n\nimport { useCallback, useMemo, useRef, useState } from 'react'\nimport type { Segment, ChipSegment, PromptAreaHandle } from './types'\nimport { segmentsToPlainText } from './prompt-area-engine'\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport type UsePromptAreaStateOptions = {\n /** Initial segment value. Defaults to `[]`. */\n initialValue?: Segment[]\n}\n\n// ---------------------------------------------------------------------------\n// Return type\n// ---------------------------------------------------------------------------\n\nexport type PromptAreaBind = {\n /** Ref to attach to PromptArea — gives access to imperative methods. */\n ref: React.RefObject<PromptAreaHandle | null>\n /** Current segment array — pass as `value` prop. */\n value: Segment[]\n /** Setter — pass as `onChange` prop. */\n onChange: (segments: Segment[]) => void\n}\n\nexport type PromptAreaState = {\n /** Props to spread directly onto `<PromptArea {...bind} />`. Contains ref, value, and onChange. */\n bind: PromptAreaBind\n /** Derived plain text representation of the current value. */\n plainText: string\n /** `true` when the value is empty or whitespace-only. */\n isEmpty: boolean\n /** `true` when the value contains at least one chip. */\n hasChips: boolean\n /** All chip segments in the current value. */\n chips: ChipSegment[]\n /** Clear all content (both state and the editor DOM). */\n clear: () => void\n /** Focus the editor. */\n focus: () => void\n /** Blur the editor. */\n blur: () => void\n /** Insert a chip at the current cursor position. */\n insertChip: (chip: Omit<ChipSegment, 'type'>) => void\n}\n\n// ---------------------------------------------------------------------------\n// Hook\n// ---------------------------------------------------------------------------\n\nexport function usePromptAreaState(options: UsePromptAreaStateOptions = {}): PromptAreaState {\n const { initialValue = [] } = options\n\n const [value, setValue] = useState<Segment[]>(initialValue)\n const ref = useRef<PromptAreaHandle>(null)\n\n // Derived\n const plainText = useMemo(() => segmentsToPlainText(value), [value])\n\n const isEmpty = useMemo(() => {\n if (value.length === 0) return true\n return value.every((seg) => seg.type === 'text' && seg.text.trim() === '')\n }, [value])\n\n const hasChips = useMemo(() => value.some((seg) => seg.type === 'chip'), [value])\n\n const chips = useMemo(\n () => value.filter((seg): seg is ChipSegment => seg.type === 'chip'),\n [value],\n )\n\n // Bind object — safe to spread onto <PromptArea>\n const bind = useMemo<PromptAreaBind>(() => ({ ref, value, onChange: setValue }), [value])\n\n // Actions that proxy to the imperative handle\n const clear = useCallback(() => {\n if (ref.current) {\n ref.current.clear()\n } else {\n setValue([])\n }\n }, [])\n\n const focus = useCallback(() => ref.current?.focus(), [])\n const blur = useCallback(() => ref.current?.blur(), [])\n\n const insertChip = useCallback(\n (chip: Omit<ChipSegment, 'type'>) => ref.current?.insertChip(chip),\n [],\n )\n\n return {\n bind,\n plainText,\n isEmpty,\n hasChips,\n chips,\n clear,\n focus,\n blur,\n insertChip,\n }\n}\n","/**\n * Convenience helpers for creating and inspecting Segments.\n *\n * These reduce boilerplate when building AI chat UIs that work with the\n * PromptArea document model.\n *\n * @example\n * ```ts\n * import { text, chip, isSegmentsEmpty, segmentsToPlainText } from './segment-helpers'\n *\n * const greeting = [text('Hello '), chip({ trigger: '@', value: 'u1', displayText: 'Alice' })]\n * isSegmentsEmpty(greeting) // false\n * segmentsToPlainText(greeting) // \"Hello @Alice\"\n * ```\n */\n\nimport type { Segment, TextSegment, ChipSegment } from './types'\nimport { segmentsToPlainText, plainTextToSegments } from './prompt-area-engine'\n\n// Re-export serialization utilities so consumers have a single import.\nexport { segmentsToPlainText, plainTextToSegments }\n\n// ---------------------------------------------------------------------------\n// Factories\n// ---------------------------------------------------------------------------\n\n/** Create a text segment. */\nexport function text(value: string): TextSegment {\n return { type: 'text', text: value }\n}\n\n/** Create a chip segment. */\nexport function chip(opts: Omit<ChipSegment, 'type'>): ChipSegment {\n return { type: 'chip', ...opts }\n}\n\n// ---------------------------------------------------------------------------\n// Predicates\n// ---------------------------------------------------------------------------\n\n/** Returns `true` when the segment array is empty or contains only whitespace text. */\nexport function isSegmentsEmpty(segments: Segment[]): boolean {\n if (segments.length === 0) return true\n return segments.every((seg) => seg.type === 'text' && seg.text.trim() === '')\n}\n\n/** Returns `true` when the segment array contains at least one chip. */\nexport function hasChips(segments: Segment[]): boolean {\n return segments.some((seg) => seg.type === 'chip')\n}\n\n/** Extracts all chip segments from a segment array. */\nexport function getChips(segments: Segment[]): ChipSegment[] {\n return segments.filter((seg): seg is ChipSegment => seg.type === 'chip')\n}\n\n/** Extracts chips matching a specific trigger character. */\nexport function getChipsByTrigger(segments: Segment[], trigger: string): ChipSegment[] {\n return segments.filter(\n (seg): seg is ChipSegment => seg.type === 'chip' && seg.trigger === trigger,\n )\n}\n","/**\n * Pre-built trigger configuration factories for common AI chat patterns.\n *\n * Each factory returns a full `TriggerConfig` with sensible defaults.\n * Pass only what you need to override.\n *\n * @example\n * ```tsx\n * <PromptArea\n * triggers={[\n * mentionTrigger({ onSearch: searchUsers }),\n * commandTrigger({ onSearch: searchCommands }),\n * hashtagTrigger(),\n * ]}\n * />\n * ```\n */\n\nimport type { TriggerConfig } from './types'\n\n// ---------------------------------------------------------------------------\n// Shared option type — everything in TriggerConfig except the keys each\n// factory sets by default.\n// ---------------------------------------------------------------------------\n\ntype TriggerPresetOptions = Omit<Partial<TriggerConfig>, 'char' | 'position' | 'mode'>\n\n// ---------------------------------------------------------------------------\n// @mention — dropdown at any position\n// ---------------------------------------------------------------------------\n\nexport type MentionTriggerOptions = TriggerPresetOptions & {\n /** Override the trigger character. Defaults to `'@'`. */\n char?: string\n}\n\n/**\n * Creates a **mention** trigger (`@`).\n *\n * Defaults: `position: 'any'`, `mode: 'dropdown'`, `chipStyle: 'pill'`,\n * accessible label `\"mention\"`.\n */\nexport function mentionTrigger(opts: MentionTriggerOptions = {}): TriggerConfig {\n const { char = '@', ...rest } = opts\n return {\n char,\n position: 'any',\n mode: 'dropdown',\n chipStyle: 'pill',\n accessibilityLabel: 'mention',\n ...rest,\n }\n}\n\n// ---------------------------------------------------------------------------\n// /command — dropdown only at line start\n// ---------------------------------------------------------------------------\n\nexport type CommandTriggerOptions = TriggerPresetOptions & {\n /** Override the trigger character. Defaults to `'/'`. */\n char?: string\n}\n\n/**\n * Creates a **command** trigger (`/`).\n *\n * Defaults: `position: 'start'`, `mode: 'dropdown'`, `chipStyle: 'inline'`,\n * accessible label `\"command\"`.\n */\nexport function commandTrigger(opts: CommandTriggerOptions = {}): TriggerConfig {\n const { char = '/', ...rest } = opts\n return {\n char,\n position: 'start',\n mode: 'dropdown',\n chipStyle: 'inline',\n accessibilityLabel: 'command',\n ...rest,\n }\n}\n\n// ---------------------------------------------------------------------------\n// #hashtag — dropdown at any position, auto-resolve on space\n// ---------------------------------------------------------------------------\n\nexport type HashtagTriggerOptions = TriggerPresetOptions & {\n /** Override the trigger character. Defaults to `'#'`. */\n char?: string\n}\n\n/**\n * Creates a **hashtag / tag** trigger (`#`).\n *\n * Defaults: `position: 'any'`, `mode: 'dropdown'`, `chipStyle: 'pill'`,\n * `resolveOnSpace: true`, accessible label `\"tag\"`.\n */\nexport function hashtagTrigger(opts: HashtagTriggerOptions = {}): TriggerConfig {\n const { char = '#', ...rest } = opts\n return {\n char,\n position: 'any',\n mode: 'dropdown',\n chipStyle: 'pill',\n resolveOnSpace: true,\n accessibilityLabel: 'tag',\n ...rest,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Generic callback trigger (e.g., for file pickers, model selectors)\n// ---------------------------------------------------------------------------\n\nexport type CallbackTriggerOptions = Omit<Partial<TriggerConfig>, 'mode'> & {\n /** The trigger character. Required. */\n char: string\n}\n\n/**\n * Creates a **callback** trigger that fires `onActivate` instead of showing\n * a dropdown. Useful for opening file pickers, model selectors, etc.\n *\n * Defaults: `position: 'start'`, `mode: 'callback'`.\n */\nexport function callbackTrigger(opts: CallbackTriggerOptions): TriggerConfig {\n const { char, ...rest } = opts\n return {\n char,\n position: 'start',\n mode: 'callback',\n ...rest,\n }\n}\n"]}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { cn } from './chunk-NF2LHZIE.js';
|
|
3
|
+
import { useRef, useState, useCallback, useEffect } from 'react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
var SHOW_THRESHOLD = 300;
|
|
7
|
+
var HIDE_THRESHOLD = 100;
|
|
8
|
+
function useScrollObserver() {
|
|
9
|
+
const scrollRef = useRef(null);
|
|
10
|
+
const [showGoToTop, setShowGoToTop] = useState(false);
|
|
11
|
+
const [showGoToBottom, setShowGoToBottom] = useState(false);
|
|
12
|
+
const rafId = useRef(0);
|
|
13
|
+
const update = useCallback(() => {
|
|
14
|
+
cancelAnimationFrame(rafId.current);
|
|
15
|
+
rafId.current = requestAnimationFrame(() => {
|
|
16
|
+
const el = scrollRef.current;
|
|
17
|
+
if (!el) return;
|
|
18
|
+
const scrollTop = el.scrollTop;
|
|
19
|
+
const distanceFromBottom = el.scrollHeight - scrollTop - el.clientHeight;
|
|
20
|
+
setShowGoToTop((prev) => prev ? scrollTop > HIDE_THRESHOLD : scrollTop > SHOW_THRESHOLD);
|
|
21
|
+
setShowGoToBottom(
|
|
22
|
+
(prev) => prev ? distanceFromBottom > HIDE_THRESHOLD : distanceFromBottom > SHOW_THRESHOLD
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
}, []);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const el = scrollRef.current;
|
|
28
|
+
if (!el) return;
|
|
29
|
+
el.addEventListener("scroll", update, { passive: true });
|
|
30
|
+
update();
|
|
31
|
+
return () => {
|
|
32
|
+
el.removeEventListener("scroll", update);
|
|
33
|
+
cancelAnimationFrame(rafId.current);
|
|
34
|
+
};
|
|
35
|
+
}, [update]);
|
|
36
|
+
const scrollToTop = useCallback(() => {
|
|
37
|
+
scrollRef.current?.scrollTo({ top: 0, behavior: "smooth" });
|
|
38
|
+
}, []);
|
|
39
|
+
const scrollToBottom = useCallback(() => {
|
|
40
|
+
const el = scrollRef.current;
|
|
41
|
+
if (!el) return;
|
|
42
|
+
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
|
|
43
|
+
}, []);
|
|
44
|
+
return { scrollRef, showGoToTop, showGoToBottom, scrollToTop, scrollToBottom };
|
|
45
|
+
}
|
|
46
|
+
var NAV_BUTTON_CLASS = "pointer-events-auto rounded-full border bg-background p-2 shadow-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors animate-in fade-in-0 zoom-in-95 duration-150";
|
|
47
|
+
function Svg({ className, children }) {
|
|
48
|
+
return /* @__PURE__ */ jsx(
|
|
49
|
+
"svg",
|
|
50
|
+
{
|
|
51
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
52
|
+
width: "24",
|
|
53
|
+
height: "24",
|
|
54
|
+
viewBox: "0 0 24 24",
|
|
55
|
+
fill: "none",
|
|
56
|
+
stroke: "currentColor",
|
|
57
|
+
strokeWidth: "2",
|
|
58
|
+
strokeLinecap: "round",
|
|
59
|
+
strokeLinejoin: "round",
|
|
60
|
+
"aria-hidden": "true",
|
|
61
|
+
className,
|
|
62
|
+
children
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
var ArrowUp = ({ className }) => /* @__PURE__ */ jsxs(Svg, { className, children: [
|
|
67
|
+
/* @__PURE__ */ jsx("path", { d: "m5 12 7-7 7 7" }),
|
|
68
|
+
/* @__PURE__ */ jsx("path", { d: "M12 19V5" })
|
|
69
|
+
] });
|
|
70
|
+
var ArrowDown = ({ className }) => /* @__PURE__ */ jsxs(Svg, { className, children: [
|
|
71
|
+
/* @__PURE__ */ jsx("path", { d: "M12 5v14" }),
|
|
72
|
+
/* @__PURE__ */ jsx("path", { d: "m19 12-7 7-7-7" })
|
|
73
|
+
] });
|
|
74
|
+
function ChatPromptLayout({
|
|
75
|
+
children,
|
|
76
|
+
prompt,
|
|
77
|
+
className,
|
|
78
|
+
"aria-label": ariaLabel,
|
|
79
|
+
"data-test-id": dataTestId,
|
|
80
|
+
ref
|
|
81
|
+
}) {
|
|
82
|
+
const { scrollRef, showGoToTop, showGoToBottom, scrollToTop, scrollToBottom } = useScrollObserver();
|
|
83
|
+
return /* @__PURE__ */ jsxs(
|
|
84
|
+
"div",
|
|
85
|
+
{
|
|
86
|
+
ref,
|
|
87
|
+
role: "region",
|
|
88
|
+
"aria-label": ariaLabel ?? "Chat layout",
|
|
89
|
+
"data-test-id": dataTestId,
|
|
90
|
+
className: cn("chat-prompt-layout", "flex h-full flex-col", className),
|
|
91
|
+
children: [
|
|
92
|
+
/* @__PURE__ */ jsxs("div", { ref: scrollRef, className: "relative flex-1 overflow-y-auto", children: [
|
|
93
|
+
children,
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { className: "pointer-events-none sticky bottom-4 flex justify-end gap-2 px-4 pb-2", children: [
|
|
95
|
+
showGoToTop && /* @__PURE__ */ jsx(
|
|
96
|
+
"button",
|
|
97
|
+
{
|
|
98
|
+
type: "button",
|
|
99
|
+
onClick: scrollToTop,
|
|
100
|
+
className: NAV_BUTTON_CLASS,
|
|
101
|
+
"aria-label": "Scroll to top",
|
|
102
|
+
children: /* @__PURE__ */ jsx(ArrowUp, { className: "size-4" })
|
|
103
|
+
}
|
|
104
|
+
),
|
|
105
|
+
showGoToBottom && /* @__PURE__ */ jsx(
|
|
106
|
+
"button",
|
|
107
|
+
{
|
|
108
|
+
type: "button",
|
|
109
|
+
onClick: scrollToBottom,
|
|
110
|
+
className: NAV_BUTTON_CLASS,
|
|
111
|
+
"aria-label": "Scroll to bottom",
|
|
112
|
+
children: /* @__PURE__ */ jsx(ArrowDown, { className: "size-4" })
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
] })
|
|
116
|
+
] }),
|
|
117
|
+
/* @__PURE__ */ jsx("div", { className: "shrink-0", children: prompt })
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export { ChatPromptLayout };
|
|
124
|
+
//# sourceMappingURL=chunk-XDKRP7UE.js.map
|
|
125
|
+
//# sourceMappingURL=chunk-XDKRP7UE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/chat-prompt-layout/use-scroll-observer.ts","../src/chat-prompt-layout/chat-prompt-layout.tsx"],"names":[],"mappings":"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","file":"chunk-XDKRP7UE.js","sourcesContent":["'use client'\n\nimport { useCallback, useEffect, useRef, useState } from 'react'\n\n/** Distance from edge before showing a button */\nconst SHOW_THRESHOLD = 300\n/** Distance from edge before hiding a button (larger = less flicker) */\nconst HIDE_THRESHOLD = 100\n\nexport function useScrollObserver() {\n const scrollRef = useRef<HTMLDivElement>(null)\n const [showGoToTop, setShowGoToTop] = useState(false)\n const [showGoToBottom, setShowGoToBottom] = useState(false)\n const rafId = useRef(0)\n\n const update = useCallback(() => {\n // Coalesce rapid scroll events into a single rAF\n cancelAnimationFrame(rafId.current)\n rafId.current = requestAnimationFrame(() => {\n const el = scrollRef.current\n if (!el) return\n\n const scrollTop = el.scrollTop\n const distanceFromBottom = el.scrollHeight - scrollTop - el.clientHeight\n\n // Hysteresis: use a higher threshold to show, lower to hide\n setShowGoToTop((prev) => (prev ? scrollTop > HIDE_THRESHOLD : scrollTop > SHOW_THRESHOLD))\n setShowGoToBottom((prev) =>\n prev ? distanceFromBottom > HIDE_THRESHOLD : distanceFromBottom > SHOW_THRESHOLD,\n )\n })\n }, [])\n\n useEffect(() => {\n const el = scrollRef.current\n if (!el) return\n\n el.addEventListener('scroll', update, { passive: true })\n update()\n\n return () => {\n el.removeEventListener('scroll', update)\n cancelAnimationFrame(rafId.current)\n }\n }, [update])\n\n const scrollToTop = useCallback(() => {\n scrollRef.current?.scrollTo({ top: 0, behavior: 'smooth' })\n }, [])\n\n const scrollToBottom = useCallback(() => {\n const el = scrollRef.current\n if (!el) return\n el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' })\n }, [])\n\n return { scrollRef, showGoToTop, showGoToBottom, scrollToTop, scrollToBottom }\n}\n","'use client'\n\nimport { cn } from '@/lib/utils'\nimport type { ChatPromptLayoutProps } from './types'\nimport { useScrollObserver } from './use-scroll-observer'\n\nconst NAV_BUTTON_CLASS =\n 'pointer-events-auto rounded-full border bg-background p-2 shadow-sm text-muted-foreground hover:bg-accent hover:text-foreground transition-colors animate-in fade-in-0 zoom-in-95 duration-150'\n\ntype IconProps = { className?: string }\n\n/** Shared SVG wrapper matching the lucide icon defaults (no dependency). */\nfunction Svg({ className, children }: IconProps & { children: React.ReactNode }) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width=\"24\"\n height=\"24\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className={className}>\n {children}\n </svg>\n )\n}\n\nconst ArrowUp = ({ className }: IconProps) => (\n <Svg className={className}>\n <path d=\"m5 12 7-7 7 7\" />\n <path d=\"M12 19V5\" />\n </Svg>\n)\nconst ArrowDown = ({ className }: IconProps) => (\n <Svg className={className}>\n <path d=\"M12 5v14\" />\n <path d=\"m19 12-7 7-7-7\" />\n </Svg>\n)\n\n/**\n * ChatPromptLayout - A full-height chat layout with scrollable messages\n * and a bottom-anchored prompt slot.\n *\n * Pass chat messages as `children` and the prompt area via the `prompt`\n * prop. Contextual scroll buttons appear when the user scrolls away\n * from the top or bottom of the messages area.\n *\n * @example\n * ```tsx\n * <ChatPromptLayout\n * className=\"h-[600px]\"\n * prompt={\n * <div className=\"border-t p-4\">\n * <PromptArea ... />\n * <ActionBar ... />\n * </div>\n * }\n * >\n * {messages.map(msg => <ChatBubble key={msg.id} {...msg} />)}\n * </ChatPromptLayout>\n * ```\n */\nexport function ChatPromptLayout({\n children,\n prompt,\n className,\n 'aria-label': ariaLabel,\n 'data-test-id': dataTestId,\n ref,\n}: ChatPromptLayoutProps & { ref?: React.Ref<HTMLDivElement> }) {\n const { scrollRef, showGoToTop, showGoToBottom, scrollToTop, scrollToBottom } =\n useScrollObserver()\n\n return (\n <div\n ref={ref}\n role=\"region\"\n aria-label={ariaLabel ?? 'Chat layout'}\n data-test-id={dataTestId}\n className={cn('chat-prompt-layout', 'flex h-full flex-col', className)}>\n <div ref={scrollRef} className=\"relative flex-1 overflow-y-auto\">\n {children}\n\n <div className=\"pointer-events-none sticky bottom-4 flex justify-end gap-2 px-4 pb-2\">\n {showGoToTop && (\n <button\n type=\"button\"\n onClick={scrollToTop}\n className={NAV_BUTTON_CLASS}\n aria-label=\"Scroll to top\">\n <ArrowUp className=\"size-4\" />\n </button>\n )}\n {showGoToBottom && (\n <button\n type=\"button\"\n onClick={scrollToBottom}\n className={NAV_BUTTON_CLASS}\n aria-label=\"Scroll to bottom\">\n <ArrowDown className=\"size-4\" />\n </button>\n )}\n </div>\n </div>\n\n <div className=\"shrink-0\">{prompt}</div>\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { S as Segment, f as TriggerConfig, C as ChipSegment, c as PromptAreaImage, P as PromptAreaFile, b as PromptAreaHandle } from '../types-C4BgDEpe.js';
|
|
3
|
+
|
|
4
|
+
type CompactPromptAreaProps = {
|
|
5
|
+
/** The document segments (controlled) */
|
|
6
|
+
value: Segment[];
|
|
7
|
+
/** Called when the content changes */
|
|
8
|
+
onChange: (segments: Segment[]) => void;
|
|
9
|
+
/** Trigger configurations */
|
|
10
|
+
triggers?: TriggerConfig[];
|
|
11
|
+
/** Placeholder text when empty. Pass an array to animate between them. */
|
|
12
|
+
placeholder?: string | string[];
|
|
13
|
+
/** Whether the input is disabled */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Whether to render simple inline markdown */
|
|
16
|
+
markdown?: boolean;
|
|
17
|
+
/** Called when Enter is pressed (without Shift) */
|
|
18
|
+
onSubmit?: (segments: Segment[]) => void;
|
|
19
|
+
/** Called when Escape is pressed */
|
|
20
|
+
onEscape?: () => void;
|
|
21
|
+
/** Called when a chip element is clicked */
|
|
22
|
+
onChipClick?: (chip: ChipSegment) => void;
|
|
23
|
+
/** Called when a new chip is added */
|
|
24
|
+
onChipAdd?: (chip: ChipSegment) => void;
|
|
25
|
+
/** Called when a chip is deleted */
|
|
26
|
+
onChipDelete?: (chip: ChipSegment) => void;
|
|
27
|
+
/** Called after content is pasted */
|
|
28
|
+
onPaste?: (data: {
|
|
29
|
+
segments: Segment[];
|
|
30
|
+
source: 'internal' | 'external';
|
|
31
|
+
}) => void;
|
|
32
|
+
/** Array of image attachments */
|
|
33
|
+
images?: PromptAreaImage[];
|
|
34
|
+
/** Called when the user pastes an image */
|
|
35
|
+
onImagePaste?: (file: File) => void;
|
|
36
|
+
/** Called when the user removes an image */
|
|
37
|
+
onImageRemove?: (image: PromptAreaImage) => void;
|
|
38
|
+
/** Array of file attachments */
|
|
39
|
+
files?: PromptAreaFile[];
|
|
40
|
+
/** Called when the user removes a file */
|
|
41
|
+
onFileRemove?: (file: PromptAreaFile) => void;
|
|
42
|
+
/** Icon for the circular plus button on the left. Defaults to Plus icon. */
|
|
43
|
+
plusButtonIcon?: React.ReactNode;
|
|
44
|
+
/** Click handler for the plus button */
|
|
45
|
+
onPlusClick?: () => void;
|
|
46
|
+
/** Icon for the circular submit button on the right. Defaults to ArrowUp icon. */
|
|
47
|
+
submitButtonIcon?: React.ReactNode;
|
|
48
|
+
/** Slot rendered immediately before the submit button (e.g., mic button) */
|
|
49
|
+
beforeSubmitSlot?: React.ReactNode;
|
|
50
|
+
/** Maximum height the expanded area can reach in pixels. Defaults to 320. */
|
|
51
|
+
maxHeight?: number;
|
|
52
|
+
/** Additional CSS class for the outer container */
|
|
53
|
+
className?: string;
|
|
54
|
+
/** Accessible label */
|
|
55
|
+
'aria-label'?: string;
|
|
56
|
+
/** data-test-id for e2e testing */
|
|
57
|
+
'data-test-id'?: string;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* CompactPromptArea – A pill-shaped prompt input that sits on a single row
|
|
62
|
+
* and expands downward on focus.
|
|
63
|
+
*
|
|
64
|
+
* - Left: circular plus button
|
|
65
|
+
* - Middle: PromptArea text input (expands down when focused)
|
|
66
|
+
* - Right: optional slot + circular submit button
|
|
67
|
+
*
|
|
68
|
+
* Reuses PromptArea internally with autoGrow enabled.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```tsx
|
|
72
|
+
* <CompactPromptArea
|
|
73
|
+
* value={segments}
|
|
74
|
+
* onChange={setSegments}
|
|
75
|
+
* placeholder="Ask anything..."
|
|
76
|
+
* onSubmit={handleSubmit}
|
|
77
|
+
* onPlusClick={() => setMenuOpen(true)}
|
|
78
|
+
* beforeSubmitSlot={<button aria-label="Voice"><Mic className="size-4" /></button>}
|
|
79
|
+
* />
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
declare function CompactPromptArea({ value, onChange, triggers, placeholder, disabled, markdown, onSubmit, onEscape, onChipClick, onChipAdd, onChipDelete, onPaste, images, onImagePaste, onImageRemove, files, onFileRemove, plusButtonIcon, onPlusClick, submitButtonIcon, beforeSubmitSlot, maxHeight, className, 'aria-label': ariaLabel, 'data-test-id': dataTestId, ref, }: CompactPromptAreaProps & {
|
|
83
|
+
ref?: React.Ref<PromptAreaHandle>;
|
|
84
|
+
}): react.JSX.Element;
|
|
85
|
+
|
|
86
|
+
export { CompactPromptArea, type CompactPromptAreaProps };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
|