jupyterlab_ai_code_assistants_extension 1.2.73 → 1.2.99
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/README.md +6 -5
- package/lib/core/colour.d.ts +2 -10
- package/lib/core/colour.js +8 -18
- package/lib/core/modes.d.ts +2 -2
- package/lib/core/panel.d.ts +15 -43
- package/lib/core/panel.js +111 -226
- package/lib/core/popup.d.ts +1 -1
- package/lib/core/popup.js +52 -11
- package/lib/core/request.js +13 -6
- package/lib/core/terminals.d.ts +2 -33
- package/lib/core/terminals.js +24 -100
- package/lib/core/types.d.ts +13 -33
- package/lib/providers/claude.js +4 -11
- package/lib/providers/codex.js +9 -12
- package/lib/providers/deepseek.d.ts +1 -2
- package/lib/providers/deepseek.js +14 -35
- package/lib/providers/gemini.js +1 -9
- package/lib/providers/kimi.js +0 -6
- package/package.json +1 -1
- package/src/__tests__/colour.spec.ts +11 -19
- package/src/__tests__/launcher.spec.ts +1 -3
- package/src/__tests__/panel.spec.ts +252 -238
- package/src/__tests__/popup.spec.ts +105 -2
- package/src/__tests__/registry.spec.ts +1 -4
- package/src/__tests__/tab-colour-capture.spec.ts +48 -3
- package/src/__tests__/terminals.spec.ts +29 -143
- package/src/core/colour.ts +8 -23
- package/src/core/modes.ts +2 -2
- package/src/core/panel.ts +118 -270
- package/src/core/popup.ts +58 -12
- package/src/core/request.ts +13 -6
- package/src/core/terminals.ts +30 -118
- package/src/core/types.ts +13 -34
- package/src/index.ts +2 -2
- package/src/providers/claude.ts +4 -11
- package/src/providers/codex.ts +9 -12
- package/src/providers/deepseek.ts +15 -37
- package/src/providers/gemini.ts +1 -9
- package/src/providers/kimi.ts +0 -6
- package/style/base.css +76 -37
package/README.md
CHANGED
|
@@ -29,16 +29,16 @@ Chat-panel extensions re-implement the agent loop and trail the real tool. This
|
|
|
29
29
|
- **Provider registry** - assistant-specific behaviour lives in one module per assistant behind capability flags; no core file names an assistant, and adding one touches no core file
|
|
30
30
|
- **Joint settings page** - one settings section covering all assistants, with a per-assistant toggle (all on by default); toggling takes effect live, no JupyterLab reload, and a change reaches the other JupyterLab windows of the same browser
|
|
31
31
|
- **Three-section side panel** - Favorites, Recent, and All projects, each scrolling independently
|
|
32
|
-
- **One-click resume** - click a row to jump back into that session in a terminal; an open terminal for the project is reused instead of duplicated. Exception: a
|
|
32
|
+
- **One-click resume** - click a row to jump back into that session in a terminal; an open terminal for the project is reused instead of duplicated. Exception: a Kimi or DeepSeek conversation started with `+` runs under an id that never reaches its process command line, so a click on that row while its terminal stays open opens a second terminal. Codex mints the id for a conversation started with `+` and for a fork through its own app-server before the terminal opens, so a click on a Codex row reuses the open terminal instead of opening a second one
|
|
33
33
|
- **File browser follows the opened session** - opening a session moves the JupyterLab file browser to that session's project folder (a setting, on by default); a right-click on a panel header toggles this and moves the panels between the left and right sidebars
|
|
34
34
|
- **Launcher tiles** - every enabled assistant has a tile in an "AI Assistants" section of the JupyterLab Launcher (after Other, under the extension's own robot-head icon); a click opens the assistant in the file browser's current folder, resuming the folder's conversation when one exists and starting a new one otherwise, through the companion `jupyterlab_basic_terminal_extension` (installed automatically). Disable an assistant and its tile goes; disable them all and the section goes with them
|
|
35
35
|
- **Your terminal's `claude -c` follows the panel** (Claude) - switching to a conversation makes plain `claude -c` in that project resume that same conversation, including one that has been compacted, which Claude Code otherwise refuses to continue. Launching repairs a compacted conversation the same way, but only switching moves what `claude -c` picks
|
|
36
36
|
- **Conversation switcher** - a right-click "Switch and Manage Sessions" submenu lists a project's other conversations by name and short id with last-activity time; "Manage Sessions..." opens a searchable popup over the full list with multi-select delete and per-row open and copy-id buttons
|
|
37
|
-
- **Branch session** - fork the current conversation into a new named session via the right-click menu; each assistant forks its own way (Claude's native `--fork-session`, Codex's `
|
|
38
|
-
- **Rename session** - name a conversation from the right-click menu, and the name is written where the assistant itself reads it back: Claude gets the record its own `/rename` writes, Kimi a custom title, Gemini the chat's summary, DeepSeek its title event. The panel shows the name the assistant actually kept. Codex
|
|
37
|
+
- **Branch session** - fork the current conversation into a new named session via the right-click menu; each assistant forks its own way (Claude's native `--fork-session`, Codex's app-server `thread/fork`, server-side copies for Kimi, Gemini and DeepSeek) behind the same menu item
|
|
38
|
+
- **Rename session** - name a conversation from the right-click menu, and the name is written where the assistant itself reads it back: Claude gets the record its own `/rename` writes, Kimi a custom title, Gemini the chat's summary, DeepSeek its title event. The panel shows the name the assistant actually kept. Codex's name is set through Codex's own app-server, so its CLI lists the same name the panel shows
|
|
39
39
|
- **Launch modes under each assistant's own name** - Claude's skip-permissions, Codex's approval bypass, Kimi's `--yolo`, Gemini's YOLO; unsafe variants carry a shield glyph in the launch menus
|
|
40
40
|
- **Coloured terminal tabs** - each session's colour tints its terminal tab via the companion `jupyterlab_colourful_tab_extension` (installed automatically). Claude's own `/color` supplies its default, Kimi derives a stable colour from the conversation id; Codex, Gemini and DeepSeek have no colour of their own until you set one, and a branched session inherits its parent's colour, which it keeps even if the parent's colour later changes or is reset. Tinting needs the companion release that reports the colours you pick and lets another extension own a tab - against an older one this extension tints no tabs at all and says so once, and the companion's own right-click colours keep working as they always did
|
|
41
|
-
- **Your
|
|
41
|
+
- **Your newest colour choice wins** - set a colour on a terminal tab and the extension remembers it for that conversation, so the tab shows it again when you reopen the conversation. In Claude Code, `/color` is the second way to choose, and whichever you used last wins: a colour picked on the tab after `/color` stays, and running `/color` after a pick takes over again, including `/color` with the same colour or a `/color` reset. `Reset Tab Colour (n)` in the session's right-click menu hands back every colour you set by hand on that project's conversations, and appears once there is one to hand back. Clearing the colour on the tab itself releases it too, for the conversation that terminal is running; `Reset Tab Colour` is the way back for a conversation with no tab open. A Kimi or DeepSeek conversation that has never been resumed cannot be tracked yet, so a colour set on its tab is not kept
|
|
42
42
|
- **Favorites** - star projects you keep coming back to via the right-click menu; favourites from the standalone extensions are migrated on first run
|
|
43
43
|
- **Remove and clean up** - drop a project's history or a project's extra parallel sessions from the right-click menu, confirmation dialog first; removed files honour JupyterLab's "move files to trash" setting
|
|
44
44
|
- **Activity at a glance** - each row shows its last activity in an aligned column; rows active within the last minute light up, rows idle for over a week dim, and rows with parallel conversations show a branch icon with the count
|
|
@@ -46,7 +46,7 @@ Chat-panel extensions re-implement the agent loop and trail the real tool. This
|
|
|
46
46
|
- **Search** - fuzzy filter per panel, toggled by the funnel button
|
|
47
47
|
- **Presentation modes** - label rows by session name or by path relative to the JupyterLab root
|
|
48
48
|
- **Conflict-safe upgrade** - if a retired standalone extension is still installed, its panel wins and this extension stands down for that assistant instead of showing a duplicate
|
|
49
|
-
- **DeepSeek in the
|
|
49
|
+
- **DeepSeek in the terminal** - the DeepSeek Harness ships no terminal chat of its own, so its panel runs the community TUI bundle (`@deepseek-harness-tui/dsh-tui`, mounted as the `tui` profile with `dsh plugin --profile tui add @deepseek-harness-tui/dsh-tui`). Without that profile, a launch is refused before a terminal opens, naming the bundle and the mount command - `dsh tui` itself would instead die in a stack trace that prints that command with a literal `<package>` where the package name goes, and never the bundle's name. The panel lists the projects under `~/.dsh/sessions` (or `$DSH_HOME`), and a click resumes that row's conversation with `dsh tui --resume <id>` in a terminal; a launch with no conversation named starts a new one, whose id the TUI mints itself. Branching and deleting work from the panel as for every other assistant
|
|
50
50
|
- **Auto-disabled when absent** - an assistant whose CLI is not on `PATH` does not show a panel
|
|
51
51
|
- **Open Callback** - when an assistant's login sends your browser to an `http://127.0.0.1:...` link that fails to load, because the login is waiting on the machine running JupyterLab, run `Open Callback` from the command palette and paste the link; the Jupyter server requests it for you, and the popup says whether the login accepted it, then closes by itself 3 seconds later. Only `http` links to that machine's own loopback are accepted
|
|
52
52
|
|
|
@@ -55,6 +55,7 @@ Chat-panel extensions re-implement the agent loop and trail the real tool. This
|
|
|
55
55
|
- JupyterLab >= 4.6.0
|
|
56
56
|
- Python >= 3.10
|
|
57
57
|
- At least one assistant CLI on `PATH`: `claude`, `codex`, `kimi`, `gemini`, or `dsh` (the DeepSeek Harness)
|
|
58
|
+
- For DeepSeek: the community TUI bundle mounted as the `tui` profile, `dsh plugin --profile tui add @deepseek-harness-tui/dsh-tui` - a DeepSeek launch without it is refused with these words before any terminal opens
|
|
58
59
|
|
|
59
60
|
## Install
|
|
60
61
|
|
package/lib/core/colour.d.ts
CHANGED
|
@@ -95,8 +95,7 @@ export declare class ColourStore {
|
|
|
95
95
|
* times it, not it.
|
|
96
96
|
*
|
|
97
97
|
* An op must never call `load`, `set` or `forget` - it would wait on a chain
|
|
98
|
-
* that is waiting on it.
|
|
99
|
-
* which is why it is not itself queued. */
|
|
98
|
+
* that is waiting on it. */
|
|
100
99
|
private _run;
|
|
101
100
|
/** Open a write against one or more ids. Counted, not flagged: two writes to
|
|
102
101
|
* one id can be queued at once, and a flag would let the first to settle
|
|
@@ -106,8 +105,6 @@ export declare class ColourStore {
|
|
|
106
105
|
private _closeWrite;
|
|
107
106
|
/** Record a colour as this conversation's own. Passing null drops the entry,
|
|
108
107
|
* which is also how a deleted conversation leaves no orphan key behind.
|
|
109
|
-
* `handSet` false marks it inherited - written on a fork's behalf rather
|
|
110
|
-
* than chosen on its tab.
|
|
111
108
|
*
|
|
112
109
|
* Answers whether the store now HOLDS the colour, which is what the capture
|
|
113
110
|
* of a colour picked on a tab gates its repaint on - see `_captureColour`.
|
|
@@ -121,17 +118,12 @@ export declare class ColourStore {
|
|
|
121
118
|
* "Did not throw" is not the test - the answer carries the whole store, so
|
|
122
119
|
* it is checked for the value that was just written. A server that accepted
|
|
123
120
|
* the request and then failed to persist it would otherwise be believed. */
|
|
124
|
-
set(sessionId: string, colour: string | null
|
|
121
|
+
set(sessionId: string, colour: string | null): Promise<boolean>;
|
|
125
122
|
/** Drop stored colours - conversations that no longer exist, or overrides
|
|
126
123
|
* the user released. One request for the whole list, so a release is never
|
|
127
124
|
* left half applied. Same contract as `set`: the answer is checked for the
|
|
128
125
|
* absence of every id, and the cache is touched only once it agrees. */
|
|
129
126
|
forget(sessionIds: string[]): Promise<boolean>;
|
|
130
|
-
/** Copy the parent's EFFECTIVE colour onto a fresh branch, so a branch of a
|
|
131
|
-
* branch inherits whatever its immediate parent actually shows, user-set
|
|
132
|
-
* override included. Called at the fork sites, once the new id is known.
|
|
133
|
-
*/
|
|
134
|
-
inherit(parentColour: string | null, childId: string): Promise<void>;
|
|
135
127
|
private readonly _providerId;
|
|
136
128
|
private readonly _serverSettings;
|
|
137
129
|
private _colours;
|
package/lib/core/colour.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
// Terminal tab tint per conversation.
|
|
2
2
|
//
|
|
3
3
|
// Effective colour, highest wins:
|
|
4
|
-
// 1. user-set - the write-back store this module keeps, per provider
|
|
4
|
+
// 1. user-set - the write-back store this module keeps, per provider.
|
|
5
|
+
// Only entries the server still holds: it releases a pick
|
|
6
|
+
// that the assistant's own later colour command overtook,
|
|
7
|
+
// because the newer of the two channels wins (DEF-COLO-306)
|
|
5
8
|
// 2. native - the conversation's own colour, when the assistant has one
|
|
6
9
|
// 3. derived - a stable hash of the conversation id
|
|
7
10
|
// 4. none - no tint
|
|
@@ -186,8 +189,7 @@ export class ColourStore {
|
|
|
186
189
|
* times it, not it.
|
|
187
190
|
*
|
|
188
191
|
* An op must never call `load`, `set` or `forget` - it would wait on a chain
|
|
189
|
-
* that is waiting on it.
|
|
190
|
-
* which is why it is not itself queued. */
|
|
192
|
+
* that is waiting on it. */
|
|
191
193
|
_run(op) {
|
|
192
194
|
const result = this._chain.then(op);
|
|
193
195
|
const settled = () => undefined;
|
|
@@ -218,8 +220,6 @@ export class ColourStore {
|
|
|
218
220
|
}
|
|
219
221
|
/** Record a colour as this conversation's own. Passing null drops the entry,
|
|
220
222
|
* which is also how a deleted conversation leaves no orphan key behind.
|
|
221
|
-
* `handSet` false marks it inherited - written on a fork's behalf rather
|
|
222
|
-
* than chosen on its tab.
|
|
223
223
|
*
|
|
224
224
|
* Answers whether the store now HOLDS the colour, which is what the capture
|
|
225
225
|
* of a colour picked on a tab gates its repaint on - see `_captureColour`.
|
|
@@ -233,7 +233,7 @@ export class ColourStore {
|
|
|
233
233
|
* "Did not throw" is not the test - the answer carries the whole store, so
|
|
234
234
|
* it is checked for the value that was just written. A server that accepted
|
|
235
235
|
* the request and then failed to persist it would otherwise be believed. */
|
|
236
|
-
async set(sessionId, colour
|
|
236
|
+
async set(sessionId, colour) {
|
|
237
237
|
if (!sessionId) {
|
|
238
238
|
return false;
|
|
239
239
|
}
|
|
@@ -249,7 +249,7 @@ export class ColourStore {
|
|
|
249
249
|
body: JSON.stringify({
|
|
250
250
|
session_id: sessionId,
|
|
251
251
|
colour,
|
|
252
|
-
hand_set:
|
|
252
|
+
hand_set: true
|
|
253
253
|
})
|
|
254
254
|
});
|
|
255
255
|
if (((_a = data.colours) !== null && _a !== void 0 ? _a : {})[sessionId] !== (colour !== null && colour !== void 0 ? colour : undefined)) {
|
|
@@ -265,7 +265,7 @@ export class ColourStore {
|
|
|
265
265
|
this._colours.delete(sessionId);
|
|
266
266
|
}
|
|
267
267
|
this._overrides.delete(sessionId);
|
|
268
|
-
if (colour
|
|
268
|
+
if (colour) {
|
|
269
269
|
this._overrides.add(sessionId);
|
|
270
270
|
}
|
|
271
271
|
return true;
|
|
@@ -316,14 +316,4 @@ export class ColourStore {
|
|
|
316
316
|
}
|
|
317
317
|
});
|
|
318
318
|
}
|
|
319
|
-
/** Copy the parent's EFFECTIVE colour onto a fresh branch, so a branch of a
|
|
320
|
-
* branch inherits whatever its immediate parent actually shows, user-set
|
|
321
|
-
* override included. Called at the fork sites, once the new id is known.
|
|
322
|
-
*/
|
|
323
|
-
async inherit(parentColour, childId) {
|
|
324
|
-
if (!parentColour || !childId) {
|
|
325
|
-
return;
|
|
326
|
-
}
|
|
327
|
-
await this.set(childId, parentColour, false);
|
|
328
|
-
}
|
|
329
319
|
}
|
package/lib/core/modes.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export interface IResolvedLaunchMode {
|
|
|
15
15
|
* do. Otherwise the first switch that is on fires. Every mode is a boolean
|
|
16
16
|
* skip-approval switch, so "on" always means the same thing.
|
|
17
17
|
*/
|
|
18
|
-
export declare function resolveLaunchMode(launchModes: ILaunchMode[], values: Record<string, boolean
|
|
18
|
+
export declare function resolveLaunchMode(launchModes: ILaunchMode[], values: Record<string, boolean>, force?: string): string | undefined;
|
|
19
19
|
/**
|
|
20
20
|
* The mode a PLAIN launch would carry right now, as a descriptor entry.
|
|
21
21
|
*
|
|
@@ -26,4 +26,4 @@ export declare function resolveLaunchMode(launchModes: ILaunchMode[], values: Re
|
|
|
26
26
|
* unmarked. Answers the entry rather than the token so the caller can use the
|
|
27
27
|
* assistant's own wording.
|
|
28
28
|
*/
|
|
29
|
-
export declare function resolvedLaunchModeEntry(launchModes: ILaunchMode[], values: Record<string, boolean
|
|
29
|
+
export declare function resolvedLaunchModeEntry(launchModes: ILaunchMode[], values: Record<string, boolean>): IResolvedLaunchMode | null;
|
package/lib/core/panel.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
37
37
|
setRecentLimit(n: number): void;
|
|
38
38
|
/** Current values of this provider's launch modes, read from settings. Each
|
|
39
39
|
* key is an `ILaunchMode.id` in the assistant's own terminology. */
|
|
40
|
-
setModes(modes: Record<string, boolean
|
|
40
|
+
setModes(modes: Record<string, boolean>): void;
|
|
41
41
|
setColouredTabs(on: boolean): void;
|
|
42
42
|
/** Whether opening a session moves the file browser to its project folder. */
|
|
43
43
|
setSyncFileBrowser(on: boolean): void;
|
|
@@ -45,6 +45,10 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
45
45
|
protected onBeforeHide(_msg: Message): void;
|
|
46
46
|
protected onCloseRequest(msg: Message): void;
|
|
47
47
|
private _buildShell;
|
|
48
|
+
/** Raise the click-time busy overlay. Counted, because an open and a branch
|
|
49
|
+
* can overlap; the overlay clears when the last one ends. */
|
|
50
|
+
private _beginBusy;
|
|
51
|
+
private _endBusy;
|
|
48
52
|
/** Show / hide the filter input. Hiding also clears the active filter, so
|
|
49
53
|
* the user never returns to an "invisible" filter narrowing the rows. */
|
|
50
54
|
private _toggleFilterBar;
|
|
@@ -143,26 +147,13 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
143
147
|
launchHere(cwd?: string): Promise<any>;
|
|
144
148
|
/** Start a brand-new conversation in the file browser's current folder. */
|
|
145
149
|
private _newSession;
|
|
146
|
-
/**
|
|
147
|
-
*
|
|
148
|
-
* `forkStrategy`.
|
|
149
|
-
*
|
|
150
|
-
* The three strategies differ only in who mints the new id and when it
|
|
151
|
-
* becomes knowable - so the shared part (ask for a name, launch a terminal
|
|
152
|
-
* on the fork, inherit the parent's tint, surface the branch) lives here
|
|
153
|
-
* once, and the strategy decides the payload and the discovery.
|
|
154
|
-
*/
|
|
150
|
+
/** Branch the active row's current conversation. Every `forkStrategy` has
|
|
151
|
+
* the server mint the fork, so this is the one path. */
|
|
155
152
|
private _branchSession;
|
|
156
|
-
/** The
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
|
|
160
|
-
/** The CLI owns both the fork verb and the id, so the id has to be
|
|
161
|
-
* discovered: snapshot the branch ids first, then diff after launch. */
|
|
162
|
-
private _branchByNativeCommand;
|
|
163
|
-
/** The assistant has no fork at all: the server copies the store entry and
|
|
164
|
-
* answers with the new id, so the branch exists before the terminal opens
|
|
165
|
-
* and one refresh surfaces it - no watcher needed. */
|
|
153
|
+
/** The server mints the new id and answers with it before the terminal
|
|
154
|
+
* opens - `server-copy` copies the store entry, `server-native` drives the
|
|
155
|
+
* assistant's own non-interactive fork path - so one refresh surfaces the
|
|
156
|
+
* branch. */
|
|
166
157
|
private _branchByServerCopy;
|
|
167
158
|
/** Drop the hand-set colours of this project's conversations and re-tint
|
|
168
159
|
* from the defaults at once, so nothing keeps showing a released colour
|
|
@@ -178,26 +169,6 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
178
169
|
* the conversation - but only for a conversation whose terminal is open and
|
|
179
170
|
* probes as this assistant's, which is why this path exists as well. */
|
|
180
171
|
private _resetColours;
|
|
181
|
-
/** Copy the parent's tint onto a fresh branch.
|
|
182
|
-
*
|
|
183
|
-
* A native-colour assistant hands every conversation its own colour, so a
|
|
184
|
-
* fork already arrives correctly tinted: only what this extension's own
|
|
185
|
-
* store holds for the parent is inherited there - an entry the parent itself
|
|
186
|
-
* inherited counts, which is what makes a branch of a branch take what its
|
|
187
|
-
* parent actually shows. Copying the parent's native tint instead would pin
|
|
188
|
-
* the fork to it and shadow the fork's own `/color` for good.
|
|
189
|
-
* Every other assistant inherits the parent's effective colour, which is the
|
|
190
|
-
* only tint it has. */
|
|
191
|
-
private _inheritColour;
|
|
192
|
-
/** Poll fast for a fork whose id we already know, then refresh once. The
|
|
193
|
-
* panel is never updated before the branch genuinely exists - it cannot list
|
|
194
|
-
* a store entry that has not been written. */
|
|
195
|
-
private _watchForBranch;
|
|
196
|
-
/** Poll fast for a fork whose id the CLI minted: whichever conversation the
|
|
197
|
-
* snapshot did not hold is the fork. It is then pinned as current (recency
|
|
198
|
-
* alone would not - the actively written parent overtakes it) and inherits
|
|
199
|
-
* the parent's tint. */
|
|
200
|
-
private _watchForNewBranch;
|
|
201
172
|
/** Name the row's current conversation, in the assistant's own store.
|
|
202
173
|
*
|
|
203
174
|
* Only ever offered where the descriptor says the assistant keeps a name
|
|
@@ -273,9 +244,7 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
273
244
|
private _variantSuffix;
|
|
274
245
|
/** Whether a forced variant would build the launch the plain item already
|
|
275
246
|
* builds - a mode left on in settings makes the plain item carry it, so the
|
|
276
|
-
* variant beside it would be a second entry for one outcome.
|
|
277
|
-
* variant can match: a forced token is the bare mode id, so an enum sitting
|
|
278
|
-
* on a widening value is a launch no variant duplicates. */
|
|
247
|
+
* variant beside it would be a second entry for one outcome. */
|
|
279
248
|
private _buildsSameLaunch;
|
|
280
249
|
/** The glyph, only where the mode WIDENS what runs without asking. */
|
|
281
250
|
private _variantIcon;
|
|
@@ -337,6 +306,9 @@ export declare class AssistantSessionsPanel extends Widget {
|
|
|
337
306
|
private readonly _removingPaths;
|
|
338
307
|
private _bodyEl;
|
|
339
308
|
private _loadingEl;
|
|
309
|
+
private _busyEl;
|
|
310
|
+
private _busyTextEl;
|
|
311
|
+
private _busyCount;
|
|
340
312
|
private _errorEl;
|
|
341
313
|
private _refreshBtn;
|
|
342
314
|
private _filterBtn;
|