@refraction-ui/astro 0.9.0 → 0.10.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.
@@ -88,7 +88,7 @@ function isOwn(m: ChatMessage): boolean {
88
88
  ) : (
89
89
  timeline.map((message) => {
90
90
  const parent = state.threadingMode === 'inline' && message.parentId
91
- ? findMessage(state.messages, message.parentId)
91
+ ? findMessage(state.messages, message.replyToId ?? message.parentId)
92
92
  : undefined
93
93
  const replyCount = getReplyCount(state.messages, message.id)
94
94
  return (
@@ -137,7 +137,7 @@ function isOwn(m: ChatMessage): boolean {
137
137
  </div>
138
138
  )}
139
139
 
140
- {state.threadingMode === 'panel' && replyCount > 0 && (
140
+ {replyCount > 0 && (
141
141
  <button type="button" class="mt-1 text-xs font-medium text-primary hover:underline" data-rfr-open-thread data-root-id={message.id}>
142
142
  💬 {replyCount} {replyCount === 1 ? 'reply' : 'replies'}
143
143
  </button>
@@ -40,6 +40,7 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
40
40
  const messagesByConv = new Map<string, ChatMessage[]>()
41
41
  let activeConversationId: string | null = config.activeConversationId ?? null
42
42
  let openThreadRootId: string | null = null
43
+ let replyTarget: string | null = null
43
44
  let status: ConversationState['status'] = 'idle'
44
45
  let error: string | null = null
45
46
  let abortController: AbortController | null = null
@@ -66,6 +67,7 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
66
67
  activeConversationId,
67
68
  messages: activeConversationId ? (messagesByConv.get(activeConversationId) ?? []) : [],
68
69
  openThreadRootId,
70
+ replyTarget,
69
71
  threadingMode,
70
72
  status,
71
73
  error,
@@ -187,6 +189,7 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
187
189
  newConversation(opts) {
188
190
  const conversation = createConversationInternal(opts ?? {})
189
191
  openThreadRootId = null
192
+ replyTarget = null
190
193
  emit()
191
194
  return conversation
192
195
  },
@@ -195,6 +198,7 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
195
198
  if (!conversations.has(conversationId) || activeConversationId === conversationId) return
196
199
  activeConversationId = conversationId
197
200
  openThreadRootId = null
201
+ replyTarget = null
198
202
  emit()
199
203
  },
200
204
 
@@ -205,6 +209,7 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
205
209
  if (activeConversationId === conversationId) {
206
210
  activeConversationId = orderedConversations()[0]?.id ?? null
207
211
  openThreadRootId = null
212
+ replyTarget = null
208
213
  }
209
214
  emit()
210
215
  },
@@ -245,6 +250,7 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
245
250
  const next = list.filter((m) => !removeIds.has(m.id))
246
251
  messagesByConv.set(activeConversationId, next)
247
252
  if (openThreadRootId && removeIds.has(openThreadRootId)) openThreadRootId = null
253
+ if (replyTarget && removeIds.has(replyTarget)) replyTarget = openThreadRootId
248
254
  emit()
249
255
  },
250
256
 
@@ -277,7 +283,9 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
277
283
 
278
284
  const conversationId = ensureActiveConversation(opts)
279
285
  const list = messagesByConv.get(conversationId)!
286
+ // Group under the originating root, but remember the specific message replied to.
280
287
  const parentId = opts?.replyTo ? rootIdOf(list, opts.replyTo) : undefined
288
+ const replyToId = opts?.replyTo
281
289
  const isFirstRoot = !parentId && selectRoots(list).length === 0
282
290
 
283
291
  const userMsg: ChatMessage = {
@@ -289,6 +297,7 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
289
297
  timestamp: new Date(),
290
298
  status: 'sent',
291
299
  parentId,
300
+ replyToId,
292
301
  attachments: opts?.attachments,
293
302
  metadata: opts?.metadata,
294
303
  }
@@ -355,12 +364,23 @@ export function createConversation(config: ConversationConfig = {}): Conversatio
355
364
 
356
365
  openThread(rootId) {
357
366
  openThreadRootId = rootId
367
+ replyTarget = rootId // reply to the root by default
368
+ emit()
369
+ },
370
+
371
+ replyTo(messageId) {
372
+ if (!activeConversationId) return
373
+ const list = messagesByConv.get(activeConversationId)!
374
+ // Open the originating thread, but target the specific message replied to.
375
+ openThreadRootId = rootIdOf(list, messageId)
376
+ replyTarget = messageId
358
377
  emit()
359
378
  },
360
379
 
361
380
  closeThread() {
362
- if (openThreadRootId === null) return
381
+ if (openThreadRootId === null && replyTarget === null) return
363
382
  openThreadRootId = null
383
+ replyTarget = null
364
384
  emit()
365
385
  },
366
386
 
@@ -68,9 +68,15 @@ export interface ChatMessage {
68
68
  error?: string
69
69
  /**
70
70
  * Root message id of the reply-thread this message belongs to. Absent on
71
- * root/main-timeline messages. Threads are one level deep.
71
+ * root/main-timeline messages. Threads are one level deep — a reply to any
72
+ * message in a thread groups under the same originating root.
72
73
  */
73
74
  parentId?: string
75
+ /**
76
+ * The specific message this is "in reply to" (for the quote), which may be a
77
+ * mid-thread reply even though `parentId` is always the originating root.
78
+ */
79
+ replyToId?: string
74
80
  /** Emoji reactions */
75
81
  reactions?: MessageReaction[]
76
82
  /** Attachments (images/gifs/files) */
@@ -136,6 +142,8 @@ export interface ConversationState {
136
142
  messages: ChatMessage[]
137
143
  /** Root id of the thread shown in the side panel, or null when closed */
138
144
  openThreadRootId: string | null
145
+ /** The specific message a reply in the open thread will target (default: the root) */
146
+ replyTarget: string | null
139
147
  /** Current threading mode */
140
148
  threadingMode: ThreadingMode
141
149
  /** Coarse store status */
@@ -210,8 +218,10 @@ export interface ConversationAPI {
210
218
  stop(): void
211
219
 
212
220
  // — threads / view —
213
- /** Open the side panel focused on a message's thread */
221
+ /** Open the side panel focused on a message's thread (reply target = root) */
214
222
  openThread(rootId: string): void
223
+ /** Open the originating thread and target a specific message for the next reply */
224
+ replyTo(messageId: string): void
215
225
  /** Close the thread side panel */
216
226
  closeThread(): void
217
227
  /** Switch threading mode */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@refraction-ui/astro",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "All Refraction UI Astro components in one package",
5
5
  "type": "module",
6
6
  "exports": {