添加pin的雏形

This commit is contained in:
fofolee 2025-04-25 07:45:55 +08:00
parent 0d4f49fcf4
commit 6f1c98fb4a
7 changed files with 263 additions and 0 deletions

View File

@ -0,0 +1,30 @@
const { ipcRenderer } = require("electron");
// 等待 DOM 加载完成
document.addEventListener("DOMContentLoaded", () => {
let parentId = null;
let windowId = null;
let commandCode = null;
// 监听父窗口发来的配置
ipcRenderer.on("window-config", (event, config) => {
parentId = event.senderId;
windowId = config.windowId;
commandCode = config.commandCode;
// 设置主题
document.documentElement.setAttribute(
"data-theme",
config.isDark ? "dark" : "light"
);
// 设置图标
document.getElementById("command-icon").src = config.icon;
});
// 点击图标执行命令
document.querySelector(".pin-container").addEventListener("click", () => {
console.log("click", parentId, `pin-execute-${windowId}`);
ipcRenderer.sendTo(parentId, `pin-execute-${windowId}`, commandCode);
});
});

108
plugin/lib/pin/service.js Normal file
View File

@ -0,0 +1,108 @@
const { ipcRenderer } = require("electron");
const { createBrowserWindow } = utools;
const pinPath = "lib/pin/view.html";
const preloadPath = "lib/pin/controller.js";
// 存储所有pin窗口的信息
const pinWindows = new Map();
/**
* 创建pin窗口
* @param {object} commandInfo - 命令信息
* @param {object} position - 窗口位置 {x, y}
* @returns {Promise} 返回窗口对象
*/
const createPinWindow = (commandInfo, position = null) => {
return new Promise((resolve) => {
const windowOptions = {
width: 52,
height: 52,
transparent: true,
frame: false,
resizable: false,
skipTaskbar: true,
alwaysOnTop: true,
focusable: false,
movable: true,
webPreferences: {
preload: preloadPath,
devTools: utools.isDev(),
},
};
// 如果指定了位置,添加到选项中
if (position) {
windowOptions.x = position.x;
windowOptions.y = position.y;
}
// 创建窗口
const UBrowser = createBrowserWindow(pinPath, windowOptions, () => {
const windowId = UBrowser.webContents.id;
UBrowser.webContents.openDevTools({
mode: "undocked",
});
// 监听命令执行请求
ipcRenderer.once(`pin-execute-${windowId}`, (event, code) => {
// 执行命令
console.log("execute command", event, code, commandInfo);
});
// 保存窗口信息
pinWindows.set(commandInfo.features.code, {
window: UBrowser,
windowId,
position: UBrowser.getPosition(),
});
// 发送配置到子窗口
ipcRenderer.sendTo(windowId, "window-config", {
isDark: utools.isDarkColors(),
icon: commandInfo.features.icon,
commandCode: commandInfo.features.code,
windowId,
});
resolve(UBrowser);
});
});
};
/**
* 移除pin窗口
* @param {string} commandCode - 命令代码
*/
const removePinWindow = (commandCode) => {
const pinInfo = pinWindows.get(commandCode);
if (pinInfo) {
pinInfo.window.destroy();
pinWindows.delete(commandCode);
}
};
/**
* 获取所有pin窗口信息
* @returns {Map} pin窗口信息Map
*/
const getPinWindows = () => {
return pinWindows;
};
/**
* 恢复所有pin窗口
* @param {Array} pinnedCommands - pin命令列表
*/
const restorePinWindows = async (pinnedCommands) => {
for (const command of pinnedCommands) {
await createPinWindow(command.info, command.position);
}
};
module.exports = {
createPinWindow,
removePinWindow,
getPinWindows,
restorePinWindows,
};

40
plugin/lib/pin/style.css Normal file
View File

@ -0,0 +1,40 @@
body {
margin: 0;
padding: 0;
background: transparent;
overflow: hidden;
user-select: none;
}
.pin-container {
width: 52px;
height: 52px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.3s ease;
}
.pin-container:hover {
transform: scale(1.1);
}
.pin-icon {
width: 45px;
height: 45px;
overflow: hidden;
}
.pin-icon img {
width: 100%;
height: 100%;
object-fit: contain;
}
body[data-theme="dark"] .pin-icon {
background: rgba(57, 57, 57, 0.09);
border: 1px solid rgb(59 58 58 / 5%);
box-shadow: 0 1px 5px rgb(0 0 0 / 20%), 0 2px 2px rgb(0 0 0 / 14%),
0 3px 1px -2px rgb(69 67 67 / 12%);
}

