Skip to content

Commit 78230fe

Browse files
authored
Merge pull request #460 from rabea-al/notification-view-details
✨ Add "View details" dialog for long JupyterLab notifications
2 parents 62569ee + 41bc12f commit 78230fe

4 files changed

Lines changed: 96 additions & 6 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Notification, showDialog, Dialog } from '@jupyterlab/apputils';
2+
import { Widget } from '@lumino/widgets';
3+
4+
const MAX_VISIBLE_CHARS = 140;
5+
const VIEW_DETAILS_LABEL = 'View details';
6+
7+
function toPlainText(value: unknown): string {
8+
try {
9+
return typeof value === 'string' ? value : JSON.stringify(value, null, 2);
10+
} catch {
11+
return String(value ?? '');
12+
}
13+
}
14+
15+
function createViewDetailsAction(fullMessage: string, dialogTitle = 'Details'): Notification.IAction {
16+
return {
17+
label: VIEW_DETAILS_LABEL,
18+
caption: 'Show full message',
19+
callback: async () => {
20+
const dialogBody = new Widget();
21+
dialogBody.addClass('xircuits-notification-details');
22+
23+
const pre = document.createElement('pre');
24+
pre.textContent = fullMessage;
25+
dialogBody.node.appendChild(pre);
26+
27+
const copyButton = Dialog.createButton({ label: 'Copy' });
28+
const closeButton = Dialog.okButton({ label: 'Close' });
29+
30+
const result = await showDialog({
31+
title: dialogTitle,
32+
body: dialogBody,
33+
buttons: [copyButton, closeButton]
34+
});
35+
36+
if (result.button.label === 'Copy') {
37+
await navigator.clipboard.writeText(fullMessage);
38+
}
39+
}
40+
};
41+
}
42+
43+
function ensureViewDetailsAction(
44+
messageText: string,
45+
options: any = {},
46+
title?: string
47+
): any {
48+
if (messageText.length <= MAX_VISIBLE_CHARS) return options;
49+
const actions = [...(options.actions ?? [])];
50+
if (!actions.some((a: any) => a?.label === VIEW_DETAILS_LABEL)) {
51+
actions.push(createViewDetailsAction(messageText, title));
52+
}
53+
return { ...options, actions };
54+
}
55+
56+
export function augmentNotifications(): void {
57+
const NotificationObj: any = Notification as any;
58+
if (NotificationObj.__xircuitsAugmented) return;
59+
60+
const wrap = (method: 'error' | 'warning' | 'info' | 'success') => {
61+
const original = NotificationObj[method]?.bind(Notification);
62+
if (!original) return;
63+
64+
NotificationObj[method] = (...args: any[]) => {
65+
const [rawMessage, rawOptions] = args;
66+
const text = toPlainText(rawMessage);
67+
const options = ensureViewDetailsAction(text, rawOptions);
68+
return original(text, options);
69+
};
70+
};
71+
72+
['error', 'warning', 'info', 'success'].forEach(wrap);
73+
NotificationObj.__xircuitsAugmented = true;
74+
}

src/helpers/notificationEffects.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ export async function resolveLibraryForNode(
9292
const candidateId = pathToLibraryId(extras.path);
9393
if (!candidateId) return { libId: null, status: 'unknown' };
9494

95+
const cleanLibId = candidateId.replace(/^xai_components[\/\\]/i, '');
96+
9597
const idx = await loadLibraryIndex();
9698
const entry = idx.get(candidateId);
97-
return computeStatusFromEntry(entry, candidateId);
99+
return computeStatusFromEntry(entry, cleanLibId);
98100
}
99101

100102
export async function showInstallForRemoteLibrary(args: {

src/index.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,10 @@ 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 { installLibrarySilently } from './context-menu/TrayContextMenu';
37-
import { normalizeLibraryName } from './tray_library/ComponentLibraryConfig';
36+
import { handleInstall, installLibrarySilently } from './context-menu/TrayContextMenu';
37+
import { augmentNotifications } from './helpers/notificationAugmentor';
3838
import { loadLibraryIndex } from './helpers/notificationEffects';
39+
import { normalizeLibraryName } from './tray_library/ComponentLibraryConfig';
3940
import { installComponentPreview } from './component_info_sidebar/previewHelper';
4041
const FACTORY = 'Xircuits editor';
4142

@@ -81,6 +82,9 @@ const xircuits: JupyterFrontEndPlugin<void> = {
8182

8283
console.log('Xircuits is activated!');
8384

85+
// Add "View details" to long notifications
86+
augmentNotifications();
87+
8488
// Creating the widget factory to register it so the document manager knows about
8589
// our new DocumentWidget
8690
const widgetFactory = new XircuitsFactory({

style/base.css

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,16 @@ body.light-mode jp-button[title="Toggle Light/Dark Mode"] .moon { visibility:
158158

159159
body.light-mode jp-button[title="Toggle Light/Dark Mode"] .sun { visibility: visible; }
160160

161-
162-
163-
161+
.xircuits-notification-details {
162+
max-height: 60vh;
163+
overflow: auto;
164+
padding: 0.25rem;
165+
}
166+
167+
.xircuits-notification-details pre {
168+
white-space: pre-wrap;
169+
word-break: break-word;
170+
margin: 0;
171+
font-family: var(--jp-code-font-family);
172+
font-size: var(--jp-code-font-size);
173+
}

0 commit comments

Comments
 (0)