mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-06-09 07:16:44 +02:00
59778f0196
* 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>
191 lines
4.9 KiB
Svelte
191 lines
4.9 KiB
Svelte
<script lang="ts">
|
|
import {
|
|
ChatAttachmentsPreviewCurrentItem,
|
|
ChatAttachmentsPreviewFileInfo,
|
|
ChatAttachmentsPreviewNavButtons,
|
|
ChatAttachmentsPreviewThumbnailStrip
|
|
} from '$lib/components/app';
|
|
import { modelsStore } from '$lib/stores/models.svelte';
|
|
import {
|
|
createBase64DataUrl,
|
|
formatFileSize,
|
|
getAttachmentDisplayItems,
|
|
getLanguageFromFilename,
|
|
isAudioFile,
|
|
isImageFile,
|
|
isMcpPrompt,
|
|
isMcpResource,
|
|
isPdfFile,
|
|
isTextFile
|
|
} from '$lib/utils';
|
|
|
|
interface PreviewItem {
|
|
id: string;
|
|
name: string;
|
|
size?: number;
|
|
preview?: string;
|
|
uploadedFile?: ChatUploadedFile;
|
|
attachment?: DatabaseMessageExtra;
|
|
textContent?: string;
|
|
isImage: boolean;
|
|
isAudio: boolean;
|
|
}
|
|
|
|
interface Props {
|
|
uploadedFiles?: ChatUploadedFile[];
|
|
attachments?: DatabaseMessageExtra[];
|
|
activeModelId?: string;
|
|
class?: string;
|
|
previewFocusIndex?: number;
|
|
}
|
|
|
|
let {
|
|
uploadedFiles = [],
|
|
attachments = [],
|
|
activeModelId,
|
|
class: className = '',
|
|
previewFocusIndex = 0
|
|
}: Props = $props();
|
|
|
|
let allItems = $derived(
|
|
getAttachmentDisplayItems({ uploadedFiles, attachments })
|
|
.filter((item) => !isMcpPrompt(item) && !isMcpResource(item))
|
|
.map(
|
|
(item): PreviewItem => ({
|
|
...item,
|
|
isImage: isImageFile(item.attachment, item.uploadedFile),
|
|
isAudio: isAudioFile(item.attachment, item.uploadedFile)
|
|
})
|
|
)
|
|
);
|
|
|
|
let currentIndex = $state(0);
|
|
|
|
$effect(() => {
|
|
if (previewFocusIndex >= 0 && previewFocusIndex < allItems.length) {
|
|
currentIndex = previewFocusIndex;
|
|
}
|
|
});
|
|
|
|
$effect(() => {
|
|
const handler = (e: Event) => {
|
|
const delta = (e as CustomEvent).detail;
|
|
|
|
if (delta < 0) {
|
|
currentIndex = currentIndex > 0 ? currentIndex - 1 : allItems.length - 1;
|
|
} else {
|
|
currentIndex = currentIndex < allItems.length - 1 ? currentIndex + 1 : 0;
|
|
}
|
|
};
|
|
|
|
document.addEventListener('chat-attachments-nav', handler);
|
|
|
|
return () => document.removeEventListener('chat-attachments-nav', handler);
|
|
});
|
|
|
|
$effect(() => {
|
|
const index = currentIndex;
|
|
setTimeout(() => {
|
|
const thumbnail = document.querySelector(`[data-thumbnail-index="${index}"]`);
|
|
|
|
thumbnail?.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' });
|
|
}, 0);
|
|
});
|
|
|
|
let currentItem = $derived(allItems[currentIndex] ?? null);
|
|
let displayName = $derived(
|
|
currentItem?.name ||
|
|
currentItem?.uploadedFile?.name ||
|
|
currentItem?.attachment?.name ||
|
|
'Unknown File'
|
|
);
|
|
let isAudio = $derived(
|
|
currentItem ? isAudioFile(currentItem.attachment, currentItem.uploadedFile) : false
|
|
);
|
|
let isImage = $derived(
|
|
currentItem ? isImageFile(currentItem.attachment, currentItem.uploadedFile) : false
|
|
);
|
|
let isPdf = $derived(
|
|
currentItem ? isPdfFile(currentItem.attachment, currentItem.uploadedFile) : false
|
|
);
|
|
let isText = $derived(
|
|
currentItem ? isTextFile(currentItem.attachment, currentItem.uploadedFile) : false
|
|
);
|
|
|
|
let displayPreview = $derived(
|
|
currentItem?.uploadedFile?.preview ||
|
|
(isImage && currentItem?.attachment && 'base64Url' in currentItem.attachment
|
|
? currentItem.attachment.base64Url
|
|
: currentItem?.preview)
|
|
);
|
|
|
|
let displayTextContent = $derived(
|
|
currentItem?.uploadedFile?.textContent ||
|
|
(currentItem?.attachment && 'content' in currentItem.attachment
|
|
? currentItem.attachment.content
|
|
: currentItem?.textContent)
|
|
);
|
|
|
|
let language = $derived(getLanguageFromFilename(displayName));
|
|
|
|
let fileSize = $derived(currentItem?.size ? formatFileSize(currentItem.size) : '');
|
|
|
|
let hasVisionModality = $derived(
|
|
currentItem && activeModelId ? modelsStore.modelSupportsVision(activeModelId) : false
|
|
);
|
|
|
|
let audioSrc = $derived(
|
|
isAudio && currentItem
|
|
? (currentItem.uploadedFile?.preview ??
|
|
(currentItem.attachment &&
|
|
'mimeType' in currentItem.attachment &&
|
|
'base64Data' in currentItem.attachment
|
|
? createBase64DataUrl(
|
|
currentItem.attachment.mimeType,
|
|
currentItem.attachment.base64Data
|
|
)
|
|
: null))
|
|
: null
|
|
);
|
|
|
|
export function prev() {
|
|
currentIndex = currentIndex > 0 ? currentIndex - 1 : allItems.length - 1;
|
|
}
|
|
|
|
export function next() {
|
|
currentIndex = currentIndex < allItems.length - 1 ? currentIndex + 1 : 0;
|
|
}
|
|
|
|
function onNavigate(index: number) {
|
|
currentIndex = index;
|
|
}
|
|
</script>
|
|
|
|
<div class="{className} flex flex-col text-white">
|
|
<div class="relative flex min-h-0 flex-1 items-center justify-center overflow-hidden">
|
|
<ChatAttachmentsPreviewNavButtons onPrev={prev} onNext={next} show={allItems.length > 1} />
|
|
|
|
<div class="flex h-full w-full flex-col items-center justify-start overflow-auto py-4">
|
|
{#if currentItem}
|
|
<ChatAttachmentsPreviewFileInfo {displayName} {fileSize} />
|
|
|
|
<ChatAttachmentsPreviewCurrentItem
|
|
{currentItem}
|
|
{isImage}
|
|
{isAudio}
|
|
{isPdf}
|
|
{isText}
|
|
{displayPreview}
|
|
{displayTextContent}
|
|
{audioSrc}
|
|
{language}
|
|
{hasVisionModality}
|
|
{activeModelId}
|
|
/>
|
|
{/if}
|
|
|
|
<ChatAttachmentsPreviewThumbnailStrip items={allItems} {currentIndex} {onNavigate} />
|
|
</div>
|
|
</div>
|
|
</div>
|