add: clipboard history widget
This commit is contained in:
parent
8deb06278f
commit
54b69f0aac
@ -6,8 +6,9 @@ import Hyprland from "gi://AstalHyprland";
|
|||||||
|
|
||||||
import QuickSettings from "widget/quick_settings/quick_settings";
|
import QuickSettings from "widget/quick_settings/quick_settings";
|
||||||
import BluetoothWindow from "widget/quick_settings/bluetooth";
|
import BluetoothWindow from "widget/quick_settings/bluetooth";
|
||||||
|
import Clipboard from "widget/launcher/clipboard";
|
||||||
import Launcher from "widget/launcher/launcher";
|
import Launcher from "widget/launcher/launcher";
|
||||||
import BatteryInfo from "@/widget/bar/battery_info";
|
import BatteryInfo from "widget/bar/battery_info";
|
||||||
import Bar from "widget/bar/bar";
|
import Bar from "widget/bar/bar";
|
||||||
|
|
||||||
const hypr = Hyprland.get_default();
|
const hypr = Hyprland.get_default();
|
||||||
@ -17,7 +18,8 @@ const components = [
|
|||||||
Launcher,
|
Launcher,
|
||||||
QuickSettings,
|
QuickSettings,
|
||||||
BluetoothWindow,
|
BluetoothWindow,
|
||||||
BatteryInfo
|
BatteryInfo,
|
||||||
|
Clipboard
|
||||||
];
|
];
|
||||||
|
|
||||||
const setupBars = async (monitor_id: number) => {
|
const setupBars = async (monitor_id: number) => {
|
||||||
|
@ -22,3 +22,11 @@ export const openOnButton = (event: Gdk.ButtonEvent, keyval: number) => (action:
|
|||||||
|
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const limit = <T>(list: T[], max: number) => list.filter((_, i) => i <= (max - 1));
|
||||||
|
export const skip = <T>(list: T[], items: number) => list.filter((_, i) => {
|
||||||
|
if (items < 0) items = 0;
|
||||||
|
if (items > (list.length - 1)) items = list.length;
|
||||||
|
|
||||||
|
return i > (items - 1);
|
||||||
|
});
|
26
ags/styles/colors.scss
Normal file
26
ags/styles/colors.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// SCSS Variables
|
||||||
|
// Generated by 'wal'
|
||||||
|
$wallpaper: "/home/sadorowo/images/wallpapers/youtube_Sheri_142.jpg";
|
||||||
|
|
||||||
|
// Special
|
||||||
|
$background: #f9f7f5;
|
||||||
|
$foreground: #4C3A47;
|
||||||
|
$cursor: #4C3A47;
|
||||||
|
|
||||||
|
// Colors
|
||||||
|
$color0: #f9f7f5;
|
||||||
|
$color1: #B77E8E;
|
||||||
|
$color2: #6E8EAF;
|
||||||
|
$color3: #138DCF;
|
||||||
|
$color4: #609DD4;
|
||||||
|
$color5: #A196AD;
|
||||||
|
$color6: #D6ABA7;
|
||||||
|
$color7: #4C3A47;
|
||||||
|
$color8: #847974;
|
||||||
|
$color9: #B77E8E;
|
||||||
|
$color10: #6E8EAF;
|
||||||
|
$color11: #138DCF;
|
||||||
|
$color12: #609DD4;
|
||||||
|
$color13: #A196AD;
|
||||||
|
$color14: #D6ABA7;
|
||||||
|
$color15: #4C3A47;
|
50
ags/styles/components/_clipboard.scss
Normal file
50
ags/styles/components/_clipboard.scss
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
@use '../colors' as *;
|
||||||
|
|
||||||
|
.clipboard {
|
||||||
|
entry {
|
||||||
|
background: $color5;
|
||||||
|
color: $color0;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: none;
|
||||||
|
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: 2px solid $color0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
padding: 10px;
|
||||||
|
background: $color5;
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $color0;
|
||||||
|
|
||||||
|
box>.content {
|
||||||
|
color: $color5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
box {
|
||||||
|
.content {
|
||||||
|
color: $color0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-found {
|
||||||
|
background: $color5;
|
||||||
|
color: $color0;
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
image {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
@forward '_bar';
|
@forward '_bar';
|
||||||
@forward '_launcher';
|
@forward '_launcher';
|
||||||
|
@forward '_clipboard';
|
||||||
@forward '_battery_info';
|
@forward '_battery_info';
|
||||||
@forward '_quick_settings';
|
@forward '_quick_settings';
|
96
ags/widget/launcher/clipboard.tsx
Normal file
96
ags/widget/launcher/clipboard.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { Variable, bind, execAsync } from "astal";
|
||||||
|
import { App, Astal, Gtk } from "astal/gtk4";
|
||||||
|
import { hideWindow, limit, skip } from "@lib/utils";
|
||||||
|
|
||||||
|
import { SHELL } from "@/settings.json";
|
||||||
|
|
||||||
|
import Hyprland from "gi://AstalHyprland";
|
||||||
|
|
||||||
|
const hide = () => App.get_window("clipboard")?.hide();
|
||||||
|
type Entry = { id: string, content: string };
|
||||||
|
|
||||||
|
function ClipboardEntry({ id, content }: Entry) {
|
||||||
|
return <button
|
||||||
|
cssClasses={["entry"]}
|
||||||
|
onClicked={() => {
|
||||||
|
hide();
|
||||||
|
execAsync([SHELL, "-c", `cliphist decode ${id} | wl-copy`]);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<box>
|
||||||
|
<label
|
||||||
|
cssClasses={["content"]}
|
||||||
|
xalign={0}
|
||||||
|
label={content}
|
||||||
|
/>
|
||||||
|
</box>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getClipboardHistory() {
|
||||||
|
try {
|
||||||
|
const ids = await execAsync([SHELL, "-c", "cliphist list | awk '{print $1}'"])
|
||||||
|
.then(it => it.split("\n"));
|
||||||
|
const contents = await execAsync([SHELL, "-c", "cliphist list | awk '{$1=\"\"; print}'"])
|
||||||
|
.then(it => it.split("\n"));
|
||||||
|
|
||||||
|
return ids
|
||||||
|
.map((id, index) => ({ id, content: contents[index] }))
|
||||||
|
.filter(({ content }) => content && content.length > 0);
|
||||||
|
} catch {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function Clipboard(_monitor_id: number) {
|
||||||
|
const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor;
|
||||||
|
|
||||||
|
const history = await getClipboardHistory();
|
||||||
|
const hypr = Hyprland.get_default();
|
||||||
|
|
||||||
|
const toSkip = Variable(0);
|
||||||
|
const list = bind(toSkip).as(count => limit(
|
||||||
|
skip(history, count),
|
||||||
|
10
|
||||||
|
));
|
||||||
|
|
||||||
|
const onScroll = (dy: number) => {
|
||||||
|
const value = toSkip.get();
|
||||||
|
|
||||||
|
if (dy < 0) {
|
||||||
|
if ((value - 10) < 0) return;
|
||||||
|
toSkip.set(value - 10)
|
||||||
|
} else {
|
||||||
|
if ((value + 10) > history.length) return;
|
||||||
|
toSkip.set(value + 10)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return <window
|
||||||
|
visible={false}
|
||||||
|
monitor={bind(hypr, "focusedMonitor").as(monitor => monitor.id)}
|
||||||
|
name={"clipboard"}
|
||||||
|
cssClasses={["clipboard"]}
|
||||||
|
keymode={Astal.Keymode.EXCLUSIVE}
|
||||||
|
anchor={TOP | BOTTOM | LEFT | RIGHT}
|
||||||
|
onKeyPressed={hideWindow}
|
||||||
|
application={App}>
|
||||||
|
<box hexpand={false} vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}>
|
||||||
|
<box cssClasses={["clipboard"]} vertical>
|
||||||
|
<box spacing={6} vertical onScroll={(_, __, dy) => onScroll(dy)}>
|
||||||
|
{list.as(list => list.map(({ id, content }) => (
|
||||||
|
<ClipboardEntry id={id} content={content} />
|
||||||
|
)))}
|
||||||
|
</box>
|
||||||
|
<box
|
||||||
|
halign={Gtk.Align.CENTER}
|
||||||
|
cssClasses={["not-found"]}
|
||||||
|
vertical
|
||||||
|
visible={list.as(it => it.length === 0)}>
|
||||||
|
<image iconName="system-search-symbolic" />
|
||||||
|
<label label="No match found" />
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
|
</window>
|
||||||
|
}
|
@ -44,7 +44,7 @@ bindd = $mainMod + Shift, 8, Move to workspace 8, movetoworkspace
|
|||||||
bindd = $mainMod + Shift, 9, Move to workspace 9, movetoworkspace, 9
|
bindd = $mainMod + Shift, 9, Move to workspace 9, movetoworkspace, 9
|
||||||
bindd = $mainMod + Shift, 0, Move to workspace 10, movetoworkspace, 10
|
bindd = $mainMod + Shift, 0, Move to workspace 10, movetoworkspace, 10
|
||||||
|
|
||||||
bindd = $mainMod, Tab, Toggle workspace overview, overview:toggle
|
bindd = $mainMod, Tab, Toggle workspace overview, overview:toggle
|
||||||
bindd = $mainMod, S, Toggle special workspace, togglespecialworkspace, magic
|
bindd = $mainMod, S, Toggle special workspace, togglespecialworkspace, magic
|
||||||
bindd = $mainMod + Shift, S, Move to special workspace, movetoworkspace, special:magic
|
bindd = $mainMod + Shift, S, Move to special workspace, movetoworkspace, special:magic
|
||||||
|
|
||||||
@ -57,7 +57,8 @@ bindmd = $mainMod, mouse:273, Resize window, resizewindow
|
|||||||
bindd = , PRINT, Take partial screenshot, exec, hyprshot -zm region -o $screenshot_dir
|
bindd = , PRINT, Take partial screenshot, exec, hyprshot -zm region -o $screenshot_dir
|
||||||
bindd = $mainMod, PRINT, Take fullscreen screenshot, exec, hyprshot -zm output -o $screenshot_dir
|
bindd = $mainMod, PRINT, Take fullscreen screenshot, exec, hyprshot -zm output -o $screenshot_dir
|
||||||
|
|
||||||
bindd = $mainMod, Space, Open app launcher, exec, ags toggle launcher
|
bindd = $mainMod, Space, Open app launcher, exec, ags toggle launcher
|
||||||
|
bindd = $mainMod, V, Open clipboard history, exec, ags toggle clipboard
|
||||||
bindd = $mainMod + Shift, T, Toggle bar, exec, ~/.config/hypr/scripts/toggle-bar-visibility.sh
|
bindd = $mainMod + Shift, T, Toggle bar, exec, ~/.config/hypr/scripts/toggle-bar-visibility.sh
|
||||||
bindd = $mainMod, SUPER_L, Open quick settings, exec, ags toggle quick_settings
|
bindd = $mainMod, SUPER_L, Open quick settings, exec, ags toggle quick_settings
|
||||||
bindd = Ctrl+Shift, R, Reload AGS, exec, ags quit; ags run --gtk4 &
|
bindd = Ctrl+Shift, R, Reload AGS, exec, ags quit; ags run --gtk4 &
|
||||||
|
Loading…
x
Reference in New Issue
Block a user