import { html, css, LitElement } from '../../ui/assets/lit-core-2.7.4.min.js'; const commonSystemShortcuts = new Set([ 'Cmd+Q', 'Cmd+W', 'Cmd+A', 'Cmd+S', 'Cmd+Z', 'Cmd+X', 'Cmd+C', 'Cmd+V', 'Cmd+P', 'Cmd+F', 'Cmd+G', 'Cmd+H', 'Cmd+M', 'Cmd+N', 'Cmd+O', 'Cmd+T', 'Ctrl+Q', 'Ctrl+W', 'Ctrl+A', 'Ctrl+S', 'Ctrl+Z', 'Ctrl+X', 'Ctrl+C', 'Ctrl+V', 'Ctrl+P', 'Ctrl+F', 'Ctrl+G', 'Ctrl+H', 'Ctrl+M', 'Ctrl+N', 'Ctrl+O', 'Ctrl+T' ]); const displayNameMap = { nextStep: 'Ask Anything', moveUp: 'Move Up Window', moveDown: 'Move Down Window', scrollUp: 'Scroll Up Response', scrollDown: 'Scroll Down Response', }; export class ShortcutSettingsView extends LitElement { static styles = css` * { font-family:'Helvetica Neue',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif; cursor:default; user-select:none; box-sizing:border-box; } :host { display:flex; width:100%; height:100%; color:white; } .container { display:flex; flex-direction:column; height:100%; background:rgba(20,20,20,.9); border-radius:12px; outline:.5px rgba(255,255,255,.2) solid; outline-offset:-1px; position:relative; overflow:hidden; padding:12px; } .close-button{position:absolute;top:10px;right:10px;inline-size:14px;block-size:14px; background:rgba(255,255,255,.1);border:none;border-radius:3px; color:rgba(255,255,255,.7);display:grid;place-items:center; font-size:14px;line-height:0;cursor:pointer;transition:.15s;z-index:10;} .close-button:hover{background:rgba(255,255,255,.2);color:rgba(255,255,255,.9);} .title{font-size:14px;font-weight:500;margin:0 0 8px;padding-bottom:8px; border-bottom:1px solid rgba(255,255,255,.1);text-align:center;} .scroll-area{flex:1 1 auto;overflow-y:auto;margin:0 -4px;padding:4px;} .shortcut-entry{display:flex;align-items:center;width:100%;gap:8px; margin-bottom:8px;font-size:12px;padding:4px;} .shortcut-name{flex:1 1 auto;color:rgba(255,255,255,.9);font-weight:300; white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} .action-btn{background:none;border:none;color:rgba(0,122,255,.8); font-size:11px;padding:0 4px;cursor:pointer;transition:.15s;} .action-btn:hover{color:#0a84ff;text-decoration:underline;} .shortcut-input{inline-size:120px;background:rgba(0,0,0,.2); border:1px solid rgba(255,255,255,.2);border-radius:4px; padding:4px 6px;font:11px 'SF Mono','Menlo',monospace; color:white;text-align:right;cursor:text;margin-left:auto;} .shortcut-input:focus,.shortcut-input.capturing{ outline:none;border-color:rgba(0,122,255,.6); box-shadow:0 0 0 1px rgba(0,122,255,.3);} .feedback{font-size:10px;margin-top:2px;min-height:12px;} .feedback.error{color:#ef4444;} .feedback.success{color:#22c55e;} .actions{display:flex;gap:4px;padding-top:8px;border-top:1px solid rgba(255,255,255,.1);} .settings-button{flex:1;background:rgba(255,255,255,.1); border:1px solid rgba(255,255,255,.2);border-radius:4px; color:white;padding:5px 10px;font-size:11px;cursor:pointer;transition:.15s;} .settings-button:hover{background:rgba(255,255,255,.15);} .settings-button.primary{background:rgba(0,122,255,.25);border-color:rgba(0,122,255,.6);} .settings-button.primary:hover{background:rgba(0,122,255,.35);} .settings-button.danger{background:rgba(255,59,48,.1);border-color:rgba(255,59,48,.3); color:rgba(255,59,48,.9);} .settings-button.danger:hover{background:rgba(255,59,48,.15); } /* ────────────────[ GLASS BYPASS ]─────────────── */ :host-context(body.has-glass) { animation: none !important; transition: none !important; transform: none !important; will-change: auto !important; } :host-context(body.has-glass) * { background: transparent !important; /* 요청한 투명 처리 */ filter: none !important; backdrop-filter: none !important; box-shadow: none !important; outline: none !important; border: none !important; border-radius: 0 !important; transition: none !important; animation: none !important; } `; static properties = { shortcuts: { type: Object, state: true }, isLoading: { type: Boolean, state: true }, capturingKey: { type: String, state: true }, feedback: { type:Object, state:true } }; constructor() { super(); this.shortcuts = {}; this.feedback = {}; this.isLoading = true; this.capturingKey = null; } connectedCallback() { super.connectedCallback(); if (!window.api) return; this.loadShortcutsHandler = (event, keybinds) => { this.shortcuts = keybinds; this.isLoading = false; }; window.api.shortcutSettingsView.onLoadShortcuts(this.loadShortcutsHandler); } disconnectedCallback() { super.disconnectedCallback(); if (window.api && this.loadShortcutsHandler) { window.api.shortcutSettingsView.removeOnLoadShortcuts(this.loadShortcutsHandler); } } handleKeydown(e, shortcutKey){ e.preventDefault(); e.stopPropagation(); const result = this._parseAccelerator(e); if(!result) return; // modifier키만 누른 상태 const {accel, error} = result; if(error){ this.feedback = {...this.feedback, [shortcutKey]:{type:'error',msg:error}}; return; } // 성공 this.shortcuts = {...this.shortcuts, [shortcutKey]:accel}; this.feedback = {...this.feedback, [shortcutKey]:{type:'success',msg:'Shortcut set'}}; this.stopCapture(); } _parseAccelerator(e){ /* returns {accel?, error?} */ const parts=[]; if(e.metaKey) parts.push('Cmd'); if(e.ctrlKey) parts.push('Ctrl'); if(e.altKey) parts.push('Alt'); if(e.shiftKey) parts.push('Shift'); const isModifier=['Meta','Control','Alt','Shift'].includes(e.key); if(isModifier) return null; const map={ArrowUp:'Up',ArrowDown:'Down',ArrowLeft:'Left',ArrowRight:'Right',' ':'Space'}; parts.push(e.key.length===1? e.key.toUpperCase() : (map[e.key]||e.key)); const accel=parts.join('+'); /* ---- validation ---- */ if(parts.length===1) return {error:'Invalid shortcut: needs a modifier'}; if(parts.length>4) return {error:'Invalid shortcut: max 4 keys'}; if(commonSystemShortcuts.has(accel)) return {error:'Invalid shortcut: system reserved'}; return {accel}; } startCapture(key){ this.capturingKey = key; this.feedback = {...this.feedback, [key]:undefined}; } disableShortcut(key){ this.shortcuts = {...this.shortcuts, [key]:''}; // 공백 => 작동 X this.feedback = {...this.feedback, [key]:{type:'success',msg:'Shortcut disabled'}}; } stopCapture() { this.capturingKey = null; } async handleSave() { if (!window.api) return; const result = await window.api.shortcutSettingsView.saveShortcuts(this.shortcuts); if (!result.success) { alert('Failed to save shortcuts: ' + result.error); } } handleClose() { if (!window.api) return; window.api.shortcutSettingsView.closeShortcutEditor(); } async handleResetToDefault() { if (!window.api) return; const confirmation = confirm("Are you sure you want to reset all shortcuts to their default values?"); if (!confirmation) return; try { const defaultShortcuts = await window.api.shortcutSettingsView.getDefaultShortcuts(); this.shortcuts = defaultShortcuts; } catch (error) { alert('Failed to load default settings.'); } } formatShortcutName(name) { if (displayNameMap[name]) { return displayNameMap[name]; } const result = name.replace(/([A-Z])/g, " $1"); return result.charAt(0).toUpperCase() + result.slice(1); } render(){ if(this.isLoading){ return html`