jupyterlab_claude_code_extension 1.1.16 → 1.1.18

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/icons.d.ts CHANGED
@@ -4,3 +4,4 @@ export declare const starFilledIcon: LabIcon;
4
4
  export declare const refreshIcon: LabIcon;
5
5
  export declare const removeIcon: LabIcon;
6
6
  export declare const shieldIcon: LabIcon;
7
+ export declare const filterIcon: LabIcon;
package/lib/icons.js CHANGED
@@ -49,3 +49,10 @@ export const shieldIcon = new LabIcon({
49
49
  name: 'jupyterlab_claude_code_extension:shield',
50
50
  svgstr: shieldSvgStr
51
51
  });
52
+ const filterSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
53
+ <path class="jp-icon3" fill="#616161" d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
54
+ </svg>`;
55
+ export const filterIcon = new LabIcon({
56
+ name: 'jupyterlab_claude_code_extension:filter',
57
+ svgstr: filterSvgStr
58
+ });
package/lib/widget.d.ts CHANGED
@@ -16,6 +16,11 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
16
16
  protected onBeforeHide(_msg: Message): void;
17
17
  protected onCloseRequest(msg: Message): void;
18
18
  private _buildShell;
19
+ /** Show / hide the filter input. Hiding also clears the active filter
20
+ * so the user does not end up with an "invisible" filter narrowing
21
+ * the rows the next time they open the panel.
22
+ */
23
+ private _toggleFilterBar;
19
24
  /** Normalise strings for filter comparison: NFD-decompose, strip combining
20
25
  * diacritic marks, lowercase, and collapse separators (`-`, `_`, `.`, `/`,
21
26
  * whitespace) entirely. So "foo-bar", "foo_bar", "foo bar", "Foo Bar" all
@@ -85,6 +90,8 @@ export declare class ClaudeCodeSessionsWidget extends Widget {
85
90
  private readonly _serverSettings;
86
91
  private _bodyEl;
87
92
  private _refreshBtn;
93
+ private _filterBtn;
94
+ private _searchEl;
88
95
  private _sessions;
89
96
  private _expanded;
90
97
  private _commands;
package/lib/widget.js CHANGED
@@ -3,7 +3,7 @@ import { folderIcon, terminalIcon } from '@jupyterlab/ui-components';
3
3
  import { CommandRegistry } from '@lumino/commands';
4
4
  import { Menu, Widget } from '@lumino/widgets';
5
5
  import { requestAPI } from './request';
6
- import { claudeIcon, refreshIcon, removeIcon, shieldIcon, starFilledIcon } from './icons';
6
+ import { claudeIcon, filterIcon, refreshIcon, removeIcon, shieldIcon, starFilledIcon } from './icons';
7
7
  const POLL_INTERVAL_MS = 30000;
8
8
  const DEFAULT_RECENT_LIMIT = 10;
9
9
  const EXPANDED_STORAGE_KEY = 'jupyterlab_claude_code_extension:expanded';
@@ -59,6 +59,8 @@ export class ClaudeCodeSessionsWidget extends Widget {
59
59
  constructor(app, rootDir, terminalTracker = null) {
60
60
  super();
61
61
  this._refreshBtn = null;
62
+ this._filterBtn = null;
63
+ this._searchEl = null;
62
64
  this._sessions = null;
63
65
  this._expanded = loadExpanded();
64
66
  this._activeSession = null;
@@ -137,6 +139,13 @@ export class ClaudeCodeSessionsWidget extends Widget {
137
139
  title.className = 'jp-ClaudeSessionsPanel-title';
138
140
  title.textContent = 'Claude Code Sessions';
139
141
  header.appendChild(title);
142
+ const filterBtn = document.createElement('button');
143
+ filterBtn.className = 'jp-ClaudeSessionsPanel-iconButton';
144
+ filterBtn.title = 'Filter sessions';
145
+ filterIcon.element({ container: filterBtn });
146
+ filterBtn.addEventListener('click', () => this._toggleFilterBar());
147
+ header.appendChild(filterBtn);
148
+ this._filterBtn = filterBtn;
140
149
  const refreshBtn = document.createElement('button');
141
150
  refreshBtn.className = 'jp-ClaudeSessionsPanel-iconButton';
142
151
  refreshBtn.title = 'Refresh';
@@ -149,10 +158,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
149
158
  search.className = 'jp-ClaudeSessionsPanel-search';
150
159
  search.placeholder = 'Filter sessions...';
151
160
  search.spellcheck = false;
161
+ // Hidden by default; the filter-icon button reveals it. Lumino's
162
+ // ``hidden`` attribute toggles ``display: none`` via the user-agent
163
+ // stylesheet so no extra CSS rule is needed.
164
+ search.hidden = true;
152
165
  search.addEventListener('input', () => {
153
166
  this._filter = search.value;
154
167
  this._render();
155
168
  });
169
+ this._searchEl = search;
156
170
  const body = document.createElement('div');
157
171
  body.className = 'jp-ClaudeSessionsPanel-body';
158
172
  root.appendChild(header);
@@ -160,6 +174,28 @@ export class ClaudeCodeSessionsWidget extends Widget {
160
174
  root.appendChild(body);
161
175
  this._bodyEl = body;
162
176
  }
177
+ /** Show / hide the filter input. Hiding also clears the active filter
178
+ * so the user does not end up with an "invisible" filter narrowing
179
+ * the rows the next time they open the panel.
180
+ */
181
+ _toggleFilterBar() {
182
+ if (!this._searchEl) {
183
+ return;
184
+ }
185
+ const show = this._searchEl.hidden;
186
+ this._searchEl.hidden = !show;
187
+ if (this._filterBtn) {
188
+ this._filterBtn.classList.toggle('jp-mod-active', show);
189
+ }
190
+ if (show) {
191
+ this._searchEl.focus();
192
+ }
193
+ else if (this._filter) {
194
+ this._filter = '';
195
+ this._searchEl.value = '';
196
+ this._render();
197
+ }
198
+ }
163
199
  /** Normalise strings for filter comparison: NFD-decompose, strip combining
164
200
  * diacritic marks, lowercase, and collapse separators (`-`, `_`, `.`, `/`,
165
201
  * whitespace) entirely. So "foo-bar", "foo_bar", "foo bar", "Foo Bar" all
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_claude_code_extension",
3
- "version": "1.1.16",
3
+ "version": "1.1.18",
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/icons.ts CHANGED
@@ -59,3 +59,12 @@ export const shieldIcon = new LabIcon({
59
59
  name: 'jupyterlab_claude_code_extension:shield',
60
60
  svgstr: shieldSvgStr
61
61
  });
62
+
63
+ const filterSvgStr = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
64
+ <path class="jp-icon3" fill="#616161" d="M10 18h4v-2h-4v2zM3 6v2h18V6H3zm3 7h12v-2H6v2z"/>
65
+ </svg>`;
66
+
67
+ export const filterIcon = new LabIcon({
68
+ name: 'jupyterlab_claude_code_extension:filter',
69
+ svgstr: filterSvgStr
70
+ });
package/src/widget.ts CHANGED
@@ -15,6 +15,7 @@ import { Message } from '@lumino/messaging';
15
15
  import { requestAPI } from './request';
