Andrew commited on
Commit
a0e1d3c
·
1 Parent(s): 3a8705d

refactor(ui): update ChatWindow for branching and persona UI

Browse files
src/lib/components/chat/ChatWindow.svelte CHANGED
@@ -47,12 +47,18 @@
47
  preprompt?: string | undefined;
48
  personaId?: string;
49
  lockedPersonaId?: string;
 
 
 
 
 
50
  files?: File[];
51
  onmessage?: (content: string) => void;
52
  onstop?: () => void;
53
- onretry?: (payload: { id: Message["id"]; content?: string }) => void;
54
  oncontinue?: (payload: { id: Message["id"] }) => void;
55
  onshowAlternateMsg?: (payload: { id: Message["id"] }) => void;
 
56
  }
57
 
58
  let {
@@ -66,12 +72,14 @@
66
  preprompt = undefined,
67
  personaId,
68
  lockedPersonaId,
 
69
  files = $bindable([]),
70
  onmessage,
71
  onstop,
72
  onretry,
73
  oncontinue,
74
  onshowAlternateMsg,
 
75
  }: Props = $props();
76
 
77
  let isReadOnly = $derived(!models.some((model) => model.id === currentModel.id));
@@ -82,6 +90,26 @@
82
  if (!personaId) return undefined;
83
  return $userSettings.personas?.find((p) => p.id === personaId);
84
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  let message: string = $state("");
87
  let shareModalOpen = $state(false);
@@ -368,9 +396,12 @@
368
  personaName={message.from === "assistant" && !message.personaResponses ? persona?.name : undefined}
369
  personaOccupation={message.from === "assistant" && !message.personaResponses ? persona?.jobSector : undefined}
370
  personaStance={message.from === "assistant" && !message.personaResponses ? persona?.stance : undefined}
 
 
371
  bind:editMsdgId
372
  onretry={(payload) => onretry?.(payload)}
373
  onshowAlternateMsg={(payload) => onshowAlternateMsg?.(payload)}
 
374
  />
375
  {/each}
