composeai 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +140 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -1
- package/dist/index.d.ts +55 -1
- package/dist/index.js +140 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/composer.css +31 -0
package/dist/index.cjs
CHANGED
|
@@ -802,6 +802,7 @@ function useComposerContext() {
|
|
|
802
802
|
}
|
|
803
803
|
function EditorShell({
|
|
804
804
|
placeholder,
|
|
805
|
+
animated,
|
|
805
806
|
mode,
|
|
806
807
|
variant,
|
|
807
808
|
multiline,
|
|
@@ -820,7 +821,11 @@ function EditorShell({
|
|
|
820
821
|
const editor = slotProps("editor", editorClass, classNames, sx);
|
|
821
822
|
const editorResolved = resolveSx(sx?.editor);
|
|
822
823
|
const placeholderBase = mirrorEditorPadding(editorResolved);
|
|
823
|
-
const placeholderClass =
|
|
824
|
+
const placeholderClass = cn(
|
|
825
|
+
isCompact ? "composer-placeholder composer-placeholder--compact" : multiline ? "composer-placeholder composer-placeholder--multiline" : "composer-placeholder composer-placeholder--inline",
|
|
826
|
+
// Adds the blinking caret after the typewriter text.
|
|
827
|
+
animated && "composer-placeholder--animated"
|
|
828
|
+
);
|
|
824
829
|
const placeholderProps = slotProps(
|
|
825
830
|
"placeholder",
|
|
826
831
|
placeholderClass,
|
|
@@ -3388,14 +3393,18 @@ function SlashCommandPlugin({ config, onSubmit }) {
|
|
|
3388
3393
|
config.trigger ?? "/",
|
|
3389
3394
|
{ minLength: 0, maxLength: 32, allowWhitespace: false }
|
|
3390
3395
|
);
|
|
3396
|
+
const itemsRef = react.useRef(config.items);
|
|
3397
|
+
itemsRef.current = config.items;
|
|
3398
|
+
const isAsync = !isSyncItems(config.items);
|
|
3391
3399
|
react.useEffect(() => {
|
|
3392
|
-
|
|
3400
|
+
const items = itemsRef.current;
|
|
3401
|
+
if (isSyncItems(items)) {
|
|
3393
3402
|
setIsLoading(false);
|
|
3394
3403
|
return;
|
|
3395
3404
|
}
|
|
3396
3405
|
let cancelled = false;
|
|
3397
3406
|
setIsLoading(true);
|
|
3398
|
-
Promise.resolve(
|
|
3407
|
+
Promise.resolve(items(query)).then((res) => {
|
|
3399
3408
|
if (cancelled) return;
|
|
3400
3409
|
setAsyncItems(res);
|
|
3401
3410
|
setIsLoading(false);
|
|
@@ -3403,7 +3412,7 @@ function SlashCommandPlugin({ config, onSubmit }) {
|
|
|
3403
3412
|
return () => {
|
|
3404
3413
|
cancelled = true;
|
|
3405
3414
|
};
|
|
3406
|
-
}, [query,
|
|
3415
|
+
}, [query, isAsync]);
|
|
3407
3416
|
const allItems = react.useMemo(() => {
|
|
3408
3417
|
return isSyncItems(config.items) ? config.items : asyncItems ?? [];
|
|
3409
3418
|
}, [config.items, asyncItems]);
|
|
@@ -3594,14 +3603,18 @@ function MentionPlugin({ config }) {
|
|
|
3594
3603
|
maxLength: 32,
|
|
3595
3604
|
allowWhitespace: false
|
|
3596
3605
|
});
|
|
3606
|
+
const itemsRef = react.useRef(config.items);
|
|
3607
|
+
itemsRef.current = config.items;
|
|
3608
|
+
const isAsync = !isSyncItems2(config.items);
|
|
3597
3609
|
react.useEffect(() => {
|
|
3598
|
-
|
|
3610
|
+
const items = itemsRef.current;
|
|
3611
|
+
if (isSyncItems2(items)) {
|
|
3599
3612
|
setIsLoading(false);
|
|
3600
3613
|
return;
|
|
3601
3614
|
}
|
|
3602
3615
|
let cancelled = false;
|
|
3603
3616
|
setIsLoading(true);
|
|
3604
|
-
Promise.resolve(
|
|
3617
|
+
Promise.resolve(items(query)).then((res) => {
|
|
3605
3618
|
if (cancelled) return;
|
|
3606
3619
|
setAsyncItems(res);
|
|
3607
3620
|
setIsLoading(false);
|
|
@@ -3609,7 +3622,7 @@ function MentionPlugin({ config }) {
|
|
|
3609
3622
|
return () => {
|
|
3610
3623
|
cancelled = true;
|
|
3611
3624
|
};
|
|
3612
|
-
}, [query,
|
|
3625
|
+
}, [query, isAsync]);
|
|
3613
3626
|
const allItems = react.useMemo(() => {
|
|
3614
3627
|
return isSyncItems2(config.items) ? config.items : asyncItems ?? [];
|
|
3615
3628
|
}, [config.items, asyncItems]);
|
|
@@ -4929,6 +4942,107 @@ function useComposerHandle(ref, onSubmit) {
|
|
|
4929
4942
|
};
|
|
4930
4943
|
}, [editor, ref, onSubmit, addFiles]);
|
|
4931
4944
|
}
|
|
4945
|
+
var DEFAULT_TIMING = {
|
|
4946
|
+
typeSpeed: 55,
|
|
4947
|
+
deleteSpeed: 28,
|
|
4948
|
+
holdDuration: 1800,
|
|
4949
|
+
pauseDuration: 450
|
|
4950
|
+
};
|
|
4951
|
+
function useAnimatedPlaceholder(phrases, enabled, options) {
|
|
4952
|
+
const [state, setState] = react.useState({
|
|
4953
|
+
text: "",
|
|
4954
|
+
active: true
|
|
4955
|
+
});
|
|
4956
|
+
const optsRef = react.useRef({});
|
|
4957
|
+
optsRef.current = options ?? {};
|
|
4958
|
+
const active = enabled && Array.isArray(phrases) && phrases.length > 0;
|
|
4959
|
+
const key = active ? phrases.join("\u241F") : "";
|
|
4960
|
+
const loop = !!options?.loop;
|
|
4961
|
+
const phrasesRef = react.useRef(phrases);
|
|
4962
|
+
phrasesRef.current = phrases;
|
|
4963
|
+
react.useEffect(() => {
|
|
4964
|
+
if (!active) {
|
|
4965
|
+
setState({ text: "", active: true });
|
|
4966
|
+
return;
|
|
4967
|
+
}
|
|
4968
|
+
const list = phrasesRef.current;
|
|
4969
|
+
const lastIdx = list.length - 1;
|
|
4970
|
+
const reduceMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
4971
|
+
if (reduceMotion) {
|
|
4972
|
+
setState({ text: list[0], active: false });
|
|
4973
|
+
return;
|
|
4974
|
+
}
|
|
4975
|
+
let timer;
|
|
4976
|
+
let phraseIdx = 0;
|
|
4977
|
+
let charIdx = 0;
|
|
4978
|
+
let phase = "typing";
|
|
4979
|
+
const tick = () => {
|
|
4980
|
+
const timing = { ...DEFAULT_TIMING, ...optsRef.current };
|
|
4981
|
+
const { typeSpeed, deleteSpeed, holdDuration, pauseDuration } = timing;
|
|
4982
|
+
const settleTo = optsRef.current.settleTo;
|
|
4983
|
+
const current = list[phraseIdx];
|
|
4984
|
+
const isLast = phraseIdx === lastIdx;
|
|
4985
|
+
switch (phase) {
|
|
4986
|
+
case "typing": {
|
|
4987
|
+
charIdx += 1;
|
|
4988
|
+
setState({ text: current.slice(0, charIdx), active: true });
|
|
4989
|
+
if (charIdx >= current.length) {
|
|
4990
|
+
phase = "holding";
|
|
4991
|
+
timer = setTimeout(tick, holdDuration);
|
|
4992
|
+
} else {
|
|
4993
|
+
timer = setTimeout(tick, typeSpeed);
|
|
4994
|
+
}
|
|
4995
|
+
break;
|
|
4996
|
+
}
|
|
4997
|
+
case "holding": {
|
|
4998
|
+
if (!loop && isLast) {
|
|
4999
|
+
if (settleTo !== void 0) {
|
|
5000
|
+
phase = "settling";
|
|
5001
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
5002
|
+
} else {
|
|
5003
|
+
setState({ text: current, active: false });
|
|
5004
|
+
}
|
|
5005
|
+
} else {
|
|
5006
|
+
phase = "deleting";
|
|
5007
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
5008
|
+
}
|
|
5009
|
+
break;
|
|
5010
|
+
}
|
|
5011
|
+
case "deleting": {
|
|
5012
|
+
charIdx -= 1;
|
|
5013
|
+
setState({ text: current.slice(0, Math.max(0, charIdx)), active: true });
|
|
5014
|
+
if (charIdx <= 0) {
|
|
5015
|
+
phase = "pausing";
|
|
5016
|
+
timer = setTimeout(tick, pauseDuration);
|
|
5017
|
+
} else {
|
|
5018
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
5019
|
+
}
|
|
5020
|
+
break;
|
|
5021
|
+
}
|
|
5022
|
+
case "pausing": {
|
|
5023
|
+
phraseIdx = phraseIdx >= lastIdx ? 0 : phraseIdx + 1;
|
|
5024
|
+
charIdx = 0;
|
|
5025
|
+
phase = "typing";
|
|
5026
|
+
timer = setTimeout(tick, typeSpeed);
|
|
5027
|
+
break;
|
|
5028
|
+
}
|
|
5029
|
+
case "settling": {
|
|
5030
|
+
charIdx -= 1;
|
|
5031
|
+
if (charIdx <= 0) {
|
|
5032
|
+
setState({ text: settleTo ?? "", active: false });
|
|
5033
|
+
} else {
|
|
5034
|
+
setState({ text: current.slice(0, charIdx), active: true });
|
|
5035
|
+
timer = setTimeout(tick, deleteSpeed);
|
|
5036
|
+
}
|
|
5037
|
+
break;
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
};
|
|
5041
|
+
timer = setTimeout(tick, DEFAULT_TIMING.typeSpeed);
|
|
5042
|
+
return () => clearTimeout(timer);
|
|
5043
|
+
}, [active, key, loop]);
|
|
5044
|
+
return active ? state : null;
|
|
5045
|
+
}
|
|
4932
5046
|
|
|
4933
5047
|
// src/internal/shortcut.ts
|
|
4934
5048
|
var MODIFIERS = /* @__PURE__ */ new Set([
|
|
@@ -5006,6 +5120,7 @@ function matchesShortcut(parsed, event) {
|
|
|
5006
5120
|
var Composer = react.forwardRef(function Composer2(props, ref) {
|
|
5007
5121
|
const {
|
|
5008
5122
|
placeholder = "Send a message\u2026",
|
|
5123
|
+
animatedPlaceholder,
|
|
5009
5124
|
onSend,
|
|
5010
5125
|
onStop,
|
|
5011
5126
|
isStreaming,
|
|
@@ -5035,6 +5150,7 @@ var Composer = react.forwardRef(function Composer2(props, ref) {
|
|
|
5035
5150
|
attachmentOptions,
|
|
5036
5151
|
dir
|
|
5037
5152
|
} = props;
|
|
5153
|
+
const hasPlaceholder = props.placeholder != null;
|
|
5038
5154
|
const tokenStyle = react.useMemo(() => {
|
|
5039
5155
|
const derived = color ? deriveColorTokens(color) : null;
|
|
5040
5156
|
if (!derived && !tokens) return void 0;
|
|
@@ -5078,6 +5194,8 @@ var Composer = react.forwardRef(function Composer2(props, ref) {
|
|
|
5078
5194
|
ComposerCard,
|
|
5079
5195
|
{
|
|
5080
5196
|
placeholder,
|
|
5197
|
+
animatedPlaceholder,
|
|
5198
|
+
hasPlaceholder,
|
|
5081
5199
|
initialValue,
|
|
5082
5200
|
handleRef: ref,
|
|
5083
5201
|
onSend,
|
|
@@ -5115,6 +5233,8 @@ var RICH_NODES = [
|
|
|
5115
5233
|
var PLAIN_NODES = [MentionNode];
|
|
5116
5234
|
function ComposerCard({
|
|
5117
5235
|
placeholder,
|
|
5236
|
+
animatedPlaceholder,
|
|
5237
|
+
hasPlaceholder,
|
|
5118
5238
|
initialValue,
|
|
5119
5239
|
handleRef,
|
|
5120
5240
|
onSend,
|
|
@@ -5176,6 +5296,8 @@ function ComposerCard({
|
|
|
5176
5296
|
ComposerInner,
|
|
5177
5297
|
{
|
|
5178
5298
|
placeholder,
|
|
5299
|
+
animatedPlaceholder,
|
|
5300
|
+
hasPlaceholder,
|
|
5179
5301
|
mode,
|
|
5180
5302
|
variant,
|
|
5181
5303
|
multiline,
|
|
@@ -5196,6 +5318,8 @@ function ComposerCard({
|
|
|
5196
5318
|
}
|
|
5197
5319
|
function ComposerInner({
|
|
5198
5320
|
placeholder,
|
|
5321
|
+
animatedPlaceholder,
|
|
5322
|
+
hasPlaceholder,
|
|
5199
5323
|
mode,
|
|
5200
5324
|
variant,
|
|
5201
5325
|
multiline,
|
|
@@ -5326,6 +5450,13 @@ function ComposerInner({
|
|
|
5326
5450
|
});
|
|
5327
5451
|
}, [editor, registerRunPrompt, submit]);
|
|
5328
5452
|
const isCompact = variant === "compact";
|
|
5453
|
+
const animatedPhrases = Array.isArray(animatedPlaceholder) ? animatedPlaceholder : animatedPlaceholder?.phrases;
|
|
5454
|
+
const animatedLoop = Array.isArray(animatedPlaceholder) ? false : !!animatedPlaceholder?.loop;
|
|
5455
|
+
const animatedFrame = useAnimatedPlaceholder(animatedPhrases, !hasText, {
|
|
5456
|
+
loop: animatedLoop,
|
|
5457
|
+
settleTo: hasPlaceholder ? placeholder : void 0
|
|
5458
|
+
});
|
|
5459
|
+
const effectivePlaceholder = animatedFrame?.text ?? placeholder;
|
|
5329
5460
|
const mermaidActive = multiline && mode === "markdown" && !!features.mermaid;
|
|
5330
5461
|
const toolbarSlot = /* @__PURE__ */ jsxRuntime.jsx(Toolbar, { extras: toolbarExtras, variant, submit });
|
|
5331
5462
|
const sendButton = /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5349,7 +5480,8 @@ function ComposerInner({
|
|
|
5349
5480
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5350
5481
|
EditorShell,
|
|
5351
5482
|
{
|
|
5352
|
-
placeholder,
|
|
5483
|
+
placeholder: effectivePlaceholder,
|
|
5484
|
+
animated: animatedFrame?.active ?? false,
|
|
5353
5485
|
mode,
|
|
5354
5486
|
variant,
|
|
5355
5487
|
multiline,
|