Skip to content

Commit 8668158

Browse files
committed
feat(clipboard): add clipboard widget with validation schema
- Add clipboard widget integrating with Windows Clipboard History - Add pydantic validation schema for clipboard configuration - Support text and image clipboard history with search - Add popup menu with blur, rounded corners, and customization - Add documentation for widget options and styling
1 parent e298aaf commit 8668158

3 files changed

Lines changed: 429 additions & 257 deletions

File tree

docs/widgets/(Widget)-Clipboard.md

Lines changed: 47 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,57 @@
22

33
A lightweight clipboard manager for YASB that integrates directly with the native Windows Clipboard History (Win + V). This widget provides real-time access to your system's clip buffer without the need for heavy local storage or complex background monitoring.
44

5-
**Note for Users:** This widget requires the following Python packages to interact with Windows APIs:
6-
`pip install winrt-Windows.ApplicationModel.DataTransfer winrt-Windows.Foundation`
7-
85
## Features
96
- **Native Windows Sync**: Syncs in real-time with your official Windows Clipboard History.
107
- **Search**: Built-in real-time search bar to filter through your text-based history.
118
- **History**: View your Clipboard history and delete a single history item or your entire history.
129
- **Image Support**: Full support for previewing and re-copying images directly from the history list.
1310
- **Long text preview**: Hover over a copied long piece of text to display complete text preview.
11+
- **Feedback**: Visual feedback when content is copied.
1412

1513
## Options
1614

1715
| Option | Type | Default | Description |
1816
| :--- | :--- | :--- | :--- |
1917
| `type` | `string` | `yasb.clipboard.ClipboardWidget` | The widget class identifier. |
20-
| `label` | `string` | `<span>\udb80\udd4d</span> {clipboard}` | Primary label format. Supports `{clipboard}` token. |
21-
| `label_alt` | `string` | `{clipboard}` | Alternative label format (swapped on right-click). |
22-
| `max_length` | `integer` | `30` | Max characters to display in the bar before truncation. |
18+
| `label` | `string` | `<span>\udb80\udd4d</span>` | Primary label format. |
19+
| `label_alt` | `string` | `CLIPBOARD` | Alternative label format (swapped on right-click). |
2320
| `max_history` | `integer` | `50` | Maximum number of history items to fetch from Windows. |
2421
| `class_name` | `string` | `""` | Additional CSS class for the widget container. |
25-
| `menu` | `dict` | (See Schema) | Configuration for the popup menu (blur, corners, alignment). |
26-
| `icons` | `dict` | (See Schema) | Custom icons for clipboard, clear, and search. |
22+
| `copied_feedback` | `string` | `\uf00c` | Feedback message when copying. |
23+
| `menu` | `dict` | (See Schema) | Configuration for the popup menu. |
24+
| `icons` | `dict` | (See Schema) | Custom icons for clipboard actions. |
25+
| `animation` | `dict` | (See Schema) | Animation configuration for label toggling. |
26+
| `label_shadow` | `dict` | (See Schema) | Shadow configuration for the label. |
27+
| `container_shadow` | `dict` | (See Schema) | Shadow configuration for the widget container. |
2728

2829
## Icons Configuration Defaults
2930

