feat: statusline support script
This commit is contained in:
@@ -10,6 +10,7 @@ export interface StatusLineModuleConfig {
|
|||||||
text: string;
|
text: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
background?: string;
|
background?: string;
|
||||||
|
scriptPath?: string; // 用于script类型的模块,指定要执行的Node.js脚本文件路径
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusLineThemeConfig {
|
export interface StatusLineThemeConfig {
|
||||||
@@ -132,11 +133,58 @@ function getColorCode(colorName: string): string {
|
|||||||
|
|
||||||
// 变量替换函数,支持{{var}}格式的变量替换
|
// 变量替换函数,支持{{var}}格式的变量替换
|
||||||
function replaceVariables(text: string, variables: Record<string, string>): string {
|
function replaceVariables(text: string, variables: Record<string, string>): string {
|
||||||
return text.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
|
return text.replace(/\{\{(\w+)\}\}/g, (_match, varName) => {
|
||||||
return variables[varName] || match;
|
return variables[varName] || "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 执行脚本并获取输出
|
||||||
|
async function executeScript(scriptPath: string, variables: Record<string, string>): Promise<string> {
|
||||||
|
try {
|
||||||
|
// 检查文件是否存在
|
||||||
|
await fs.access(scriptPath);
|
||||||
|
|
||||||
|
// 使用require动态加载脚本模块
|
||||||
|
const scriptModule = require(scriptPath);
|
||||||
|
|
||||||
|
// 如果导出的是函数,则调用它并传入变量
|
||||||
|
if (typeof scriptModule === 'function') {
|
||||||
|
const result = scriptModule(variables);
|
||||||
|
// 如果返回的是Promise,则等待它完成
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
return await result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果导出的是default函数,则调用它
|
||||||
|
if (scriptModule.default && typeof scriptModule.default === 'function') {
|
||||||
|
const result = scriptModule.default(variables);
|
||||||
|
// 如果返回的是Promise,则等待它完成
|
||||||
|
if (result instanceof Promise) {
|
||||||
|
return await result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果导出的是字符串,则直接返回
|
||||||
|
if (typeof scriptModule === 'string') {
|
||||||
|
return scriptModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果导出的是default字符串,则返回它
|
||||||
|
if (scriptModule.default && typeof scriptModule.default === 'string') {
|
||||||
|
return scriptModule.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 默认情况下返回空字符串
|
||||||
|
return "";
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`执行脚本 ${scriptPath} 时出错:`, error);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 默认主题配置 - 使用Nerd Fonts图标和美观配色
|
// 默认主题配置 - 使用Nerd Fonts图标和美观配色
|
||||||
const DEFAULT_THEME: StatusLineThemeConfig = {
|
const DEFAULT_THEME: StatusLineThemeConfig = {
|
||||||
modules: [
|
modules: [
|
||||||
@@ -490,9 +538,9 @@ export async function parseStatusLineData(input: StatusLineInput): Promise<strin
|
|||||||
|
|
||||||
// 根据风格渲染状态行
|
// 根据风格渲染状态行
|
||||||
if (isPowerline) {
|
if (isPowerline) {
|
||||||
return renderPowerlineStyle(theme, variables);
|
return await renderPowerlineStyle(theme, variables);
|
||||||
} else {
|
} else {
|
||||||
return renderDefaultStyle(theme, variables);
|
return await renderDefaultStyle(theme, variables);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 发生错误时返回空字符串
|
// 发生错误时返回空字符串
|
||||||
@@ -529,10 +577,10 @@ async function getProjectThemeConfigForStyle(style: string): Promise<StatusLineT
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 渲染默认风格的状态行
|
// 渲染默认风格的状态行
|
||||||
function renderDefaultStyle(
|
async function renderDefaultStyle(
|
||||||
theme: StatusLineThemeConfig,
|
theme: StatusLineThemeConfig,
|
||||||
variables: Record<string, string>
|
variables: Record<string, string>
|
||||||
): string {
|
): Promise<string> {
|
||||||
const modules = theme.modules || DEFAULT_THEME.modules;
|
const modules = theme.modules || DEFAULT_THEME.modules;
|
||||||
const parts: string[] = [];
|
const parts: string[] = [];
|
||||||
|
|
||||||
@@ -542,19 +590,30 @@ function renderDefaultStyle(
|
|||||||
const color = module.color ? getColorCode(module.color) : "";
|
const color = module.color ? getColorCode(module.color) : "";
|
||||||
const background = module.background ? getColorCode(module.background) : "";
|
const background = module.background ? getColorCode(module.background) : "";
|
||||||
const icon = module.icon || "";
|
const icon = module.icon || "";
|
||||||
const text = replaceVariables(module.text, variables);
|
|
||||||
|
|
||||||
// 如果text为空且不是usage类型,则跳过该模块
|
// 如果是script类型,执行脚本获取文本
|
||||||
if (!text && module.type !== "usage") {
|
let text = "";
|
||||||
|
if (module.type === "script" && module.scriptPath) {
|
||||||
|
text = await executeScript(module.scriptPath, variables);
|
||||||
|
} else {
|
||||||
|
text = replaceVariables(module.text, variables);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建显示文本
|
||||||
|
let displayText = "";
|
||||||
|
if (icon) {
|
||||||
|
displayText += `${icon} `;
|
||||||
|
}
|
||||||
|
displayText += text;
|
||||||
|
|
||||||
|
// 如果displayText为空,或者只有图标没有实际文本,则跳过该模块
|
||||||
|
if (!displayText || !text) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建模块字符串
|
// 构建模块字符串
|
||||||
let part = `${background}${color}`;
|
let part = `${background}${color}`;
|
||||||
if (icon) {
|
part += `${displayText}${COLORS.reset}`;
|
||||||
part += `${icon} `;
|
|
||||||
}
|
|
||||||
part += `${text}${COLORS.reset}`;
|
|
||||||
|
|
||||||
parts.push(part);
|
parts.push(part);
|
||||||
}
|
}
|
||||||
@@ -701,10 +760,10 @@ function segment(text: string, textFg: string, bgColor: string, nextBgColor: str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 渲染Powerline风格的状态行
|
// 渲染Powerline风格的状态行
|
||||||
function renderPowerlineStyle(
|
async function renderPowerlineStyle(
|
||||||
theme: StatusLineThemeConfig,
|
theme: StatusLineThemeConfig,
|
||||||
variables: Record<string, string>
|
variables: Record<string, string>
|
||||||
): string {
|
): Promise<string> {
|
||||||
const modules = theme.modules || POWERLINE_THEME.modules;
|
const modules = theme.modules || POWERLINE_THEME.modules;
|
||||||
const segments: string[] = [];
|
const segments: string[] = [];
|
||||||
|
|
||||||
@@ -714,11 +773,13 @@ function renderPowerlineStyle(
|
|||||||
const color = module.color || "white";
|
const color = module.color || "white";
|
||||||
const backgroundName = module.background || "";
|
const backgroundName = module.background || "";
|
||||||
const icon = module.icon || "";
|
const icon = module.icon || "";
|
||||||
const text = replaceVariables(module.text, variables);
|
|
||||||
|
|
||||||
// 如果text为空且不是usage类型,则跳过该模块
|
// 如果是script类型,执行脚本获取文本
|
||||||
if (!text && module.type !== "usage") {
|
let text = "";
|
||||||
continue;
|
if (module.type === "script" && module.scriptPath) {
|
||||||
|
text = await executeScript(module.scriptPath, variables);
|
||||||
|
} else {
|
||||||
|
text = replaceVariables(module.text, variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建显示文本
|
// 构建显示文本
|
||||||
@@ -728,6 +789,11 @@ function renderPowerlineStyle(
|
|||||||
}
|
}
|
||||||
displayText += text;
|
displayText += text;
|
||||||
|
|
||||||
|
// 如果displayText为空,或者只有图标没有实际文本,则跳过该模块
|
||||||
|
if (!displayText || !text) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 获取下一个模块的背景色(用于分隔符)
|
// 获取下一个模块的背景色(用于分隔符)
|
||||||
let nextBackground: string | null = null;
|
let nextBackground: string | null = null;
|
||||||
if (i < modules.length - 1) {
|
if (i < modules.length - 1) {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ const MODULE_TYPES = [
|
|||||||
{ label: "gitBranch", value: "gitBranch" },
|
{ label: "gitBranch", value: "gitBranch" },
|
||||||
{ label: "model", value: "model" },
|
{ label: "model", value: "model" },
|
||||||
{ label: "usage", value: "usage" },
|
{ label: "usage", value: "usage" },
|
||||||
|
{ label: "script", value: "script" },
|
||||||
];
|
];
|
||||||
|
|
||||||
// ANSI颜色代码映射
|
// ANSI颜色代码映射
|
||||||
@@ -675,6 +676,15 @@ export function StatusLineConfigDialog({
|
|||||||
color: "bright_magenta",
|
color: "bright_magenta",
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
case "script":
|
||||||
|
newModule = {
|
||||||
|
type: "script",
|
||||||
|
icon: "📜",
|
||||||
|
text: "Script Module",
|
||||||
|
color: "bright_cyan",
|
||||||
|
scriptPath: "",
|
||||||
|
};
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
newModule = { ...DEFAULT_MODULE, type: moduleType };
|
newModule = { ...DEFAULT_MODULE, type: moduleType };
|
||||||
}
|
}
|
||||||
@@ -920,6 +930,31 @@ export function StatusLineConfigDialog({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Script Path 输入框 - 仅在type为script时显示 */}
|
||||||
|
{selectedModule.type === "script" && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="module-script-path">
|
||||||
|
脚本路径
|
||||||
|
</Label>
|
||||||
|
<Input
|
||||||
|
id="module-script-path"
|
||||||
|
value={selectedModule.scriptPath || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
handleModuleChange(
|
||||||
|
selectedModuleIndex,
|
||||||
|
"scriptPath",
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="例如: /path/to/your/script.js"
|
||||||
|
/>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
输入Node.js脚本文件的绝对路径
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -135,6 +135,7 @@
|
|||||||
"gitBranch": "Git Branch",
|
"gitBranch": "Git Branch",
|
||||||
"model": "Model",
|
"model": "Model",
|
||||||
"usage": "Usage",
|
"usage": "Usage",
|
||||||
|
"script": "Script",
|
||||||
"background_none": "None",
|
"background_none": "None",
|
||||||
"color_black": "Black",
|
"color_black": "Black",
|
||||||
"color_red": "Red",
|
"color_red": "Red",
|
||||||
|
|||||||
@@ -135,6 +135,7 @@
|
|||||||
"gitBranch": "Git分支",
|
"gitBranch": "Git分支",
|
||||||
"model": "模型",
|
"model": "模型",
|
||||||
"usage": "使用情况",
|
"usage": "使用情况",
|
||||||
|
"script": "脚本",
|
||||||
"background_none": "无",
|
"background_none": "无",
|
||||||
"color_black": "黑色",
|
"color_black": "黑色",
|
||||||
"color_red": "红色",
|
"color_red": "红色",
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export interface StatusLineModuleConfig {
|
|||||||
text: string;
|
text: string;
|
||||||
color?: string;
|
color?: string;
|
||||||
background?: string;
|
background?: string;
|
||||||
|
scriptPath?: string; // 用于script类型的模块,指定要执行的Node.js脚本文件路径
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatusLineThemeConfig {
|
export interface StatusLineThemeConfig {
|
||||||
|
|||||||
Reference in New Issue
Block a user