diff --git a/src/helpers/notificationAugmentor.ts b/src/helpers/notificationAugmentor.ts new file mode 100644 index 00000000..8b3e32fa --- /dev/null +++ b/src/helpers/notificationAugmentor.ts @@ -0,0 +1,74 @@ +import { Notification, showDialog, Dialog } from '@jupyterlab/apputils'; +import { Widget } from '@lumino/widgets'; + +const MAX_VISIBLE_CHARS = 140; +const VIEW_DETAILS_LABEL = 'View details'; + +function toPlainText(value: unknown): string { + try { + return typeof value === 'string' ? value : JSON.stringify(value, null, 2); + } catch { + return String(value ?? ''); + } +} + +function createViewDetailsAction(fullMessage: string, dialogTitle = 'Details'): Notification.IAction { + return { + label: VIEW_DETAILS_LABEL, + caption: 'Show full message', + callback: async () => { + const dialogBody = new Widget(); + dialogBody.addClass('xircuits-notification-details'); + + const pre = document.createElement('pre'); + pre.textContent = fullMessage; + dialogBody.node.appendChild(pre); + + const copyButton = Dialog.createButton({ label: 'Copy' }); + const closeButton = Dialog.okButton({ label: 'Close' }); + + const result = await showDialog({ + title: dialogTitle, + body: dialogBody, + buttons: [copyButton, closeButton] + }); + + if (result.button.label === 'Copy') { + await navigator.clipboard.writeText(fullMessage); + } + } + }; +} + +function ensureViewDetailsAction( + messageText: string, + options: any = {}, + title?: string +): any { + if (messageText.length <= MAX_VISIBLE_CHARS) return options; + const actions = [...(options.actions ?? [])]; + if (!actions.some((a: any) => a?.label === VIEW_DETAILS_LABEL)) { + actions.push(createViewDetailsAction(messageText, title)); + } + return { ...options, actions }; +} + +export function augmentNotifications(): void { + const NotificationObj: any = Notification as any; + if (NotificationObj.__xircuitsAugmented) return; + + const wrap = (method: 'error' | 'warning' | 'info' | 'success') => { + const original = NotificationObj[method]?.bind(Notification); + if (!original) return; + + NotificationObj[method] = (...args: any[]) => { + const [rawMessage, rawOptions] = args; + const text = toPlainText(rawMessage); + const options = ensureViewDetailsAction(text, rawOptions); + return original(text, options); + }; + }; + + ['error', 'warning', 'info', 'success'].forEach(wrap); + NotificationObj.__xircuitsAugmented = true; +} diff --git a/src/helpers/notificationEffects.ts b/src/helpers/notificationEffects.ts index e140763a..e96a623f 100644 --- a/src/helpers/notificationEffects.ts +++ b/src/helpers/notificationEffects.ts @@ -92,9 +92,11 @@ export async function resolveLibraryForNode( const candidateId = pathToLibraryId(extras.path); if (!candidateId) return { libId: null, status: 'unknown' }; + const cleanLibId = candidateId.replace(/^xai_components[\/\\]/i, ''); + const idx = await loadLibraryIndex(); const entry = idx.get(candidateId); - return computeStatusFromEntry(entry, candidateId); + return computeStatusFromEntry(entry, cleanLibId); } export async function showInstallForRemoteLibrary(args: { diff --git a/src/index.tsx b/src/index.tsx index 25a6f769..a13771a4 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -33,9 +33,10 @@ import type { Signal } from "@lumino/signaling"; import { commandIDs } from "./commands/CommandIDs"; import { IEditorTracker } from '@jupyterlab/fileeditor'; import { IMainMenu } from '@jupyterlab/mainmenu'; -import { installLibrarySilently } from './context-menu/TrayContextMenu'; -import { normalizeLibraryName } from './tray_library/ComponentLibraryConfig'; +import { handleInstall, installLibrarySilently } from './context-menu/TrayContextMenu'; +import { augmentNotifications } from './helpers/notificationAugmentor'; import { loadLibraryIndex } from './helpers/notificationEffects'; +import { normalizeLibraryName } from './tray_library/ComponentLibraryConfig'; import { installComponentPreview } from './component_info_sidebar/previewHelper'; const FACTORY = 'Xircuits editor'; @@ -81,6 +82,9 @@ const xircuits: JupyterFrontEndPlugin = { console.log('Xircuits is activated!'); + // Add "View details" to long notifications + augmentNotifications(); + // Creating the widget factory to register it so the document manager knows about // our new DocumentWidget const widgetFactory = new XircuitsFactory({ diff --git a/style/base.css b/style/base.css index 4642ce49..8bdd090b 100644 --- a/style/base.css +++ b/style/base.css @@ -158,6 +158,16 @@ body.light-mode jp-button[title="Toggle Light/Dark Mode"] .moon { visibility: body.light-mode jp-button[title="Toggle Light/Dark Mode"] .sun { visibility: visible; } - - - +.xircuits-notification-details { + max-height: 60vh; + overflow: auto; + padding: 0.25rem; +} + +.xircuits-notification-details pre { + white-space: pre-wrap; + word-break: break-word; + margin: 0; + font-family: var(--jp-code-font-family); + font-size: var(--jp-code-font-size); +} \ No newline at end of file