* webui: Move static build output from `tools/server/public` to `build/ui` directory * refactor: Move to `tools/ui` * refactor: rename CMake variables and preprocessor defines - Rename LLAMA_BUILD_WEBUI -> LLAMA_BUILD_UI (old kept as deprecated) - Rename LLAMA_USE_PREBUILT_WEBUI -> LLAMA_USE_PREBUILT_UI (old kept as deprecated) - Backward compat: old vars auto-forward to new ones with DEPRECATION warning - Rename internal vars: WEBUI_SOURCE -> UI_SOURCE, WEBUI_SOURCE_DIR -> UI_SOURCE_DIR, etc. - Rename HF bucket: LLAMA_WEBUI_HF_BUCKET -> LLAMA_UI_HF_BUCKET - Emit both LLAMA_BUILD_WEBUI and LLAMA_BUILD_UI preprocessor defines - Emit both LLAMA_WEBUI_DEFAULT_ENABLED and LLAMA_UI_DEFAULT_ENABLED * refactor: rename CLI flags (--webui -> --ui) with backward compat - Add --ui/--no-ui (old --webui/--no-webui kept as deprecated aliases) - Add --ui-config (old --webui-config kept as deprecated alias) - Add --ui-config-file (old --webui-config-file kept as deprecated alias) - Add --ui-mcp-proxy/--no-ui-mcp-proxy (old --webui-mcp-proxy kept as deprecated) - Add new env vars: LLAMA_ARG_UI, LLAMA_ARG_UI_CONFIG, LLAMA_ARG_UI_CONFIG_FILE, LLAMA_ARG_UI_MCP_PROXY - C++ struct fields: params.ui, params.ui_config_json, params.ui_mcp_proxy added alongside old fields - Backward compat: old fields synced to new ones in g_params_to_internals * refactor: update C++ server internals with backward compat - Rename json_webui_settings -> json_ui_settings (both kept in server_context_meta) - Rename params.webui usage -> params.ui (both synced, old still works) - JSON API emits both "ui"/"ui_settings" and "webui"/"webui_settings" keys - Server routes use params.ui_mcp_proxy || params.webui_mcp_proxy - Preprocessor guards use #if defined(LLAMA_BUILD_UI) || defined(LLAMA_BUILD_WEBUI) * refactor: rename CI/CD workflows, artifacts, and build script - Rename webui-build.yml -> ui-build.yml; artifact webui-build -> ui-build - Rename webui-publish.yml -> ui-publish.yml; var HF_BUCKET_WEBUI_STATIC_OUTPUT -> HF_BUCKET_UI_STATIC_OUTPUT - Rename server-webui.yml -> server-ui.yml; job webui-build/checks -> ui-build/checks - Update server.yml: job/artifact refs webui-build -> ui-build - Update release.yml: all webui-build/publish refs -> ui-build/publish; HF_TOKEN_WEBUI_STATIC_OUTPUT -> HF_TOKEN_UI_STATIC_OUTPUT - Update server-self-hosted.yml: webui-build -> ui-build - Update build-self-hosted.yml: HF_WEBUI_VERSION -> HF_UI_VERSION - Rename webui-download.cmake -> ui-download.cmake (internal refs updated) - Update labeler.yml: server/webui -> server/ui path label * docs: update CODEOWNERS and server README docs - Update CODEOWNERS: team ggml-org/llama-webui -> ggml-org/llama-ui, path /tools/server/webui/ -> /tools/ui/ - Update server README.md: CLI tables show --ui flags with deprecated --webui aliases - Update server README-dev.md: "WebUI" -> "UI", paths updated to tools/ui/ * fix: Small fixes for UI build * fix: CMake.txt syntax * chore: Formatting * fix: `.editorconfig` for llama-ui * chore: Formatting * refactor: Use `APP_NAME` in Error route * refactor: Cleanup * refactor: Single migration service * make llama-ui a linkable target * fix: UI Build output * fix: Missing change * fix: separate llama-ui npm build output into build/tools/ui/dist subfolder + use cmake npm build instead of downloading ui-build.yml artifacts in CI * refactor: UI workflows cleanup --------- Co-authored-by: Xuan Son Nguyen <son@huggingface.co>
123 lines
3.7 KiB
Svelte
123 lines
3.7 KiB
Svelte
<script lang="ts">
|
|
import * as Dialog from '$lib/components/ui/dialog';
|
|
import { Download } from '@lucide/svelte';
|
|
import { Button } from '$lib/components/ui/button';
|
|
import { mcpStore } from '$lib/stores/mcp.svelte';
|
|
import { SyntaxHighlightedCode, ActionIconCopyToClipboard } from '$lib/components/app';
|
|
import {
|
|
getLanguageFromFilename,
|
|
isCodeResource,
|
|
isImageResource,
|
|
downloadResourceContent
|
|
} from '$lib/utils';
|
|
import { MimeTypeIncludes, MimeTypeText } from '$lib/enums';
|
|
import { DEFAULT_RESOURCE_FILENAME } from '$lib/constants';
|
|
import type { DatabaseMessageExtraMcpResource } from '$lib/types';
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onOpenChange?: (open: boolean) => void;
|
|
extra: DatabaseMessageExtraMcpResource;
|
|
}
|
|
|
|
let { open = $bindable(), onOpenChange, extra }: Props = $props();
|
|
|
|
const serverName = $derived(mcpStore.getServerDisplayName(extra.serverName));
|
|
const favicon = $derived(mcpStore.getServerFavicon(extra.serverName));
|
|
|
|
function getLanguage(): string {
|
|
if (extra.mimeType?.includes(MimeTypeIncludes.JSON)) return MimeTypeIncludes.JSON;
|
|
if (extra.mimeType?.includes(MimeTypeIncludes.JAVASCRIPT)) return MimeTypeIncludes.JAVASCRIPT;
|
|
if (extra.mimeType?.includes(MimeTypeIncludes.TYPESCRIPT)) return MimeTypeIncludes.TYPESCRIPT;
|
|
|
|
const name = extra.name || extra.uri || '';
|
|
|
|
return getLanguageFromFilename(name) || 'plaintext';
|
|
}
|
|
|
|
function handleDownload() {
|
|
if (!extra.content) return;
|
|
|
|
downloadResourceContent(
|
|
extra.content,
|
|
extra.mimeType || MimeTypeText.PLAIN,
|
|
extra.name || DEFAULT_RESOURCE_FILENAME
|
|
);
|
|
}
|
|
</script>
|
|
|
|
<Dialog.Root bind:open {onOpenChange}>
|
|
<Dialog.Content class="grid max-h-[90vh] max-w-5xl overflow-hidden sm:w-auto sm:max-w-6xl">
|
|
<Dialog.Header>
|
|
<Dialog.Title class="pr-8">{extra.name}</Dialog.Title>
|
|
|
|
<Dialog.Description>
|
|
<div class="flex items-center gap-2">
|
|
<span class="text-xs text-muted-foreground">{extra.uri}</span>
|
|
|
|
{#if serverName}
|
|
<span class="flex items-center gap-1 text-xs text-muted-foreground">
|
|
·
|
|
{#if favicon}
|
|
<img
|
|
src={favicon}
|
|
alt=""
|
|
class="h-3 w-3 shrink-0 rounded-sm"
|
|
onerror={(e) => {
|
|
(e.currentTarget as HTMLImageElement).style.display = 'none';
|
|
}}
|
|
/>
|
|
{/if}
|
|
{serverName}
|
|
</span>
|
|
{/if}
|
|
|
|
{#if extra.mimeType}
|
|
<span class="rounded bg-muted px-1.5 py-0.5 text-xs">{extra.mimeType}</span>
|
|
{/if}
|
|
</div>
|
|
</Dialog.Description>
|
|
</Dialog.Header>
|
|
|
|
<div class="flex items-center justify-end gap-1">
|
|
<ActionIconCopyToClipboard
|
|
text={extra.content}
|
|
canCopy={!!extra.content}
|
|
ariaLabel="Copy content"
|
|
/>
|
|
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
class="h-7 w-7 p-0"
|
|
onclick={handleDownload}
|
|
disabled={!extra.content}
|
|
title="Download content"
|
|
>
|
|
<Download class="h-3.5 w-3.5" />
|
|
</Button>
|
|
</div>
|
|
|
|
<div class="overflow-auto">
|
|
{#if isImageResource(extra.mimeType, extra.uri) && extra.content}
|
|
<div class="flex items-center justify-center">
|
|
<img
|
|
src={extra.content.startsWith('data:')
|
|
? extra.content
|
|
: `data:${extra.mimeType || 'image/png'};base64,${extra.content}`}
|
|
alt={extra.name}
|
|
class="max-h-[70vh] max-w-full rounded object-contain"
|
|
/>
|
|
</div>
|
|
{:else if isCodeResource(extra.mimeType, extra.uri) && extra.content}
|
|
<SyntaxHighlightedCode code={extra.content} language={getLanguage()} maxHeight="70vh" />
|
|
{:else if extra.content}
|
|
<pre
|
|
class="max-h-[70vh] overflow-auto rounded-md border bg-muted/30 p-4 font-mono text-sm break-words whitespace-pre-wrap">{extra.content}</pre>
|
|
{:else}
|
|
<div class="py-8 text-center text-sm text-muted-foreground">No content available</div>
|
|
{/if}
|
|
</div>
|
|
</Dialog.Content>
|
|
</Dialog.Root>
|