From 97130dbe27ab85b759073d6290a7cfbd37b50ced Mon Sep 17 00:00:00 2001 From: Franek <me@sador.me> Date: Sun, 27 Apr 2025 15:35:41 +0200 Subject: [PATCH] fix(ags): clipboard logic --- ags/styles/colors.scss | 40 ++++++------ ags/styles/components/_clipboard.scss | 27 ++++++--- ags/styles/components/_launcher.scss | 27 ++++----- ags/widget/launcher/clipboard.tsx | 87 ++++++++++++++++----------- ags/widget/launcher/launcher.tsx | 44 +++++++------- 5 files changed, 124 insertions(+), 101 deletions(-) diff --git a/ags/styles/colors.scss b/ags/styles/colors.scss index 87f2e7b..dba36a0 100644 --- a/ags/styles/colors.scss +++ b/ags/styles/colors.scss @@ -1,26 +1,26 @@ // SCSS Variables // Generated by 'wal' -$wallpaper: "/home/sadorowo/images/wallpapers/youtube_Sheri_142.jpg"; +$wallpaper: "/home/sadorowo/images/wallpapers/youtube_Sheri_170.jpg"; // Special -$background: #f9f7f5; -$foreground: #4C3A47; -$cursor: #4C3A47; +$background: #f9f6f3; +$foreground: #251F23; +$cursor: #251F23; // 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; +$color0: #f9f6f3; +$color1: #AB8772; +$color2: #D69768; +$color3: #727188; +$color4: #897A85; +$color5: #79869C; +$color6: #A29597; +$color7: #251F23; +$color8: #82766c; +$color9: #AB8772; +$color10: #D69768; +$color11: #727188; +$color12: #897A85; +$color13: #79869C; +$color14: #A29597; +$color15: #251F23; diff --git a/ags/styles/components/_clipboard.scss b/ags/styles/components/_clipboard.scss index ff67294..e17eadb 100644 --- a/ags/styles/components/_clipboard.scss +++ b/ags/styles/components/_clipboard.scss @@ -8,12 +8,20 @@ border: none; margin-bottom: 6px; - + &:focus { outline: 2px solid $color0; } } + .entry.primary { + background: $color3; + + &:hover>.content { + color: $color3; + } + } + .entry { padding: 10px; background: $color5; @@ -22,17 +30,16 @@ &:hover { background: $color0; - - box>.content { + + >.content { color: $color5; } } - box { - .content { - color: $color0; - font-size: 14px; - font-weight: 600; - } + + .content { + color: $color0; + font-size: 14px; + font-weight: 600; } } @@ -47,4 +54,4 @@ margin-bottom: 8px; } } -} +} \ No newline at end of file diff --git a/ags/styles/components/_launcher.scss b/ags/styles/components/_launcher.scss index d81eb4d..684a04f 100644 --- a/ags/styles/components/_launcher.scss +++ b/ags/styles/components/_launcher.scss @@ -8,7 +8,7 @@ border: none; margin-bottom: 6px; - + &:focus { outline: 2px solid $color0; } @@ -22,8 +22,9 @@ &:hover { background: $color0; - - box>.name, box>.description { + + box>.name, + box>.description { color: $color5; } } @@ -32,17 +33,15 @@ margin-right: 12px; } - box { - .name { - color: $color0; - font-size: 14px; - font-weight: 600; - } + .name { + color: $color0; + font-size: 14px; + font-weight: 600; + } - .description { - color: $color0; - font-size: 12px; - } + .description { + color: $color0; + font-size: 12px; } } @@ -57,4 +56,4 @@ margin-bottom: 8px; } } -} +} \ No newline at end of file diff --git a/ags/widget/launcher/clipboard.tsx b/ags/widget/launcher/clipboard.tsx index 791bbc7..96d9661 100644 --- a/ags/widget/launcher/clipboard.tsx +++ b/ags/widget/launcher/clipboard.tsx @@ -17,55 +17,61 @@ function ClipboardEntry({ id, content }: Entry) { execAsync([SHELL, "-c", `cliphist decode ${id} | wl-copy`]); }} > - <box> - <label - cssClasses={["content"]} - xalign={0} - label={content} - /> - </box> + <label + cssClasses={["content"]} + xalign={0} + label={content} + /> </button> } -async function getClipboardHistory() { +async function getClipboardHistory(history: Variable<Entry[]>) { 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 + history.set(ids .map((id, index) => ({ id, content: contents[index] })) - .filter(({ content }) => content && content.length > 0); + .filter(({ content }) => content && content.length > 0)); } catch { - return []; + history.set([]); } } 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 history: Variable<Entry[]> = Variable([]); const toSkip = Variable(0); - const list = bind(toSkip).as(count => limit( - skip(history, count), + + const list = Variable.derive([ + bind(history), + bind(toSkip) + ], (history, count) => limit( + skip(history, count), 10 )); + const isEmpty = bind(list).as(list => list.length === 0); 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; + if ((value + 10) > history.get().length) return; toSkip.set(value + 10) } }; + const setup = (self: Gtk.Window) => self.connect('notify::visible', async () => { + await getClipboardHistory(history) + }) + return <window visible={false} monitor={bind(hypr, "focusedMonitor").as(monitor => monitor.id)} @@ -74,22 +80,35 @@ export default async function Clipboard(_monitor_id: number) { 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> + application={App} + setup={setup}> + <box hexpand={false} cssClasses={["clipboard"]} vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}> + <box spacing={6} vertical onScroll={(_, __, dy) => onScroll(dy)}> + {bind(list).as(list => list.map(({ id, content }) => ( + <ClipboardEntry id={id} content={content} /> + )))} + <button + cssClasses={["entry", "primary"]} + visible={isEmpty.as(empty => !empty)} + onClicked={() => { + execAsync([SHELL, "-c", "cliphist wipe"]) + history.set([]) + }} + > + <label + cssClasses={["content"]} + xalign={0} + label="Wipe clipboard history" + /> + </button> + </box> + <box + halign={Gtk.Align.CENTER} + cssClasses={["not-found"]} + vertical + visible={isEmpty}> + <image iconName="system-search-symbolic" /> + <label label="No match found" /> </box> </box> </window> diff --git a/ags/widget/launcher/launcher.tsx b/ags/widget/launcher/launcher.tsx index 0e73568..9385c88 100644 --- a/ags/widget/launcher/launcher.tsx +++ b/ags/widget/launcher/launcher.tsx @@ -12,8 +12,8 @@ function AppButton({ app }: { app: Apps.Application }) { cssClasses={["application"]} onClicked={() => { hide(); - app.launch(); - }} + app.launch(); + }} > <box> <image iconName={app.iconName} /> @@ -37,7 +37,7 @@ function AppButton({ app }: { app: Apps.Application }) { export default function Launcher(_monitor_id: number) { const { TOP, BOTTOM, LEFT, RIGHT } = Astal.WindowAnchor; - const hypr = Hyprland.get_default(); + const hypr = Hyprland.get_default(); const apps = new Apps.Apps(); const query = Variable("") @@ -56,26 +56,24 @@ export default function Launcher(_monitor_id: number) { anchor={TOP | BOTTOM | LEFT | RIGHT} onKeyPressed={hideWindow} application={App}> - <box hexpand={false} vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}> - <box cssClasses={["launcher"]} vertical> - <entry - placeholderText="Search" - onChanged={self => query.set(self.text)} - onActivate={onEnter} - /> - <box spacing={6} vertical> - {list.as(list => list.map(app => ( - <AppButton app={app} /> - )))} - </box> - <box - halign={Gtk.Align.CENTER} - cssClasses={["not-found"]} - vertical - visible={list.as(l => l.length === 0)}> - <image iconName="system-search-symbolic" /> - <label label="No match found" /> - </box> + <box hexpand={false} cssClasses={["launcher"]} vertical halign={Gtk.Align.CENTER} valign={Gtk.Align.CENTER}> + <entry + placeholderText="Search" + onChanged={self => query.set(self.text)} + onActivate={onEnter} + /> + <box spacing={6} vertical> + {list.as(list => list.map(app => ( + <AppButton app={app} /> + )))} + </box> + <box + halign={Gtk.Align.CENTER} + cssClasses={["not-found"]} + vertical + visible={list.as(l => l.length === 0)}> + <image iconName="system-search-symbolic" /> + <label label="No match found" /> </box> </box> </window>