Files
tao.chenandClaude cd1aba6698 dev: initial implementation of notebook-snapshot extension
Manual-commit notebook snapshot extension (JupyterLab 4):

Backend
- Storage adapter layer (Local + S3 via boto3, pip extra)
  - Dumb put/get/list/delete contract; key validation
  - S3Storage: lazy boto3 import, S3ConnectionError with friendly
    messages + connectivity check at startup
- SnapshotStore
  - Strip code outputs/execution_count, MD5 on cleaned JSON
  - Gzipped envelope (id/timestamp/name/description/hash/size/notebook)
  - Append-only manifest.json (rebuildable from version files)
  - SnapshotUnchangedError when new hash matches most recent version
- REST API (commit / list / content) under /snapshot/ namespace
- traitlets config (storage_type, local_root, s3_* + S3_* env fallback)

Frontend
- snapshot:commit command (toolbar button + command palette)
- Dialog for name + description
- SnapshotPanel (left sidebar timeline) + DiffWidget (main area)
- Cell diff: id-match first, LCS fallback; jsdiff for line diff
- Restore via model.fromJSON() + context.save() (no refresh)
- All AGENTS.md hard conventions enforced

Tests: 33 backend pytest passing (storage + store + routes)

Docs: AGENTS.md, design.md, README.md synced with implementation.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-21 17:33:55 +08:00

148 lines
4.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# notebook-snapshot
JupyterLab 扩展:**手动保存 .ipynb 快照**,无 Git 依赖。
左侧栏查看版本时间轴、Cell 级 diff、一键无刷新恢复。
## 功能
- 工具栏「保存快照」按钮 / 命令面板 `snapshot:commit` 触发
- 对话框填写 `name`(必填)+ `description`(可选)后保存
- 内容与最近版本相同时自动跳过(弹 warning),避免重复快照
- 左侧栏时间轴,支持对比(Cell 级 diff,行级红绿高亮)和恢复
- 恢复走 `model.fromJSON()` + `context.save()`,无页面刷新
- 历史 append-only,永不删除 / 改写
## 存储后端
- **Local**(默认):`<root_dir>/.notebook_versions` 或自定义路径
- **S3**pip extra):通过 boto3 对接 AWS S3 / MinIO / 任何 S3-API 兼容服务
## 环境要求
- JupyterLab >= 4.0.0
- Python >= 3.10
- Node.js(构建前端用)
## 安装
```bash
# 1. 后端(基础包零依赖)
pip install snapshot
# 2. 前端构建
jlpm install
jlpm build
# 3. 启用 server extension
jupyter server extension enable snapshot
jupyter labextension develop . --overwrite # dev 安装;正式版 wheel 跳过此步
```
可选 S3 后端:
```bash
pip install "snapshot[s3]" # 装 boto3
```
## 配置
写在 `~/.jupyter/jupyter_server_config.py`(用户级)或 `<项目>/jupyter_server_config.py`(项目级):
| 字段 | 默认 | 说明 |
|------|------|------|
| `storage_type` | `"local"` | `"local"``"s3"` |
| `local_root` | `""``<root_dir>/.notebook_versions` | Local 存储根目录;**建议显式设置**避免污染 notebook 目录 |
| `s3_endpoint` | `""`env: `S3_ENDPOINT` | `localhost:9000`MinIO/ `s3.us-east-1.amazonaws.com`AWS |
| `s3_access_key` | `""`env: `S3_ACCESS_KEY` | |
| `s3_secret_key` | `""`env: `S3_SECRET_KEY` | |
| `s3_bucket` | `"notebook-snapshot"` | 不存在自动建 |
| `s3_secure` | `False` | HTTPS |
| `s3_prefix` | `""` | 桶内 key 前缀 |
### 示例
```python
# 切到自定义 local 目录
c.SnapshotConfig.local_root = "/var/lib/jupyter/snapshots"
# 切到 MinIO
c.SnapshotConfig.storage_type = "s3"
c.SnapshotConfig.s3_endpoint = "minio.example.com:9000"
c.SnapshotConfig.s3_bucket = "team-snapshots"
# 凭证走 envexport S3_ACCESS_KEY=... ; export S3_SECRET_KEY=...
# 切到 AWS S3
c.SnapshotConfig.storage_type = "s3"
c.SnapshotConfig.s3_endpoint = "s3.us-east-1.amazonaws.com"
c.SnapshotConfig.s3_secure = True
# 用 AWS 标准凭证:export AWS_ACCESS_KEY_ID=... ; export AWS_SECRET_ACCESS_KEY=...
# (注:当前版本仅读取 S3_* 环境变量;如需 AWS_* 自动识别,参考 boto3 文档配置)
```
环境变量 / 命令行 / 配置文件三种方式优先级遵循 traitlets 规则;改完配置**必须重启 `jupyter lab`**。
## 开发
```bash
source .venv/bin/activate # 本仓库用 uv/venv
pip install -e ".[dev,test,s3]" # 后端装 dev/test/s3 依赖
.venv/bin/jlpm install # 前端依赖
.venv/bin/jlpm build # 一次性构建(tsc + rspack + labextension
.venv/bin/jlpm watch # 终端 1:监听 src/ 自动重编
jupyter lab # 终端 2:启动 JupyterLab
```
**改了 Python 后端**:重启 `jupyter lab`(不需要 rebuild 前端)。
**改了前端 TypeScript**`jlpm watch` 自动重编,浏览器刷新即可。
## 验证
```bash
# 后端
.venv/bin/pytest snapshot/tests/ -vv
# 前端类型检查 + 打包
.venv/bin/jlpm build
# Lint
.venv/bin/jlpm lint:check
```
## 架构
参见 [design.md](design.md)。
简要:
- **后端**`/snapshot/` 命名空间下三个端点(commit / list / content);`SnapshotStore` 包装 `StorageAdapter`Local + S3),gzip + envelope 存盘,manifest 是可重建的索引
- **前端**`SnapshotModel` + `SnapshotPanel`(左侧栏) + `DiffWidget`(主区) + `snapshot:commit` 命令(工具栏 + 命令面板)
硬约定(实现时不得违背)见 [AGENTS.md](AGENTS.md)。
## 排错
```bash
jupyter server extension list # 后端 extension 状态
jupyter labextension list # 前端 extension 状态
```
- **工具栏没有「保存快照」按钮**:刷新浏览器;确认 `jupyter labextension list` 显示 enabled + OK
- **保存时 400**:检查 `path` / `content` / `name` 是否齐全
- **保存后弹"内容未变更"**:正常行为——内容与最近版本 hash 相同会自动跳过,改动 cell 后再保存即可
- **S3 连不上**:确认 `endpoint`(不带 scheme)、`secure`http/https)、bucket 存在性;凭证走 env 别写进配置
## 卸载
```bash
jupyter server extension disable snapshot
pip uninstall snapshot
```
## AI 工具支持
本项目遵循 [AGENTS.md](https://agents.md) 标准,配套符号链接:
- `CLAUDE.md``AGENTS.md`Claude Code
- 其他工具:Cursor / Copilot / Windsurf / Aider 等直接读取 `AGENTS.md`
详细开发规范见 [AGENTS.md](AGENTS.md),设计文档见 [design.md](design.md)。