mishig HF Staff commited on
Commit
80ab124
·
verified ·
1 Parent(s): 7cdbacf

Update source

Browse files
Files changed (5) hide show
  1. package-lock.json +28 -3
  2. package.json +4 -0
  3. src/app.css +102 -0
  4. src/lib/TraceViewer.svelte +27 -10
  5. src/lib/markdown.js +13 -0
package-lock.json CHANGED
@@ -1,12 +1,16 @@
1
  {
2
- "name": "traces-viewer",
3
  "version": "0.1.0",
4
  "lockfileVersion": 3,
5
  "requires": true,
6
  "packages": {
7
  "": {
8
- "name": "traces-viewer",
9
  "version": "0.1.0",
 
 
 
 
10
  "devDependencies": {
11
  "@sveltejs/vite-plugin-svelte": "^5.0.3",
12
  "@tailwindcss/vite": "^4.2.2",
@@ -1190,7 +1194,7 @@
1190
  "version": "2.0.7",
1191
  "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
1192
  "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
1193
- "dev": true,
1194
  "license": "MIT"
1195
  },
1196
  "node_modules/acorn": {
@@ -1281,6 +1285,15 @@
1281
  "dev": true,
1282
  "license": "MIT"
1283
  },
 
 
 
 
 
 
 
 
 
1284
  "node_modules/enhanced-resolve": {
1285
  "version": "5.20.1",
1286
  "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
@@ -1710,6 +1723,18 @@
1710
  "@jridgewell/sourcemap-codec": "^1.5.5"
1711
  }
1712
  },
 
 
 
 
 
 
 
 
 
 
 
 
1713
  "node_modules/ms": {
1714
  "version": "2.1.3",
1715
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
 
1
  {
2
+ "name": "traces-replay",
3
  "version": "0.1.0",
4
  "lockfileVersion": 3,
5
  "requires": true,
6
  "packages": {
7
  "": {
8
+ "name": "traces-replay",
9
  "version": "0.1.0",
10
+ "dependencies": {
11
+ "dompurify": "^3.4.0",
12
+ "marked": "^18.0.2"
13
+ },
14
  "devDependencies": {
15
  "@sveltejs/vite-plugin-svelte": "^5.0.3",
16
  "@tailwindcss/vite": "^4.2.2",
 
1194
  "version": "2.0.7",
1195
  "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
1196
  "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
1197
+ "devOptional": true,
1198
  "license": "MIT"
1199
  },
1200
  "node_modules/acorn": {
 
1285
  "dev": true,
1286
  "license": "MIT"
1287
  },
1288
+ "node_modules/dompurify": {
1289
+ "version": "3.4.0",
1290
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz",
1291
+ "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==",
1292
+ "license": "(MPL-2.0 OR Apache-2.0)",
1293
+ "optionalDependencies": {
1294
+ "@types/trusted-types": "^2.0.7"
1295
+ }
1296
+ },
1297
  "node_modules/enhanced-resolve": {
1298
  "version": "5.20.1",
1299
  "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz",
 
1723
  "@jridgewell/sourcemap-codec": "^1.5.5"
1724
  }
1725
  },
1726
+ "node_modules/marked": {
1727
+ "version": "18.0.2",
1728
+ "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.2.tgz",
1729
+ "integrity": "sha512-NsmlUYBS/Zg57rgDWMYdnre6OTj4e+qq/JS2ot3KrYLSoHLw+sDu0Nm1ZGpRgYAq6c+b1ekaY5NzVchMCQnzcg==",
1730
+ "license": "MIT",
1731
+ "bin": {
1732
+ "marked": "bin/marked.js"
1733
+ },
1734
+ "engines": {
1735
+ "node": ">= 20"
1736
+ }
1737
+ },
1738
  "node_modules/ms": {
1739
  "version": "2.1.3",
1740
  "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
package.json CHANGED
@@ -14,5 +14,9 @@
14
  "svelte": "^5.16.1",
15
  "tailwindcss": "^4.2.2",
16
  "vite": "^6.0.7"
 
 
 
 
17
  }
18
  }
 
14
  "svelte": "^5.16.1",
15
  "tailwindcss": "^4.2.2",
16
  "vite": "^6.0.7"
17
+ },
18
+ "dependencies": {
19
+ "dompurify": "^3.4.0",
20
+ "marked": "^18.0.2"
21
  }
22
  }
src/app.css CHANGED
@@ -52,6 +52,108 @@
52
  0 0 80px rgba(255, 210, 30, 0.45);
53
  }
54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  @utility page-bg {
56
  background:
57
  radial-gradient(at 20% 20%, #fff4b8 0%, transparent 55%),
 
52
  0 0 80px rgba(255, 210, 30, 0.45);
53
  }
54
 
55
+ @utility prose-trace {
56
+ word-break: break-word;
57
+
58
+ p {
59
+ margin: 0.4em 0;
60
+ }
61
+ p:first-child {
62
+ margin-top: 0;
63
+ }
64
+ p:last-child {
65
+ margin-bottom: 0;
66
+ }
67
+ h1, h2, h3, h4, h5, h6 {
68
+ font-weight: 600;
69
+ margin: 0.8em 0 0.3em;
70
+ line-height: 1.3;
71
+ }
72
+ h1 { font-size: 1.25em; }
73
+ h2 { font-size: 1.15em; }
74
+ h3 { font-size: 1.05em; }
75
+ h4, h5, h6 { font-size: 1em; }
76
+ ul, ol {
77
+ margin: 0.3em 0;
78
+ padding-left: 1.4em;
79
+ }
80
+ ul { list-style: disc; }
81
+ ol { list-style: decimal; }
82
+ li { margin: 0.15em 0; }
83
+ li > p { margin: 0.15em 0; }
84
+ code {
85
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, monospace;
86
+ font-size: 0.92em;
87
+ background: #f5f5f2;
88
+ border: 1px solid #eeeae0;
89
+ border-radius: 3px;
90
+ padding: 0 0.3em;
91
+ }
92
+ pre {
93
+ background: #f5f5f2;
94
+ border: 1px solid #eeeae0;
95
+ border-radius: 4px;
96
+ padding: 0.6em 0.8em;
97
+ overflow-x: auto;
98
+ margin: 0.5em 0;
99
+ line-height: 1.5;
100
+ }
101
+ pre code {
102
+ background: transparent;
103
+ border: 0;
104
+ padding: 0;
105
+ font-size: 0.92em;
106
+ }
107
+ blockquote {
108
+ border-left: 3px solid #e5e5e0;
109
+ padding-left: 0.8em;
110
+ color: #5f5f5c;
111
+ margin: 0.5em 0;
112
+ }
113
+ a {
114
+ color: #8b5cf6;
115
+ text-decoration: underline;
116
+ }
117
+ a:hover {
118
+ text-decoration: none;
119
+ }
120
+ hr {
121
+ border: 0;
122
+ border-top: 1px solid #eeeae0;
123
+ margin: 0.8em 0;
124
+ }
125
+ table {
126
+ border-collapse: collapse;
127
+ margin: 0.5em 0;
128
+ font-size: 0.95em;
129
+ }
130
+ th, td {
131
+ border: 1px solid #eeeae0;
132
+ padding: 0.3em 0.6em;
133
+ text-align: left;
134
+ }
135
+ th {
136
+ background: #faf9f5;
137
+ font-weight: 600;
138
+ }
139
+ img {
140
+ max-width: 100%;
141
+ height: auto;
142
+ border-radius: 4px;
143
+ }
144
+ }
145
+
146
+ @utility prose-thinking {
147
+ code, pre {
148
+ background: #faf5ff;
149
+ border-color: #e9d5ff;
150
+ }
151
+ blockquote {
152
+ border-left-color: #e9d5ff;
153
+ color: #7e22ce;
154
+ }
155
+ }
156
+
157
  @utility page-bg {
158
  background:
159
  radial-gradient(at 20% 20%, #fff4b8 0%, transparent 55%),
src/lib/TraceViewer.svelte CHANGED
@@ -1,6 +1,7 @@
1
  <script>
2
  import { tick, onMount, onDestroy } from 'svelte';
3
  import { parseJsonl, toRawUrl } from './parse.js';
 
4
 
5
  const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
6
  let spinnerFrame = $state(0);
@@ -449,22 +450,38 @@
449
  >
450
  <div class="flex-1 min-w-0">
451
  {#if block.kind === 'text'}
452
- <pre
453
- class="whitespace-pre-wrap break-words text-[13px] text-[#232323] leading-[1.65] font-mono">{block._typedText}{#if block._typing}<span
454
- class="animate-blink text-[#8b5cf6]"
455
- aria-hidden="true">▎</span
456
- >{/if}</pre>
 
 
 
 
 
 
 
 
457
  {:else if block.kind === 'thinking'}
458
  <details class="py-0.5" open>
459
  <summary
460
  class="cursor-pointer text-[11px] text-[#8b5cf6] font-semibold select-none hover:underline"
461
  >thinking</summary
462
  >
463
- <pre
464
- class="whitespace-pre-wrap break-words text-[12px] text-[#6b21a8] mt-1 leading-[1.65] pl-[1ch] border-l border-[#e9d5ff]">{block._typedText}{#if block._typing}<span
465
- class="animate-blink text-[#8b5cf6]"
466
- aria-hidden="true">▎</span
467
- >{/if}</pre>
 
 
 
 
 
 
 
 
468
  </details>
469
  {:else if block.kind === 'tool_call'}
470
  <div class="py-0.5">
 
1
  <script>
2
  import { tick, onMount, onDestroy } from 'svelte';
3
  import { parseJsonl, toRawUrl } from './parse.js';
4
+ import { renderMarkdown } from './markdown.js';
5
 
6
  const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
7
  let spinnerFrame = $state(0);
 
450
  >
451
  <div class="flex-1 min-w-0">
452
  {#if block.kind === 'text'}
453
+ {#if block._typing || block._typedText !== block.text}
454
+ <pre
455
+ class="whitespace-pre-wrap break-words text-[13px] text-[#232323] leading-[1.65] font-mono">{block._typedText}{#if block._typing}<span
456
+ class="animate-blink text-[#8b5cf6]"
457
+ aria-hidden="true"></span
458
+ >{/if}</pre>
459
+ {:else}
460
+ <div
461
+ class="prose-trace text-[13px] text-[#232323] leading-[1.65]"
462
+ >
463
+ {@html renderMarkdown(block.text)}
464
+ </div>
465
+ {/if}
466
  {:else if block.kind === 'thinking'}
467
  <details class="py-0.5" open>
468
  <summary
469
  class="cursor-pointer text-[11px] text-[#8b5cf6] font-semibold select-none hover:underline"
470
  >thinking</summary
471
  >
472
+ {#if block._typing || block._typedText !== block.text}
473
+ <pre
474
+ class="whitespace-pre-wrap break-words text-[12px] text-[#6b21a8] mt-1 leading-[1.65] pl-[1ch] border-l border-[#e9d5ff]">{block._typedText}{#if block._typing}<span
475
+ class="animate-blink text-[#8b5cf6]"
476
+ aria-hidden="true"></span
477
+ >{/if}</pre>
478
+ {:else}
479
+ <div
480
+ class="prose-trace prose-thinking text-[12px] text-[#6b21a8] mt-1 leading-[1.65] pl-[1ch] border-l border-[#e9d5ff]"
481
+ >
482
+ {@html renderMarkdown(block.text)}
483
+ </div>
484
+ {/if}
485
  </details>
486
  {:else if block.kind === 'tool_call'}
487
  <div class="py-0.5">
src/lib/markdown.js ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { marked } from 'marked';
2
+ import DOMPurify from 'dompurify';
3
+
4
+ marked.setOptions({
5
+ gfm: true,
6
+ breaks: true,
7
+ });
8
+
9
+ export function renderMarkdown(text) {
10
+ if (!text) return '';
11
+ const html = marked.parse(String(text));
12
+ return DOMPurify.sanitize(html, { ADD_ATTR: ['target', 'rel'] });
13
+ }