From bc1828bde689042650b419177bf250196d536d48 Mon Sep 17 00:00:00 2001 From: "tao.chen" <93983997+taochen-ct@users.noreply.github.com> Date: Tue, 21 Jul 2026 18:23:34 +0800 Subject: [PATCH] fix: surface hash-duplicate warning and add manual refresh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues fixed: 1. **Skipped-commit warning not visible** — When the user commits unchanged content (hash matches most recent version), the backend correctly returns {skipped: true, reason: "unchanged", message: ...} but the frontend Notification.warning was easy to miss (default 4-5s autoClose, small toast at top-right). Now uses {autoClose: false} so the warning stays until manually dismissed, plus a console.log of the raw commit response for debugging. Added a fallback branch that warns clearly if the response shape is unexpected. 2. **No manual refresh on the panel** — In S3 setups the list call can occasionally lag the just-committed entry (manifest strong consistency is a per-implementation property). Added a "刷新" button at the top of the sidebar that calls model.refresh(), letting the user force a re-fetch when the auto-refresh misses. 3. **S3 addressing_style** — user-side config addition: S3Storage now uses path-style addressing (endpoint/bucket/key) which is the format required by MinIO and several other S3-compatible services. Co-Authored-By: Claude --- snapshot/storage/s3.py | 5 +- src/commands.ts | 23 +++++- src/widgets/SnapshotPanel.tsx | 138 +++++++++++++++++++--------------- style/base.css | 31 ++++++++ 4 files changed, 130 insertions(+), 67 deletions(-) diff --git a/snapshot/storage/s3.py b/snapshot/storage/s3.py index a08c435..5c454dc 100644 --- a/snapshot/storage/s3.py +++ b/snapshot/storage/s3.py @@ -82,7 +82,10 @@ class S3Storage(StorageAdapter): aws_access_key_id=access_key, aws_secret_access_key=secret_key, region_name="us-east-1", - config=BotoConfig(signature_version="s3v4"), + config=BotoConfig( + signature_version='s3v4', + s3={'addressing_style': 'path'}, + ), ) self._ClientError = ClientError self._connect_and_ensure_bucket() diff --git a/src/commands.ts b/src/commands.ts index 9e97be7..b2140b7 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -64,12 +64,27 @@ export function registerCommands( name, description ); - if (result.skipped) { - // Content hash matches the most recent version — nothing to save. - Notification.warning(result.message ?? '内容未变更,已跳过保存'); - } else if (result.entry) { + // eslint-disable-next-line no-console + console.log('[snapshot] commit response:', result); + if (result && result.skipped) { + // Hash matched the most recent version — nothing to save. Use + // autoClose:false so the warning stays visible until the user + // dismisses it (the default 4-5s timeout is easy to miss). + Notification.warning( + result.message ?? '内容未变更,已跳过保存', + { autoClose: false } + ); + } else if (result && result.entry) { await model.refresh(); Notification.success(`已保存快照:${name}`); + } else { + // Unexpected response shape — surface clearly so the user is not + // left wondering whether the save happened. + // eslint-disable-next-line no-console + console.warn('[snapshot] unexpected commit response shape:', result); + Notification.warning('保存响应格式异常,请查看浏览器控制台', { + autoClose: false + }); } } catch (e) { Notification.error(`保存快照失败:${(e as Error).message}`); diff --git a/src/widgets/SnapshotPanel.tsx b/src/widgets/SnapshotPanel.tsx index 3b03a15..b25b172 100644 --- a/src/widgets/SnapshotPanel.tsx +++ b/src/widgets/SnapshotPanel.tsx @@ -46,70 +46,84 @@ const Panel: React.FC = ({ model, app, tracker, serverSettings, onOp if (!model.path) { return
未选中 notebook
; } - if (model.loading && model.versions.length === 0) { - return
加载中…
; - } - if (model.versions.length === 0) { - return ( -
-
暂无快照
-
点击工具栏「保存快照」按钮创建
-
- ); - } return ( - + + ) : ( + + )} + ); }; diff --git a/style/base.css b/style/base.css index ec6f0d1..859dc83 100644 --- a/style/base.css +++ b/style/base.css @@ -13,6 +13,37 @@ box-sizing: border-box; } +.jp-snapshot-panel-container { + display: flex; + flex-direction: column; + height: 100%; +} + +.jp-snapshot-panel-toolbar { + display: flex; + justify-content: flex-end; + margin-bottom: 6px; +} + +.jp-snapshot-panel-refresh-button { + font-size: 0.8em; + padding: 2px 8px; + 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; +} + +.jp-snapshot-panel-refresh-button:hover:not(:disabled) { + background: var(--jp-layout-color3, #e8e8e8); +} + +.jp-snapshot-panel-refresh-button:disabled { + opacity: 0.6; + cursor: not-allowed; +} + .jp-snapshot-panel-empty { color: var(--jp-ui-font-color2, #555); padding: 12px;