Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,71 @@

## [Unreleased]

### Added

- Four theme families (`ember`, `graphite`, `brick`, `rose`) with light/dark/system modes, driven
entirely by semantic CSS design tokens. A contrast gate (`pnpm check:contrast`) verifies 464 color
pairs across all 8 family/mode combinations against WCAG 4.5:1 / 3:1, and a token gate
(`pnpm check:tokens`) fails the build on palette names or color literals in components.
- Ten independently configurable global shortcuts (`toggle_window` plus `quick_paste_1..9`) stored in
a new `shortcut_bindings` table. Each action can be enabled, disabled, and re-recorded on its own;
disabling slots does not renumber the remaining quick-paste indices.
- Transactional shortcut registration: the full set is validated up front, only changed registrations
are touched, and any failure rolls back both the new registrations and the previously unregistered
ones. `unregister_all` is deliberately not used, so other applications' global shortcuts are never
collaterally released.
- Window state persistence in a new `window_state` table, recorded in DIP alongside `scale_factor`
and `monitor_id` so size and position restore correctly across displays with different scaling.
- Image fidelity storage: `binary_blobs` (SHA-256-deduplicated bytes) and
`clipboard_item_representations` with `source` / `canonical` / `thumbnail` roles. The
OS-provided original encoding is preserved when available, a canonical PNG covers bitmap-only
sources, and thumbnails are physically isolated preview copies that never affect paste or export.
- On-demand image media IPC, so the clipboard list no longer transfers full-resolution originals.
- Settings redesign with a navigation rail and General/Appearance/Shortcuts/Behavior/Data/About
panels,
including data capacity reporting, diagnostics, explicit save/cancel with unsaved-change guarding,
and per-field error states.
- `docs/UPGRADE_V8.md` covering the v7→v8 upgrade, automatic backup and rollback paths, the
migration log reference, old/new config keys, image capacity policy, Windows shortcut conflicts and
`Win`-key limits, gate results, and known limitations.

### Changed

- Database schema is now `db_version = 8`. The v7→v8 migration runs in a single transaction, is
idempotent via `INSERT OR IGNORE`, and is preceded by an automatic
`klip.db.pre-v8-<millis>.bak` backup that is integrity-checked before use. If migration fails, the
backup is restored automatically and the failure names the backup path.
- Legacy image data URLs migrate to `canonical` blob representations with generated thumbnails.
Images that cannot be parsed as PNG are left untouched and logged for diagnostics rather than
rewritten; images over 128 MiB are skipped without blocking migration.
- Default window size is `680 x 720` DIP (was `560 x 760`), minimum `360 x 480`. Size is adjusted by
dragging the window and remembered automatically; the settings page now reports default, minimum,
and current size as read-only information instead of pixel inputs. Upgrades that still had the old
`560 x 760` default move to `680 x 720`; any user-modified size is preserved as-is.
- Quick-paste shortcuts are seeded enabled on upgrade (preserving existing behavior) and disabled on
fresh installs.
- Window hiding is split into independent `hide_on_focus_loss` and `hide_after_paste` settings, both
defaulting to the previous combined behavior.
- The per-image `5 MiB` gate is removed. Single images are bounded by 40,000,000 pixels and 160 MiB
RGBA, so 1920x1080, 4K, and common 8K screenshots are no longer silently skipped. Total image
storage is bounded by the new `image_budget_bytes` setting (default 2 GiB), which evicts the oldest
unfavorited images first and never evicts favorites.
- `pnpm verify` and `pnpm release:verify` now run the contrast, i18n, and token gates.

### Fixed

- Missing or corrupted image blobs now return locatable integrity errors instead of hiding the
affected clipboard entry.

### Known Limitations

- Installer real-machine verification (tray, autostart, window restore, clipboard formats on a clean
Windows install) has not been performed for this work. See `docs/UPGRADE_V8.md` section 10.
- "Original preservation" means preserving the representation the OS actually provided; bytes that
were never in the clipboard cannot be recovered afterwards.
- `Win` combinations can be recorded, but Windows may claim new combinations after system updates.
Klip defers to the actual registration result rather than promising a stable allowlist.

## [0.2.0] - 2026-08-10

### Added
Expand Down
245 changes: 242 additions & 3 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,148 @@ void

---

### 1.5 系统操作
### 1.5 快捷键绑定、窗口状态与图片存储

`db_version = 8` 引入的命令。快捷键不再通过 `set_config` 写 `hotkey_*` 键,改用下面的
`set_shortcut_bindings`;`hotkey_toggle_window` 与 `hotkey_quick_paste_prefix` 仅作为
迁移来源保留。

#### `get_shortcut_bindings`

读取十个动作的绑定。

**参数**: 无

**返回**:
```typescript
ShortcutBinding[] // 见 3.8
```

---

#### `set_shortcut_bindings`

整体替换绑定,并同步重新注册全局快捷键。

**参数**:
```typescript
{
bindings: ShortcutBinding[];
}
```

**返回**:
```typescript
void
```

**事务语义**:

1. 先校验并归一化组合键,非法组合直接报错,不产生任何副作用
2. 再向系统注册新绑定;任一条注册失败则整体失败,已注册的部分回滚
3. 注册成功后才写库;写库失败会把运行时注册回滚到旧绑定
4. 全部成功后广播 `shortcut-registration-changed`

失败时错误信息里会带上组合键,便于前端定位到具体行。若回滚本身也失败,错误信息会追加
`runtime rollback failed: ...`——此时运行时注册与数据库可能不一致,需要重启修复。

