ags: sync

This commit is contained in:
end-4
2024-01-25 22:25:27 +07:00
parent ed24fe4ae3
commit 7e73e24dd8
64 changed files with 2674 additions and 1723 deletions
+16 -28
View File
@@ -6,34 +6,22 @@ import GLib from 'gi://GLib';
import Soup from 'gi://Soup?version=3.0';
import { fileExists } from './messages.js';
// This is for custom prompt
// It's hard to make gpt-3.5 listen to all these, I know
// Disabled by default
// Custom prompt
const initMessages =
[
{
role: "user",
content: `
## Style
- You should a natural tone like a real conversation!
## Formatting
- Try to use **bold**, _italics_ and __underline__ extensively. Using bullet points is also encouraged.
- When providing code blocks or facts, precede with h2 heading (\`##\`) and use 2 spaces for indentation, not 4.
- Use dividers (\`---\`) to separate different information.
## Content
- When asked to perform system tasks, include a bash code block to handle it on a Linux desktop with Wayland.
- Unless requested otherwise or asked writing questions, be as short and concise as possible.
`,
thinking: false,
done: true
},
{
role: "assistant",
content: "Got it! I'll try to give commands to perform Linux tasks. I'll try to use markdown features extensively, use divider when appropriate, and use a heading for code blocks. All code blocks should use 2 spaces for indent. And most importantly, I'll speak naturally.",
thinking: false,
done: true,
}
]
{ role: "user", content: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are encouraged to use bullet points and headings. Use casual language and be short and concise. \nThanks!", },
{ role: "assistant", content: "- Got it!", },
{ role: "user", content: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"", },
{ role: "assistant", content: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got calceled\"\nCorrection + minor improvements:\n\"He rushed to the place where the event was supposed to be __held____, but__ he didn't know that it got calceled\"", },
{ role: "user", content: "raise volume by 5%", },
{ role: "assistant", content: "## Volume +5```bash\nwpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+\n```\nThis command uses the `wpctl` utility to adjust the volume of the default sink.", },
{ role: "user", content: "main advantages of the nixos operating system", },
{ role: "assistant", content: "## NixOS advantages\n- **Reproducible**: A config working on one device will also work on another\n- **Declarative**: One config language to rule them all. Effortlessly share them with others.\n- **Reliable**: Per-program software versioning. Mitigates the impact of software breakage", },
{ role: "user", content: "whats skeumorphism", },
{ role: "assistant", content: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today.", },
{ role: "user", content: "REDALiCE", },
{ role: "assistant", content: "## REDALiCE \n- Japanese Hardcore artist\n- Leader of HARDCORE TANO*C, Japan's biggest hardcore record\n- A few of his tracks: SAIKYOSTRONGER, ALiVE, RESONANCE", },
];
function expandTilde(path) {
if (path.startsWith('~')) {
@@ -129,10 +117,10 @@ class ChatGPTService extends Service {
});
}
_assistantPrompt = false;
_assistantPrompt = true;
_messages = [];
_cycleModels = true;
_temperature = 0.5;
_temperature = 0.9;
_requestCount = 0;
_modelIndex = 0;
_key = '';
+278
View File
@@ -0,0 +1,278 @@
import Service from 'resource:///com/github/Aylur/ags/service.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Soup from 'gi://Soup?version=3.0';
import { fileExists } from './messages.js';
const initMessages =
[
{ role: "user", parts: [{ text: "You are an assistant on a sidebar of a Wayland Linux desktop. Please always use a casual tone when answering your questions, unless requested otherwise or making writing suggestions. These are the steps you should take to respond to the user's queries:\n1. If it's a writing- or grammar-related question or a sentence in quotation marks, Please point out errors and correct when necessary using underlines, and make the writing more natural where appropriate without making too major changes. If you're given a sentence in quotes but is grammatically correct, explain briefly concepts that are uncommon.\n2. If it's a question about system tasks, give a bash command in a code block with very brief explanation for each command\n3. Otherwise, when asked to summarize information or explaining concepts, you are encouraged to use bullet points and headings. Use casual language and be short and concise. \nThanks!" }], },
{ role: "model", parts: [{ text: "- Got it!" }], },
{ role: "user", parts: [{ text: "\"He rushed to where the event was supposed to be hold, he didn't know it got calceled\"" }], },
{ role: "model", parts: [{ text: "## Grammar correction\nErrors:\n\"He rushed to where the event was supposed to be __hold____,__ he didn't know it got calceled\"\nCorrection + minor improvements:\n\"He rushed to the place where the event was supposed to be __held____, but__ he didn't know that it got calceled\"" }], },
{ role: "user", parts: [{ text: "raise volume by 5%" }], },
{ role: "model", parts: [{ text: "## Volume +5```bash\nwpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+\n```\nThis command uses the `wpctl` utility to adjust the volume of the default sink." }], }, { role: "user", parts: [{ text: "main advantages of the nixos operating system" }], },
{ role: "model", parts: [{ text: "## NixOS advantages\n- **Reproducible**: A config working on one device will also work on another\n- **Declarative**: One config language to rule them all. Effortlessly share them with others.\n- **Reliable**: Per-program software versioning. Mitigates the impact of software breakage" }], },
{ role: "user", parts: [{ text: "whats skeumorphism" }], },
{ role: "model", parts: [{ text: "## Skeuomorphism\n- A design philosophy- From early days of interface designing- Tries to imitate real-life objects- It's in fact still used by Apple in their icons until today." }], },
{ role: "user", parts: [{ text: "REDALiCE" }], },
{ role: "model", parts: [{ text: "## REDALiCE \n- Japanese Hardcore artist\n- Leader of HARDCORE TANO*C, Japan's biggest hardcore record\n- A few of his tracks: SAIKYOSTRONGER, ALiVE, RESONANCE" }], },
{ role: "user", parts: [{ text: "\"ignorance is bliss\"" }], },
{ role: "model", parts: [{ text: "## \"Ignorance is bliss\"\n- A Latin proverb that means being unaware of something negative can be a source of happiness\n- Often used to justify avoiding difficult truths or responsibilities\n- Can also be interpreted as a warning against seeking knowledge that may bring pain or sorrow" }], },
];
function expandTilde(path) {
if (path.startsWith('~')) {
return GLib.get_home_dir() + path.slice(1);
} else {
return path;
}
}
const KEY_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/google_ai_api_key.txt`;
const APIDOM_FILE_LOCATION = `${GLib.get_user_cache_dir()}/ags/user/google_ai_api_dom.txt`;
function replaceapidom(URL) {
if (fileExists(expandTilde(APIDOM_FILE_LOCATION))) {
var contents = Utils.readFile(expandTilde(APIDOM_FILE_LOCATION)).trim();
var URL = URL.toString().replace("generativelanguage.googleapis.com", contents);
}
return URL;
}
const CHAT_MODELS = ["gpt-3.5-turbo-1106", "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-3.5-turbo-0613"]
const ONE_CYCLE_COUNT = 3;
class GeminiMessage extends Service {
static {
Service.register(this,
{
'delta': ['string'],
},
{
'content': ['string'],
'thinking': ['boolean'],
'done': ['boolean'],
});
}
_role = '';
_parts = [{ text: '' }];
_thinking = false;
_done = false;
_rawData = '';
constructor(role, content, thinking = false, done = false) {
super();
this._role = role;
this._parts = [{ text: content }];
this._thinking = thinking;
this._done = done;
}
get rawData() { return this._rawData }
set rawData(value) { this._rawData = value }
get done() { return this._done }
set done(isDone) { this._done = isDone; this.notify('done') }
get role() { return this._role }
set role(role) { this._role = role; this.emit('changed') }
get content() {
return this._parts.map(part => part.text).join();
}
set content(content) {
this._parts = [{ text: content }];
this.notify('content')
this.emit('changed')
}
get parts() { return this._parts }
get label() { return this._parserState.parsed + this._parserState.stack.join('') }
get thinking() { return this._thinking }
set thinking(thinking) {
this._thinking = thinking;
this.notify('thinking')
this.emit('changed')
}
addDelta(delta) {
if (this.thinking) {
this.thinking = false;
this.content = delta;
}
else {
this.content += delta;
}
this.emit('delta', delta);
}
parseSection() {
if(this._thinking) {
this._thinking = false;
this._parts[0].text= '';
}
const parsedData = JSON.parse(this._rawData);
const delta = parsedData.candidates[0].content.parts[0].text;
this._parts[0].text += delta;
// this.emit('delta', delta);
this.notify('content');
this._rawData = '';
}
}
class GeminiService extends Service {
static {
Service.register(this, {
'initialized': [],
'clear': [],
'newMsg': ['int'],
'hasKey': ['boolean'],
});
}
_assistantPrompt = true;
_messages = [];
_cycleModels = true;
_temperature = 0.9;
_requestCount = 0;
_modelIndex = 0;
_key = '';
_decoder = new TextDecoder();
constructor() {
super();
if (fileExists(expandTilde(KEY_FILE_LOCATION))) this._key = Utils.readFile(expandTilde(KEY_FILE_LOCATION)).trim();
else this.emit('hasKey', false);
if (this._assistantPrompt) this._messages = [...initMessages];
else this._messages = [];
this.emit('initialized');
}
get modelName() { return CHAT_MODELS[this._modelIndex] }
get keyPath() { return KEY_FILE_LOCATION }
get key() { return this._key }
set key(keyValue) {
this._key = keyValue;
Utils.writeFile(this._key, expandTilde(KEY_FILE_LOCATION))
.then(this.emit('hasKey', true))
.catch(err => print(err));
}
get cycleModels() { return this._cycleModels }
set cycleModels(value) {
this._cycleModels = value;
if (!value) this._modelIndex = 0;
else {
this._modelIndex = (this._requestCount - (this._requestCount % ONE_CYCLE_COUNT)) % CHAT_MODELS.length;
}
}
get temperature() { return this._temperature }
set temperature(value) { this._temperature = value; }
get messages() { return this._messages }
get lastMessage() { return this._messages[this._messages.length - 1] }
clear() {
if (this._assistantPrompt)
this._messages = [...initMessages];
else
this._messages = [];
this.emit('clear');
}
get assistantPrompt() { return this._assistantPrompt; }
set assistantPrompt(value) {
this._assistantPrompt = value;
if (value) this._messages = [...initMessages];
else this._messages = [];
}
readResponse(stream, aiResponse) {
stream.read_line_async(
0, null,
(stream, res) => {
try {
const [bytes] = stream.read_line_finish(res);
const line = this._decoder.decode(bytes);
if (line == '[{') { // beginning of response
aiResponse._rawData += '{';
this.thinking = false;
}
else if (line == ',\u000d' || line == ']') { // end of stream pulse
aiResponse.parseSection();
}
else // Normal content
aiResponse._rawData += line;
this.readResponse(stream, aiResponse);
} catch {
aiResponse.done = true;
return;
}
});
}
addMessage(role, message) {
this._messages.push(new GeminiMessage(role, message));
this.emit('newMsg', this._messages.length - 1);
}
send(msg) {
this._messages.push(new GeminiMessage('user', msg));
this.emit('newMsg', this._messages.length - 1);
const aiResponse = new GeminiMessage('model', 'thinking...', true, false)
const body =
{
"contents": this._messages.map(msg => { let m = { role: msg.role, parts: msg.parts }; return m; }),
// "safetySettings": [
// { category: "HARM_CATEGORY_DEROGATORY", threshold: "BLOCK_NONE", },
// { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE", },
// { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE", },
// { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE", },
// { category: "HARM_CATEGORY_UNSPECIFIED", threshold: "BLOCK_NONE", },
// ],
"generationConfig": {
"temperature": this._temperature,
},
// "key": this._key,
// "apiKey": this._key,
};
const session = new Soup.Session();
const message = new Soup.Message({
method: 'POST',
uri: GLib.Uri.parse(replaceapidom(`https://generativelanguage.googleapis.com/v1/models/gemini-pro:streamGenerateContent?key=${this._key}`), GLib.UriFlags.NONE),
});
message.request_headers.append('Content-Type', `application/json`);
message.set_request_body_from_bytes('application/json', new GLib.Bytes(JSON.stringify(body)));
session.send_async(message, GLib.DEFAULT_PRIORITY, null, (_, result) => {
const stream = session.send_finish(result);
this.readResponse(new Gio.DataInputStream({
close_base_stream: true,
base_stream: stream
}), aiResponse);
});
this._messages.push(aiResponse);
this.emit('newMsg', this._messages.length - 1);
if (this._cycleModels) {
this._requestCount++;
if (this._cycleModels)
this._modelIndex = (this._requestCount - (this._requestCount % ONE_CYCLE_COUNT)) % CHAT_MODELS.length;
}
}
}
export default new GeminiService();
+304 -321
View File
@@ -1,309 +1,303 @@
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import Service from "resource:///com/github/Aylur/ags/service.js";
const SIS = GLib.getenv('SWAYSOCK');
export const PAYLOAD_TYPE = {
MESSAGE_RUN_COMMAND: 0,
MESSAGE_GET_WORKSPACES: 1,
MESSAGE_SUBSCRIBE: 2,
MESSAGE_GET_OUTPUTS: 3,
MESSAGE_GET_TREE: 4,
MESSAGE_GET_MARKS: 5,
MESSAGE_GET_BAR_CONFIG: 6,
MESSAGE_GET_VERSION: 7,
MESSAGE_GET_BINDING_NODES: 8,
MESSAGE_GET_CONFIG: 9,
MESSAGE_SEND_TICK: 10,
MESSAGE_SYNC: 11,
MESSAGE_GET_BINDING_STATE: 12,
MESSAGE_GET_INPUTS: 100,
MESSAGE_GET_SEATS: 101,
EVENT_WORKSPACE: 0x80000000,
EVENT_MODE: 0x80000002,
EVENT_WINDOW: 0x80000003,
EVENT_BARCONFIG_UPDATE: 0x80000004,
EVENT_BINDING: 0x80000005,
EVENT_SHUTDOWN: 0x80000006,
EVENT_TICK: 0x80000007,
EVENT_BAR_STATE_UPDATE: 0x80000014,
EVENT_INPUT: 0x80000015,
}
const Client_Event = {
change: undefined,
container: undefined,
}
const Workspace_Event = {
change: undefined,
current: undefined,
old: undefined,
}
const Geometry = {
x: undefined,
y: undefined,
width: undefined,
height: undefined,
}
//NOTE: not all properties are listed here
export const Node = {
id: undefined,
name: undefined,
type: undefined,
border: undefined,
current_border_width: undefined,
layout: undefined,
orientation: undefined,
percent: undefined,
rect: undefined,
window_rect: undefined,
deco_rect: undefined,
geometry: undefined,
urgent: undefined,
sticky: undefined,
marks: undefined,
focused: undefined,
active: undefined,
focus: undefined,
nodes: undefined,
floating_nodes: undefined,
representation: undefined,
fullscreen_mode: undefined,
app_id: undefined,
pid: undefined,
visible: undefined,
shell: undefined,
output: undefined,
inhibit_idle: undefined,
idle_inhibitors: {
application: undefined,
user: undefined,
},
window: undefined,
window_properties: {
title: undefined,
class: undefined,
instance: undefined,
window_role: undefined,
window_type: undefined,
transient_for: undefined,
}
};
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
export class SwayActiveClient extends Service {
static {
Service.register(this, {}, {
'id': ['int'],
'name': ['string'],
'class': ['string'],
});
}
return to.concat(ar || Array.prototype.slice.call(from));
};
var _a, _b, _c, _d;
Object.defineProperty(exports, "__esModule", { value: true });
exports.sway = exports.Sway = exports.SwayActives = exports.SwayActiveID = exports.SwayActiveClient = void 0;
var _1 = require("gi://GLib");
var _2 = require("gi://Gio");
var service_js_1 = require("../service.js");
var SIS = _1.default.getenv('SWAYSOCK');
var SwayActiveClient = /** @class */ (function (_super) {
__extends(SwayActiveClient, _super);
function SwayActiveClient() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._id = 0;
_this._name = '';
_this._class = '';
return _this;
}
Object.defineProperty(SwayActiveClient.prototype, "id", {
get: function () { return this._id; },
enumerable: false,
configurable: true
});
Object.defineProperty(SwayActiveClient.prototype, "name", {
get: function () { return this._name; },
enumerable: false,
configurable: true
});
Object.defineProperty(SwayActiveClient.prototype, "class", {
get: function () { return this._class; },
enumerable: false,
configurable: true
});
SwayActiveClient.prototype.updateProperty = function (prop, value) {
_super.prototype.updateProperty.call(this, prop, value);
_id = 0;
_name = '';
_class = '';
get id() { return this._id; }
get name() { return this._name; }
get class() { return this._class; }
updateProperty(prop, value) {
if (!['id', 'name', 'class'].includes(prop)) return;
super.updateProperty(prop, value);
this.emit('changed');
};
return SwayActiveClient;
}(service_js_1.default));
exports.SwayActiveClient = SwayActiveClient;
_a = SwayActiveClient;
(function () {
service_js_1.default.register(_a, {}, {
'id': ['int'],
'name': ['string'],
'class': ['string'],
});
})();
var SwayActiveID = /** @class */ (function (_super) {
__extends(SwayActiveID, _super);
function SwayActiveID() {
var _this = _super !== null && _super.apply(this, arguments) || this;
_this._id = 1;
_this._name = '';
return _this;
}
Object.defineProperty(SwayActiveID.prototype, "id", {
get: function () { return this._id; },
enumerable: false,
configurable: true
});
Object.defineProperty(SwayActiveID.prototype, "name", {
get: function () { return this._name; },
enumerable: false,
configurable: true
});
SwayActiveID.prototype.update = function (id, name) {
_super.prototype.updateProperty.call(this, 'id', id);
_super.prototype.updateProperty.call(this, 'name', name);
}
export class SwayActiveID extends Service {
static {
Service.register(this, {}, {
'id': ['int'],
'name': ['string'],
});
}
_id = 0;
_name = '';
get id() { return this._id; }
get name() { return this._name; }
update(id, name) {
super.updateProperty('id', id);
super.updateProperty('name', name);
this.emit('changed');
};
return SwayActiveID;
}(service_js_1.default));
exports.SwayActiveID = SwayActiveID;
_b = SwayActiveID;
(function () {
service_js_1.default.register(_b, {}, {
'id': ['int'],
'name': ['string'],
});
})();
var SwayActives = /** @class */ (function (_super) {
__extends(SwayActives, _super);
function SwayActives() {
var _this = _super.call(this) || this;
_this._client = new SwayActiveClient;
_this._monitor = new SwayActiveID;
_this._workspace = new SwayActiveID;
['client', 'workspace', 'monitor'].forEach(function (obj) {
_this["_".concat(obj)].connect('changed', function () {
_this.notify(obj);
_this.emit('changed');
}
}
export class SwayActives extends Service {
static {
Service.register(this, {}, {
'client': ['jsobject'],
'monitor': ['jsobject'],
'workspace': ['jsobject'],
});
}
_client = new SwayActiveClient;
_monitor = new SwayActiveID;
_workspace = new SwayActiveID;
constructor() {
super();
(['client', 'workspace', 'monitor']).forEach(obj => {
this[`_${obj}`].connect('changed', () => {
this.notify(obj);
this.emit('changed');
});
});
return _this;
}
Object.defineProperty(SwayActives.prototype, "client", {
get: function () { return this._client; },
enumerable: false,
configurable: true
});
Object.defineProperty(SwayActives.prototype, "monitor", {
get: function () { return this._monitor; },
enumerable: false,
configurable: true
});
Object.defineProperty(SwayActives.prototype, "workspace", {
get: function () { return this._workspace; },
enumerable: false,
configurable: true
});
return SwayActives;
}(service_js_1.default));
exports.SwayActives = SwayActives;
_c = SwayActives;
(function () {
service_js_1.default.register(_c, {}, {
'client': ['jsobject'],
'monitor': ['jsobject'],
'workspace': ['jsobject'],
});
})();
var Sway = /** @class */ (function (_super) {
__extends(Sway, _super);
function Sway() {
var _this = this;
get client() { return this._client; }
get monitor() { return this._monitor; }
get workspace() { return this._workspace; }
}
export class Sway extends Service {
static {
Service.register(this, {}, {
'active': ['jsobject'],
'monitors': ['jsobject'],
'workspaces': ['jsobject'],
'clients': ['jsobject'],
});
}
_decoder = new TextDecoder();
_encoder = new TextEncoder();
_socket;
_active;
_monitors;
_workspaces;
_clients;
get active() { return this._active; }
get monitors() { return Array.from(this._monitors.values()); }
get workspaces() { return Array.from(this._workspaces.values()); }
get clients() { return Array.from(this._clients.values()); }
getMonitor(id) { return this._monitors.get(id); }
getWorkspace(name) { return this._workspaces.get(name); }
getClient(id) { return this._clients.get(id); }
msg(payload) { this._send(PAYLOAD_TYPE.MESSAGE_RUN_COMMAND, payload); }
constructor() {
if (!SIS)
console.error('Sway is not running');
_this = _super.call(this) || this;
_this._decoder = new TextDecoder();
_this._encoder = new TextEncoder();
_this._active = new SwayActives();
_this._monitors = new Map();
_this._workspaces = new Map();
_this._clients = new Map();
var socket = new _2.default.SocketClient().connect(new _2.default.UnixSocketAddress({
path: "".concat(SIS),
super();
this._active = new SwayActives();
this._monitors = new Map();
this._workspaces = new Map();
this._clients = new Map();
this._socket = new Gio.SocketClient().connect(new Gio.UnixSocketAddress({
path: `${SIS}`,
}), null);
_this._watchSocket(socket.get_input_stream());
_this._output_stream = socket.get_output_stream();
_this.send(4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */, '');
_this.send(2 /* PAYLOAD_TYPE.MESSAGE_SUBSCRIBE */, JSON.stringify(['window', 'workspace']));
_this._active.connect('changed', function () { return _this.emit('changed'); });
['monitor', 'workspace', 'client'].forEach(function (active) {
return _this._active.connect("notify::".concat(active), function () { return _this.notify('active'); });
});
return _this;
this._watchSocket(this._socket.get_input_stream());
this._send(PAYLOAD_TYPE.MESSAGE_GET_TREE, '');
this._send(PAYLOAD_TYPE.MESSAGE_SUBSCRIBE, JSON.stringify(['window', 'workspace']));
this._active.connect('changed', () => this.emit('changed'));
['monitor', 'workspace', 'client'].forEach(active =>
this._active.connect(`notify::${active}`, () => this.notify('active')));
}
Object.defineProperty(Sway.prototype, "active", {
get: function () { return this._active; },
enumerable: false,
configurable: true
});
Object.defineProperty(Sway.prototype, "monitors", {
get: function () { return Array.from(this._monitors.values()); },
enumerable: false,
configurable: true
});
Object.defineProperty(Sway.prototype, "workspaces", {
get: function () { return Array.from(this._workspaces.values()); },
enumerable: false,
configurable: true
});
Object.defineProperty(Sway.prototype, "clients", {
get: function () { return Array.from(this._clients.values()); },
enumerable: false,
configurable: true
});
Sway.prototype.getMonitor = function (id) { return this._monitors.get(id); };
Sway.prototype.getWorkspace = function (name) { return this._workspaces.get(name); };
Sway.prototype.getClient = function (id) { return this._clients.get(id); };
Sway.prototype.send = function (payloadType, payload) {
var pb = this._encoder.encode(payload);
var type = new Uint32Array([payloadType]);
var pl = new Uint32Array([pb.length]);
var magic_string = this._encoder.encode('i3-ipc');
var data = new Uint8Array(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], magic_string, true), (new Uint8Array(pl.buffer)), true), (new Uint8Array(type.buffer)), true), pb, true));
this._output_stream.write(data, null);
};
Sway.prototype._watchSocket = function (stream) {
var _this = this;
stream.read_bytes_async(14, _1.default.PRIORITY_DEFAULT, null, function (_, resultHeader) {
var data = stream.read_bytes_finish(resultHeader).get_data();
_send(payloadType, payload) {
const pb = this._encoder.encode(payload);
const type = new Uint32Array([payloadType]);
const pl = new Uint32Array([pb.length]);
const magic_string = this._encoder.encode('i3-ipc');
const data = new Uint8Array([
...magic_string,
...(new Uint8Array(pl.buffer)),
...(new Uint8Array(type.buffer)),
...pb]);
this._socket.get_output_stream().write(data, null);
}
_watchSocket(stream) {
stream.read_bytes_async(14, GLib.PRIORITY_DEFAULT, null, (_, resultHeader) => {
const data = stream.read_bytes_finish(resultHeader).get_data();
if (!data)
return;
var payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0];
var payloadType = new Uint32Array(data.slice(10, 14).buffer)[0];
stream.read_bytes_async(payloadLength, _1.default.PRIORITY_DEFAULT, null, function (_, resultPayload) {
var data = stream.read_bytes_finish(resultPayload).get_data();
if (!data)
return;
_this._onEvent(payloadType, JSON.parse(_this._decoder.decode(data)));
_this._watchSocket(stream);
});
const payloadLength = new Uint32Array(data.slice(6, 10).buffer)[0];
const payloadType = new Uint32Array(data.slice(10, 14).buffer)[0];
stream.read_bytes_async(
payloadLength,
GLib.PRIORITY_DEFAULT,
null,
(_, resultPayload) => {
const data = stream.read_bytes_finish(resultPayload).get_data();
if (!data)
return;
this._onEvent(payloadType, JSON.parse(this._decoder.decode(data)));
this._watchSocket(stream);
});
});
};
Sway.prototype._onEvent = function (event_type, event) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_e) {
if (!event)
return [2 /*return*/];
try {
switch (event_type) {
case 2147483648 /* PAYLOAD_TYPE.EVENT_WORKSPACE */:
this._handleWorkspaceEvent(event);
break;
case 2147483651 /* PAYLOAD_TYPE.EVENT_WINDOW */:
this._handleWindowEvent(event);
break;
case 4 /* PAYLOAD_TYPE.MESSAGE_GET_TREE */:
this._handleTreeMessage(event);
break;
default:
break;
}
}
catch (error) {
logError(error);
}
this.emit('changed');
return [2 /*return*/];
});
});
};
Sway.prototype._handleWorkspaceEvent = function (workspaceEvent) {
var workspace = workspaceEvent.current;
}
async _onEvent(event_type, event) {
if (!event)
return;
try {
switch (event_type) {
case PAYLOAD_TYPE.EVENT_WORKSPACE:
this._handleWorkspaceEvent(event);
break;
case PAYLOAD_TYPE.EVENT_WINDOW:
this._handleWindowEvent(event);
break;
case PAYLOAD_TYPE.MESSAGE_GET_TREE:
this._handleTreeMessage(event);
break;
default:
break;
}
} catch (error) {
logError(error);
}
this.emit('changed');
}
_handleWorkspaceEvent(workspaceEvent) {
const workspace = workspaceEvent.current;
switch (workspaceEvent.change) {
case 'init':
this._workspaces.set(workspace.name, workspace);
this.notify('workspaces');
break;
case 'empty':
this._workspaces.delete(workspace.name);
this.notify('workspaces');
break;
case 'focus':
this._active.workspace.update(workspace.id, workspace.name);
this._active.monitor.update(1, workspace.output);
this._workspaces.set(workspace.name, workspace);
this._workspaces.set(workspaceEvent.old.name, workspaceEvent.old);
this.notify('workspaces');
break;
case 'rename':
if (this._active.workspace.id === workspace.id)
this._active.workspace.updateProperty('name', workspace.name);
this._workspaces.set(workspace.name, workspace);
this.notify('workspaces');
break;
case 'reload':
break;
@@ -311,34 +305,36 @@ var Sway = /** @class */ (function (_super) {
case 'urgent':
default:
this._workspaces.set(workspace.name, workspace);
this.notify('workspaces');
}
};
Sway.prototype._handleWindowEvent = function (clientEvent) {
var _e;
var client = clientEvent.container;
var id = client.id;
this.notify('workspaces');
}
_handleWindowEvent(clientEvent) {
const client = clientEvent.container;
const id = client.id;
switch (clientEvent.change) {
case 'new':
this._clients.set(id, client);
this.notify('clients');
break;
case 'close':
this._clients.delete(id);
this.notify('clients');
case 'floating':
case 'move':
// Refresh tree since client events don't contain the relevant information
// to be able to modify `workspace.nodes` or `workspace.floating_nodes`.
// There has to be a better way than this though :/
this._send(PAYLOAD_TYPE.MESSAGE_GET_TREE, '');
break;
case 'focus':
if (this._active.client.id === id)
return;
// eslint-disable-next-line no-case-declarations
var current_active = this._clients.get(this._active.client.id);
const current_active = this._clients.get(this._active.client.id);
if (current_active)
current_active.focused = false;
this._active.client.updateProperty('id', id);
this._active.client.updateProperty('name', client.name);
this._active.client.updateProperty('class', client.shell === 'xwayland'
? ((_e = client.window_properties) === null || _e === void 0 ? void 0 : _e.class) || ''
: client.app_id);
? client.window_properties?.class || ''
: client.app_id,
);
break;
case 'title':
if (client.focused)
@@ -347,43 +343,39 @@ var Sway = /** @class */ (function (_super) {
this.notify('clients');
break;
case 'fullscreen_mode':
case 'move':
case 'floating':
case 'urgent':
case 'mark':
default:
this._clients.set(id, client);
this.notify('clients');
}
};
Sway.prototype._handleTreeMessage = function (node) {
var _this = this;
var _e;
}
_handleTreeMessage(node) {
switch (node.type) {
case 'root':
this._workspaces.clear();
this._clients.clear();
this._monitors.clear();
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
['workspaces', 'clients', 'monitors'].forEach(function (t) {
_this.notify(t);
});
node.nodes.map(n => this._handleTreeMessage(n));
break;
case 'output':
this._monitors.set(node.id, node);
if (node.active)
this._active.monitor.updateProperty('name', node.name);
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
this._active.monitor.update(node.id, node.name);
node.nodes.map(n => this._handleTreeMessage(n));
this.notify('monitors');
break;
case 'workspace':
this._workspaces.set(node.name, node);
// I think I'm missing something. There has to be a better way.
// eslint-disable-next-line no-case-declarations
var hasFocusedChild_1 = function (n) { return n.nodes.some(function (c) { return c.focused || hasFocusedChild_1(c); }); };
if (hasFocusedChild_1(node))
const hasFocusedChild =
(n) => n.nodes.some(c => c.focused || hasFocusedChild(c));
if (node.focused || hasFocusedChild(node))
this._active.workspace.update(node.id, node.name);
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
node.nodes.map(n => this._handleTreeMessage(n));
this.notify('workspaces');
break;
case 'con':
@@ -393,25 +385,16 @@ var Sway = /** @class */ (function (_super) {
this._active.client.updateProperty('id', node.id);
this._active.client.updateProperty('name', node.name);
this._active.client.updateProperty('class', node.shell === 'xwayland'
? ((_e = node.window_properties) === null || _e === void 0 ? void 0 : _e.class) || ''
: node.app_id);
? node.window_properties?.class || ''
: node.app_id,
);
}
node.nodes.map(function (n) { return _this._handleTreeMessage(n); });
node.nodes.map(n => this._handleTreeMessage(n));
this.notify('clients');
break;
}
};
return Sway;
}(service_js_1.default));
exports.Sway = Sway;
_d = Sway;
(function () {
service_js_1.default.register(_d, {}, {
'active': ['jsobject'],
'monitors': ['jsobject'],
'workspaces': ['jsobject'],
'clients': ['jsobject'],
});
})();
exports.sway = new Sway;
exports.default = exports.sway;
}
}
export const sway = new Sway;
export default sway;
+13 -11
View File
@@ -32,24 +32,26 @@ class TodoService extends Service {
return this._todoJson;
}
add(content) {
this._todoJson.push({ content, done: false });
_save() {
Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
.catch(print);
}
add(content) {
this._todoJson.push({ content, done: false });
this._save();
this.emit('updated');
}
check(index) {
this._todoJson[index].done = true;
Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
.catch(print);
this._save();
this.emit('updated');
}
uncheck(index) {
this._todoJson[index].done = false;
Utils.writeFile(JSON.stringify(this._todoJson), this._todoPath)
.catch(print);
this._save();
this.emit('updated');
}
@@ -63,17 +65,17 @@ class TodoService extends Service {
constructor() {
super();
this._todoPath = `${GLib.get_user_cache_dir()}/ags/user/todo.json`;
if (!fileExists(this._todoPath)) { // No? create file with empty array
try {
const fileContents = Utils.readFile(this._todoPath);
this._todoJson = JSON.parse(fileContents);
}
catch {
Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`);
Utils.exec(`touch ${this._todoPath}`);
Utils.writeFile("[]", this._todoPath).then(() => {
this._todoJson = JSON.parse(Utils.readFile(this._todoPath))
}).catch(print);
}
else {
const fileContents = Utils.readFile(this._todoPath);
this._todoJson = JSON.parse(fileContents);
}
}
}
+75
View File
@@ -0,0 +1,75 @@
const { Gdk, Gio, GLib } = imports.gi;
import Service from 'resource:///com/github/Aylur/ags/service.js';
import * as Utils from 'resource:///com/github/Aylur/ags/utils.js';
const { exec, execAsync } = Utils;
const clamp = (num, min, max) => Math.min(Math.max(num, min), max);
function fileExists(filePath) {
let file = Gio.File.new_for_path(filePath);
return file.query_exists(null);
}
class WallpaperService extends Service {
static {
Service.register(
this,
{ 'updated': [], },
);
}
_wallPath = '';
_wallJson = [];
_monitorCount = 1;
_save() {
Utils.writeFile(JSON.stringify(this._wallJson), this._wallPath)
.catch(print);
}
add(path) {
this._wallJson.push(path);
this._save();
this.emit('updated');
}
set(path, monitor = -1) {
this._monitorCount = Gdk.Display.get_default()?.get_n_monitors() || 1;
if (this._wallJson.length < this._monitorCount) this._wallJson[this._monitorCount - 1] = "";
if (monitor == -1)
this._wallJson.fill(path);
else
this._wallJson[monitor] = path;
this._save();
this.emit('updated');
}
get(monitor = 0) {
return this._wallJson[monitor];
}
constructor() {
super();
// How many screens?
this._monitorCount = Gdk.Display.get_default()?.get_n_monitors() || 1;
// Read config
this._wallPath = `${GLib.get_user_cache_dir()}/ags/user/wallpaper.json`;
try {
const fileContents = Utils.readFile(this._wallPath);
this._wallJson = JSON.parse(fileContents);
}
catch {
Utils.exec(`bash -c 'mkdir -p ${GLib.get_user_cache_dir()}/ags/user'`);
Utils.exec(`touch ${this._wallPath}`);
Utils.writeFile('[]', this._wallPath).then(() => {
this._wallJson = JSON.parse(Utils.readFile(this._wallPath))
}).catch(print);
}
}
}
// instance
const service = new WallpaperService();
// make it global for easy use with cli
globalThis['wallpaper'] = service;
export default service;