indusy 2024-06-12 20:24:24 +08:00
parent df448b68af
commit 82077fc12a
23 changed files with 2464 additions and 0 deletions

1
define.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export {}

37
package.json Normal file
View File

@ -0,0 +1,37 @@
{
"name": "gnome-shell-extension-clash-indicator",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"prebuild": "rimraf dist",
"build": "rollup -c",
"build:dev": "rollup -c --environment DEBUG",
"test": "sh test.sh",
"push": "sh push.sh"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@9.3.0+sha512.ee7b93e0c2bd11409c6424f92b866f31d3ea1bef5fbe47d3c7500cdc3c9668833d2e55681ad66df5b640c61fa9dc25d546efa54d76d7f8bf54b13614ac293631",
"devDependencies": {
"@girs/clutter-14": "14.0.0-4.0.0-beta.5",
"@girs/gio-2.0": "2.78.0-3.3.0",
"@girs/gjs": "^3.3.0",
"@girs/glib-2.0": "2.78.0-3.3.0",
"@girs/gnome-shell": "46.0.0-beta8",
"@girs/gobject-2.0": "2.78.0-3.3.0",
"@girs/gtk-4.0": "4.12.5-3.3.0",
"@girs/soup-3.0": "3.4.4-3.3.0",
"@girs/st-14": "14.0.0-4.0.0-beta.5",
"@rollup/plugin-commonjs": "^26.0.1",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"rimraf": "^5.0.7",
"rollup": "^4.18.0",
"rollup-plugin-copy": "^3.5.0",
"rollup-plugin-tsconfig-paths": "^1.5.2",
"rollup-plugin-typescript2": "^0.36.0"
}
}

1994
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

3
push.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/sh -e
ln -s ${PWD}/dist/ ${HOME}/.local/share/gnome-shell/extensions/clash-indicator@indusy.gmail.com

68
rollup.config.mjs Normal file
View File

@ -0,0 +1,68 @@
// @ts-check
import { defineConfig } from "rollup";
import commonjs from "@rollup/plugin-commonjs";
import nodeResolve from "@rollup/plugin-node-resolve";
import json from "@rollup/plugin-json";
import terser from "@rollup/plugin-terser";
import typescript from "rollup-plugin-typescript2";
import copy from "rollup-plugin-copy";
import tsconfigPaths from 'rollup-plugin-tsconfig-paths'
/** @type {(options: {matches: RegExp[]}) => import("rollup").Plugin} */
const excludeImports = (options) => {
const matches = options.matches;
return {
name: "exclude-imports-plugin",
resolveId(source, importer) {
if (importer) {
if (matches.some((r) => r.test(source))) {
return {
id: source,
external: true,
};
}
}
return null;
},
};
};
const config = defineConfig(() => {
const commonPlugs = [
nodeResolve(),
commonjs(),
json(),
typescript({ check: false }),
excludeImports({
matches: [/^gi:/, /^resource:/],
}),
// @ts-ignore
].concat(process.env.DEBUG ? null : terser(), tsconfigPaths());
/** @type {import('rollup').RollupOptions} */
const extensionModule = {
input: "src/extension.ts",
output: {
name: "[name].js",
dir: "dist",
format: "es",
},
plugins: [
...commonPlugs,
copy({
targets: [
{
src: "src/metadata.json",
dest: "dist",
},
],
}),
],
};
return [extensionModule];
});
export default config;

View File

@ -0,0 +1,10 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { registerGObjectClass } from "../utils/gjs";
@registerGObjectClass
export class AllowLAN extends PopupMenu.PopupMenuItem {
constructor() {
super("允许局域网连接");
}
}

View File

