@webmcp-auto-ui/ui 2.5.25 → 2.5.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
prompt: '#6366f1',
|
|
28
28
|
schema: '#06b6d4',
|
|
29
29
|
warning: '#eab308',
|
|
30
|
+
recipe: '#ec4899',
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
function fmtTime(ts: number): string {
|
|
@@ -87,6 +88,12 @@
|
|
|
87
88
|
<span class="ac-tag" class:ac-tag-recette={prov.tag === 'recette'} class:ac-tag-impro={prov.tag === 'impro'}>{prov.tag}</span>
|
|
88
89
|
{/if}
|
|
89
90
|
<span class="ac-detail">{prov.rest}</span>
|
|
91
|
+
{:else if log.type === 'recipe'}
|
|
92
|
+
{@const sepIdx = log.detail.indexOf(' · ')}
|
|
93
|
+
{@const rid = sepIdx > 0 ? log.detail.slice(0, sepIdx) : log.detail}
|
|
94
|
+
{@const rest = sepIdx > 0 ? log.detail.slice(sepIdx + 3) : ''}
|
|
95
|
+
<span class="ac-tag ac-tag-recipe">📄 {rid}</span>
|
|
96
|
+
<span class="ac-detail">{rest}</span>
|
|
90
97
|
{:else}
|
|
91
98
|
<span class="ac-detail">
|
|
92
99
|
{log.detail}
|
|
@@ -243,6 +250,10 @@
|
|
|
243
250
|
color: #fb923c;
|
|
244
251
|
background: rgba(251, 146, 60, 0.1);
|
|
245
252
|
}
|
|
253
|
+
.ac-tag-recipe {
|
|
254
|
+
color: #ec4899;
|
|
255
|
+
background: rgba(236, 72, 153, 0.1);
|
|
256
|
+
}
|
|
246
257
|
|
|
247
258
|
.ac-clickable {
|
|
248
259
|
cursor: pointer;
|
|
@@ -1,9 +1,39 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { fly } from 'svelte/transition';
|
|
3
|
+
import { renderMarkdown } from '../primitives/markdown-renderer.js';
|
|
3
4
|
|
|
4
5
|
interface EphemeralMsg { id: string; role: 'user' | 'assistant'; html: string; }
|
|
5
6
|
interface Props { ephemeral: EphemeralMsg[]; }
|
|
6
7
|
let { ephemeral }: Props = $props();
|
|
8
|
+
|
|
9
|
+
// Detect if content has any markdown markers worth parsing.
|
|
10
|
+
// If not, we skip marked entirely and fall back to {@html} for the
|
|
11
|
+
// pre-existing HTML snippets (e.g. "<strong>tool_name</strong>").
|
|
12
|
+
const MD_RE = /(^|\n)\s*(#{1,6}\s|[-*+]\s|\d+\.\s|>\s)|\*\*|__|`|```|~~~|!\[|\[[^\]]+\]\(/;
|
|
13
|
+
function looksLikeMarkdown(src: string): boolean {
|
|
14
|
+
return MD_RE.test(src);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Gracefully close dangling code fences during streaming so that
|
|
18
|
+
// a half-received ``` block still renders as code instead of
|
|
19
|
+
// swallowing the rest of the message.
|
|
20
|
+
function closeDanglingFences(src: string): string {
|
|
21
|
+
const fences = (src.match(/```/g) ?? []).length;
|
|
22
|
+
if (fences % 2 === 1) return src + '\n```';
|
|
23
|
+
const tildes = (src.match(/~~~/g) ?? []).length;
|
|
24
|
+
if (tildes % 2 === 1) return src + '\n~~~';
|
|
25
|
+
return src;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function renderContent(src: string): string {
|
|
29
|
+
if (!src) return '';
|
|
30
|
+
if (!looksLikeMarkdown(src)) return src;
|
|
31
|
+
try {
|
|
32
|
+
return renderMarkdown(closeDanglingFences(src));
|
|
33
|
+
} catch {
|
|
34
|
+
return src;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
7
37
|
</script>
|
|
8
38
|
|
|
9
39
|
<div class="flex flex-col gap-2 items-start w-full">
|
|
@@ -13,7 +43,7 @@
|
|
|
13
43
|
out:fly={{ y: -32, duration: 450, opacity: 0 }}
|
|
14
44
|
class="ephemeral-msg {msg.role}"
|
|
15
45
|
>
|
|
16
|
-
{@html msg.html}
|
|
46
|
+
{@html renderContent(msg.html)}
|
|
17
47
|
</div>
|
|
18
48
|
{/each}
|
|
19
49
|
</div>
|
|
@@ -41,4 +71,41 @@
|
|
|
41
71
|
color: var(--color-text1);
|
|
42
72
|
align-self: flex-start;
|
|
43
73
|
}
|
|
74
|
+
/* Markdown tweaks scoped to the ephemeral bubble — keep margins tight. */
|
|
75
|
+
.ephemeral-msg :global(p) { margin: 0.25rem 0; }
|
|
76
|
+
.ephemeral-msg :global(p:first-child) { margin-top: 0; }
|
|
77
|
+
.ephemeral-msg :global(p:last-child) { margin-bottom: 0; }
|
|
78
|
+
.ephemeral-msg :global(h1),
|
|
79
|
+
.ephemeral-msg :global(h2),
|
|
80
|
+
.ephemeral-msg :global(h3),
|
|
81
|
+
.ephemeral-msg :global(h4) {
|
|
82
|
+
font-weight: 600;
|
|
83
|
+
margin: 0.35rem 0 0.25rem;
|
|
84
|
+
font-size: 0.78rem;
|
|
85
|
+
}
|
|
86
|
+
.ephemeral-msg :global(ul),
|
|
87
|
+
.ephemeral-msg :global(ol) {
|
|
88
|
+
margin: 0.3rem 0;
|
|
89
|
+
padding-left: 1.1rem;
|
|
90
|
+
}
|
|
91
|
+
.ephemeral-msg :global(li) { margin: 0.1rem 0; }
|
|
92
|
+
.ephemeral-msg :global(code) {
|
|
93
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
94
|
+
font-size: 0.66rem;
|
|
95
|
+
background: rgba(0, 0, 0, 0.28);
|
|
96
|
+
padding: 0.05rem 0.25rem;
|
|
97
|
+
border-radius: 0.2rem;
|
|
98
|
+
}
|
|
99
|
+
.ephemeral-msg :global(pre) {
|
|
100
|
+
background: rgba(0, 0, 0, 0.35);
|
|
101
|
+
border-radius: 0.3rem;
|
|
102
|
+
padding: 0.5rem;
|
|
103
|
+
margin: 0.35rem 0;
|
|
104
|
+
overflow-x: auto;
|
|
105
|
+
font-size: 0.66rem;
|
|
106
|
+
}
|
|
107
|
+
.ephemeral-msg :global(pre code) { background: transparent; padding: 0; }
|
|
108
|
+
.ephemeral-msg :global(strong) { font-weight: 600; }
|
|
109
|
+
.ephemeral-msg :global(em) { font-style: italic; }
|
|
110
|
+
.ephemeral-msg :global(a) { color: rgb(96, 165, 250); text-decoration: underline; }
|
|
44
111
|
</style>
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
onconnect?: () => void;
|
|
14
14
|
ondisconnect?: () => void;
|
|
15
15
|
class?: string;
|
|
16
|
-
compact?: boolean;
|
|
16
|
+
compact?: boolean; // hide token field entirely
|
|
17
|
+
showToken?: boolean; // toggle controlled from outside
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
let {
|
|
@@ -29,9 +30,9 @@
|
|
|
29
30
|
ondisconnect,
|
|
30
31
|
class: cls = '',
|
|
31
32
|
compact = false,
|
|
33
|
+
showToken = $bindable(false),
|
|
32
34
|
}: Props = $props();
|
|
33
35
|
|
|
34
|
-
let showToken = $state(false);
|
|
35
36
|
let debounceTimer: ReturnType<typeof setTimeout> | null = null;
|
|
36
37
|
|
|
37
38
|
function handleUrlInput(e: Event) {
|
|
@@ -82,25 +83,14 @@
|
|
|
82
83
|
<McpStatus {connecting} {connected} name={serverName || 'not connected'} />
|
|
83
84
|
</div>
|
|
84
85
|
|
|
85
|
-
{#if !compact}
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
</button>
|
|
94
|
-
{#if showToken}
|
|
95
|
-
<input
|
|
96
|
-
type="password"
|
|
97
|
-
value={token}
|
|
98
|
-
oninput={(e) => { token = (e.target as HTMLInputElement).value; onTokenChange?.(token); }}
|
|
99
|
-
placeholder="Bearer token (optional)"
|
|
100
|
-
class="flex-1 font-mono text-xs bg-surface2 border border-border2 rounded px-2 h-7 text-text1 outline-none placeholder:text-text2/40 focus:border-accent/50 transition-colors"
|
|
101
|
-
/>
|
|
102
|
-
{/if}
|
|
103
|
-
</div>
|
|
86
|
+
{#if !compact && showToken}
|
|
87
|
+
<input
|
|
88
|
+
type="password"
|
|
89
|
+
value={token}
|
|
90
|
+
oninput={(e) => { token = (e.target as HTMLInputElement).value; onTokenChange?.(token); }}
|
|
91
|
+
placeholder="Bearer token (optional)"
|
|
92
|
+
class="font-mono text-xs bg-surface2 border border-border2 rounded px-2 h-7 text-text1 outline-none placeholder:text-text2/40 focus:border-accent/50 transition-colors"
|
|
93
|
+
/>
|
|
104
94
|
{/if}
|
|
105
95
|
|
|
106
96
|
{#if error}
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
effectivePrompt?: string;
|
|
7
7
|
maxTokens?: number;
|
|
8
8
|
maxContextTokens?: number;
|
|
9
|
-
maxTools?: number;
|
|
10
|
-
maxMessages?: number;
|
|
11
9
|
maxResultLength?: number;
|
|
12
10
|
compressHistory?: boolean;
|
|
13
11
|
compressPreview?: number;
|
|
@@ -26,8 +24,6 @@
|
|
|
26
24
|
effectivePrompt = '',
|
|
27
25
|
maxTokens = $bindable(4096),
|
|
28
26
|
maxContextTokens = $bindable(150_000),
|
|
29
|
-
maxTools = $bindable(8),
|
|
30
|
-
maxMessages = $bindable(8),
|
|
31
27
|
maxResultLength = $bindable(10000),
|
|
32
28
|
compressHistory = $bindable(false),
|
|
33
29
|
compressPreview = $bindable(500),
|
|
@@ -106,15 +102,15 @@
|
|
|
106
102
|
<label class="text-[9px] font-mono text-text2 uppercase tracking-wider">System Prompt</label>
|
|
107
103
|
<div class="flex items-center gap-2">
|
|
108
104
|
{#if promptSaved && customMode}
|
|
109
|
-
<span class="text-[9px] font-mono text-teal transition-opacity">✓
|
|
105
|
+
<span class="text-[9px] font-mono text-teal transition-opacity">✓ applied</span>
|
|
110
106
|
{/if}
|
|
111
107
|
{#if hasEffective}
|
|
112
108
|
{#if customMode}
|
|
113
109
|
<button class="text-[9px] font-mono text-accent2 hover:text-accent transition-colors"
|
|
114
|
-
onclick={resetToAuto}>
|
|
110
|
+
onclick={resetToAuto}>reset</button>
|
|
115
111
|
{:else}
|
|
116
112
|
<button class="text-[9px] font-mono text-accent hover:text-text1 transition-colors"
|
|
117
|
-
onclick={enterCustomMode}>
|
|
113
|
+
onclick={enterCustomMode}>customize</button>
|
|
118
114
|
{/if}
|
|
119
115
|
{/if}
|
|
120
116
|
</div>
|
|
@@ -125,15 +121,15 @@
|
|
|
125
121
|
value={displayedPrompt}
|
|
126
122
|
rows={8}
|
|
127
123
|
class="w-full bg-surface2/50 border border-border2/50 rounded-lg px-3 py-2 text-xs font-mono text-text2 outline-none resize-none cursor-default"
|
|
128
|
-
placeholder="
|
|
124
|
+
placeholder="Auto-generated prompt"
|
|
129
125
|
></textarea>
|
|
130
|
-
<div class="text-[8px] font-mono text-text2/50">
|
|
126
|
+
<div class="text-[8px] font-mono text-text2/50">auto-generated prompt — click customize to edit</div>
|
|
131
127
|
{:else}
|
|
132
128
|
<textarea
|
|
133
129
|
bind:value={systemPrompt}
|
|
134
130
|
rows={5}
|
|
135
131
|
class="w-full bg-surface2 border border-border2 rounded-lg px-3 py-2 text-xs font-mono text-text1 outline-none resize-none focus:border-accent/50 transition-colors placeholder:text-text2/40"
|
|
136
|
-
placeholder="
|
|
132
|
+
placeholder="System instructions for the agent…"
|
|
137
133
|
></textarea>
|
|
138
134
|
{/if}
|
|
139
135
|
</div>
|
|
@@ -186,28 +182,6 @@
|
|
|
186
182
|
class="w-full accent-accent" />
|
|
187
183
|
</div>
|
|
188
184
|
|
|
189
|
-
<!-- Max tools (WASM only) -->
|
|
190
|
-
{#if modelType === 'wasm'}
|
|
191
|
-
<div>
|
|
192
|
-
<div class="flex justify-between items-baseline mb-1">
|
|
193
|
-
<span class="text-[9px] font-mono text-text2 uppercase tracking-wider">Max tools (WASM)</span>
|
|
194
|
-
<span class="font-mono text-xs text-text1">{maxTools}</span>
|
|
195
|
-
</div>
|
|
196
|
-
<input type="range" bind:value={maxTools}
|
|
197
|
-
min={4} max={20} step={1}
|
|
198
|
-
class="w-full accent-accent" />
|
|
199
|
-
</div>
|
|
200
|
-
<div>
|
|
201
|
-
<div class="flex justify-between items-baseline mb-1">
|
|
202
|
-
<span class="text-[9px] font-mono text-text2 uppercase tracking-wider">Max messages (WASM)</span>
|
|
203
|
-
<span class="font-mono text-xs text-text1">{maxMessages}</span>
|
|
204
|
-
</div>
|
|
205
|
-
<input type="range" bind:value={maxMessages}
|
|
206
|
-
min={2} max={64} step={1}
|
|
207
|
-
class="w-full accent-accent" />
|
|
208
|
-
</div>
|
|
209
|
-
{/if}
|
|
210
|
-
|
|
211
185
|
<!-- Max result length -->
|
|
212
186
|
<div>
|
|
213
187
|
<div class="flex justify-between items-baseline mb-1">
|
|
@@ -223,7 +197,7 @@
|
|
|
223
197
|
<div>
|
|
224
198
|
<label class="flex items-center gap-2 cursor-pointer select-none mb-1">
|
|
225
199
|
<input type="checkbox" bind:checked={compressHistory} class="accent-accent w-3.5 h-3.5" />
|
|
226
|
-
<span class="text-[9px] font-mono text-text2 uppercase tracking-wider">
|
|
200
|
+
<span class="text-[9px] font-mono text-text2 uppercase tracking-wider">Truncate history</span>
|
|
227
201
|
</label>
|
|
228
202
|
{#if compressHistory}
|
|
229
203
|
<div class="flex justify-between items-baseline mb-1">
|
|
@@ -248,7 +222,7 @@
|
|
|
248
222
|
</div>
|
|
249
223
|
<div class="pl-5">
|
|
250
224
|
<div class="flex justify-between items-baseline mb-1">
|
|
251
|
-
<span class="text-[9px] font-mono text-text2 uppercase tracking-wider">
|
|
225
|
+
<span class="text-[9px] font-mono text-text2 uppercase tracking-wider">Inline residue (chars)</span>
|
|
252
226
|
<span class="font-mono text-xs text-text1">{ragResidueSize}</span>
|
|
253
227
|
</div>
|
|
254
228
|
<input type="range" bind:value={ragResidueSize}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
interface Server {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
description: string;
|
|
6
|
+
widgetCount: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Props {
|
|
10
|
+
servers: Server[];
|
|
11
|
+
enabledServers?: Set<string>;
|
|
12
|
+
onToggle?: (id: string) => void;
|
|
13
|
+
recipeCountByServer?: Record<string, number>;
|
|
14
|
+
toolCountByServer?: Record<string, number>;
|
|
15
|
+
onrecipeclick?: (id: string) => void;
|
|
16
|
+
ontoolclick?: (id: string) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let {
|
|
20
|
+
servers,
|
|
21
|
+
enabledServers = new Set<string>(),
|
|
22
|
+
onToggle,
|
|
23
|
+
recipeCountByServer,
|
|
24
|
+
toolCountByServer,
|
|
25
|
+
onrecipeclick,
|
|
26
|
+
ontoolclick,
|
|
27
|
+
}: Props = $props();
|
|
28
|
+
|
|
29
|
+
let collapsed = $state(true);
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<div class="flex flex-col gap-2">
|
|
33
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
34
|
+
<div class="flex items-center gap-1 cursor-pointer select-none"
|
|
35
|
+
onclick={() => collapsed = !collapsed}>
|
|
36
|
+
<span class="text-[9px] font-mono text-text2 uppercase tracking-wider">WebMCP servers</span>
|
|
37
|
+
<span class="text-[9px] text-text2/60 font-mono">({enabledServers.size}/{servers.length})</span>
|
|
38
|
+
<span class="text-[10px] text-text2 ml-auto transition-transform {collapsed ? '' : 'rotate-90'}">{@html '▶'}</span>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
{#if !collapsed}
|
|
42
|
+
<div class="flex flex-col gap-1">
|
|
43
|
+
{#each servers as srv (srv.id)}
|
|
44
|
+
{@const enabled = enabledServers.has(srv.id)}
|
|
45
|
+
{@const recipes = recipeCountByServer?.[srv.id] ?? 0}
|
|
46
|
+
{@const tools = toolCountByServer?.[srv.id] ?? 0}
|
|
47
|
+
<div class="group flex items-center gap-2 px-2 py-1.5 rounded border border-border2 bg-surface2 hover:border-accent/30 transition-colors">
|
|
48
|
+
<input
|
|
49
|
+
type="checkbox"
|
|
50
|
+
checked={enabled}
|
|
51
|
+
onchange={() => onToggle?.(srv.id)}
|
|
52
|
+
class="w-3.5 h-3.5 rounded border-border2 accent-accent cursor-pointer flex-shrink-0"
|
|
53
|
+
/>
|
|
54
|
+
<div class="flex-1 min-w-0 flex flex-col">
|
|
55
|
+
<span class="font-mono text-xs font-medium text-text1 truncate">{srv.label}</span>
|
|
56
|
+
{#if srv.description}
|
|
57
|
+
<span class="text-[10px] text-text2 truncate">{srv.description}</span>
|
|
58
|
+
{/if}
|
|
59
|
+
{#if enabled && (recipes > 0 || tools > 0)}
|
|
60
|
+
<span class="flex items-center gap-1.5 mt-0.5">
|
|
61
|
+
{#if recipes > 0}
|
|
62
|
+
<button class="text-[10px] font-mono text-accent hover:underline"
|
|
63
|
+
onclick={(e) => { e.stopPropagation(); onrecipeclick?.(srv.id); }}>
|
|
64
|
+
{recipes} recipes
|
|
65
|
+
</button>
|
|
66
|
+
{/if}
|
|
67
|
+
{#if recipes > 0 && tools > 0}
|
|
68
|
+
<span class="text-[10px] text-text2">·</span>
|
|
69
|
+
{/if}
|
|
70
|
+
{#if tools > 0}
|
|
71
|
+
<button class="text-[10px] font-mono text-accent hover:underline"
|
|
72
|
+
onclick={(e) => { e.stopPropagation(); ontoolclick?.(srv.id); }}>
|
|
73
|
+
{tools} tools
|
|
74
|
+
</button>
|
|
75
|
+
{/if}
|
|
76
|
+
</span>
|
|
77
|
+
{/if}
|
|
78
|
+
</div>
|
|
79
|
+
<span class="text-[9px] font-mono text-text2/50 flex-shrink-0">{srv.widgetCount}w</span>
|
|
80
|
+
</div>
|
|
81
|
+
{/each}
|
|
82
|
+
</div>
|
|
83
|
+
{/if}
|
|
84
|
+
</div>
|
package/src/index.ts
CHANGED
|
@@ -95,6 +95,7 @@ export type { ChatFeedItem, ChatBubble, ChatBlock } from './agent/ChatPanel.svel
|
|
|
95
95
|
export { default as AgentConsole } from './agent/AgentConsole.svelte';
|
|
96
96
|
export { default as SettingsPanel } from './agent/SettingsPanel.svelte';
|
|
97
97
|
export { default as RemoteMCPserversDemo } from './agent/RemoteMCPserversDemo.svelte';
|
|
98
|
+
export { default as WebMCPserversList } from './agent/WebMCPserversList.svelte';
|
|
98
99
|
export { default as EphemeralBubble } from './agent/EphemeralBubble.svelte';
|
|
99
100
|
export { default as TokenBubble } from './agent/TokenBubble.svelte';
|
|
100
101
|
export { default as DiagnosticModal } from './agent/DiagnosticModal.svelte';
|