dylanebert commited on
Commit
7a07363
·
1 Parent(s): 7ad6991

gsap safety

Browse files
.claude/commands/nourish.md CHANGED
@@ -7,7 +7,6 @@ Complete conversation by updating context and applying cleanup.
7
  @CLAUDE.md
8
  @layers/structure.md
9
  @layers/context-template.md
10
- @llms.txt
11
 
12
  User arguments: "$ARGUMENTS"
13
 
 
7
  @CLAUDE.md
8
  @layers/structure.md
9
  @layers/context-template.md
 
10
 
11
  User arguments: "$ARGUMENTS"
12
 
src/lib/components/chat/ChatPanel.svelte CHANGED
@@ -40,6 +40,17 @@
40
 
41
  onDestroy(() => {
42
  agentService.disconnect();
 
 
 
 
 
 
 
 
 
 
 
43
  });
44
 
45
  $: if ($authStore.isAuthenticated && !$authStore.loading && !hasConnected) {
@@ -286,7 +297,6 @@
286
  <div class="messages" bind:this={messagesContainer} on:scroll={handleScroll}>
287
  {#if !$authStore.isAuthenticated && !$authStore.loading}
288
  <div class="auth-prompt">
289
- <p>Sign in to chat.</p>
290
  <button bind:this={authPromptBtn} on:click={() => authStore.login()} class="auth-prompt-btn">
291
  Sign in with 🤗 Hugging Face
292
  </button>
@@ -639,12 +649,6 @@
639
  text-align: center;
640
  }
641
 
642
- .auth-prompt p {
643
- color: rgba(255, 255, 255, 0.6);
644
- margin-bottom: 1.5rem;
645
- font-size: 0.875rem;
646
- }
647
-
648
  .auth-prompt-btn {
649
  padding: 0.6rem 1.8rem;
650
  background: rgba(255, 210, 30, 0.08);
 
40
 
41
  onDestroy(() => {
42
  agentService.disconnect();
43
+
44
+ if (scrollAnimation) {
45
+ scrollAnimation.kill();
46
+ }
47
+
48
+ if (sendButton) gsap.killTweensOf(sendButton);
49
+ if (stopButton) gsap.killTweensOf(stopButton);
50
+ if (inputTextarea) gsap.killTweensOf(inputTextarea);
51
+ if (authPromptBtn) gsap.killTweensOf(authPromptBtn);
52
+ if (clearButton) gsap.killTweensOf(clearButton);
53
+ if (messagesContainer) gsap.killTweensOf(messagesContainer);
54
  });
55
 
56
  $: if ($authStore.isAuthenticated && !$authStore.loading && !hasConnected) {
 
297
  <div class="messages" bind:this={messagesContainer} on:scroll={handleScroll}>
298
  {#if !$authStore.isAuthenticated && !$authStore.loading}
299
  <div class="auth-prompt">
 
300
  <button bind:this={authPromptBtn} on:click={() => authStore.login()} class="auth-prompt-btn">
301
  Sign in with 🤗 Hugging Face
302
  </button>
 
649
  text-align: center;
650
  }
651
 
 
 
 
 
 
 
652
  .auth-prompt-btn {
653
  padding: 0.6rem 1.8rem;
654
  background: rgba(255, 210, 30, 0.08);
src/lib/components/chat/ExampleMessages.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- import { onMount } from "svelte";
3
  import { fade } from "svelte/transition";
4
  import gsap from "gsap";
5
 
@@ -33,6 +33,14 @@
33
  });
34
  });
35
 
 
 
 
 
 
 
 
 
36
  function handleClick(text: string) {
37
  onSendMessage(text);
38
  }
 
1
  <script lang="ts">
2
+ import { onMount, onDestroy } from "svelte";
3
  import { fade } from "svelte/transition";
4
  import gsap from "gsap";
5
 
 
33
  });
34
  });
35
 
36
+ onDestroy(() => {
37
+ exampleCards.forEach((card) => {
38
+ if (card) {
39
+ gsap.killTweensOf(card);
40
+ }
41
+ });
42
+ });
43
+
44
  function handleClick(text: string) {
45
  onSendMessage(text);
46
  }
src/lib/components/chat/InProgressBlock.svelte CHANGED
@@ -189,19 +189,11 @@
189
  if (collapseTimeout) clearTimeout(collapseTimeout);
190
  if (timeline) timeline.kill();
191
 
192
- if (progressBar && blockElement) {
193
- gsap.to(progressBar, {
194
- width: "100%",
195
- duration: 0.2,
196
- ease: "power2.out",
197
- });
198
- gsap.to(blockElement, {
199
- opacity: 0,
200
- scale: 0.95,
201
- duration: 0.3,
202
- ease: "power2.in",
203
- });
204
- }
205
  });
206
  </script>
207
 
 
189
  if (collapseTimeout) clearTimeout(collapseTimeout);
190
  if (timeline) timeline.kill();
191
 
192
+ if (expandIcon) gsap.killTweensOf(expandIcon);
193
+ if (contentElement) gsap.killTweensOf(contentElement);
194
+ if (statusElement) gsap.killTweensOf(statusElement);
195
+ if (progressBar) gsap.killTweensOf(progressBar);
196
+ if (blockElement) gsap.killTweensOf(blockElement);
 
 
 
 
 
 
 
 
197
  });
198
  </script>
199
 
src/lib/components/chat/ReasoningBlock.svelte CHANGED
@@ -11,6 +11,7 @@
11
  let contentElement: HTMLDivElement;
12
  let blockElement: HTMLDivElement;
13
  let collapseTimeout: number | null = null;
 
14
 
15
  $: if (responseComplete && autoCollapse && isExpanded) {
16
  if (collapseTimeout) clearTimeout(collapseTimeout);
@@ -35,7 +36,9 @@
35
  function animateExpand() {
36
  if (!iconElement || !contentElement) return;
37
 
38
- gsap.timeline()
 
 
39
  .to(iconElement, {
40
  rotation: 180,
41
  duration: 0.15,
@@ -58,7 +61,9 @@
58
  function animateCollapse() {
59
  if (!iconElement || !contentElement) return;
60
 
61
- gsap.timeline()
 
 
62
  .to(iconElement, {
63
  rotation: 0,
64
  duration: 0.1,
@@ -71,7 +76,9 @@
71
  duration: 0.1,
72
  ease: "power2.in",
73
  onComplete: () => {
74
- gsap.set(contentElement, { display: 'none' });
 
 
75
  }
76
  }, 0)
77
  .to(blockElement, {
@@ -119,6 +126,11 @@
119
 
120
  onDestroy(() => {
121
  if (collapseTimeout) clearTimeout(collapseTimeout);
 
 
 
 
 
122
  });
123
  </script>
124
 
 
11
  let contentElement: HTMLDivElement;
12
  let blockElement: HTMLDivElement;
13
  let collapseTimeout: number | null = null;
14
+ let activeTimeline: gsap.core.Timeline | null = null;
15
 
16
  $: if (responseComplete && autoCollapse && isExpanded) {
17
  if (collapseTimeout) clearTimeout(collapseTimeout);
 
36
  function animateExpand() {
37
  if (!iconElement || !contentElement) return;
38
 
39
+ if (activeTimeline) activeTimeline.kill();
40
+
41
+ activeTimeline = gsap.timeline()
42
  .to(iconElement, {
43
  rotation: 180,
44
  duration: 0.15,
 
61
  function animateCollapse() {
62
  if (!iconElement || !contentElement) return;
63
 
64
+ if (activeTimeline) activeTimeline.kill();
65
+
66
+ activeTimeline = gsap.timeline()
67
  .to(iconElement, {
68
  rotation: 0,
69
  duration: 0.1,
 
76
  duration: 0.1,
77
  ease: "power2.in",
78
  onComplete: () => {
79
+ if (contentElement) {
80
+ gsap.set(contentElement, { display: 'none' });
81
+ }
82
  }
83
  }, 0)
84
  .to(blockElement, {
 
126
 
127
  onDestroy(() => {
128
  if (collapseTimeout) clearTimeout(collapseTimeout);
129
+ if (activeTimeline) activeTimeline.kill();
130
+
131
+ if (iconElement) gsap.killTweensOf(iconElement);
132
+ if (contentElement) gsap.killTweensOf(contentElement);
133
+ if (blockElement) gsap.killTweensOf(blockElement);
134
  });
135
  </script>
136
 
src/lib/components/chat/context.md CHANGED
@@ -4,7 +4,7 @@ AI chat interface with real-time streaming and conversation control.
4
 
5
  ## Components
6
 
7
- - `ChatPanel.svelte` - Main chat UI with header bar, clear button, smooth scroll, stop button when processing
8
  - `ExampleMessages.svelte` - Clickable prompt suggestions when chat is empty
9
  - `MessageSegment.svelte` - Renders text, tool invocations, and results
10
  - `StreamingText.svelte` - Optimized character streaming with state persistence
@@ -21,3 +21,4 @@ AI chat interface with real-time streaming and conversation control.
21
  - Tool invocations displayed through dedicated segments
22
  - Clean separation between text and tool content
23
  - Abortable conversations via stop button during processing
 
 
4
 
5
  ## Components
6
 
7
+ - `ChatPanel.svelte` - Main chat UI with header bar, clear button, smooth scroll, stop button
8
  - `ExampleMessages.svelte` - Clickable prompt suggestions when chat is empty
9
  - `MessageSegment.svelte` - Renders text, tool invocations, and results
10
  - `StreamingText.svelte` - Optimized character streaming with state persistence
 
21
  - Tool invocations displayed through dedicated segments
22
  - Clean separation between text and tool content
23
  - Abortable conversations via stop button during processing
24
+ - All GSAP animations properly cleaned up on component destruction
src/main.ts CHANGED
@@ -1,6 +1,11 @@
1
  import "./app.css";
2
  import App from "./App.svelte";
3
  import { consoleSyncService } from "./lib/services/console-sync";
 
 
 
 
 
4
 
5
  const app = new App({
6
  target: document.getElementById("app")!,
 
1
  import "./app.css";
2
  import App from "./App.svelte";
3
  import { consoleSyncService } from "./lib/services/console-sync";
4
+ import gsap from "gsap";
5
+
6
+ gsap.config({
7
+ nullTargetWarn: false,
8
+ });
9
 
10
  const app = new App({
11
  target: document.getElementById("app")!,