Compare commits

..
Author SHA1 Message Date
zhongyidianandClaude Opus 5 9ab67b1bd3 fix: 完善学习进度同步与拼图收集流程
统一阶段拼图配置和进度上报,增加同步写入协调与失败重试,优化收集确认及同步反馈,并展示菜单探索进度。补充相关回归测试与字体素材。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-10-12 09:29:21 +08:00
zhongyidianandClaude Opus 5 6491b0c918 fix: 优化进度调试与答题交互
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 10:29:54 +08:00
zhongyidian d445f81e60 chore: 更新素材资源和组件优化
- 更新陈云、鲁迅、聂耳等故居的展品图片素材
- 更新拼图细节图片
- 优化学习地图底图和详情图
- 更新音频素材
- 优化各故居 store 和页面组件
2026-08-12 11:35:09 +08:00
zhongyidianandClaude Opus 4.8 165374b986 fix: 修复从场景返回首页时误弹续学提示的问题
问题:用户从对话/体悟/内化场景点击「离开」返回故居首页时,会立即弹出「继续学习」提示,干扰正常流程。

修复:
- 新增 previousPath 模块级变量记录上一个路由
- 检测进入首页时上一个路由是否为本故居的 place/reflection/internalization/draw
- 如果是刚从场景离开的,不弹窗,让用户自主选择下一步

影响文件:
- src/hooks/useResidenceContinue.ts

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 14:32:40 +08:00
zhongyidianandClaude Opus 4.8 73f6df92ce chore: 更新素材和组件优化
- 更新展品图片(鲁迅/毛泽东/孙中山/黄炎培/聂耳/张乐平故居)
- 新增点击查看图标、报纸素材
- 优化视频组件、拼图组件、展品组件
- 更新陈云音频资源
- 新增输入验证文档和工具函数
- 组件布局和动画优化

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:50:40 +08:00
zhongyidianandClaude Opus 4.8 73d02eb62d feat: 实现故居续学弹窗功能
新增功能:
- 统一续学逻辑:8个故居首页接入自动续学提示弹窗
- 完成判定:基于拼图碎片统一判断对话/体悟/内化是否完成
- 断点续学:对话回lastVisitedPlace、体悟回reflectionRoute、内化回internalization
- 触发条件:会话内每故居仅提示一次 + 首页视频播完后 + store加载成功/空时
- 安全防护:组件卸载后不再setState、同步失败不弹窗避免用旧状态误导

新增组件:
- ContinueLearningModal:纯CSS哑光米纸+朱红古籍风弹窗
- useResidenceContinue:封装触发逻辑+跳转+去重的共享hook

