enzostvs HF Staff commited on
Commit
8cc084b
·
1 Parent(s): b9ff581

stylized markdown think

Browse files
src/lib/components/chat/Message.svelte CHANGED
@@ -38,7 +38,7 @@
38
  {:else}
39
  {#if message.reasoning}
40
  <Think
41
- isThinking={!message.content || (message.content as string)?.trim() === ''}
42
  content={message.reasoning as string}
43
  />
44
  {/if}
 
38
  {:else}
39
  {#if message.reasoning}
40
  <Think
41
+ isThinking={!!message.content && (message.content as string)?.trim() !== ''}
42
  content={message.reasoning as string}
43
  />
44
  {/if}
src/lib/components/chat/markdown/Code.svelte CHANGED
@@ -2,7 +2,9 @@
2
  import Button from '$lib/components/ui/button/button.svelte';
3
  import { Check, Copy } from '@lucide/svelte';
4
  import { HighlightAuto } from 'svelte-highlight';
5
- import 'svelte-highlight/styles/github.css';
 
 
6
 
7
  let {
8
  lang,
@@ -17,16 +19,30 @@
17
  copiedCode = true;
18
  setTimeout(() => (copiedCode = false), 2000);
19
  }
 
 
 
 
 
 
 
 
 
 
 
 
20
  </script>
21
 
22
  <div class="overflow-hidden {className}">
23
  {#if lang}
24
- <div class="flex items-center justify-between border-b border-border/60 bg-muted px-3 py-1.5">
 
 
25
  <span class="font-mono text-[11px] text-muted-foreground">{lang}</span>
26
  </div>
27
  {/if}
28
  <div class="group relative">
29
- <HighlightAuto code={text} class="font-mono text-[13px] leading-relaxed" />
30
  <Button
31
  variant="outline"
32
  class="absolute top-2 right-2 opacity-0 group-hover:opacity-100"
 
2
  import Button from '$lib/components/ui/button/button.svelte';
3
  import { Check, Copy } from '@lucide/svelte';
4
  import { HighlightAuto } from 'svelte-highlight';
5
+ import { mode } from 'mode-watcher';
6
+ import githubDarkUrl from 'svelte-highlight/styles/github-dark.css?url';
7
+ import githubUrl from 'svelte-highlight/styles/github.css?url';
8
 
9
  let {
10
  lang,
 
19
  copiedCode = true;
20
  setTimeout(() => (copiedCode = false), 2000);
21
  }
22
+
23
+ $effect(() => {
24
+ const themeUrl = mode.current === 'dark' ? githubDarkUrl : githubUrl;
25
+ let link = document.getElementById('highlight-theme') as HTMLLinkElement | null;
26
+ if (!link) {
27
+ link = document.createElement('link');
28
+ link.id = 'highlight-theme';
29
+ link.rel = 'stylesheet';
30
+ document.head.appendChild(link);
31
+ }
32
+ link.href = themeUrl;
33
+ });
34
  </script>
35
 
36
  <div class="overflow-hidden {className}">
37
  {#if lang}
38
+ <div
39
+ class="flex items-center justify-between border-b border-border/60 bg-muted px-3 py-1.5 dark:bg-accent/30"
40
+ >
41
  <span class="font-mono text-[11px] text-muted-foreground">{lang}</span>
42
  </div>
43
  {/if}
44
  <div class="group relative">
45
+ <HighlightAuto code={text} class="font-mono text-[11px] leading-relaxed" />
46
  <Button
47
  variant="outline"
48
  class="absolute top-2 right-2 opacity-0 group-hover:opacity-100"
src/lib/components/chat/markdown/Codespan.svelte CHANGED
@@ -3,6 +3,6 @@
3
  </script>
4
 
5
  <code
6
- class="rounded-md border border-border/40 bg-muted px-1.5 py-0.5 font-mono text-[13px] text-foreground/85"
7
  >{raw.replace(/`/g, '')}</code
8
  >
 
3
  </script>
4
 
5
  <code
6
+ class="rounded-md border border-border/40 bg-accent-foreground/5 px-1.5 py-0.5 font-mono text-[11px] text-foreground/85"
7
  >{raw.replace(/`/g, '')}</code
8
  >
src/lib/components/chat/markdown/Heading.svelte CHANGED
@@ -5,19 +5,19 @@
5
  </script>
6
 
7
  {#if depth === 1}
8
- <h1 class="mt-4 mb-2 text-2xl font-semibold text-foreground first:mt-0">
9
  {@render children?.()}
10
  </h1>
11
  {:else if depth === 2}
12
- <h2 class="mt-3.5 mb-2 text-xl font-semibold text-foreground first:mt-0">
13
  {@render children?.()}
14
  </h2>
15
  {:else if depth === 3}
16
- <h3 class="mt-3 mb-1.5 text-lg font-semibold text-foreground first:mt-0">
17
  {@render children?.()}
18
  </h3>
19
  {:else}
20
- <h4 class="mt-2.5 mb-1 text-base font-semibold text-foreground first:mt-0">
21
  {@render children?.()}
22
  </h4>
23
  {/if}
 
5
  </script>
6
 
7
  {#if depth === 1}
8
+ <h1 class="mt-4 mb-2 text-base font-semibold text-foreground first:mt-0">
9
  {@render children?.()}
10
  </h1>
11
  {:else if depth === 2}
12
+ <h2 class="mt-3.5 mb-2 text-base font-semibold text-foreground first:mt-0">
13
  {@render children?.()}
14
  </h2>
15
  {:else if depth === 3}
16
+ <h3 class="mt-3 mb-1.5 text-sm font-semibold text-foreground first:mt-0">
17
  {@render children?.()}
18
  </h3>
19
  {:else}
20
+ <h4 class="mt-2.5 mb-1 text-sm font-semibold text-foreground first:mt-0">
21
  {@render children?.()}
22
  </h4>
23
  {/if}
src/lib/components/chat/markdown/List.svelte CHANGED
@@ -6,14 +6,14 @@
6
 
7
  {#if ordered}
8
  <ol
9
- class="my-2 list-outside list-decimal space-y-1 pl-5 text-base text-foreground/90 marker:text-muted-foreground"
10
  {start}
11
  >
12
  {@render children?.()}
13
  </ol>
14
  {:else}
15
  <ul
16
- class="my-2 list-outside list-disc space-y-1 pl-5 text-base text-foreground/90 marker:text-muted-foreground"
17
  >
18
  {@render children?.()}
19
  </ul>
 
6
 
7
  {#if ordered}
8
  <ol
9
+ class="my-2 list-outside list-decimal space-y-1 pl-5 text-xs text-foreground/90 marker:text-muted-foreground"
10
  {start}
11
  >
12
  {@render children?.()}
13
  </ol>
14
  {:else}
15
  <ul
16
+ class="my-2 list-outside list-disc space-y-1 pl-5 text-xs text-foreground/90 marker:text-muted-foreground"
17
  >
18
  {@render children?.()}
19
  </ul>
src/lib/components/chat/markdown/Paragraph.svelte CHANGED
@@ -3,4 +3,4 @@
3
  let { children }: { children?: Snippet } = $props();
4
  </script>
5
 
6
- <p class="mb-3 text-base leading-relaxed text-foreground/90 last:mb-0">{@render children?.()}</p>
 
3
  let { children }: { children?: Snippet } = $props();
4
  </script>
5
 
6
+ <p class="mb-3 text-xs leading-relaxed text-foreground/90 last:mb-0">{@render children?.()}</p>
src/lib/components/chat/markdown/Think.svelte CHANGED
@@ -1,8 +1,20 @@
1
  <script lang="ts">
2
  import { slide } from 'svelte/transition';
3
  import { Brain, ChevronDown } from '@lucide/svelte';
 
4
 
5
  import Spinner from '$lib/components/loading/Spinner.svelte';
 
 
 
 
 
 
 
 
 
 
 
6
  let { isThinking, content }: { isThinking?: boolean; content?: string } = $props();
7
 
8
  let isCollapsed = $state.raw<boolean>(false);
@@ -10,15 +22,36 @@
10
  function toggleCollapse() {
11
  isCollapsed = !isCollapsed;
12
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  </script>
14
 
15
- <div class="mb-4 overflow-hidden rounded-xl border border-border bg-accent text-xs">
 
 
16
  <button
17
- class="flex w-full cursor-pointer items-center gap-2.5 p-3 text-left transition-colors hover:bg-accent/80"
18
  onclick={toggleCollapse}
19
  >
20
  <div
21
- class="flex size-7 min-h-7 min-w-7 items-center justify-center rounded-lg bg-linear-to-br from-primary/20 to-primary/5"
 
22
  >
23
  <Brain class="size-3.5 text-muted-foreground" />
24
  </div>
@@ -37,7 +70,7 @@
37
  {#if !isCollapsed}
38
  <div transition:slide={{ duration: 200 }}>
39
  <div class="border-t border-border/60 px-4 py-3">
40
- <p class="whitespace-pre-wrap text-muted-foreground">{content?.trimStart()}</p>
41
  </div>
42
  </div>
43
  {/if}
 
1
  <script lang="ts">
2
  import { slide } from 'svelte/transition';
3
  import { Brain, ChevronDown } from '@lucide/svelte';
4
+ import SvelteMarkdown from 'svelte-markdown';
5
 
6
  import Spinner from '$lib/components/loading/Spinner.svelte';
7
+
8
+ import Paragraph from './Paragraph.svelte';
9
+ import Heading from './Heading.svelte';
10
+ import Code from './Code.svelte';
11
+ import Codespan from './Codespan.svelte';
12
+ import Blockquote from './Blockquote.svelte';
13
+ import List from './List.svelte';
14
+ import ListItem from './ListItem.svelte';
15
+ import Link from './Link.svelte';
16
+ import Hr from './Hr.svelte';
17
+
18
  let { isThinking, content }: { isThinking?: boolean; content?: string } = $props();
19
 
20
  let isCollapsed = $state.raw<boolean>(false);
 
22
  function toggleCollapse() {
23
  isCollapsed = !isCollapsed;
24
  }
25
+
26
+ const renderers = {
27
+ paragraph: Paragraph,
28
+ heading: Heading,
29
+ code: Code,
30
+ codespan: Codespan,
31
+ blockquote: Blockquote,
32
+ list: List,
33
+ listitem: ListItem,
34
+ link: Link,
35
+ hr: Hr
36
+ };
37
+
38
+ $effect(() => {
39
+ if (!isThinking) {
40
+ isCollapsed = true;
41
+ }
42
+ });
43
  </script>
44
 
45
+ <div
46
+ class="mb-4 overflow-hidden rounded-xl border border-border bg-accent text-xs dark:bg-accent/30"
47
+ >
48
  <button
49
+ class="flex w-full cursor-pointer items-center gap-2.5 p-3 text-left transition-colors hover:bg-accent/80 dark:hover:bg-accent/30"
50
  onclick={toggleCollapse}
51
  >
52
  <div
53
+ class="flex size-7 min-h-7 min-w-7 items-center justify-center rounded-lg bg-linear-to-br from-primary/20 to-primary/5 {isThinking &&
54
+ 'animate-pulse'}"
55
  >
56
  <Brain class="size-3.5 text-muted-foreground" />
57
  </div>
 
70
  {#if !isCollapsed}
71
  <div transition:slide={{ duration: 200 }}>
72
  <div class="border-t border-border/60 px-4 py-3">
73
+ <SvelteMarkdown source={content?.trimStart()} renderers={renderers as any} />
74
  </div>
75
  </div>
76
  {/if}
src/lib/components/chat/markdown/think/Blockquote.svelte ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ let { children }: { children?: Snippet } = $props();
4
+ </script>
5
+
6
+ <blockquote class="my-2 border-l-2 border-muted-foreground/25 pl-2.5 text-muted-foreground/80 italic">
7
+ {@render children?.()}
8
+ </blockquote>
src/lib/components/chat/markdown/think/Code.svelte ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import Button from '$lib/components/ui/button/button.svelte';
3
+ import { Check, Copy } from '@lucide/svelte';
4
+ import { HighlightAuto } from 'svelte-highlight';
5
+ import { mode } from 'mode-watcher';
6
+ import githubDarkUrl from 'svelte-highlight/styles/github-dark.css?url';
7
+ import githubUrl from 'svelte-highlight/styles/github.css?url';
8
+
9
+ let { lang, text }: { lang?: string; text: string } = $props();
10
+
11
+ let copiedCode = $state(false);
12
+
13
+ async function copy(text: string) {
14
+ await navigator.clipboard.writeText(text);
15
+ copiedCode = true;
16
+ setTimeout(() => (copiedCode = false), 2000);
17
+ }
18
+
19
+ $effect(() => {
20
+ const themeUrl = mode.current === 'dark' ? githubDarkUrl : githubUrl;
21
+ let link = document.getElementById('highlight-theme') as HTMLLinkElement | null;
22
+ if (!link) {
23
+ link = document.createElement('link');
24
+ link.id = 'highlight-theme';
25
+ link.rel = 'stylesheet';
26
+ document.head.appendChild(link);
27
+ }
28
+ link.href = themeUrl;
29
+ });
30
+ </script>
31
+
32
+ <div class="my-2 overflow-hidden rounded-md border border-border/50 bg-muted/40">
33
+ {#if lang}
34
+ <div
35
+ class="flex items-center justify-between border-b border-border/50 bg-muted/60 px-2.5 py-1 dark:bg-accent/20"
36
+ >
37
+ <span class="font-mono text-[10px] text-muted-foreground">{lang}</span>
38
+ </div>
39
+ {/if}
40
+ <div class="group relative">
41
+ <HighlightAuto code={text} class="font-mono text-[11px] leading-relaxed" />
42
+ <Button
43
+ variant="outline"
44
+ class="absolute top-1.5 right-1.5 opacity-0 group-hover:opacity-100"
45
+ size="icon-xs"
46
+ onclick={() => copy(text)}
47
+ >
48
+ {#if copiedCode}
49
+ <Check class="size-3 text-green-500" />
50
+ {:else}
51
+ <Copy class="size-3" />
52
+ {/if}
53
+ </Button>
54
+ </div>
55
+ </div>
src/lib/components/chat/markdown/think/Codespan.svelte ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ let { raw }: { raw: string } = $props();
3
+ </script>
4
+
5
+ <code
6
+ class="rounded border border-border/30 bg-muted px-1 py-px font-mono text-[10px] text-foreground/80"
7
+ >{raw.replace(/`/g, '')}</code
8
+ >
src/lib/components/chat/markdown/think/Heading.svelte ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ let { depth, children }: { depth: number; raw?: string; text?: string; children?: Snippet } =
4
+ $props();
5
+ </script>
6
+
7
+ {#if depth === 1}
8
+ <h1 class="mt-3 mb-1.5 text-sm font-semibold text-foreground first:mt-0">
9
+ {@render children?.()}
10
+ </h1>
11
+ {:else if depth === 2}
12
+ <h2 class="mt-2.5 mb-1 text-[13px] font-semibold text-foreground first:mt-0">
13
+ {@render children?.()}
14
+ </h2>
15
+ {:else if depth === 3}
16
+ <h3 class="mt-2 mb-1 text-xs font-semibold text-foreground first:mt-0">
17
+ {@render children?.()}
18
+ </h3>
19
+ {:else}
20
+ <h4 class="mt-1.5 mb-0.5 text-[11px] font-semibold text-foreground first:mt-0">
21
+ {@render children?.()}
22
+ </h4>
23
+ {/if}
src/lib/components/chat/markdown/think/Hr.svelte ADDED
@@ -0,0 +1 @@
 
 
1
+ <hr class="my-2.5 border-t border-border/40" />
src/lib/components/chat/markdown/think/Link.svelte ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ let {
4
+ href = '',
5
+ title,
6
+ children
7
+ }: { href?: string; title?: string; children?: Snippet } = $props();
8
+ </script>
9
+
10
+ <a
11
+ {href}
12
+ {title}
13
+ target="_blank"
14
+ rel="noopener noreferrer"
15
+ class="text-primary/80 underline decoration-primary/30 underline-offset-2 transition-colors hover:text-primary hover:decoration-primary"
16
+ >
17
+ {@render children?.()}
18
+ </a>
src/lib/components/chat/markdown/think/List.svelte ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ let { ordered, start, children }: { ordered: boolean; start?: number; children?: Snippet } =
4
+ $props();
5
+ </script>
6
+
7
+ {#if ordered}
8
+ <ol
9
+ class="my-1.5 list-outside list-decimal space-y-0.5 pl-4 text-foreground/75 marker:text-muted-foreground"
10
+ {start}
11
+ >
12
+ {@render children?.()}
13
+ </ol>
14
+ {:else}
15
+ <ul
16
+ class="my-1.5 list-outside list-disc space-y-0.5 pl-4 text-foreground/75 marker:text-muted-foreground"
17
+ >
18
+ {@render children?.()}
19
+ </ul>
20
+ {/if}
src/lib/components/chat/markdown/think/ListItem.svelte ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ let { children }: { children?: Snippet } = $props();
4
+ </script>
5
+
6
+ <li class="leading-relaxed">{@render children?.()}</li>
src/lib/components/chat/markdown/think/Paragraph.svelte ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ let { children }: { children?: Snippet } = $props();
4
+ </script>
5
+
6
+ <p class="mb-2 leading-relaxed text-foreground/75 last:mb-0">{@render children?.()}</p>