refactor(ags clipboard): make a clipboard service
All checks were successful
Discord / discord commits (push) Has been skipped
All checks were successful
Discord / discord commits (push) Has been skipped
This commit is contained in:
parent
9c91d8634f
commit
f793e546f6
3 changed files with 142 additions and 50 deletions
|
@ -72,7 +72,15 @@
|
||||||
"prefer-template": ["warn"],
|
"prefer-template": ["warn"],
|
||||||
|
|
||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-unused-vars": "warn",
|
"@typescript-eslint/no-unused-vars": ["warn", {
|
||||||
|
"args": "all",
|
||||||
|
"argsIgnorePattern": "^_",
|
||||||
|
"caughtErrors": "all",
|
||||||
|
"caughtErrorsIgnorePattern": "^_",
|
||||||
|
"destructuredArrayIgnorePattern": "^_",
|
||||||
|
"varsIgnorePattern": "^_",
|
||||||
|
"ignoreRestSiblings": true
|
||||||
|
}],
|
||||||
"@typescript-eslint/no-unsafe-declaration-merging": "off",
|
"@typescript-eslint/no-unsafe-declaration-merging": "off",
|
||||||
|
|
||||||
"@stylistic/array-bracket-newline": ["warn", "consistent"],
|
"@stylistic/array-bracket-newline": ["warn", "consistent"],
|
||||||
|
|
106
modules/ags/config/services/clipboard.ts
Normal file
106
modules/ags/config/services/clipboard.ts
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
const { execAsync } = Utils;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: add wl-paste --watch to not have to get history every time
|
||||||
|
const MAX_CLIPS = 100;
|
||||||
|
|
||||||
|
class Clipboard extends Service {
|
||||||
|
static {
|
||||||
|
Service.register(this, {
|
||||||
|
'clip-added': ['string'],
|
||||||
|
'history-searched': [],
|
||||||
|
}, {
|
||||||
|
clips: ['jsobject'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clips_left = 0;
|
||||||
|
private _clips: Map<number, { clip: string; isImage: boolean }> = new Map();
|
||||||
|
|
||||||
|
get clips() {
|
||||||
|
return this._clips;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private _decrementClipsLeft() {
|
||||||
|
if (--this._clips_left === 0) {
|
||||||
|
this.emit('history-searched');
|
||||||
|
// FIXME: this is necessary when not putting a cap on clip amount
|
||||||
|
// exec(`prlimit --pid ${exec('pgrep ags')} --nofile=1024:`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _decodeItem(index: string): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const decodedItem = await execAsync([
|
||||||
|
'bash', '-c', `cliphist list | grep ${index} | cliphist decode`,
|
||||||
|
]);
|
||||||
|
|
||||||
|
this._decrementClipsLeft();
|
||||||
|
|
||||||
|
return decodedItem;
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
this._decrementClipsLeft();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public copyOldItem(key: string | number): void {
|
||||||
|
execAsync([
|
||||||
|
'bash', '-c', `cliphist list | grep ${key} | cliphist decode | wl-copy`,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getHistory() {
|
||||||
|
this._clips = new Map();
|
||||||
|
|
||||||
|
// FIXME: this is necessary when not putting a cap on clip amount
|
||||||
|
// exec(`prlimit --pid ${exec('pgrep ags')} --nofile=10024:`);
|
||||||
|
|
||||||
|
// This command comes from '../../clipboard/script.sh'
|
||||||
|
execAsync('clipboard-manager')
|
||||||
|
.then((out) => {
|
||||||
|
const rawClips = out.split('\n');
|
||||||
|
|
||||||
|
this._clips_left = Math.min(rawClips.length - 1, MAX_CLIPS);
|
||||||
|
|
||||||
|
rawClips.forEach(async(clip, i) => {
|
||||||
|
if (i > MAX_CLIPS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clip.includes('img')) {
|
||||||
|
this._decrementClipsLeft();
|
||||||
|
this._clips.set(
|
||||||
|
parseInt((clip.match('[0-9]+') ?? [''])[0]),
|
||||||
|
{
|
||||||
|
clip,
|
||||||
|
isImage: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const decodedClip = await this._decodeItem(clip);
|
||||||
|
|
||||||
|
if (decodedClip) {
|
||||||
|
this._clips.set(
|
||||||
|
parseInt(clip),
|
||||||
|
{
|
||||||
|
clip: decodedClip,
|
||||||
|
isImage: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => console.error(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const clipboard = new Clipboard();
|
||||||
|
|
||||||
|
export default clipboard;
|
|
@ -1,40 +1,33 @@
|
||||||
const { Box, Icon, Label } = Widget;
|
const { Box, Icon, Label } = Widget;
|
||||||
const { execAsync } = Utils;
|
|
||||||
|
|
||||||
import { Fzf, FzfResultItem } from 'fzf';
|
import { Fzf, FzfResultItem } from 'fzf';
|
||||||
import Gtk from 'gi://Gtk?version=3.0';
|
import Gtk from 'gi://Gtk?version=3.0';
|
||||||
|
import Clipboard from '../../services/clipboard.ts';
|
||||||
|
|
||||||
import CursorBox from '../misc/cursorbox.ts';
|
import CursorBox from '../misc/cursorbox.ts';
|
||||||
import SortedList from '../misc/sorted-list.ts';
|
import SortedList from '../misc/sorted-list.ts';
|
||||||
|
|
||||||
|
|
||||||
const N_ITEMS = 30;
|
|
||||||
|
|
||||||
const copyOldItem = (key: string | number): void => {
|
|
||||||
execAsync([
|
|
||||||
'bash', '-c', `cliphist list | grep ${key} | cliphist decode | wl-copy`,
|
|
||||||
]);
|
|
||||||
App.closeWindow('win-clipboard');
|
|
||||||
};
|
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
let CopiedItems = [] as [string, number][];
|
let fzfResults: FzfResultItem<[number, { clip: string, isImage: boolean }]>[];
|
||||||
let fzfResults: FzfResultItem<[string, number]>[];
|
|
||||||
|
|
||||||
const getKey = (r: Gtk.ListBoxRow) => parseInt(r.get_child()?.name ?? '');
|
const getKey = (r: Gtk.ListBoxRow): number => parseInt(r.get_child()?.name ?? '0');
|
||||||
|
|
||||||
const makeItem = (list: Gtk.ListBox, key: string, val: string) => {
|
|
||||||
CopiedItems.push([val, parseInt(key)]);
|
|
||||||
|
|
||||||
|
const makeItem = (
|
||||||
|
list: Gtk.ListBox,
|
||||||
|
key: number,
|
||||||
|
val: string,
|
||||||
|
isImage: boolean,
|
||||||
|
): void => {
|
||||||
const widget = CursorBox({
|
const widget = CursorBox({
|
||||||
class_name: 'item',
|
class_name: 'item',
|
||||||
name: key,
|
name: key.toString(),
|
||||||
|
|
||||||
on_primary_click_release: () => copyOldItem(key),
|
on_primary_click_release: () => Clipboard.copyOldItem(key),
|
||||||
|
|
||||||
child: Box({
|
child: Box({
|
||||||
children: [
|
children: [
|
||||||
val.startsWith('img:') ?
|
isImage ?
|
||||||
Icon({
|
Icon({
|
||||||
icon: val.replace('img:', ''),
|
icon: val.replace('img:', ''),
|
||||||
size: 100 * 2,
|
size: 100 * 2,
|
||||||
|
@ -53,41 +46,26 @@ export default () => {
|
||||||
widget.show_all();
|
widget.show_all();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Decode old item:
|
|
||||||
const decodeItem = (list: Gtk.ListBox, index: string) => {
|
|
||||||
execAsync([
|
|
||||||
'bash', '-c', `cliphist list | grep ${index} | cliphist decode`,
|
|
||||||
]).then((out) => {
|
|
||||||
makeItem(list, index, out);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return SortedList({
|
return SortedList({
|
||||||
name: 'clipboard',
|
name: 'clipboard',
|
||||||
class_name: 'clipboard',
|
class_name: 'clipboard',
|
||||||
transition: 'slide top',
|
transition: 'slide top',
|
||||||
|
|
||||||
on_select: (r) => copyOldItem(getKey(r)),
|
on_select: (r) => Clipboard.copyOldItem(getKey(r)),
|
||||||
|
|
||||||
init_rows: (list) => {
|
init_rows: (list) => {
|
||||||
CopiedItems = [];
|
Clipboard.getHistory();
|
||||||
|
|
||||||
execAsync('clipboard-manager').then((out) => {
|
const connectId = Clipboard.connect('history-searched', () => {
|
||||||
list.get_children()?.forEach((ch) => {
|
list.get_children().forEach((row) => {
|
||||||
ch.destroy();
|
row.destroy();
|
||||||
});
|
});
|
||||||
|
Clipboard.clips.forEach((clip, key) => {
|
||||||
const items = out.split('\n');
|
makeItem(list, key, clip.clip, clip.isImage);
|
||||||
|
});
|
||||||
for (let i = 0; i < N_ITEMS; ++i) {
|
Clipboard.disconnect(connectId);
|
||||||
if (items[i].includes('img')) {
|
});
|
||||||
makeItem(list, (items[i].match('[0-9]+') ?? [''])[0], items[i]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
decodeItem(list, items[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).catch(console.error);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
set_sort: (text, list) => {
|
set_sort: (text, list) => {
|
||||||
|
@ -95,16 +73,16 @@ export default () => {
|
||||||
list.set_sort_func((row1, row2) => getKey(row2) - getKey(row1));
|
list.set_sort_func((row1, row2) => getKey(row2) - getKey(row1));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const fzf = new Fzf(CopiedItems, {
|
const fzf = new Fzf([...Clipboard.clips.entries()], {
|
||||||
selector: (item) => item[0],
|
selector: ([_key, { clip }]) => clip,
|
||||||
|
|
||||||
tiebreakers: [(a, b) => b[1] - a[1]],
|
tiebreakers: [(a, b) => b[0] - a[0]],
|
||||||
});
|
});
|
||||||
|
|
||||||
fzfResults = fzf.find(text);
|
fzfResults = fzf.find(text);
|
||||||
list.set_sort_func((a, b) => {
|
list.set_sort_func((a, b) => {
|
||||||
const row1 = fzfResults.find((f) => f.item[1] === getKey(a))?.score ?? 0;
|
const row1 = fzfResults.find((f) => f.item[0] === getKey(a))?.score ?? 0;
|
||||||
const row2 = fzfResults.find((f) => f.item[1] === getKey(b))?.score ?? 0;
|
const row2 = fzfResults.find((f) => f.item[0] === getKey(b))?.score ?? 0;
|
||||||
|
|
||||||
return row2 - row1;
|
return row2 - row1;
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue