@signalflare-ai/ui 1.0.0 → 1.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/CHANGELOG.md +12 -0
- package/ai/component-registry.json +71 -1
- package/ai/component-registry.md +58 -1
- package/dist/.build-complete +1 -1
- package/dist/{ai-prompt-input-Dy1LfxPk.js → ai-prompt-input-CuluUzpf.js} +475 -20
- package/dist/ai-prompt-input-CuluUzpf.js.map +1 -0
- package/dist/components/ai-prompt-input.js +2 -2
- package/dist/index.js +2 -2
- package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts +46 -2
- package/dist/src/components/ai-prompt-input/ai-prompt-input.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/controller.d.ts +10 -2
- package/dist/src/components/ai-prompt-input/controller.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/index.d.ts +2 -2
- package/dist/src/components/ai-prompt-input/index.d.ts.map +1 -1
- package/dist/src/components/ai-prompt-input/types.d.ts +16 -0
- package/dist/src/components/ai-prompt-input/types.d.ts.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/styles/sf-binding.css +8 -0
- package/dist/styles/sf-standalone.css +1 -1
- package/package.json +1 -1
- package/scripts/component-registry/index.ts +2 -2
- package/dist/ai-prompt-input-Dy1LfxPk.js.map +0 -1
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { t as cn } from "./cn-YROP2_ox.js";
|
|
3
3
|
import { t as Tooltip } from "./tooltip-Cb7QW-7H.js";
|
|
4
4
|
import { t as Button } from "./button-De0267YU.js";
|
|
5
|
-
import { t as InputGroup } from "./input-DddtBN-g.js";
|
|
6
5
|
import { Fragment, createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
7
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
import { ArrowUpIcon, ArrowsClockwiseIcon, CaretDownIcon, CheckIcon, FileIcon, FileTextIcon, ImageIcon, MicrophoneIcon, PaperclipIcon, PlusIcon, SpinnerGapIcon, SquareIcon, XIcon } from "@phosphor-icons/react";
|
|
@@ -442,7 +441,8 @@ var useStore = (source, selector = (s) => s, compare) => useSelector(source, sel
|
|
|
442
441
|
*/
|
|
443
442
|
var DEFAULT_REQUEST = {
|
|
444
443
|
text: "",
|
|
445
|
-
files: []
|
|
444
|
+
files: [],
|
|
445
|
+
tags: []
|
|
446
446
|
};
|
|
447
447
|
var DEFAULT_DISPLAY = { sendStatus: "idle" };
|
|
448
448
|
function createPromptInputRequestController(options = {}) {
|
|
@@ -494,13 +494,60 @@ function createPromptInputRequestController(options = {}) {
|
|
|
494
494
|
};
|
|
495
495
|
});
|
|
496
496
|
},
|
|
497
|
+
addTags(tags) {
|
|
498
|
+
request.setState((prev) => {
|
|
499
|
+
const existing = prev.tags ?? [];
|
|
500
|
+
const incoming = tags.filter((tag) => !existing.some((item) => item.id === tag.id));
|
|
501
|
+
return {
|
|
502
|
+
...prev,
|
|
503
|
+
tags: [...existing, ...incoming]
|
|
504
|
+
};
|
|
505
|
+
});
|
|
506
|
+
},
|
|
507
|
+
insertTag(tag, range) {
|
|
508
|
+
request.setState((prev) => {
|
|
509
|
+
const mention = tag.mention ?? `@${tag.label.replace(/\s+/g, "-").toLowerCase()}`;
|
|
510
|
+
const text = prev.text ?? "";
|
|
511
|
+
const start = range?.start ?? text.length;
|
|
512
|
+
const end = range?.end ?? text.length;
|
|
513
|
+
const prefix = text.slice(0, start);
|
|
514
|
+
const suffix = text.slice(end);
|
|
515
|
+
const needsPrefixSpace = prefix.length > 0 && !/\s$/.test(prefix);
|
|
516
|
+
const needsSuffixSpace = suffix.length > 0 && !/^\s/.test(suffix);
|
|
517
|
+
const nextText = `${prefix}${needsPrefixSpace ? " " : ""}${mention}${needsSuffixSpace ? " " : ""}${suffix}`;
|
|
518
|
+
const nextTag = {
|
|
519
|
+
...tag,
|
|
520
|
+
mention
|
|
521
|
+
};
|
|
522
|
+
const existing = prev.tags ?? [];
|
|
523
|
+
const nextTags = existing.some((item) => item.id === tag.id) ? existing.map((item) => item.id === tag.id ? nextTag : item) : [...existing, nextTag];
|
|
524
|
+
return {
|
|
525
|
+
...prev,
|
|
526
|
+
text: nextText,
|
|
527
|
+
tags: nextTags
|
|
528
|
+
};
|
|
529
|
+
});
|
|
530
|
+
},
|
|
531
|
+
removeTag(id) {
|
|
532
|
+
request.setState((prev) => ({
|
|
533
|
+
...prev,
|
|
534
|
+
tags: (prev.tags ?? []).filter((tag) => tag.id !== id)
|
|
535
|
+
}));
|
|
536
|
+
},
|
|
537
|
+
clearTags() {
|
|
538
|
+
request.setState((prev) => ({
|
|
539
|
+
...prev,
|
|
540
|
+
tags: []
|
|
541
|
+
}));
|
|
542
|
+
},
|
|
497
543
|
resetRequest() {
|
|
498
544
|
request.setState((prev) => {
|
|
499
545
|
for (const f of prev.files) if (f.url.startsWith("blob:")) URL.revokeObjectURL(f.url);
|
|
500
546
|
return {
|
|
501
547
|
...prev,
|
|
502
548
|
text: "",
|
|
503
|
-
files: []
|
|
549
|
+
files: [],
|
|
550
|
+
tags: []
|
|
504
551
|
};
|
|
505
552
|
});
|
|
506
553
|
},
|
|
@@ -698,9 +745,10 @@ function makeAttachmentId() {
|
|
|
698
745
|
* </PromptInput>
|
|
699
746
|
* ```
|
|
700
747
|
*/
|
|
701
|
-
|
|
748
|
+
function PromptInput({ className, accept, multiple, globalDrop, maxFiles, maxFileSize, onError, onSubmit, children, backLayer, backLayerTitle = "Context", backLayerOpen, onBackLayerOpenChange, autoOpenBackLayerWhen, ...props }) {
|
|
702
749
|
const hasBackLayer = backLayer !== void 0;
|
|
703
750
|
const controller = useOptionalPromptInputController();
|
|
751
|
+
const requestCtrl = useOptionalPromptInputRequestController();
|
|
704
752
|
const usingProvider = !!controller;
|
|
705
753
|
const inputRef = useRef(null);
|
|
706
754
|
const anchorRef = useRef(null);
|
|
@@ -714,6 +762,7 @@ var PromptInput = ({ className, accept, multiple, globalDrop, maxFiles, maxFileS
|
|
|
714
762
|
}, [autoOpenBackLayerWhen, onBackLayerOpenChange]);
|
|
715
763
|
const [items, setItems] = useState([]);
|
|
716
764
|
const files = usingProvider ? controller.attachments.files : items;
|
|
765
|
+
const tags = useStore(requestCtrl?.request ?? FALLBACK_REQUEST_STORE, (state) => state.tags ?? []);
|
|
717
766
|
const openFileDialogLocal = useCallback(() => {
|
|
718
767
|
inputRef.current?.click();
|
|
719
768
|
}, []);
|
|
@@ -837,7 +886,7 @@ var PromptInput = ({ className, accept, multiple, globalDrop, maxFiles, maxFileS
|
|
|
837
886
|
const handleSubmit = (event) => {
|
|
838
887
|
event.preventDefault();
|
|
839
888
|
const form = event.currentTarget;
|
|
840
|
-
const text = usingProvider ? controller.textInput.value : new FormData(form).get("message") || "";
|
|
889
|
+
const text = requestCtrl ? requestCtrl.request.state.text : usingProvider ? controller.textInput.value : new FormData(form).get("message") || "";
|
|
841
890
|
if (!usingProvider) form.reset();
|
|
842
891
|
const doSubmit = async () => {
|
|
843
892
|
const convertedFiles = await Promise.all(files.map(async (item) => {
|
|
@@ -850,10 +899,14 @@ var PromptInput = ({ className, accept, multiple, globalDrop, maxFiles, maxFileS
|
|
|
850
899
|
try {
|
|
851
900
|
await onSubmit({
|
|
852
901
|
text,
|
|
853
|
-
files: convertedFiles
|
|
902
|
+
files: convertedFiles,
|
|
903
|
+
tags
|
|
854
904
|
}, event);
|
|
855
|
-
|
|
856
|
-
|
|
905
|
+
if (requestCtrl) requestCtrl.resetRequest();
|
|
906
|
+
else {
|
|
907
|
+
clear();
|
|
908
|
+
if (usingProvider) controller.textInput.clear();
|
|
909
|
+
}
|
|
857
910
|
} catch {}
|
|
858
911
|
};
|
|
859
912
|
doSubmit().catch(() => {});
|
|
@@ -878,11 +931,14 @@ var PromptInput = ({ className, accept, multiple, globalDrop, maxFiles, maxFileS
|
|
|
878
931
|
children: backLayer
|
|
879
932
|
}),
|
|
880
933
|
/* @__PURE__ */ jsx("div", {
|
|
881
|
-
className: "flex flex-col overflow-
|
|
934
|
+
className: "flex flex-col overflow-visible rounded-xl bg-sf-base ring ring-sf-line/50 focus-within:ring-sf-ring",
|
|
882
935
|
children
|
|
883
936
|
})
|
|
884
937
|
]
|
|
885
|
-
}) : /* @__PURE__ */ jsx(
|
|
938
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
939
|
+
className: "flex w-full flex-col items-stretch overflow-visible rounded-xl border-0 bg-sf-base shadow-xs ring ring-sf-line focus-within:ring-sf-ring",
|
|
940
|
+
children
|
|
941
|
+
});
|
|
886
942
|
const inner = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
887
943
|
/* @__PURE__ */ jsx("span", {
|
|
888
944
|
"aria-hidden": "true",
|
|
@@ -909,7 +965,7 @@ var PromptInput = ({ className, accept, multiple, globalDrop, maxFiles, maxFileS
|
|
|
909
965
|
value: ctx,
|
|
910
966
|
children: inner
|
|
911
967
|
});
|
|
912
|
-
}
|
|
968
|
+
}
|
|
913
969
|
var PromptInputBody = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
914
970
|
className: cn("contents", className),
|
|
915
971
|
...props
|
|
@@ -970,6 +1026,8 @@ var handleTextareaKeyDown = (e) => {
|
|
|
970
1026
|
*/
|
|
971
1027
|
var PromptInputTextarea = ({ onChange, className, placeholder = "What would you like to know?", ...props }) => {
|
|
972
1028
|
const controller = useOptionalPromptInputController();
|
|
1029
|
+
const requestCtrl = useOptionalPromptInputRequestController();
|
|
1030
|
+
const requestText = useStore(requestCtrl?.request ?? FALLBACK_REQUEST_STORE, (state) => typeof state.text === "string" ? state.text : "");
|
|
973
1031
|
const attachments = usePromptInputAttachments();
|
|
974
1032
|
const handlePaste = (event) => {
|
|
975
1033
|
const items = event.clipboardData?.items;
|
|
@@ -984,7 +1042,13 @@ var PromptInputTextarea = ({ onChange, className, placeholder = "What would you
|
|
|
984
1042
|
attachments.add(pastedFiles);
|
|
985
1043
|
}
|
|
986
1044
|
};
|
|
987
|
-
const controlledProps =
|
|
1045
|
+
const controlledProps = requestCtrl ? {
|
|
1046
|
+
value: requestText,
|
|
1047
|
+
onChange: (e) => {
|
|
1048
|
+
requestCtrl.setRequestField("text", e.currentTarget.value);
|
|
1049
|
+
onChange?.(e);
|
|
1050
|
+
}
|
|
1051
|
+
} : controller ? {
|
|
988
1052
|
value: controller.textInput.value,
|
|
989
1053
|
onChange: (e) => {
|
|
990
1054
|
controller.textInput.setInput(e.currentTarget.value);
|
|
@@ -992,7 +1056,7 @@ var PromptInputTextarea = ({ onChange, className, placeholder = "What would you
|
|
|
992
1056
|
}
|
|
993
1057
|
} : { onChange };
|
|
994
1058
|
return /* @__PURE__ */ jsx("textarea", {
|
|
995
|
-
className: cn("field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3
|
|
1059
|
+
className: cn("field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 pt-3 pb-1 text-sm text-sf-default outline-none placeholder:text-sf-inactive", className),
|
|
996
1060
|
name: "message",
|
|
997
1061
|
onKeyDown: handleTextareaKeyDown,
|
|
998
1062
|
onPaste: handlePaste,
|
|
@@ -1002,12 +1066,300 @@ var PromptInputTextarea = ({ onChange, className, placeholder = "What would you
|
|
|
1002
1066
|
...controlledProps
|
|
1003
1067
|
});
|
|
1004
1068
|
};
|
|
1069
|
+
var getTagMention = (tag) => tag.mention ?? `@${tag.label.replace(/\s+/g, "-").toLowerCase()}`;
|
|
1070
|
+
var findMentionAtCursor = (text, cursor) => {
|
|
1071
|
+
const beforeCursor = text.slice(0, cursor);
|
|
1072
|
+
const match = /(^|\s)@([\w-]*)$/.exec(beforeCursor);
|
|
1073
|
+
if (!match) return null;
|
|
1074
|
+
return {
|
|
1075
|
+
start: cursor - match[0].trimStart().length,
|
|
1076
|
+
end: cursor,
|
|
1077
|
+
query: match[2] ?? ""
|
|
1078
|
+
};
|
|
1079
|
+
};
|
|
1080
|
+
var renderInlineReferenceText = (text, tags) => {
|
|
1081
|
+
if (text.length === 0) return null;
|
|
1082
|
+
const mentions = tags.map((tag) => ({
|
|
1083
|
+
tag,
|
|
1084
|
+
mention: getTagMention(tag)
|
|
1085
|
+
})).filter((item) => item.mention.length > 0).sort((a, b) => b.mention.length - a.mention.length);
|
|
1086
|
+
const parts = [];
|
|
1087
|
+
let index = 0;
|
|
1088
|
+
while (index < text.length) {
|
|
1089
|
+
let match;
|
|
1090
|
+
for (const mention of mentions) if (text.startsWith(mention.mention, index)) {
|
|
1091
|
+
match = mention;
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
if (match) {
|
|
1095
|
+
parts.push(/* @__PURE__ */ jsx("span", {
|
|
1096
|
+
className: "rounded-md bg-sf-tint font-medium text-sf-strong ring-1 ring-sf-line",
|
|
1097
|
+
children: match.mention
|
|
1098
|
+
}, `${match.tag.id}-${index}`));
|
|
1099
|
+
index += match.mention.length;
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
parts.push(text[index]);
|
|
1103
|
+
index += 1;
|
|
1104
|
+
}
|
|
1105
|
+
return parts;
|
|
1106
|
+
};
|
|
1107
|
+
var getTextareaCursorPosition = (textarea, wrapper) => {
|
|
1108
|
+
if (!wrapper) return {
|
|
1109
|
+
left: 12,
|
|
1110
|
+
top: 0
|
|
1111
|
+
};
|
|
1112
|
+
const computed = window.getComputedStyle(textarea);
|
|
1113
|
+
const mirror = document.createElement("div");
|
|
1114
|
+
const marker = document.createElement("span");
|
|
1115
|
+
const wrapperRect = wrapper.getBoundingClientRect();
|
|
1116
|
+
for (const property of [
|
|
1117
|
+
"borderBottomWidth",
|
|
1118
|
+
"borderLeftWidth",
|
|
1119
|
+
"borderRightWidth",
|
|
1120
|
+
"borderTopWidth",
|
|
1121
|
+
"boxSizing",
|
|
1122
|
+
"fontFamily",
|
|
1123
|
+
"fontSize",
|
|
1124
|
+
"fontStyle",
|
|
1125
|
+
"fontWeight",
|
|
1126
|
+
"letterSpacing",
|
|
1127
|
+
"lineHeight",
|
|
1128
|
+
"paddingBottom",
|
|
1129
|
+
"paddingLeft",
|
|
1130
|
+
"paddingRight",
|
|
1131
|
+
"paddingTop",
|
|
1132
|
+
"textTransform",
|
|
1133
|
+
"width",
|
|
1134
|
+
"wordSpacing"
|
|
1135
|
+
]) mirror.style[property] = computed[property];
|
|
1136
|
+
mirror.style.position = "absolute";
|
|
1137
|
+
mirror.style.visibility = "hidden";
|
|
1138
|
+
mirror.style.whiteSpace = "pre-wrap";
|
|
1139
|
+
mirror.style.overflowWrap = "break-word";
|
|
1140
|
+
mirror.textContent = textarea.value.slice(0, textarea.selectionStart);
|
|
1141
|
+
marker.textContent = "";
|
|
1142
|
+
mirror.append(marker);
|
|
1143
|
+
document.body.append(mirror);
|
|
1144
|
+
const markerRect = marker.getBoundingClientRect();
|
|
1145
|
+
const top = markerRect.top - mirror.getBoundingClientRect().top + textarea.offsetTop - 6;
|
|
1146
|
+
const left = Math.min(Math.max(markerRect.left - mirror.getBoundingClientRect().left + textarea.offsetLeft, 12), Math.max(wrapperRect.width - 300, 12));
|
|
1147
|
+
mirror.remove();
|
|
1148
|
+
return {
|
|
1149
|
+
left,
|
|
1150
|
+
top
|
|
1151
|
+
};
|
|
1152
|
+
};
|
|
1153
|
+
var PromptInputReferencePicker = ({ activeIndex, onSelect, options, style }) => /* @__PURE__ */ jsxs("div", {
|
|
1154
|
+
className: "absolute z-50 w-72 -translate-y-full overflow-hidden rounded-lg bg-sf-elevated p-1 shadow-lg ring ring-sf-line",
|
|
1155
|
+
style,
|
|
1156
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
1157
|
+
className: "px-2 py-1.5 text-xs font-medium text-sf-subtle",
|
|
1158
|
+
children: "References"
|
|
1159
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
1160
|
+
className: "max-h-56 overflow-y-auto",
|
|
1161
|
+
children: options.map((tag, index) => /* @__PURE__ */ jsxs("button", {
|
|
1162
|
+
"aria-selected": index === activeIndex,
|
|
1163
|
+
className: cn("flex w-full items-start gap-2 rounded-md px-2 py-2 text-left text-sm text-sf-default hover:bg-sf-tint aria-selected:bg-sf-tint", index === activeIndex && "bg-sf-tint"),
|
|
1164
|
+
onMouseDown: (event) => {
|
|
1165
|
+
event.preventDefault();
|
|
1166
|
+
onSelect(tag);
|
|
1167
|
+
},
|
|
1168
|
+
role: "option",
|
|
1169
|
+
type: "button",
|
|
1170
|
+
children: [
|
|
1171
|
+
tag.icon && /* @__PURE__ */ jsx("span", {
|
|
1172
|
+
className: "mt-0.5 shrink-0",
|
|
1173
|
+
children: tag.icon
|
|
1174
|
+
}),
|
|
1175
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1176
|
+
className: "min-w-0 flex-1",
|
|
1177
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1178
|
+
className: "block truncate font-medium",
|
|
1179
|
+
children: tag.label
|
|
1180
|
+
}), tag.description && /* @__PURE__ */ jsx("span", {
|
|
1181
|
+
className: "block truncate text-xs text-sf-subtle",
|
|
1182
|
+
children: tag.description
|
|
1183
|
+
})]
|
|
1184
|
+
}),
|
|
1185
|
+
/* @__PURE__ */ jsx("span", {
|
|
1186
|
+
className: "shrink-0 text-xs text-sf-subtle",
|
|
1187
|
+
children: getTagMention(tag)
|
|
1188
|
+
})
|
|
1189
|
+
]
|
|
1190
|
+
}, tag.id))
|
|
1191
|
+
})]
|
|
1192
|
+
});
|
|
1193
|
+
var PromptInputEditor = ({ className, placeholder = "What would you like to know?", onReferenceSearch, references, onChange, onKeyDown, style, ...props }) => {
|
|
1194
|
+
const controller = useOptionalPromptInputController();
|
|
1195
|
+
const requestCtrl = useOptionalPromptInputRequestController();
|
|
1196
|
+
const textareaRef = useRef(null);
|
|
1197
|
+
const wrapperRef = useRef(null);
|
|
1198
|
+
const [mention, setMention] = useState(null);
|
|
1199
|
+
const [options, setOptions] = useState([]);
|
|
1200
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
1201
|
+
const [pickerPosition, setPickerPosition] = useState({
|
|
1202
|
+
left: 12,
|
|
1203
|
+
top: 0
|
|
1204
|
+
});
|
|
1205
|
+
const requestText = useStore(requestCtrl?.request ?? FALLBACK_REQUEST_STORE, (state) => typeof state.text === "string" ? state.text : "");
|
|
1206
|
+
const tags = useStore(requestCtrl?.request ?? FALLBACK_REQUEST_STORE, (state) => Array.isArray(state.tags) ? state.tags : []);
|
|
1207
|
+
const attachments = usePromptInputAttachments();
|
|
1208
|
+
const value = requestCtrl ? requestText : controller?.textInput.value ?? "";
|
|
1209
|
+
const updateMention = useCallback((textarea) => {
|
|
1210
|
+
const nextMention = findMentionAtCursor(textarea.value, textarea.selectionStart);
|
|
1211
|
+
if (nextMention) setPickerPosition(getTextareaCursorPosition(textarea, wrapperRef.current));
|
|
1212
|
+
setMention((current) => {
|
|
1213
|
+
if (!(current || nextMention)) return current;
|
|
1214
|
+
if (current && nextMention && current.start === nextMention.start && current.end === nextMention.end && current.query === nextMention.query) return current;
|
|
1215
|
+
return nextMention;
|
|
1216
|
+
});
|
|
1217
|
+
}, []);
|
|
1218
|
+
useEffect(() => {
|
|
1219
|
+
let cancelled = false;
|
|
1220
|
+
if (!mention) {
|
|
1221
|
+
setOptions([]);
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
const load = async () => {
|
|
1225
|
+
const result = onReferenceSearch ? await onReferenceSearch({
|
|
1226
|
+
trigger: "@",
|
|
1227
|
+
query: mention.query
|
|
1228
|
+
}) : (references ?? []).filter((tag) => tag.label.toLowerCase().includes(mention.query.toLowerCase()));
|
|
1229
|
+
if (!cancelled) {
|
|
1230
|
+
setOptions(result);
|
|
1231
|
+
setActiveIndex((index) => index < result.length ? index : 0);
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
load().catch(() => {
|
|
1235
|
+
if (!cancelled) setOptions([]);
|
|
1236
|
+
});
|
|
1237
|
+
return () => {
|
|
1238
|
+
cancelled = true;
|
|
1239
|
+
};
|
|
1240
|
+
}, [
|
|
1241
|
+
mention,
|
|
1242
|
+
onReferenceSearch,
|
|
1243
|
+
references
|
|
1244
|
+
]);
|
|
1245
|
+
const insertReference = useCallback((tag) => {
|
|
1246
|
+
if (!(requestCtrl && mention)) return;
|
|
1247
|
+
const mentionText = getTagMention(tag);
|
|
1248
|
+
requestCtrl.insertTag({
|
|
1249
|
+
...tag,
|
|
1250
|
+
mention: mentionText
|
|
1251
|
+
}, mention);
|
|
1252
|
+
setMention(null);
|
|
1253
|
+
setOptions([]);
|
|
1254
|
+
requestAnimationFrame(() => {
|
|
1255
|
+
const textarea = textareaRef.current;
|
|
1256
|
+
if (!textarea) return;
|
|
1257
|
+
const cursor = mention.start + mentionText.length + 1;
|
|
1258
|
+
textarea.focus();
|
|
1259
|
+
textarea.setSelectionRange(cursor, cursor);
|
|
1260
|
+
});
|
|
1261
|
+
}, [mention, requestCtrl]);
|
|
1262
|
+
const handlePaste = (event) => {
|
|
1263
|
+
const items = event.clipboardData?.items;
|
|
1264
|
+
if (!items) return;
|
|
1265
|
+
const pastedFiles = [];
|
|
1266
|
+
for (const item of items) if (item.kind === "file") {
|
|
1267
|
+
const file = item.getAsFile();
|
|
1268
|
+
if (file) pastedFiles.push(file);
|
|
1269
|
+
}
|
|
1270
|
+
if (pastedFiles.length > 0) {
|
|
1271
|
+
event.preventDefault();
|
|
1272
|
+
attachments.add(pastedFiles);
|
|
1273
|
+
}
|
|
1274
|
+
};
|
|
1275
|
+
const handleKeyDown = (event) => {
|
|
1276
|
+
if (mention && options.length > 0) {
|
|
1277
|
+
if (event.key === "ArrowDown") {
|
|
1278
|
+
event.preventDefault();
|
|
1279
|
+
setActiveIndex((index) => (index + 1) % options.length);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (event.key === "ArrowUp") {
|
|
1283
|
+
event.preventDefault();
|
|
1284
|
+
setActiveIndex((index) => (index - 1 + options.length) % options.length);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
if (event.key === "Enter" || event.key === "Tab") {
|
|
1288
|
+
event.preventDefault();
|
|
1289
|
+
insertReference(options[activeIndex]);
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
if (event.key === "Escape") {
|
|
1293
|
+
event.preventDefault();
|
|
1294
|
+
setMention(null);
|
|
1295
|
+
setOptions([]);
|
|
1296
|
+
return;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
handleTextareaKeyDown(event);
|
|
1300
|
+
onKeyDown?.(event);
|
|
1301
|
+
};
|
|
1302
|
+
const handleChange = (event) => {
|
|
1303
|
+
if (requestCtrl) requestCtrl.setRequestField("text", event.currentTarget.value);
|
|
1304
|
+
else if (controller) controller.textInput.setInput(event.currentTarget.value);
|
|
1305
|
+
updateMention(event.currentTarget);
|
|
1306
|
+
onChange?.(event);
|
|
1307
|
+
};
|
|
1308
|
+
const showPicker = mention && options.length > 0;
|
|
1309
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1310
|
+
className: "relative",
|
|
1311
|
+
ref: wrapperRef,
|
|
1312
|
+
children: [
|
|
1313
|
+
/* @__PURE__ */ jsx("div", {
|
|
1314
|
+
"aria-hidden": "true",
|
|
1315
|
+
className: cn("pointer-events-none field-sizing-content max-h-48 min-h-16 w-full overflow-hidden whitespace-pre-wrap break-words px-3 pt-3 pb-1 text-sm text-sf-default", value.length === 0 && "text-transparent"),
|
|
1316
|
+
children: renderInlineReferenceText(value, tags)
|
|
1317
|
+
}),
|
|
1318
|
+
/* @__PURE__ */ jsx("textarea", {
|
|
1319
|
+
className: cn("absolute inset-0 field-sizing-content max-h-48 min-h-16 w-full resize-none border-0 bg-transparent px-3 pt-3 pb-1 text-sm text-transparent outline-none placeholder:text-sf-inactive", className),
|
|
1320
|
+
name: "message",
|
|
1321
|
+
onChange: handleChange,
|
|
1322
|
+
onClick: (event) => updateMention(event.currentTarget),
|
|
1323
|
+
onKeyDown: handleKeyDown,
|
|
1324
|
+
onKeyUp: (event) => {
|
|
1325
|
+
if ([
|
|
1326
|
+
"ArrowDown",
|
|
1327
|
+
"ArrowUp",
|
|
1328
|
+
"Enter",
|
|
1329
|
+
"Tab",
|
|
1330
|
+
"Escape"
|
|
1331
|
+
].includes(event.key)) return;
|
|
1332
|
+
updateMention(event.currentTarget);
|
|
1333
|
+
},
|
|
1334
|
+
onPaste: handlePaste,
|
|
1335
|
+
placeholder,
|
|
1336
|
+
ref: textareaRef,
|
|
1337
|
+
rows: 1,
|
|
1338
|
+
style: {
|
|
1339
|
+
caretColor: "var(--text-color-sf-default)",
|
|
1340
|
+
...style
|
|
1341
|
+
},
|
|
1342
|
+
value,
|
|
1343
|
+
...props
|
|
1344
|
+
}),
|
|
1345
|
+
showPicker && /* @__PURE__ */ jsx(PromptInputReferencePicker, {
|
|
1346
|
+
activeIndex,
|
|
1347
|
+
onSelect: insertReference,
|
|
1348
|
+
options,
|
|
1349
|
+
style: {
|
|
1350
|
+
left: pickerPosition.left,
|
|
1351
|
+
top: pickerPosition.top
|
|
1352
|
+
}
|
|
1353
|
+
})
|
|
1354
|
+
]
|
|
1355
|
+
});
|
|
1356
|
+
};
|
|
1005
1357
|
var PromptInputToolbar = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
1006
|
-
className: cn("
|
|
1358
|
+
className: cn("grid grid-cols-[minmax(0,1fr)_auto] items-end gap-2 px-2.5 pt-1 pb-2", className),
|
|
1007
1359
|
...props
|
|
1008
1360
|
});
|
|
1009
1361
|
var PromptInputTools = ({ className, ...props }) => /* @__PURE__ */ jsx("div", {
|
|
1010
|
-
className: cn("flex min-w-0 items-center gap-1", className),
|
|
1362
|
+
className: cn("flex min-w-0 flex-wrap items-center gap-1", className),
|
|
1011
1363
|
...props
|
|
1012
1364
|
});
|
|
1013
1365
|
var PromptInputButton = ({ variant = "ghost", size = "sm", className, ...props }) => /* @__PURE__ */ jsx(Button, {
|
|
@@ -1032,7 +1384,7 @@ var PromptInputSubmit = ({ className, variant = "primary", size = "sm", status =
|
|
|
1032
1384
|
else Icon = /* @__PURE__ */ jsx(ArrowUpIcon, { className: "size-4" });
|
|
1033
1385
|
return /* @__PURE__ */ jsx(Button, {
|
|
1034
1386
|
"aria-label": "Submit",
|
|
1035
|
-
className: cn(className),
|
|
1387
|
+
className: cn("size-8 shrink-0 justify-center rounded-lg p-0", className),
|
|
1036
1388
|
size,
|
|
1037
1389
|
type: "submit",
|
|
1038
1390
|
variant,
|
|
@@ -1435,17 +1787,107 @@ function PromptInputAttachments({ className, children, ...props }) {
|
|
|
1435
1787
|
if (attachments.files.length === 0) return null;
|
|
1436
1788
|
return /* @__PURE__ */ jsx("div", {
|
|
1437
1789
|
"aria-live": "polite",
|
|
1438
|
-
className: cn("overflow-hidden px-2 transition-[height] duration-200 ease-out", className),
|
|
1790
|
+
className: cn("overflow-hidden px-2.5 transition-[height] duration-200 ease-out", className),
|
|
1439
1791
|
style: { height: attachments.files.length ? height : 0 },
|
|
1440
1792
|
...props,
|
|
1441
1793
|
children: /* @__PURE__ */ jsx("div", {
|
|
1442
|
-
className: "flex flex-wrap gap-2
|
|
1794
|
+
className: "flex flex-wrap gap-2 pt-2 pb-1",
|
|
1443
1795
|
ref: contentRef,
|
|
1444
1796
|
children: attachments.files.map((file) => /* @__PURE__ */ jsx(Fragment, { children: children(file) }, file.id))
|
|
1445
1797
|
})
|
|
1446
1798
|
});
|
|
1447
1799
|
}
|
|
1448
1800
|
/**
|
|
1801
|
+
* Single reference chip for prompt tags. Includes a remove button when mounted
|
|
1802
|
+
* inside a PromptInputRequestControllerProvider.
|
|
1803
|
+
*/
|
|
1804
|
+
function PromptInputTag({ data, className, ...props }) {
|
|
1805
|
+
const requestCtrl = useOptionalPromptInputRequestController();
|
|
1806
|
+
const handleRemove = useCallback(() => {
|
|
1807
|
+
requestCtrl?.removeTag(data.id);
|
|
1808
|
+
}, [requestCtrl, data.id]);
|
|
1809
|
+
const chip = /* @__PURE__ */ jsxs("div", {
|
|
1810
|
+
className: cn("group inline-flex h-8 items-center gap-2 rounded-md border border-sf-info/30 bg-sf-info-tint px-2 text-sm text-sf-default", className),
|
|
1811
|
+
...props,
|
|
1812
|
+
children: [
|
|
1813
|
+
data.icon && /* @__PURE__ */ jsx("div", {
|
|
1814
|
+
className: "flex size-4 shrink-0 items-center justify-center",
|
|
1815
|
+
children: data.icon
|
|
1816
|
+
}),
|
|
1817
|
+
/* @__PURE__ */ jsx("span", {
|
|
1818
|
+
className: "max-w-[180px] truncate font-medium",
|
|
1819
|
+
children: data.label
|
|
1820
|
+
}),
|
|
1821
|
+
requestCtrl && /* @__PURE__ */ jsx(Button, {
|
|
1822
|
+
"aria-label": "Remove tag",
|
|
1823
|
+
className: "ml-0.5 size-4 shrink-0 rounded-full p-0 opacity-0 transition-opacity group-hover:opacity-100",
|
|
1824
|
+
onClick: handleRemove,
|
|
1825
|
+
size: "sm",
|
|
1826
|
+
type: "button",
|
|
1827
|
+
variant: "ghost",
|
|
1828
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-3" })
|
|
1829
|
+
})
|
|
1830
|
+
]
|
|
1831
|
+
});
|
|
1832
|
+
if (!data.description) return chip;
|
|
1833
|
+
return /* @__PURE__ */ jsx(Tooltip, {
|
|
1834
|
+
content: data.description,
|
|
1835
|
+
children: chip
|
|
1836
|
+
});
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Renders all prompt tags using a render prop. Hidden when no tags.
|
|
1840
|
+
* Animates height as tags are added/removed.
|
|
1841
|
+
*/
|
|
1842
|
+
function PromptInputTags({ className, children, ...props }) {
|
|
1843
|
+
const tags = useStore(useOptionalPromptInputRequestController()?.request ?? FALLBACK_REQUEST_STORE, (state) => state.tags ?? []);
|
|
1844
|
+
const [height, setHeight] = useState(0);
|
|
1845
|
+
const contentRef = useRef(null);
|
|
1846
|
+
useLayoutEffect(() => {
|
|
1847
|
+
const el = contentRef.current;
|
|
1848
|
+
if (!el) return;
|
|
1849
|
+
const ro = new ResizeObserver(() => {
|
|
1850
|
+
setHeight(el.getBoundingClientRect().height);
|
|
1851
|
+
});
|
|
1852
|
+
ro.observe(el);
|
|
1853
|
+
setHeight(el.getBoundingClientRect().height);
|
|
1854
|
+
return () => ro.disconnect();
|
|
1855
|
+
}, []);
|
|
1856
|
+
useLayoutEffect(() => {
|
|
1857
|
+
const el = contentRef.current;
|
|
1858
|
+
if (el) setHeight(el.getBoundingClientRect().height);
|
|
1859
|
+
}, [tags.length]);
|
|
1860
|
+
if (tags.length === 0) return null;
|
|
1861
|
+
return /* @__PURE__ */ jsx("div", {
|
|
1862
|
+
"aria-live": "polite",
|
|
1863
|
+
className: cn("overflow-hidden px-2.5 transition-[height] duration-200 ease-out", className),
|
|
1864
|
+
style: { height: tags.length ? height : 0 },
|
|
1865
|
+
...props,
|
|
1866
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1867
|
+
className: "flex flex-wrap gap-2 pt-2 pb-1",
|
|
1868
|
+
ref: contentRef,
|
|
1869
|
+
children: tags.map((tag) => /* @__PURE__ */ jsx(Fragment, { children: children(tag) }, tag.id))
|
|
1870
|
+
})
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Toolbar trigger for opening a consumer-owned tag picker.
|
|
1875
|
+
*/
|
|
1876
|
+
function PromptInputAddTagButton({ children, onClick, onOpenPicker, ...props }) {
|
|
1877
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
1878
|
+
"aria-label": "Add tag",
|
|
1879
|
+
onClick: (event) => {
|
|
1880
|
+
onOpenPicker?.();
|
|
1881
|
+
onClick?.(event);
|
|
1882
|
+
},
|
|
1883
|
+
size: "sm",
|
|
1884
|
+
type: "button",
|
|
1885
|
+
variant: "ghost",
|
|
1886
|
+
...props,
|
|
1887
|
+
children: children ?? /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" })
|
|
1888
|
+
});
|
|
1889
|
+
}
|
|
1890
|
+
/**
|
|
1449
1891
|
* Voice input button. Uses the Web Speech API (Chrome/Edge). Hidden on unsupported browsers.
|
|
1450
1892
|
* Pulses while listening.
|
|
1451
1893
|
*/
|
|
@@ -1535,7 +1977,20 @@ var PromptInputAttachButton = ({ "aria-label": ariaLabel = "Attach file", classN
|
|
|
1535
1977
|
})
|
|
1536
1978
|
});
|
|
1537
1979
|
};
|
|
1980
|
+
PromptInput.BackLayer = PromptInputBackLayer;
|
|
1981
|
+
PromptInput.Textarea = PromptInputTextarea;
|
|
1982
|
+
PromptInput.Editor = PromptInputEditor;
|
|
1983
|
+
PromptInput.Toolbar = PromptInputToolbar;
|
|
1984
|
+
PromptInput.Tools = PromptInputTools;
|
|
1985
|
+
PromptInput.Submit = PromptInputSubmit;
|
|
1986
|
+
PromptInput.ModeSelector = PromptInputModeSelector;
|
|
1987
|
+
PromptInput.AddTagButton = PromptInputAddTagButton;
|
|
1988
|
+
PromptInput.Tags = PromptInputTags;
|
|
1989
|
+
PromptInput.Tag = PromptInputTag;
|
|
1990
|
+
PromptInput.AttachButton = PromptInputAttachButton;
|
|
1991
|
+
PromptInput.Attachments = PromptInputAttachments;
|
|
1992
|
+
PromptInput.Attachment = PromptInputAttachment;
|
|
1538
1993
|
//#endregion
|
|
1539
|
-
export {
|
|
1994
|
+
export { SF_AI_PROMPT_INPUT_DEFAULT_VARIANTS as A, useRequestField as B, PromptInputSpeechButton as C, PromptInputTextarea as D, PromptInputTags as E, PromptInputRequestControllerProvider as F, createPromptInputRequestController as I, useDisplayField as L, usePromptInputAttachments as M, usePromptInputController as N, PromptInputToolbar as O, useProviderAttachments as P, useOptionalPromptInputRequestController as R, PromptInputProvider as S, PromptInputTag as T, useSetRequestField as V, PromptInputModeCycle as _, PromptInputActionMenuItem as a, PromptInputModelCycle as b, PromptInputAttachButton as c, PromptInputBackLayer as d, PromptInputBody as f, PromptInputEditor as g, PromptInputCompactSelect as h, PromptInputActionMenuContent as i, SF_AI_PROMPT_INPUT_VARIANTS as j, PromptInputTools as k, PromptInputAttachment as l, PromptInputCompactCycle as m, PromptInputActionAddAttachments as n, PromptInputActionMenuTrigger as o, PromptInputButton as p, PromptInputActionMenu as r, PromptInputAddTagButton as s, PromptInput as t, PromptInputAttachments as u, PromptInputModeSelect as v, PromptInputSubmit as w, PromptInputModelSelect as x, PromptInputModeSelector as y, usePromptInputRequestController as z };
|
|
1540
1995
|
|
|
1541
|
-
//# sourceMappingURL=ai-prompt-input-
|
|
1996
|
+
//# sourceMappingURL=ai-prompt-input-CuluUzpf.js.map
|