Spaces:
Running
Running
Update source
Browse files- package-lock.json +28 -3
- package.json +4 -0
- src/app.css +102 -0
- src/lib/TraceViewer.svelte +27 -10
- src/lib/markdown.js +13 -0
package-lock.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
| 1 |
{
|
| 2 |
-
"name": "traces-
|
| 3 |
"version": "0.1.0",
|
| 4 |
"lockfileVersion": 3,
|
| 5 |
"requires": true,
|
| 6 |
"packages": {
|
| 7 |
"": {
|
| 8 |
-
"name": "traces-
|
| 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 |
-
"
|
| 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 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
+
}
|