diff --git a/package.json b/package.json index 0979390..736a3c6 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@jupyterlab/notebook": "^4.0.0", "@jupyterlab/services": "^7.0.0", "@jupyterlab/settingregistry": "^4.0.0", + "@jupyterlab/ui-components": "^4.0.0", "@lumino/signaling": "^2.0.0", "@lumino/widgets": "^2.0.0", "diff": "^5.2.0", diff --git a/src/index.ts b/src/index.ts index 7fa28e9..83a7aa9 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ import { import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook'; import { ToolbarButton, MainAreaWidget } from '@jupyterlab/apputils'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; +import { saveIcon } from '@jupyterlab/ui-components'; import { requestAPI } from './request'; import { registerCommands, CommandIDs } from './commands'; @@ -59,7 +60,11 @@ const plugin: JupyterFrontEndPlugin = { onOpenDiff: openDiff }); panel.id = 'snapshot:panel'; - panel.title.label = '快照历史'; + // Icon-only panel tab: the title text becomes a hover caption so the + // sidebar stays compact. saveIcon is reused for visual consistency with + // the commit toolbar button (both signal "save" semantics). + panel.title.icon = saveIcon; + panel.title.caption = '快照历史'; app.shell.add(panel, 'left', { rank: 200 }); // Keep the model in sync with the current notebook. @@ -73,13 +78,14 @@ const plugin: JupyterFrontEndPlugin = { registerCommands(app, tracker, model); - // Add a "保存快照" button to every notebook panel's toolbar. + // Add an icon-only "保存快照" button to every notebook panel's toolbar. + // The tooltip on hover carries the full description. // NOTE: docRegistry.addWidgetExtension('Notebook', ...) places its // returned widget in the *main area* (a new tab), not on the toolbar. // The toolbar wants panel.toolbar.addItem directly. const addCommitButton = (panelHost: NotebookPanel) => { const button = new ToolbarButton({ - label: '保存快照', + icon: saveIcon, tooltip: '保存当前 notebook 为快照', onClick: () => { void app.commands.execute(CommandIDs.commit); diff --git a/src/widgets/SnapshotPanel.tsx b/src/widgets/SnapshotPanel.tsx index b25b172..a8e542e 100644 --- a/src/widgets/SnapshotPanel.tsx +++ b/src/widgets/SnapshotPanel.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { ReactWidget } from '@jupyterlab/apputils'; import { ServerConnection } from '@jupyterlab/services'; +import { refreshIcon } from '@jupyterlab/ui-components'; import { getSnapshotContent } from '../api'; import { INotebook } from '../diff'; @@ -50,14 +51,18 @@ const Panel: React.FC = ({ model, app, tracker, serverSettings, onOp
{model.loading && model.versions.length === 0 ? ( diff --git a/style/base.css b/style/base.css index 859dc83..a980803 100644 --- a/style/base.css +++ b/style/base.css @@ -26,13 +26,18 @@ } .jp-snapshot-panel-refresh-button { - font-size: 0.8em; - padding: 2px 8px; + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 24px; + padding: 0; border: 1px solid var(--jp-border-color2, #ccc); border-radius: 3px; background: var(--jp-layout-color2, #f5f5f5); color: var(--jp-ui-font-color1, #333); cursor: pointer; + line-height: 0; } .jp-snapshot-panel-refresh-button:hover:not(:disabled) { @@ -44,6 +49,20 @@ cursor: not-allowed; } +.jp-snapshot-panel-refresh-button.is-loading svg, +.jp-snapshot-panel-refresh-button.is-loading .jp-Icon { + animation: jp-snapshot-spin 1s linear infinite; +} + +@keyframes jp-snapshot-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + .jp-snapshot-panel-empty { color: var(--jp-ui-font-color2, #555); padding: 12px; diff --git a/uv.lock b/uv.lock index 5421989..d3398d7 100644 --- a/uv.lock +++ b/uv.lock @@ -1969,7 +1969,6 @@ wheels = [ name = "snapshot" source = { editable = "." } dependencies = [ - { name = "boto3" }, { name = "jupyter-server" }, ] @@ -1978,6 +1977,9 @@ dev = [ { name = "jupyter-builder" }, { name = "jupyterlab" }, ] +s3 = [ + { name = "boto3" }, +] test = [ { name = "coverage" }, { name = "pytest" }, @@ -1988,7 +1990,7 @@ test = [ [package.metadata] requires-dist = [ - { name = "boto3", specifier = ">=1.43.52" }, + { name = "boto3", marker = "extra == 's3'", specifier = ">=1.34.0" }, { name = "coverage", marker = "extra == 'test'" }, { name = "jupyter-builder", marker = "extra == 'dev'", specifier = ">=1.0.0" }, { name = "jupyter-server", specifier = ">=2.4.0,<3" }, @@ -1998,7 +2000,7 @@ requires-dist = [ { name = "pytest-cov", marker = "extra == 'test'" }, { name = "pytest-jupyter", extras = ["server"], marker = "extra == 'test'", specifier = ">=0.6.0" }, ] -provides-extras = ["dev", "test"] +provides-extras = ["dev", "s3", "test"] [[package]] name = "soupsieve" diff --git a/yarn.lock b/yarn.lock index 1673c6d..3b7330e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2785,7 +2785,7 @@ __metadata: languageName: node linkType: hard -"@jupyterlab/ui-components@npm:^4.6.1": +"@jupyterlab/ui-components@npm:^4.0.0, @jupyterlab/ui-components@npm:^4.6.1": version: 4.6.1 resolution: "@jupyterlab/ui-components@npm:4.6.1" dependencies: @@ -9222,6 +9222,7 @@ __metadata: "@jupyterlab/settingregistry": ^4.0.0 "@jupyterlab/testing": ^4.6.1 "@jupyterlab/testutils": ^4.0.0 + "@jupyterlab/ui-components": ^4.0.0 "@lumino/signaling": ^2.0.0 "@lumino/widgets": ^2.0.0 "@module-federation/runtime-tools": ^2.0.0