From 29664516da3a2fe1d391ba9312134bcaabf18f1c Mon Sep 17 00:00:00 2001 From: markshawn2020 Date: Thu, 9 Oct 2025 11:34:18 +0800 Subject: [PATCH] feat: support copy path mode --- packages/core/src/client/index.ts | 175 ++++++++++++++++++++++---- packages/core/types/client/index.d.ts | 6 + 2 files changed, 160 insertions(+), 21 deletions(-) diff --git a/packages/core/src/client/index.ts b/packages/core/src/client/index.ts index 8321107..40a12a0 100644 --- a/packages/core/src/client/index.ts +++ b/packages/core/src/client/index.ts @@ -2,7 +2,6 @@ import { LitElement, TemplateResult, css, html } from 'lit'; import { property, query, state } from 'lit/decorators.js'; import { styleMap } from 'lit/directives/style-map.js'; import { PathName, DefaultPort } from '../shared'; -import { formatOpenPath } from 'launch-ide'; const styleId = '__code-inspector-unique-id'; const AstroFile = 'data-astro-source-file'; @@ -154,6 +153,12 @@ export class CodeInspectorComponent extends LitElement { sendType: 'xhr' | 'img' = 'xhr'; @state() activeNode: ActiveNode = {}; + @state() + actionMode: 'ide' | 'copy' = 'ide'; // 模式:ide-在IDE中打开,copy-复制路径 + @state() + showModeToast = false; // 显示模式切换提示 + @state() + modeToastTimer: number | null = null; // 模式切换提示定时器 @query('#inspector-switch') inspectorSwitchRef!: HTMLDivElement; @@ -530,30 +535,53 @@ export class CodeInspectorComponent extends LitElement { return targetUrl; }; + // 切换模式 + toggleMode = () => { + this.actionMode = this.actionMode === 'ide' ? 'copy' : 'ide'; + this.showModeToastNotification(); + }; + + // 显示模式切换提示 + showModeToastNotification = () => { + // 清除之前的定时器 + if (this.modeToastTimer !== null) { + window.clearTimeout(this.modeToastTimer); + } + + this.showModeToast = true; + + // 2秒后自动隐藏 + this.modeToastTimer = window.setTimeout(() => { + this.showModeToast = false; + this.modeToastTimer = null; + }, 2000); + }; + // 触发功能的处理 trackCode = () => { - if (this.locate) { - // 请求本地服务端,打开vscode - if (this.sendType === 'xhr') { - this.sendXHR(); - } else { - this.sendImg(); + // 根据当前模式执行对应操作 + if (this.actionMode === 'ide') { + // IDE模式:打开编辑器 + if (this.locate) { + // 请求本地服务端,打开vscode + if (this.sendType === 'xhr') { + this.sendXHR(); + } else { + this.sendImg(); + } } + if (this.target) { + window.open(this.buildTargetUrl(), '_blank'); + } + } else if (this.actionMode === 'copy') { + // 复制模式:复制路径 + // 直接复制文件路径,格式:/path/to/file.tsx:line:column + const pathToCopy = `${this.element.path}:${this.element.line}:${this.element.column}`; + this.copyToClipboard(pathToCopy); } - if (this.copy) { - const path = formatOpenPath( - this.element.path, - String(this.element.line), - String(this.element.column), - this.copy - ); - this.copyToClipboard(path[0]); - } - if (this.target) { - window.open(this.buildTargetUrl(), '_blank'); - } + window.dispatchEvent(new CustomEvent('code-inspector:trackCode', { - detail: this.element, + detail: { ...this.element, mode: this.actionMode }, })); }; @@ -743,6 +771,23 @@ export class CodeInspectorComponent extends LitElement { } }; + // 监听键盘按下,处理模式切换 + handleKeyDown = (e: KeyboardEvent) => { + // Shift+Alt+C 切换模式 + // 检查 key, code, 和 keyCode 以确保在不同系统上都能工作 + const isC = + e.key === 'c' || + e.key === 'C' || + e.code === 'KeyC' || + e.keyCode === 67; + + if (e.shiftKey && e.altKey && isC && !e.ctrlKey && !e.metaKey) { + e.preventDefault(); + e.stopPropagation(); + this.toggleMode(); + } + }; + // 监听键盘抬起,清除遮罩层 handleKeyUp = (e: KeyboardEvent) => { if (!this.isTracking(e) && !this.open) { @@ -882,6 +927,7 @@ export class CodeInspectorComponent extends LitElement { window.addEventListener('touchmove', this.handleDrag, true); window.addEventListener('click', this.handleMouseClick, true); window.addEventListener('pointerdown', this.handlePointerDown, true); + window.addEventListener('keydown', this.handleKeyDown, true); window.addEventListener('keyup', this.handleKeyUp, true); window.addEventListener('mouseleave', this.removeCover, true); window.addEventListener('mouseup', this.handleMouseUp, true); @@ -896,6 +942,7 @@ export class CodeInspectorComponent extends LitElement { window.removeEventListener('touchmove', this.handleDrag, true); window.removeEventListener('click', this.handleMouseClick, true); window.removeEventListener('pointerdown', this.handlePointerDown, true); + window.removeEventListener('keydown', this.handleKeyDown, true); window.removeEventListener('keyup', this.handleKeyUp, true); window.removeEventListener('mouseleave', this.removeCover, true); window.removeEventListener('mouseup', this.handleMouseUp, true); @@ -995,12 +1042,20 @@ export class CodeInspectorComponent extends LitElement {
<${this.element.name}> - click to open editor + ${ + this.actionMode === 'ide' + ? 'click to open editor' + : 'click to copy path' + }
${this.element.path}:${this.element.line}:${this.element.column}
+
+ Mode: ${this.actionMode === 'ide' ? '📝 IDE' : '📋 Copy'} + (Shift+Alt+C to toggle) +
@@ -1149,6 +1204,26 @@ export class CodeInspectorComponent extends LitElement { > ${this.activeNode.content} +
+
+ ${ + this.actionMode === 'ide' + ? html`
📝
+
+
IDE Mode
+
Click to open in editor
+
` + : html`
📋
+
+
Copy Mode
+
Click to copy path
+
` + } +
+
`; } @@ -1234,6 +1309,21 @@ export class CodeInspectorComponent extends LitElement { line-height: 12px; margin-top: 4px; } + .mode-indicator { + color: #006aff; + font-size: 11px; + margin-top: 6px; + padding-top: 4px; + border-top: 1px solid #eee; + display: flex; + align-items: center; + gap: 4px; + } + .mode-hint { + color: #999; + font-size: 10px; + margin-left: 4px; + } .inspector-switch { position: fixed; z-index: 9999999999999; @@ -1336,6 +1426,49 @@ export class CodeInspectorComponent extends LitElement { .close-icon { cursor: pointer; } + .mode-toast { + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%) translateY(-100px); + z-index: 99999999999999; + background: rgba(0, 0, 0, 0.85); + color: white; + padding: 12px 20px; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + opacity: 0; + transition: all 0.3s ease-in-out; + pointer-events: none; + font-family: 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', + sans-serif; + } + .mode-toast.show { + transform: translateX(-50%) translateY(0); + opacity: 1; + } + .mode-toast-content { + display: flex; + align-items: center; + gap: 12px; + } + .mode-toast-icon { + font-size: 24px; + line-height: 1; + } + .mode-toast-text { + display: flex; + flex-direction: column; + gap: 2px; + } + .mode-toast-title { + font-size: 14px; + font-weight: bold; + } + .mode-toast-desc { + font-size: 12px; + opacity: 0.8; + } `; } diff --git a/packages/core/types/client/index.d.ts b/packages/core/types/client/index.d.ts index 84d327b..8b99910 100644 --- a/packages/core/types/client/index.d.ts +++ b/packages/core/types/client/index.d.ts @@ -94,6 +94,9 @@ export declare class CodeInspectorComponent extends LitElement { preUserSelect: string; sendType: 'xhr' | 'img'; activeNode: ActiveNode; + actionMode: 'ide' | 'copy'; + showModeToast: boolean; + modeToastTimer: number | null; inspectorSwitchRef: HTMLDivElement; codeInspectorContainerRef: HTMLDivElement; elementInfoRef: HTMLDivElement; @@ -133,6 +136,8 @@ export declare class CodeInspectorComponent extends LitElement { sendXHR: () => void; sendImg: () => void; buildTargetUrl: () => string; + toggleMode: () => void; + showModeToastNotification: () => void; trackCode: () => void; copyToClipboard(text: string): void; handleDrag: (e: MouseEvent | TouchEvent) => void; @@ -142,6 +147,7 @@ export declare class CodeInspectorComponent extends LitElement { handleContextMenu: (e: MouseEvent) => void; generateNodeTree: (nodePath: HTMLElement[]) => TreeNode; handlePointerDown: (e: PointerEvent) => void; + handleKeyDown: (e: KeyboardEvent) => void; handleKeyUp: (e: KeyboardEvent) => void; printTip: () => void; getMousePosition: (e: MouseEvent | TouchEvent) => {