@talex-touch/utils 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,94 @@
1
+ export enum ChannelType {
2
+ MAIN = "main",
3
+ PLUGIN = "plugin"
4
+ }
5
+
6
+ export enum DataCode {
7
+ SUCCESS = 200,
8
+ NETWORK_ERROR = 500,
9
+ ERROR = 100
10
+ }
11
+
12
+ export interface ITouchChannel {
13
+
14
+ /**
15
+ * Register a channel
16
+ * @description Register a channel, and return a function to cancel the registration
17
+ * @param type {@link ChannelType} The type of channel
18
+ * @param eventName {string} The name of event, must be unique in the channel {@link ChannelType
19
+ * @param callback {Function} The callback function
20
+ */
21
+ regChannel(type: ChannelType, eventName: string, callback: Function): () => void
22
+
23
+ /**
24
+ * Send a message to a channel
25
+ * @param type {@link ChannelType} The type of channel
26
+ * @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
27
+ * @param arg {any} The arguments of the message
28
+ */
29
+ send(type: ChannelType, eventName: string, arg?: any): Promise<any>
30
+ }
31
+
32
+ export interface ITouchClientChannel {
33
+
34
+ /**
35
+ * Register a channel
36
+ * @description Register a channel, and return a function to cancel the registration
37
+ * @param type {@link ChannelType} The type of channel
38
+ * @param eventName {string} The name of event, must be unique in the channel {@link ChannelType
39
+ * @param callback {Function} The callback function
40
+ */
41
+ regChannel(eventName: string, callback: Function): () => void
42
+
43
+ /**
44
+ * Send a message to a channel
45
+ * @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
46
+ * @param arg {any} The arguments of the message
47
+ */
48
+ send(eventName: string, arg?: any): Promise<any>
49
+
50
+ /**
51
+ * Send a message to a channel and get the response
52
+ * @param eventName {string} The name of event, must be unique in the channel {@link ChannelType}
53
+ * @param arg {any} The arguments of the message
54
+ */
55
+ sendSync(eventName: string, arg?: any): any
56
+ }
57
+
58
+ export interface RawChannelSyncData {
59
+ timeStamp: number
60
+ /**
61
+ * The sync-data timeout
62
+ * @description The unit is milliseconds, and set it to 0 to cancel it.
63
+ * @default 10000
64
+ */
65
+ timeout: 10000
66
+
67
+ /**
68
+ * The sync-data unique id
69
+ * @description To identify each sync-request.
70
+ */
71
+ id: string
72
+ }
73
+
74
+ export interface RawChannelHeaderData {
75
+ status: 'reply' | 'request'
76
+ type: ChannelType
77
+ _originData?: any
78
+ }
79
+
80
+ export interface RawChannelData {
81
+ name: string
82
+ header: RawChannelHeaderData
83
+ sync?: RawChannelSyncData
84
+ }
85
+
86
+ export interface RawStandardChannelData extends RawChannelData {
87
+ code: DataCode
88
+ data?: any
89
+ plugin?: string
90
+ }
91
+
92
+ export interface StandardChannelData extends RawStandardChannelData {
93
+ reply: (code: DataCode, data: any, options?: any) => void
94
+ }
@@ -0,0 +1,39 @@
1
+ export async function sleep(time: number) {
2
+ return new Promise(resolve => setTimeout(() => resolve(time), time))
3
+ }
4
+
5
+ export function anyStr2Num(str: string): BigInt {
6
+ const numbers = new Array<String>
7
+ let minium = Infinity
8
+
9
+ for (let index = 0; index < str.length; index++) {
10
+ const e = +str.charCodeAt(index);
11
+
12
+ numbers.push(("" + e).padStart(5, "0"))
13
+
14
+ if ( minium > e ) minium = e
15
+
16
+ }
17
+
18
+ // each number transform
19
+ numbers.forEach((e, i) => numbers[i] = (BigInt(+e) - BigInt(minium)).toString().padStart(2, "0"))
20
+
21
+ return BigInt(`${minium}000${BigInt(+numbers.join(""))}`)
22
+ }
23
+
24
+ export function num2anyStr(num: BigInt): string {
25
+ const strs = num.toString().split("000")
26
+ const baseNum = +strs[0]
27
+ const length = +strs[1].length / 2
28
+
29
+ let text = ''
30
+
31
+ for ( let i = 0; i < length; i++ ) {
32
+ const str = strs[1].slice(i * 2, i * 2 + 2)
33
+
34
+ // strs[1] = strs[1].replace(str, (BigInt(+str) + BigInt(baseNum)).toString().padStart(5, "0"))
35
+ text += String.fromCharCode(+str + baseNum)
36
+ }
37
+
38
+ return text
39
+ }
@@ -0,0 +1,87 @@
1
+ export type EventHandler = (event: ITouchEvent) => void;
2
+
3
+ export interface EventHandlerWrapper {
4
+ /**
5
+ * Event handler
6
+ */
7
+ handler: EventHandler;
8
+
9
+ /**
10
+ * Event type
11
+ * @see EventType
12
+ * @default EventType.PERSIST (must be, if implements)
13
+ */
14
+ type?: EventType;
15
+ }
16
+
17
+ export enum EventType {
18
+ PERSIST,
19
+ CONSUME,
20
+ }
21
+
22
+ export interface ITouchEvent<E = any> {
23
+ /**
24
+ * Event name
25
+ */
26
+ name: E;
27
+
28
+ /**
29
+ * Event data
30
+ */
31
+ data?: any;
32
+ }
33
+
34
+ export interface ITouchEventBus<E> {
35
+ map: Map<E, Set<EventHandlerWrapper>>;
36
+
37
+ /**
38
+ * Subscribe touch-app events (any kind of events extends from TouchEvent)
39
+ * @param event EventName (extends from TouchEvent)
40
+ * @param handler Event handler (extends from EventHandler)
41
+ * @returns true if the event was added, otherwise false
42
+ */
43
+ on(event: E, handler: EventHandler): boolean;
44
+
45
+ /**
46
+ * Subscribe touch-app events (any kind of events extends from TouchEvent)
47
+ * @param event EventName (extends from TouchEvent)
48
+ * @param handler Event handler (extends from EventHandler)
49
+ * @returns true if the event was added, otherwise false
50
+ */
51
+ once(event: E, handler: EventHandler): boolean;
52
+
53
+ /**
54
+ * UnSubscribe touch-app events (any kind of events extends from TouchEvent)
55
+ * @param event EventName (extends from TouchEvent)
56
+ * @param handler Event handler (extends from EventHandler)
57
+ * @returns true if the event was removed, otherwise false
58
+ * @example
59
+ * ```ts
60
+ * const handler = (event: TouchEvent) => {
61
+ * console.log(event)
62
+ * }
63
+ */
64
+ off<T extends ITouchEvent>(event: E, handler: EventHandler): boolean;
65
+
66
+ /**
67
+ * UnSubscribe touch-app events all matched (any kind of events extends from TouchEvent)
68
+ * @param event EventName (extends from TouchEvent)
69
+ * @returns true if the event was added, otherwise false
70
+ */
71
+ offAll(event: E): boolean;
72
+
73
+ /**
74
+ * UnSubscribe touch-app events all matched (any kind of events extends from TouchEvent)
75
+ * @param event EventName (extends from TouchEvent)
76
+ * @param handler Event handler (extends from EventHandler)
77
+ * @returns true if the event was added, otherwise false
78
+ */
79
+ off<T extends ITouchEvent>(event: E, handler: EventHandler): boolean;
80
+
81
+ /**
82
+ * Emit touch-app events (any kind of events extends from TouchEvent)
83
+ * @param event EventName (extends from TouchEvent)
84
+ * @param data Event data (extends from TouchEvent)
85
+ */
86
+ emit<T extends ITouchEvent<E>>(event: E, data: T): void;
87
+ }
@@ -0,0 +1,38 @@
1
+ import fse from "fs-extra";
2
+ import path from "path";
3
+ import fs from "fs";
4
+
5
+ export function genFileTree(basePath: string, depth: number = 0, options = {
6
+ maxDepth: 1,
7
+ ignores: [
8
+ "node_modules",
9
+ ".git",
10
+ ".vscode",
11
+ ".idea",
12
+ ".github",
13
+ ".gitignore"
14
+ ]
15
+ }) {
16
+ if (depth > options.maxDepth) return;
17
+ const name = path.basename(basePath);
18
+
19
+ if (options.ignores.includes(name)) return;
20
+
21
+ if (depth === 0) {
22
+ console.log("Touch tree helper: ");
23
+ console.log(` > ${name}:`);
24
+ }
25
+
26
+
27
+ fse.readdirSync(basePath).forEach((file) => {
28
+ const fullPath = path.join(basePath, file);
29
+ const stat = fs.statSync(fullPath);
30
+
31
+ if (stat.isDirectory()) {
32
+ console.log(` ├${"─".repeat(depth + 2)} ${file}:`);
33
+ genFileTree(fullPath, depth + 1);
34
+ } else {
35
+ console.log(` ├${"─".repeat(depth * 2 )} ${file}`);
36
+ }
37
+ });
38
+ }
package/index.ts ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "main": "./index.ts",
3
+ "name": "@talex-touch/utils",
4
+ "author": "TalexDreamSoul",
5
+ "module": "./index.ts",
6
+ "license": "MPL-2.0",
7
+ "private": false,
8
+ "version": "1.0.0",
9
+ "scripts": {
10
+ "publish": "npm publish --access public"
11
+ },
12
+ "keywords": [
13
+ "vue",
14
+ "electron",
15
+ "talex-touch"
16
+ ],
17
+ "repository": {
18
+ "url": "https://github.com/talex-touch/talex-touch.git",
19
+ "type": "git"
20
+ },
21
+ "description": "TalexTouch series utils",
22
+ "dependencies": {
23
+ "vue": "^3.2.47"
24
+ },
25
+ "peerDependencies": {
26
+ "vue": "^3.2.47",
27
+ "electron": "^24.4.0"
28
+ }
29
+ }
@@ -0,0 +1,182 @@
1
+ const { ipcRenderer, IpcMainEvent } = require("electron");
2
+ import {
3
+ ChannelType,
4
+ DataCode,
5
+ ITouchClientChannel,
6
+ RawChannelSyncData,
7
+ RawStandardChannelData,
8
+ StandardChannelData,
9
+ } from "./../channel";
10
+
11
+ class TouchChannel implements ITouchClientChannel {
12
+ channelMap: Map<string, Function[]> = new Map();
13
+
14
+ pendingMap: Map<string, Function> = new Map();
15
+
16
+ plugin: string;
17
+
18
+ constructor(pluginName: string) {
19
+ this.plugin = pluginName;
20
+
21
+ ipcRenderer.on("@main-process-message", this.__handle_main.bind(this));
22
+ }
23
+
24
+ __parse_raw_data(e, arg): RawStandardChannelData | null {
25
+ // console.log("Raw data: ", arg, e);
26
+ if (arg) {
27
+ const { name, header, code, plugin, data, sync } = arg;
28
+
29
+ if (header) {
30
+ return {
31
+ header: {
32
+ status: header.status || "request",
33
+ type: ChannelType.PLUGIN,
34
+ _originData: arg,
35
+ },
36
+ sync,
37
+ code,
38
+ data,
39
+ plugin,
40
+ name: name as string,
41
+ };
42
+ }
43
+ }
44
+
45
+ console.error(e, arg);
46
+ return null;
47
+ // throw new Error("Invalid message!");
48
+ }
49
+
50
+ __handle_main(e: typeof IpcMainEvent, arg: any): any {
51
+ const rawData = this.__parse_raw_data(e, arg);
52
+ if ( !rawData ) return
53
+
54
+ if ( rawData.header.status === 'reply' && rawData.sync ) {
55
+ const { id } = rawData.sync;
56
+
57
+ return this.pendingMap.get(id)?.(rawData);
58
+ }
59
+
60
+ if ( rawData.plugin !== this.plugin ) return
61
+
62
+ this.channelMap.get(rawData.name)?.forEach((func) => {
63
+ const handInData: StandardChannelData = {
64
+ reply: (code: DataCode, data: any, options: any) => {
65
+ e.sender.send(
66
+ "@main-process-message",
67
+ this.__parse_sender(code, rawData, data, rawData.sync)
68
+ );
69
+ },
70
+ ...rawData,
71
+ };
72
+
73
+ const res = func(handInData);
74
+
75
+ if (res && res instanceof Promise) return;
76
+
77
+ handInData.reply(DataCode.SUCCESS, res);
78
+ });
79
+ }
80
+
81
+ __parse_sender(
82
+ code: DataCode,
83
+ rawData: RawStandardChannelData,
84
+ data: any,
85
+ sync?: RawChannelSyncData
86
+ ): RawStandardChannelData {
87
+ return {
88
+ code,
89
+ data,
90
+ sync: !sync
91
+ ? undefined
92
+ : {
93
+ timeStamp: new Date().getTime(),
94
+ // reply sync timeout should follow the request timeout, unless user set it.
95
+ timeout: sync.timeout,
96
+ id: sync.id,
97
+ },
98
+ name: rawData.name,
99
+ header: {
100
+ status: "reply",
101
+ type: rawData.header.type,
102
+ _originData: rawData.header._originData,
103
+ },
104
+ };
105
+ }
106
+
107
+ regChannel(
108
+ eventName: string,
109
+ callback: Function
110
+ ): () => void {
111
+ const listeners = this.channelMap.get(eventName) || [];
112
+
113
+ if (!listeners.includes(callback)) {
114
+ listeners.push(callback);
115
+ } else return () => void 0;
116
+
117
+ this.channelMap.set(eventName, listeners);
118
+
119
+ return () => {
120
+ const index = listeners.indexOf(callback);
121
+
122
+ if (index !== -1) {
123
+ listeners.splice(index, 1);
124
+ }
125
+ };
126
+ }
127
+
128
+ send(eventName: string, arg: any): Promise<any> {
129
+ const uniqueId = `${new Date().getTime()}#${eventName}@${Math.random().toString(
130
+ 12
131
+ )}`;
132
+
133
+ const data = {
134
+ code: DataCode.SUCCESS,
135
+ data: arg,
136
+ sync: {
137
+ timeStamp: new Date().getTime(),
138
+ timeout: 10000,
139
+ id: uniqueId,
140
+ },
141
+ name: eventName,
142
+ plugin: this.plugin,
143
+ header: {
144
+ status: "request",
145
+ type: ChannelType.PLUGIN,
146
+ },
147
+ } as RawStandardChannelData;
148
+
149
+ return new Promise((resolve) => {
150
+
151
+ ipcRenderer.send("@main-process-message", data);
152
+
153
+ this.pendingMap.set(uniqueId, (res) => {
154
+ this.pendingMap.delete(uniqueId);
155
+
156
+ resolve(res.data);
157
+ });
158
+ });
159
+ }
160
+
161
+ sendSync(eventName: string, arg?: any): any {
162
+ const data = {
163
+ code: DataCode.SUCCESS,
164
+ data: arg,
165
+ name: eventName,
166
+ plugin: this.plugin,
167
+ header: {
168
+ status: "request",
169
+ type: ChannelType.PLUGIN,
170
+ },
171
+ } as RawStandardChannelData;
172
+
173
+ const res = this.__parse_raw_data(null, ipcRenderer.sendSync("@main-process-message", data))!
174
+
175
+ if ( res.header.status === 'reply' ) return res.data;
176
+
177
+ return res;
178
+
179
+ }
180
+ }
181
+
182
+ export const touchChannel: ITouchClientChannel = window['$channel'] = new TouchChannel(window.$plugin.name);
@@ -0,0 +1,56 @@
1
+ export enum PluginStatus {
2
+ DISABLED,
3
+ DISABLING,
4
+
5
+ CRASHED,
6
+
7
+ ENABLED,
8
+ ACTIVE,
9
+
10
+ LOADING,
11
+ LOADED
12
+ }
13
+
14
+ export interface IPluginIcon {
15
+ type: string | 'remixicon'
16
+ value: any
17
+ }
18
+
19
+ export interface IPluginBaseInfo {
20
+ name: string
21
+ readme: string
22
+ version: string
23
+ desc: string
24
+ icon: IPluginIcon
25
+ }
26
+
27
+ export interface IPluginDev {
28
+ enable: boolean
29
+ address: string
30
+ }
31
+
32
+ export interface IPluginWebview {
33
+ }
34
+
35
+ export interface ITouchPlugin extends IPluginBaseInfo {
36
+ dev: IPluginDev
37
+ webViewInit: boolean
38
+ webview: IPluginWebview
39
+
40
+ get status(): PluginStatus
41
+ set status(v: PluginStatus)
42
+
43
+ enable(): Promise<boolean>
44
+ disable(): Promise<boolean>
45
+ }
46
+
47
+ export interface IPluginManager {
48
+ plugins: Map<string, ITouchPlugin>
49
+ active: string | null
50
+ pluginPath: string
51
+
52
+ setActivePlugin(pluginName: string): boolean
53
+
54
+ loadPlugin(pluginName: string): Promise<boolean>
55
+ unloadPlugin(pluginName: string): Promise<boolean>
56
+ }
@@ -0,0 +1,34 @@
1
+ import { touchChannel } from "./channel";
2
+
3
+ // window type
4
+ declare global {
5
+ export interface Window {
6
+ $plugin: {
7
+ name: string;
8
+ path: Object;
9
+ };
10
+ $send: (type: string, data: any) => void;
11
+ $sendSync: (type: string, data: any) => Promise<any>;
12
+ $regChannel: (type: string, callback: Function) => void;
13
+ $crash: (message: string, extraData: any) => void;
14
+ $config: {
15
+ themeStyle: any;
16
+ };
17
+ }
18
+ }
19
+
20
+ export function init(window: Window) {
21
+ const plugin = window.$plugin;
22
+ if (!plugin)
23
+ throw new Error("Plugin has a fatal error! Please check your plugin!");
24
+
25
+ window.$crash = function(message, extraData) {
26
+ window.$send("crash", { message, ...extraData });
27
+ };
28
+ }
29
+
30
+ export function initBridge(window: Window) {
31
+ window.$send = touchChannel.send;
32
+ window.$sendSync = touchChannel.sendSync;
33
+ window.$regChannel = touchChannel.regChannel;
34
+ }
@@ -0,0 +1,55 @@
1
+ import { computed, customRef } from 'vue'
2
+
3
+ export function useModelWrapper(props: any, emit: any, name = 'modelValue') {
4
+ return computed({
5
+ get: () => props[name],
6
+ set: (value) => emit(`update:${name}`, value)
7
+ })
8
+ }
9
+
10
+ export function throttleRef(value: any, time: number) {
11
+
12
+ let ts = 0
13
+
14
+ return customRef((track, trigger) => {
15
+ return {
16
+ get() {
17
+ track()
18
+ return value
19
+ },
20
+ set(newValue) {
21
+
22
+ if( new Date().getTime() - ts < time ) return
23
+
24
+ value = newValue
25
+ track()
26
+ trigger()
27
+ ts = new Date().getTime()
28
+ }
29
+ }
30
+ })
31
+
32
+ }
33
+
34
+ export function debounceRef(value: any, delay: number) {
35
+
36
+ let timer: any
37
+
38
+ return customRef((track, trigger) => {
39
+ return {
40
+ get() {
41
+ track()
42
+ return value
43
+ },
44
+ set(newValue) {
45
+ clearTimeout(timer)
46
+ timer = setTimeout(() => {
47
+ value = newValue
48
+ track()
49
+ trigger()
50
+ }, delay)
51
+ }
52
+ }
53
+ })
54
+
55
+ }