@refraction-ui/react 0.10.0 → 0.12.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 +271 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +271 -171
- package/dist/index.js.map +1 -1
- package/dist/internal/conversation/index.d.cts +12 -2
- package/dist/internal/conversation/index.d.ts +12 -2
- package/dist/internal/react-conversation/index.d.cts +5 -1
- package/dist/internal/react-conversation/index.d.ts +5 -1
- package/dist/internal/react-cookie-consent/index.d.cts +1 -2
- package/dist/internal/react-cookie-consent/index.d.ts +1 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -32660,6 +32660,7 @@ function createConversation(config = {}) {
|
|
|
32660
32660
|
const messagesByConv = /* @__PURE__ */ new Map();
|
|
32661
32661
|
let activeConversationId = config.activeConversationId ?? null;
|
|
32662
32662
|
let openThreadRootId = null;
|
|
32663
|
+
let replyTarget = null;
|
|
32663
32664
|
let status = "idle";
|
|
32664
32665
|
let error = null;
|
|
32665
32666
|
let abortController = null;
|
|
@@ -32681,6 +32682,7 @@ function createConversation(config = {}) {
|
|
|
32681
32682
|
activeConversationId,
|
|
32682
32683
|
messages: activeConversationId ? messagesByConv.get(activeConversationId) ?? [] : [],
|
|
32683
32684
|
openThreadRootId,
|
|
32685
|
+
replyTarget,
|
|
32684
32686
|
threadingMode,
|
|
32685
32687
|
status,
|
|
32686
32688
|
error
|
|
@@ -32777,6 +32779,7 @@ function createConversation(config = {}) {
|
|
|
32777
32779
|
newConversation(opts) {
|
|
32778
32780
|
const conversation = createConversationInternal(opts ?? {});
|
|
32779
32781
|
openThreadRootId = null;
|
|
32782
|
+
replyTarget = null;
|
|
32780
32783
|
emit2();
|
|
32781
32784
|
return conversation;
|
|
32782
32785
|
},
|
|
@@ -32784,6 +32787,7 @@ function createConversation(config = {}) {
|
|
|
32784
32787
|
if (!conversations.has(conversationId) || activeConversationId === conversationId) return;
|
|
32785
32788
|
activeConversationId = conversationId;
|
|
32786
32789
|
openThreadRootId = null;
|
|
32790
|
+
replyTarget = null;
|
|
32787
32791
|
emit2();
|
|
32788
32792
|
},
|
|
32789
32793
|
deleteConversation(conversationId) {
|
|
@@ -32793,6 +32797,7 @@ function createConversation(config = {}) {
|
|
|
32793
32797
|
if (activeConversationId === conversationId) {
|
|
32794
32798
|
activeConversationId = orderedConversations()[0]?.id ?? null;
|
|
32795
32799
|
openThreadRootId = null;
|
|
32800
|
+
replyTarget = null;
|
|
32796
32801
|
}
|
|
32797
32802
|
emit2();
|
|
32798
32803
|
},
|
|
@@ -32830,6 +32835,7 @@ function createConversation(config = {}) {
|
|
|
32830
32835
|
const next = list.filter((m2) => !removeIds.has(m2.id));
|
|
32831
32836
|
messagesByConv.set(activeConversationId, next);
|
|
32832
32837
|
if (openThreadRootId && removeIds.has(openThreadRootId)) openThreadRootId = null;
|
|
32838
|
+
if (replyTarget && removeIds.has(replyTarget)) replyTarget = openThreadRootId;
|
|
32833
32839
|
emit2();
|
|
32834
32840
|
},
|
|
32835
32841
|
react(messageId, emoji) {
|
|
@@ -32860,6 +32866,7 @@ function createConversation(config = {}) {
|
|
|
32860
32866
|
const conversationId = ensureActiveConversation(opts);
|
|
32861
32867
|
const list = messagesByConv.get(conversationId);
|
|
32862
32868
|
const parentId = opts?.replyTo ? rootIdOf(list, opts.replyTo) : void 0;
|
|
32869
|
+
const replyToId = opts?.replyTo;
|
|
32863
32870
|
const isFirstRoot = !parentId && selectRoots(list).length === 0;
|
|
32864
32871
|
const userMsg = {
|
|
32865
32872
|
id: generateId("rfr-msg"),
|
|
@@ -32870,6 +32877,7 @@ function createConversation(config = {}) {
|
|
|
32870
32877
|
timestamp: /* @__PURE__ */ new Date(),
|
|
32871
32878
|
status: "sent",
|
|
32872
32879
|
parentId,
|
|
32880
|
+
replyToId,
|
|
32873
32881
|
attachments: opts?.attachments,
|
|
32874
32882
|
metadata: opts?.metadata
|
|
32875
32883
|
};
|
|
@@ -32926,11 +32934,20 @@ function createConversation(config = {}) {
|
|
|
32926
32934
|
},
|
|
32927
32935
|
openThread(rootId) {
|
|
32928
32936
|
openThreadRootId = rootId;
|
|
32937
|
+
replyTarget = rootId;
|
|
32938
|
+
emit2();
|
|
32939
|
+
},
|
|
32940
|
+
replyTo(messageId) {
|
|
32941
|
+
if (!activeConversationId) return;
|
|
32942
|
+
const list = messagesByConv.get(activeConversationId);
|
|
32943
|
+
openThreadRootId = rootIdOf(list, messageId);
|
|
32944
|
+
replyTarget = messageId;
|
|
32929
32945
|
emit2();
|
|
32930
32946
|
},
|
|
32931
32947
|
closeThread() {
|
|
32932
|
-
if (openThreadRootId === null) return;
|
|
32948
|
+
if (openThreadRootId === null && replyTarget === null) return;
|
|
32933
32949
|
openThreadRootId = null;
|
|
32950
|
+
replyTarget = null;
|
|
32934
32951
|
emit2();
|
|
32935
32952
|
},
|
|
32936
32953
|
setThreadingMode(mode) {
|
|
@@ -33274,6 +33291,7 @@ function useConversation(config) {
|
|
|
33274
33291
|
retryLast: api.retryLast,
|
|
33275
33292
|
stop: api.stop,
|
|
33276
33293
|
openThread: api.openThread,
|
|
33294
|
+
replyTo: api.replyTo,
|
|
33277
33295
|
closeThread: api.closeThread,
|
|
33278
33296
|
setThreadingMode: api.setThreadingMode
|
|
33279
33297
|
};
|
|
@@ -33338,6 +33356,8 @@ function Composer({
|
|
|
33338
33356
|
toolbar = true,
|
|
33339
33357
|
emoji = true,
|
|
33340
33358
|
attachments = true,
|
|
33359
|
+
error,
|
|
33360
|
+
onRetry,
|
|
33341
33361
|
onSubmit,
|
|
33342
33362
|
onStop,
|
|
33343
33363
|
onSlashCommand,
|
|
@@ -33478,148 +33498,154 @@ ${sel}
|
|
|
33478
33498
|
submit();
|
|
33479
33499
|
}
|
|
33480
33500
|
}
|
|
33501
|
+
const iconBtn = "flex h-8 w-8 items-center justify-center rounded-lg text-muted-foreground hover:bg-accent hover:text-foreground";
|
|
33481
33502
|
const toolbarBtn = (label, title, kind) => h(
|
|
33482
33503
|
"button",
|
|
33483
33504
|
{
|
|
33484
33505
|
key: kind,
|
|
33485
33506
|
type: "button",
|
|
33486
33507
|
title,
|
|
33487
|
-
className: "
|
|
33508
|
+
className: cn(iconBtn, "text-xs font-medium"),
|
|
33488
33509
|
onMouseDown: (e2) => e2.preventDefault(),
|
|
33489
33510
|
// keep textarea selection
|
|
33490
33511
|
onClick: () => format(kind)
|
|
33491
33512
|
},
|
|
33492
33513
|
label
|
|
33493
33514
|
);
|
|
33494
|
-
|
|
33515
|
+
const menu = menuOpen ? h(
|
|
33495
33516
|
"div",
|
|
33496
|
-
{
|
|
33497
|
-
|
|
33498
|
-
|
|
33517
|
+
{
|
|
33518
|
+
className: "absolute bottom-full left-0 z-20 mb-2 w-72 overflow-hidden rounded-xl border border-border bg-popover shadow-lg",
|
|
33519
|
+
role: "listbox"
|
|
33520
|
+
},
|
|
33521
|
+
h(
|
|
33499
33522
|
"div",
|
|
33500
|
-
{ className: "
|
|
33501
|
-
|
|
33502
|
-
|
|
33503
|
-
|
|
33504
|
-
|
|
33505
|
-
|
|
33506
|
-
|
|
33507
|
-
|
|
33508
|
-
|
|
33509
|
-
|
|
33510
|
-
|
|
33511
|
-
|
|
33512
|
-
|
|
33513
|
-
"
|
|
33514
|
-
)
|
|
33515
|
-
|
|
33523
|
+
{ className: "border-b border-border px-3 py-1.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground" },
|
|
33524
|
+
trigger?.type === "/" ? "Commands" : trigger?.type === "@" ? "Mentions" : "Emoji"
|
|
33525
|
+
),
|
|
33526
|
+
...items.map(
|
|
33527
|
+
(it2, i2) => h(
|
|
33528
|
+
"button",
|
|
33529
|
+
{
|
|
33530
|
+
key: it2.key,
|
|
33531
|
+
type: "button",
|
|
33532
|
+
role: "option",
|
|
33533
|
+
"aria-selected": i2 === active,
|
|
33534
|
+
className: cn(
|
|
33535
|
+
"flex w-full items-center gap-2 px-3 py-2 text-left text-sm",
|
|
33536
|
+
i2 === active ? "bg-accent" : "hover:bg-accent/50"
|
|
33537
|
+
),
|
|
33538
|
+
onMouseEnter: () => setActive(i2),
|
|
33539
|
+
onMouseDown: (e2) => e2.preventDefault(),
|
|
33540
|
+
onClick: () => selectItem(i2)
|
|
33541
|
+
},
|
|
33542
|
+
it2.icon ? h("span", { className: "w-4 text-center text-muted-foreground" }, it2.icon) : null,
|
|
33543
|
+
h("span", { className: "flex-1 truncate" }, it2.primary),
|
|
33544
|
+
it2.secondary ? h("span", { className: "truncate text-xs text-muted-foreground" }, it2.secondary) : null
|
|
33516
33545
|
)
|
|
33517
|
-
)
|
|
33518
|
-
|
|
33519
|
-
|
|
33520
|
-
|
|
33521
|
-
|
|
33522
|
-
toolbarBtn("B", "Bold (\u2318B)", "bold"),
|
|
33523
|
-
toolbarBtn("\u{1D456}", "Italic (\u2318I)", "italic"),
|
|
33524
|
-
toolbarBtn("</>", "Code (\u2318E)", "code"),
|
|
33525
|
-
toolbarBtn("\u{1F517}", "Link (\u2318K)", "link"),
|
|
33526
|
-
toolbarBtn("\u275D", "Quote", "quote"),
|
|
33527
|
-
toolbarBtn("\u2022", "Bulleted list", "ul"),
|
|
33528
|
-
toolbarBtn("1.", "Numbered list", "ol")
|
|
33529
|
-
) : null,
|
|
33530
|
-
// input row (relative for the popup menu)
|
|
33546
|
+
)
|
|
33547
|
+
) : null;
|
|
33548
|
+
return h(
|
|
33549
|
+
"div",
|
|
33550
|
+
{ className: "p-3" },
|
|
33531
33551
|
h(
|
|
33532
33552
|
"div",
|
|
33533
|
-
{ className: "relative
|
|
33534
|
-
|
|
33553
|
+
{ className: "relative" },
|
|
33554
|
+
menu,
|
|
33555
|
+
// unified input card
|
|
33556
|
+
h(
|
|
33535
33557
|
"div",
|
|
33536
33558
|
{
|
|
33537
|
-
className: "
|
|
33538
|
-
role: "listbox"
|
|
33559
|
+
className: "overflow-hidden rounded-2xl border border-border bg-background transition focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/40"
|
|
33539
33560
|
},
|
|
33561
|
+
// error banner
|
|
33562
|
+
error ? h(
|
|
33563
|
+
"div",
|
|
33564
|
+
{ className: "flex items-center gap-2 border-b border-border bg-destructive/5 px-3 py-2 text-xs text-destructive", role: "alert" },
|
|
33565
|
+
h("span", { className: "flex-1 truncate" }, error),
|
|
33566
|
+
onRetry ? h("button", { type: "button", className: "font-medium underline", onClick: () => onRetry() }, "Retry") : null
|
|
33567
|
+
) : null,
|
|
33568
|
+
// attachment chips
|
|
33569
|
+
pending.length > 0 ? h(
|
|
33570
|
+
"div",
|
|
33571
|
+
{ className: "flex flex-wrap gap-2 px-3 pt-3" },
|
|
33572
|
+
...pending.map(
|
|
33573
|
+
(a2) => h(
|
|
33574
|
+
"span",
|
|
33575
|
+
{ key: a2.id, className: "inline-flex items-center gap-1 rounded-md bg-muted px-2 py-0.5 text-xs" },
|
|
33576
|
+
a2.name,
|
|
33577
|
+
h(
|
|
33578
|
+
"button",
|
|
33579
|
+
{ type: "button", className: "text-muted-foreground hover:text-destructive", onClick: () => setPending((p2) => p2.filter((x2) => x2.id !== a2.id)) },
|
|
33580
|
+
"\u2715"
|
|
33581
|
+
)
|
|
33582
|
+
)
|
|
33583
|
+
)
|
|
33584
|
+
) : null,
|
|
33585
|
+
// textarea (borderless)
|
|
33586
|
+
h("textarea", {
|
|
33587
|
+
ref,
|
|
33588
|
+
className: "block max-h-40 w-full resize-none bg-transparent px-3.5 py-3 text-sm placeholder:text-muted-foreground focus:outline-none",
|
|
33589
|
+
rows: 1,
|
|
33590
|
+
value,
|
|
33591
|
+
placeholder,
|
|
33592
|
+
autoFocus,
|
|
33593
|
+
"aria-label": "Message",
|
|
33594
|
+
onChange: (e2) => syncFromTextarea(e2.target),
|
|
33595
|
+
onClick: (e2) => syncFromTextarea(e2.currentTarget),
|
|
33596
|
+
onKeyUp: (e2) => syncFromTextarea(e2.currentTarget),
|
|
33597
|
+
onKeyDown,
|
|
33598
|
+
onBlur: () => setTimeout(() => setTrigger(null), 120)
|
|
33599
|
+
}),
|
|
33600
|
+
// bottom action bar
|
|
33540
33601
|
h(
|
|
33541
33602
|
"div",
|
|
33542
|
-
{ className: "
|
|
33543
|
-
|
|
33544
|
-
|
|
33545
|
-
|
|
33546
|
-
|
|
33603
|
+
{ className: "flex items-center gap-0.5 px-2 pb-2" },
|
|
33604
|
+
attachments ? h(
|
|
33605
|
+
React11__namespace.Fragment,
|
|
33606
|
+
null,
|
|
33607
|
+
h("input", {
|
|
33608
|
+
ref: fileRef,
|
|
33609
|
+
type: "file",
|
|
33610
|
+
accept: "image/*",
|
|
33611
|
+
multiple: true,
|
|
33612
|
+
className: "hidden",
|
|
33613
|
+
onChange: (e2) => {
|
|
33614
|
+
onFiles(e2.target.files);
|
|
33615
|
+
e2.target.value = "";
|
|
33616
|
+
}
|
|
33617
|
+
}),
|
|
33618
|
+
h("button", { type: "button", className: iconBtn, "aria-label": "Attach image or GIF", onClick: () => fileRef.current?.click() }, "\u{1F4CE}")
|
|
33619
|
+
) : null,
|
|
33620
|
+
attachments && toolbar ? h("span", { className: "mx-1 h-5 w-px bg-border" }) : null,
|
|
33621
|
+
toolbar ? h(
|
|
33622
|
+
React11__namespace.Fragment,
|
|
33623
|
+
null,
|
|
33624
|
+
toolbarBtn("B", "Bold (\u2318B)", "bold"),
|
|
33625
|
+
toolbarBtn("\u{1D456}", "Italic (\u2318I)", "italic"),
|
|
33626
|
+
toolbarBtn("</>", "Code (\u2318E)", "code"),
|
|
33627
|
+
toolbarBtn("\u{1F517}", "Link (\u2318K)", "link"),
|
|
33628
|
+
toolbarBtn("\u275D", "Quote", "quote"),
|
|
33629
|
+
toolbarBtn("\u2022", "Bulleted list", "ul"),
|
|
33630
|
+
toolbarBtn("1.", "Numbered list", "ol")
|
|
33631
|
+
) : null,
|
|
33632
|
+
h("div", { className: "flex-1" }),
|
|
33633
|
+
busy ? h(
|
|
33634
|
+
"button",
|
|
33635
|
+
{ type: "button", "aria-label": "Stop", className: "flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-primary-foreground", onClick: () => onStop?.() },
|
|
33636
|
+
"\u25A0"
|
|
33637
|
+
) : h(
|
|
33547
33638
|
"button",
|
|
33548
33639
|
{
|
|
33549
|
-
key: it2.key,
|
|
33550
33640
|
type: "button",
|
|
33551
|
-
|
|
33552
|
-
"
|
|
33553
|
-
|
|
33554
|
-
|
|
33555
|
-
i2 === active ? "bg-accent" : "hover:bg-accent/50"
|
|
33556
|
-
),
|
|
33557
|
-
onMouseEnter: () => setActive(i2),
|
|
33558
|
-
onMouseDown: (e2) => e2.preventDefault(),
|
|
33559
|
-
onClick: () => selectItem(i2)
|
|
33641
|
+
"aria-label": "Send",
|
|
33642
|
+
className: "flex h-8 w-8 items-center justify-center rounded-lg bg-primary text-base font-semibold text-primary-foreground transition disabled:opacity-40",
|
|
33643
|
+
disabled: !value.trim() && pending.length === 0,
|
|
33644
|
+
onClick: submit
|
|
33560
33645
|
},
|
|
33561
|
-
|
|
33562
|
-
h("span", { className: "flex-1 truncate" }, it2.primary),
|
|
33563
|
-
it2.secondary ? h("span", { className: "truncate text-xs text-muted-foreground" }, it2.secondary) : null
|
|
33646
|
+
"\u2191"
|
|
33564
33647
|
)
|
|
33565
33648
|
)
|
|
33566
|
-
) : null,
|
|
33567
|
-
attachments ? h(
|
|
33568
|
-
React11__namespace.Fragment,
|
|
33569
|
-
null,
|
|
33570
|
-
h("input", {
|
|
33571
|
-
ref: fileRef,
|
|
33572
|
-
type: "file",
|
|
33573
|
-
accept: "image/*",
|
|
33574
|
-
multiple: true,
|
|
33575
|
-
className: "hidden",
|
|
33576
|
-
onChange: (e2) => {
|
|
33577
|
-
onFiles(e2.target.files);
|
|
33578
|
-
e2.target.value = "";
|
|
33579
|
-
}
|
|
33580
|
-
}),
|
|
33581
|
-
h(
|
|
33582
|
-
"button",
|
|
33583
|
-
{
|
|
33584
|
-
type: "button",
|
|
33585
|
-
className: "rounded-md border border-border px-2 py-2 text-sm hover:bg-accent",
|
|
33586
|
-
"aria-label": "Attach image or GIF",
|
|
33587
|
-
onClick: () => fileRef.current?.click()
|
|
33588
|
-
},
|
|
33589
|
-
"\u{1F4CE}"
|
|
33590
|
-
)
|
|
33591
|
-
) : null,
|
|
33592
|
-
h("textarea", {
|
|
33593
|
-
ref,
|
|
33594
|
-
className: "max-h-40 flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary",
|
|
33595
|
-
rows: 1,
|
|
33596
|
-
value,
|
|
33597
|
-
placeholder,
|
|
33598
|
-
autoFocus,
|
|
33599
|
-
"aria-label": "Message",
|
|
33600
|
-
onChange: (e2) => syncFromTextarea(e2.target),
|
|
33601
|
-
onClick: (e2) => syncFromTextarea(e2.currentTarget),
|
|
33602
|
-
onKeyUp: (e2) => syncFromTextarea(e2.currentTarget),
|
|
33603
|
-
onKeyDown,
|
|
33604
|
-
onBlur: () => setTimeout(() => setTrigger(null), 120)
|
|
33605
|
-
}),
|
|
33606
|
-
busy ? h(
|
|
33607
|
-
"button",
|
|
33608
|
-
{
|
|
33609
|
-
type: "button",
|
|
33610
|
-
className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground",
|
|
33611
|
-
onClick: () => onStop?.()
|
|
33612
|
-
},
|
|
33613
|
-
"Stop"
|
|
33614
|
-
) : h(
|
|
33615
|
-
"button",
|
|
33616
|
-
{
|
|
33617
|
-
type: "button",
|
|
33618
|
-
className: "rounded-md bg-primary px-3 py-2 text-sm font-medium text-primary-foreground disabled:opacity-50",
|
|
33619
|
-
disabled: !value.trim() && pending.length === 0,
|
|
33620
|
-
onClick: submit
|
|
33621
|
-
},
|
|
33622
|
-
"Send"
|
|
33623
33649
|
)
|
|
33624
33650
|
)
|
|
33625
33651
|
);
|
|
@@ -33736,7 +33762,7 @@ function HoverActions({
|
|
|
33736
33762
|
onToggleEmojis,
|
|
33737
33763
|
align
|
|
33738
33764
|
}) {
|
|
33739
|
-
const {
|
|
33765
|
+
const { replyTo, deleteMessage } = conversation;
|
|
33740
33766
|
return h2(
|
|
33741
33767
|
"div",
|
|
33742
33768
|
{
|
|
@@ -33745,7 +33771,8 @@ function HoverActions({
|
|
|
33745
33771
|
align === "end" && "justify-end"
|
|
33746
33772
|
)
|
|
33747
33773
|
},
|
|
33748
|
-
|
|
33774
|
+
// Reply targets this specific message but groups under the originating root.
|
|
33775
|
+
h2("button", { type: "button", className: "hover:text-foreground", onClick: () => replyTo(message.id) }, "Reply"),
|
|
33749
33776
|
h2("button", { type: "button", className: "hover:text-foreground", onClick: onToggleEmojis }, "React"),
|
|
33750
33777
|
isOwn ? h2("button", { type: "button", className: "hover:text-foreground", onClick: onEdit }, "Edit") : null,
|
|
33751
33778
|
isOwn ? h2("button", { type: "button", className: "hover:text-destructive", onClick: () => deleteMessage(message.id) }, "Delete") : null
|
|
@@ -33807,7 +33834,7 @@ function MessageRow({
|
|
|
33807
33834
|
const inner = h2(
|
|
33808
33835
|
React11__namespace.Fragment,
|
|
33809
33836
|
null,
|
|
33810
|
-
quotedParent ? h2(QuotedParent, { parent: quotedParent, onClick: () => openThread(quotedParent.id) }) : null,
|
|
33837
|
+
quotedParent ? h2(QuotedParent, { parent: quotedParent, onClick: () => openThread(rootIdOf(state.messages, quotedParent.id)) }) : null,
|
|
33811
33838
|
editing ? h2(EditField, { message, conversation, onDone: () => setEditing(false) }) : isUser ? h2("div", { className: "inline-block rounded-2xl rounded-br-sm bg-primary/10 px-3 py-2 text-left" }, h2(MessageBody, { message })) : h2(MessageBody, { message }),
|
|
33812
33839
|
h2(Reactions, { message, onReact: (e2) => react(message.id, e2), align }),
|
|
33813
33840
|
showThreadAffordance && replyCount > 0 ? h2(
|
|
@@ -33907,13 +33934,14 @@ function ThreadPanel({ conversation, currentUserId, composer }) {
|
|
|
33907
33934
|
const rootId = state.openThreadRootId;
|
|
33908
33935
|
if (!rootId) return null;
|
|
33909
33936
|
const messages = selectThreadMessages(state.messages, rootId);
|
|
33937
|
+
const target = state.replyTarget && state.replyTarget !== rootId ? findMessage(state.messages, state.replyTarget) : void 0;
|
|
33910
33938
|
return h2(
|
|
33911
33939
|
"aside",
|
|
33912
33940
|
{ className: "flex w-80 flex-col border-l border-border", "aria-label": "Thread" },
|
|
33913
33941
|
h2(
|
|
33914
33942
|
"div",
|
|
33915
33943
|
{ className: "flex items-center justify-between border-b border-border px-3 py-2" },
|
|
33916
|
-
h2("span", { className: "text-sm font-semibold" },
|
|
33944
|
+
h2("span", { className: "text-sm font-semibold" }, `Thread \xB7 ${messages.length - 1} ${messages.length - 1 === 1 ? "reply" : "replies"}`),
|
|
33917
33945
|
h2("button", { type: "button", className: "text-muted-foreground hover:text-foreground", "aria-label": "Close thread", onClick: () => conversation.closeThread() }, "\u2715")
|
|
33918
33946
|
),
|
|
33919
33947
|
h2(
|
|
@@ -33921,6 +33949,12 @@ function ThreadPanel({ conversation, currentUserId, composer }) {
|
|
|
33921
33949
|
{ className: "flex-1 overflow-y-auto p-1" },
|
|
33922
33950
|
...messages.map((m2) => h2(MessageRow, { key: m2.id, message: m2, conversation, currentUserId, showThreadAffordance: false }))
|
|
33923
33951
|
),
|
|
33952
|
+
target ? h2(
|
|
33953
|
+
"div",
|
|
33954
|
+
{ className: "flex items-center justify-between gap-2 border-t border-border bg-muted/40 px-3 py-1 text-xs text-muted-foreground" },
|
|
33955
|
+
h2("span", { className: "truncate" }, `\u21B3 Replying to ${target.author.name}`),
|
|
33956
|
+
h2("button", { type: "button", className: "hover:text-foreground", onClick: () => conversation.openThread(rootId) }, "Reply to thread instead")
|
|
33957
|
+
) : null,
|
|
33924
33958
|
composer
|
|
33925
33959
|
);
|
|
33926
33960
|
}
|
|
@@ -33959,9 +33993,13 @@ function Chat({
|
|
|
33959
33993
|
const timeline = selectMainTimeline(state.messages, state.threadingMode);
|
|
33960
33994
|
const activeConv = state.conversations.find((c2) => c2.id === state.activeConversationId);
|
|
33961
33995
|
const busy = state.status === "sending" || state.status === "streaming";
|
|
33996
|
+
const error = state.status === "error" ? state.error : null;
|
|
33997
|
+
const onRetry = () => void conversation.retryLast();
|
|
33962
33998
|
const mainComposer = h2(Composer, {
|
|
33963
33999
|
placeholder,
|
|
33964
34000
|
busy,
|
|
34001
|
+
error,
|
|
34002
|
+
onRetry,
|
|
33965
34003
|
slashCommands,
|
|
33966
34004
|
mentions,
|
|
33967
34005
|
onSlashCommand,
|
|
@@ -33972,11 +34010,13 @@ function Chat({
|
|
|
33972
34010
|
const threadComposer = state.openThreadRootId ? h2(Composer, {
|
|
33973
34011
|
placeholder: "Reply\u2026",
|
|
33974
34012
|
busy,
|
|
34013
|
+
error,
|
|
34014
|
+
onRetry,
|
|
33975
34015
|
slashCommands,
|
|
33976
34016
|
mentions,
|
|
33977
34017
|
onSlashCommand,
|
|
33978
34018
|
toolbar: composerToolbar,
|
|
33979
|
-
onSubmit: (content, atts) => void sendMessage(content, { replyTo: state.openThreadRootId, attachments: atts }),
|
|
34019
|
+
onSubmit: (content, atts) => void sendMessage(content, { replyTo: state.replyTarget ?? state.openThreadRootId, attachments: atts }),
|
|
33980
34020
|
onStop: () => conversation.stop()
|
|
33981
34021
|
}) : null;
|
|
33982
34022
|
const body = timeline.length === 0 ? h2("div", { className: "flex flex-1 items-center justify-center p-6 text-sm text-muted-foreground" }, emptyState ?? "No messages yet. Say hello \u{1F44B}") : h2(
|
|
@@ -33988,8 +34028,10 @@ function Chat({
|
|
|
33988
34028
|
message: m2,
|
|
33989
34029
|
conversation,
|
|
33990
34030
|
currentUserId,
|
|
33991
|
-
|
|
33992
|
-
|
|
34031
|
+
// Show the "N replies" count on originating messages in BOTH modes.
|
|
34032
|
+
showThreadAffordance: true,
|
|
34033
|
+
// Inline: quote the specific message replied to (falls back to the root).
|
|
34034
|
+
quotedParent: state.threadingMode === "inline" && m2.parentId ? findMessage(state.messages, m2.replyToId ?? m2.parentId) : void 0
|
|
33993
34035
|
})
|
|
33994
34036
|
)
|
|
33995
34037
|
);
|
|
@@ -34273,14 +34315,56 @@ function useCookieConsent(config) {
|
|
|
34273
34315
|
};
|
|
34274
34316
|
}
|
|
34275
34317
|
var h3 = React11__namespace.createElement;
|
|
34276
|
-
var
|
|
34277
|
-
var
|
|
34278
|
-
var
|
|
34318
|
+
var btnBase = "inline-flex items-center justify-center rounded-lg px-3.5 py-2 text-sm font-medium transition-colors";
|
|
34319
|
+
var btnPrimary = cn(btnBase, "bg-primary text-primary-foreground hover:opacity-90");
|
|
34320
|
+
var btnGhost = cn(btnBase, "border border-border hover:bg-accent");
|
|
34321
|
+
var btnLink = "text-sm font-medium text-muted-foreground underline-offset-4 hover:text-foreground hover:underline";
|
|
34322
|
+
function Toggle({
|
|
34323
|
+
checked,
|
|
34324
|
+
disabled,
|
|
34325
|
+
onChange,
|
|
34326
|
+
label
|
|
34327
|
+
}) {
|
|
34328
|
+
if (disabled) {
|
|
34329
|
+
return h3(
|
|
34330
|
+
"span",
|
|
34331
|
+
{ className: "rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground" },
|
|
34332
|
+
"Always on"
|
|
34333
|
+
);
|
|
34334
|
+
}
|
|
34335
|
+
return h3(
|
|
34336
|
+
"button",
|
|
34337
|
+
{
|
|
34338
|
+
type: "button",
|
|
34339
|
+
role: "switch",
|
|
34340
|
+
"aria-checked": checked,
|
|
34341
|
+
"aria-label": label,
|
|
34342
|
+
onClick: () => onChange(!checked),
|
|
34343
|
+
className: cn(
|
|
34344
|
+
"relative inline-flex h-5 w-9 shrink-0 items-center rounded-full transition-colors",
|
|
34345
|
+
checked ? "bg-primary" : "bg-muted"
|
|
34346
|
+
)
|
|
34347
|
+
},
|
|
34348
|
+
h3("span", {
|
|
34349
|
+
className: cn(
|
|
34350
|
+
"inline-block h-4 w-4 transform rounded-full bg-background shadow transition-transform",
|
|
34351
|
+
checked ? "translate-x-[1.125rem]" : "translate-x-0.5"
|
|
34352
|
+
)
|
|
34353
|
+
})
|
|
34354
|
+
);
|
|
34355
|
+
}
|
|
34356
|
+
function CookieIcon() {
|
|
34357
|
+
return h3(
|
|
34358
|
+
"div",
|
|
34359
|
+
{ className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-accent text-xl", "aria-hidden": true },
|
|
34360
|
+
"\u{1F36A}"
|
|
34361
|
+
);
|
|
34362
|
+
}
|
|
34279
34363
|
function CookieConsent({
|
|
34280
34364
|
consent,
|
|
34281
34365
|
position = "bottom",
|
|
34282
34366
|
title = "We use cookies",
|
|
34283
|
-
description = "We use cookies to run the site, remember your preferences, and measure traffic. Choose which
|
|
34367
|
+
description = "We use cookies to run the site, remember your preferences, and measure traffic. Choose which to allow.",
|
|
34284
34368
|
policyUrl,
|
|
34285
34369
|
policyLabel = "Cookie policy",
|
|
34286
34370
|
className
|
|
@@ -34288,64 +34372,80 @@ function CookieConsent({
|
|
|
34288
34372
|
const { state, acceptAll, rejectAll, savePreferences, setPreference } = consent;
|
|
34289
34373
|
const [settings, setSettings] = React11__namespace.useState(false);
|
|
34290
34374
|
if (!state.open) return null;
|
|
34291
|
-
const wrapper = cn(
|
|
34292
|
-
|
|
34293
|
-
|
|
34294
|
-
|
|
34295
|
-
);
|
|
34296
|
-
const panel = "mx-auto max-w-3xl rounded-xl border border-border bg-background p-4 shadow-lg";
|
|
34297
|
-
const header = h3(
|
|
34298
|
-
"div",
|
|
34299
|
-
null,
|
|
34300
|
-
h3("h2", { className: "text-base font-semibold" }, title),
|
|
34301
|
-
h3("p", { className: "mt-1 text-sm text-muted-foreground" }, description),
|
|
34302
|
-
policyUrl ? h3("a", { href: policyUrl, target: "_blank", rel: "noreferrer", className: cn(btnLink, "mt-1 inline-block") }, policyLabel) : null
|
|
34303
|
-
);
|
|
34304
|
-
const promptActions = h3(
|
|
34375
|
+
const wrapper = cn("fixed inset-x-0 z-50 p-4", position === "bottom" ? "bottom-0" : "top-0", className);
|
|
34376
|
+
const panel = "mx-auto max-w-2xl overflow-hidden rounded-2xl border border-border bg-background shadow-lg";
|
|
34377
|
+
const policy = policyUrl ? h3("a", { href: policyUrl, target: "_blank", rel: "noreferrer", className: cn(btnLink, "whitespace-nowrap") }, policyLabel) : null;
|
|
34378
|
+
const promptView = h3(
|
|
34305
34379
|
"div",
|
|
34306
|
-
{ className: "
|
|
34307
|
-
h3(
|
|
34308
|
-
h3(
|
|
34309
|
-
|
|
34380
|
+
{ className: "flex flex-col gap-4 p-5 sm:flex-row sm:items-center" },
|
|
34381
|
+
h3(CookieIcon),
|
|
34382
|
+
h3(
|
|
34383
|
+
"div",
|
|
34384
|
+
{ className: "min-w-0 flex-1" },
|
|
34385
|
+
h3("p", { className: "text-sm font-semibold" }, title),
|
|
34386
|
+
h3(
|
|
34387
|
+
"p",
|
|
34388
|
+
{ className: "mt-0.5 text-sm leading-relaxed text-muted-foreground" },
|
|
34389
|
+
description,
|
|
34390
|
+
policy ? h3(React11__namespace.Fragment, null, " ", policy) : null
|
|
34391
|
+
)
|
|
34392
|
+
),
|
|
34393
|
+
h3(
|
|
34394
|
+
"div",
|
|
34395
|
+
{ className: "flex flex-wrap items-center gap-2 sm:shrink-0" },
|
|
34396
|
+
h3("button", { type: "button", className: btnLink, onClick: () => setSettings(true) }, "Customize"),
|
|
34397
|
+
h3("button", { type: "button", className: btnGhost, onClick: () => rejectAll() }, "Reject all"),
|
|
34398
|
+
h3("button", { type: "button", className: btnPrimary, onClick: () => acceptAll() }, "Accept all")
|
|
34399
|
+
)
|
|
34310
34400
|
);
|
|
34311
34401
|
const settingsView = h3(
|
|
34312
34402
|
"div",
|
|
34313
|
-
{ className: "
|
|
34314
|
-
|
|
34315
|
-
|
|
34316
|
-
|
|
34317
|
-
|
|
34318
|
-
|
|
34319
|
-
|
|
34320
|
-
|
|
34321
|
-
h3(
|
|
34322
|
-
|
|
34323
|
-
|
|
34324
|
-
|
|
34325
|
-
|
|
34326
|
-
|
|
34327
|
-
|
|
34328
|
-
|
|
34329
|
-
|
|
34330
|
-
|
|
34331
|
-
|
|
34332
|
-
|
|
34333
|
-
|
|
34334
|
-
|
|
34403
|
+
{ className: "p-5" },
|
|
34404
|
+
h3(
|
|
34405
|
+
"div",
|
|
34406
|
+
{ className: "flex items-center gap-3" },
|
|
34407
|
+
h3(CookieIcon),
|
|
34408
|
+
h3(
|
|
34409
|
+
"div",
|
|
34410
|
+
null,
|
|
34411
|
+
h3("p", { className: "text-sm font-semibold" }, "Cookie preferences"),
|
|
34412
|
+
h3("p", { className: "text-xs text-muted-foreground" }, "Toggle the categories you want to allow.")
|
|
34413
|
+
)
|
|
34414
|
+
),
|
|
34415
|
+
h3(
|
|
34416
|
+
"div",
|
|
34417
|
+
{ className: "mt-4 space-y-2" },
|
|
34418
|
+
...state.categories.map(
|
|
34419
|
+
(cat) => h3(
|
|
34420
|
+
"div",
|
|
34421
|
+
{ key: cat.id, className: "flex items-center justify-between gap-4 rounded-xl border border-border p-3" },
|
|
34422
|
+
h3(
|
|
34423
|
+
"div",
|
|
34424
|
+
{ className: "min-w-0" },
|
|
34425
|
+
h3("p", { className: "text-sm font-medium" }, cat.label),
|
|
34426
|
+
cat.description ? h3("p", { className: "mt-0.5 text-xs text-muted-foreground" }, cat.description) : null
|
|
34427
|
+
),
|
|
34428
|
+
h3(Toggle, {
|
|
34429
|
+
checked: !!state.preferences[cat.id],
|
|
34430
|
+
disabled: cat.required,
|
|
34431
|
+
label: cat.label,
|
|
34432
|
+
onChange: (v2) => setPreference(cat.id, v2)
|
|
34433
|
+
})
|
|
34434
|
+
)
|
|
34335
34435
|
)
|
|
34336
34436
|
),
|
|
34337
34437
|
h3(
|
|
34338
34438
|
"div",
|
|
34339
|
-
{ className: "flex flex-wrap items-center gap-2
|
|
34340
|
-
h3("button", { type: "button", className:
|
|
34341
|
-
h3("button", { type: "button", className: btnGhost, onClick: () => acceptAll() }, "Accept all"),
|
|
34342
|
-
h3("button", { type: "button", className:
|
|
34439
|
+
{ className: "mt-4 flex flex-wrap items-center gap-2" },
|
|
34440
|
+
h3("button", { type: "button", className: btnLink, onClick: () => setSettings(false) }, "\u2190 Back"),
|
|
34441
|
+
h3("button", { type: "button", className: cn(btnGhost, "sm:ml-auto"), onClick: () => acceptAll() }, "Accept all"),
|
|
34442
|
+
h3("button", { type: "button", className: btnPrimary, onClick: () => savePreferences(state.preferences) }, "Save preferences")
|
|
34343
34443
|
)
|
|
34344
34444
|
);
|
|
34345
34445
|
return h3(
|
|
34346
34446
|
"div",
|
|
34347
34447
|
{ className: wrapper, role: "dialog", "aria-label": "Cookie consent", "aria-modal": false },
|
|
34348
|
-
h3("div", { className: panel },
|
|
34448
|
+
h3("div", { className: panel }, settings ? settingsView : promptView)
|
|
34349
34449
|
);
|
|
34350
34450
|
}
|
|
34351
34451
|
|