mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-05-07 12:16:04 +00:00
Migrate to React framework
This commit is contained in:
parent
e5a89c9182
commit
243ff95198
|
@ -3,9 +3,6 @@
|
||||||
<head>
|
<head>
|
||||||
<title>WelsonJS Editor</title>
|
<title>WelsonJS Editor</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||||
<link rel="stylesheet" href="http://localhost:3000/ajax/libs/metroui/dev/lib/metro.css" integrity="sha384-4XgOiXH2ZMaWt5s5B35yKi7EAOabhZvx7wO8Jr71q2vZ+uONdRza/6CsK2kpyocd" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="http://localhost:3000/ajax/libs/metroui/dev/lib/icons.css" integrity="sha384-FuLND994etg+RtnpPSPMyNBvL+fEz+xGhbN61WUWuDEeZ+wJzcQ8SGqAMuI5hWrt" crossorigin="anonymous">
|
|
||||||
<link rel="stylesheet" href="http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/editor/editor.main.css" integrity="sha384-06yHXpYRlHEPaR4AS0fB/W+lMN09Zh5e1XMtfkNQdHV38OlhfkOEW5M+pCj3QskC" crossorigin="anonymous">
|
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -34,436 +31,497 @@
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav data-role="ribbonmenu">
|
<div id="app"></div>
|
||||||
<ul class="tabs-holder">
|
|
||||||
<li class="static"><a href="#">WelsonJS</a></li>
|
|
||||||
<li><a href="#editor-tab">Editor</a></li>
|
|
||||||
</ul>
|
|
||||||
<div class="content-holder">
|
|
||||||
<div class="section" id="editor-tab">
|
|
||||||
<div class="group">
|
|
||||||
<button id="btnOpenFile" class="ribbon-button">
|
|
||||||
<span class="icon mif-folder-open"></span>
|
|
||||||
<span class="caption">Open File</span>
|
|
||||||
</button>
|
|
||||||
<button id="btnSaveFile" class="ribbon-button">
|
|
||||||
<span class="icon mif-floppy-disks"></span>
|
|
||||||
<span class="caption">Save File</span>
|
|
||||||
</button>
|
|
||||||
<span class="title">File</span>
|
|
||||||
</div>
|
|
||||||
<div class="group">
|
|
||||||
<button id="btnCopilot" class="ribbon-button">
|
|
||||||
<span class="icon mif-rocket"></span>
|
|
||||||
<span class="caption">Copilot</span>
|
|
||||||
</button>
|
|
||||||
<button id="btnAzureAi" class="ribbon-button">
|
|
||||||
<span class="icon mif-rocket"></span>
|
|
||||||
<span class="caption">Azure AI</span>
|
|
||||||
</button>
|
|
||||||
<button id="btnSavePrompt" class="ribbon-button">
|
|
||||||
<span class="icon mif-floppy-disks"></span>
|
|
||||||
<span class="caption">Save</span>
|
|
||||||
</button>
|
|
||||||
<button id="btnLoadPrompt" class="ribbon-button">
|
|
||||||
<span class="icon mif-file-upload"></span>
|
|
||||||
<span class="caption">Load</span>
|
|
||||||
</button>
|
|
||||||
<span class="title">Generative AI</span>
|
|
||||||
</div>
|
|
||||||
<div class="group">
|
|
||||||
<button id="btnWhois" class="ribbon-button">
|
|
||||||
<span class="icon mif-earth"></span>
|
|
||||||
<span class="caption">Whois</span>
|
|
||||||
</button>
|
|
||||||
<button id="btnDnsQuery" class="ribbon-button">
|
|
||||||
<span class="icon mif-earth"></span>
|
|
||||||
<span class="caption">DNS</span>
|
|
||||||
</button>
|
|
||||||
<span class="title">Network tools</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div id="container"></div>
|
|
||||||
<input type="file" id="fileInput">
|
|
||||||
|
|
||||||
<div class="banner"><a href="https://github.com/gnh1201/welsonjs">WelsonJS</a> Code Editor powered by <a href="https://github.com/microsoft/monaco-editor">Microsoft Monaco Editor</a>.</div>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var require = {
|
function loadResource(url, mimeType, integrity) {
|
||||||
paths: {
|
mimeType = mimeType || 'application/javascript';
|
||||||
vs: 'http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="http://localhost:3000/ajax/libs/axios/1.8.4/axios.min.js" integrity="sha384-06w+raHvkSL3+E7mbQ2X6DZwI5A3veU8Ba+NLrAPxxRGw4Xy78sihHDHQMustMM4" crossorigin="anonymous"></script>
|
|
||||||
<script src="http://localhost:3000/ajax/libs/fast-xml-parser/4.5.1/fxparser.min.js" integrity="sha384-ae/HepOQ8hiJ/VA6yGwPMGXQXOkT/lJpjlcQ7EUgibUcfnBltuozgNj4IgOZ9QLc" crossorigin="anonymous"></script>
|
|
||||||
<script src="http://localhost:3000/ajax/libs/dompurify/3.2.4/purify.min.js" integrity="sha384-eEu5CTj3qGvu9PdJuS+YlkNi7d2XxQROAFYOr59zgObtlcux1ae1Il3u7jvdCSWu" crossorigin="anonymous"></script>
|
|
||||||
<script src="http://localhost:3000/ajax/libs/metroui/dev/lib/metro.js" integrity="sha384-grz4KlnFmdCd5ELenGIdPkUL/l+44UC4SniSke/OZQyRYXaQ1EDlGigacn6z4hGB" crossorigin="anonymous"></script>
|
|
||||||
<script src="http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/loader.js" integrity="sha384-pHG02SG8pId94Np3AbPmBEJ1yPqaH0IkJGLSNGXYmuGhkazT8Lr/57WYpbkGjJtu" crossorigin="anonymous"></script>
|
|
||||||
<script src="http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/editor/editor.main.js" integrity="sha384-fj9z+NUc93I3woCCy5IRQfrQ8Amu1E27tllwgb5gz3d9Vr1ymS13xcF6two3e4KH" crossorigin="anonymous"></script>
|
|
||||||
<script>
|
|
||||||
let editor;
|
|
||||||
let currentFileName = "sayhello.js";
|
|
||||||
const serverPrefix = "http://localhost:3000/";
|
|
||||||
let settings = {};
|
|
||||||
let promptMessages = [];
|
|
||||||
|
|
||||||
function fetchSettings() {
|
return new Promise((resolve, reject) => {
|
||||||
axios.get(`${serverPrefix}settings`)
|
let el;
|
||||||
.then(response => {
|
|
||||||
const parser = new XMLParser();
|
if (mimeType === 'text/css') {
|
||||||
const result = parser.parse(response.data);
|
el = document.createElement('link');
|
||||||
settings = result.settings;
|
el.rel = 'stylesheet';
|
||||||
});
|
el.href = url;
|
||||||
|
} else {
|
||||||
|
el = document.createElement('script');
|
||||||
|
el.src = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integrity) {
|
||||||
|
el.integrity = integrity;
|
||||||
|
el.crossOrigin = 'anonymous';
|
||||||
|
}
|
||||||
|
|
||||||
|
el.onload = resolve;
|
||||||
|
el.onerror = reject;
|
||||||
|
|
||||||
|
(mimeType === 'text/css' ? document.head : document.body).appendChild(el);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizeEditor() {
|
Promise.all([
|
||||||
if (editor) {
|
loadResource("http://localhost:3000/ajax/libs/metroui/dev/lib/metro.css", "text/css", "sha384-4XgOiXH2ZMaWt5s5B35yKi7EAOabhZvx7wO8Jr71q2vZ+uONdRza/6CsK2kpyocd"),
|
||||||
const ribbonHeight = document.querySelector('nav').offsetHeight;
|
loadResource("http://localhost:3000/ajax/libs/metroui/dev/lib/icons.css", "text/css", "sha384-FuLND994etg+RtnpPSPMyNBvL+fEz+xGhbN61WUWuDEeZ+wJzcQ8SGqAMuI5hWrt"),
|
||||||
const bannerHeight = document.querySelector('.banner').offsetHeight;
|
loadResource("http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/editor/editor.main.css", "text/css", "sha384-06yHXpYRlHEPaR4AS0fB/W+lMN09Zh5e1XMtfkNQdHV38OlhfkOEW5M+pCj3QskC"),
|
||||||
const containerHeight = document.documentElement.clientHeight - ribbonHeight - bannerHeight;
|
loadResource("http://localhost:3000/ajax/libs/axios/1.8.4/axios.min.js", null, "sha384-06w+raHvkSL3+E7mbQ2X6DZwI5A3veU8Ba+NLrAPxxRGw4Xy78sihHDHQMustMM4"),
|
||||||
document.getElementById('container').style.height = containerHeight + 'px';
|
loadResource("http://localhost:3000/ajax/libs/fast-xml-parser/4.5.1/fxparser.min.js", null, "sha384-ae/HepOQ8hiJ/VA6yGwPMGXQXOkT/lJpjlcQ7EUgibUcfnBltuozgNj4IgOZ9QLc"),
|
||||||
editor.layout();
|
loadResource("http://localhost:3000/ajax/libs/dompurify/3.2.4/purify.min.js", null, "sha384-eEu5CTj3qGvu9PdJuS+YlkNi7d2XxQROAFYOr59zgObtlcux1ae1Il3u7jvdCSWu"),
|
||||||
|
loadResource("http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/loader.js", null, "sha384-pHG02SG8pId94Np3AbPmBEJ1yPqaH0IkJGLSNGXYmuGhkazT8Lr/57WYpbkGjJtu"),
|
||||||
|
loadResource("http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs/editor/editor.main.js", null, "sha384-fj9z+NUc93I3woCCy5IRQfrQ8Amu1E27tllwgb5gz3d9Vr1ymS13xcF6two3e4KH"),
|
||||||
|
loadResource("https://unpkg.com/react@18/umd/react.development.js"),
|
||||||
|
loadResource("https://unpkg.com/react-dom@18/umd/react-dom.development.js")
|
||||||
|
]).then(() => {
|
||||||
|
require.config({
|
||||||
|
paths: {
|
||||||
|
vs: 'http://localhost:3000/ajax/libs/monaco-editor/0.52.2/min/vs'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const _e = React.createElement;
|
||||||
|
|
||||||
|
function Button({ id, icon, caption, onClick }) {
|
||||||
|
return _e(
|
||||||
|
'button',
|
||||||
|
{ id, className: 'ribbon-button', onClick },
|
||||||
|
_e('span', { className: `icon ${icon}` }),
|
||||||
|
_e('span', { className: 'caption' }, caption)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function getSuggestions(word, range) {
|
function Group({ title, buttons }) {
|
||||||
return axios.get(`${serverPrefix}completion/${encodeURIComponent(word)}`)
|
return _e(
|
||||||
.then(response => {
|
'div',
|
||||||
const parser = new XMLParser();
|
{ className: 'group' },
|
||||||
const result = parser.parse(response.data);
|
buttons.map((btn, index) =>
|
||||||
|
_e(Button, { key: index, ...btn })
|
||||||
|
),
|
||||||
|
_e('span', { className: 'title' }, title)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!result.suggestions || !result.suggestions.item) {
|
function RibbonMenu({
|
||||||
|
onOpenFileClick, onSaveFileClick, onCopliotClick, onAzureAiClick,
|
||||||
|
onSavePromptClick, onLoadPromptClick, onQueryWhoisClick, onQueryDnsClick
|
||||||
|
}) {
|
||||||
|
const fileButtons = [
|
||||||
|
{
|
||||||
|
id: 'btnOpenFile',
|
||||||
|
icon: 'mif-folder-open',
|
||||||
|
caption: 'Open File',
|
||||||
|
onClick: onOpenFileClick
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'btnSaveFile',
|
||||||
|
icon: 'mif-floppy-disks',
|
||||||
|
caption: 'Save File',
|
||||||
|
onClick: onSaveFileClick
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const aiButtons = [
|
||||||
|
{ id: 'btnCopilot', icon: 'mif-rocket', caption: 'Copilot', onClick: onCopliotClick },
|
||||||
|
{ id: 'btnAzureAi', icon: 'mif-rocket', caption: 'Azure AI', onClick: onAzureAiClick },
|
||||||
|
{ id: 'btnSavePrompt', icon: 'mif-floppy-disks', caption: 'Save', onClick: onSavePromptClick },
|
||||||
|
{ id: 'btnLoadPrompt', icon: 'mif-file-upload', caption: 'Load', onClick: onLoadPromptClick }
|
||||||
|
];
|
||||||
|
|
||||||
|
const networkToolsButtons = [
|
||||||
|
{ id: 'btnWhois', icon: 'mif-earth', caption: 'Whois', onClick: onQueryWhoisClick },
|
||||||
|
{ id: 'btnDnsQuery', icon: 'mif-earth', caption: 'DNS', onClick: onQueryDnsClick }
|
||||||
|
];
|
||||||
|
|
||||||
|
return _e(
|
||||||
|
'nav',
|
||||||
|
{ 'className': 'ribbon-menu' },
|
||||||
|
_e('ul', { className: 'tabs-holder' },
|
||||||
|
_e('li', { className: 'static' }, _e('a', { href: '#heading-tab' }, 'WelsonJS')),
|
||||||
|
_e('li', { className: 'active' }, _e('a', { href: '#editor-tab' }, 'Editor'))
|
||||||
|
),
|
||||||
|
_e('div', { className: 'content-holder' },
|
||||||
|
_e('div', { className: 'section active', id: 'editor-tab' },
|
||||||
|
_e(Group, { title: 'File', buttons: fileButtons }),
|
||||||
|
_e(Group, { title: 'Generative AI', buttons: aiButtons }),
|
||||||
|
_e(Group, { title: 'Network tools', buttons: networkToolsButtons })
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Editor({ editorRef }) {
|
||||||
|
const containerRef = React.useRef(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (!containerRef.current) return;
|
||||||
|
|
||||||
|
require(['vs/editor/editor.main'], () => {
|
||||||
|
const instance = monaco.editor.create(containerRef.current, {
|
||||||
|
value: ['// lib/sayhello.js', 'function say() {', ' console.log("hello");', '}', '', 'exports.say = say;', '', 'exports.VERSIONINFO = "SayHello (sayhello.js) version 0.1";', 'exports.AUTHOR = "abuse@catswords.net";', 'exports.global = global;', 'exports.require = global.require;'].join('\n'),
|
||||||
|
language: 'javascript'
|
||||||
|
});
|
||||||
|
|
||||||
|
editorRef.current = instance;
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return _e('div', { id: 'editor', ref: containerRef });
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const serverPrefix = "http://localhost:3000/";
|
||||||
|
const editorRef = React.useRef(null);
|
||||||
|
const settingsRef = React.useRef({});
|
||||||
|
const fileNameRef = React.useRef('sayhello.js');
|
||||||
|
const promptMessagesRef = React.useRef([]);
|
||||||
|
|
||||||
|
const fetchSettings = () => axios.get(`${serverPrefix}settings`)
|
||||||
|
.then(response => {
|
||||||
|
const parser = new XMLParser();
|
||||||
|
const result = parser.parse(response.data);
|
||||||
|
settingsRef.current = result.settings;
|
||||||
|
});
|
||||||
|
|
||||||
|
const resizeEditor = () => {
|
||||||
|
if (editorRef.current) {
|
||||||
|
const ribbon = document.querySelector('nav')?.offsetHeight || 0;
|
||||||
|
const banner = document.querySelector('.banner')?.offsetHeight || 0;
|
||||||
|
const h = document.documentElement.clientHeight - ribbon - banner;
|
||||||
|
const editorDiv = document.getElementById('editor');
|
||||||
|
if (editorDiv) editorDiv.style.height = h + 'px';
|
||||||
|
if (editorRef.current) editorRef.current.layout();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSuggestions = () => axios.get(`${serverPrefix}completion/${encodeURIComponent(word)}`)
|
||||||
|
.then(response => {
|
||||||
|
const parser = new XMLParser();
|
||||||
|
const result = parser.parse(response.data);
|
||||||
|
|
||||||
|
if (!result.suggestions || !result.suggestions.item) {
|
||||||
|
return {
|
||||||
|
suggestions: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = Array.isArray(result.suggestions.item) ? result.suggestions.item : [result.suggestions.item];
|
||||||
|
const suggestions = items.map(function (item) {
|
||||||
|
return {
|
||||||
|
label: item.label,
|
||||||
|
kind: monaco.languages.CompletionItemKind.Text,
|
||||||
|
documentation: item.documentation || "",
|
||||||
|
insertText: '"' + item.insertText + '"',
|
||||||
|
range: range
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
suggestions: suggestions
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
return {
|
return {
|
||||||
suggestions: []
|
suggestions: []
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
const items = Array.isArray(result.suggestions.item) ? result.suggestions.item : [result.suggestions.item];
|
|
||||||
const suggestions = items.map(function (item) {
|
|
||||||
return {
|
|
||||||
label: item.label,
|
|
||||||
kind: monaco.languages.CompletionItemKind.Text,
|
|
||||||
documentation: item.documentation || "",
|
|
||||||
insertText: '"' + item.insertText + '"',
|
|
||||||
range: range
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
|
||||||
suggestions: suggestions
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(function () {
|
|
||||||
return {
|
|
||||||
suggestions: []
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushPromptMessage(role, content) {
|
const pushPromptMessage = (role, content) => {
|
||||||
promptMessages.push({
|
promptMessagesRef.current.push({
|
||||||
role: role,
|
role: role,
|
||||||
content: content
|
content: content
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
require(["vs/editor/editor.main"], function () {
|
|
||||||
editor = monaco.editor.create(document.getElementById('container'), {
|
|
||||||
value: ['// lib/sayhello.js', 'function say() {', ' console.log("hello");', '}', '', 'exports.say = say;', '', 'exports.VERSIONINFO = "SayHello (sayhello.js) version 0.1";', 'exports.AUTHOR = "abuse@catswords.net";', 'exports.global = global;', 'exports.require = global.require;'].join('\n'),
|
|
||||||
language: 'javascript'
|
|
||||||
});
|
|
||||||
|
|
||||||
monaco.languages.registerCompletionItemProvider('javascript', {
|
|
||||||
provideCompletionItems: function (model, position) {
|
|
||||||
const word = model.getWordUntilPosition(position);
|
|
||||||
const range = {
|
|
||||||
startLineNumber: position.lineNumber,
|
|
||||||
endLineNumber: position.lineNumber,
|
|
||||||
startColumn: word.startColumn,
|
|
||||||
endColumn: word.endColumn
|
|
||||||
};
|
|
||||||
|
|
||||||
return getSuggestions(word.word, range);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener('resize', resizeEditor);
|
|
||||||
|
|
||||||
function getFileLanguage(fileName) {
|
|
||||||
const extension = fileName.split('.').pop().toLowerCase();
|
|
||||||
const languageMap = {
|
|
||||||
'js': 'javascript',
|
|
||||||
'ts': 'typescript',
|
|
||||||
'html': 'html',
|
|
||||||
'css': 'css',
|
|
||||||
'json': 'json',
|
|
||||||
'py': 'python',
|
|
||||||
'java': 'java',
|
|
||||||
'c': 'c',
|
|
||||||
'cpp': 'cpp',
|
|
||||||
'cs': 'csharp',
|
|
||||||
'php': 'php',
|
|
||||||
'rb': 'ruby',
|
|
||||||
'go': 'go',
|
|
||||||
'rs': 'rust'
|
|
||||||
};
|
|
||||||
return languageMap[extension] || 'plaintext';
|
|
||||||
}
|
|
||||||
|
|
||||||
function navigate(href) {
|
|
||||||
const a = document.createElement("a");
|
|
||||||
a.href = href;
|
|
||||||
a.target = "_blank";
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
}
|
|
||||||
|
|
||||||
function appendTextToEditor(text) {
|
|
||||||
const position = editor.getPosition();
|
|
||||||
const range = new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column);
|
|
||||||
editor.executeEdits("my-source", [{
|
|
||||||
range: range,
|
|
||||||
text: "\n" + text,
|
|
||||||
forceMoveMarkers: true
|
|
||||||
}]);
|
|
||||||
resizeEditor();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById("fileInput").onchange = function (event) {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
currentFileName = file.name;
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = function (e) {
|
|
||||||
let language = getFileLanguage(file.name);
|
|
||||||
monaco.editor.setModelLanguage(editor.getModel(), language);
|
|
||||||
editor.setValue(e.target.result);
|
|
||||||
};
|
|
||||||
reader.readAsText(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("btnOpenFile").onclick = function () {
|
|
||||||
document.getElementById('fileInput').click();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("btnSaveFile").onclick = function () {
|
|
||||||
const text = editor.getValue();
|
|
||||||
const fileName = prompt("Enter file name:", currentFileName);
|
|
||||||
if (!fileName) return;
|
|
||||||
|
|
||||||
currentFileName = fileName;
|
|
||||||
|
|
||||||
const blob = new Blob([text], { type: 'text/plain' });
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = URL.createObjectURL(blob);
|
|
||||||
a.download = fileName;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("btnCopilot").onclick = function () {
|
|
||||||
const promptMessage = prompt("Enter a prompt message:", '');
|
|
||||||
if (!promptMessage || promptMessage.trim() == '') {
|
|
||||||
alert("A prompt message is required.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendTextToEditor(`\n// ${promptMessage}... Generating text with Copilot...`);
|
|
||||||
pushPromptMessage("user", promptMessage);
|
|
||||||
|
|
||||||
(async function () {
|
|
||||||
const targetWsUrl = await getTargetByUrl('copilot.microsoft.com');
|
|
||||||
if (targetWsUrl) {
|
|
||||||
await sendPromptMessage(targetWsUrl, promptMessage);
|
|
||||||
} else {
|
|
||||||
alert("Microsoft Copilot not running. Please visit copilot.microsoft.com first.");
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("btnAzureAi").onclick = function () {
|
|
||||||
const promptMessage = prompt("Enter a prompt message:", '');
|
|
||||||
if (!promptMessage || promptMessage.trim() == '') {
|
|
||||||
alert("A prompt message is required.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendTextToEditor(`\n// ${promptMessage}... Generating text with Azure AI...`);
|
|
||||||
pushPromptMessage("user", promptMessage);
|
|
||||||
|
|
||||||
const apiKey = settings.AzureAiServiceApiKey;
|
|
||||||
const url = `${settings.AzureAiServicePrefix}models/chat/completions?api-version=${settings.AzureAiServiceApiVersion}`;
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
messages: promptMessages,
|
|
||||||
max_tokens: 2048,
|
|
||||||
temperature: 0.8,
|
|
||||||
top_p: 0.1,
|
|
||||||
presence_penalty: 0,
|
|
||||||
frequency_penalty: 0,
|
|
||||||
model: 'Phi-4'
|
|
||||||
};
|
|
||||||
|
|
||||||
axios.post(url, data, {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'api-key': apiKey
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
response.data.choices.forEach(x => {
|
|
||||||
const responseContent = x.message.content;
|
|
||||||
pushPromptMessage("assistant", responseContent);
|
|
||||||
|
|
||||||
const responseText = DOMPurify.sanitize(responseContent, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
|
||||||
appendTextToEditor(`/*\n${responseText}\n*/`);
|
|
||||||
});
|
});
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error:', error.response?.data || error.message);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("btnSavePrompt").onclick = function () {
|
|
||||||
const text = JSON.stringify(promptMessages, null, 4);
|
|
||||||
const blob = new Blob([text], { type: 'text/plain' });
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.href = URL.createObjectURL(blob);
|
|
||||||
a.download = "prompt.json";
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
document.body.removeChild(a);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("btnLoadPrompt").onclick = function () {
|
|
||||||
const fileInput = document.createElement('input');
|
|
||||||
fileInput.type = 'file';
|
|
||||||
fileInput.accept = '.json';
|
|
||||||
fileInput.onchange = function (event) {
|
|
||||||
const file = event.target.files[0];
|
|
||||||
if (!file) return;
|
|
||||||
|
|
||||||
let reader = new FileReader();
|
|
||||||
reader.onload = function (e) {
|
|
||||||
promptMessages = JSON.parse(e.target.result);
|
|
||||||
appendTextToEditor("\n//Prompt loaded successfully.");
|
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
|
||||||
};
|
|
||||||
fileInput.click();
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById("btnWhois").onclick = function () {
|
const navigate = (href) => {
|
||||||
const hostname = prompt("Enter a hostname or IP address:", '');
|
const a = document.createElement("a");
|
||||||
if (!hostname || hostname.trim() == '') {
|
a.href = href;
|
||||||
appendTextToEditor("\n// A hostname or IP address is required.");
|
a.target = "_blank";
|
||||||
return;
|
document.body.appendChild(a);
|
||||||
}
|
a.click();
|
||||||
|
};
|
||||||
|
|
||||||
axios.get(`${serverPrefix}whois/${hostname}`).then(response => {
|
const appendTextToEditor = (text) => {
|
||||||
const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
const position = editor.getPosition();
|
||||||
appendTextToEditor(`/*\n${responseText}\n*/`);
|
const range = new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column);
|
||||||
pushPromptMessage("system", responseText);
|
editor.executeEdits('', [{
|
||||||
}).catch(error => {
|
range: range,
|
||||||
console.error(error);
|
text: "\n" + text,
|
||||||
});
|
forceMoveMarkers: true
|
||||||
};
|
}]);
|
||||||
|
resizeEditor();
|
||||||
|
};
|
||||||
|
|
||||||
document.getElementById("btnDnsQuery").onclick = function () {
|
const openFile = () => {
|
||||||
const hostname = prompt("Enter a hostname or IP address:", '');
|
const fileInput = document.createElement('input');
|
||||||
if (!hostname || hostname.trim() == '') {
|
fileInput.type = 'file';
|
||||||
appendTextToEditor("\n// A hostname or IP address is required.");
|
fileInput.onchange = () => {
|
||||||
return;
|
const file = fileInput.files[0];
|
||||||
}
|
|
||||||
|
|
||||||
axios.get(`${serverPrefix}dns-query/${hostname}`).then(response => {
|
if (!file)
|
||||||
const responseText = response.data;
|
return;
|
||||||
appendTextToEditor(`/*\n${responseText}\n*/`);
|
|
||||||
pushPromptMessage("system", responseText);
|
|
||||||
}).catch(error => {
|
|
||||||
console.error(error);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async function getTargetByUrl(urlPart) {
|
const reader = new FileReader();
|
||||||
const response = await fetch(`${serverPrefix}devtools/json`);
|
reader.onload = (e) => {
|
||||||
const targets = await response.json();
|
const fileName = file.name;
|
||||||
|
const ext = fileName.split('.').pop().toLowerCase();
|
||||||
|
const langMap = {
|
||||||
|
js: 'javascript', ts: 'typescript', html: 'html',
|
||||||
|
css: 'css', json: 'json', py: 'python', java: 'java',
|
||||||
|
c: 'c', cpp: 'cpp', cs: 'csharp', php: 'php',
|
||||||
|
rb: 'ruby', go: 'go', rs: 'rust'
|
||||||
|
};
|
||||||
|
const lang = langMap[ext] || 'plaintext';
|
||||||
|
|
||||||
const target = targets.find(target => target.url.includes(urlPart));
|
monaco.editor.setModelLanguage(editorRef.current.getModel(), lang);
|
||||||
|
editorRef.current.setValue(e.target.result);
|
||||||
|
|
||||||
if (target) {
|
fileNameRef.current = fileName;
|
||||||
console.log(`Found target: ${target.title} (${target.id})`);
|
};
|
||||||
return target.webSocketDebuggerUrl;
|
reader.readAsText(file);
|
||||||
} else {
|
};
|
||||||
console.log('Target not found');
|
fileInput.click();
|
||||||
return null;
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendPromptMessage(wsUrl, promptMessage) {
|
const saveFile = () => {
|
||||||
const socket = new WebSocket(wsUrl);
|
const text = editorRef.current.getValue();
|
||||||
const steps = [
|
const fileName = prompt("Enter file name:", fileNameRef.current);
|
||||||
{
|
|
||||||
id: 1,
|
if (!fileName)
|
||||||
method: 'Input.insertText',
|
return;
|
||||||
params: {
|
|
||||||
text: promptMessage
|
const blob = new Blob([text], { type: 'text/plain' });
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.download = fileNameRef.current;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendMessageToCopilot = () => {
|
||||||
|
const promptMessage = prompt("Enter a prompt message:", '');
|
||||||
|
if (!promptMessage || promptMessage.trim() == '') {
|
||||||
|
alert("A prompt message is required.");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
method: 'Input.dispatchKeyEvent',
|
|
||||||
params: {
|
|
||||||
type: 'keyDown',
|
|
||||||
key: 'Enter',
|
|
||||||
code: 'Enter'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
method: 'Runtime.evaluate',
|
|
||||||
params: {
|
|
||||||
expression: '((e)=>e[e.length-1].querySelector("code")?.innerText||e[e.length-1].innerText)(document.querySelectorAll("[data-content=ai-message]"))'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
socket.onopen = () => {
|
appendTextToEditor(`\n// ${promptMessage}... Generating text with Copilot...`);
|
||||||
steps.forEach((step) => {
|
pushPromptMessage("user", promptMessage);
|
||||||
if (step.id == 3) {
|
|
||||||
setTimeout(() => {
|
(async () => {
|
||||||
socket.send(JSON.stringify(step));
|
const targetWsUrl = await getTargetByUrl('copilot.microsoft.com');
|
||||||
}, 9000);
|
if (targetWsUrl) {
|
||||||
|
await _sendMessageToCopilot(targetWsUrl, promptMessage);
|
||||||
|
} else {
|
||||||
|
alert("Microsoft Copilot not running. Please visit copilot.microsoft.com first.");
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
|
||||||
|
const _sendMessageToCopilot = async (wsUrl, promptMessage) => {
|
||||||
|
const socket = new WebSocket(wsUrl);
|
||||||
|
const steps = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
method: 'Input.insertText',
|
||||||
|
params: {
|
||||||
|
text: promptMessage
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
method: 'Input.dispatchKeyEvent',
|
||||||
|
params: {
|
||||||
|
type: 'keyDown',
|
||||||
|
key: 'Enter',
|
||||||
|
code: 'Enter'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
method: 'Runtime.evaluate',
|
||||||
|
params: {
|
||||||
|
expression: '((e)=>e[e.length-1].querySelector("code")?.innerText||e[e.length-1].innerText)(document.querySelectorAll("[data-content=ai-message]"))'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
socket.onopen = () => {
|
||||||
|
steps.forEach((step) => {
|
||||||
|
if (step.id == 3) {
|
||||||
|
setTimeout(() => {
|
||||||
|
socket.send(JSON.stringify(step));
|
||||||
|
}, 9000);
|
||||||
|
} else {
|
||||||
|
socket.send(JSON.stringify(step));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onmessage = (event) => {
|
||||||
|
const response = JSON.parse(event.data);
|
||||||
|
console.log("Sent successfully:", response.result);
|
||||||
|
|
||||||
|
if (response.id == 3) {
|
||||||
|
const responseContent = response.result.result.value;
|
||||||
|
|
||||||
|
appendTextToEditor(responseContent);
|
||||||
|
pushPromptMessage("assistant", responseContent);
|
||||||
|
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTargetByUrl = async (urlPart) => {
|
||||||
|
const response = await fetch(`${serverPrefix}devtools/json`);
|
||||||
|
const targets = await response.json();
|
||||||
|
|
||||||
|
const target = targets.find(target => target.url.includes(urlPart));
|
||||||
|
|
||||||
|
if (target) {
|
||||||
|
console.log(`Found target: ${target.title} (${target.id})`);
|
||||||
|
return target.webSocketDebuggerUrl;
|
||||||
} else {
|
} else {
|
||||||
socket.send(JSON.stringify(step));
|
console.log('Target not found');
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
};
|
|
||||||
|
|
||||||
socket.onmessage = (event) => {
|
const sendMessageToAzureAi = () => {
|
||||||
const response = JSON.parse(event.data);
|
const promptMessage = prompt("Enter a prompt message:", '');
|
||||||
console.log("Sent successfully:", response.result);
|
if (!promptMessage || promptMessage.trim() == '') {
|
||||||
|
alert("A prompt message is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (response.id == 3) {
|
appendTextToEditor(`\n// ${promptMessage}... Generating text with Azure AI...`);
|
||||||
const responseContent = response.result.result.value;
|
pushPromptMessage("user", promptMessage);
|
||||||
|
|
||||||
appendTextToEditor(responseContent);
|
const apiKey = settingsRef.current.AzureAiServiceApiKey;
|
||||||
pushPromptMessage("assistant", responseContent);
|
const url = `${settingsRef.current.AzureAiServicePrefix}models/chat/completions?api-version=${settingsRef.current.AzureAiServiceApiVersion}`;
|
||||||
|
|
||||||
socket.close();
|
const data = {
|
||||||
}
|
messages: promptMessagesRef.current,
|
||||||
};
|
max_tokens: 2048,
|
||||||
}
|
temperature: 0.8,
|
||||||
|
top_p: 0.1,
|
||||||
|
presence_penalty: 0,
|
||||||
|
frequency_penalty: 0,
|
||||||
|
model: 'Phi-4'
|
||||||
|
};
|
||||||
|
|
||||||
fetchSettings();
|
axios.post(url, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'api-key': apiKey
|
||||||
|
}
|
||||||
|
}).then(response => {
|
||||||
|
response.data.choices.forEach(x => {
|
||||||
|
const responseContent = x.message.content;
|
||||||
|
pushPromptMessage("assistant", responseContent);
|
||||||
|
|
||||||
|
const responseText = DOMPurify.sanitize(responseContent, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
||||||
|
appendTextToEditor(`/*\n${responseText}\n*/`);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error.response?.data || error.message);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const savePromptMessages = () => {
|
||||||
|
const text = JSON.stringify(promptMessages.current, null, 4);
|
||||||
|
const blob = new Blob([text], { type: 'text/plain' });
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = URL.createObjectURL(blob);
|
||||||
|
a.download = "prompt.json";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadPromptMessages = () => {
|
||||||
|
const fileInput = document.createElement('input');
|
||||||
|
fileInput.type = 'file';
|
||||||
|
fileInput.accept = '.json';
|
||||||
|
fileInput.onchange = function (event) {
|
||||||
|
const file = event.target.files[0];
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function (e) {
|
||||||
|
promptMessages = JSON.parse(e.target.result);
|
||||||
|
appendTextToEditor("\n//Prompt loaded successfully.");
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
};
|
||||||
|
fileInput.click();
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryWhois = () => {
|
||||||
|
const hostname = prompt("Enter a hostname or IP address:", '');
|
||||||
|
if (!hostname || hostname.trim() == '') {
|
||||||
|
appendTextToEditor("\n// A hostname or IP address is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.get(`${serverPrefix}whois/${hostname}`).then(response => {
|
||||||
|
const responseText = DOMPurify.sanitize(response.data, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] });
|
||||||
|
appendTextToEditor(`/*\n${responseText}\n*/`);
|
||||||
|
pushPromptMessage("system", responseText);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryDns = () => {
|
||||||
|
const hostname = prompt("Enter a hostname or IP address:", '');
|
||||||
|
if (!hostname || hostname.trim() == '') {
|
||||||
|
appendTextToEditor("\n// A hostname or IP address is required.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
axios.get(`${serverPrefix}dns-query/${hostname}`).then(response => {
|
||||||
|
const responseText = response.data;
|
||||||
|
appendTextToEditor(`/*\n${responseText}\n*/`);
|
||||||
|
pushPromptMessage("system", responseText);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
resizeEditor();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.dispatchEvent(new Event('resize'));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
fetchSettings();
|
||||||
|
|
||||||
|
return _e('div', { className: 'app' },
|
||||||
|
_e(RibbonMenu, {
|
||||||
|
onOpenFileClick: openFile,
|
||||||
|
onSaveFileClick: saveFile,
|
||||||
|
onCopliotClick: sendMessageToCopilot,
|
||||||
|
onAzureAiClick: sendMessageToAzureAi,
|
||||||
|
onSavePromptClick: savePromptMessages,
|
||||||
|
onLoadPromptClick: loadPromptMessages,
|
||||||
|
onQueryWhoisClick: queryWhois,
|
||||||
|
onQueryDnsClick: queryDns
|
||||||
|
}),
|
||||||
|
_e('div', { className: 'banner' }, 'WelsonJS Editor'),
|
||||||
|
_e(Editor, { editorRef })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.getElementById('app');
|
||||||
|
const root = ReactDOM.createRoot(container);
|
||||||
|
root.render(_e(App));
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user