mirror of https://github.com/msgbyte/tailchat
feat: source-ref允许在vscode中快速打开
parent
2d4b7985c3
commit
1c7c84da7c
@ -0,0 +1 @@
|
||||
declare module 'source-ref-open-vscode' {}
|
@ -0,0 +1,219 @@
|
||||
(function (win, doc) {
|
||||
const sourceMap = {};
|
||||
const sourceMapReverse = {};
|
||||
let cursor = 0;
|
||||
let timer = null;
|
||||
|
||||
function openVscode(node) {
|
||||
let path = null;
|
||||
if (node.dataset.sid) {
|
||||
path = sidToURI(node.dataset.sid);
|
||||
}
|
||||
if (node.dataset.source) {
|
||||
path = 'vscode://file/' + node.dataset.source;
|
||||
}
|
||||
if (!path) {
|
||||
return console.warn('Not found data-source');
|
||||
}
|
||||
win.location.href = path;
|
||||
}
|
||||
function sourceToId(node) {
|
||||
if (!node.dataset.source) return;
|
||||
const source = node.dataset.source;
|
||||
const [file, row, column] = source.split(':');
|
||||
if (!sourceMap[file]) {
|
||||
cursor++;
|
||||
sourceMap[file] = cursor;
|
||||
sourceMapReverse[cursor] = file;
|
||||
}
|
||||
const id = sourceMap[file];
|
||||
node.removeAttribute('data-source');
|
||||
node.setAttribute('data-sid', `${id}:${row}:${column}`);
|
||||
}
|
||||
function sidToURI(sid) {
|
||||
const [id, row, column] = sid.split(':');
|
||||
const path =
|
||||
'vscode://file/' + sourceMapReverse[id] + ':' + row + ':' + column;
|
||||
return path;
|
||||
}
|
||||
|
||||
class Selector {
|
||||
constructor(node) {
|
||||
this.sids = [];
|
||||
this.containerId = '__source-ref-panel';
|
||||
this.getAncestorSids = (node) => {
|
||||
const sids = [];
|
||||
let cur = node;
|
||||
while (cur !== doc.body) {
|
||||
if (cur.dataset.sid) {
|
||||
sids.push(cur.dataset.sid);
|
||||
}
|
||||
cur = cur.parentElement;
|
||||
}
|
||||
return sids;
|
||||
};
|
||||
this.focusBlock = null;
|
||||
this.setFocusBlock = (target) => {
|
||||
if (target === null) {
|
||||
// clear if target is null
|
||||
if (this.focusBlock) {
|
||||
doc.body.removeChild(this.focusBlock);
|
||||
this.focusBlock = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.focusBlock) {
|
||||
this.focusBlock = doc.createElement('div');
|
||||
this.focusBlock.className = '__source-ref-mask';
|
||||
this.focusBlock.style.position = 'absolute';
|
||||
this.focusBlock.style.backgroundColor = 'rgba(134, 185, 242, 0.5)';
|
||||
|
||||
doc.body.appendChild(this.focusBlock);
|
||||
}
|
||||
|
||||
const rect = target.getBoundingClientRect();
|
||||
|
||||
this.focusBlock.style.height = rect.height + 'px';
|
||||
this.focusBlock.style.width = rect.width + 'px';
|
||||
this.focusBlock.style.left = rect.x + 'px';
|
||||
this.focusBlock.style.top = rect.y + 'px';
|
||||
};
|
||||
this.getContainer = () => {
|
||||
const container = doc.getElementById(this.containerId);
|
||||
if (!container) {
|
||||
const div = doc.createElement('div');
|
||||
div.id = this.containerId;
|
||||
doc.body.appendChild(div);
|
||||
|
||||
// Add dom red border on hover
|
||||
div.addEventListener('mouseover', (e) => {
|
||||
const node = e.target;
|
||||
if (node.dataset.tid) {
|
||||
const target = doc.querySelector(
|
||||
`[data-sid="${node.dataset.tid}"]`
|
||||
);
|
||||
if (target) {
|
||||
target.classList.add('__source-ref-selected');
|
||||
this.setFocusBlock(target);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Remove dom red border when leave
|
||||
div.addEventListener('mouseout', (e) => {
|
||||
const node = e.target;
|
||||
if (node.dataset.tid) {
|
||||
const target = doc.querySelector(
|
||||
`[data-sid="${node.dataset.tid}"]`
|
||||
);
|
||||
if (target) {
|
||||
target.classList.remove('__source-ref-selected');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
this.setFocusBlock(null);
|
||||
doc.body.removeChild(div);
|
||||
};
|
||||
|
||||
// click event
|
||||
div.addEventListener('click', (e) => {
|
||||
const node = e.target;
|
||||
const command = node.dataset.command;
|
||||
switch (command) {
|
||||
case 'close': {
|
||||
e.stopPropagation();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
default:
|
||||
console.warn('Unknown command', command);
|
||||
}
|
||||
});
|
||||
|
||||
// keyboard event
|
||||
function escKeyHandler(e) {
|
||||
if (e.key === 'Escape') {
|
||||
e.stopPropagation();
|
||||
close();
|
||||
doc.removeEventListener('keydown', escKeyHandler);
|
||||
}
|
||||
}
|
||||
doc.addEventListener('keydown', escKeyHandler);
|
||||
|
||||
return div;
|
||||
}
|
||||
return container;
|
||||
};
|
||||
this.renderHTML = () => {
|
||||
const html = `
|
||||
<div style="
|
||||
position: fixed;
|
||||
background: white;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
opacity: 0.6;
|
||||
border-radius: 0 10px 0 0;
|
||||
">
|
||||
<div style="cursor:pointer;margin:10px;text-align:right;font-size:18px;" data-command="close">X</div>
|
||||
${this.sids
|
||||
.map((sid) => {
|
||||
const uri = sidToURI(sid);
|
||||
// 这里加了一个左向省略,暂时没用上,先放着
|
||||
return `<a href="${uri}" style="
|
||||
display: block;
|
||||
margin: 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
direction: rtl;
|
||||
text-align: left;
|
||||
" data-tid="${sid}">source-ref: ${uri}</a>`;
|
||||
})
|
||||
.join('')}
|
||||
</div>
|
||||
`;
|
||||
const container = this.getContainer();
|
||||
container.innerHTML = html;
|
||||
};
|
||||
this.sids = this.getAncestorSids(node);
|
||||
}
|
||||
}
|
||||
function init() {
|
||||
win.vscode = (node = win.$0) => {
|
||||
openVscode(node);
|
||||
};
|
||||
doc.body.addEventListener(
|
||||
'click',
|
||||
(e) => {
|
||||
if (e.altKey) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
const selector = new Selector(e.target);
|
||||
selector.renderHTML();
|
||||
}
|
||||
},
|
||||
true
|
||||
);
|
||||
const mo = new MutationObserver(() => {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(() => {
|
||||
// recal sid
|
||||
doc
|
||||
.querySelectorAll('[data-source]')
|
||||
.forEach((node) => sourceToId(node));
|
||||
}, 500);
|
||||
});
|
||||
mo.observe(doc.body, {
|
||||
attributes: true,
|
||||
childList: true,
|
||||
subtree: true,
|
||||
});
|
||||
}
|
||||
init();
|
||||
})(window, document);
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "source-ref-open-vscode",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"prepare": "tsc",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "moonrailgun",
|
||||
"license": "MIT"
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"module": "commonjs",
|
||||
|
||||
"target": "ES2018",
|
||||
"lib": ["ES2018"],
|
||||
|
||||
"declaration": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
Loading…
Reference in New Issue