> 实现不使用 `unregister_all`:全量注销会在失败路径上连带丢掉其他仍然有效的快捷键。

---

#### `get_window_state`

读取窗口的保存状态。

**参数**:
```typescript
{
windowLabel?: string; // 默认 "main"
}
```

**返回**:
```typescript
WindowState | null // 见 3.8;从未保存过时为 null
```

---

#### `reset_window_state`

把窗口恢复为默认尺寸并在当前活动显示器居中,同时写回新状态。

**参数**:
```typescript
{
windowLabel?: string; // 默认 "main"
}
```

**返回**:
```typescript
WindowState // 重置后的状态
```

---

#### `get_storage_usage`

读取图片存储用量与预算。

**参数**: 无

**返回**:
```typescript
StorageUsage // 见 3.8
```

`budgetBytes` 为 `null` 表示用户选择了不限制。

---

#### `get_image_representation`

取出图片可粘贴的原始字节,优先 `source`,其次 `canonical`。

**参数**:
```typescript
{
itemId: number;
format?: string; // 指定格式名,如 "png";省略则按优先级选取
}
```

**返回**:
```typescript
number[] // 字节数组
```

**错误**: 条目不存在、不是图片,或没有任何可用表示时返回错误。

---

#### `get_image_thumbnail`

取出列表预览用的缩略图字节。

**参数**:
```typescript
{
itemId: number;
}
```

**返回**:
```typescript
number[] // PNG 字节数组
```

缩略图与 `source` / `canonical` 物理隔离,**不可**用于复制或导出。

---

### 1.6 系统操作

#### `toggle_window`

Expand Down Expand Up @@ -889,6 +1030,51 @@ ClipboardItem

---

### 2.3 快捷键、窗口与图片存储事件

#### `shortcut-registration-changed`

`set_shortcut_bindings` 成功注册并落库后触发,用于让其他窗口同步显示。

**数据**:
```typescript
ShortcutBinding[]
```

---

#### `window-state-changed`

窗口尺寸或位置稳定后触发(拖动过程中不会连续发事件)。

**数据**:
```typescript
WindowState
```

---

#### `image-storage-warning`

图片存储触及边界时触发。

**数据**:
```typescript
{
code: 'capacity_cleanup' // 已清理最旧的未收藏图片以回到预算内
| 'capacity_exceeded' // 超出预算且无可清理项
| 'representation_too_large' // 单张图片超过单图上限,未保存
| 'capture_failed'; // 图片采集失败
message: string;
itemIds: number[]; // 受影响的条目;无关联时为空数组
}
```

Klip 不会为了腾出空间压缩图片,收藏的条目也不参与清理——`capacity_cleanup` 只删除
最旧的未收藏图片。

---

## 3. 数据类型

### 3.1 ClipboardItem
Expand Down Expand Up @@ -959,8 +1145,12 @@ interface AppConfig {
hotkey_quick_paste_prefix: string;
auto_start: boolean; // 启动时会与系统层面的自启状态同步
close_to_tray: boolean;
window_width: number;
window_height: number;
hide_on_focus_loss?: boolean;
hide_after_paste?: boolean;
show_window_on_startup?: boolean;
always_on_top?: boolean;
window_width: number; // DIP,最小 360
window_height: number; // DIP,最小 480
search_debounce_ms: number;
language: string;
sensitive_capture_policy: 'flag' | 'skip';
Expand All @@ -973,9 +1163,18 @@ interface AppConfig {
encryption_status: string;
sync_folder: string;
plugin_folder: string;
theme_family?: ThemeFamily;
theme_mode?: ThemeMode;
image_budget_bytes?: number; // -1 表示不限制
}

type ThemeFamily = 'ember' | 'graphite' | 'brick' | 'rose';
type ThemeMode = 'light' | 'dark' | 'system';
```

> `hotkey_toggle_window` 与 `hotkey_quick_paste_prefix` 自 v8 起只作为迁移来源保留,
> 运行时的快捷键状态以 `shortcut_bindings` 为准,通过 1.5 的命令读写。

### 3.4 Snippet / SourceRule / AdvancedSearchQuery

```typescript
Expand Down Expand Up @@ -1066,6 +1265,46 @@ interface SystemInfo {
}
```

### 3.8 ShortcutBinding / WindowState / StorageUsage

`db_version = 8` 引入。字段以 camelCase 序列化。

```typescript
type ShortcutActionId =
| 'toggle_window'
| 'quick_paste_1' | 'quick_paste_2' | 'quick_paste_3'
| 'quick_paste_4' | 'quick_paste_5' | 'quick_paste_6'
| 'quick_paste_7' | 'quick_paste_8' | 'quick_paste_9';

interface ShortcutBinding {
actionId: ShortcutActionId;
enabled: boolean;
/** 禁用时可为 null;enabled 为 true 时必须有值 */
accelerator: string | null;
updatedAt: number;
}

interface WindowState {
windowLabel: string;
widthDip: number;
heightDip: number;
/** null 表示在当前活动显示器居中 */
x: number | null;
y: number | null;
monitorId: string | null;
scaleFactor: number | null;
updatedAt: number;
}

interface StorageUsage {
usedBytes: number;
/** null 表示不限制 */
budgetBytes: number | null;
imageBytes: number;
blobCount: number;
}
```

---

## 4. 前端 API 封装
Expand Down
Loading
Loading