Skip to main content
Glama
ExcelHighlighter.ts8.41 kB
import { Change, HighlightOptions } from '../types'; export interface ExcelHighlight { id: string; range: string; type: 'insert' | 'delete' | 'modify' | 'format'; color: string; comment?: string; timestamp: number; } export class ExcelHighlighter { private highlights = new Map<string, ExcelHighlight>(); private colorMap = { insert: '#E8F5E9', delete: '#FFEBEE', modify: '#FFF9C4', format: '#E3F2FD' }; async highlightCell(range: string, change: Change, options?: HighlightOptions): Promise<void> { const highlight: ExcelHighlight = { id: change.id, range, type: change.type, color: options?.color || this.colorMap[change.type], comment: change.description, timestamp: Date.now() }; this.highlights.set(change.id, highlight); await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const cellRange = worksheet.getRange(range); // 设置背景色 cellRange.format.fill.color = highlight.color; // 添加边框 cellRange.format.borders.getItem('EdgeTop').style = Excel.BorderLineStyle.thin; cellRange.format.borders.getItem('EdgeBottom').style = Excel.BorderLineStyle.thin; cellRange.format.borders.getItem('EdgeLeft').style = Excel.BorderLineStyle.thin; cellRange.format.borders.getItem('EdgeRight').style = Excel.BorderLineStyle.thin; // 添加注释 if (highlight.comment) { worksheet.comments.add(range, highlight.comment, 'AI Assistant'); } await context.sync(); }); } async removeHighlight(changeId: string): Promise<void> { const highlight = this.highlights.get(changeId); if (!highlight) return; await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const cellRange = worksheet.getRange(highlight.range); // 清除格式 cellRange.format.fill.clear(); cellRange.format.borders.getItem('EdgeTop').style = Excel.BorderLineStyle.none; cellRange.format.borders.getItem('EdgeBottom').style = Excel.BorderLineStyle.none; cellRange.format.borders.getItem('EdgeLeft').style = Excel.BorderLineStyle.none; cellRange.format.borders.getItem('EdgeRight').style = Excel.BorderLineStyle.none; // 删除注释 const comments = worksheet.comments.load('items'); await context.sync(); for (const comment of comments.items) { if (comment.content.includes(changeId)) { comment.delete(); } } await context.sync(); }); this.highlights.delete(changeId); } async highlightRange(startRange: string, endRange: string, change: Change): Promise<void> { const rangeAddress = `${startRange}:${endRange}`; await this.highlightCell(rangeAddress, change); } async highlightColumn(column: string, change: Change): Promise<void> { await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const usedRange = worksheet.getUsedRange(); usedRange.load('rowCount'); await context.sync(); const rangeAddress = `${column}1:${column}${usedRange.rowCount}`; await this.highlightCell(rangeAddress, change); }); } async highlightRow(row: number, change: Change): Promise<void> { await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const usedRange = worksheet.getUsedRange(); usedRange.load('columnCount'); await context.sync(); const endColumn = String.fromCharCode(64 + usedRange.columnCount); const rangeAddress = `A${row}:${endColumn}${row}`; await this.highlightCell(rangeAddress, change); }); } async clearAllHighlights(): Promise<void> { await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const usedRange = worksheet.getUsedRange(); // 清除所有格式 usedRange.format.fill.clear(); usedRange.format.borders.getItem('EdgeTop').style = Excel.BorderLineStyle.none; usedRange.format.borders.getItem('EdgeBottom').style = Excel.BorderLineStyle.none; usedRange.format.borders.getItem('EdgeLeft').style = Excel.BorderLineStyle.none; usedRange.format.borders.getItem('EdgeRight').style = Excel.BorderLineStyle.none; // 删除所有注释 const comments = worksheet.comments.load('items'); await context.sync(); for (const comment of comments.items) { comment.delete(); } await context.sync(); }); this.highlights.clear(); } async getHighlightedCells(): Promise<ExcelHighlight[]> { return Array.from(this.highlights.values()); } async updateHighlightColor(changeId: string, color: string): Promise<void> { const highlight = this.highlights.get(changeId); if (!highlight) return; highlight.color = color; await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const cellRange = worksheet.getRange(highlight.range); cellRange.format.fill.color = color; await context.sync(); }); } async blinkHighlight(changeId: string, duration: number = 2000): Promise<void> { const highlight = this.highlights.get(changeId); if (!highlight) return; const originalColor = highlight.color; const blinkColor = '#FFFF00'; // 黄色闪烁 await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const cellRange = worksheet.getRange(highlight.range); // 闪烁效果 const blinkInterval = setInterval(async () => { cellRange.format.fill.color = cellRange.format.fill.color === originalColor ? blinkColor : originalColor; await context.sync(); }, 200); // 停止闪烁 setTimeout(() => { clearInterval(blinkInterval); cellRange.format.fill.color = originalColor; context.sync(); }, duration); }); } async addConditionalHighlight(range: string, condition: string, color: string): Promise<string> { const highlightId = `conditional_${Date.now()}`; await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const cellRange = worksheet.getRange(range); const conditionalFormat = cellRange.conditionalFormats.add(Excel.ConditionalFormatType.custom); conditionalFormat.custom.rule.formula = condition; conditionalFormat.custom.format.fill.color = color; await context.sync(); }); return highlightId; } async removeConditionalHighlight(range: string): Promise<void> { await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const cellRange = worksheet.getRange(range); cellRange.conditionalFormats.clearAll(); await context.sync(); }); } getHighlightStats(): { total: number; byType: Record<string, number>; oldest: number | null; newest: number | null; } { const highlights = Array.from(this.highlights.values()); const byType: Record<string, number> = {}; for (const highlight of highlights) { byType[highlight.type] = (byType[highlight.type] || 0) + 1; } const timestamps = highlights.map(h => h.timestamp); return { total: highlights.length, byType, oldest: timestamps.length > 0 ? Math.min(...timestamps) : null, newest: timestamps.length > 0 ? Math.max(...timestamps) : null }; } async exportHighlights(): Promise<string> { const highlights = Array.from(this.highlights.values()); return JSON.stringify(highlights, null, 2); } async importHighlights(data: string): Promise<void> { const highlights: ExcelHighlight[] = JSON.parse(data); for (const highlight of highlights) { this.highlights.set(highlight.id, highlight); // 重新应用高亮 await Excel.run(async (context) => { const worksheet = context.workbook.worksheets.getActiveWorksheet(); const cellRange = worksheet.getRange(highlight.range); cellRange.format.fill.color = highlight.color; await context.sync(); }); } } } export default ExcelHighlighter;

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/walkingzzzy/office-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server