fix(FN-000): add qmd install button
This commit is contained in:
@@ -26,6 +26,7 @@ const mockFetchGlobalConcurrency = vi.fn();
|
||||
const mockUpdateGlobalConcurrency = vi.fn();
|
||||
const mockFetchMemoryBackendStatus = vi.fn();
|
||||
const mockTestMemoryRetrieval = vi.fn();
|
||||
const mockInstallQmd = vi.fn();
|
||||
|
||||
vi.mock("../api", () => ({
|
||||
fetchSettings: (...args: unknown[]) => mockFetchSettings(...args),
|
||||
@@ -49,6 +50,7 @@ vi.mock("../api", () => ({
|
||||
updateGlobalConcurrency: (...args: unknown[]) => mockUpdateGlobalConcurrency(...args),
|
||||
fetchMemoryBackendStatus: (...args: unknown[]) => mockFetchMemoryBackendStatus(...args),
|
||||
testMemoryRetrieval: (...args: unknown[]) => mockTestMemoryRetrieval(...args),
|
||||
installQmd: (...args: unknown[]) => mockInstallQmd(...args),
|
||||
}));
|
||||
|
||||
// Mock the hook
|
||||
@@ -127,6 +129,11 @@ describe("SettingsModal", () => {
|
||||
qmdInstallCommand: "bun add -g qmd",
|
||||
results: [],
|
||||
});
|
||||
mockInstallQmd.mockResolvedValue({
|
||||
success: true,
|
||||
qmdAvailable: true,
|
||||
qmdInstallCommand: "bun add -g qmd",
|
||||
});
|
||||
mockImportSettings.mockResolvedValue({ success: true, globalCount: 0, projectCount: 0 });
|
||||
mockFetchGlobalConcurrency.mockResolvedValue({ globalMaxConcurrent: 4, currentlyActive: 0, queuedCount: 0, projectsActive: {} });
|
||||
mockUpdateGlobalConcurrency.mockResolvedValue({ globalMaxConcurrent: 4, currentlyActive: 0, queuedCount: 0, projectsActive: {} });
|
||||
@@ -434,6 +441,53 @@ describe("SettingsModal", () => {
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
it("installs qmd from the missing qmd prompt", async () => {
|
||||
const addToast = vi.fn();
|
||||
const refresh = vi.fn(() => Promise.resolve());
|
||||
mockUseMemoryBackendStatus.mockReturnValue({
|
||||
status: {
|
||||
currentBackend: "qmd",
|
||||
capabilities: {
|
||||
readable: true,
|
||||
writable: true,
|
||||
supportsAtomicWrite: false,
|
||||
hasConflictResolution: false,
|
||||
persistent: true,
|
||||
},
|
||||
availableBackends: ["file", "readonly", "qmd"],
|
||||
qmdAvailable: false,
|
||||
qmdInstallCommand: "bun add -g qmd",
|
||||
},
|
||||
currentBackend: "qmd",
|
||||
capabilities: {
|
||||
readable: true,
|
||||
writable: true,
|
||||
supportsAtomicWrite: false,
|
||||
hasConflictResolution: false,
|
||||
persistent: true,
|
||||
},
|
||||
availableBackends: ["file", "readonly", "qmd"],
|
||||
loading: false,
|
||||
error: null,
|
||||
refresh,
|
||||
});
|
||||
|
||||
renderModal({ addToast });
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockFetchSettings).toHaveBeenCalled();
|
||||
});
|
||||
await userEvent.click(screen.getByText("Memory"));
|
||||
|
||||
await userEvent.click(await screen.findByRole("button", { name: "Install qmd" }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockInstallQmd).toHaveBeenCalledWith(undefined);
|
||||
});
|
||||
expect(refresh).toHaveBeenCalled();
|
||||
expect(addToast).toHaveBeenCalledWith("qmd installed successfully", "success");
|
||||
});
|
||||
|
||||
it("loads and shows memory editor content when navigating to Memory", async () => {
|
||||
renderModal();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useEffect, useCallback, useRef } from "react";
|
||||
import { Globe, Folder } from "lucide-react";
|
||||
import { THINKING_LEVELS, PROMPT_KEY_CATALOG, isGlobalSettingsKey, isProjectSettingsKey } from "@fusion/core";
|
||||
import type { Settings, GlobalSettings, ThemeMode, ColorTheme, ModelPreset, NtfyNotificationEvent, PromptKey, AgentPromptsConfig } from "@fusion/core";
|
||||
import { fetchSettings, fetchSettingsByScope, updateSettings, updateGlobalSettings, fetchAuthStatus, loginProvider, logoutProvider, saveApiKey, clearApiKey, fetchModels, testNtfyNotification, fetchBackups, createBackup, exportSettings, importSettings, fetchMemoryFile, fetchMemoryFiles, saveMemoryFile, fetchGlobalConcurrency, updateGlobalConcurrency, fetchPiExtensions, updatePiExtensions, testMemoryRetrieval } from "../api";
|
||||
import { fetchSettings, fetchSettingsByScope, updateSettings, updateGlobalSettings, fetchAuthStatus, loginProvider, logoutProvider, saveApiKey, clearApiKey, fetchModels, testNtfyNotification, fetchBackups, createBackup, exportSettings, importSettings, fetchMemoryFile, fetchMemoryFiles, saveMemoryFile, fetchGlobalConcurrency, updateGlobalConcurrency, fetchPiExtensions, updatePiExtensions, installQmd, testMemoryRetrieval } from "../api";
|
||||
import type { AuthProvider, ModelInfo, BackupListResponse, SettingsExportData, MemoryBackendCapabilities, MemoryFileInfo, MemoryRetrievalTestResult, PiExtensionSettings } from "../api";
|
||||
import { useMemoryBackendStatus } from "../hooks/useMemoryBackendStatus";
|
||||
import type { ToastType } from "../hooks/useToast";
|
||||
@@ -190,6 +190,7 @@ export function SettingsModal({
|
||||
const [memoryTestQuery, setMemoryTestQuery] = useState("");
|
||||
const [memoryTestLoading, setMemoryTestLoading] = useState(false);
|
||||
const [memoryTestResult, setMemoryTestResult] = useState<MemoryRetrievalTestResult | null>(null);
|
||||
const [qmdInstallLoading, setQmdInstallLoading] = useState(false);
|
||||
|
||||
// Global concurrency state
|
||||
const [globalMaxConcurrent, setGlobalMaxConcurrent] = useState<number | undefined>(4);
|
||||
@@ -209,6 +210,7 @@ export function SettingsModal({
|
||||
capabilities: memoryCapabilities,
|
||||
loading: memoryBackendLoading,
|
||||
error: memoryBackendError,
|
||||
refresh: refreshMemoryBackend,
|
||||
} = useMemoryBackendStatus({ projectId });
|
||||
|
||||
useEffect(() => {
|
||||
@@ -896,6 +898,22 @@ export function SettingsModal({
|
||||
}
|
||||
}, [memoryTestQuery, projectId, addToast]);
|
||||
|
||||
const handleInstallQmd = useCallback(async () => {
|
||||
setQmdInstallLoading(true);
|
||||
try {
|
||||
const result = await installQmd(projectId);
|
||||
await refreshMemoryBackend();
|
||||
addToast(
|
||||
result.qmdAvailable ? "qmd installed successfully" : "qmd install finished, but qmd is still unavailable",
|
||||
result.qmdAvailable ? "success" : "warning",
|
||||
);
|
||||
} catch (err: any) {
|
||||
addToast(err?.message || "Failed to install qmd", "error");
|
||||
} finally {
|
||||
setQmdInstallLoading(false);
|
||||
}
|
||||
}, [projectId, refreshMemoryBackend, addToast]);
|
||||
|
||||
const savePresetDraft = () => {
|
||||
if (!presetDraft) return;
|
||||
|
||||
@@ -2222,8 +2240,18 @@ export function SettingsModal({
|
||||
|
||||
{backendStatus?.qmdAvailable === false && (
|
||||
<div className="settings-empty-state memory-status-message">
|
||||
qmd is not installed. Search will use local files.
|
||||
Install indexed retrieval: <code>{backendStatus.qmdInstallCommand || "bun add -g qmd"}</code>
|
||||
<span>
|
||||
qmd is not installed. Search will use local files.
|
||||
Install indexed retrieval: <code>{backendStatus.qmdInstallCommand || "bun add -g qmd"}</code>
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-secondary btn-sm"
|
||||
onClick={handleInstallQmd}
|
||||
disabled={qmdInstallLoading}
|
||||
>
|
||||
{qmdInstallLoading ? "Installing…" : "Install qmd"}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ vi.mock("../../api", () => ({
|
||||
fetchMemoryFile: vi.fn((path = ".fusion/memory/DREAMS.md") => Promise.resolve({ path, content: "" })),
|
||||
saveMemoryFile: vi.fn(() => Promise.resolve({ success: true })),
|
||||
compactMemory: vi.fn(() => Promise.resolve({ content: "# Compacted Memory\n\nImportant content." })),
|
||||
installQmd: vi.fn(() => Promise.resolve({ success: true, qmdAvailable: true, qmdInstallCommand: "bun add -g qmd" })),
|
||||
testMemoryRetrieval: vi.fn(() => Promise.resolve({
|
||||
query: "project memory",
|
||||
qmdAvailable: true,
|
||||
|
||||
@@ -69,6 +69,7 @@ vi.mock("../../api", () => ({
|
||||
})),
|
||||
fetchMemoryFile: vi.fn((path = ".fusion/memory/DREAMS.md") => Promise.resolve({ path, content: "" })),
|
||||
saveMemoryFile: vi.fn(() => Promise.resolve({ success: true })),
|
||||
installQmd: vi.fn(() => Promise.resolve({ success: true, qmdAvailable: true, qmdInstallCommand: "bun add -g qmd" })),
|
||||
testMemoryRetrieval: vi.fn(() => Promise.resolve({
|
||||
query: "project memory",
|
||||
qmdAvailable: true,
|
||||
|
||||
Reference in New Issue
Block a user