Skip to content

Commit fb0fcd5

Browse files
authored
Merge pull request #446 from rabea-al/fix/template-install-check
🐛 Fix template library install checks
2 parents 6b59aba + 92ce9e6 commit fb0fcd5

File tree

3 files changed

+60
-28
lines changed

3 files changed

+60
-28
lines changed

src/context-menu/TrayContextMenu.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,34 @@ export async function handleInstall(
5555
}
5656
}
5757

58+
export async function installLibrarySilently(app: any, libraryName: string): Promise<boolean> {
59+
const normalized = normalizeLibraryName(libraryName);
60+
61+
const installPromise = requestAPI<any>('library/install', {
62+
method: 'POST',
63+
body: JSON.stringify({ libraryName: normalized })
64+
});
65+
66+
Notification.promise(installPromise, {
67+
pending: { message: `Installing ${libraryName} library...`, options: { autoClose: 3000 } },
68+
success: { message: () => `Library ${libraryName} installed successfully.`, options: { autoClose: 3000 } },
69+
error: { message: (err) => `Failed to install ${libraryName}: ${err}`, options: { autoClose: false } }
70+
});
71+
72+
try {
73+
const res = await installPromise;
74+
if (res.status === 'OK') {
75+
await app.commands.execute(commandIDs.refreshComponentList);
76+
return true;
77+
}
78+
console.error(`Installation failed: ${res.error || 'Unknown error'}`);
79+
return false;
80+
} catch (e) {
81+
console.error('Installation error:', e);
82+
return false;
83+
}
84+
}
85+
5886
export interface TrayContextMenuProps {
5987
app: any;
6088
x: number;

src/helpers/notificationEffects.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ function pathToLibraryId(rawPath?: string | null): string | null {
6565
return normalizeLibraryName(m[1]);
6666
}
6767

68-
async function loadLibraryIndex(): Promise<Map<string, LibraryEntry>> {
68+
export async function loadLibraryIndex(): Promise<Map<string, LibraryEntry>> {
6969
const res: any = await requestAPI('library/get_config', { method: 'GET' });
7070
const libs = res?.config?.libraries;
7171
if (!Array.isArray(libs)) throw new Error('Invalid library response');

src/index.tsx

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ import type { Signal } from "@lumino/signaling";
3333
import { commandIDs } from "./commands/CommandIDs";
3434
import { IEditorTracker } from '@jupyterlab/fileeditor';
3535
import { IMainMenu } from '@jupyterlab/mainmenu';
36-
import { handleInstall } from './context-menu/TrayContextMenu';
37-
36+
import { installLibrarySilently } from './context-menu/TrayContextMenu';
37+
import { normalizeLibraryName } from './tray_library/ComponentLibraryConfig';
38+
import { loadLibraryIndex } from './helpers/notificationEffects';
3839
import { installComponentPreview } from './component_info_sidebar/previewHelper';
3940
const FACTORY = 'Xircuits editor';
4041

@@ -578,22 +579,17 @@ const xircuits: JupyterFrontEndPlugin<void> = {
578579
});
579580
}
580581

581-
async function getInstalledLibraries(): Promise<Set<string>> {
582-
try {
583-
const result = await requestAPI<any>('library/get_config', {
584-
method: 'GET'
585-
});
586-
587-
return new Set<string>(
588-
result.config.libraries
589-
.filter((lib: any) => lib.status === 'installed' && typeof lib.name === 'string')
590-
.map((lib: any) => lib.name)
591-
);
592-
} catch (err) {
593-
console.error('Failed to load library config via API:', err);
594-
return new Set();
582+
async function getInstalledIds(): Promise<Set<string>> {
583+
const idx = await loadLibraryIndex();
584+
const set = new Set<string>();
585+
for (const [id, entry] of idx) {
586+
if (String(entry.status).toLowerCase() === 'installed') {
587+
set.add(id);
588+
}
595589
}
590+
return set;
596591
}
592+
597593
app.commands.addCommand(commandIDs.fetchExamples, {
598594
label: 'Fetch Example Workflows',
599595
caption: 'Fetch example workflows into the examples directory',
@@ -615,24 +611,32 @@ const xircuits: JupyterFrontEndPlugin<void> = {
615611
icon: xircuitsIcon,
616612
execute: async () => {
617613
const currentPath = browserFactory.tracker.currentWidget?.model.path ?? '';
618-
const installedLibs = await getInstalledLibraries();
614+
const installedIds = await getInstalledIds();
619615

620-
for (const lib of libraries) {
621-
if (installedLibs.has(lib)) {
622-
console.log(`Library ${lib} already installed. Skipping.`);
623-
continue;
624-
}
616+
const pairs = libraries.map(lib => ({
617+
raw: lib,
618+
id: normalizeLibraryName(lib)
619+
}));
625620

626-
const ok = await handleInstall(app, lib, () =>
627-
app.commands.execute(commandIDs.refreshComponentList)
628-
);
621+
const missing = pairs.filter(p => !installedIds.has(p.id));
629622

623+
if (missing.length) {
624+
const list = missing.map(p => p.raw).join(', ');
625+
const ok = window.confirm(`This workflow template requires the following component libraries: ${list}. Would you like to install them now?`);
626+
if (!ok) {
627+
console.warn('User cancelled installation.');
628+
return;
629+
}
630+
631+
for (const { raw, id } of missing) {
632+
const ok = await installLibrarySilently(app, raw);
630633
if (!ok) {
631-
console.warn(`Aborted: ${lib} not installed.`);
634+
console.warn(`Aborted: ${raw} not installed.`);
632635
return;
633636
}
634-
installedLibs.add(lib);
637+
installedIds.add(id);
635638
}
639+
}
636640

637641
// Currently the templates are stored at the `examples` dir
638642
await app.commands.execute(commandIDs.fetchExamples);

0 commit comments

Comments
 (0)