@jupytergis/jupytergis-lab 0.2.1 → 0.4.0
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/index.js +15 -50
- package/lib/notebookrenderer.d.ts +18 -6
- package/lib/notebookrenderer.js +100 -20
- package/package.json +9 -9
- package/style/base.css +16 -1
package/lib/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { IMainMenu } from '@jupyterlab/mainmenu';
|
|
|
6
6
|
import { IStateDB } from '@jupyterlab/statedb';
|
|
7
7
|
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
8
8
|
import { Menu } from '@lumino/widgets';
|
|
9
|
-
import {
|
|
9
|
+
import { notebookRendererPlugin } from './notebookrenderer';
|
|
10
10
|
const NAME_SPACE = 'jupytergis';
|
|
11
11
|
const plugin = {
|
|
12
12
|
id: 'jupytergis:lab:main-menu',
|
|
@@ -29,6 +29,7 @@ const plugin = {
|
|
|
29
29
|
const stateDbManager = GlobalStateDbManager.getInstance();
|
|
30
30
|
stateDbManager.initialize(state);
|
|
31
31
|
addCommands(app, tracker, translator, formSchemaRegistry, layerBrowserRegistry, state, completionProviderManager);
|
|
32
|
+
app.shell.addClass('data-jgis-keybinding');
|
|
32
33
|
// SOURCES context menu
|
|
33
34
|
const newSourceSubMenu = new Menu({ commands: app.commands });
|
|
34
35
|
newSourceSubMenu.title.label = translator
|
|
@@ -71,21 +72,11 @@ const plugin = {
|
|
|
71
72
|
rank: 1,
|
|
72
73
|
command: CommandIDs.removeSource
|
|
73
74
|
});
|
|
74
|
-
app.commands.addKeyBinding({
|
|
75
|
-
command: CommandIDs.removeSource,
|
|
76
|
-
keys: ['Delete'],
|
|
77
|
-
selector: '.jp-gis-source.jp-gis-sourceUnused'
|
|
78
|
-
});
|
|
79
75
|
app.contextMenu.addItem({
|
|
80
76
|
selector: '.jp-gis-source',
|
|
81
77
|
rank: 1,
|
|
82
78
|
command: CommandIDs.renameSource
|
|
83
79
|
});
|
|
84
|
-
app.commands.addKeyBinding({
|
|
85
|
-
command: CommandIDs.renameSource,
|
|
86
|
-
keys: ['F2'],
|
|
87
|
-
selector: '.jp-gis-sourceItem'
|
|
88
|
-
});
|
|
89
80
|
// LAYERS and LAYER GROUPS context menu
|
|
90
81
|
app.contextMenu.addItem({
|
|
91
82
|
command: CommandIDs.symbology,
|
|
@@ -103,20 +94,15 @@ const plugin = {
|
|
|
103
94
|
selector: '.jp-gis-layerItem',
|
|
104
95
|
rank: 2
|
|
105
96
|
});
|
|
106
|
-
app.commands.addKeyBinding({
|
|
107
|
-
command: CommandIDs.removeLayer,
|
|
108
|
-
keys: ['Delete'],
|
|
109
|
-
selector: '.jp-gis-layerItem'
|
|
110
|
-
});
|
|
111
97
|
app.contextMenu.addItem({
|
|
112
98
|
command: CommandIDs.renameLayer,
|
|
113
99
|
selector: '.jp-gis-layerItem',
|
|
114
100
|
rank: 2
|
|
115
101
|
});
|
|
116
|
-
app.
|
|
117
|
-
command: CommandIDs.
|
|
118
|
-
|
|
119
|
-
|
|
102
|
+
app.contextMenu.addItem({
|
|
103
|
+
command: CommandIDs.zoomToLayer,
|
|
104
|
+
selector: '.jp-gis-layerItem',
|
|
105
|
+
rank: 2
|
|
120
106
|
});
|
|
121
107
|
const moveLayerSubmenu = new Menu({ commands: app.commands });
|
|
122
108
|
moveLayerSubmenu.title.label = translator
|
|
@@ -135,21 +121,11 @@ const plugin = {
|
|
|
135
121
|
selector: '.jp-gis-layerGroupHeader',
|
|
136
122
|
rank: 2
|
|
137
123
|
});
|
|
138
|
-
app.commands.addKeyBinding({
|
|
139
|
-
command: CommandIDs.removeGroup,
|
|
140
|
-
keys: ['Delete'],
|
|
141
|
-
selector: '.jp-gis-layerGroupHeader'
|
|
142
|
-
});
|
|
143
124
|
app.contextMenu.addItem({
|
|
144
125
|
command: CommandIDs.renameGroup,
|
|
145
126
|
selector: '.jp-gis-layerGroupHeader',
|
|
146
127
|
rank: 2
|
|
147
128
|
});
|
|
148
|
-
app.commands.addKeyBinding({
|
|
149
|
-
command: CommandIDs.renameGroup,
|
|
150
|
-
keys: ['F2'],
|
|
151
|
-
selector: '.jp-gis-layerGroupHeader'
|
|
152
|
-
});
|
|
153
129
|
// Separator
|
|
154
130
|
app.contextMenu.addItem({
|
|
155
131
|
type: 'separator',
|
|
@@ -181,25 +157,13 @@ const plugin = {
|
|
|
181
157
|
command: CommandIDs.newImageLayer,
|
|
182
158
|
args: { from: 'contextMenu' }
|
|
183
159
|
});
|
|
160
|
+
newLayerSubMenu.addItem({
|
|
161
|
+
command: CommandIDs.newHeatmapLayer,
|
|
162
|
+
args: { from: 'contextMenu' }
|
|
163
|
+
});
|
|
184
164
|
if (mainMenu) {
|
|
185
165
|
populateMenus(mainMenu, isEnabled);
|
|
186
166
|
}
|
|
187
|
-
// Keybindings for the console
|
|
188
|
-
app.commands.addKeyBinding({
|
|
189
|
-
command: CommandIDs.executeConsole,
|
|
190
|
-
keys: ['Shift Enter'],
|
|
191
|
-
selector: ".jpgis-console .jp-CodeConsole[data-jp-interaction-mode='notebook'] .jp-CodeConsole-promptCell"
|
|
192
|
-
});
|
|
193
|
-
app.commands.addKeyBinding({
|
|
194
|
-
command: CommandIDs.invokeCompleter,
|
|
195
|
-
keys: ['Tab'],
|
|
196
|
-
selector: '.jpgis-console .jp-CodeConsole-promptCell .jp-mod-completer-enabled'
|
|
197
|
-
});
|
|
198
|
-
app.commands.addKeyBinding({
|
|
199
|
-
command: CommandIDs.selectCompleter,
|
|
200
|
-
keys: ['Enter'],
|
|
201
|
-
selector: '.jpgis-console .jp-ConsolePanel .jp-mod-completer-active'
|
|
202
|
-
});
|
|
203
167
|
}
|
|
204
168
|
};
|
|
205
169
|
const controlPanel = {
|
|
@@ -217,7 +181,8 @@ const controlPanel = {
|
|
|
217
181
|
const leftControlPanel = new LeftPanelWidget({
|
|
218
182
|
model: controlModel,
|
|
219
183
|
tracker,
|
|
220
|
-
state
|
|
184
|
+
state,
|
|
185
|
+
commands: app.commands
|
|
221
186
|
});
|
|
222
187
|
leftControlPanel.id = 'jupytergis::leftControlPanel';
|
|
223
188
|
leftControlPanel.title.caption = 'JupyterGIS Control Panel';
|
|
@@ -258,10 +223,10 @@ function populateMenus(mainMenu, isEnabled) {
|
|
|
258
223
|
*/
|
|
259
224
|
function buildGroupsMenu(contextMenu, tracker) {
|
|
260
225
|
var _a, _b, _c, _d;
|
|
261
|
-
if (!((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.
|
|
226
|
+
if (!((_a = tracker.currentWidget) === null || _a === void 0 ? void 0 : _a.model)) {
|
|
262
227
|
return;
|
|
263
228
|
}
|
|
264
|
-
const model = (_b = tracker.currentWidget) === null || _b === void 0 ? void 0 : _b.
|
|
229
|
+
const model = (_b = tracker.currentWidget) === null || _b === void 0 ? void 0 : _b.model;
|
|
265
230
|
const submenu = (_d = (_c = contextMenu.menu.items.find(item => {
|
|
266
231
|
var _a;
|
|
267
232
|
return item.type === 'submenu' &&
|
|
@@ -307,4 +272,4 @@ function buildGroupsMenu(contextMenu, tracker) {
|
|
|
307
272
|
command: CommandIDs.moveLayerToNewGroup
|
|
308
273
|
});
|
|
309
274
|
}
|
|
310
|
-
export default [plugin, controlPanel,
|
|
275
|
+
export default [plugin, controlPanel, notebookRendererPlugin];
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JupyterGISOutputWidget, JupyterGISTracker } from '@jupytergis/base';
|
|
2
|
+
import { IJGISExternalCommandRegistry, JupyterGISModel } from '@jupytergis/schema';
|
|
2
3
|
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
|
|
4
|
+
import { CommandRegistry } from '@lumino/commands';
|
|
3
5
|
import { Panel } from '@lumino/widgets';
|
|
4
6
|
import { JupyterYModel } from 'yjs-widgets';
|
|
5
7
|
export interface ICommMetadata {
|
|
@@ -14,10 +16,20 @@ export declare class YJupyterGISModel extends JupyterYModel {
|
|
|
14
16
|
jupyterGISModel: JupyterGISModel;
|
|
15
17
|
}
|
|
16
18
|
export declare class YJupyterGISLuminoWidget extends Panel {
|
|
17
|
-
constructor(options:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
constructor(options: IOptions);
|
|
20
|
+
get jgisWidget(): JupyterGISOutputWidget;
|
|
21
|
+
/**
|
|
22
|
+
* Build the widget and add it to the panel.
|
|
23
|
+
* @param options
|
|
24
|
+
*/
|
|
25
|
+
private _buildWidget;
|
|
21
26
|
private _jgisWidget;
|
|
22
27
|
}
|
|
23
|
-
|
|
28
|
+
interface IOptions {
|
|
29
|
+
commands: CommandRegistry;
|
|
30
|
+
model: JupyterGISModel;
|
|
31
|
+
externalCommands?: IJGISExternalCommandRegistry;
|
|
32
|
+
tracker?: JupyterGISTracker;
|
|
33
|
+
}
|
|
34
|
+
export declare const notebookRendererPlugin: JupyterFrontEndPlugin<void>;
|
|
35
|
+
export {};
|
package/lib/notebookrenderer.js
CHANGED
|
@@ -1,44 +1,114 @@
|
|
|
1
1
|
import { ICollaborativeDrive } from '@jupyter/collaborative-drive';
|
|
2
|
-
import { JupyterGISPanel } from '@jupytergis/base';
|
|
3
|
-
import { JupyterGISModel } from '@jupytergis/schema';
|
|
2
|
+
import { JupyterGISOutputWidget, JupyterGISPanel, ToolbarWidget } from '@jupytergis/base';
|
|
3
|
+
import { IJGISExternalCommandRegistryToken, IJupyterGISDocTracker, JupyterGISModel } from '@jupytergis/schema';
|
|
4
|
+
import { showErrorMessage } from '@jupyterlab/apputils';
|
|
5
|
+
import { ConsolePanel } from '@jupyterlab/console';
|
|
6
|
+
import { PathExt } from '@jupyterlab/coreutils';
|
|
7
|
+
import { NotebookPanel } from '@jupyterlab/notebook';
|
|
4
8
|
import { MessageLoop } from '@lumino/messaging';
|
|
5
9
|
import { Panel, Widget } from '@lumino/widgets';
|
|
6
|
-
import { IJupyterYWidgetManager, JupyterYModel } from 'yjs-widgets';
|
|
10
|
+
import { IJupyterYWidgetManager, JupyterYDoc, JupyterYModel } from 'yjs-widgets';
|
|
7
11
|
export const CLASS_NAME = 'jupytergis-notebook-widget';
|
|
8
12
|
export class YJupyterGISModel extends JupyterYModel {
|
|
9
13
|
}
|
|
10
14
|
export class YJupyterGISLuminoWidget extends Panel {
|
|
11
15
|
constructor(options) {
|
|
12
16
|
super();
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Build the widget and add it to the panel.
|
|
19
|
+
* @param options
|
|
20
|
+
*/
|
|
21
|
+
this._buildWidget = (options) => {
|
|
22
|
+
const { commands, model, externalCommands, tracker } = options;
|
|
23
|
+
const content = new JupyterGISPanel({ model, commandRegistry: commands });
|
|
24
|
+
let toolbar = undefined;
|
|
25
|
+
if (model.filePath) {
|
|
26
|
+
toolbar = new ToolbarWidget({
|
|
27
|
+
commands,
|
|
28
|
+
model,
|
|
29
|
+
externalCommands: (externalCommands === null || externalCommands === void 0 ? void 0 : externalCommands.getCommands()) || []
|
|
30
|
+
});
|
|
16
31
|
}
|
|
32
|
+
this._jgisWidget = new JupyterGISOutputWidget({
|
|
33
|
+
model,
|
|
34
|
+
content,
|
|
35
|
+
toolbar
|
|
36
|
+
});
|
|
37
|
+
this.addWidget(this._jgisWidget);
|
|
38
|
+
tracker === null || tracker === void 0 ? void 0 : tracker.add(this._jgisWidget);
|
|
17
39
|
};
|
|
40
|
+
const { model } = options;
|
|
18
41
|
this.addClass(CLASS_NAME);
|
|
19
|
-
this.
|
|
20
|
-
|
|
42
|
+
this._buildWidget(options);
|
|
43
|
+
// If the filepath was not set when building the widget, the toolbar is not built.
|
|
44
|
+
// The widget has to be built again to include the toolbar.
|
|
45
|
+
const onchange = (_, args) => {
|
|
46
|
+
if (args.stateChange) {
|
|
47
|
+
args.stateChange.forEach((change) => {
|
|
48
|
+
var _a;
|
|
49
|
+
if (change.name === 'path') {
|
|
50
|
+
(_a = this.layout) === null || _a === void 0 ? void 0 : _a.removeWidget(this._jgisWidget);
|
|
51
|
+
this._jgisWidget.dispose();
|
|
52
|
+
this._buildWidget(options);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
model.sharedModel.changed.connect(onchange);
|
|
58
|
+
}
|
|
59
|
+
get jgisWidget() {
|
|
60
|
+
return this._jgisWidget;
|
|
21
61
|
}
|
|
22
62
|
}
|
|
23
|
-
export const
|
|
63
|
+
export const notebookRendererPlugin = {
|
|
24
64
|
id: 'jupytergis:yjswidget-plugin',
|
|
25
65
|
autoStart: true,
|
|
26
|
-
optional: [
|
|
27
|
-
|
|
66
|
+
optional: [
|
|
67
|
+
IJGISExternalCommandRegistryToken,
|
|
68
|
+
IJupyterGISDocTracker,
|
|
69
|
+
IJupyterYWidgetManager,
|
|
70
|
+
ICollaborativeDrive
|
|
71
|
+
],
|
|
72
|
+
activate: (app, externalCommandRegistry, jgisTracker, yWidgetManager, drive) => {
|
|
28
73
|
if (!yWidgetManager) {
|
|
29
74
|
console.error('Missing IJupyterYWidgetManager token!');
|
|
30
75
|
return;
|
|
31
76
|
}
|
|
32
|
-
if (!drive) {
|
|
33
|
-
console.error('Cannot setup JupyterGIS Python API without a collaborative drive');
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
77
|
class YJupyterGISModelFactory extends YJupyterGISModel {
|
|
37
|
-
|
|
78
|
+
async initialize(commMetadata) {
|
|
38
79
|
const { path, format, contentType } = commMetadata;
|
|
39
80
|
const fileFormat = format;
|
|
81
|
+
if (!drive) {
|
|
82
|
+
showErrorMessage('Error using the JupyterGIS Python API', 'You cannot use the JupyterGIS Python API without a collaborative drive. You need to install a package providing collaboration features (e.g. jupyter-collaboration).');
|
|
83
|
+
throw new Error('Failed to create the YDoc without a collaborative drive');
|
|
84
|
+
}
|
|
85
|
+
// The path of the project is relative to the path of the notebook
|
|
86
|
+
let currentWidgetPath = '';
|
|
87
|
+
const currentWidget = app.shell.currentWidget;
|
|
88
|
+
if (currentWidget instanceof NotebookPanel ||
|
|
89
|
+
currentWidget instanceof ConsolePanel) {
|
|
90
|
+
currentWidgetPath = currentWidget.sessionContext.path;
|
|
91
|
+
}
|
|
92
|
+
let localPath = '';
|
|
93
|
+
if (path) {
|
|
94
|
+
localPath = PathExt.join(PathExt.dirname(currentWidgetPath), path);
|
|
95
|
+
// If the file does not exist yet, create it
|
|
96
|
+
try {
|
|
97
|
+
await app.serviceManager.contents.get(localPath);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
await app.serviceManager.contents.save(localPath, {
|
|
101
|
+
content: btoa('{}'),
|
|
102
|
+
format: 'base64'
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
// If the user did not provide a path, do not create
|
|
108
|
+
localPath = PathExt.join(PathExt.dirname(currentWidgetPath), 'unsaved_project');
|
|
109
|
+
}
|
|
40
110
|
const sharedModel = drive.sharedModelFactory.createNew({
|
|
41
|
-
path,
|
|
111
|
+
path: localPath,
|
|
42
112
|
format: fileFormat,
|
|
43
113
|
contentType,
|
|
44
114
|
collaborative: true
|
|
@@ -46,7 +116,10 @@ export const notebookRenderePlugin = {
|
|
|
46
116
|
this.jupyterGISModel = new JupyterGISModel({
|
|
47
117
|
sharedModel: sharedModel
|
|
48
118
|
});
|
|
49
|
-
|
|
119
|
+
this.jupyterGISModel.contentsManager = app.serviceManager.contents;
|
|
120
|
+
this.jupyterGISModel.filePath = localPath;
|
|
121
|
+
this.ydoc = this.jupyterGISModel.sharedModel.ydoc;
|
|
122
|
+
this.sharedModel = new JupyterYDoc(commMetadata, this.ydoc);
|
|
50
123
|
}
|
|
51
124
|
}
|
|
52
125
|
class YJupyterGISWidget {
|
|
@@ -54,13 +127,20 @@ export const notebookRenderePlugin = {
|
|
|
54
127
|
this.yModel = yModel;
|
|
55
128
|
this.node = node;
|
|
56
129
|
const widget = new YJupyterGISLuminoWidget({
|
|
57
|
-
|
|
130
|
+
commands: app.commands,
|
|
131
|
+
model: yModel.jupyterGISModel,
|
|
132
|
+
externalCommands: externalCommandRegistry,
|
|
133
|
+
tracker: jgisTracker
|
|
58
134
|
});
|
|
59
|
-
|
|
135
|
+
this._jgisWidget = widget.jgisWidget;
|
|
60
136
|
MessageLoop.sendMessage(widget, Widget.Msg.BeforeAttach);
|
|
61
137
|
node.appendChild(widget.node);
|
|
62
138
|
MessageLoop.sendMessage(widget, Widget.Msg.AfterAttach);
|
|
63
139
|
}
|
|
140
|
+
dispose() {
|
|
141
|
+
// Dispose of the widget.
|
|
142
|
+
this._jgisWidget.dispose();
|
|
143
|
+
}
|
|
64
144
|
}
|
|
65
145
|
yWidgetManager.registerWidget('@jupytergis:widget', YJupyterGISModelFactory, YJupyterGISWidget);
|
|
66
146
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupytergis/jupytergis-lab",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "JupyterGIS Lab extension.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jupyter",
|
|
@@ -53,23 +53,23 @@
|
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@jupyter/collaborative-drive": "^3.0.0",
|
|
56
|
-
"@jupytergis/base": "^0.
|
|
57
|
-
"@jupytergis/
|
|
58
|
-
"@jupytergis/schema": "^0.2.1",
|
|
56
|
+
"@jupytergis/base": "^0.4.0",
|
|
57
|
+
"@jupytergis/schema": "^0.4.0",
|
|
59
58
|
"@jupyterlab/application": "^4.3.0",
|
|
60
59
|
"@jupyterlab/apputils": "^4.3.0",
|
|
60
|
+
"@jupyterlab/completer": "^4.3.0",
|
|
61
|
+
"@jupyterlab/console": "^4.3.0",
|
|
61
62
|
"@jupyterlab/coreutils": "^6.3.0",
|
|
62
|
-
"@jupyterlab/docregistry": "^4.3.0",
|
|
63
|
-
"@jupyterlab/filebrowser": "^4.3.0",
|
|
64
|
-
"@jupyterlab/launcher": "^4.3.0",
|
|
65
63
|
"@jupyterlab/mainmenu": "^4.3.0",
|
|
64
|
+
"@jupyterlab/notebook": "^4.3.0",
|
|
66
65
|
"@jupyterlab/services": "^7.3.0",
|
|
66
|
+
"@jupyterlab/statedb": "^4.3.0",
|
|
67
67
|
"@jupyterlab/translation": "^4.3.0",
|
|
68
|
-
"@
|
|
68
|
+
"@lumino/commands": "^2.0.0",
|
|
69
69
|
"@lumino/messaging": "^2.0.0",
|
|
70
70
|
"@lumino/widgets": "^2.0.0",
|
|
71
71
|
"react": "^18.0.1",
|
|
72
|
-
"yjs-widgets": "^0.
|
|
72
|
+
"yjs-widgets": "^0.4"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@jupyterlab/builder": "^4.3.0",
|
package/style/base.css
CHANGED
|
@@ -212,10 +212,16 @@ div.jGIS-toolbar-widget > div.jp-Toolbar-item:last-child {
|
|
|
212
212
|
border: solid 1.5px red;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
.jGIS-Mainview-Container {
|
|
216
|
+
display: flex;
|
|
217
|
+
flex-direction: column;
|
|
218
|
+
height: 100%;
|
|
219
|
+
}
|
|
220
|
+
|
|
215
221
|
.jGIS-Mainview {
|
|
216
222
|
width: 100%;
|
|
217
|
-
height: 100%;
|
|
218
223
|
box-sizing: border-box;
|
|
224
|
+
flex: 1;
|
|
219
225
|
}
|
|
220
226
|
|
|
221
227
|
.jGIS-Popup-Wrapper {
|
|
@@ -523,6 +529,7 @@ div.jGIS-toolbar-widget > div.jp-Toolbar-item:last-child {
|
|
|
523
529
|
order: 1;
|
|
524
530
|
margin-top: 2px;
|
|
525
531
|
margin-bottom: 2px;
|
|
532
|
+
text-transform: capitalize;
|
|
526
533
|
}
|
|
527
534
|
|
|
528
535
|
.jGIS-property-panel
|
|
@@ -731,3 +738,11 @@ div.jGIS-toolbar-widget > div.jp-Toolbar-item:last-child {
|
|
|
731
738
|
.jgis-identify-grid-body:last-of-type strong:last-of-type {
|
|
732
739
|
padding-bottom: 8px;
|
|
733
740
|
}
|
|
741
|
+
|
|
742
|
+
/* Style the file path text */
|
|
743
|
+
.file-container {
|
|
744
|
+
display: flex;
|
|
745
|
+
align-items: center;
|
|
746
|
+
margin-bottom: 16px;
|
|
747
|
+
gap: 10px;
|
|
748
|
+
}
|