jupyterlab_claude_code_extension 1.2.42 → 1.2.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/widget.d.ts +25 -6
- package/lib/widget.js +102 -23
- package/package.json +1 -1
- package/src/__tests__/jupyterlab_claude_code_extension.spec.ts +1 -0
- package/src/widget.ts +116 -24
package/lib/widget.d.ts
CHANGED
|
@@ -88,13 +88,32 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
|
|
|
88
88
|
* colourful-tab extension is not installed (the token is optional). An
|
|
89
89
|
* absent/unknown colour clears the tint. */
|
|
90
90
|
private _applyTerminalColour;
|
|
91
|
-
/** Re-tint
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
91
|
+
/** Re-tint EVERY open Claude terminal tab from the freshest session colours,
|
|
92
|
+
* whether the plugin launched it or the user opened it themselves. Walks
|
|
93
|
+
* JupyterLab's terminal tracker (the registry of all open terminals) rather
|
|
94
|
+
* than only the plugin's own launch cache, and re-resolves each terminal's
|
|
95
|
+
* Claude conversation on every pass via the ``terminal-cwd`` probe - so a tab
|
|
96
|
+
* whose terminal has since started (or switched) a Claude conversation is
|
|
97
|
+
* re-tinted correctly rather than pinned to a stale identity. The probe is a
|
|
98
|
+
* few /proc reads per terminal; runs after each fetch (launch refresh + the
|
|
99
|
+
* 30s poll), which is already gated to skip while a context menu is open. */
|
|
97
100
|
private _reconcileTerminalColours;
|
|
101
|
+
/** Resolve a terminal's Claude identity: does it run Claude, which
|
|
102
|
+
* conversation id, and its cwd(s). Panel-launched terminals are already known
|
|
103
|
+
* (their conversation was confirmed at launch), so those skip the backend
|
|
104
|
+
* probe; every other terminal is probed once via ``terminal-cwd``. Returns
|
|
105
|
+
* ``null`` when the terminal has no session name yet or the probe fails. */
|
|
106
|
+
private _interrogateTerminal;
|
|
107
|
+
/** Pick a terminal's tab colour from the session list. When the terminal runs
|
|
108
|
+
* a known conversation (its argv carried a ``--resume`` / ``--session-id``),
|
|
109
|
+
* match on that id ONLY: it takes the colour of the exact conversation, or -
|
|
110
|
+
* when that conversation is not the project's representative row - clears,
|
|
111
|
+
* rather than borrowing a sibling branch's colour. The cwd fallback is used
|
|
112
|
+
* ONLY for a terminal with no conversation id (a bare ``claude`` / ``-c``),
|
|
113
|
+
* matching the project the terminal is cwd'd in and preferring the longest
|
|
114
|
+
* matching path so a nested project wins over its parent. Returns null (clear)
|
|
115
|
+
* when no row matches or the matched row has no colour. */
|
|
116
|
+
private _colourForTerminal;
|
|
98
117
|
private _findTerminalForCwd;
|
|
99
118
|
private _showCloseExistingDialog;
|
|
100
119
|
/** Show a modal with a spinner while the terminal is being launched. The
|
package/lib/widget.js
CHANGED
|
@@ -353,7 +353,8 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
353
353
|
const data = await requestAPI('sessions', this._serverSettings, { cache: 'no-store' });
|
|
354
354
|
this._sessions = (_a = data.sessions) !== null && _a !== void 0 ? _a : [];
|
|
355
355
|
this._render();
|
|
356
|
-
|
|
356
|
+
// Best-effort tab tinting; never let a colour failure break the fetch.
|
|
357
|
+
this._reconcileTerminalColours().catch(() => undefined);
|
|
357
358
|
}
|
|
358
359
|
async _toggleFavourite(session) {
|
|
359
360
|
const next = !session.favourite;
|
|
@@ -669,23 +670,93 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
669
670
|
}
|
|
670
671
|
this._colourfulTabs.setColour(widget, claudeTabColourId(color));
|
|
671
672
|
}
|
|
672
|
-
/** Re-tint
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
|
|
673
|
+
/** Re-tint EVERY open Claude terminal tab from the freshest session colours,
|
|
674
|
+
* whether the plugin launched it or the user opened it themselves. Walks
|
|
675
|
+
* JupyterLab's terminal tracker (the registry of all open terminals) rather
|
|
676
|
+
* than only the plugin's own launch cache, and re-resolves each terminal's
|
|
677
|
+
* Claude conversation on every pass via the ``terminal-cwd`` probe - so a tab
|
|
678
|
+
* whose terminal has since started (or switched) a Claude conversation is
|
|
679
|
+
* re-tinted correctly rather than pinned to a stale identity. The probe is a
|
|
680
|
+
* few /proc reads per terminal; runs after each fetch (launch refresh + the
|
|
681
|
+
* 30s poll), which is already gated to skip while a context menu is open. */
|
|
682
|
+
async _reconcileTerminalColours() {
|
|
679
683
|
var _a;
|
|
684
|
+
if (!this._colourfulTabs || !this._terminalTracker) {
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
680
687
|
const sessions = (_a = this._sessions) !== null && _a !== void 0 ? _a : [];
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
if (
|
|
684
|
-
|
|
688
|
+
const widgets = [];
|
|
689
|
+
this._terminalTracker.forEach((widget) => {
|
|
690
|
+
if (widget && !widget.isDisposed) {
|
|
691
|
+
widgets.push(widget);
|
|
685
692
|
}
|
|
686
|
-
const match = sessions.find(s => s.project_path === projectPath && s.session_id === entry.sessionId);
|
|
687
|
-
this._applyTerminalColour(entry.widget, (_a = match === null || match === void 0 ? void 0 : match.color) !== null && _a !== void 0 ? _a : null);
|
|
688
693
|
});
|
|
694
|
+
await Promise.all(widgets.map(async (widget) => {
|
|
695
|
+
const info = await this._interrogateTerminal(widget);
|
|
696
|
+
if (!info || !info.hasClaude || widget.isDisposed) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
this._applyTerminalColour(widget, this._colourForTerminal(info, sessions));
|
|
700
|
+
}));
|
|
701
|
+
}
|
|
702
|
+
/** Resolve a terminal's Claude identity: does it run Claude, which
|
|
703
|
+
* conversation id, and its cwd(s). Panel-launched terminals are already known
|
|
704
|
+
* (their conversation was confirmed at launch), so those skip the backend
|
|
705
|
+
* probe; every other terminal is probed once via ``terminal-cwd``. Returns
|
|
706
|
+
* ``null`` when the terminal has no session name yet or the probe fails. */
|
|
707
|
+
async _interrogateTerminal(widget) {
|
|
708
|
+
var _a, _b, _c;
|
|
709
|
+
for (const entry of this._terminalsByPath.values()) {
|
|
710
|
+
if (entry.widget === widget && entry.sessionId) {
|
|
711
|
+
return { hasClaude: true, sessionId: entry.sessionId, cwds: [] };
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
const sessName = (_b = (_a = widget === null || widget === void 0 ? void 0 : widget.content) === null || _a === void 0 ? void 0 : _a.session) === null || _b === void 0 ? void 0 : _b.name;
|
|
715
|
+
if (typeof sessName !== 'string' || !sessName) {
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
try {
|
|
719
|
+
const data = await requestAPI(`terminal-cwd/${encodeURIComponent(sessName)}`, this._serverSettings);
|
|
720
|
+
return {
|
|
721
|
+
hasClaude: !!(data === null || data === void 0 ? void 0 : data.has_claude),
|
|
722
|
+
sessionId: (_c = data === null || data === void 0 ? void 0 : data.session_id) !== null && _c !== void 0 ? _c : null,
|
|
723
|
+
cwds: Array.isArray(data === null || data === void 0 ? void 0 : data.cwds) ? data.cwds : []
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
catch (_err) {
|
|
727
|
+
// Backend may 404 for a terminal that vanished between enumeration and
|
|
728
|
+
// probe - treat as unresolved and retry on the next poll.
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
/** Pick a terminal's tab colour from the session list. When the terminal runs
|
|
733
|
+
* a known conversation (its argv carried a ``--resume`` / ``--session-id``),
|
|
734
|
+
* match on that id ONLY: it takes the colour of the exact conversation, or -
|
|
735
|
+
* when that conversation is not the project's representative row - clears,
|
|
736
|
+
* rather than borrowing a sibling branch's colour. The cwd fallback is used
|
|
737
|
+
* ONLY for a terminal with no conversation id (a bare ``claude`` / ``-c``),
|
|
738
|
+
* matching the project the terminal is cwd'd in and preferring the longest
|
|
739
|
+
* matching path so a nested project wins over its parent. Returns null (clear)
|
|
740
|
+
* when no row matches or the matched row has no colour. */
|
|
741
|
+
_colourForTerminal(info, sessions) {
|
|
742
|
+
var _a, _b;
|
|
743
|
+
if (info.sessionId) {
|
|
744
|
+
const bySession = sessions.find(s => s.session_id === info.sessionId);
|
|
745
|
+
return bySession ? ((_a = bySession.color) !== null && _a !== void 0 ? _a : null) : null;
|
|
746
|
+
}
|
|
747
|
+
let best;
|
|
748
|
+
let bestLen = -1;
|
|
749
|
+
for (const raw of info.cwds) {
|
|
750
|
+
const cwd = raw.replace(/\/+$/, '');
|
|
751
|
+
for (const s of sessions) {
|
|
752
|
+
const p = s.project_path.replace(/\/+$/, '');
|
|
753
|
+
if ((cwd === p || cwd.startsWith(p + '/')) && p.length > bestLen) {
|
|
754
|
+
best = s;
|
|
755
|
+
bestLen = p.length;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
return best ? ((_b = best.color) !== null && _b !== void 0 ? _b : null) : null;
|
|
689
760
|
}
|
|
690
761
|
async _findTerminalForCwd(projectPath, wantedSessionId) {
|
|
691
762
|
var _a, _b, _c;
|
|
@@ -767,9 +838,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
767
838
|
body,
|
|
768
839
|
buttons: []
|
|
769
840
|
});
|
|
770
|
-
// launch() returns a Promise we don't await -
|
|
771
|
-
// when the spawn completes
|
|
772
|
-
|
|
841
|
+
// launch() returns a Promise we don't await - the caller dismisses this
|
|
842
|
+
// dialog with .dispose() when the spawn completes. Disposing an open Lumino
|
|
843
|
+
// dialog rejects that promise with `undefined`, so catch it here to keep a
|
|
844
|
+
// benign teardown from surfacing as an unhandled promise rejection.
|
|
845
|
+
dialog.launch().catch(() => undefined);
|
|
773
846
|
return dialog;
|
|
774
847
|
}
|
|
775
848
|
_wireTerminalDisposal(projectPath, widget) {
|
|
@@ -1171,7 +1244,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1171
1244
|
Notification.warning('Folder is outside the JupyterLab root - cannot open a terminal there.', { autoClose: 4000 });
|
|
1172
1245
|
return;
|
|
1173
1246
|
}
|
|
1174
|
-
|
|
1247
|
+
this._app.commands
|
|
1248
|
+
.execute('terminal:create-new', { cwd: rel })
|
|
1249
|
+
.catch(err => this._showError(err));
|
|
1175
1250
|
}
|
|
1176
1251
|
});
|
|
1177
1252
|
this._commands.addCommand('claude-code-sessions:show-in-filebrowser', {
|
|
@@ -1190,9 +1265,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1190
1265
|
}
|
|
1191
1266
|
// JL's built-in command navigates the default file browser to the
|
|
1192
1267
|
// path and reveals the browser panel.
|
|
1193
|
-
|
|
1194
|
-
path: rel
|
|
1195
|
-
|
|
1268
|
+
this._app.commands
|
|
1269
|
+
.execute('filebrowser:go-to-path', { path: rel })
|
|
1270
|
+
.catch(err => this._showError(err));
|
|
1196
1271
|
}
|
|
1197
1272
|
});
|
|
1198
1273
|
this._commands.addCommand('claude-code-sessions:copy-path', {
|
|
@@ -1760,7 +1835,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1760
1835
|
});
|
|
1761
1836
|
render();
|
|
1762
1837
|
updateControls();
|
|
1763
|
-
|
|
1838
|
+
// dialog.dispose() (on open/switch) rejects this promise with `undefined`;
|
|
1839
|
+
// catch it so the teardown never surfaces as an unhandled promise rejection.
|
|
1840
|
+
dialog.launch().catch(() => undefined);
|
|
1764
1841
|
search.focus();
|
|
1765
1842
|
}
|
|
1766
1843
|
/** Delete the given branch sessions of the active row's project.
|
|
@@ -1890,7 +1967,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1890
1967
|
: `Branch switch failed: ${err}`, { autoClose: 4000 });
|
|
1891
1968
|
}
|
|
1892
1969
|
finally {
|
|
1893
|
-
|
|
1970
|
+
// Best-effort refresh - never let a transient fetch failure reject this
|
|
1971
|
+
// fire-and-forget switch (its callers do not await it).
|
|
1972
|
+
await this._fetch().catch(() => undefined);
|
|
1894
1973
|
}
|
|
1895
1974
|
}
|
|
1896
1975
|
/** Open a specific conversation branch in its own terminal.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jupyterlab_claude_code_extension",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.44",
|
|
4
4
|
"description": "Browse, resume, and manage your Claude Code CLI sessions from a JupyterLab side panel. One click reactivates the right terminal - no duplicate tabs, live remote-control indicator, and favourites for the projects you keep coming back to.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
package/src/widget.ts
CHANGED
|
@@ -428,7 +428,8 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
428
428
|
);
|
|
429
429
|
this._sessions = data.sessions ?? [];
|
|
430
430
|
this._render();
|
|
431
|
-
|
|
431
|
+
// Best-effort tab tinting; never let a colour failure break the fetch.
|
|
432
|
+
this._reconcileTerminalColours().catch(() => undefined);
|
|
432
433
|
}
|
|
433
434
|
|
|
434
435
|
private async _toggleFavourite(session: ISession): Promise<void> {
|
|
@@ -798,23 +799,106 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
798
799
|
this._colourfulTabs.setColour(widget, claudeTabColourId(color));
|
|
799
800
|
}
|
|
800
801
|
|
|
801
|
-
/** Re-tint
|
|
802
|
-
*
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
|
|
802
|
+
/** Re-tint EVERY open Claude terminal tab from the freshest session colours,
|
|
803
|
+
* whether the plugin launched it or the user opened it themselves. Walks
|
|
804
|
+
* JupyterLab's terminal tracker (the registry of all open terminals) rather
|
|
805
|
+
* than only the plugin's own launch cache, and re-resolves each terminal's
|
|
806
|
+
* Claude conversation on every pass via the ``terminal-cwd`` probe - so a tab
|
|
807
|
+
* whose terminal has since started (or switched) a Claude conversation is
|
|
808
|
+
* re-tinted correctly rather than pinned to a stale identity. The probe is a
|
|
809
|
+
* few /proc reads per terminal; runs after each fetch (launch refresh + the
|
|
810
|
+
* 30s poll), which is already gated to skip while a context menu is open. */
|
|
811
|
+
private async _reconcileTerminalColours(): Promise<void> {
|
|
812
|
+
if (!this._colourfulTabs || !this._terminalTracker) {
|
|
813
|
+
return;
|
|
814
|
+
}
|
|
808
815
|
const sessions = this._sessions ?? [];
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
816
|
+
const widgets: any[] = [];
|
|
817
|
+
this._terminalTracker.forEach((widget: any) => {
|
|
818
|
+
if (widget && !widget.isDisposed) {
|
|
819
|
+
widgets.push(widget);
|
|
812
820
|
}
|
|
813
|
-
const match = sessions.find(
|
|
814
|
-
s => s.project_path === projectPath && s.session_id === entry.sessionId
|
|
815
|
-
);
|
|
816
|
-
this._applyTerminalColour(entry.widget, match?.color ?? null);
|
|
817
821
|
});
|
|
822
|
+
await Promise.all(
|
|
823
|
+
widgets.map(async widget => {
|
|
824
|
+
const info = await this._interrogateTerminal(widget);
|
|
825
|
+
if (!info || !info.hasClaude || widget.isDisposed) {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
this._applyTerminalColour(
|
|
829
|
+
widget,
|
|
830
|
+
this._colourForTerminal(info, sessions)
|
|
831
|
+
);
|
|
832
|
+
})
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/** Resolve a terminal's Claude identity: does it run Claude, which
|
|
837
|
+
* conversation id, and its cwd(s). Panel-launched terminals are already known
|
|
838
|
+
* (their conversation was confirmed at launch), so those skip the backend
|
|
839
|
+
* probe; every other terminal is probed once via ``terminal-cwd``. Returns
|
|
840
|
+
* ``null`` when the terminal has no session name yet or the probe fails. */
|
|
841
|
+
private async _interrogateTerminal(widget: any): Promise<{
|
|
842
|
+
hasClaude: boolean;
|
|
843
|
+
sessionId: string | null;
|
|
844
|
+
cwds: string[];
|
|
845
|
+
} | null> {
|
|
846
|
+
for (const entry of this._terminalsByPath.values()) {
|
|
847
|
+
if (entry.widget === widget && entry.sessionId) {
|
|
848
|
+
return { hasClaude: true, sessionId: entry.sessionId, cwds: [] };
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const sessName: string | undefined = widget?.content?.session?.name;
|
|
852
|
+
if (typeof sessName !== 'string' || !sessName) {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
try {
|
|
856
|
+
const data = await requestAPI<ITerminalCwdResponse>(
|
|
857
|
+
`terminal-cwd/${encodeURIComponent(sessName)}`,
|
|
858
|
+
this._serverSettings
|
|
859
|
+
);
|
|
860
|
+
return {
|
|
861
|
+
hasClaude: !!data?.has_claude,
|
|
862
|
+
sessionId: data?.session_id ?? null,
|
|
863
|
+
cwds: Array.isArray(data?.cwds) ? data.cwds : []
|
|
864
|
+
};
|
|
865
|
+
} catch (_err) {
|
|
866
|
+
// Backend may 404 for a terminal that vanished between enumeration and
|
|
867
|
+
// probe - treat as unresolved and retry on the next poll.
|
|
868
|
+
return null;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/** Pick a terminal's tab colour from the session list. When the terminal runs
|
|
873
|
+
* a known conversation (its argv carried a ``--resume`` / ``--session-id``),
|
|
874
|
+
* match on that id ONLY: it takes the colour of the exact conversation, or -
|
|
875
|
+
* when that conversation is not the project's representative row - clears,
|
|
876
|
+
* rather than borrowing a sibling branch's colour. The cwd fallback is used
|
|
877
|
+
* ONLY for a terminal with no conversation id (a bare ``claude`` / ``-c``),
|
|
878
|
+
* matching the project the terminal is cwd'd in and preferring the longest
|
|
879
|
+
* matching path so a nested project wins over its parent. Returns null (clear)
|
|
880
|
+
* when no row matches or the matched row has no colour. */
|
|
881
|
+
private _colourForTerminal(
|
|
882
|
+
info: { sessionId: string | null; cwds: string[] },
|
|
883
|
+
sessions: ISession[]
|
|
884
|
+
): string | null {
|
|
885
|
+
if (info.sessionId) {
|
|
886
|
+
const bySession = sessions.find(s => s.session_id === info.sessionId);
|
|
887
|
+
return bySession ? (bySession.color ?? null) : null;
|
|
888
|
+
}
|
|
889
|
+
let best: ISession | undefined;
|
|
890
|
+
let bestLen = -1;
|
|
891
|
+
for (const raw of info.cwds) {
|
|
892
|
+
const cwd = raw.replace(/\/+$/, '');
|
|
893
|
+
for (const s of sessions) {
|
|
894
|
+
const p = s.project_path.replace(/\/+$/, '');
|
|
895
|
+
if ((cwd === p || cwd.startsWith(p + '/')) && p.length > bestLen) {
|
|
896
|
+
best = s;
|
|
897
|
+
bestLen = p.length;
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
return best ? (best.color ?? null) : null;
|
|
818
902
|
}
|
|
819
903
|
|
|
820
904
|
private async _findTerminalForCwd(
|
|
@@ -906,9 +990,11 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
906
990
|
body,
|
|
907
991
|
buttons: []
|
|
908
992
|
});
|
|
909
|
-
// launch() returns a Promise we don't await -
|
|
910
|
-
// when the spawn completes
|
|
911
|
-
|
|
993
|
+
// launch() returns a Promise we don't await - the caller dismisses this
|
|
994
|
+
// dialog with .dispose() when the spawn completes. Disposing an open Lumino
|
|
995
|
+
// dialog rejects that promise with `undefined`, so catch it here to keep a
|
|
996
|
+
// benign teardown from surfacing as an unhandled promise rejection.
|
|
997
|
+
dialog.launch().catch(() => undefined);
|
|
912
998
|
return dialog;
|
|
913
999
|
}
|
|
914
1000
|
|
|
@@ -1356,7 +1442,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1356
1442
|
);
|
|
1357
1443
|
return;
|
|
1358
1444
|
}
|
|
1359
|
-
|
|
1445
|
+
this._app.commands
|
|
1446
|
+
.execute('terminal:create-new', { cwd: rel })
|
|
1447
|
+
.catch(err => this._showError(err));
|
|
1360
1448
|
}
|
|
1361
1449
|
});
|
|
1362
1450
|
|
|
@@ -1379,9 +1467,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
1379
1467
|
}
|
|
1380
1468
|
// JL's built-in command navigates the default file browser to the
|
|
1381
1469
|
// path and reveals the browser panel.
|
|
1382
|
-
|
|
1383
|
-
path: rel
|
|
1384
|
-
|
|
1470
|
+
this._app.commands
|
|
1471
|
+
.execute('filebrowser:go-to-path', { path: rel })
|
|
1472
|
+
.catch(err => this._showError(err));
|
|
1385
1473
|
}
|
|
1386
1474
|
});
|
|
1387
1475
|
|
|
@@ -2009,7 +2097,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
2009
2097
|
render();
|
|
2010
2098
|
updateControls();
|
|
2011
2099
|
|
|
2012
|
-
|
|
2100
|
+
// dialog.dispose() (on open/switch) rejects this promise with `undefined`;
|
|
2101
|
+
// catch it so the teardown never surfaces as an unhandled promise rejection.
|
|
2102
|
+
dialog.launch().catch(() => undefined);
|
|
2013
2103
|
search.focus();
|
|
2014
2104
|
}
|
|
2015
2105
|
|
|
@@ -2156,7 +2246,9 @@ export class ClaudeCodeSessionsWidget extends Widget {
|
|
|
2156
2246
|
{ autoClose: 4000 }
|
|
2157
2247
|
);
|
|
2158
2248
|
} finally {
|
|
2159
|
-
|
|
2249
|
+
// Best-effort refresh - never let a transient fetch failure reject this
|
|
2250
|
+
// fire-and-forget switch (its callers do not await it).
|
|
2251
|
+
await this._fetch().catch(() => undefined);
|
|
2160
2252
|
}
|
|
2161
2253
|
}
|
|
2162
2254
|
|