15
plugin/lib/pin/view.html Normal file
View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Pin</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="pin-container">
<div class="pin-icon">
<img id="command-icon" src="" alt="command icon" />
</div>
</div>
</body>
</html>

View File

@ -20,6 +20,7 @@ const md5 = (input) => {
window.lodashM = require("./lib/lodashMini"); window.lodashM = require("./lib/lodashMini");
window.pinyinMatch = require("pinyin-match"); window.pinyinMatch = require("pinyin-match");
window.pinService = require("./lib/pin/service");
window.DOMPurify = DOMPurify; window.DOMPurify = DOMPurify;
const createTerminalCommand = require("./lib/createTerminalCommand"); const createTerminalCommand = require("./lib/createTerminalCommand");

View File

@ -122,6 +122,10 @@ export default defineComponent({
window.quickcommandHttpServer().run(this.nativeProfile.serverPort); window.quickcommandHttpServer().run(this.nativeProfile.serverPort);
console.log("Server Start..."); console.log("Server Start...");
} }
//
if (this.nativeProfile.pinnedCommands) {
window.pinService.restorePinWindows(this.nativeProfile.pinnedCommands);
}
}, },
enterPlugin(enter) { enterPlugin(enter) {
this.updateExp(); this.updateExp();

View File

@ -4,6 +4,20 @@
v-ripple v-ripple
:class="{ [`text-${disabledColor}`]: !isActivated, command: 1 }" :class="{ [`text-${disabledColor}`]: !isActivated, command: 1 }"
> >
<q-badge floating transparent style="z-index: 1000" v-if="isActivated">
<q-btn
flat
round
dense
:color="isPinned ? 'amber' : 'grey'"
icon="push_pin"
@click.stop="togglePin"
size="12px"
>
<q-tooltip>{{ isPinned ? "取消固定到桌面" : "固定到桌面" }}</q-tooltip>
</q-btn>
</q-badge>
<component <component
:is="currentLayout" :is="currentLayout"
:commandInfo="commandInfo" :commandInfo="commandInfo"
@ -56,6 +70,33 @@ export default {
"--icon-url": `url(${this.commandInfo.features.icon})`, "--icon-url": `url(${this.commandInfo.features.icon})`,
}; };
}, },
isPinned() {
return (
this.$root.nativeProfile.pinnedCommands?.some(
(cmd) => cmd.info.features.code === this.commandInfo.features.code
) || false
);
},
},
methods: {
async togglePin() {
if (this.isPinned) {
window.pinService.removePinWindow(this.commandInfo.features.code);
this.$root.nativeProfile.pinnedCommands =
this.$root.nativeProfile.pinnedCommands.filter(
(cmd) => cmd.info.features.code !== this.commandInfo.features.code
);
} else {
await window.pinService.createPinWindow(this.commandInfo);
if (!this.$root.nativeProfile.pinnedCommands) {
this.$root.nativeProfile.pinnedCommands = [];
}
this.$root.nativeProfile.pinnedCommands.push({
info: this.commandInfo,
position: utools.getCursorScreenPoint(),
});
}
},
}, },
}; };
</script> </script>
@ -80,4 +121,28 @@ export default {
box-shadow: 0 1px 5px rgb(0 0 0 / 20%), 0 2px 2px rgb(0 0 0 / 14%), box-shadow: 0 1px 5px rgb(0 0 0 / 20%), 0 2px 2px rgb(0 0 0 / 14%),
0 3px 1px -2px rgb(69 67 67 / 12%); 0 3px 1px -2px rgb(69 67 67 / 12%);
} }
.pin-icon {
opacity: 0;
transform: translateY(-5px);
visibility: hidden;
transition: all 0.35s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, opacity, visibility;
}
.q-card:hover .pin-icon {
opacity: 1;
transform: translateY(0);
visibility: visible;
backdrop-filter: blur(1px);
}
.pin-icon .q-btn {
transition: transform 0.35s cubic-bezier(0.68, -0.6, 0.32, 1.6);
will-change: transform;
}
.pin-icon .q-btn:hover {
transform: scale(1.15);
}
</style> </style>