@particle-academy/react-fancy 3.0.1 → 3.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/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, round) {
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 (round) {
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,790 @@ 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
+ }) {
12161
+ const [text, setText] = react.useState("");
12162
+ const [attachments, setAttachments] = react.useState([]);
12163
+ const [picker, setPicker] = react.useState(null);
12164
+ const taRef = react.useRef(null);
12165
+ const [dragOver, setDragOver] = react.useState(false);
12166
+ const colors = mentionColor ?? DEFAULT_MENTION_COLOR;
12167
+ const tokens = react.useMemo(
12168
+ () => Math.ceil(text.length / Math.max(1, charsPerToken)),
12169
+ [text, charsPerToken]
12170
+ );
12171
+ const ratio = Math.min(1, tokens / budgetTokens);
12172
+ const meterColor = ratio < 0.6 ? "#10b981" : ratio < 0.85 ? "#f59e0b" : "#ef4444";
12173
+ const filteredCmds = react.useMemo(
12174
+ () => picker?.kind === "cmd" ? commands.filter(
12175
+ (c) => c.name.slice(1).toLowerCase().startsWith(picker.query.toLowerCase())
12176
+ ) : [],
12177
+ [picker, commands]
12178
+ );
12179
+ const filteredMentions = react.useMemo(
12180
+ () => picker?.kind === "mention" ? mentions.filter(
12181
+ (m) => m.name.toLowerCase().includes(picker.query.toLowerCase())
12182
+ ) : [],
12183
+ [picker, mentions]
12184
+ );
12185
+ const items = picker?.kind === "cmd" ? filteredCmds : picker?.kind === "mention" ? filteredMentions : [];
12186
+ react.useEffect(() => {
12187
+ const ta = taRef.current;
12188
+ if (!ta) return;
12189
+ ta.style.height = "auto";
12190
+ ta.style.height = Math.min(maxHeight, ta.scrollHeight) + "px";
12191
+ }, [text, maxHeight]);
12192
+ const updateText = (next, caret) => {
12193
+ setText(next);
12194
+ let triggerIdx = -1;
12195
+ let triggerKind = null;
12196
+ for (let i = caret - 1; i >= 0; i--) {
12197
+ const ch = next[i];
12198
+ if (ch === "@") {
12199
+ triggerKind = "mention";
12200
+ triggerIdx = i;
12201
+ break;
12202
+ }
12203
+ if (ch === "/" && (i === 0 || /\s/.test(next[i - 1] ?? ""))) {
12204
+ triggerKind = "cmd";
12205
+ triggerIdx = i;
12206
+ break;
12207
+ }
12208
+ if (/\s/.test(ch)) break;
12209
+ }
12210
+ if (triggerKind !== null && triggerIdx >= 0) {
12211
+ const query = next.slice(triggerIdx + 1, caret);
12212
+ setPicker({ kind: triggerKind, start: triggerIdx, query, cursor: 0 });
12213
+ } else {
12214
+ setPicker(null);
12215
+ }
12216
+ };
12217
+ const insertChoice = (i) => {
12218
+ if (!picker || items.length === 0) return;
12219
+ const choice = items[i] ?? items[0];
12220
+ const insert = picker.kind === "cmd" ? choice.name + " " : `@${choice.id} `;
12221
+ const before = text.slice(0, picker.start);
12222
+ const after = text.slice(picker.start + 1 + picker.query.length);
12223
+ const next = before + insert + after;
12224
+ setText(next);
12225
+ setPicker(null);
12226
+ requestAnimationFrame(() => {
12227
+ const ta = taRef.current;
12228
+ if (!ta) return;
12229
+ ta.focus();
12230
+ const pos = before.length + insert.length;
12231
+ ta.setSelectionRange(pos, pos);
12232
+ });
12233
+ };
12234
+ const submit = () => {
12235
+ if (!text.trim() && attachments.length === 0) return;
12236
+ onSubmit(text, attachments);
12237
+ setText("");
12238
+ setAttachments([]);
12239
+ setPicker(null);
12240
+ };
12241
+ const onKeyDown = (e) => {
12242
+ if (picker) {
12243
+ if (e.key === "ArrowDown") {
12244
+ e.preventDefault();
12245
+ setPicker(
12246
+ (p) => p ? { ...p, cursor: Math.min(items.length - 1, p.cursor + 1) } : p
12247
+ );
12248
+ return;
12249
+ }
12250
+ if (e.key === "ArrowUp") {
12251
+ e.preventDefault();
12252
+ setPicker((p) => p ? { ...p, cursor: Math.max(0, p.cursor - 1) } : p);
12253
+ return;
12254
+ }
12255
+ if (e.key === "Enter" && !e.metaKey && !e.ctrlKey && items.length > 0) {
12256
+ e.preventDefault();
12257
+ insertChoice(picker.cursor);
12258
+ return;
12259
+ }
12260
+ if (e.key === "Escape") {
12261
+ e.preventDefault();
12262
+ setPicker(null);
12263
+ return;
12264
+ }
12265
+ }
12266
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
12267
+ e.preventDefault();
12268
+ submit();
12269
+ }
12270
+ };
12271
+ const onDrop = (e) => {
12272
+ e.preventDefault();
12273
+ setDragOver(false);
12274
+ const files = Array.from(e.dataTransfer.files);
12275
+ setAttachments((cur) => [
12276
+ ...cur,
12277
+ ...files.map((f) => ({
12278
+ id: `${f.name}-${Date.now()}-${Math.random()}`,
12279
+ name: f.name,
12280
+ bytes: f.size
12281
+ }))
12282
+ ]);
12283
+ };
12284
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12285
+ "div",
12286
+ {
12287
+ onDragOver: (e) => {
12288
+ e.preventDefault();
12289
+ setDragOver(true);
12290
+ },
12291
+ onDragLeave: () => setDragOver(false),
12292
+ onDrop,
12293
+ 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`,
12294
+ children: [
12295
+ 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(
12296
+ "span",
12297
+ {
12298
+ className: "inline-flex items-center gap-1.5 rounded-md bg-zinc-100 px-2 py-0.5 text-[11px] dark:bg-zinc-800",
12299
+ children: [
12300
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u{1F4CE}" }),
12301
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono", children: a.name }),
12302
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-zinc-400", children: fmtSize(a.bytes) }),
12303
+ /* @__PURE__ */ jsxRuntime.jsx(
12304
+ "button",
12305
+ {
12306
+ onClick: () => setAttachments((cur) => cur.filter((x) => x.id !== a.id)),
12307
+ className: "opacity-50 hover:opacity-100",
12308
+ "aria-label": "Remove attachment",
12309
+ children: "\xD7"
12310
+ }
12311
+ )
12312
+ ]
12313
+ },
12314
+ a.id
12315
+ )) }),
12316
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
12317
+ /* @__PURE__ */ jsxRuntime.jsx(
12318
+ "textarea",
12319
+ {
12320
+ ref: taRef,
12321
+ value: text,
12322
+ onChange: (e) => updateText(e.target.value, e.target.selectionStart),
12323
+ onKeyDown,
12324
+ placeholder,
12325
+ spellCheck: false,
12326
+ className: "block w-full resize-none bg-transparent px-3 py-2.5 text-[14px] leading-relaxed outline-none placeholder:text-zinc-400",
12327
+ rows: 3
12328
+ }
12329
+ ),
12330
+ 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: [
12331
+ /* @__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: [
12332
+ picker.kind === "cmd" ? "Commands" : "Mention",
12333
+ " \xB7 ",
12334
+ items.length
12335
+ ] }),
12336
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "max-h-56 overflow-y-auto", children: items.map((item, i) => {
12337
+ const active = i === picker.cursor;
12338
+ if (picker.kind === "cmd") {
12339
+ const c = item;
12340
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12341
+ "li",
12342
+ {
12343
+ onMouseDown: (e) => {
12344
+ e.preventDefault();
12345
+ insertChoice(i);
12346
+ },
12347
+ onMouseEnter: () => setPicker((p) => p ? { ...p, cursor: i } : p),
12348
+ 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"}`,
12349
+ children: [
12350
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "font-mono font-medium text-violet-700 dark:text-violet-300", children: c.name }),
12351
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[11px] text-zinc-500", children: c.hint })
12352
+ ]
12353
+ },
12354
+ c.name
12355
+ );
12356
+ }
12357
+ const m = item;
12358
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12359
+ "li",
12360
+ {
12361
+ onMouseDown: (e) => {
12362
+ e.preventDefault();
12363
+ insertChoice(i);
12364
+ },
12365
+ onMouseEnter: () => setPicker((p) => p ? { ...p, cursor: i } : p),
12366
+ 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"}`,
12367
+ children: [
12368
+ /* @__PURE__ */ jsxRuntime.jsx(
12369
+ "span",
12370
+ {
12371
+ className: "h-2 w-2 rounded-full",
12372
+ style: { backgroundColor: colors[m.kind] ?? "#71717a" }
12373
+ }
12374
+ ),
12375
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: m.name }),
12376
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-[10px] uppercase tracking-wider text-zinc-400", children: m.kind })
12377
+ ]
12378
+ },
12379
+ m.id
12380
+ );
12381
+ }) })
12382
+ ] })
12383
+ ] }),
12384
+ /* @__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: [
12385
+ /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { content: "Drop files here, or click", children: /* @__PURE__ */ jsxRuntime.jsx(
12386
+ Action,
12387
+ {
12388
+ variant: "ghost",
12389
+ size: "sm",
12390
+ onClick: () => {
12391
+ },
12392
+ children: "\u{1F4CE} attach"
12393
+ }
12394
+ ) }),
12395
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-2 flex items-center gap-1.5", children: [
12396
+ /* @__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(
12397
+ "div",
12398
+ {
12399
+ className: "h-full rounded-full transition-all",
12400
+ style: { width: `${ratio * 100}%`, backgroundColor: meterColor }
12401
+ }
12402
+ ) }),
12403
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-[11px]", style: { color: meterColor }, children: [
12404
+ fmtTokens(tokens),
12405
+ " / ",
12406
+ fmtTokens(budgetTokens)
12407
+ ] })
12408
+ ] }),
12409
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-2", children: [
12410
+ showHint && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden text-[10px] text-zinc-500 sm:inline", children: [
12411
+ /* @__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" }),
12412
+ " ",
12413
+ "+",
12414
+ " ",
12415
+ /* @__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" }),
12416
+ " ",
12417
+ "to send"
12418
+ ] }),
12419
+ /* @__PURE__ */ jsxRuntime.jsx(Action, { color: "violet", size: "sm", onClick: submit, children: "send \u2192" })
12420
+ ] })
12421
+ ] })
12422
+ ]
12423
+ }
12424
+ );
12425
+ }
12426
+ function fmtTokens(n) {
12427
+ if (n < 1e3) return `${n}`;
12428
+ return `${(n / 1e3).toFixed(1)}k`;
12429
+ }
12430
+ function fmtSize(b) {
12431
+ if (b < 1024) return `${b} B`;
12432
+ if (b < 1024 * 1024) return `${(b / 1024).toFixed(0)} KB`;
12433
+ return `${(b / 1024 / 1024).toFixed(1)} MB`;
12434
+ }
12435
+ function MagicWand({
12436
+ value,
12437
+ onValueChange,
12438
+ actions,
12439
+ appearance = "floating",
12440
+ autoHide = true,
12441
+ rows = 6,
12442
+ placeholder,
12443
+ onAction
12444
+ }) {
12445
+ const taRef = react.useRef(null);
12446
+ const wandRef = react.useRef(null);
12447
+ const [sel, setSel] = react.useState(null);
12448
+ const [pos, setPos] = react.useState(null);
12449
+ const [busy, setBusy] = react.useState(null);
12450
+ const measureSelection = react.useCallback(() => {
12451
+ const ta = taRef.current;
12452
+ if (!ta) return;
12453
+ const start = ta.selectionStart ?? 0;
12454
+ const end = ta.selectionEnd ?? 0;
12455
+ if (start === end) {
12456
+ setSel(null);
12457
+ setPos(null);
12458
+ return;
12459
+ }
12460
+ const text = ta.value.slice(start, end);
12461
+ setSel({ start, end, text });
12462
+ const rect = caretRect(ta, start, end);
12463
+ if (rect) setPos({ x: rect.x, y: rect.y });
12464
+ }, []);
12465
+ react.useEffect(() => {
12466
+ if (!autoHide) return;
12467
+ const onScroll = () => {
12468
+ setSel(null);
12469
+ setPos(null);
12470
+ };
12471
+ window.addEventListener("scroll", onScroll, true);
12472
+ return () => window.removeEventListener("scroll", onScroll, true);
12473
+ }, [autoHide]);
12474
+ react.useEffect(() => {
12475
+ if (!autoHide) return;
12476
+ const onDocMouseDown = (e) => {
12477
+ if (wandRef.current?.contains(e.target)) return;
12478
+ if (taRef.current?.contains(e.target)) return;
12479
+ setSel(null);
12480
+ setPos(null);
12481
+ };
12482
+ document.addEventListener("mousedown", onDocMouseDown);
12483
+ return () => document.removeEventListener("mousedown", onDocMouseDown);
12484
+ }, [autoHide]);
12485
+ const handleAction = async (action) => {
12486
+ if (!sel) return;
12487
+ setBusy(action.id);
12488
+ try {
12489
+ const replacement = await action.run(sel.text, sel);
12490
+ const next = value.slice(0, sel.start) + replacement + value.slice(sel.end);
12491
+ onValueChange(next);
12492
+ onAction?.(action, sel, replacement);
12493
+ } finally {
12494
+ setBusy(null);
12495
+ setSel(null);
12496
+ setPos(null);
12497
+ }
12498
+ };
12499
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
12500
+ /* @__PURE__ */ jsxRuntime.jsx(
12501
+ Textarea,
12502
+ {
12503
+ ref: taRef,
12504
+ value,
12505
+ onValueChange,
12506
+ onSelect: measureSelection,
12507
+ onKeyUp: measureSelection,
12508
+ onMouseUp: measureSelection,
12509
+ rows,
12510
+ placeholder
12511
+ }
12512
+ ),
12513
+ sel && pos && /* @__PURE__ */ jsxRuntime.jsx(
12514
+ Wand,
12515
+ {
12516
+ ref: wandRef,
12517
+ pos,
12518
+ actions,
12519
+ appearance,
12520
+ busy,
12521
+ onAction: handleAction,
12522
+ selectionLength: sel.text.length
12523
+ }
12524
+ )
12525
+ ] });
12526
+ }
12527
+ function Wand({
12528
+ ref,
12529
+ pos,
12530
+ actions,
12531
+ appearance,
12532
+ busy,
12533
+ onAction,
12534
+ selectionLength
12535
+ }) {
12536
+ return /* @__PURE__ */ jsxRuntime.jsxs(
12537
+ "div",
12538
+ {
12539
+ ref,
12540
+ className: "absolute z-20 -translate-x-1/2 -translate-y-full",
12541
+ style: { left: pos.x, top: pos.y - 6 },
12542
+ children: [
12543
+ /* @__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: [
12544
+ /* @__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: [
12545
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2726" }),
12546
+ appearance === "floating" && /* @__PURE__ */ jsxRuntime.jsxs(Badge, { color: "violet", children: [
12547
+ selectionLength,
12548
+ " chars"
12549
+ ] })
12550
+ ] }),
12551
+ actions.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
12552
+ Action,
12553
+ {
12554
+ size: "sm",
12555
+ variant: "ghost",
12556
+ onClick: () => onAction(a),
12557
+ disabled: busy !== null,
12558
+ title: a.hint,
12559
+ children: [
12560
+ busy === a.id ? "\u2026" : appearance === "pill" ? a.label[0] : a.label,
12561
+ 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 })
12562
+ ]
12563
+ },
12564
+ a.id
12565
+ ))
12566
+ ] }),
12567
+ /* @__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" }) })
12568
+ ]
12569
+ }
12570
+ );
12571
+ }
12572
+ function caretRect(ta, start, end) {
12573
+ const div = document.createElement("div");
12574
+ const style = getComputedStyle(ta);
12575
+ const props = [
12576
+ "boxSizing",
12577
+ "width",
12578
+ "height",
12579
+ "overflowX",
12580
+ "overflowY",
12581
+ "borderTopWidth",
12582
+ "borderRightWidth",
12583
+ "borderBottomWidth",
12584
+ "borderLeftWidth",
12585
+ "paddingTop",
12586
+ "paddingRight",
12587
+ "paddingBottom",
12588
+ "paddingLeft",
12589
+ "fontStyle",
12590
+ "fontVariant",
12591
+ "fontWeight",
12592
+ "fontStretch",
12593
+ "fontSize",
12594
+ "fontSizeAdjust",
12595
+ "lineHeight",
12596
+ "fontFamily",
12597
+ "textAlign",
12598
+ "textTransform",
12599
+ "textIndent",
12600
+ "letterSpacing",
12601
+ "wordSpacing",
12602
+ "tabSize"
12603
+ ];
12604
+ for (const p of props) div.style[p] = style[p];
12605
+ div.style.position = "absolute";
12606
+ div.style.top = "-9999px";
12607
+ div.style.left = "-9999px";
12608
+ div.style.whiteSpace = "pre-wrap";
12609
+ div.style.wordWrap = "break-word";
12610
+ const value = ta.value;
12611
+ div.textContent = value.substring(0, start);
12612
+ const startSpan = document.createElement("span");
12613
+ startSpan.textContent = value.substring(start, end) || ".";
12614
+ div.appendChild(startSpan);
12615
+ document.body.appendChild(div);
12616
+ const taRect = ta.getBoundingClientRect();
12617
+ const parentRect = ta.offsetParent?.getBoundingClientRect() ?? taRect;
12618
+ const spanRect = startSpan.getBoundingClientRect();
12619
+ const divRect = div.getBoundingClientRect();
12620
+ const offsetX = spanRect.left - divRect.left;
12621
+ const offsetY = spanRect.top - divRect.top;
12622
+ document.body.removeChild(div);
12623
+ const x = taRect.left - parentRect.left + offsetX + spanRect.width / 2;
12624
+ const y = taRect.top - parentRect.top + offsetY - ta.scrollTop;
12625
+ return { x, y };
12626
+ }
11843
12627
 
11844
12628
  exports.Accordion = Accordion;
11845
12629
  exports.AccordionPanel = AccordionPanel;
@@ -11878,9 +12662,11 @@ exports.Heading = Heading;
11878
12662
  exports.Icon = Icon;
11879
12663
  exports.Input = Input;
11880
12664
  exports.Kanban = Kanban;
12665
+ exports.MagicWand = MagicWand;
11881
12666
  exports.Menu = Menu2;
11882
12667
  exports.MobileMenu = MobileMenu;
11883
12668
  exports.Modal = Modal;
12669
+ exports.MoodMeter = MoodMeter;
11884
12670
  exports.MultiSwitch = MultiSwitch;
11885
12671
  exports.Navbar = Navbar;
11886
12672
  exports.OtpInput = OtpInput;
@@ -11890,7 +12676,9 @@ exports.Popover = Popover;
11890
12676
  exports.Portal = Portal;
11891
12677
  exports.Profile = Profile;
11892
12678
  exports.Progress = Progress;
12679
+ exports.PromptInput = PromptInput;
11893
12680
  exports.RadioGroup = RadioGroup;
12681
+ exports.ReasonTag = ReasonTag;
11894
12682
  exports.SKIN_TONES = SKIN_TONES;
11895
12683
  exports.Select = Select;
11896
12684
  exports.Separator = Separator;