3031
| Key | Default | Description |
3132
| :--- | :--- | :--- |
32-
| `clipboard` | `\udb80\udd4d` | Main widget icon used on the bar and in the "Copied!" flash. |
33-
| `clear` | `\uf1f8` | Global clear icon (Clears the system's unpinned history). |
34-
| `search_clear` | `\uf00d` | Icon used for general UI elements. |
33+
| `clear_icon` | `\uf1f8` | Clear all history icon. |
34+
| `delete_icon` | `\uf1f8` | Delete item icon. |
35+
36+
## Menu Configuration Defaults
37+
38+
| Key | Default | Description |
39+
| :--- | :--- | :--- |
40+
| `blur` | `true` | Enable blur effect on popup. |
41+
| `round_corners` | `true` | Enable rounded corners on popup. |
42+
| `round_corners_type` | `"normal"` | Corner radius style (`"normal"` or `"small"`). |
43+
| `border_color` | `"System"` | Border color for the popup. |
44+
| `alignment` | `"right"` | Horizontal alignment relative to widget (`"left"`, `"right"`, `"center"`). |
45+
| `direction` | `"down"` | Vertical direction for popup (`"up"` or `"down"`). |
46+
| `offset_top` | `6` | Top offset in pixels. |
47+
| `offset_left` | `0` | Left offset in pixels. |
48+
| `max_item_length` | `50` | Max characters to display for text items (10-200). |
3549

3650
## Callbacks
3751

3852
| Function | Description |
3953
| :--- | :--- |
4054
| `toggle_menu` | Opens/closes the clipboard history popup (Scheduled asynchronously). |
4155
| `toggle_label` | Switches display between `label` and `label_alt` on the bar. |
42-
| `do_nothing` | No action. |
4356

4457
---
4558

@@ -50,17 +63,19 @@ A lightweight clipboard manager for YASB that integrates directly with the nativ
5063
clipboard:
5164
type: "yasb.clipboard.ClipboardWidget"
5265
options:
53-
label: "<span>\udb80\udd4d</span> {clipboard}"
54-
label_alt: "<span>CLIPBOARD:</span> {clipboard}"
55-
max_length: 25
66+
label: "<span>\uf0ea</span>"
67+
label_alt: "<span>CLIPBOARD</span>"
68+
icons:
69+
delete_icon: "\uf1f8"
70+
clear_icon: "\uf1f8"
71+
copied_feedback: "\uf00c"
5672
menu:
5773
blur: false
5874
round_corners: false
5975
alignment: "right"
6076
direction: "down"
6177
callbacks:
6278
on_left: "toggle_menu"
63-
on_middle: "do_nothing"
6479
on_right: "toggle_label"
6580
```
6681
@@ -69,19 +84,19 @@ A lightweight clipboard manager for YASB that integrates directly with the nativ
6984
### Example CSS
7085
7186
```css
87+
/* Widget on the bar */
7288
.clipboard-widget {
7389
font-family: "JetBrainsMono Nerd Font", "Segoe UI Variable";
7490
}
75-
/* Widget on the bar */
91+
7692
.clipboard-widget .label {
77-
padding: 0 6px;
78-
color: var(--mauve);
93+
color: #89b4fa;
7994
}
8095

8196
/* Main Popup Container */
8297
.clipboard-menu {
83-
background-color: var(--bg-color1);
84-
border: 1px solid var(--bg-color2);
98+
background-color: #1e1e2e;
99+
border: 1px solid #11111b;
85100
border-radius: 10px;
86101
padding: 8px;
87102
}
@@ -93,18 +108,18 @@ A lightweight clipboard manager for YASB that integrates directly with the nativ
93108
border-radius: 6px;
94109
padding: 6px 10px;
95110
margin-bottom: 6px;
96-
color: var(--text1);
111+
color: #cdd6f4;
97112
font-size: 13px;
98113
}
99114

100115
.clipboard-menu .search-input:focus {
101-
border: 1px solid var(--blue);
116+
border: 1px solid #89b4fa;
102117
}
103118

104119
/* Global Clear Button */
105120
.clipboard-menu .clear-button {
106121
background-color: rgba(243, 139, 168, 0.1);
107-
color: var(--red);
122+
color: #f38ba8;
108123
border-radius: 6px;
109124
padding: 5px;
110125
margin-bottom: 8px;
@@ -116,48 +131,28 @@ A lightweight clipboard manager for YASB that integrates directly with the nativ
116131
background-color: rgba(243, 139, 168, 0.2);
117132
}
118133

119-
/* Scroll Area Styling */
120-
.clipboard-menu .scroll-area {
121-
background: transparent;
122-
border: none;
123-
}
124-
125-
/* Individual Clipboard Items (Buttons) */
134+
/* Clipboard Items */
126135
.clipboard-menu .clipboard-item {
127136
background-color: rgba(255, 255, 255, 0.03);
128137
border: 1px solid transparent;
129138
border-radius: 5px;
130139
padding: 8px;
131140
margin-bottom: 4px;
132141
text-align: left;
133-
color: var(--text1);
142+
color: #cdd6f4;
134143
font-size: 12px;
135144
}
136145