@ -0,0 +1,47 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { GLib } from "@girs/glib-2.0";
import { Gio } from "@girs/gio-2.0";
import * as Main from "@girs/gnome-shell/ui/main";
import { registerGObjectClass } from "../utils/gjs";
import { ExtensionMetadata } from "@girs/gnome-shell/extensions/extension";
import { getCoreVersion } from "../core";
@registerGObjectClass
export class Configure extends PopupMenu.PopupSubMenuMenuItem {
constructor(options: { metadata: ExtensionMetadata }) {
super("配置");
const separator = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addAction("config", () => {});
this.menu.addMenuItem(separator);
this.menu.addAction("打开配置文件夹", () => {
const file = Gio.File.new_for_path(
`${GLib.get_user_data_dir()}/${options.metadata.uuid}`
);
const appInfo = Gio.AppInfo.get_default_for_type(
file
.query_info(
"standard::content-type",
Gio.FileQueryInfoFlags.NONE,
null
)
.get_content_type()!,
false
);
if (appInfo) {
appInfo.launch([file], null);
}
});
this.menu.addAction("托管配置", () => {});
const versionIt = new PopupMenu.PopupMenuItem('点击查看版本号')
versionIt.connect('activate', () => {
const version = getCoreVersion();
log(version)
})
this.menu.addMenuItem(versionIt)
}
}

12
src/components/enabler.ts Normal file
View File

@ -0,0 +1,12 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import * as Main from "@girs/gnome-shell/ui/main";
import { registerGObjectClass } from "../utils/gjs";
import { fetch } from "../utils/fetch";
@registerGObjectClass
export class Enabler extends PopupMenu.PopupMenuItem {
constructor() {
super("启用");
}
}

10
src/components/exit.ts Normal file
View File

@ -0,0 +1,10 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { registerGObjectClass } from "../utils/gjs";
@registerGObjectClass
export class Exit extends PopupMenu.PopupMenuItem {
constructor() {
super("退出");
}
}

View File

@ -0,0 +1,58 @@
import * as PanelMenu from "@girs/gnome-shell/ui/panelMenu";
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { St } from "@girs/st-14";
import { registerGObjectClass } from "../utils/gjs";
import { Enabler } from "./enabler";
import { Mode } from "./mode";
import { ProxyEnv } from "./proxy-env";
import { SystemProxy } from "./system-proxy";
import { StartAtBoot } from "./start-at-boot";
import { AllowLAN } from "./allow-lan";
import { Configure } from "./configure";
import { Settings } from "./settings";
import { ExtensionMetadata } from "@girs/gnome-shell/extensions/extension";
@registerGObjectClass
export class Indicator extends PanelMenu.Button {
metadata: ExtensionMetadata;
constructor(options: { metadata: ExtensionMetadata }) {
super(0.0, _("Clash Indicator"));
this.metadata = options.metadata;
this.add_child(
new St.Icon({
icon_name: "face-smile-symbolic",
style_class: "system-status-icon",
})
);
this.menu = this.menu as PopupMenu.PopupMenu
const enabler = new Enabler();
this.menu.addMenuItem(enabler);
const mode = new Mode();
this.menu.addMenuItem(mode);
const proxyEnv = new ProxyEnv();
this.menu.addMenuItem(proxyEnv);
const systemProxy = new SystemProxy();
this.menu.addMenuItem(systemProxy);
const startAtBoot = new StartAtBoot();
this.menu.addMenuItem(startAtBoot);
const allowLAN = new AllowLAN();
this.menu.addMenuItem(allowLAN);
const settings = new Settings();
this.menu.addMenuItem(settings);
const configure = new Configure({
metadata: this.metadata,
});
this.menu.addMenuItem(configure);
}
}

21
src/components/mode.ts Normal file
View File

@ -0,0 +1,21 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { registerGObjectClass } from "../utils/gjs";
@registerGObjectClass
export class Mode extends PopupMenu.PopupSubMenuMenuItem {
constructor() {
super("出站模式");
this.label.text = "出站模式(规则)";
this.menu.addAction("规则模式", () => {
globalThis.log("修改为规则模式");
});
this.menu.addAction("全局模式", () => {
globalThis.log("修改为全局模式");
});
this.menu.addAction("直连模式", () => {
globalThis.log("修改为直连模式");
});
}
}

View File

@ -0,0 +1,14 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { registerGObjectClass } from "../utils/gjs";
@registerGObjectClass
export class ProxyEnv extends PopupMenu.PopupMenuItem {
constructor() {
super("复制环境变量");
this.connect("activate", () => {
globalThis.log("复制环境变量");
});
}
}

View File

@ -0,0 +1,10 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { registerGObjectClass } from "../utils/gjs";
@registerGObjectClass
export class Settings extends PopupMenu.PopupMenuItem {
constructor() {
super("打开设置面板");
}
}

View File