376
  {#if isReadOnly}
@@ -427,7 +458,6 @@
427
  <div
428
  class="no-scrollbar mb-3 flex w-full select-none justify-start gap-2 overflow-x-auto whitespace-nowrap text-gray-400 dark:text-gray-500"
429
  >
430
- <!-- <span class=" text-gray-500 dark:text-gray-400">Follow ups</span> -->
431
  {#each routerFollowUps as followUp}
432
  <button
433
  class="flex items-center gap-1 rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400"
@@ -460,19 +490,6 @@
460
  <div class="w-full">
461
  <div class="flex w-full *:mb-3">
462
  {#if !loading}
463
- <!-- Retry button commented out - regeneration disabled -->
464
- <!-- {#if lastIsError}
465
- <RetryBtn
466
- classNames="ml-auto"
467
- onClick={() => {
468
- if (lastMessage && lastMessage.ancestors) {
469
- onretry?.({
470
- id: lastMessage.id,
471
- });
472
- }
473
- }}
474
- />
475
- {:else -->
476
  {#if messages && lastMessage && lastMessage.interrupted && !isReadOnly}
477
  <div class="ml-auto gap-2">
478
  <ContinueBtn
@@ -563,7 +580,11 @@
563
  <ChatInput value="Sorry, something went wrong. Please try again." disabled={true} />
564
  {:else}
565
  <ChatInput
566
- placeholder={isReadOnly ? "This conversation is read-only." : "Ask anything"}
 
 
 
 
567
  {loading}
568
  bind:value={message}
569
  bind:files
 
47
  preprompt?: string | undefined;
48
  personaId?: string;
49
  lockedPersonaId?: string;
50
+ branchState?: {
51
+ messageId: string;
52
+ personaId: string;
53
+ personaName: string;
54
+ } | null;
55
  files?: File[];
56
  onmessage?: (content: string) => void;
57
  onstop?: () => void;
58
+ onretry?: (payload: { id: Message["id"]; content?: string; personaId?: string }) => void;
59
  oncontinue?: (payload: { id: Message["id"] }) => void;
60
  onshowAlternateMsg?: (payload: { id: Message["id"] }) => void;
61
+ onbranch?: (messageId: string, personaId: string) => void;
62
  }
63
 
64
  let {
 
72
  preprompt = undefined,
73
  personaId,
74
  lockedPersonaId,
75
+ branchState,
76
  files = $bindable([]),
77
  onmessage,
78
  onstop,
79
  onretry,
80
  oncontinue,
81
  onshowAlternateMsg,
82
+ onbranch,
83
  }: Props = $props();
84
 
85
  let isReadOnly = $derived(!models.some((model) => model.id === currentModel.id));
 
90
  if (!personaId) return undefined;
91
  return $userSettings.personas?.find((p) => p.id === personaId);
92
  });
93
+
94
+ // Compute branch points: messages that other messages branch from
95
+ // Map of messageId -> array of persona names that branched from it
96
+ let branchPointInfo = $derived.by(() => {
97
+ const branchPoints = new Map<string, string[]>();
98
+ messages.forEach(msg => {
99
+ if (msg.branchedFrom) {
100
+ const messageId = msg.branchedFrom.messageId;
101
+ const personaName = $userSettings.personas?.find(p => p.id === msg.branchedFrom?.personaId)?.name || msg.branchedFrom.personaId;
102
+ if (!branchPoints.has(messageId)) {
103
+ branchPoints.set(messageId, []);
104
+ }
105
+ const personas = branchPoints.get(messageId)!;
106
+ if (!personas.includes(personaName)) {
107
+ personas.push(personaName);
108
+ }
109
+ }
110
+ });
111
+ return branchPoints;
112
+ });
113
 
114
  let message: string = $state("");
115
  let shareModalOpen = $state(false);
 
396
  personaName={message.from === "assistant" && !message.personaResponses ? persona?.name : undefined}
397
  personaOccupation={message.from === "assistant" && !message.personaResponses ? persona?.jobSector : undefined}
398
  personaStance={message.from === "assistant" && !message.personaResponses ? persona?.stance : undefined}
399
+ {branchState}
400
+ branchPersonas={branchPointInfo.get(message.id) ?? []}
401
  bind:editMsdgId
402
  onretry={(payload) => onretry?.(payload)}
403
  onshowAlternateMsg={(payload) => onshowAlternateMsg?.(payload)}
404
+ onbranch={(messageId, personaId) => onbranch?.(messageId, personaId)}
405
  />
406
  {/each}
407
  {#if isReadOnly}
 
458
  <div
459
  class="no-scrollbar mb-3 flex w-full select-none justify-start gap-2 overflow-x-auto whitespace-nowrap text-gray-400 dark:text-gray-500"
460
  >
 
461
  {#each routerFollowUps as followUp}
462
  <button
463
  class="flex items-center gap-1 rounded-lg bg-gray-100/90 px-2 py-0.5 text-center text-sm backdrop-blur hover:text-gray-500 dark:bg-gray-700/50 dark:hover:text-gray-400"
 
490
  <div class="w-full">
491
  <div class="flex w-full *:mb-3">
492
  {#if !loading}
 
 
 
 
 
 
 
 
 
 
 
 
 
493
  {#if messages && lastMessage && lastMessage.interrupted && !isReadOnly}
494
  <div class="ml-auto gap-2">
495
  <ContinueBtn
 
580
  <ChatInput value="Sorry, something went wrong. Please try again." disabled={true} />
581
  {:else}
582
  <ChatInput
583
+ placeholder={isReadOnly
584
+ ? "This conversation is read-only."
585
+ : branchState
586
+ ? `Branched from ${branchState.personaName}`
587
+ : "Ask anything"}
588
  {loading}
589
  bind:value={message}
590
  bind:files