137146
.clipboard-menu .clipboard-item:hover {
138147
background-color: rgba(255, 255, 255, 0.08);
139-
border: 1px solid var(--bg-color2);
140-
}
141-
142-
/* Image Item Specifics (if needed) */
143-
.clipboard-menu .clipboard-item [icon] {
144-
margin-right: 8px;
145-
}
146-
147-
/* Scrollbar Styling (Optional but looks better) */
148-
QScrollBar:vertical {
149-
border: none;
150-
background: transparent;
151-
width: 4px;
148+
border: 1px solid #11111b;
152149
}
153150

154-
QScrollBar::handle:vertical {
155-
background: var(--bg-color2);
156-
border-radius: 2px;
151+
/* Delete Button */
152+
.clipboard-menu .delete-button {
153+
background: rgba(243, 139, 168, 0.1);
157154
}
158-
159-
/* Styling for the temporary 'Copied!' flash */
160-
.clipboard-widget .label {
161-
transition: all 0.2s ease-in-out;
155+
.clipboard-menu .delete-button:hover {
156+
background-color: rgba(243, 139, 168, 0.2);
162157
}
163158
```
Lines changed: 55 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,56 @@
1-
DEFAULTS = {
2-
"label": "<span>\udb80\udd4d</span> {clipboard}",
3-
"label_alt": "{clipboard}",
4-
"class_name": "",
5-
"max_length": 30,
6-
"max_history": 50,
7-
"container_padding": {"top": 0, "left": 0, "bottom": 0, "right": 0},
8-
"animation": {"enabled": True, "type": "fadeInOut", "duration": 200},
9-
"menu": {
10-
"blur": True,
11-
"round_corners": True,
12-
"round_corners_type": "normal",
13-
"border_color": "System",
14-
"alignment": "right",
15-
"direction": "down",
16-
"offset_top": 6,
17-
"offset_left": 0,
18-
"max_item_length": 50,
19-
},
20-
"icons": {
21-
"clipboard": "\udb80\udd4d",
22-
"clear": "\uf1f8",
23-
"search_clear": "\uf00d",
24-
},
25-
"callbacks": {"on_left": "toggle_menu", "on_middle": "do_nothing", "on_right": "toggle_label"},
26-
}
1+
from typing import Literal
272

28-
VALIDATION_SCHEMA = {
29-
"label": {"type": "string", "default": DEFAULTS["label"]},
30-
"label_alt": {"type": "string", "default": DEFAULTS["label_alt"]},
31-
"class_name": {"type": "string", "required": False, "default": DEFAULTS["class_name"]},
32-
"max_length": {"type": "integer", "required": False, "default": DEFAULTS["max_length"], "min": 5, "max": 100},
33-
"max_history": {"type": "integer", "required": False, "default": DEFAULTS["max_history"], "min": 10, "max": 500},
34-
"container_padding": {
35-
"type": "dict",
36-
"required": False,
37-
"schema": {
38-
"top": {"type": "integer", "default": DEFAULTS["container_padding"]["top"]},
39-
"left": {"type": "integer", "default": DEFAULTS["container_padding"]["left"]},
40-
"bottom": {"type": "integer", "default": DEFAULTS["container_padding"]["bottom"]},
41-
"right": {"type": "integer", "default": DEFAULTS["container_padding"]["right"]},
42-
},
43-
"default": DEFAULTS["container_padding"],
44-
},
45-
"animation": {
46-
"type": "dict",
47-
"required": False,
48-
"schema": {
49-
"enabled": {"type": "boolean", "default": DEFAULTS["animation"]["enabled"]},
50-
"type": {"type": "string", "default": DEFAULTS["animation"]["type"]},
51-
"duration": {"type": "integer", "default": DEFAULTS["animation"]["duration"]},
52-
},
53-
"default": DEFAULTS["animation"],
54-
},
55-
"menu": {
56-
"type": "dict",
57-
"required": False,
58-
"schema": {
59-
"blur": {"type": "boolean", "default": DEFAULTS["menu"]["blur"]},
60-
"round_corners": {"type": "boolean", "default": DEFAULTS["menu"]["round_corners"]},
61-
"round_corners_type": {
62-
"type": "string",
63-
"default": DEFAULTS["menu"]["round_corners_type"],
64-
"allowed": ["normal", "small"],
65-
},
66-
"border_color": {"type": "string", "default": DEFAULTS["menu"]["border_color"]},
67-
"alignment": {"type": "string", "default": DEFAULTS["menu"]["alignment"]},
68-
"direction": {"type": "string", "default": DEFAULTS["menu"]["direction"]},
69-
"offset_top": {"type": "integer", "default": DEFAULTS["menu"]["offset_top"]},
70-
"offset_left": {"type": "integer", "default": DEFAULTS["menu"]["offset_left"]},
71-
"max_item_length": {"type": "integer", "default": DEFAULTS["menu"]["max_item_length"]},
72-
},
73-
"default": DEFAULTS["menu"],
74-
},
75-
"icons": {
76-
"type": "dict",
77-
"required": False,
78-
"schema": {
79-
"clipboard": {"type": "string", "default": DEFAULTS["icons"]["clipboard"]},
80-
"clear": {"type": "string", "default": DEFAULTS["icons"]["clear"]},
81-
"search_clear": {"type": "string", "default": DEFAULTS["icons"]["search_clear"]},
82-
},
83-
"default": DEFAULTS["icons"],
84-
},
85-
"callbacks": {
86-
"type": "dict",
87-
"required": False,
88-
"schema": {
89-
"on_left": {"type": "string", "default": DEFAULTS["callbacks"]["on_left"]},
90-
"on_middle": {"type": "string", "default": DEFAULTS["callbacks"]["on_middle"]},
91-
"on_right": {"type": "string", "default": DEFAULTS["callbacks"]["on_right"]},
92-
},
93-
"default": DEFAULTS["callbacks"],
94-
},
95-
}
3+
from pydantic import Field
4+
5+
from core.validation.widgets.base_model import (
6+
AnimationConfig,
7+
CallbacksConfig,
8+
CustomBaseModel,
9+
KeybindingConfig,
10+
ShadowConfig,
11+
)
12+
13+
14+
class ClipboardMenuConfig(CustomBaseModel):
15+
"""Configuration for the clipboard popup menu."""
16+
17+
blur: bool = True
18+
round_corners: bool = True
19+
round_corners_type: Literal["normal", "small"] = "normal"
20+
border_color: str = "System"
21+
alignment: Literal["left", "right", "center"] = "right"
22+
direction: Literal["up", "down"] = "down"
23+
offset_top: int = 6
24+
offset_left: int = 0
25+
max_item_length: int = Field(default=50, ge=10, le=200)
26+
27+
28+
class ClipboardIconsConfig(CustomBaseModel):
29+
"""Configuration for clipboard widget icons."""
30+
31+
clear_icon: str = "\uf1f8"
32+
delete_icon: str = "\uf1f8"
33+
34+
35+
class ClipboardCallbacksConfig(CallbacksConfig):
36+
"""Callbacks configuration with clipboard-specific defaults."""
37+
38+
on_left: str = "toggle_menu"
39+
on_right: str = "toggle_label"
40+
41+
42+
class ClipboardConfig(CustomBaseModel):
43+
"""Main configuration model for the Clipboard widget."""
44+
45+
label: str = "<span>\udb80\udd4d</span>"
46+
label_alt: str = "CLIPBOARD"
47+
class_name: str = ""
48+
copied_feedback: str = "\uf00c"
49+
max_history: int = Field(default=50, ge=10, le=500)
50+
menu: ClipboardMenuConfig = ClipboardMenuConfig()
51+
icons: ClipboardIconsConfig = ClipboardIconsConfig()
52+
animation: AnimationConfig = AnimationConfig()
53+
label_shadow: ShadowConfig = ShadowConfig()
54+
container_shadow: ShadowConfig = ShadowConfig()
55+
keybindings: list[KeybindingConfig] = []
56+
callbacks: ClipboardCallbacksConfig = ClipboardCallbacksConfig()

0 commit comments

Comments
 (0)