From 54b69f0aac58f5f780cdfbc2a0c64a8f4eae11c6 Mon Sep 17 00:00:00 2001 From: Franek Date: Sat, 26 Apr 2025 21:48:58 +0200 Subject: [PATCH] add: clipboard history widget --- ags/app.ts | 6 +- ags/lib/utils.ts | 14 +++- ags/styles/colors.scss | 26 ++++++++ ags/styles/components/_clipboard.scss | 50 ++++++++++++++ ags/styles/components/_index.scss | 1 + ags/widget/launcher/clipboard.tsx | 96 +++++++++++++++++++++++++++ hypr/config/binds.conf | 5 +- 7 files changed, 191 insertions(+), 7 deletions(-) create mode 100644 ags/styles/colors.scss create mode 100644 ags/styles/components/_clipboard.scss create mode 100644 ags/widget/launcher/clipboard.tsx diff --git a/ags/app.ts b/ags/app.ts index 40c1804..1044852 100644 --- a/ags/app.ts +++ b/ags/app.ts @@ -6,8 +6,9 @@ import Hyprland from "gi://AstalHyprland"; import QuickSettings from "widget/quick_settings/quick_settings"; import BluetoothWindow from "widget/quick_settings/bluetooth"; +import Clipboard from "widget/launcher/clipboard"; 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"; const hypr = Hyprland.get_default(); @@ -17,7 +18,8 @@ const components = [ Launcher, QuickSettings, BluetoothWindow, - BatteryInfo + BatteryInfo, + Clipboard ]; const setupBars = async (monitor_id: number) => { diff --git a/ags/lib/utils.ts b/ags/lib/utils.ts index 1c58da2..96eab72 100644 --- a/ags/lib/utils.ts +++ b/ags/lib/utils.ts @@ -9,8 +9,8 @@ export const hideWindow = (self: Gtk.Window, keyval: number) => { const keys = ["quick_settings"].includes(self.name) ? [Gdk.KEY_Escape] : [ - Gdk.KEY_Escape, - Gdk.KEY_Super_L, + Gdk.KEY_Escape, + Gdk.KEY_Super_L, Gdk.KEY_Super_R ]; @@ -21,4 +21,12 @@ export const openOnButton = (event: Gdk.ButtonEvent, keyval: number) => (action: if (event.get_button() !== keyval) return; action(); -} \ No newline at end of file +} + +export const limit = (list: T[], max: number) => list.filter((_, i) => i <= (max - 1)); +export const skip = (list: T[], items: number) => list.filter((_, i) => { + if (items < 0) items = 0; + if (items > (list.length - 1)) items = list.length; + + return i > (items - 1); +}); \ No newline at end of file diff --git a/ags/styles/colors.scss b/ags/styles/colors.scss new file mode 100644 index 0000000..87f2e7b --- /dev/null +++ b/ags/styles/colors.scss @@ -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; diff --git a/ags/styles/components/_clipboard.scss b/ags/styles/components/_clipboard.scss new file mode 100644 index 0000000..ff67294 --- /dev/null +++ b/ags/styles/components/_clipboard.scss @@ -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; + } + } +} diff --git a/ags/styles/components/_index.scss b/ags/styles/components/_index.scss index 8ef99aa..fd8d0e6 100644 --- a/ags/styles/components/_index.scss +++ b/ags/styles/components/_index.scss @@ -1,4 +1,5 @@ @forward '_bar'; @forward '_launcher'; +@forward '_clipboard'; @forward '_battery_info'; @forward '_quick_settings'; \ No newline at end of file diff --git a/ags/widget/launcher/clipboard.tsx b/ags/widget/launcher/clipboard.tsx new file mode 100644 index 0000000..791bbc7 --- /dev/null +++ b/ags/widget/launcher/clipboard.tsx @@ -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 +} + +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 monitor.id)} + name={"clipboard"} + cssClasses={["clipboard"]} + keymode={Astal.Keymode.EXCLUSIVE} + anchor={TOP | BOTTOM | LEFT | RIGHT} + onKeyPressed={hideWindow} + application={App}> + + + onScroll(dy)}> + {list.as(list => list.map(({ id, content }) => ( + + )))} + + it.length === 0)}> + + + + + +} diff --git a/hypr/config/binds.conf b/hypr/config/binds.conf index 00c8b2d..d7fc6e7 100644 --- a/hypr/config/binds.conf +++ b/hypr/config/binds.conf @@ -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, 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 + 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 = $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, SUPER_L, Open quick settings, exec, ags toggle quick_settings bindd = Ctrl+Shift, R, Reload AGS, exec, ags quit; ags run --gtk4 &