Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/app/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 24 additions & 2 deletions src/app/src/renderer/AddModelPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import { useSystem } from './hooks/useSystem';
import { TASK_RECIPE_MAP } from './utils/recipeCompatibility';

export interface AddModelInitialValues {
name: string;
Expand Down Expand Up @@ -58,12 +59,32 @@ const AddModelPanel: React.FC<AddModelPanelProps> = ({ onClose, onInstall, initi
useEffect(() => {
const newForm = createEmptyForm(initialValues);
if (initialValues?.mmprojOptions && initialValues.mmprojOptions.length > 0) {
newForm.mmproj = initialValues.mmprojOptions[0];
// Prefer BF16 > F16 > F32 > first available
const priority = [/bf16/i, /f16/i, /f32/i];
let best = initialValues.mmprojOptions[0];
for (const pattern of priority) {
const match = initialValues.mmprojOptions.find(f => pattern.test(f));
if (match) { best = match; break; }
}
newForm.mmproj = best;
}
setForm(newForm);
setError(null);
}, [initialValues]);

// Detect recipe/name mismatch — warn when checkpoint suggests a different modality
const recipeMismatchWarning = useMemo(() => {
const checkpoint = form.checkpoint.toLowerCase();
const name = form.name.toLowerCase();
const combined = `${checkpoint} ${name}`;
for (const mapping of TASK_RECIPE_MAP) {
if (mapping.namePatterns.some(p => p.test(combined)) && form.recipe !== mapping.recipe) {
return `This looks like a ${mapping.label} model. The selected recipe (${RECIPE_LABELS[form.recipe] ?? form.recipe}) may not be compatible.`;
}
}
return null;
}, [form.checkpoint, form.name, form.recipe]);

const handleChange = (field: string, value: string | boolean) => {
setForm(prev => ({ ...prev, [field]: value }));
setError(null);
Expand Down Expand Up @@ -252,6 +273,7 @@ const AddModelPanel: React.FC<AddModelPanelProps> = ({ onClose, onInstall, initi
</div>
</div>

{recipeMismatchWarning && <div className="form-warning">{recipeMismatchWarning}</div>}
{error && <div className="form-error">{error}</div>}
</div>

Expand Down
40 changes: 36 additions & 4 deletions src/app/src/renderer/ConfirmDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import React, { useEffect, useRef, useState } from 'react';

interface KeepFilesOption {
label: string;
defaultChecked?: boolean;
}

interface ConfirmDialogProps {
isOpen: boolean;
title: string;
message: string;
confirmText?: string;
cancelText?: string;
danger?: boolean;
keepFilesOption?: KeepFilesOption;
keepFiles?: boolean;
onKeepFilesChange?: (keepFiles: boolean) => void;
onConfirm: () => void;
onCancel: () => void;
}
Expand All @@ -18,6 +26,9 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
confirmText = 'Confirm',
cancelText = 'Cancel',
danger = false,
keepFilesOption,
keepFiles,
onKeepFilesChange,
onConfirm,
onCancel
}) => {
Expand Down Expand Up @@ -59,6 +70,16 @@ const ConfirmDialog: React.FC<ConfirmDialogProps> = ({
<div className="confirm-dialog" ref={dialogRef}>
<h3 className="confirm-dialog-title">{title}</h3>
<p className="confirm-dialog-message">{message}</p>
{keepFilesOption && (
<label className="confirm-dialog-checkbox">
<input
type="checkbox"
checked={keepFiles ?? false}
onChange={(e) => onKeepFilesChange?.(e.target.checked)}
/>
<span>{keepFilesOption.label}</span>
</label>
)}
<div className="confirm-dialog-actions">
<button
className="confirm-dialog-btn confirm-dialog-btn-cancel"
Expand Down Expand Up @@ -86,6 +107,12 @@ interface ConfirmOptions {
confirmText?: string;
cancelText?: string;
danger?: boolean;
keepFilesOption?: KeepFilesOption;
}

export interface ConfirmResult {
confirmed: boolean;
keepFiles: boolean;
}

export const useConfirmDialog = () => {
Expand All @@ -94,10 +121,12 @@ export const useConfirmDialog = () => {
title: '',
message: '',
});
const resolveRef = useRef<((value: boolean) => void) | null>(null);
const resolveRef = useRef<((value: ConfirmResult) => void) | null>(null);
const [keepFiles, setKeepFiles] = useState(false);

const confirm = (opts: ConfirmOptions): Promise<boolean> => {
const confirm = (opts: ConfirmOptions): Promise<ConfirmResult> => {
return new Promise((resolve) => {
setKeepFiles(opts.keepFilesOption?.defaultChecked ?? false);
setOptions(opts);
setIsOpen(true);
resolveRef.current = resolve;
Expand All @@ -106,12 +135,12 @@ export const useConfirmDialog = () => {

const handleConfirm = () => {
setIsOpen(false);
resolveRef.current?.(true);
resolveRef.current?.({ confirmed: true, keepFiles });
};

const handleCancel = () => {
setIsOpen(false);
resolveRef.current?.(false);
resolveRef.current?.({ confirmed: false, keepFiles: false });
};

const ConfirmDialogComponent = () => (
Expand All @@ -122,6 +151,9 @@ export const useConfirmDialog = () => {
confirmText={options.confirmText}
cancelText={options.cancelText}
danger={options.danger}
keepFilesOption={options.keepFilesOption}
keepFiles={keepFiles}
onKeepFilesChange={(v) => setKeepFiles(v)}
onConfirm={handleConfirm}
onCancel={handleCancel}
/>
Expand Down
Loading