16
16
  import {
17
17
  claudeIcon,
18
+ filterIcon,
18
19
  refreshIcon,
19
20
  removeIcon,
20
21
  shieldIcon,
@@ -183,6 +184,14 @@ export class ClaudeCodeSessionsWidget extends Widget {
183
184
  title.textContent = 'Claude Code Sessions';
184
185
  header.appendChild(title);
185
186
 
187
+ const filterBtn = document.createElement('button');
188
+ filterBtn.className = 'jp-ClaudeSessionsPanel-iconButton';
189
+ filterBtn.title = 'Filter sessions';
190
+ filterIcon.element({ container: filterBtn });
191
+ filterBtn.addEventListener('click', () => this._toggleFilterBar());
192
+ header.appendChild(filterBtn);
193
+ this._filterBtn = filterBtn;
194
+
186
195
  const refreshBtn = document.createElement('button');
187
196
  refreshBtn.className = 'jp-ClaudeSessionsPanel-iconButton';
188
197
  refreshBtn.title = 'Refresh';
@@ -196,10 +205,15 @@ export class ClaudeCodeSessionsWidget extends Widget {
196
205
  search.className = 'jp-ClaudeSessionsPanel-search';
197
206
  search.placeholder = 'Filter sessions...';
198
207
  search.spellcheck = false;
208
+ // Hidden by default; the filter-icon button reveals it. Lumino's
209
+ // ``hidden`` attribute toggles ``display: none`` via the user-agent
210
+ // stylesheet so no extra CSS rule is needed.
211
+ search.hidden = true;
199
212
  search.addEventListener('input', () => {
200
213
  this._filter = search.value;
201
214
  this._render();
202
215
  });
216
+ this._searchEl = search;
203
217
 
204
218
  const body = document.createElement('div');
205
219
  body.className = 'jp-ClaudeSessionsPanel-body';
@@ -211,6 +225,28 @@ export class ClaudeCodeSessionsWidget extends Widget {
211
225
  this._bodyEl = body;
212
226
  }
213
227
 
228
+ /** Show / hide the filter input. Hiding also clears the active filter
229
+ * so the user does not end up with an "invisible" filter narrowing
230
+ * the rows the next time they open the panel.
231
+ */
232
+ private _toggleFilterBar(): void {
233
+ if (!this._searchEl) {
234
+ return;
235
+ }
236
+ const show = this._searchEl.hidden;
237
+ this._searchEl.hidden = !show;
238
+ if (this._filterBtn) {
239
+ this._filterBtn.classList.toggle('jp-mod-active', show);
240
+ }
241
+ if (show) {
242
+ this._searchEl.focus();
243
+ } else if (this._filter) {
244
+ this._filter = '';
245
+ this._searchEl.value = '';
246
+ this._render();
247
+ }
248
+ }
249
+
214
250
  /** Normalise strings for filter comparison: NFD-decompose, strip combining
215
251
  * diacritic marks, lowercase, and collapse separators (`-`, `_`, `.`, `/`,
216
252
  * whitespace) entirely. So "foo-bar", "foo_bar", "foo bar", "Foo Bar" all
@@ -1062,6 +1098,8 @@ export class ClaudeCodeSessionsWidget extends Widget {
1062
1098
  private readonly _serverSettings: ServerConnection.ISettings;
1063
1099
  private _bodyEl!: HTMLDivElement;
1064
1100
  private _refreshBtn: HTMLButtonElement | null = null;
1101
+ private _filterBtn: HTMLButtonElement | null = null;
1102
+ private _searchEl: HTMLInputElement | null = null;
1065
1103
  private _sessions: ISession[] | null = null;
1066
1104
  private _expanded: Record<SectionKey, boolean> = loadExpanded();
1067
1105
  private _commands!: CommandRegistry;
package/style/base.css CHANGED
@@ -55,6 +55,11 @@
55
55
  color: var(--jp-ui-font-color1);
56
56
  }
57
57
 
58
+ .jp-ClaudeSessionsPanel-iconButton.jp-mod-active {
59
+ background: var(--jp-layout-color3);
60
+ color: var(--jp-brand-color1);
61
+ }
62
+
58
63
  .jp-ClaudeSessionsPanel-iconButton svg {
59
64
  width: 14px;
60
65
  height: 14px;