@particle-academy/react-fancy 3.0.1 → 3.2.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/README.md +11 -0
- package/dist/index.cjs +1283 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +430 -2
- package/dist/index.d.ts +430 -2
- package/dist/index.js +1274 -3
- package/dist/index.js.map +1 -1
- package/docs/ChatDrawer.md +75 -0
- package/docs/InputTag.md +155 -0
- package/docs/MagicWand.md +65 -0
- package/docs/MoodMeter.md +63 -0
- package/docs/PromptInput.md +70 -0
- package/docs/ReasonTag.md +63 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9223,11 +9223,11 @@ function computeTicks(min, max, count = 5) {
|
|
|
9223
9223
|
}
|
|
9224
9224
|
return ticks;
|
|
9225
9225
|
}
|
|
9226
|
-
function niceNum(range2,
|
|
9226
|
+
function niceNum(range2, round2) {
|
|
9227
9227
|
const exponent = Math.floor(Math.log10(range2));
|
|
9228
9228
|
const fraction = range2 / Math.pow(10, exponent);
|
|
9229
9229
|
let niceFraction;
|
|
9230
|
-
if (
|
|
9230
|
+
if (round2) {
|
|
9231
9231
|
if (fraction < 1.5) niceFraction = 1;
|
|
9232
9232
|
else if (fraction < 3) niceFraction = 2;
|
|
9233
9233
|
else if (fraction < 7) niceFraction = 5;
|
|
@@ -11840,6 +11840,1277 @@ TreeNavRoot.displayName = "TreeNav";
|
|
|
11840
11840
|
var TreeNav = Object.assign(TreeNavRoot, {
|
|
11841
11841
|
Node: TreeNode
|
|
11842
11842
|
});
|
|
11843
|
+
var CONFIDENCE_TIERS = [
|
|
11844
|
+
{ min: 0.85, color: "#10b981", label: "high" },
|
|
11845
|
+
{ min: 0.6, color: "#f59e0b", label: "medium" },
|
|
11846
|
+
{ min: 0, color: "#ef4444", label: "low" }
|
|
11847
|
+
];
|
|
11848
|
+
function tier(c) {
|
|
11849
|
+
return CONFIDENCE_TIERS.find((t) => c >= t.min) ?? CONFIDENCE_TIERS[2];
|
|
11850
|
+
}
|
|
11851
|
+
function ReasonTag({
|
|
11852
|
+
value,
|
|
11853
|
+
reason,
|
|
11854
|
+
confidence = 1,
|
|
11855
|
+
sources = [],
|
|
11856
|
+
by,
|
|
11857
|
+
theme = "dot",
|
|
11858
|
+
pinned = false,
|
|
11859
|
+
onFollowUp,
|
|
11860
|
+
className
|
|
11861
|
+
}) {
|
|
11862
|
+
const t = tier(confidence);
|
|
11863
|
+
const trigger = theme === "chip" ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11864
|
+
"span",
|
|
11865
|
+
{
|
|
11866
|
+
className: `inline-flex cursor-help items-center gap-1 rounded-full px-2 py-0.5 text-[12px] font-medium ${className ?? ""}`,
|
|
11867
|
+
style: { backgroundColor: t.color + "22", color: t.color },
|
|
11868
|
+
children: [
|
|
11869
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11870
|
+
"span",
|
|
11871
|
+
{
|
|
11872
|
+
className: "h-1.5 w-1.5 rounded-full",
|
|
11873
|
+
style: { backgroundColor: t.color }
|
|
11874
|
+
}
|
|
11875
|
+
),
|
|
11876
|
+
value,
|
|
11877
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] opacity-70", children: "?" })
|
|
11878
|
+
]
|
|
11879
|
+
}
|
|
11880
|
+
) : theme === "underline" ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
11881
|
+
"span",
|
|
11882
|
+
{
|
|
11883
|
+
className: `cursor-help underline decoration-dotted underline-offset-2 ${className ?? ""}`,
|
|
11884
|
+
style: { textDecorationColor: t.color },
|
|
11885
|
+
children: [
|
|
11886
|
+
value,
|
|
11887
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11888
|
+
"span",
|
|
11889
|
+
{
|
|
11890
|
+
className: "ml-0.5 font-mono text-[10px]",
|
|
11891
|
+
style: { color: t.color },
|
|
11892
|
+
children: "?"
|
|
11893
|
+
}
|
|
11894
|
+
)
|
|
11895
|
+
]
|
|
11896
|
+
}
|
|
11897
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `inline-flex cursor-help items-baseline gap-1 ${className ?? ""}`, children: [
|
|
11898
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: value }),
|
|
11899
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11900
|
+
"span",
|
|
11901
|
+
{
|
|
11902
|
+
className: "inline-block h-1.5 w-1.5 rounded-full align-middle",
|
|
11903
|
+
style: { backgroundColor: t.color },
|
|
11904
|
+
title: "reason available"
|
|
11905
|
+
}
|
|
11906
|
+
)
|
|
11907
|
+
] });
|
|
11908
|
+
if (pinned) {
|
|
11909
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex flex-col items-start gap-0.5 align-top", children: [
|
|
11910
|
+
trigger,
|
|
11911
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
11912
|
+
"span",
|
|
11913
|
+
{
|
|
11914
|
+
className: "block max-w-[280px] rounded border-l-2 pl-2 text-[11px] leading-snug text-zinc-600 dark:text-zinc-300",
|
|
11915
|
+
style: { borderColor: t.color },
|
|
11916
|
+
children: reason
|
|
11917
|
+
}
|
|
11918
|
+
)
|
|
11919
|
+
] });
|
|
11920
|
+
}
|
|
11921
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { hover: true, children: [
|
|
11922
|
+
/* @__PURE__ */ jsxRuntime.jsx(Popover.Trigger, { children: trigger }),
|
|
11923
|
+
/* @__PURE__ */ jsxRuntime.jsx(Popover.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-72 space-y-2 text-sm", children: [
|
|
11924
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
|
|
11925
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
11926
|
+
"span",
|
|
11927
|
+
{
|
|
11928
|
+
className: "text-[10px] font-semibold uppercase tracking-wider",
|
|
11929
|
+
style: { color: t.color },
|
|
11930
|
+
children: [
|
|
11931
|
+
t.label,
|
|
11932
|
+
" confidence \xB7 ",
|
|
11933
|
+
(confidence * 100).toFixed(0),
|
|
11934
|
+
"%"
|
|
11935
|
+
]
|
|
11936
|
+
}
|
|
11937
|
+
),
|
|
11938
|
+
by && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto font-mono text-[10px] text-zinc-400", children: [
|
|
11939
|
+
"@",
|
|
11940
|
+
by
|
|
11941
|
+
] })
|
|
11942
|
+
] }),
|
|
11943
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[13px] leading-snug text-zinc-700 dark:text-zinc-200", children: reason }),
|
|
11944
|
+
sources.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
11945
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] uppercase tracking-wider text-zinc-400", children: "sources" }),
|
|
11946
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "mt-0.5 space-y-0.5 text-[12px]", children: sources.map((s, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: s.href ? /* @__PURE__ */ jsxRuntime.jsx("a", { className: "text-violet-600 hover:underline", href: s.href, children: s.label }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-600 dark:text-zinc-300", children: s.label }) }, i)) })
|
|
11947
|
+
] }),
|
|
11948
|
+
onFollowUp && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end pt-1", children: /* @__PURE__ */ jsxRuntime.jsx(Action, { size: "sm", variant: "ghost", onClick: onFollowUp, children: "ask follow-up" }) })
|
|
11949
|
+
] }) })
|
|
11950
|
+
] });
|
|
11951
|
+
}
|
|
11952
|
+
function MoodMeter({
|
|
11953
|
+
min,
|
|
11954
|
+
max,
|
|
11955
|
+
step,
|
|
11956
|
+
value,
|
|
11957
|
+
confidence,
|
|
11958
|
+
onChange,
|
|
11959
|
+
posted,
|
|
11960
|
+
width = 320,
|
|
11961
|
+
height = 220,
|
|
11962
|
+
showGrid = true,
|
|
11963
|
+
color = "#0ea5e9",
|
|
11964
|
+
postedColor = "#a855f7",
|
|
11965
|
+
prefix = "",
|
|
11966
|
+
suffix = "",
|
|
11967
|
+
formatValue,
|
|
11968
|
+
className
|
|
11969
|
+
}) {
|
|
11970
|
+
const ref = react.useRef(null);
|
|
11971
|
+
const [dragging, setDragging] = react.useState(false);
|
|
11972
|
+
const snapStep = step ?? (max - min) / 100;
|
|
11973
|
+
const fmt = react.useCallback(
|
|
11974
|
+
(v) => {
|
|
11975
|
+
if (formatValue) return formatValue(v);
|
|
11976
|
+
const num = snapStep < 1 ? v.toFixed(2) : Math.round(v).toString();
|
|
11977
|
+
return `${prefix}${num}${suffix}`;
|
|
11978
|
+
},
|
|
11979
|
+
[formatValue, snapStep, prefix, suffix]
|
|
11980
|
+
);
|
|
11981
|
+
const set = react.useCallback(
|
|
11982
|
+
(clientX, clientY) => {
|
|
11983
|
+
const el = ref.current;
|
|
11984
|
+
if (!el) return;
|
|
11985
|
+
const rect = el.getBoundingClientRect();
|
|
11986
|
+
const x = clamp((clientX - rect.left) / rect.width, 0, 1);
|
|
11987
|
+
const y = clamp((clientY - rect.top) / rect.height, 0, 1);
|
|
11988
|
+
const raw = min + x * (max - min);
|
|
11989
|
+
const snapped = round(raw, snapStep, min);
|
|
11990
|
+
const c = clamp(1 - y, 0, 1);
|
|
11991
|
+
onChange(snapped, Math.round(c * 100) / 100);
|
|
11992
|
+
},
|
|
11993
|
+
[min, max, snapStep, onChange]
|
|
11994
|
+
);
|
|
11995
|
+
const onPointerDown = (e) => {
|
|
11996
|
+
e.target.setPointerCapture?.(e.pointerId);
|
|
11997
|
+
setDragging(true);
|
|
11998
|
+
set(e.clientX, e.clientY);
|
|
11999
|
+
};
|
|
12000
|
+
const onPointerMove = (e) => {
|
|
12001
|
+
if (!dragging) return;
|
|
12002
|
+
set(e.clientX, e.clientY);
|
|
12003
|
+
};
|
|
12004
|
+
const onPointerUp = () => setDragging(false);
|
|
12005
|
+
const xPct = (value - min) / (max - min) * 100;
|
|
12006
|
+
const yPct = (1 - confidence) * 100;
|
|
12007
|
+
const haloR = 18 + (1 - confidence) * 70;
|
|
12008
|
+
const pxPct = posted ? (posted.value - min) / (max - min) * 100 : 0;
|
|
12009
|
+
const pyPct = posted ? (1 - posted.confidence) * 100 : 0;
|
|
12010
|
+
const pHaloR = posted ? 16 + (1 - posted.confidence) * 60 : 0;
|
|
12011
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12012
|
+
"div",
|
|
12013
|
+
{
|
|
12014
|
+
ref,
|
|
12015
|
+
onPointerDown,
|
|
12016
|
+
onPointerMove,
|
|
12017
|
+
onPointerUp,
|
|
12018
|
+
onPointerCancel: onPointerUp,
|
|
12019
|
+
className: `relative cursor-crosshair touch-none select-none overflow-hidden rounded-md border border-zinc-200 bg-zinc-50 dark:border-zinc-800 dark:bg-zinc-950 ${className ?? ""}`,
|
|
12020
|
+
style: { width, height },
|
|
12021
|
+
children: [
|
|
12022
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "pointer-events-none absolute inset-0 h-full w-full", "aria-hidden": true, children: [
|
|
12023
|
+
[0.25, 0.5, 0.75].map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
12024
|
+
"line",
|
|
12025
|
+
{
|
|
12026
|
+
x1: `${p * 100}%`,
|
|
12027
|
+
x2: `${p * 100}%`,
|
|
12028
|
+
y1: "0",
|
|
12029
|
+
y2: "100%",
|
|
12030
|
+
stroke: "#a1a1aa",
|
|
12031
|
+
strokeOpacity: 0.18,
|
|
12032
|
+
strokeDasharray: "3 4"
|
|
12033
|
+
},
|
|
12034
|
+
`v${i}`
|
|
12035
|
+
)),
|
|
12036
|
+
[0.25, 0.5, 0.75].map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
12037
|
+
"line",
|
|
12038
|
+
{
|
|
12039
|
+
x1: "0",
|
|
12040
|
+
x2: "100%",
|
|
12041
|
+
y1: `${p * 100}%`,
|
|
12042
|
+
y2: `${p * 100}%`,
|
|
12043
|
+
stroke: "#a1a1aa",
|
|
12044
|
+
strokeOpacity: 0.18,
|
|
12045
|
+
strokeDasharray: "3 4"
|
|
12046
|
+
},
|
|
12047
|
+
`h${i}`
|
|
12048
|
+
))
|
|
12049
|
+
] }),
|
|
12050
|
+
showGrid && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
12051
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute left-1.5 top-1.5 text-[10px] uppercase tracking-wider text-zinc-400", children: "\u2191 sure" }),
|
|
12052
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute bottom-1.5 left-1.5 text-[10px] uppercase tracking-wider text-zinc-400", children: "\u2193 unsure" }),
|
|
12053
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-none absolute right-1.5 top-1.5 text-[10px] uppercase tracking-wider text-zinc-400", children: [
|
|
12054
|
+
fmt(max),
|
|
12055
|
+
" \u2192"
|
|
12056
|
+
] }),
|
|
12057
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-none absolute bottom-1.5 right-1.5 text-[10px] uppercase tracking-wider text-zinc-400", children: [
|
|
12058
|
+
"\u2190 ",
|
|
12059
|
+
fmt(min)
|
|
12060
|
+
] })
|
|
12061
|
+
] }),
|
|
12062
|
+
posted && /* @__PURE__ */ jsxRuntime.jsx(
|
|
12063
|
+
Handle,
|
|
12064
|
+
{
|
|
12065
|
+
xPct: pxPct,
|
|
12066
|
+
yPct: pyPct,
|
|
12067
|
+
haloR: pHaloR,
|
|
12068
|
+
color: postedColor,
|
|
12069
|
+
dashed: true,
|
|
12070
|
+
label: "agent"
|
|
12071
|
+
}
|
|
12072
|
+
),
|
|
12073
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12074
|
+
Handle,
|
|
12075
|
+
{
|
|
12076
|
+
xPct,
|
|
12077
|
+
yPct,
|
|
12078
|
+
haloR,
|
|
12079
|
+
color,
|
|
12080
|
+
label: fmt(value)
|
|
12081
|
+
}
|
|
12082
|
+
)
|
|
12083
|
+
]
|
|
12084
|
+
}
|
|
12085
|
+
);
|
|
12086
|
+
}
|
|
12087
|
+
function Handle({
|
|
12088
|
+
xPct,
|
|
12089
|
+
yPct,
|
|
12090
|
+
haloR,
|
|
12091
|
+
color,
|
|
12092
|
+
dashed = false,
|
|
12093
|
+
label
|
|
12094
|
+
}) {
|
|
12095
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
12096
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12097
|
+
"div",
|
|
12098
|
+
{
|
|
12099
|
+
className: "pointer-events-none absolute -translate-x-1/2 -translate-y-1/2 rounded-full",
|
|
12100
|
+
style: {
|
|
12101
|
+
left: `${xPct}%`,
|
|
12102
|
+
top: `${yPct}%`,
|
|
12103
|
+
width: haloR * 2,
|
|
12104
|
+
height: haloR * 2,
|
|
12105
|
+
background: color + (dashed ? "11" : "22"),
|
|
12106
|
+
border: dashed ? `1px dashed ${color}` : "none"
|
|
12107
|
+
}
|
|
12108
|
+
}
|
|
12109
|
+
),
|
|
12110
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12111
|
+
"div",
|
|
12112
|
+
{
|
|
12113
|
+
className: "pointer-events-none absolute -translate-x-1/2 -translate-y-1/2 rounded-full ring-2 ring-white dark:ring-zinc-900",
|
|
12114
|
+
style: {
|
|
12115
|
+
left: `${xPct}%`,
|
|
12116
|
+
top: `${yPct}%`,
|
|
12117
|
+
width: 14,
|
|
12118
|
+
height: 14,
|
|
12119
|
+
background: color,
|
|
12120
|
+
opacity: dashed ? 0.6 : 1
|
|
12121
|
+
}
|
|
12122
|
+
}
|
|
12123
|
+
),
|
|
12124
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12125
|
+
"div",
|
|
12126
|
+
{
|
|
12127
|
+
className: "pointer-events-none absolute -translate-x-1/2 rounded px-1.5 py-0.5 font-mono text-[10px]",
|
|
12128
|
+
style: {
|
|
12129
|
+
left: `${xPct}%`,
|
|
12130
|
+
top: `calc(${yPct}% + 14px)`,
|
|
12131
|
+
color
|
|
12132
|
+
},
|
|
12133
|
+
children: label
|
|
12134
|
+
}
|
|
12135
|
+
)
|
|
12136
|
+
] });
|
|
12137
|
+
}
|
|
12138
|
+
function clamp(n, lo, hi) {
|
|
12139
|
+
return Math.max(lo, Math.min(hi, n));
|
|
12140
|
+
}
|
|
12141
|
+
function round(n, step, min) {
|
|
12142
|
+
if (step >= 1) return Math.round(n);
|
|
12143
|
+
return Math.round((n - min) / step) * step + min;
|
|
12144
|
+
}
|
|
12145
|
+
var DEFAULT_MENTION_COLOR = {
|
|
12146
|
+
agent: "#a855f7",
|
|
12147
|
+
file: "#10b981",
|
|
12148
|
+
person: "#3b82f6"
|
|
12149
|
+
};
|
|
12150
|
+
function PromptInput({
|
|
12151
|
+
budgetTokens,
|
|
12152
|
+
commands = [],
|
|
12153
|
+
mentions = [],
|
|
12154
|
+
showHint = true,
|
|
12155
|
+
onSubmit,
|
|
12156
|
+
placeholder = "Ask anything. Type / for commands, @ for mentions. \u2318/Ctrl+Enter to send.",
|
|
12157
|
+
charsPerToken = 4,
|
|
12158
|
+
mentionColor,
|
|
12159
|
+
maxHeight = 280,
|
|
12160
|
+
aboveInput
|
|
12161
|
+
}) {
|
|
12162
|
+
const [text, setText] = react.useState("");
|
|
12163
|
+
const [attachments, setAttachments] = react.useState([]);
|
|
12164
|
+
const [picker, setPicker] = react.useState(null);
|
|
12165
|
+
const taRef = react.useRef(null);
|
|
12166
|
+
const [dragOver, setDragOver] = react.useState(false);
|
|
12167
|
+
const colors = mentionColor ?? DEFAULT_MENTION_COLOR;
|
|
12168
|
+
const tokens = react.useMemo(
|
|
12169
|
+
() => Math.ceil(text.length / Math.max(1, charsPerToken)),
|
|
12170
|
+
[text, charsPerToken]
|
|
12171
|
+
);
|
|
12172
|
+
const ratio = Math.min(1, tokens / budgetTokens);
|
|
12173
|
+
const meterColor = ratio < 0.6 ? "#10b981" : ratio < 0.85 ? "#f59e0b" : "#ef4444";
|
|
12174
|
+
const filteredCmds = react.useMemo(
|
|
12175
|
+
() => picker?.kind === "cmd" ? commands.filter(
|
|
12176
|
+
(c) => c.name.slice(1).toLowerCase().startsWith(picker.query.toLowerCase())
|
|
12177
|
+
) : [],
|
|
12178
|
+
[picker, commands]
|
|
12179
|
+
);
|
|
12180
|
+
const filteredMentions = react.useMemo(
|
|
12181
|
+
() => picker?.kind === "mention" ? mentions.filter(
|
|
12182
|
+
(m) => m.name.toLowerCase().includes(picker.query.toLowerCase())
|
|
12183
|
+
) : [],
|
|
12184
|
+
[picker, mentions]
|
|
12185
|
+
);
|
|
12186
|
+
const items = picker?.kind === "cmd" ? filteredCmds : picker?.kind === "mention" ? filteredMentions : [];
|
|
12187
|
+
react.useEffect(() => {
|
|
12188
|
+
const ta = taRef.current;
|
|
12189
|
+
if (!ta) return;
|
|
12190
|
+
ta.style.height = "auto";
|
|
12191
|
+
ta.style.height = Math.min(maxHeight, ta.scrollHeight) + "px";
|
|
12192
|
+
}, [text, maxHeight]);
|
|
12193
|
+
const updateText = (next, caret) => {
|
|
12194
|
+
setText(next);
|
|
12195
|
+
let triggerIdx = -1;
|
|
12196
|
+
let triggerKind = null;
|
|
12197
|
+
for (let i = caret - 1; i >= 0; i--) {
|
|
12198
|
+
const ch = next[i];
|
|
12199
|
+
if (ch === "@") {
|
|
12200
|
+
triggerKind = "mention";
|
|
12201
|
+
triggerIdx = i;
|
|
12202
|
+
break;
|
|
12203
|
+
}
|
|
12204
|
+
if (ch === "/" && (i === 0 || /\s/.test(next[i - 1] ?? ""))) {
|
|
12205
|
+
triggerKind = "cmd";
|
|
12206
|
+
triggerIdx = i;
|
|
12207
|
+
break;
|
|
12208
|
+
}
|
|
12209
|
+
if (/\s/.test(ch)) break;
|
|
12210
|
+
}
|
|
12211
|
+
if (triggerKind !== null && triggerIdx >= 0) {
|
|
12212
|
+
const query = next.slice(triggerIdx + 1, caret);
|
|
12213
|
+
setPicker({ kind: triggerKind, start: triggerIdx, query, cursor: 0 });
|
|
12214
|
+
} else {
|
|
12215
|
+
setPicker(null);
|
|
12216
|
+
}
|
|
12217
|
+
};
|
|
12218
|
+
const insertChoice = (i) => {
|
|
12219
|
+
if (!picker || items.length === 0) return;
|
|
12220
|
+
const choice = items[i] ?? items[0];
|
|
12221
|
+
const insert = picker.kind === "cmd" ? choice.name + " " : `@${choice.id} `;
|
|
12222
|
+
const before = text.slice(0, picker.start);
|
|
12223
|
+
const after = text.slice(picker.start + 1 + picker.query.length);
|
|
12224
|
+
const next = before + insert + after;
|
|
12225
|
+
setText(next);
|
|
12226
|
+
setPicker(null);
|
|
12227
|
+
requestAnimationFrame(() => {
|
|
12228
|
+
const ta = taRef.current;
|
|
12229
|
+
if (!ta) return;
|
|
12230
|
+
ta.focus();
|
|
12231
|
+
const pos = before.length + insert.length;
|
|
12232
|
+
ta.setSelectionRange(pos, pos);
|
|
12233
|
+
});
|
|
12234
|
+
};
|
|
12235
|
+
const submit = () => {
|
|
12236
|
+
if (!text.trim() && attachments.length === 0) return;
|
|
12237
|
+
onSubmit(text, attachments);
|
|
12238
|
+
setText("");
|
|
12239
|
+
setAttachments([]);
|
|
12240
|
+
setPicker(null);
|
|
12241
|
+
};
|
|
12242
|
+
const onKeyDown = (e) => {
|
|
12243
|
+
if (picker) {
|
|
12244
|
+
if (e.key === "ArrowDown") {
|
|
12245
|
+
e.preventDefault();
|
|
12246
|
+
setPicker(
|
|
12247
|
+
(p) => p ? { ...p, cursor: Math.min(items.length - 1, p.cursor + 1) } : p
|
|
12248
|
+
);
|
|
12249
|
+
return;
|
|
12250
|
+
}
|
|
12251
|
+
if (e.key === "ArrowUp") {
|
|
12252
|
+
e.preventDefault();
|
|
12253
|
+
setPicker((p) => p ? { ...p, cursor: Math.max(0, p.cursor - 1) } : p);
|
|
12254
|
+
return;
|
|
12255
|
+
}
|
|
12256
|
+
if (e.key === "Enter" && !e.metaKey && !e.ctrlKey && items.length > 0) {
|
|
12257
|
+
e.preventDefault();
|
|
12258
|
+
insertChoice(picker.cursor);
|
|
12259
|
+
return;
|
|
12260
|
+
}
|
|
12261
|
+
if (e.key === "Escape") {
|
|
12262
|
+
e.preventDefault();
|
|
12263
|
+
setPicker(null);
|
|
12264
|
+
return;
|
|
12265
|
+
}
|
|
12266
|
+
}
|
|
12267
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
|
|
12268
|
+
e.preventDefault();
|
|
12269
|
+
submit();
|
|
12270
|
+
}
|
|
12271
|
+
};
|
|
12272
|
+
const onDrop = (e) => {
|
|
12273
|
+
e.preventDefault();
|
|
12274
|
+
setDragOver(false);
|
|
12275
|
+
const files = Array.from(e.dataTransfer.files);
|
|
12276
|
+
setAttachments((cur) => [
|
|
12277
|
+
...cur,
|
|
12278
|
+
...files.map((f) => ({
|
|
12279
|
+
id: `${f.name}-${Date.now()}-${Math.random()}`,
|
|
12280
|
+
name: f.name,
|
|
12281
|
+
bytes: f.size
|
|
12282
|
+
}))
|
|
12283
|
+
]);
|
|
12284
|
+
};
|
|
12285
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12286
|
+
"div",
|
|
12287
|
+
{
|
|
12288
|
+
onDragOver: (e) => {
|
|
12289
|
+
e.preventDefault();
|
|
12290
|
+
setDragOver(true);
|
|
12291
|
+
},
|
|
12292
|
+
onDragLeave: () => setDragOver(false),
|
|
12293
|
+
onDrop,
|
|
12294
|
+
className: `relative rounded-md border transition ${dragOver ? "border-violet-400 bg-violet-50/50 dark:border-violet-600 dark:bg-violet-950/30" : "border-zinc-200 dark:border-zinc-800"} bg-white dark:bg-zinc-900`,
|
|
12295
|
+
children: [
|
|
12296
|
+
aboveInput && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-zinc-200 dark:border-zinc-800", children: aboveInput }),
|
|
12297
|
+
attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1.5 border-b border-zinc-200 px-3 py-2 dark:border-zinc-800", children: attachments.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12298
|
+
"span",
|
|
12299
|
+
{
|
|
12300
|
+
className: "inline-flex items-center gap-1.5 rounded-md bg-zinc-100 px-2 py-0.5 text-[11px] dark:bg-zinc-800",
|
|
12301
|
+
children: [
|
|
12302
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u{1F4CE}" }),
|
|
12303
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: a.name }),
|
|
12304
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400", children: fmtSize(a.bytes) }),
|
|
12305
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12306
|
+
"button",
|
|
12307
|
+
{
|
|
12308
|
+
onClick: () => setAttachments((cur) => cur.filter((x) => x.id !== a.id)),
|
|
12309
|
+
className: "opacity-50 hover:opacity-100",
|
|
12310
|
+
"aria-label": "Remove attachment",
|
|
12311
|
+
children: "\xD7"
|
|
12312
|
+
}
|
|
12313
|
+
)
|
|
12314
|
+
]
|
|
12315
|
+
},
|
|
12316
|
+
a.id
|
|
12317
|
+
)) }),
|
|
12318
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
12319
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12320
|
+
"textarea",
|
|
12321
|
+
{
|
|
12322
|
+
ref: taRef,
|
|
12323
|
+
value: text,
|
|
12324
|
+
onChange: (e) => updateText(e.target.value, e.target.selectionStart),
|
|
12325
|
+
onKeyDown,
|
|
12326
|
+
placeholder,
|
|
12327
|
+
spellCheck: false,
|
|
12328
|
+
className: "block w-full resize-none bg-transparent px-3 py-2.5 text-[14px] leading-relaxed outline-none placeholder:text-zinc-400",
|
|
12329
|
+
rows: 3
|
|
12330
|
+
}
|
|
12331
|
+
),
|
|
12332
|
+
picker && items.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute bottom-full left-2 z-10 mb-1 w-72 overflow-hidden rounded-md border border-zinc-200 bg-white shadow-lg dark:border-zinc-700 dark:bg-zinc-900", children: [
|
|
12333
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-zinc-100 bg-zinc-50 px-2 py-1 text-[10px] font-semibold uppercase tracking-wider text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950", children: [
|
|
12334
|
+
picker.kind === "cmd" ? "Commands" : "Mention",
|
|
12335
|
+
" \xB7 ",
|
|
12336
|
+
items.length
|
|
12337
|
+
] }),
|
|
12338
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "max-h-56 overflow-y-auto", children: items.map((item, i) => {
|
|
12339
|
+
const active = i === picker.cursor;
|
|
12340
|
+
if (picker.kind === "cmd") {
|
|
12341
|
+
const c = item;
|
|
12342
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12343
|
+
"li",
|
|
12344
|
+
{
|
|
12345
|
+
onMouseDown: (e) => {
|
|
12346
|
+
e.preventDefault();
|
|
12347
|
+
insertChoice(i);
|
|
12348
|
+
},
|
|
12349
|
+
onMouseEnter: () => setPicker((p) => p ? { ...p, cursor: i } : p),
|
|
12350
|
+
className: `cursor-pointer px-2 py-1.5 text-[12px] ${active ? "bg-violet-100 dark:bg-violet-900/30" : "hover:bg-zinc-50 dark:hover:bg-zinc-800"}`,
|
|
12351
|
+
children: [
|
|
12352
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono font-medium text-violet-700 dark:text-violet-300", children: c.name }),
|
|
12353
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-zinc-500", children: c.hint })
|
|
12354
|
+
]
|
|
12355
|
+
},
|
|
12356
|
+
c.name
|
|
12357
|
+
);
|
|
12358
|
+
}
|
|
12359
|
+
const m = item;
|
|
12360
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12361
|
+
"li",
|
|
12362
|
+
{
|
|
12363
|
+
onMouseDown: (e) => {
|
|
12364
|
+
e.preventDefault();
|
|
12365
|
+
insertChoice(i);
|
|
12366
|
+
},
|
|
12367
|
+
onMouseEnter: () => setPicker((p) => p ? { ...p, cursor: i } : p),
|
|
12368
|
+
className: `flex cursor-pointer items-center gap-2 px-2 py-1.5 text-[12px] ${active ? "bg-violet-100 dark:bg-violet-900/30" : "hover:bg-zinc-50 dark:hover:bg-zinc-800"}`,
|
|
12369
|
+
children: [
|
|
12370
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12371
|
+
"span",
|
|
12372
|
+
{
|
|
12373
|
+
className: "h-2 w-2 rounded-full",
|
|
12374
|
+
style: { backgroundColor: colors[m.kind] ?? "#71717a" }
|
|
12375
|
+
}
|
|
12376
|
+
),
|
|
12377
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: m.name }),
|
|
12378
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-[10px] uppercase tracking-wider text-zinc-400", children: m.kind })
|
|
12379
|
+
]
|
|
12380
|
+
},
|
|
12381
|
+
m.id
|
|
12382
|
+
);
|
|
12383
|
+
}) })
|
|
12384
|
+
] })
|
|
12385
|
+
] }),
|
|
12386
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-t border-zinc-200 bg-zinc-50/60 px-3 py-2 dark:border-zinc-800 dark:bg-zinc-900/40", children: [
|
|
12387
|
+
/* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "Drop files here, or click", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12388
|
+
Action,
|
|
12389
|
+
{
|
|
12390
|
+
variant: "ghost",
|
|
12391
|
+
size: "sm",
|
|
12392
|
+
onClick: () => {
|
|
12393
|
+
},
|
|
12394
|
+
children: "\u{1F4CE} attach"
|
|
12395
|
+
}
|
|
12396
|
+
) }),
|
|
12397
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-2 flex items-center gap-1.5", children: [
|
|
12398
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1.5 w-24 overflow-hidden rounded-full bg-zinc-200 dark:bg-zinc-700", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12399
|
+
"div",
|
|
12400
|
+
{
|
|
12401
|
+
className: "h-full rounded-full transition-all",
|
|
12402
|
+
style: { width: `${ratio * 100}%`, backgroundColor: meterColor }
|
|
12403
|
+
}
|
|
12404
|
+
) }),
|
|
12405
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-[11px]", style: { color: meterColor }, children: [
|
|
12406
|
+
fmtTokens(tokens),
|
|
12407
|
+
" / ",
|
|
12408
|
+
fmtTokens(budgetTokens)
|
|
12409
|
+
] })
|
|
12410
|
+
] }),
|
|
12411
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
|
|
12412
|
+
showHint && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden text-[10px] text-zinc-500 sm:inline", children: [
|
|
12413
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { className: "rounded border border-zinc-300 bg-zinc-50 px-1 py-0.5 font-mono text-[9px] text-zinc-700 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300", children: "\u2318" }),
|
|
12414
|
+
" ",
|
|
12415
|
+
"+",
|
|
12416
|
+
" ",
|
|
12417
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { className: "rounded border border-zinc-300 bg-zinc-50 px-1 py-0.5 font-mono text-[9px] text-zinc-700 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-300", children: "Enter" }),
|
|
12418
|
+
" ",
|
|
12419
|
+
"to send"
|
|
12420
|
+
] }),
|
|
12421
|
+
/* @__PURE__ */ jsxRuntime.jsx(Action, { color: "violet", size: "sm", onClick: submit, children: "send \u2192" })
|
|
12422
|
+
] })
|
|
12423
|
+
] })
|
|
12424
|
+
]
|
|
12425
|
+
}
|
|
12426
|
+
);
|
|
12427
|
+
}
|
|
12428
|
+
function fmtTokens(n) {
|
|
12429
|
+
if (n < 1e3) return `${n}`;
|
|
12430
|
+
return `${(n / 1e3).toFixed(1)}k`;
|
|
12431
|
+
}
|
|
12432
|
+
function fmtSize(b) {
|
|
12433
|
+
if (b < 1024) return `${b} B`;
|
|
12434
|
+
if (b < 1024 * 1024) return `${(b / 1024).toFixed(0)} KB`;
|
|
12435
|
+
return `${(b / 1024 / 1024).toFixed(1)} MB`;
|
|
12436
|
+
}
|
|
12437
|
+
function ChatDrawer({
|
|
12438
|
+
tabs,
|
|
12439
|
+
activeTabId,
|
|
12440
|
+
onTabChange,
|
|
12441
|
+
open = true,
|
|
12442
|
+
onToggle,
|
|
12443
|
+
children,
|
|
12444
|
+
minBodyHeight = 140,
|
|
12445
|
+
className
|
|
12446
|
+
}) {
|
|
12447
|
+
const handleToggle = react.useCallback(() => {
|
|
12448
|
+
onToggle?.(!open);
|
|
12449
|
+
}, [onToggle, open]);
|
|
12450
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
|
|
12451
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 px-2.5 py-2", children: [
|
|
12452
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-1.5", children: tabs.map((tab, i) => {
|
|
12453
|
+
const active = tab.id === activeTabId;
|
|
12454
|
+
const number = tab.number === null ? null : tab.number ?? i + 1;
|
|
12455
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12456
|
+
"button",
|
|
12457
|
+
{
|
|
12458
|
+
type: "button",
|
|
12459
|
+
onClick: () => onTabChange(tab.id),
|
|
12460
|
+
className: [
|
|
12461
|
+
"group inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-[12px] font-medium transition",
|
|
12462
|
+
active ? "border-violet-500 bg-violet-500 text-white" : "border-zinc-200 bg-transparent text-zinc-700 hover:border-zinc-300 hover:bg-zinc-50 dark:border-zinc-700 dark:text-zinc-300 dark:hover:border-zinc-600 dark:hover:bg-zinc-800"
|
|
12463
|
+
].join(" "),
|
|
12464
|
+
"aria-pressed": active,
|
|
12465
|
+
children: [
|
|
12466
|
+
number !== null && /* @__PURE__ */ jsxRuntime.jsx(
|
|
12467
|
+
"span",
|
|
12468
|
+
{
|
|
12469
|
+
className: [
|
|
12470
|
+
"inline-flex h-4 w-4 items-center justify-center rounded-full text-[10px] font-semibold",
|
|
12471
|
+
active ? "bg-white/20 text-white" : "bg-zinc-200 text-zinc-600 group-hover:bg-zinc-300 dark:bg-zinc-700 dark:text-zinc-300"
|
|
12472
|
+
].join(" "),
|
|
12473
|
+
children: number
|
|
12474
|
+
}
|
|
12475
|
+
),
|
|
12476
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate max-w-[180px]", children: tab.label })
|
|
12477
|
+
]
|
|
12478
|
+
},
|
|
12479
|
+
tab.id
|
|
12480
|
+
);
|
|
12481
|
+
}) }),
|
|
12482
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12483
|
+
"button",
|
|
12484
|
+
{
|
|
12485
|
+
type: "button",
|
|
12486
|
+
onClick: handleToggle,
|
|
12487
|
+
"aria-label": open ? "Collapse drawer" : "Expand drawer",
|
|
12488
|
+
"aria-expanded": open,
|
|
12489
|
+
className: "ml-auto inline-flex h-6 w-6 items-center justify-center rounded-full text-zinc-400 transition hover:bg-zinc-100 hover:text-zinc-700 dark:hover:bg-zinc-800 dark:hover:text-zinc-200",
|
|
12490
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12491
|
+
Icon,
|
|
12492
|
+
{
|
|
12493
|
+
name: open ? "chevron-down" : "chevron-up",
|
|
12494
|
+
className: "h-3.5 w-3.5"
|
|
12495
|
+
}
|
|
12496
|
+
)
|
|
12497
|
+
}
|
|
12498
|
+
)
|
|
12499
|
+
] }),
|
|
12500
|
+
open && /* @__PURE__ */ jsxRuntime.jsx(
|
|
12501
|
+
"div",
|
|
12502
|
+
{
|
|
12503
|
+
className: "px-2.5 pb-2.5",
|
|
12504
|
+
style: { minHeight: minBodyHeight },
|
|
12505
|
+
children
|
|
12506
|
+
}
|
|
12507
|
+
)
|
|
12508
|
+
] });
|
|
12509
|
+
}
|
|
12510
|
+
function defaultKeyOf(item) {
|
|
12511
|
+
if (item && typeof item === "object") {
|
|
12512
|
+
const r = item;
|
|
12513
|
+
if (typeof r.name === "string") return r.name;
|
|
12514
|
+
if (typeof r.id === "string") return r.id;
|
|
12515
|
+
}
|
|
12516
|
+
return String(item);
|
|
12517
|
+
}
|
|
12518
|
+
function defaultFilter(item, query) {
|
|
12519
|
+
if (!query) return true;
|
|
12520
|
+
return defaultKeyOf(item).toLowerCase().includes(query.toLowerCase());
|
|
12521
|
+
}
|
|
12522
|
+
function detectTrigger(text, caret, triggerChars) {
|
|
12523
|
+
for (let i = caret - 1; i >= 0; i--) {
|
|
12524
|
+
const ch = text[i];
|
|
12525
|
+
if (ch === void 0) break;
|
|
12526
|
+
if (triggerChars.has(ch)) {
|
|
12527
|
+
if (i === 0 || /\s/.test(text[i - 1])) {
|
|
12528
|
+
return {
|
|
12529
|
+
char: ch,
|
|
12530
|
+
start: i,
|
|
12531
|
+
end: caret,
|
|
12532
|
+
query: text.slice(i + 1, caret)
|
|
12533
|
+
};
|
|
12534
|
+
}
|
|
12535
|
+
return null;
|
|
12536
|
+
}
|
|
12537
|
+
if (/\s/.test(ch)) return null;
|
|
12538
|
+
}
|
|
12539
|
+
return null;
|
|
12540
|
+
}
|
|
12541
|
+
function InputTag({
|
|
12542
|
+
adapter,
|
|
12543
|
+
triggers,
|
|
12544
|
+
maxItems = 8,
|
|
12545
|
+
placement = "bottom-left",
|
|
12546
|
+
className,
|
|
12547
|
+
style,
|
|
12548
|
+
onPick
|
|
12549
|
+
}) {
|
|
12550
|
+
const triggerChars = react.useMemo(() => new Set(Object.keys(triggers)), [triggers]);
|
|
12551
|
+
const [active, setActive] = react.useState(null);
|
|
12552
|
+
const [cursor, setCursor] = react.useState(0);
|
|
12553
|
+
const [anchorRect, setAnchorRect] = react.useState(null);
|
|
12554
|
+
const stateRef = react.useRef({ text: "", caretIndex: 0 });
|
|
12555
|
+
const activeRef = react.useRef(null);
|
|
12556
|
+
const cursorRef = react.useRef(0);
|
|
12557
|
+
activeRef.current = active;
|
|
12558
|
+
cursorRef.current = cursor;
|
|
12559
|
+
const items = react.useMemo(() => {
|
|
12560
|
+
if (!active) return [];
|
|
12561
|
+
const cfg2 = triggers[active.char];
|
|
12562
|
+
if (!cfg2) return [];
|
|
12563
|
+
const filter = cfg2.filter ?? defaultFilter;
|
|
12564
|
+
const out = [];
|
|
12565
|
+
for (const item of cfg2.items) {
|
|
12566
|
+
if (filter(item, active.query)) out.push(item);
|
|
12567
|
+
if (out.length >= maxItems) break;
|
|
12568
|
+
}
|
|
12569
|
+
return out;
|
|
12570
|
+
}, [active, triggers, maxItems]);
|
|
12571
|
+
react.useEffect(() => {
|
|
12572
|
+
return adapter.subscribe((s) => {
|
|
12573
|
+
stateRef.current = s;
|
|
12574
|
+
const next = detectTrigger(s.text, s.caretIndex, triggerChars);
|
|
12575
|
+
setActive((prev) => {
|
|
12576
|
+
if (!prev || !next || prev.char !== next.char || prev.start !== next.start) {
|
|
12577
|
+
setCursor(0);
|
|
12578
|
+
}
|
|
12579
|
+
return next;
|
|
12580
|
+
});
|
|
12581
|
+
if (next) setAnchorRect(adapter.getAnchorRect());
|
|
12582
|
+
});
|
|
12583
|
+
}, [adapter, triggerChars]);
|
|
12584
|
+
react.useLayoutEffect(() => {
|
|
12585
|
+
if (!active) return;
|
|
12586
|
+
const update = () => setAnchorRect(adapter.getAnchorRect());
|
|
12587
|
+
update();
|
|
12588
|
+
window.addEventListener("scroll", update, true);
|
|
12589
|
+
window.addEventListener("resize", update);
|
|
12590
|
+
return () => {
|
|
12591
|
+
window.removeEventListener("scroll", update, true);
|
|
12592
|
+
window.removeEventListener("resize", update);
|
|
12593
|
+
};
|
|
12594
|
+
}, [active, adapter]);
|
|
12595
|
+
react.useEffect(() => {
|
|
12596
|
+
if (cursor >= items.length) setCursor(Math.max(0, items.length - 1));
|
|
12597
|
+
}, [items.length, cursor]);
|
|
12598
|
+
const pickItem = react.useCallback(
|
|
12599
|
+
(item) => {
|
|
12600
|
+
const cur = activeRef.current;
|
|
12601
|
+
if (!cur) return;
|
|
12602
|
+
const cfg2 = triggers[cur.char];
|
|
12603
|
+
if (!cfg2) return;
|
|
12604
|
+
const replacement = cfg2.insert(item, cur.query);
|
|
12605
|
+
adapter.replaceRange(cur.start, cur.end, replacement);
|
|
12606
|
+
onPick?.({ triggerChar: cur.char, query: cur.query, item });
|
|
12607
|
+
setActive(null);
|
|
12608
|
+
},
|
|
12609
|
+
[adapter, triggers, onPick]
|
|
12610
|
+
);
|
|
12611
|
+
react.useEffect(() => {
|
|
12612
|
+
return adapter.onKey((e) => {
|
|
12613
|
+
const cur = activeRef.current;
|
|
12614
|
+
if (!cur) return false;
|
|
12615
|
+
const visible = items;
|
|
12616
|
+
switch (e.key) {
|
|
12617
|
+
case "ArrowDown":
|
|
12618
|
+
if (visible.length === 0) return false;
|
|
12619
|
+
setCursor((c) => (c + 1) % visible.length);
|
|
12620
|
+
return true;
|
|
12621
|
+
case "ArrowUp":
|
|
12622
|
+
if (visible.length === 0) return false;
|
|
12623
|
+
setCursor((c) => (c - 1 + visible.length) % visible.length);
|
|
12624
|
+
return true;
|
|
12625
|
+
case "Enter":
|
|
12626
|
+
case "Tab": {
|
|
12627
|
+
if (visible.length === 0) return false;
|
|
12628
|
+
const idx = Math.min(cursorRef.current, visible.length - 1);
|
|
12629
|
+
pickItem(visible[idx]);
|
|
12630
|
+
return true;
|
|
12631
|
+
}
|
|
12632
|
+
case "Escape":
|
|
12633
|
+
setActive(null);
|
|
12634
|
+
return true;
|
|
12635
|
+
default:
|
|
12636
|
+
return false;
|
|
12637
|
+
}
|
|
12638
|
+
});
|
|
12639
|
+
}, [adapter, items, pickItem]);
|
|
12640
|
+
if (!active || !anchorRect || items.length === 0) return null;
|
|
12641
|
+
const cfg = triggers[active.char];
|
|
12642
|
+
if (!cfg) return null;
|
|
12643
|
+
const popStyle = computePopoverStyle(anchorRect, placement);
|
|
12644
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
12645
|
+
"div",
|
|
12646
|
+
{
|
|
12647
|
+
role: "listbox",
|
|
12648
|
+
"aria-label": cfg.label ?? `Pick a ${active.char === "/" ? "command" : "mention"}`,
|
|
12649
|
+
className: [
|
|
12650
|
+
"fixed z-50 w-72 overflow-hidden rounded-md border border-zinc-200 bg-white shadow-lg dark:border-zinc-700 dark:bg-zinc-900",
|
|
12651
|
+
className ?? ""
|
|
12652
|
+
].filter(Boolean).join(" "),
|
|
12653
|
+
style: { ...popStyle, ...style },
|
|
12654
|
+
children: [
|
|
12655
|
+
(cfg.label || active.query) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-b border-zinc-100 bg-zinc-50 px-2 py-1 text-[10px] font-semibold uppercase tracking-wider text-zinc-500 dark:border-zinc-800 dark:bg-zinc-950", children: [
|
|
12656
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: cfg.label ?? labelForChar(active.char) }),
|
|
12657
|
+
active.query && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-[10px] normal-case tracking-normal text-zinc-400", children: [
|
|
12658
|
+
active.char,
|
|
12659
|
+
active.query
|
|
12660
|
+
] })
|
|
12661
|
+
] }),
|
|
12662
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "max-h-60 overflow-y-auto", children: items.map((item, i) => {
|
|
12663
|
+
const renderer = cfg.render ?? ((it) => /* @__PURE__ */ jsxRuntime.jsx("span", { children: defaultKeyOf(it) }));
|
|
12664
|
+
const active2 = i === cursor;
|
|
12665
|
+
const key = (cfg.keyOf ?? defaultKeyOf)(item);
|
|
12666
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12667
|
+
"li",
|
|
12668
|
+
{
|
|
12669
|
+
role: "option",
|
|
12670
|
+
"aria-selected": active2,
|
|
12671
|
+
onMouseDown: (e) => {
|
|
12672
|
+
e.preventDefault();
|
|
12673
|
+
pickItem(item);
|
|
12674
|
+
},
|
|
12675
|
+
onMouseEnter: () => setCursor(i),
|
|
12676
|
+
className: [
|
|
12677
|
+
"cursor-pointer px-2 py-1.5 text-[12px]",
|
|
12678
|
+
active2 ? "bg-violet-100 dark:bg-violet-900/30" : "hover:bg-zinc-50 dark:hover:bg-zinc-800"
|
|
12679
|
+
].join(" "),
|
|
12680
|
+
children: renderer(item, active2)
|
|
12681
|
+
},
|
|
12682
|
+
key
|
|
12683
|
+
);
|
|
12684
|
+
}) })
|
|
12685
|
+
]
|
|
12686
|
+
}
|
|
12687
|
+
);
|
|
12688
|
+
}
|
|
12689
|
+
function labelForChar(ch) {
|
|
12690
|
+
if (ch === "/") return "Commands";
|
|
12691
|
+
if (ch === "@") return "Mentions";
|
|
12692
|
+
if (ch === "#") return "Tags";
|
|
12693
|
+
if (ch === ":") return "Emoji";
|
|
12694
|
+
return `Trigger ${ch}`;
|
|
12695
|
+
}
|
|
12696
|
+
function computePopoverStyle(anchor, placement) {
|
|
12697
|
+
const offset = 4;
|
|
12698
|
+
switch (placement) {
|
|
12699
|
+
case "bottom-right":
|
|
12700
|
+
return { top: anchor.bottom + offset, right: window.innerWidth - anchor.right };
|
|
12701
|
+
case "top-left":
|
|
12702
|
+
return { bottom: window.innerHeight - anchor.top + offset, left: anchor.left };
|
|
12703
|
+
case "top-right":
|
|
12704
|
+
return { bottom: window.innerHeight - anchor.top + offset, right: window.innerWidth - anchor.right };
|
|
12705
|
+
case "bottom-left":
|
|
12706
|
+
default:
|
|
12707
|
+
return { top: anchor.bottom + offset, left: anchor.left };
|
|
12708
|
+
}
|
|
12709
|
+
}
|
|
12710
|
+
|
|
12711
|
+
// src/components/InputTag/adapters.ts
|
|
12712
|
+
function nativeValueSetter(el) {
|
|
12713
|
+
const proto = el instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
12714
|
+
const desc = Object.getOwnPropertyDescriptor(proto, "value");
|
|
12715
|
+
if (!desc?.set) return null;
|
|
12716
|
+
return desc.set.bind(el);
|
|
12717
|
+
}
|
|
12718
|
+
function replaceInValueElement(el, start, end, replacement) {
|
|
12719
|
+
const current = el.value;
|
|
12720
|
+
const next = current.slice(0, start) + replacement + current.slice(end);
|
|
12721
|
+
const setter = nativeValueSetter(el);
|
|
12722
|
+
if (setter) {
|
|
12723
|
+
setter(next);
|
|
12724
|
+
} else {
|
|
12725
|
+
el.value = next;
|
|
12726
|
+
}
|
|
12727
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
12728
|
+
const caret = start + replacement.length;
|
|
12729
|
+
el.setSelectionRange(caret, caret);
|
|
12730
|
+
el.focus();
|
|
12731
|
+
}
|
|
12732
|
+
function valueElementAdapter(ref) {
|
|
12733
|
+
return {
|
|
12734
|
+
subscribe(fn) {
|
|
12735
|
+
const handler = () => {
|
|
12736
|
+
const el2 = ref.current;
|
|
12737
|
+
if (!el2) return;
|
|
12738
|
+
fn({
|
|
12739
|
+
text: el2.value,
|
|
12740
|
+
caretIndex: el2.selectionStart ?? el2.value.length
|
|
12741
|
+
});
|
|
12742
|
+
};
|
|
12743
|
+
const el = ref.current;
|
|
12744
|
+
if (el) {
|
|
12745
|
+
el.addEventListener("input", handler);
|
|
12746
|
+
el.addEventListener("click", handler);
|
|
12747
|
+
el.addEventListener("keyup", handler);
|
|
12748
|
+
el.addEventListener("focus", handler);
|
|
12749
|
+
}
|
|
12750
|
+
return () => {
|
|
12751
|
+
const el2 = ref.current;
|
|
12752
|
+
if (el2) {
|
|
12753
|
+
el2.removeEventListener("input", handler);
|
|
12754
|
+
el2.removeEventListener("click", handler);
|
|
12755
|
+
el2.removeEventListener("keyup", handler);
|
|
12756
|
+
el2.removeEventListener("focus", handler);
|
|
12757
|
+
}
|
|
12758
|
+
};
|
|
12759
|
+
},
|
|
12760
|
+
replaceRange(start, end, replacement) {
|
|
12761
|
+
const el = ref.current;
|
|
12762
|
+
if (!el) return;
|
|
12763
|
+
replaceInValueElement(el, start, end, replacement);
|
|
12764
|
+
},
|
|
12765
|
+
getAnchorRect() {
|
|
12766
|
+
const el = ref.current;
|
|
12767
|
+
return el ? el.getBoundingClientRect() : null;
|
|
12768
|
+
},
|
|
12769
|
+
onKey(handler) {
|
|
12770
|
+
const fn = (e) => {
|
|
12771
|
+
if (handler(e)) {
|
|
12772
|
+
e.preventDefault();
|
|
12773
|
+
e.stopPropagation();
|
|
12774
|
+
}
|
|
12775
|
+
};
|
|
12776
|
+
const el = ref.current;
|
|
12777
|
+
if (el) el.addEventListener("keydown", fn);
|
|
12778
|
+
return () => {
|
|
12779
|
+
const el2 = ref.current;
|
|
12780
|
+
if (el2) el2.removeEventListener("keydown", fn);
|
|
12781
|
+
};
|
|
12782
|
+
}
|
|
12783
|
+
};
|
|
12784
|
+
}
|
|
12785
|
+
function textareaAdapter(ref) {
|
|
12786
|
+
return valueElementAdapter(ref);
|
|
12787
|
+
}
|
|
12788
|
+
function inputAdapter(ref) {
|
|
12789
|
+
return valueElementAdapter(ref);
|
|
12790
|
+
}
|
|
12791
|
+
function contentEditableAdapter(ref) {
|
|
12792
|
+
function caretIndex(el) {
|
|
12793
|
+
const sel = window.getSelection();
|
|
12794
|
+
if (!sel || sel.rangeCount === 0) return 0;
|
|
12795
|
+
const range2 = sel.getRangeAt(0).cloneRange();
|
|
12796
|
+
range2.selectNodeContents(el);
|
|
12797
|
+
range2.setEnd(sel.focusNode, sel.focusOffset);
|
|
12798
|
+
return range2.toString().length;
|
|
12799
|
+
}
|
|
12800
|
+
function setCaret(el, index) {
|
|
12801
|
+
const sel = window.getSelection();
|
|
12802
|
+
if (!sel) return;
|
|
12803
|
+
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
|
|
12804
|
+
let remaining = index;
|
|
12805
|
+
let node = walker.nextNode();
|
|
12806
|
+
while (node) {
|
|
12807
|
+
const len = (node.textContent ?? "").length;
|
|
12808
|
+
if (remaining <= len) {
|
|
12809
|
+
const range3 = document.createRange();
|
|
12810
|
+
range3.setStart(node, remaining);
|
|
12811
|
+
range3.collapse(true);
|
|
12812
|
+
sel.removeAllRanges();
|
|
12813
|
+
sel.addRange(range3);
|
|
12814
|
+
return;
|
|
12815
|
+
}
|
|
12816
|
+
remaining -= len;
|
|
12817
|
+
node = walker.nextNode();
|
|
12818
|
+
}
|
|
12819
|
+
const range2 = document.createRange();
|
|
12820
|
+
range2.selectNodeContents(el);
|
|
12821
|
+
range2.collapse(false);
|
|
12822
|
+
sel.removeAllRanges();
|
|
12823
|
+
sel.addRange(range2);
|
|
12824
|
+
}
|
|
12825
|
+
return {
|
|
12826
|
+
subscribe(fn) {
|
|
12827
|
+
const handler = () => {
|
|
12828
|
+
const el2 = ref.current;
|
|
12829
|
+
if (!el2) return;
|
|
12830
|
+
fn({ text: el2.textContent ?? "", caretIndex: caretIndex(el2) });
|
|
12831
|
+
};
|
|
12832
|
+
const el = ref.current;
|
|
12833
|
+
if (el) {
|
|
12834
|
+
el.addEventListener("input", handler);
|
|
12835
|
+
el.addEventListener("click", handler);
|
|
12836
|
+
el.addEventListener("keyup", handler);
|
|
12837
|
+
el.addEventListener("focus", handler);
|
|
12838
|
+
}
|
|
12839
|
+
return () => {
|
|
12840
|
+
const el2 = ref.current;
|
|
12841
|
+
if (el2) {
|
|
12842
|
+
el2.removeEventListener("input", handler);
|
|
12843
|
+
el2.removeEventListener("click", handler);
|
|
12844
|
+
el2.removeEventListener("keyup", handler);
|
|
12845
|
+
el2.removeEventListener("focus", handler);
|
|
12846
|
+
}
|
|
12847
|
+
};
|
|
12848
|
+
},
|
|
12849
|
+
replaceRange(start, end, replacement) {
|
|
12850
|
+
const el = ref.current;
|
|
12851
|
+
if (!el) return;
|
|
12852
|
+
const current = el.textContent ?? "";
|
|
12853
|
+
const next = current.slice(0, start) + replacement + current.slice(end);
|
|
12854
|
+
el.textContent = next;
|
|
12855
|
+
setCaret(el, start + replacement.length);
|
|
12856
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
12857
|
+
el.focus();
|
|
12858
|
+
},
|
|
12859
|
+
getAnchorRect() {
|
|
12860
|
+
const el = ref.current;
|
|
12861
|
+
return el ? el.getBoundingClientRect() : null;
|
|
12862
|
+
},
|
|
12863
|
+
onKey(handler) {
|
|
12864
|
+
const fn = (e) => {
|
|
12865
|
+
if (handler(e)) {
|
|
12866
|
+
e.preventDefault();
|
|
12867
|
+
e.stopPropagation();
|
|
12868
|
+
}
|
|
12869
|
+
};
|
|
12870
|
+
const el = ref.current;
|
|
12871
|
+
if (el) el.addEventListener("keydown", fn);
|
|
12872
|
+
return () => {
|
|
12873
|
+
const el2 = ref.current;
|
|
12874
|
+
if (el2) el2.removeEventListener("keydown", fn);
|
|
12875
|
+
};
|
|
12876
|
+
}
|
|
12877
|
+
};
|
|
12878
|
+
}
|
|
12879
|
+
function controlledAdapter(opts) {
|
|
12880
|
+
let listener = null;
|
|
12881
|
+
let keyHandler = null;
|
|
12882
|
+
const installKey = () => {
|
|
12883
|
+
const el = opts.anchorRef.current;
|
|
12884
|
+
if (!el || !keyHandler) return () => {
|
|
12885
|
+
};
|
|
12886
|
+
const fn = (e) => {
|
|
12887
|
+
if (keyHandler && keyHandler(e)) {
|
|
12888
|
+
e.preventDefault();
|
|
12889
|
+
e.stopPropagation();
|
|
12890
|
+
}
|
|
12891
|
+
};
|
|
12892
|
+
el.addEventListener("keydown", fn);
|
|
12893
|
+
return () => el.removeEventListener("keydown", fn);
|
|
12894
|
+
};
|
|
12895
|
+
return {
|
|
12896
|
+
notify(state) {
|
|
12897
|
+
listener?.(state);
|
|
12898
|
+
},
|
|
12899
|
+
subscribe(fn) {
|
|
12900
|
+
listener = fn;
|
|
12901
|
+
return () => {
|
|
12902
|
+
if (listener === fn) listener = null;
|
|
12903
|
+
};
|
|
12904
|
+
},
|
|
12905
|
+
replaceRange(start, end, replacement) {
|
|
12906
|
+
opts.onReplaceRange(start, end, replacement);
|
|
12907
|
+
},
|
|
12908
|
+
getAnchorRect() {
|
|
12909
|
+
const el = opts.anchorRef.current;
|
|
12910
|
+
return el ? el.getBoundingClientRect() : null;
|
|
12911
|
+
},
|
|
12912
|
+
onKey(handler) {
|
|
12913
|
+
keyHandler = handler;
|
|
12914
|
+
const uninstall = installKey();
|
|
12915
|
+
return () => {
|
|
12916
|
+
if (keyHandler === handler) keyHandler = null;
|
|
12917
|
+
uninstall();
|
|
12918
|
+
};
|
|
12919
|
+
}
|
|
12920
|
+
};
|
|
12921
|
+
}
|
|
12922
|
+
function MagicWand({
|
|
12923
|
+
value,
|
|
12924
|
+
onValueChange,
|
|
12925
|
+
actions,
|
|
12926
|
+
appearance = "floating",
|
|
12927
|
+
autoHide = true,
|
|
12928
|
+
rows = 6,
|
|
12929
|
+
placeholder,
|
|
12930
|
+
onAction
|
|
12931
|
+
}) {
|
|
12932
|
+
const taRef = react.useRef(null);
|
|
12933
|
+
const wandRef = react.useRef(null);
|
|
12934
|
+
const [sel, setSel] = react.useState(null);
|
|
12935
|
+
const [pos, setPos] = react.useState(null);
|
|
12936
|
+
const [busy, setBusy] = react.useState(null);
|
|
12937
|
+
const measureSelection = react.useCallback(() => {
|
|
12938
|
+
const ta = taRef.current;
|
|
12939
|
+
if (!ta) return;
|
|
12940
|
+
const start = ta.selectionStart ?? 0;
|
|
12941
|
+
const end = ta.selectionEnd ?? 0;
|
|
12942
|
+
if (start === end) {
|
|
12943
|
+
setSel(null);
|
|
12944
|
+
setPos(null);
|
|
12945
|
+
return;
|
|
12946
|
+
}
|
|
12947
|
+
const text = ta.value.slice(start, end);
|
|
12948
|
+
setSel({ start, end, text });
|
|
12949
|
+
const rect = caretRect(ta, start, end);
|
|
12950
|
+
if (rect) setPos({ x: rect.x, y: rect.y });
|
|
12951
|
+
}, []);
|
|
12952
|
+
react.useEffect(() => {
|
|
12953
|
+
if (!autoHide) return;
|
|
12954
|
+
const onScroll = () => {
|
|
12955
|
+
setSel(null);
|
|
12956
|
+
setPos(null);
|
|
12957
|
+
};
|
|
12958
|
+
window.addEventListener("scroll", onScroll, true);
|
|
12959
|
+
return () => window.removeEventListener("scroll", onScroll, true);
|
|
12960
|
+
}, [autoHide]);
|
|
12961
|
+
react.useEffect(() => {
|
|
12962
|
+
if (!autoHide) return;
|
|
12963
|
+
const onDocMouseDown = (e) => {
|
|
12964
|
+
if (wandRef.current?.contains(e.target)) return;
|
|
12965
|
+
if (taRef.current?.contains(e.target)) return;
|
|
12966
|
+
setSel(null);
|
|
12967
|
+
setPos(null);
|
|
12968
|
+
};
|
|
12969
|
+
document.addEventListener("mousedown", onDocMouseDown);
|
|
12970
|
+
return () => document.removeEventListener("mousedown", onDocMouseDown);
|
|
12971
|
+
}, [autoHide]);
|
|
12972
|
+
const handleAction = async (action) => {
|
|
12973
|
+
if (!sel) return;
|
|
12974
|
+
setBusy(action.id);
|
|
12975
|
+
try {
|
|
12976
|
+
const replacement = await action.run(sel.text, sel);
|
|
12977
|
+
const next = value.slice(0, sel.start) + replacement + value.slice(sel.end);
|
|
12978
|
+
onValueChange(next);
|
|
12979
|
+
onAction?.(action, sel, replacement);
|
|
12980
|
+
} finally {
|
|
12981
|
+
setBusy(null);
|
|
12982
|
+
setSel(null);
|
|
12983
|
+
setPos(null);
|
|
12984
|
+
}
|
|
12985
|
+
};
|
|
12986
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
|
|
12987
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
12988
|
+
Textarea,
|
|
12989
|
+
{
|
|
12990
|
+
ref: taRef,
|
|
12991
|
+
value,
|
|
12992
|
+
onValueChange,
|
|
12993
|
+
onSelect: measureSelection,
|
|
12994
|
+
onKeyUp: measureSelection,
|
|
12995
|
+
onMouseUp: measureSelection,
|
|
12996
|
+
rows,
|
|
12997
|
+
placeholder
|
|
12998
|
+
}
|
|
12999
|
+
),
|
|
13000
|
+
sel && pos && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13001
|
+
Wand,
|
|
13002
|
+
{
|
|
13003
|
+
ref: wandRef,
|
|
13004
|
+
pos,
|
|
13005
|
+
actions,
|
|
13006
|
+
appearance,
|
|
13007
|
+
busy,
|
|
13008
|
+
onAction: handleAction,
|
|
13009
|
+
selectionLength: sel.text.length
|
|
13010
|
+
}
|
|
13011
|
+
)
|
|
13012
|
+
] });
|
|
13013
|
+
}
|
|
13014
|
+
function Wand({
|
|
13015
|
+
ref,
|
|
13016
|
+
pos,
|
|
13017
|
+
actions,
|
|
13018
|
+
appearance,
|
|
13019
|
+
busy,
|
|
13020
|
+
onAction,
|
|
13021
|
+
selectionLength
|
|
13022
|
+
}) {
|
|
13023
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13024
|
+
"div",
|
|
13025
|
+
{
|
|
13026
|
+
ref,
|
|
13027
|
+
className: "absolute z-20 -translate-x-1/2 -translate-y-full",
|
|
13028
|
+
style: { left: pos.x, top: pos.y - 6 },
|
|
13029
|
+
children: [
|
|
13030
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 rounded-full border border-violet-300 bg-white px-1.5 py-1 shadow-lg ring-1 ring-violet-200 dark:border-violet-700 dark:bg-zinc-900 dark:ring-violet-900", children: [
|
|
13031
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-1 mr-0.5 inline-flex items-center gap-1 text-[10px] font-medium text-violet-700 dark:text-violet-300", children: [
|
|
13032
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2726" }),
|
|
13033
|
+
appearance === "floating" && /* @__PURE__ */ jsxRuntime.jsxs(Badge, { color: "violet", children: [
|
|
13034
|
+
selectionLength,
|
|
13035
|
+
" chars"
|
|
13036
|
+
] })
|
|
13037
|
+
] }),
|
|
13038
|
+
actions.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13039
|
+
Action,
|
|
13040
|
+
{
|
|
13041
|
+
size: "sm",
|
|
13042
|
+
variant: "ghost",
|
|
13043
|
+
onClick: () => onAction(a),
|
|
13044
|
+
disabled: busy !== null,
|
|
13045
|
+
title: a.hint,
|
|
13046
|
+
children: [
|
|
13047
|
+
busy === a.id ? "\u2026" : appearance === "pill" ? a.label[0] : a.label,
|
|
13048
|
+
appearance === "floating" && a.tag && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 rounded-full bg-zinc-100 px-1 text-[9px] font-medium text-zinc-500 dark:bg-zinc-800", children: a.tag })
|
|
13049
|
+
]
|
|
13050
|
+
},
|
|
13051
|
+
a.id
|
|
13052
|
+
))
|
|
13053
|
+
] }),
|
|
13054
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute left-1/2 top-full -translate-x-1/2 -translate-y-px", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2 w-2 rotate-45 border-b border-r border-violet-300 bg-white dark:border-violet-700 dark:bg-zinc-900" }) })
|
|
13055
|
+
]
|
|
13056
|
+
}
|
|
13057
|
+
);
|
|
13058
|
+
}
|
|
13059
|
+
function caretRect(ta, start, end) {
|
|
13060
|
+
const div = document.createElement("div");
|
|
13061
|
+
const style = getComputedStyle(ta);
|
|
13062
|
+
const props = [
|
|
13063
|
+
"boxSizing",
|
|
13064
|
+
"width",
|
|
13065
|
+
"height",
|
|
13066
|
+
"overflowX",
|
|
13067
|
+
"overflowY",
|
|
13068
|
+
"borderTopWidth",
|
|
13069
|
+
"borderRightWidth",
|
|
13070
|
+
"borderBottomWidth",
|
|
13071
|
+
"borderLeftWidth",
|
|
13072
|
+
"paddingTop",
|
|
13073
|
+
"paddingRight",
|
|
13074
|
+
"paddingBottom",
|
|
13075
|
+
"paddingLeft",
|
|
13076
|
+
"fontStyle",
|
|
13077
|
+
"fontVariant",
|
|
13078
|
+
"fontWeight",
|
|
13079
|
+
"fontStretch",
|
|
13080
|
+
"fontSize",
|
|
13081
|
+
"fontSizeAdjust",
|
|
13082
|
+
"lineHeight",
|
|
13083
|
+
"fontFamily",
|
|
13084
|
+
"textAlign",
|
|
13085
|
+
"textTransform",
|
|
13086
|
+
"textIndent",
|
|
13087
|
+
"letterSpacing",
|
|
13088
|
+
"wordSpacing",
|
|
13089
|
+
"tabSize"
|
|
13090
|
+
];
|
|
13091
|
+
for (const p of props) div.style[p] = style[p];
|
|
13092
|
+
div.style.position = "absolute";
|
|
13093
|
+
div.style.top = "-9999px";
|
|
13094
|
+
div.style.left = "-9999px";
|
|
13095
|
+
div.style.whiteSpace = "pre-wrap";
|
|
13096
|
+
div.style.wordWrap = "break-word";
|
|
13097
|
+
const value = ta.value;
|
|
13098
|
+
div.textContent = value.substring(0, start);
|
|
13099
|
+
const startSpan = document.createElement("span");
|
|
13100
|
+
startSpan.textContent = value.substring(start, end) || ".";
|
|
13101
|
+
div.appendChild(startSpan);
|
|
13102
|
+
document.body.appendChild(div);
|
|
13103
|
+
const taRect = ta.getBoundingClientRect();
|
|
13104
|
+
const parentRect = ta.offsetParent?.getBoundingClientRect() ?? taRect;
|
|
13105
|
+
const spanRect = startSpan.getBoundingClientRect();
|
|
13106
|
+
const divRect = div.getBoundingClientRect();
|
|
13107
|
+
const offsetX = spanRect.left - divRect.left;
|
|
13108
|
+
const offsetY = spanRect.top - divRect.top;
|
|
13109
|
+
document.body.removeChild(div);
|
|
13110
|
+
const x = taRect.left - parentRect.left + offsetX + spanRect.width / 2;
|
|
13111
|
+
const y = taRect.top - parentRect.top + offsetY - ta.scrollTop;
|
|
13112
|
+
return { x, y };
|
|
13113
|
+
}
|
|
11843
13114
|
|
|
11844
13115
|
exports.Accordion = Accordion;
|
|
11845
13116
|
exports.AccordionPanel = AccordionPanel;
|
|
@@ -11857,6 +13128,7 @@ exports.Callout = Callout;
|
|
|
11857
13128
|
exports.Card = Card;
|
|
11858
13129
|
exports.Carousel = Carousel;
|
|
11859
13130
|
exports.Chart = Chart;
|
|
13131
|
+
exports.ChatDrawer = ChatDrawer;
|
|
11860
13132
|
exports.Checkbox = Checkbox;
|
|
11861
13133
|
exports.CheckboxGroup = CheckboxGroup;
|
|
11862
13134
|
exports.ColorPicker = ColorPicker;
|
|
@@ -11877,10 +13149,13 @@ exports.FileUpload = FileUpload;
|
|
|
11877
13149
|
exports.Heading = Heading;
|
|
11878
13150
|
exports.Icon = Icon;
|
|
11879
13151
|
exports.Input = Input;
|
|
13152
|
+
exports.InputTag = InputTag;
|
|
11880
13153
|
exports.Kanban = Kanban;
|
|
13154
|
+
exports.MagicWand = MagicWand;
|
|
11881
13155
|
exports.Menu = Menu2;
|
|
11882
13156
|
exports.MobileMenu = MobileMenu;
|
|
11883
13157
|
exports.Modal = Modal;
|
|
13158
|
+
exports.MoodMeter = MoodMeter;
|
|
11884
13159
|
exports.MultiSwitch = MultiSwitch;
|
|
11885
13160
|
exports.Navbar = Navbar;
|
|
11886
13161
|
exports.OtpInput = OtpInput;
|
|
@@ -11890,7 +13165,9 @@ exports.Popover = Popover;
|
|
|
11890
13165
|
exports.Portal = Portal;
|
|
11891
13166
|
exports.Profile = Profile;
|
|
11892
13167
|
exports.Progress = Progress;
|
|
13168
|
+
exports.PromptInput = PromptInput;
|
|
11893
13169
|
exports.RadioGroup = RadioGroup;
|
|
13170
|
+
exports.ReasonTag = ReasonTag;
|
|
11894
13171
|
exports.SKIN_TONES = SKIN_TONES;
|
|
11895
13172
|
exports.Select = Select;
|
|
11896
13173
|
exports.Separator = Separator;
|
|
@@ -11910,8 +13187,11 @@ exports.TreeNav = TreeNav;
|
|
|
11910
13187
|
exports.applyTone = applyTone;
|
|
11911
13188
|
exports.cn = cn;
|
|
11912
13189
|
exports.configureIcons = configureIcons;
|
|
13190
|
+
exports.contentEditableAdapter = contentEditableAdapter;
|
|
13191
|
+
exports.controlledAdapter = controlledAdapter;
|
|
11913
13192
|
exports.find = find;
|
|
11914
13193
|
exports.hasSkinTones = hasSkinTones;
|
|
13194
|
+
exports.inputAdapter = inputAdapter;
|
|
11915
13195
|
exports.registerExtension = registerExtension;
|
|
11916
13196
|
exports.registerExtensions = registerExtensions;
|
|
11917
13197
|
exports.registerIconSet = registerIconSet;
|
|
@@ -11921,6 +13201,7 @@ exports.sanitizeHref = sanitizeHref;
|
|
|
11921
13201
|
exports.sanitizeHtml = sanitizeHtml;
|
|
11922
13202
|
exports.search = search;
|
|
11923
13203
|
exports.skinTones = skinTones;
|
|
13204
|
+
exports.textareaAdapter = textareaAdapter;
|
|
11924
13205
|
exports.useAccordion = useAccordion;
|
|
11925
13206
|
exports.useAccordionPanel = useAccordionPanel;
|
|
11926
13207
|
exports.useAccordionSection = useAccordionSection;
|