feat: source-ref允许在vscode中快速打开

release/desktop
moonrailgun 3 years ago
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"]
}

@ -3,7 +3,6 @@
"outDir": "dist",
"module": "commonjs",
// Node 10
"target": "ES2018",
"lib": ["ES2018"],

@ -314,6 +314,7 @@ importers:
rollup-plugin-copy: ^3.4.0
rollup-plugin-replace: ^2.2.0
socket.io-client: ^4.1.2
source-ref-open-vscode: workspace:^1.0.0
style-loader: ^3.0.0
tailchat-design: workspace:^1.0.0
tailchat-plugin-declaration-generator: workspace:^1.0.0
@ -364,6 +365,7 @@ importers:
react-virtualized-auto-sizer: 1.0.6_sfoxds7t5ydpegc3knd667wn6m
react-virtuoso: 2.11.0_sfoxds7t5ydpegc3knd667wn6m
socket.io-client: 4.4.0
source-ref-open-vscode: link:../packages/source-ref-open-vscode
tailchat-design: link:../packages/design
tailchat-shared: link:../shared
tailwindcss: 2.2.19_hqu7j45oxtlo2g5y3qitzppg3y
@ -17051,7 +17053,7 @@ packages:
serialize-javascript: 6.0.0
source-map: 0.6.1
terser: 5.13.1
webpack: 5.72.1
webpack: 5.72.1_webpack-cli@4.9.2
dev: true
/terser/4.8.0:

@ -225,13 +225,13 @@ const config: Configuration = {
overlay: false,
},
},
// resolveLoader: {
// alias: {
// 'source-ref-loader': require.resolve(
// '../../packages/source-ref-webpack-loader/src'
// ),
// },
// },
resolveLoader: {
alias: {
'source-ref-loader': require.resolve(
'../../packages/source-ref-webpack-loader/src'
),
},
},
module: {
rules: [
{
@ -246,12 +246,12 @@ const config: Configuration = {
tsconfigRaw: require('../tsconfig.json'),
},
},
// {
// loader: 'source-ref-loader',
// options: {
// available: false,
// },
// },
{
loader: 'source-ref-loader',
options: {
available: isDev,
},
},
],
},
{

@ -54,6 +54,7 @@
"react-virtualized-auto-sizer": "^1.0.6",
"react-virtuoso": "^2.8.3",
"socket.io-client": "^4.1.2",
"source-ref-open-vscode": "workspace:^1.0.0",
"tailchat-design": "workspace:^1.0.0",
"tailchat-shared": "*",
"tailwindcss": "^2.2.4",

@ -13,11 +13,16 @@ import {
fetchGlobalConfig,
request,
isValidStr,
isDevelopment,
} from 'tailchat-shared';
import { getPopupContainer } from './utils/dom-helper';
import { getUserJWT } from './utils/jwt-helper';
import _get from 'lodash/get';
if (isDevelopment) {
import('source-ref-open-vscode');
}
const webStorage = buildStorage(window.localStorage);
setStorage(() => webStorage);

Loading…
Cancel
Save