@ -0,0 +1,10 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { registerGObjectClass } from "../utils/gjs";
@registerGObjectClass
export class StartAtBoot extends PopupMenu.PopupMenuItem {
constructor() {
super("开机启动");
}
}

View File

@ -0,0 +1,10 @@
import * as PopupMenu from "@girs/gnome-shell/ui/popupMenu";
import { registerGObjectClass } from "../utils/gjs";
@registerGObjectClass
export class SystemProxy extends PopupMenu.PopupMenuItem {
constructor() {
super("系统代理");
}
}

9
src/core/index.ts Normal file
View File

@ -0,0 +1,9 @@
import { fetch } from "../utils/fetch";
function startClash() {}
export function getCoreVersion() {
const dataText = fetch.get('/version')
const {version} = JSON.parse(dataText || '{}')
return version
}

60
src/extension.ts Normal file
View File

@ -0,0 +1,60 @@
/* extension.js
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
import {
Extension,
ExtensionMetadata,
gettext as _,
} from "@girs/gnome-shell/extensions/extension";
import { GLib } from "@girs/glib-2.0";
import * as Main from "@girs/gnome-shell/ui/main";
import { Indicator } from "./components/indicator";
export default class ClashIndicatorExtension extends Extension {
private indicator!: Indicator | null;
constructor(metadata: ExtensionMetadata) {
super(metadata);
this.init();
}
init() {
const dataDir = GLib.build_filenamev([
GLib.get_user_data_dir(),
this.metadata.uuid,
]);
log(dataDir);
if (!GLib.file_test(dataDir, GLib.FileTest.EXISTS)) {
GLib.mkdir_with_parents(dataDir, 0o755);
log(dataDir);
}
}
enable() {
this.indicator = new Indicator({
metadata: this.metadata,
});
Main.panel.addToStatusArea(this.uuid, this.indicator);
}
disable() {
this.indicator?.destroy();
this.indicator = null;
}
}

9
src/metadata.json Normal file
View File

@ -0,0 +1,9 @@
{
"name": "Clash Indicator",
"description": "A clash user interface.",
"uuid": "clash-indicator@indusy.gmail.com",
"shell-version": [
"46"
]
}

0
src/stylesheet.css Normal file
View File

25
src/utils/fetch.ts Normal file
View File

@ -0,0 +1,25 @@
import { Soup } from "@girs/soup-3.0";
import { GLib } from "@girs/glib-2.0";
const baseUrl = "http://localhost:9097";
function get(url: string) {
const session = new Soup.Session();
const message = Soup.Message.new("GET", baseUrl + url);
try {
const dataBytes = session.send_and_read(message, null);
const textDecoder = new TextDecoder();
const dataText = textDecoder.decode(dataBytes.get_data()!);
dataBytes.get_data()?.forEach((x) => {
log(x);
});
return dataText;
} catch (error) {}
}
export const fetch = {
get,
};

29
src/utils/gjs.ts Normal file
View File

@ -0,0 +1,29 @@
import GObject from "@girs/gobject-2.0";
export function registerGObjectClass<
K,
T extends {
metaInfo?: GObject.MetaInfo<any, any, any>;
new (...params: any[]): K;
}
>(target: T) {
if (Object.prototype.hasOwnProperty.call(target, "metaInfo")) {
// @ts-ignore
return GObject.registerClass<K, T>(
target.metaInfo!,
target
) as typeof target;
} else {
// @ts-ignore
return GObject.registerClass<K, T>(target) as typeof target;
}
}
export interface SignalRepresentationType<A extends any[]> {
param_types: A;
accumulator: number;
}
export type SignalsDefinition<T extends string> = {
[key in T]: SignalRepresentationType<any> | Record<string, never>;
};

9
test.sh Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh -e
export G_MESSAGES_DEBUG=
export MUTTER_DEBUG_DUMMY_MODE_SPECS=1366x768
export SHELL_DEBUG=all
dbus-run-session -- \
gnome-shell --nested \
--wayland

18
tsconfig.json Normal file
View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"forceConsistentCasingInFileNames": true,
"strict": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"lib": ["ESNext", "DOM"],
"types": [
"@girs/gjs",
"@girs/gtk-4.0",
"@girs/gnome-shell/extensions/global"
]
},
"include": ["src", "define.d.ts"]
}