涉及文件:
- src/config/progression.ts:getResidenceStageCompletion、resolveResidenceContinueTarget
- src/hooks/useResidenceContinue.ts:统一续学hook
- src/components/layout/alert/continue-learning-modal.tsx:弹窗组件
- src/pages/*/home.tsx:8个故居首页接入

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:44:27 +08:00
zhongyidianandClaude Opus 4.7 49ecabbf3a fix: 修复开发/测试包模拟进度被易班同步覆盖的问题
进度模拟面板写入 puzzleStore 后,App 启动时易班同步会将其覆盖;
现在在同步完成后按 sessionStorage 中保存的模式重放模拟进度,
确保测试包的"到达某故居/全流程完成"调试入口生效。

同时纳入近期未提交的页面、组件、资源与环境变量改动。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-23 10:19:36 +08:00
zhongyidianandClaude Opus 4.7 6d40e19346 feat: update residence learning flows
Commit non-media application and asset changes while leaving audio, video, and environment files out of version control.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:15:26 +08:00
zhongyidianandClaude Opus 4.7 68ba22a33f fix: update quiz flows and chenyun reflection progression
Add the requested Rong Desheng quiz gates and fix Chen Yun reflection progression so success prompts and trend chart state advance correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:54:21 +08:00
zhongyidian 284302341c feat: add progression logic for puzzle residences
- Implemented functions to manage puzzle progress including checking if residences are started, complete, and unlocked.
- Added utility functions to retrieve piece IDs based on residence and route.
- Introduced a new TypeScript file for progression management.
- Integrated puzzle data configuration and residence data for seamless progression tracking.
2026-05-01 21:19:25 +08:00
zhongyidianandClaude Opus 4.7 5a1c3cc368 Merge origin/master into main
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:01:23 +08:00
kevinzhu 4a2be3b4d0 feat:修改英文双引号 2026-04-30 09:17:31 +08:00
441 changed files with 18627 additions and 4033 deletions

No files matched your search

+15 -1
View File
@@ -45,7 +45,21 @@
"Bash(git -C /Users/zhongyidian/工作/育享/沪上又见故人来/shanghai-residences/.claude/worktrees/agent-a41031e2 show main:src/pages/zhangleping/home.tsx)",
"Bash(git -C /Users/zhongyidian/工作/育享/沪上又见故人来/shanghai-residences/.claude/worktrees/agent-a41031e2 show main:src/store/storeNames.ts)",
"Bash(git -C /Users/zhongyidian/工作/育享/沪上又见故人来/shanghai-residences/.claude/worktrees/agent-a41031e2 grep -n \"getUserDefine\" main -- src/pages/zhangleping src/store/zhanglepingStore.ts src/App.tsx)",
"Bash(git -C /Users/zhongyidian/工作/育享/沪上又见故人来/shanghai-residences/.claude/worktrees/agent-a41031e2 grep -n \"setDrawArtwork\\\\|clearDrawArtwork\\\\|drawArtworkDataUrl\" main -- src)"
"Bash(git -C /Users/zhongyidian/工作/育享/沪上又见故人来/shanghai-residences/.claude/worktrees/agent-a41031e2 grep -n \"setDrawArtwork\\\\|clearDrawArtwork\\\\|drawArtworkDataUrl\" main -- src)",
"Bash(git grep *)",
"Bash(git commit -m ' *)",
"Bash(bun ls-files *)",
"Bash(bunx prettier *)",
"Bash(awk 'NR>=216 && NR<=380' src/config/puzzleData.ts)",
"Bash(awk 'NR>=380 && NR<=540' src/config/puzzleData.ts)",
"Bash(awk '/^worktree / {wt=$2} /^branch / {br=$2; if \\(wt ~ /\\\\.claude\\\\/worktrees\\\\/agent-/\\) print wt\"\\\\t\"br}')",
"Read(//tmp/**)",
"Bash(git check-ignore *)",
"Bash(git config *)",
"WebSearch",
"Bash(grep -v \"^--$\")",
"Bash(grep -E \"^\\\\S+.*>.*</GuideStepsItem>$\")",
"Bash(git -C \"/Users/zhongyidian/工作/育享/沪上又见故人来/shanghai-residences/.claude/worktrees/agent-ab7540fc239364906\" status --short)"
],
"deny": [
"Read(public/**)",
+1
View File
@@ -0,0 +1 @@
VITE_DEBUG_ENTRY=false
+1
View File
@@ -0,0 +1 @@
VITE_DEBUG_ENTRY=false
+2
View File
@@ -0,0 +1,2 @@
VITE_DEBUG_ENTRY=true
VITE_DISABLE_FIRST_WATCH_VIDEO_GATING=true
+128
View File
@@ -0,0 +1,128 @@
# 信件输入校验功能说明
## 功能概述
为信件模板(`LetterTemplate.tsx`)的输入框添加了严格的内容校验,包括:
1. **禁止英文字母** - 自动过滤并提示
2. **禁止数字** - 自动过滤并提示
3. **禁止所有标点符号和特殊符号**(包括中文标点和英文标点)- 自动过滤并提示
4. **敏感词过滤** - 检测并替换为星号,提交时校验
## 实现细节
### 1. 核心函数(`src/lib/utils/sensitiveWords.ts`)
#### `filterInput(input: string): string`
实时过滤输入内容,在用户输入时自动去除:
- 英文字母(a-z, A-Z)
- 数字(0-9)
- 所有标点符号(包括中文标点:,。!?;:等)
- 所有特殊符号(@#$%^&*等)
- 敏感词(替换为星号)
#### `validateInput(input: string): { valid: boolean, message: string }`
提交时完整校验,返回详细的错误信息:
- 空内容检测
- 英文字母检测
- 数字检测
- 特殊符号检测
- 敏感词检测
#### 辅助函数
- `containsEnglish()` - 检测是否包含英文
- `containsNumber()` - 检测是否包含数字
- `containsSpecialChars()` - 检测是否包含标点符号或特殊符号(包括中文标点)
- `containsSensitiveWord()` - 检测是否包含敏感词
### 2. 敏感词库
已扩展的敏感词库包括:
- 基础不文明用语
- 暴力相关词汇
- 违法违规内容
- 歧视性用语
可根据实际需求在 `SENSITIVE_WORDS` 数组中扩展。
### 3. 用户体验
#### 实时过滤
用户输入时:
- **中文输入法支持**:在输入拼音时不会被过滤,等用户选择中文后才进行过滤
- **英文字母、数字、所有标点符号和特殊符号** 会被立即删除(用户无法输入)
- **敏感词** 会被替换为星号(例如:"垃圾" → "**")
- **中文字符** 可以正常输入
示例:
- 输入 "你好abc123世界" → 实时显示 "你好世界"
- 输入 "测试垃圾内容" → 实时显示 "测试**内容"
- 输入 "你好,世界!" → 实时显示 "你好世界"(标点被过滤)
- 使用中文输入法输入 "nihao" → 看到拼音 → 选择"你好" → 显示"你好" ✅
#### 提交校验
当用户点击提交时:
- 如果内容包含被禁止的字符或敏感词,会显示相应提示
- 错误的输入框会清空并标红
- 用户需要重新填写
#### 允许的内容
- ✅ 纯中文字符(汉字)
- ✅ 空格
- ❌ 英文字母(自动删除)
- ❌ 数字(自动删除)
- ❌ 所有标点符号(包括中文标点如,。!?;:等,自动删除)
- ❌ 所有特殊符号(@#$%^&*等,自动删除)
- ⚠️ 敏感词(替换为星号 * )
## 测试
所有校验函数都已通过测试:
- ✓ 英文字母检测和过滤
- ✓ 数字检测和过滤
- ✓ 特殊符号检测和过滤
- ✓ 敏感词检测和过滤
- ✓ 中文标点符号保留
- ✓ 完整的提交校验流程
## 使用示例
```typescript
import { filterInput, validateInput } from '@/lib/utils/sensitiveWords'
// 实时过滤
const handleChange = (value: string) => {
const filtered = filterInput(value) // 自动过滤英文、数字、特殊符号、敏感词
setValue(filtered)
}
// 提交前校验
const handleSubmit = () => {
const result = validateInput(value)
if (!result.valid) {
showHint(result.message)
return
}
// 继续提交逻辑
}
```
## 配置选项
在 `LetterFieldConfig` 中可以通过 `sanitize` 字段控制是否启用校验:
```typescript
const field: LetterFieldConfig = {
type: 'input',
sanitize: true, // 默认启用校验
// sanitize: false, // 禁用校验(不建议)
}
```
## 注意事项
1. **落款姓名** 也会进行相同的校验
2. **中文输入法支持** - 使用 `onCompositionStart` 和 `onCompositionEnd` 事件处理中文输入法,确保拼音输入过程不会被过滤
3. **敏感词库** 需要定期维护和更新
4. 如果需要添加更多敏感词,请在 `sensitiveWords.ts` 的 `SENSITIVE_WORDS` 数组中添加
5. 所有标点符号(包括中文标点)都会被过滤
+49 -1
View File
@@ -8,6 +8,7 @@
"axios": "^1.12.2",
"clsx": "^2.1.1",
"framer-motion": "^12.23.26",
"html-to-image": "^1.11.13",
"lucide-react": "^0.545.0",
"mammoth": "^1.11.0",
"react": "^19.1.1",
@@ -47,6 +48,7 @@
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.1",
"vite": "^7.1.7",
"vitest": "^5.0.1",
},
},
},
@@ -347,6 +349,10 @@
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
"@types/chai": ["@types/chai@5.2.3", "https://registry.npmmirror.com/@types/chai/-/chai-5.2.3.tgz", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="],
"@types/deep-eql": ["@types/deep-eql@4.0.2", "https://registry.npmmirror.com/@types/deep-eql/-/deep-eql-4.0.2.tgz", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="],
"@types/doctrine": ["@types/doctrine@0.0.9", "https://registry.npmmirror.com/@types/doctrine/-/doctrine-0.0.9.tgz", {}, "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA=="],
"@types/estree": ["@types/estree@1.0.8", "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
@@ -389,6 +395,8 @@
"@vitest/expect": ["@vitest/expect@2.0.5", "https://registry.npmmirror.com/@vitest/expect/-/expect-2.0.5.tgz", { "dependencies": { "@vitest/spy": "2.0.5", "@vitest/utils": "2.0.5", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" } }, "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA=="],
"@vitest/mocker": ["@vitest/mocker@5.0.1", "https://registry.npmmirror.com/@vitest/mocker/-/mocker-5.0.1.tgz", { "dependencies": { "@jridgewell/trace-mapping": "0.3.31", "@vitest/spy": "5.0.1", "estree-walker": "^3.0.3", "magic-string": "^1.2.3" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-6K1DoBNAPGvuOcSsGA4D6x+5zEEff/KmOOP3uetT2TrGpVfI+HRHRnJJfKi5ib/g1vx8IYHQD8s0pbJz8WQI7Q=="],
"@vitest/pretty-format": ["@vitest/pretty-format@2.1.9", "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", { "dependencies": { "tinyrainbow": "^1.2.0" } }, "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ=="],
"@vitest/spy": ["@vitest/spy@2.0.5", "https://registry.npmmirror.com/@vitest/spy/-/spy-2.0.5.tgz", { "dependencies": { "tinyspy": "^3.0.0" } }, "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA=="],
@@ -449,7 +457,7 @@
"caniuse-lite": ["caniuse-lite@1.0.30001774", "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz", {}, "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA=="],
"chai": ["chai@5.3.3", "https://registry.npmmirror.com/chai/-/chai-5.3.3.tgz", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
"chai": ["chai@6.2.2", "https://registry.npmmirror.com/chai/-/chai-6.2.2.tgz", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
"chalk": ["chalk@4.1.2", "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
@@ -519,6 +527,8 @@
"es-errors": ["es-errors@1.3.0", "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
"es-module-lexer": ["es-module-lexer@2.3.2", "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-2.3.2.tgz", {}, "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw=="],
"es-object-atoms": ["es-object-atoms@1.1.1", "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="],
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
@@ -559,6 +569,8 @@
"esutils": ["esutils@2.0.3", "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"expect-type": ["expect-type@1.4.0", "https://registry.npmmirror.com/expect-type/-/expect-type-1.4.0.tgz", {}, "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA=="],
"fast-deep-equal": ["fast-deep-equal@3.1.3", "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-diff": ["fast-diff@1.3.0", "https://registry.npmmirror.com/fast-diff/-/fast-diff-1.3.0.tgz", {}, "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="],
@@ -621,6 +633,8 @@
"hasown": ["hasown@2.0.2", "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
"html-to-image": ["html-to-image@1.11.13", "https://registry.npmmirror.com/html-to-image/-/html-to-image-1.11.13.tgz", {}, "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg=="],
"ignore": ["ignore@7.0.5", "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"immediate": ["immediate@3.0.6", "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
@@ -763,6 +777,8 @@
"node-releases": ["node-releases@2.0.27", "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.27.tgz", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="],
"obug": ["obug@2.2.1", "https://registry.npmmirror.com/obug/-/obug-2.2.1.tgz", {}, "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q=="],
"open": ["open@8.4.2", "https://registry.npmmirror.com/open/-/open-8.4.2.tgz", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="],
"option": ["option@0.2.4", "https://registry.npmmirror.com/option/-/option-0.2.4.tgz", {}, "sha512-pkEqbDyl8ou5cpq+VsnQbe/WlEy5qS7xPzMS1U55OCG9KPvwFD46zDbxQIj3egJSFc3D+XhYOPUzz49zQAVy7A=="],
@@ -865,6 +881,8 @@
"shebang-regex": ["shebang-regex@3.0.0", "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"siginfo": ["siginfo@2.0.0", "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
"signal-exit": ["signal-exit@4.1.0", "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="],
"source-map": ["source-map@0.6.1", "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
@@ -873,6 +891,10 @@
"sprintf-js": ["sprintf-js@1.0.3", "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="],
"stackback": ["stackback@0.0.2", "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
"std-env": ["std-env@4.2.0", "https://registry.npmmirror.com/std-env/-/std-env-4.2.0.tgz", {}, "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw=="],
"storybook": ["storybook@8.6.17", "https://registry.npmmirror.com/storybook/-/storybook-8.6.17.tgz", { "dependencies": { "@storybook/core": "8.6.17" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": { "getstorybook": "./bin/index.cjs", "sb": "./bin/index.cjs", "storybook": "./bin/index.cjs" } }, "sha512-krR/l680A6qVnkGiK9p8jY0ucX3+kFCs2f4zw+S3w2Cdq8EiM/tFebPcX2V4S3z2UsO0v0dwAJOJNpzbFPdmVg=="],
"string-width": ["string-width@5.1.2", "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="],
@@ -905,6 +927,10 @@
"tiny-invariant": ["tiny-invariant@1.3.3", "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
"tinybench": ["tinybench@6.1.4", "https://registry.npmmirror.com/tinybench/-/tinybench-6.1.4.tgz", {}, "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ=="],
"tinyexec": ["tinyexec@1.3.0", "https://registry.npmmirror.com/tinyexec/-/tinyexec-1.3.0.tgz", {}, "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ=="],
"tinyglobby": ["tinyglobby@0.2.15", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
"tinyrainbow": ["tinyrainbow@1.2.0", "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz", {}, "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ=="],
@@ -945,12 +971,16 @@
"vite": ["vite@7.3.1", "https://registry.npmmirror.com/vite/-/vite-7.3.1.tgz", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
"vitest": ["vitest@5.0.1", "https://registry.npmmirror.com/vitest/-/vitest-5.0.1.tgz", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/mocker": "5.0.1", "chai": "^6.2.2", "es-module-lexer": "^2.3.2", "expect-type": "^1.4.0", "magic-string": "^1.2.3", "obug": "^2.1.4", "picomatch": "^4.0.7", "std-env": "^4.2.0", "tinybench": "6.1.4", "tinyexec": "1.3.0", "tinyglobby": "^0.2.17", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "5.0.1", "@vitest/browser-preview": "5.0.1", "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", "@vitest/coverage-istanbul": "5.0.1", "@vitest/coverage-v8": "5.0.1", "@vitest/ui": "5.0.1", "happy-dom": "*", "jsdom": "*", "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "./vitest.mjs" } }, "sha512-iA95lQbKEkvrtTkdAgnWbXfbipWiiWe/hDl2P5tMi6WFwD76G0NxXAGp/M9EOcYupeGJRr6wppMc7CoA41TQjg=="],
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
"which": ["which@2.0.2", "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"which-typed-array": ["which-typed-array@1.1.20", "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.20.tgz", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="],
"why-is-node-running": ["why-is-node-running@2.3.0", "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="],
"word-wrap": ["word-wrap@1.2.5", "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"wrap-ansi": ["wrap-ansi@8.1.0", "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
@@ -1011,6 +1041,14 @@
"@vitest/expect/@vitest/utils": ["@vitest/utils@2.0.5", "https://registry.npmmirror.com/@vitest/utils/-/utils-2.0.5.tgz", { "dependencies": { "@vitest/pretty-format": "2.0.5", "estree-walker": "^3.0.3", "loupe": "^3.1.1", "tinyrainbow": "^1.2.0" } }, "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ=="],
"@vitest/expect/chai": ["chai@5.3.3", "https://registry.npmmirror.com/chai/-/chai-5.3.3.tgz", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
"@vitest/mocker/@vitest/spy": ["@vitest/spy@5.0.1", "https://registry.npmmirror.com/@vitest/spy/-/spy-5.0.1.tgz", {}, "sha512-rbto/mF/SGERxEgYOek7Xm6B9b+y+mVoo+f4b2LymYO8zM1b7uB5nHuhVMTP2hxdzgxvGiZYGxGIaMvL5y180Q=="],
"@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"@vitest/mocker/magic-string": ["magic-string@1.4.1", "https://registry.npmmirror.com/magic-string/-/magic-string-1.4.1.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow=="],
"eslint/ignore": ["ignore@5.3.2", "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"glob/minimatch": ["minimatch@9.0.8", "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.8.tgz", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-reYkDYtj/b19TeqbNZCV4q9t+Yxylf/rYBsLb42SXJatTv4/ylq5lEiAmhA/IToxO7NI2UzNMghHoHuaqDkAjw=="],
@@ -1029,6 +1067,12 @@
"strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
"vitest/magic-string": ["magic-string@1.4.1", "https://registry.npmmirror.com/magic-string/-/magic-string-1.4.1.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.6.0" } }, "sha512-8lyCu36ErXR0J9uaGKlKQoiLZKmtI63YGLE8G2o9jyRPdr4X47LusSOwgOJOzcVtp81fTAAjxR7BwKz682Jhow=="],
"vitest/picomatch": ["picomatch@4.0.7", "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.7.tgz", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="],
"vitest/tinyglobby": ["tinyglobby@0.2.17", "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="],
"wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
"wrap-ansi-cjs/string-width": ["string-width@4.2.3", "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
@@ -1093,8 +1137,12 @@
"@vitest/expect/@vitest/utils/estree-walker": ["estree-walker@3.0.3", "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"@vitest/mocker/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="],
"glob/minimatch/brace-expansion": ["brace-expansion@5.0.3", "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.3.tgz", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA=="],
"vitest/magic-string/@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.6.0", "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", {}, "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw=="],
"wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
+1 -1
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
+56
View File
@@ -8,6 +8,9 @@
"name": "shanghai-residences",
"version": "0.0.0",
"dependencies": {
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"axios": "^1.12.2",
"clsx": "^2.1.1",
"framer-motion": "^12.23.26",
@@ -339,6 +342,59 @@
"node": ">=6.9.0"
}
},
"node_modules/@dnd-kit/accessibility": {
"version": "3.1.1",
"resolved": "https://registry.npmmirror.com/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz",
"integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@dnd-kit/core": {
"version": "6.3.1",
"resolved": "https://registry.npmmirror.com/@dnd-kit/core/-/core-6.3.1.tgz",
"integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==",
"license": "MIT",
"dependencies": {
"@dnd-kit/accessibility": "^3.1.1",
"@dnd-kit/utilities": "^3.2.2",
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@dnd-kit/sortable": {
"version": "10.0.0",
"resolved": "https://registry.npmmirror.com/@dnd-kit/sortable/-/sortable-10.0.0.tgz",
"integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==",
"license": "MIT",
"dependencies": {
"@dnd-kit/utilities": "^3.2.2",
"tslib": "^2.0.0"
},
"peerDependencies": {
"@dnd-kit/core": "^6.3.0",
"react": ">=16.8.0"
}
},
"node_modules/@dnd-kit/utilities": {
"version": "3.2.2",
"resolved": "https://registry.npmmirror.com/@dnd-kit/utilities/-/utilities-3.2.2.tgz",
"integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==",
"license": "MIT",
"dependencies": {
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.3",
"resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+7 -3
View File
@@ -8,13 +8,16 @@
"bun": ">=1.0.0"
},
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"dev": "vite --mode development",
"dev:test": "vite --mode test",
"build": "tsc && vite build --mode production",
"build:test": "tsc && vite build --mode test",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:check": "prettier --check .",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
@@ -62,6 +65,7 @@
"tailwindcss": "^4.1.14",
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.1",
"vite": "^7.1.7"
"vite": "^7.1.7",
"vitest": "^5.0.1"
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4901 -1
View File
File diff suppressed because it is too large. Load diff
+245 -195
View File
@@ -8,145 +8,157 @@
* http://en.wikipedia.org/wiki/Base64
*/
(function(global) {
'use strict';
;(function (global) {
'use strict'
// existing version for noConflict()
var _Base64 = global.Base64;
var version = "2.1.5";
var _Base64 = global.Base64
var version = '2.1.5'
// if node.js, we use Buffer
var buffer;
var buffer
if (typeof module !== 'undefined' && module.exports) {
buffer = require('buffer').Buffer;
buffer = require('buffer').Buffer
}
// constants
var b64chars
= 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var b64tab = function(bin) {
var t = {};
for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i;
return t;
}(b64chars);
var fromCharCode = String.fromCharCode;
var b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
var b64tab = (function (bin) {
var t = {}
for (var i = 0, l = bin.length; i < l; i++) t[bin.charAt(i)] = i
return t
})(b64chars)
var fromCharCode = String.fromCharCode
// encoder stuff
var cb_utob = function (c) {
if (c.length < 2) {
var cc = c.charCodeAt(0);
return cc < 0x80 ? c
: cc < 0x800 ? (fromCharCode(0xc0 | (cc >>> 6))
+ fromCharCode(0x80 | (cc & 0x3f)))
: (fromCharCode(0xe0 | ((cc >>> 12) & 0x0f))
+ fromCharCode(0x80 | ((cc >>> 6) & 0x3f))
+ fromCharCode(0x80 | ( cc & 0x3f)));
var cc = c.charCodeAt(0)
return cc < 0x80
? c
: cc < 0x800
? fromCharCode(0xc0 | (cc >>> 6)) + fromCharCode(0x80 | (cc & 0x3f))
: fromCharCode(0xe0 | ((cc >>> 12) & 0x0f)) +
fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) +
fromCharCode(0x80 | (cc & 0x3f))
} else {
var cc = 0x10000
+ (c.charCodeAt(0) - 0xD800) * 0x400
+ (c.charCodeAt(1) - 0xDC00);
return (fromCharCode(0xf0 | ((cc >>> 18) & 0x07))
+ fromCharCode(0x80 | ((cc >>> 12) & 0x3f))
+ fromCharCode(0x80 | ((cc >>> 6) & 0x3f))
+ fromCharCode(0x80 | ( cc & 0x3f)));
var cc = 0x10000 + (c.charCodeAt(0) - 0xd800) * 0x400 + (c.charCodeAt(1) - 0xdc00)
return (
fromCharCode(0xf0 | ((cc >>> 18) & 0x07)) +
fromCharCode(0x80 | ((cc >>> 12) & 0x3f)) +
fromCharCode(0x80 | ((cc >>> 6) & 0x3f)) +
fromCharCode(0x80 | (cc & 0x3f))
)
}
};
var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g;
}
var re_utob = /[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g
var utob = function (u) {
return u.replace(re_utob, cb_utob);
};
return u.replace(re_utob, cb_utob)
}
var cb_encode = function (ccc) {
var padlen = [0, 2, 1][ccc.length % 3],
ord = ccc.charCodeAt(0) << 16
| ((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8)
| ((ccc.length > 2 ? ccc.charCodeAt(2) : 0)),
ord =
(ccc.charCodeAt(0) << 16) |
((ccc.length > 1 ? ccc.charCodeAt(1) : 0) << 8) |
(ccc.length > 2 ? ccc.charCodeAt(2) : 0),
chars = [
b64chars.charAt(ord >>> 18),
b64chars.charAt((ord >>> 12) & 63),
padlen >= 2 ? '=' : b64chars.charAt((ord >>> 6) & 63),
padlen >= 1 ? '=' : b64chars.charAt(ord & 63)
];
return chars.join('');
};
var btoa = global.btoa ? function(b) {
return global.btoa(b);
} : function(b) {
return b.replace(/[\s\S]{1,3}/g, cb_encode);
};
padlen >= 1 ? '=' : b64chars.charAt(ord & 63),
]
return chars.join('')
}
var btoa = global.btoa
? function (b) {
return global.btoa(b)
}
: function (b) {
return b.replace(/[\s\S]{1,3}/g, cb_encode)
}
var _encode = buffer
? function (u) { return (new buffer(u)).toString('base64') }
: function (u) { return btoa(utob(u)) }
;
? function (u) {
return new buffer(u).toString('base64')
}
: function (u) {
return btoa(utob(u))
}
var encode = function (u, urisafe) {
return !urisafe
? _encode(u)
: _encode(u).replace(/[+\/]/g, function(m0) {
return m0 == '+' ? '-' : '_';
}).replace(/=/g, '');
};
var encodeURI = function(u) { return encode(u, true) };
: _encode(u)
.replace(/[+\/]/g, function (m0) {
return m0 == '+' ? '-' : '_'
})
.replace(/=/g, '')
}
var encodeURI = function (u) {
return encode(u, true)
}
// decoder stuff
var re_btou = new RegExp([
'[\xC0-\xDF][\x80-\xBF]',
'[\xE0-\xEF][\x80-\xBF]{2}',
'[\xF0-\xF7][\x80-\xBF]{3}'
].join('|'), 'g');
var re_btou = new RegExp(
['[\xC0-\xDF][\x80-\xBF]', '[\xE0-\xEF][\x80-\xBF]{2}', '[\xF0-\xF7][\x80-\xBF]{3}'].join('|'),
'g'
)
var cb_btou = function (cccc) {
switch (cccc.length) {
case 4:
var cp = ((0x07 & cccc.charCodeAt(0)) << 18)
| ((0x3f & cccc.charCodeAt(1)) << 12)
| ((0x3f & cccc.charCodeAt(2)) << 6)
| (0x3f & cccc.charCodeAt(3)),
offset = cp - 0x10000;
return (fromCharCode((offset >>> 10) + 0xD800)
+ fromCharCode((offset & 0x3FF) + 0xDC00));
var cp =
((0x07 & cccc.charCodeAt(0)) << 18) |
((0x3f & cccc.charCodeAt(1)) << 12) |
((0x3f & cccc.charCodeAt(2)) << 6) |
(0x3f & cccc.charCodeAt(3)),
offset = cp - 0x10000
return fromCharCode((offset >>> 10) + 0xd800) + fromCharCode((offset & 0x3ff) + 0xdc00)
case 3:
return fromCharCode(
((0x0f & cccc.charCodeAt(0)) << 12)
| ((0x3f & cccc.charCodeAt(1)) << 6)
| (0x3f & cccc.charCodeAt(2))
);
((0x0f & cccc.charCodeAt(0)) << 12) |
((0x3f & cccc.charCodeAt(1)) << 6) |
(0x3f & cccc.charCodeAt(2))
)
default:
return fromCharCode(
((0x1f & cccc.charCodeAt(0)) << 6)
| (0x3f & cccc.charCodeAt(1))
);
return fromCharCode(((0x1f & cccc.charCodeAt(0)) << 6) | (0x3f & cccc.charCodeAt(1)))
}
}
};
var btou = function (b) {
return b.replace(re_btou, cb_btou);
};
return b.replace(re_btou, cb_btou)
}
var cb_decode = function (cccc) {
var len = cccc.length,
padlen = len % 4,
n = (len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0)
| (len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0)
| (len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0)
| (len > 3 ? b64tab[cccc.charAt(3)] : 0),
chars = [
fromCharCode( n >>> 16),
fromCharCode((n >>> 8) & 0xff),
fromCharCode( n & 0xff)
];
chars.length -= [0, 0, 2, 1][padlen];
return chars.join('');
};
var atob = global.atob ? function(a) {
return global.atob(a);
} : function(a){
return a.replace(/[\s\S]{1,4}/g, cb_decode);
};
n =
(len > 0 ? b64tab[cccc.charAt(0)] << 18 : 0) |
(len > 1 ? b64tab[cccc.charAt(1)] << 12 : 0) |
(len > 2 ? b64tab[cccc.charAt(2)] << 6 : 0) |
(len > 3 ? b64tab[cccc.charAt(3)] : 0),
chars = [fromCharCode(n >>> 16), fromCharCode((n >>> 8) & 0xff), fromCharCode(n & 0xff)]
chars.length -= [0, 0, 2, 1][padlen]
return chars.join('')
}
var atob = global.atob
? function (a) {
return global.atob(a)
}
: function (a) {
return a.replace(/[\s\S]{1,4}/g, cb_decode)
}
var _decode = buffer
? function(a) { return (new buffer(a, 'base64')).toString() }
: function(a) { return btou(atob(a)) };
? function (a) {
return new buffer(a, 'base64').toString()
}
: function (a) {
return btou(atob(a))
}
var decode = function (a) {
return _decode(
a.replace(/[-_]/g, function(m0) { return m0 == '-' ? '+' : '/' })
a
.replace(/[-_]/g, function (m0) {
return m0 == '-' ? '+' : '/'
})
.replace(/[^A-Za-z0-9\+\/]/g, '')
);
};
)
}
var noConflict = function () {
var Base64 = global.Base64;
global.Base64 = _Base64;
return Base64;
};
var Base64 = global.Base64
global.Base64 = _Base64
return Base64
}
// export Base64
global.Base64 = {
VERSION: version,
@@ -159,33 +171,42 @@
encodeURI: encodeURI,
btou: btou,
decode: decode,
noConflict: noConflict
};
noConflict: noConflict,
}
// if ES5 is available, make Base64.extendString() available
if (typeof Object.defineProperty === 'function') {
var noEnum = function (v) {
return {value:v,enumerable:false,writable:true,configurable:true};
};
return { value: v, enumerable: false, writable: true, configurable: true }
}
global.Base64.extendString = function () {
Object.defineProperty(
String.prototype, 'fromBase64', noEnum(function () {
String.prototype,
'fromBase64',
noEnum(function () {
return decode(this)
}));
})
)
Object.defineProperty(
String.prototype, 'toBase64', noEnum(function (urisafe) {
String.prototype,
'toBase64',
noEnum(function (urisafe) {
return encode(this, urisafe)
}));
})
)
Object.defineProperty(
String.prototype, 'toBase64URI', noEnum(function () {
String.prototype,
'toBase64URI',
noEnum(function () {
return encode(this, true)
}));
};
})
)
}
}
// that's it!
})(this);
})(this)
if (this['Meteor']) {
Base64 = global.Base64; // for normal export in Meteor.js
Base64 = global.Base64 // for normal export in Meteor.js
}
;(function (root) {
@@ -193,15 +214,15 @@ if (this['Meteor']) {
var videoajax = function (argument) {
// body...
var host = '/';
var host = '/'
this._setHost = function (hoststr) {
host = hoststr;
};
this._getHost = function(){
return host;
host = hoststr
}
this._getHost = function () {
return host
}
}
};
$.extend(videoajax.prototype, {
/**
@@ -209,7 +230,7 @@ if (this['Meteor']) {
* @param hosts {String} default: '/' 主域名字符串
*/
setHost: function (hosts) {
this._setHost(hosts);
this._setHost(hosts)
},
//user_id,course_id,lesson_id,break_point
/**
@@ -222,14 +243,17 @@ if (this['Meteor']) {
*@param callback {Function} 回调方法
*/
quitBreakPoint: function (data, callback) {
var paramfiled = ["user_id","course_id","lesson_id","break_point"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=zzplatform&c=userview&a=addBreakPoint',{
userViewPoint:dataStr
},function(data){
isFunction(callback) ? callback(data.code == 200,data) : '';
var paramfiled = ['user_id', 'course_id', 'lesson_id', 'break_point']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=zzplatform&c=userview&a=addBreakPoint',
{
userViewPoint: dataStr,
},
function (data) {
isFunction(callback) ? callback(data.code == 200, data) : ''
}
);
)
},
/**
* user_id:123,course_id:23,lesson_id:3
@@ -241,15 +265,18 @@ if (this['Meteor']) {
*@param callback {Function} 回调方法
*/
getBreakPoint: function (data, callback) {
var paramfiled = ["user_id","course_id","lesson_id"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=zzplatform&c=userview&a=userbreakpoint',{
userCourseInfo:dataStr
},function(data){
isFunction(callback) ? callback(data.code == 200,data) : '';
var paramfiled = ['user_id', 'course_id', 'lesson_id']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=zzplatform&c=userview&a=userbreakpoint',
{
userCourseInfo: dataStr,
},
function (data) {
isFunction(callback) ? callback(data.code == 200, data) : ''
// isFunction(callback) ? callback(data) : '';
}
);
)
},
/**
*用户答题答案
@@ -262,15 +289,17 @@ if (this['Meteor']) {
*@param callback {Function} 回调方法
*/
getUserAnswer: function (data, callback) {
var paramfiled = ["user_id","lesson_id","course_id","question_order","answer"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=zzplatform&c=userview&a=addUserAnswer',{
userAnswerInfo:dataStr
},function(data){
isFunction(callback) ? callback(data.code == 200,data) : '';
var paramfiled = ['user_id', 'lesson_id', 'course_id', 'question_order', 'answer']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=zzplatform&c=userview&a=addUserAnswer',
{
userAnswerInfo: dataStr,
},
function (data) {
isFunction(callback) ? callback(data.code == 200, data) : ''
}
);
)
},
/**
*播放结束
@@ -280,13 +309,16 @@ if (this['Meteor']) {
*@param callback {Function} 回调方法
*/
finishCourse: function (data, callback) {
var paramfiled = ["order","co","process"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=user&c=learnrecord&a=playcount',data,function(data){
isFunction(callback) ? callback(data.code == 200,data) : '';
var paramfiled = ['order', 'co', 'process']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=user&c=learnrecord&a=playcount',
data,
function (data) {
isFunction(callback) ? callback(data.code == 200, data) : ''
// isFunction(callback) ? callback(data) : '';
}
);
)
},
/**
*课程进度获取
@@ -300,20 +332,23 @@ if (this['Meteor']) {
* status为false时, data保存的是错误提示
*/
getfinishCourse: function (data, callback) {
var paramfiled = ["order","co"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=user&c=learnrecord&a=getuserlessionprocess',data,function(data){
isFunction(callback) ? callback(data) : '';
var paramfiled = ['order', 'co']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=user&c=learnrecord&a=getuserlessionprocess',
data,
function (data) {
isFunction(callback) ? callback(data) : ''
// isFunction(callback) ? callback(data) : '';
}
);
)
},
/**
*解码加密的breakpoint
* @pparam basecode {String} 要解码的字符串
*/
fromBaseCode: function (basecode) {
return getBreakPoint(basecode);
return getBreakPoint(basecode)
},
/**
*通过key值和课时值获取自定的数据
@@ -323,13 +358,17 @@ if (this['Meteor']) {
*@param callback {Function} 回调方法
*/
getUserDefineInfo: function (data, callback) {
var paramfiled = ["lessonId","order"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=zzplatform&c=userview&a=getUserDefineInfo',{
defineDate:dataStr
},function(data){
isFunction(callback) ? callback(data) : '';
});
var paramfiled = ['lessonId', 'order']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=zzplatform&c=userview&a=getUserDefineInfo',
{
defineDate: dataStr,
},
function (data) {
isFunction(callback) ? callback(data) : ''
}
)
},
/**
*通过key值和课时值及用户ID获取自定的数据
@@ -339,13 +378,17 @@ if (this['Meteor']) {
*@param callback {Function} 回调方法
*/
getUserDefineInfoByUser: function (data, callback) {
var paramfiled = ["lessonId","order"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=zzplatform&c=userview&a=getUserDefineInfoByUser',{
defineUserDate:dataStr
},function(data){
isFunction(callback) ? callback(data) : '';
});
var paramfiled = ['lessonId', 'order']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=zzplatform&c=userview&a=getUserDefineInfoByUser',
{
defineUserDate: dataStr,
},
function (data) {
isFunction(callback) ? callback(data) : ''
}
)
},
/**
*增加自定数据
@@ -356,52 +399,59 @@ if (this['Meteor']) {
*@param callback {Function} 回调方法
*/
addUserDefine: function (data, callback) {
var paramfiled = ["lessonId","key","content"];
var dataStr = verfiyData(paramfiled,data);
getDate(this._getHost()+'index.php?m=zzplatform&c=userview&a=addUserDefine',{
addDefineDate:dataStr
},function(data){
isFunction(callback) ? callback(data) : '';
});
var paramfiled = ['lessonId', 'key', 'content']
var dataStr = verfiyData(paramfiled, data)
getDate(
this._getHost() + 'index.php?m=zzplatform&c=userview&a=addUserDefine',
{
addDefineDate: dataStr,
},
function (data) {
isFunction(callback) ? callback(data) : ''
}
)
},
})
var getDate = function (url, data, callback) {
$.get(url,data,function(data){
callback(data);
},'json')
};
$.get(
url,
data,
function (data) {
callback(data)
},
'json'
)
}
var isFunction = function (fn) {
if(typeof fn === 'function')return true;
return false;
if (typeof fn === 'function') return true
return false
}
var verfiyData = function (field, data) {
var arr=[];
console.log(field);
console.log('<br/>');
console.log(data);
var arr = []
console.log(field)
console.log('<br/>')
console.log(data)
for (var i in field) {
if (!data[field[i]]) {
throw new Error('data is error');
throw new Error('data is error')
}
arr.push(field[i]+':'+data[field[i]]);
arr.push(field[i] + ':' + data[field[i]])
}
if (root.JSON) {
return JSON.stringify(data);
return JSON.stringify(data)
} else {
return '{'+arr.join(',')+'}';
return '{' + arr.join(',') + '}'
}
}
var getBreakPoint = function (base64point) {
var newbc = base64point.split('').reverse().join('');
return Base64.fromBase64(newbc);
var newbc = base64point.split('').reverse().join('')
return Base64.fromBase64(newbc)
}
root.videoAjax = function () {
return new videoajax();
return new videoajax()
}
}(this));
})(this)
+45 -17
View File
@@ -1,17 +1,24 @@
import { RouterProvider } from 'react-router-dom'
import { Suspense, useEffect } from 'react'
import { Suspense, useEffect, useState } from 'react'
import router from './router'
import { ScaleProvider, useScale } from './contexts/ScaleContext'
import { RotateHint } from './components/RotateHint'
import { HintHost } from './components'
import { useUserStore } from './store'
import { getUserDefine } from '@/lib/utils/api-common'
import {
usePuzzleStore,
deserializePuzzles,
PUZZLE_LESSON_ID,
PUZZLE_KEY,
} from '@/store/puzzleStore'
SyncLoadingHost,
SyncPromptHost,
showYibanSyncErrorPrompt,
showYibanUnauthenticatedPrompt,
} from '@/components'
import { usePuzzleStore, loadPuzzleStore } from '@/store/puzzleStore'
const applyStoredProgressionDebugMode = async () => {
if (import.meta.env.VITE_DEBUG_ENTRY !== 'true') return false
const debug = await import('@/debug/progressionDebug')
return debug.applyStoredProgressionDebugMode()
}
/**
* 应用内容组件
@@ -19,11 +26,12 @@ import {
*/
function AppContent() {
const { scaleStyle } = useScale()
const [bootstrapDone, setBootstrapDone] = useState(false)
const setCourseInfo = useUserStore((state) => state.setCourseInfo)
useEffect(() => {
const params = new URLSearchParams(window.location.search)
const courseId = params.get('courseid')
const courseId = params.get('courseId') ?? params.get('courseid')
const uid = params.get('uid')
if (courseId || uid) {
@@ -37,17 +45,35 @@ function AppContent() {
useEffect(() => {
;(async () => {
try {
const raw = await getUserDefine(PUZZLE_LESSON_ID, PUZZLE_KEY)
if (!raw) return
const data = JSON.parse(raw) as { puzzles?: Record<string, number[]> }
if (data.puzzles) {
usePuzzleStore.setState((prev) => ({
...prev,
puzzles: deserializePuzzles(data.puzzles),
}))
const status = await loadPuzzleStore({ backend: 'yiban' })
if (status === 'success' || status === 'empty') {
useUserStore.getState().setYibanConnectionStatus('connected')
return
}
useUserStore.getState().setYibanConnectionStatus('disconnected')
await loadPuzzleStore({ backend: 'session' })
if (status === 'unauthenticated') {
showYibanUnauthenticatedPrompt()
} else if (status === 'error') {
showYibanSyncErrorPrompt()
}
} catch (e) {
useUserStore.getState().setYibanConnectionStatus('disconnected')
await loadPuzzleStore({ backend: 'session' })
showYibanSyncErrorPrompt()
console.error('Failed to load puzzle store', e)
} finally {
await applyStoredProgressionDebugMode()
// 仅在测试包启用时应用阶段覆盖(防止 sessionStorage 残留污染生产数据)
if (import.meta.env.VITE_DEBUG_ENTRY === 'true') {
const { applyStoredStageOverrides } = await import('@/debug/progressionDebug')
applyStoredStageOverrides()
}
usePuzzleStore.getState().setLoaded(true)
setBootstrapDone(true)
}
})()
}, [])
@@ -62,9 +88,11 @@ function AppContent() {
}}
>
<Suspense fallback={<div>Loading...</div>}>
<RouterProvider router={router} />
{bootstrapDone && <RouterProvider router={router} />}
<HintHost />
<SyncPromptHost />
</Suspense>
<SyncLoadingHost visible={!bootstrapDone} />
</div>
</div>
<RotateHint />
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 928 KiB

After

Width:  |  Height:  |  Size: 920 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 665 KiB

After

Width:  |  Height:  |  Size: 664 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 422 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1003 KiB

After

Width:  |  Height:  |  Size: 907 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 933 KiB

After

Width:  |  Height:  |  Size: 933 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 KiB

After

Width:  |  Height:  |  Size: 567 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Loaded 100 of 441 files, more files were not shown because too many files have changed in this diff. Show more