diff --git a/packages/source-ref-open-vscode/index.d.ts b/packages/source-ref-open-vscode/index.d.ts
new file mode 100644
index 00000000..c9fb6c54
--- /dev/null
+++ b/packages/source-ref-open-vscode/index.d.ts
@@ -0,0 +1 @@
+declare module 'source-ref-open-vscode' {}
diff --git a/packages/source-ref-open-vscode/index.js b/packages/source-ref-open-vscode/index.js
new file mode 100644
index 00000000..f083fe2c
--- /dev/null
+++ b/packages/source-ref-open-vscode/index.js
@@ -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 = `
+
+
X
+ ${this.sids
+ .map((sid) => {
+ const uri = sidToURI(sid);
+ // 这里加了一个左向省略,暂时没用上,先放着
+ return `
source-ref: ${uri}`;
+ })
+ .join('')}
+
+ `;
+ 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);
diff --git a/packages/source-ref-open-vscode/package.json b/packages/source-ref-open-vscode/package.json
new file mode 100644
index 00000000..9a66c46a
--- /dev/null
+++ b/packages/source-ref-open-vscode/package.json
@@ -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"
+}
diff --git a/packages/source-ref-open-vscode/tsconfig.json b/packages/source-ref-open-vscode/tsconfig.json
new file mode 100644
index 00000000..8999cdc0
--- /dev/null
+++ b/packages/source-ref-open-vscode/tsconfig.json
@@ -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"]
+}
diff --git a/packages/source-ref-webpack-loader/tsconfig.json b/packages/source-ref-webpack-loader/tsconfig.json
index 1e84caf7..8999cdc0 100644
--- a/packages/source-ref-webpack-loader/tsconfig.json
+++ b/packages/source-ref-webpack-loader/tsconfig.json
@@ -3,7 +3,6 @@
"outDir": "dist",
"module": "commonjs",
- // Node 10
"target": "ES2018",
"lib": ["ES2018"],
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3df216af..5df6a967 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -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:
diff --git a/web/build/webpack.config.ts b/web/build/webpack.config.ts
index 635f4092..c9b45257 100644
--- a/web/build/webpack.config.ts
+++ b/web/build/webpack.config.ts
@@ -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,
+ },
+ },
],
},
{
diff --git a/web/package.json b/web/package.json
index 7a86aa86..d5c04a51 100644
--- a/web/package.json
+++ b/web/package.json
@@ -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",
diff --git a/web/src/init.tsx b/web/src/init.tsx
index 241bc48e..2e904c70 100644
--- a/web/src/init.tsx
+++ b/web/src/init.tsx
@@ -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);