diff --git a/nixosModules/ags/v2/app.ts b/nixosModules/ags/v2/app.ts
index 2552284f..69fd1ab9 100644
--- a/nixosModules/ags/v2/app.ts
+++ b/nixosModules/ags/v2/app.ts
@@ -8,6 +8,7 @@ import AppLauncher from './widgets/applauncher/main';
import Bar from './widgets/bar/wim';
import BgFade from './widgets/bg-fade/main';
import Calendar from './widgets/date/main';
+import Clipboard from './widgets/clipboard/main';
import Corners from './widgets/corners/main';
import IconBrowser from './widgets/icon-browser/main';
import { NotifPopups, NotifCenter } from './widgets/notifs/main';
@@ -59,6 +60,7 @@ switch (CONF) {
Bar();
BgFade();
Calendar();
+ Clipboard();
Corners();
IconBrowser();
NotifPopups();
diff --git a/nixosModules/ags/v2/style.scss b/nixosModules/ags/v2/style.scss
index 0203084e..af07abc2 100644
--- a/nixosModules/ags/v2/style.scss
+++ b/nixosModules/ags/v2/style.scss
@@ -40,6 +40,7 @@ progressbar {
@import 'widgets/applauncher/style.scss';
@import 'widgets/bar/style.scss';
+@import 'widgets/clipboard/style.scss';
@import 'widgets/date/style.scss';
@import 'widgets/icon-browser/style.scss';
@import 'widgets/lockscreen/style.scss';
diff --git a/nixosModules/ags/v2/widgets/clipboard/clip-item.tsx b/nixosModules/ags/v2/widgets/clipboard/clip-item.tsx
new file mode 100644
index 00000000..dea0f33b
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/clipboard/clip-item.tsx
@@ -0,0 +1,96 @@
+import { execAsync } from 'astal';
+import { register } from 'astal/gobject';
+import { Gtk, Widget } from 'astal/gtk3';
+
+export interface EntryObject {
+ id: number
+ content: string
+ entry: string
+}
+
+const SCALE = 150;
+const BINARY_DATA = /\[\[ binary data (\d+) (KiB|MiB) (\w+) (\d+)x(\d+) \]\]/;
+
+export const CLIP_SCRIPT = `${SRC}/widgets/clipboard/cliphist.sh`;
+
+@register()
+export class ClipItem extends Widget.Box {
+ declare id: number;
+ declare content: string;
+
+ public show_image(file: string, width: string | number, height: string | number) {
+ this.children[2].destroy();
+
+ const initCss = () => {
+ const _widthPx = Number(width);
+ const heightPx = Number(height);
+ const maxWidth = 400;
+ const widthPx = (_widthPx / heightPx) * SCALE;
+
+ let css = `background-image: url("${file}");`;
+
+ if (widthPx > maxWidth) {
+ const newHeightPx = (SCALE / widthPx) * maxWidth;
+
+ css += `min-height: ${newHeightPx}px; min-width: ${maxWidth}px;`;
+ }
+ else {
+ css += `min-height: 150px; min-width: ${widthPx}px;`;
+ }
+
+ return css;
+ };
+
+ const icon = (
+
+ );
+
+ this.children = [...this.children, icon];
+ };
+
+ constructor({ item }: { item: EntryObject }) {
+ super({
+ children: [
+ ,
+ ,
+ ,
+ ],
+ });
+
+ this.id = item.id;
+ this.content = item.content;
+
+ const matches = this.content.match(BINARY_DATA);
+
+ if (matches) {
+ // const size = matches[1];
+ const format = matches[3];
+ const width = matches[4];
+ const height = matches[5];
+
+ if (format === 'png') {
+ execAsync(`${CLIP_SCRIPT} --save-by-id ${this.id}`)
+ .then((file) => {
+ this.show_image(file, width, height);
+ })
+ .catch(print);
+ }
+ }
+ }
+}
diff --git a/nixosModules/ags/v2/widgets/clipboard/cliphist.sh b/nixosModules/ags/v2/widgets/clipboard/cliphist.sh
new file mode 100755
index 00000000..149ac683
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/clipboard/cliphist.sh
@@ -0,0 +1,44 @@
+#!/usr/bin/env bash
+# https://github.com/koeqaife/hyprland-material-you/blob/d23cf9d524522c8c215664c2c3334c2b51609cae/ags/scripts/cliphist.sh
+
+get() {
+ cliphist list | iconv -f "$(locale charmap)" -t UTF-8 -c
+}
+
+copy_by_id() {
+ id=$1
+ cliphist decode "$id" | wl-copy
+}
+
+clear() {
+ cliphist wipe
+}
+
+save_cache_file() {
+ id=$1
+
+ output_file="/tmp/ags/cliphist/$id.png"
+
+ if [[ ! -f "$output_file" ]]; then
+ mkdir -p "/tmp/ags/cliphist/"
+ cliphist decode "$id" >"$output_file"
+ fi
+
+ echo "$output_file"
+}
+
+clear_tmp() {
+ rm "/tmp/ags/cliphist/*"
+}
+
+if [[ "$1" == "--get" ]]; then
+ get
+elif [[ "$1" == "--copy-by-id" ]]; then
+ { copy_by_id "$2"; }
+elif [[ "$1" == "--save-by-id" ]]; then
+ { save_cache_file "$2"; }
+elif [[ "$1" == "--clear-cache" ]]; then
+ clear_tmp
+elif [[ "$1" == "--clear" ]]; then
+ clear
+fi
diff --git a/nixosModules/ags/v2/widgets/clipboard/main.tsx b/nixosModules/ags/v2/widgets/clipboard/main.tsx
new file mode 100644
index 00000000..29ebbc14
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/clipboard/main.tsx
@@ -0,0 +1,66 @@
+import { execAsync } from 'astal';
+import { App } from 'astal/gtk3';
+
+import SortedList from '../misc/sorted-list';
+
+import { CLIP_SCRIPT, ClipItem, EntryObject } from './clip-item';
+
+
+export default () => SortedList({
+ name: 'clipboard',
+
+ create_list: async() => {
+ const output = await execAsync(`${CLIP_SCRIPT} --get`)
+ .then((str) => str)
+ .catch((err) => {
+ print(err);
+
+ return '';
+ });
+
+ return output
+ .split('\n')
+ .filter((line) => line.trim() !== '')
+ .map((entry) => {
+ const [id, ...content] = entry.split('\t');
+
+ return { id: parseInt(id.trim()), content: content.join(' ').trim(), entry };
+ });
+ },
+
+ create_row: (item) => new ClipItem({ item }),
+
+ fzf_options: {
+ selector: (item) => item.content,
+ },
+
+ compare_props: ['id'],
+
+ on_row_activated: (row) => {
+ const clip = row.get_children()[0] as ClipItem;
+
+ execAsync(`${CLIP_SCRIPT} --copy-by-id ${clip.id}`);
+ App.get_window('win-clipboard')?.set_visible(false);
+ },
+
+ sort_func: (a, b, entry, fzfResults) => {
+ const row1 = a.get_children()[0] as ClipItem;
+ const row2 = b.get_children()[0] as ClipItem;
+
+ if (entry.text === '' || entry.text === '-') {
+ a.set_visible(true);
+ b.set_visible(true);
+
+ return row2.id - row1.id;
+ }
+ else {
+ const s1 = fzfResults.find((r) => r.item.id === row1.id)?.score ?? 0;
+ const s2 = fzfResults.find((r) => r.item.id === row2.id)?.score ?? 0;
+
+ a.set_visible(s1 !== 0);
+ b.set_visible(s2 !== 0);
+
+ return s2 - s1;
+ }
+ },
+});
diff --git a/nixosModules/ags/v2/widgets/clipboard/style.scss b/nixosModules/ags/v2/widgets/clipboard/style.scss
new file mode 100644
index 00000000..8ff3349a
--- /dev/null
+++ b/nixosModules/ags/v2/widgets/clipboard/style.scss
@@ -0,0 +1,4 @@
+.clipboard .list row box {
+ margin: 20px;
+ font-size: 16px;
+}