refactor: separate js code from html

This commit is contained in:
Levi Yan
2025-04-22 15:13:14 +08:00
repo.diff.parent b9720a9419
repo.diff.commit 63566f4ad5
repo.diff.stats_desc%!(EXTRA int=2, int=943, int=920)

repo.diff.view_file

@@ -0,0 +1,942 @@
<!-- ======================================= Script ==================================-->
<script>
// ===================================== Initialization ===========================
// Global variables
DEVSTAR_DOMAIN = ""
DEVSTAR_DOMAIN_TEST_URL = "" // It is empty by default in the production environment
var LANGUAGE = "zh-cn" // support display language: zh-cn or en
var USERTOKEN = null
var USERNAME = null
var SIGNED = false
window.onload = async function () {
if ("" === DEVSTAR_DOMAIN_TEST_URL) {
await getDevstarDomainFromVSCode()
.then(async devstarDomain => {
DEVSTAR_DOMAIN = devstarDomain.endsWith("/") ? devstarDomain.substring(0, devstarDomain.length - 1) : devstarDomain
})
.catch(error => {
DEVSTAR_DOMAIN = "https://devstar.cn"
console.error('Failed to get devstar domain: ', error, '. Use default domain: ', DEVSTAR_DOMAIN)
})
} else {
DEVSTAR_DOMAIN = DEVSTAR_DOMAIN_TEST_URL
}
// initiate home
// display language
const config = await getHomeConfigFromVSCode()
console.log(config)
// related to login status
await getUserTokenFromVSCode()
.then(async userToken => {
// verify user token
await verifyToken(userToken)
.then(result => {
// initialize user token
USERTOKEN = userToken
})
.catch(error => {
console.error('Error in verifying token:', error)
})
})
.catch(error => {
console.error("Failed to get user token from vscode: ", error)
})
await getUsernameFromVSCode()
.then(async username => {
USERNAME = username
})
.catch(error => {
console.error('Failed to get user name from vscode: ', error)
})
SIGNED = true ? USERTOKEN && USERNAME : false;
await reloadPageModules()
}
async function getHomeConfigFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getHomeConfig', null)
.then(async data => {
console.log(data)
const config = data.homeConfig
if (config === undefined) {
reject("homeConfig is undefined")
}
resolve(config)
})
.catch(error => {
reject(error)
})
})
}
async function loadCreatingRepoForm() {
// fetch template data
const issueLabels = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/label/templates2`)
const gitignores = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/gitignore/templates`)
const licenses = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/licenses`)
const readmes = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/readme/templates`).then(data => { return data.data })
const object_formats = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/object_formats`).then(data => { return data.data })
// console.log('issueLabels, gitignores, licenses, readmes, object_formats', issueLabels, gitignores, licenses, readmes, object_formats)
// generate menus
const issue_label_menu = document.getElementById('issue_label_menu');
issueLabels.forEach(label => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', label.DisplayName);
newItem.innerHTML = `${label.DisplayName}<br><i>(${label.Description})</i>`;
issue_label_menu.appendChild(newItem);
});
const gitignore_menu = document.getElementById('gitignore_menu');
gitignores.forEach(gitignore => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', gitignore)
newItem.innerHTML = `${gitignore}`;
gitignore_menu.appendChild(newItem);
})
const license_menu = document.getElementById('license_menu');
licenses.forEach(license => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', license.name)
newItem.innerHTML = `${license.name}`;
license_menu.appendChild(newItem);
})
const readme_menu = document.getElementById('readme_menu');
readmes.forEach(readme => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', readme.name)
newItem.innerHTML = `${readme.name}`;
readme_menu.appendChild(newItem);
})
const defaultBranchElement = document.getElementById('default_branch');
if (defaultBranchElement) {
defaultBranchElement.value = 'main'; // Set the value of the input field to "main"
defaultBranchElement.placeholder = 'main'; // Optionally update the placeholder as well
}
const defaultObjectFormatElement = document.getElementById('object_format_name');
if (defaultObjectFormatElement) {
defaultObjectFormatElement.value = object_formats[0].name
}
const default_object_format_name = document.getElementById('default_object_format_name')
if (default_object_format_name) {
default_object_format_name.innerHTML = object_formats[0].name
}
const object_format_menu = document.getElementById('object_format_menu')
object_formats.forEach(object_format => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', object_format.name)
newItem.innerHTML = `${object_format.name}`;
object_format_menu.appendChild(newItem);
})
}
async function fetchTemplateData(url) {
return new Promise((resolve, reject) => {
try {
fetch(url, {
method: 'GET',
})
.then(response => {
response.json().then(data => {
if (response.ok) {
resolve(data)
} else {
throw new Error(`Failed to fetch template data from url: ${url} Error: ${data.message}`)
}
})
})
} catch (error) {
reject(error)
console.error(error)
}
})
}
async function getDevstarDomainFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getDevstarDomain', null)
.then(async data => {
const devstarDomain = data.devstarDomain
if (undefined === devstarDomain) {
reject("devstar domain is undefined")
}
resolve(devstarDomain)
})
.catch(error => {
reject(error)
})
})
}
async function getUserTokenFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUserToken', null)
.then(async data => {
const userToken = data.userToken
if (userToken === undefined) {
reject("userToken is undefined")
}
resolve(userToken)
})
.catch(error => {
reject(error)
})
})
}
async function getUsernameFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUsername', null)
.then(async data => {
const username = data.username
if (username === undefined) {
reject('username is undefined')
}
resolve(username)
})
.catch(error => {
reject(error)
})
})
}
function verifyToken(token) {
return new Promise((resolve, reject) => {
fetch(DEVSTAR_DOMAIN + '/api/devcontainer/user', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
if (response.ok) {
resolve(response.status)
} else {
reject(new Error("Error code", response.status))
}
})
})
}
async function reloadPageModules() {
// everytime login status change, call this function
if (SIGNED) {
// head
// document.getElementById('signup-link').style.display = 'none'
document.getElementById('signin-link').style.display = 'none'
document.getElementById('logout-link').style.display = 'flex'
// html content
document.getElementById('contentBeforeSigned').style.display = 'none'
document.getElementById('contentAfterSigned').style.display = 'block'
// load data for page
loadRepositories() // repo list
await loadCreatingRepoForm() // form for creating repo
} else {
// document.getElementById('signup-link').style.display = 'flex'
document.getElementById('signin-link').style.display = 'flex'
document.getElementById('logout-link').style.display = 'none'
// html content
document.getElementById('contentBeforeSigned').style.display = 'block'
document.getElementById('contentAfterSigned').style.display = 'none'
}
}
// ===================================== login and logout ===========================
// login model
function openLoginModal() {
// check if user has logged in, only show login modal when user has not logged in
if (SIGNED) {
console.log('User has logged in')
showInformationNotification('已登录')
} else {
document.getElementById('loginModal').style.display = 'block';
}
}
function closeLoginModal() {
document.getElementById('loginModal').style.display = 'none';
}
async function login() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
const url = DEVSTAR_DOMAIN + `/api/v1/users/${username}/tokens`;
// Base64编码用户名和密码
const base64Credentials = btoa(username + ':' + password);
const tokenName = generateTokenName(10);
postData = {
"name": tokenName,
"scopes": ["write:user", "write:repository"]
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + base64Credentials
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(async data => {
if (response.ok) {
// store token in global variable and vscode global state
USERTOKEN = data.sha1;
USERNAME = username;
setUserTokenToVSCode(USERTOKEN);
setUsernameToVSCode(username);
SIGNED = true
await reloadPageModules()
closeLoginModal()
// if user public key exist, meaning that public key has been uploaded
await getUserPublicKeyFromVSCode()
.then(async userPublicKey => {
if (userPublicKey === '') {
await createUserPublicKeyByVSCode()
.then(async () => {
// only upload new created public key
await getUserPublicKeyFromVSCode()
.then(async userPublicKey => {
const dataFromResp = await communicateVSCodeByWebview('getMachineName', {});
const machineName = dataFromResp.machineName;
// key title: username-machine-timestamp
const timestamp = Date.now();
const keyTitle = `${USERNAME}-${machineName}-${timestamp}`
await uploadNewCreatedPublicKey(userPublicKey, keyTitle);
})
.catch(error => {
console.error("Failed to get NEW CREATED user public key from vscode: ", error)
})
})
}
})
.catch(error => {
console.error("Failed to get user public key from vscode: ", error)
})
} else {
showErrorNotification(`登录失败!\nError: ${data.message}`)
throw new Error(`登录失败!\nError: ${data.message}`);
}
})
})
.catch(error => {
console.error('There has been a problem when logging', error);
});
}
function generateTokenName(length = 10) {
// tokenName is random string and number and _ combination (10 characters)
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_';
let name = 'vscode_login_token_';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
name += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return name
}
async function logout() {
// remove token and username from global variable and vscode global state
USERTOKEN = null
USERNAME = null
await setUserTokenToVSCode('')
await setUsernameToVSCode('')
SIGNED = false
await reloadPageModules()
}
async function setUserTokenToVSCode(userToken) {
var removeUserToken = false;
if ('' === userToken) {
removeUserToken = true
}
communicateVSCodeByWebview('setUserToken', { userToken: userToken })
.then(result => {
if (result.ok) {
console.log(removeUserToken ? 'User token has been removed from vscode' : 'User token has been stored in vscode')
} else {
console.error(removeUserToken ? 'Failed to remove user token from vscode' : 'Failed to store user token into vscode')
}
})
.catch(error => {
console.error('Failed to set user token into vscode:', error)
})
}
async function setUsernameToVSCode(username) {
var removeUsername = false;
if ('' === username) {
removeUsername = true;
}
await communicateVSCodeByWebview('setUsername', { username: username })
.then(result => {
if (result.ok) {
console.log(removeUsername ? 'User name has been removed from vscode' : 'User name has been stored in vscode')
} else {
console.error(removeUsername ? 'Failed to removing user name from vscode' : 'Failed to store user name in vscode')
}
})
.catch(error => {
console.error("Error happened when setting user name: ", error)
})
}
async function getUserPublicKeyFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUserPublicKey', {})
.then(data => {
const publicKey = data.userPublicKey;
resolve(publicKey)
})
.catch(error => {
reject(error)
})
})
}
async function createUserPublicKeyByVSCode() {
await communicateVSCodeByWebview('createUserPublicKey', {})
.then(res => {
if (res.ok) {
console.log('User public key has been created')
} else {
console.error('Failed to create user public key')
}
})
.catch(error => {
console.error('Failed to request to create user public key: ', erro)
})
}
async function uploadNewCreatedPublicKey(userPublicKey, keyTitle) {
const postData = {
"key": userPublicKey,
"title": keyTitle
}
const uploadUrl = DEVSTAR_DOMAIN + `/api/v1/user/keys`;
fetch(uploadUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + USERTOKEN
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
if (response.ok) {
console.log("Successfully upload new created public key.\n", data)
} else {
throw new Error(`Failed to upload new created public key!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
// ===================================== Repo ===========================
function loadRepositories() {
// clear old data
const repoList = document.getElementById('repo_list')
repoList.innerHTML = '';
// load new data
var url = DEVSTAR_DOMAIN + "/api/v1/user/repos?page=1&limit=20"
var token = USERTOKEN
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
if (response.ok) {
let repos = data;
repos.forEach((repo, index) => {
const repoFullName = repo.full_name;
const repoURL = repo.html_url;
const repoID = repo.id;
hasDevContainer(repoID)
.then(found => {
addItemForRepoList(repoFullName, repoURL, repoID, found)
})
})
} else {
throw new Error(`Failed to load repos, Error: ${data.message}`);
}
})
})
.catch(error => {
console.error(error);
});
}
function addItemForRepoList(repoFullName, repoURL, repoID, hasDevContainer) {
const repoList = document.getElementById('repo_list');
// 创建新的 flex-item 元素
const flexItem = document.createElement('div');
flexItem.classList.add('flex-item');
// 创建 flex-item-main
const flexItemMain = document.createElement('div');
flexItemMain.classList.add('flex-item-main');
// 创建 flex-item-header
const flexItemHeader = document.createElement('div');
flexItemHeader.classList.add('flex-item-header');
// 创建 flex-item-title
const flexItemTitle = document.createElement('div');
flexItemTitle.classList.add('flex-item-title');
// 创建名称部分
const repoNameDiv = document.createElement('div');
repoNameDiv.classList.add('text', 'name');
repoNameDiv.textContent = repoFullName;
// 创建 flex-item-trailing
const flexItemTrailing = document.createElement('div');
flexItemTrailing.classList.add('flex-item-trailing');
// 创建按钮
const createContainerBtn = document.createElement('button');
createContainerBtn.id = `${repoID}_createContainerBtn`
createContainerBtn.classList.add('ui', 'green', 'button');
createContainerBtn.textContent = 'Create Container';
createContainerBtn.onclick = function () {
createDevContainer(repoID)
}
const openProjectBtn = document.createElement('button');
openProjectBtn.id = `${repoID}_openProjectBtn`
openProjectBtn.classList.add('ui', 'primary', 'button');
openProjectBtn.textContent = 'Open Project';
openProjectBtn.onclick = function () {
openDevContainer(repoID)
}
const deleteContainerBtn = document.createElement('button');
deleteContainerBtn.id = `${repoID}_deleteContainerBtn`
deleteContainerBtn.classList.add('ui', 'red', 'button');
deleteContainerBtn.textContent = 'Delete Container';
deleteContainerBtn.onclick = function () {
deleteDevContainer(repoID)
}
// 创建 flex-item-body
const flexItemBody = document.createElement('div');
flexItemBody.classList.add('flex-item-body');
flexItemBody.textContent = repoURL;
// 组合所有部分
flexItemTitle.appendChild(repoNameDiv);
flexItemTrailing.appendChild(createContainerBtn);
flexItemTrailing.appendChild(openProjectBtn);
flexItemTrailing.appendChild(deleteContainerBtn);
flexItemHeader.appendChild(flexItemTitle);
flexItemHeader.appendChild(flexItemTrailing);
flexItemMain.appendChild(flexItemHeader);
flexItemMain.appendChild(flexItemBody);
flexItem.appendChild(flexItemMain);
// 将新创建的 flex-item 添加到 repo_list 中
repoList.appendChild(flexItem);
// 调整按钮显示
adjustBtnForRepoItem(repoID, hasDevContainer)
}
function adjustBtnForRepoItem(repoId, hasDevContainer) {
const createContainerBtn = document.getElementById(`${repoId}_createContainerBtn`);
const openProjectBtn = document.getElementById(`${repoId}_openProjectBtn`);
const deleteContainerBtn = document.getElementById(`${repoId}_deleteContainerBtn`);
if (hasDevContainer) {
createContainerBtn.style.display = 'none';
openProjectBtn.style.display = 'block';
deleteContainerBtn.style.display = 'block';
} else {
createContainerBtn.style.display = 'block';
openProjectBtn.style.display = 'none';
deleteContainerBtn.style.display = 'none';
}
}
// 创建仓库modal
function openModalForCreateRepository() {
document.getElementById('modalForCreatingRepo').style.display = "block";
}
function closeModalForCreatingRepo() {
document.getElementById('modalForCreatingRepo').style.display = "none";
}
// 点击窗外关闭弹窗
window.onclick = function (event) {
if (event.target == document.getElementById('modalForCreatingRepo')) {
closeModalForCreatingRepo();
}
if (event.target == document.getElementById('loginModal')) {
closeLoginModal();
}
}
function createRepo() {
// 这里添加实际的表单提交逻辑
// 模拟表单处理
var repo_name = document.getElementById('repo_name').value;
// 暂时不支持private
// var is_private = document.getElementById('is_private').checked;
var description = document.getElementById('description').value;
var template_url = document.getElementById('devstar_template').value;
var issue_labels = document.getElementById('issue_label').value
var gitignore = document.getElementById('gitignore').value
var license = document.getElementById('license').value
var readme = document.getElementById('readme').value
var default_branch = document.getElementById('default_branch').value;
var object_format_name = document.getElementById('object_format_name').value;
var is_template = document.getElementById('is_template').checked
const url = DEVSTAR_DOMAIN + "/api/v1/user/repos"
var token = USERTOKEN
const postData = {
'name': repo_name,
// 'private': is_private,
'trust_model': 'default',
"auto_init": true
}
if (description) {
postData.description = description
}
if (issue_labels) {
postData.issue_labels = issue_labels
}
if (gitignore) {
postData.gitignore = gitignore
}
if (license) {
postData.license = license
}
if (readme) {
postData.readme = readme
}
if (default_branch) {
postData.default_branch = default_branch
}
if (object_format_name) {
postData.object_format_name = object_format_name
}
if (is_template) {
postData.template = is_template
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
if (response.ok) {
showInformationNotification('项目创建成功!')
console.log('项目创建成功!')
loadRepositories()
closeModalForCreatingRepo(); // 关闭创建项目弹窗
} else {
showErrorNotification(`项目创建失败!\nError: ${data.message}`)
throw new Error(`项目创建失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
return false;
}
// ===================================== Projects ===========================
async function openDevContainer(repoId) {
console.log("opening project")
// open devcontainer through repoId
var url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const queryParams = new URLSearchParams({
repoId: repoId,
wait: true
}).toString();
const urlWithParams = `${url}?${queryParams}`;
fetch(urlWithParams, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
// container start successfully
// get devContainer ssh connection information
const devContainerHost = data.data.devContainerHost
const devContainerUsername = data.data.devContainerUsername
const devContainerPort = data.data.devContainerPort
const devContainerWorkDir = data.data.devContainerWorkDir
// default: open with key
communicateVSCodeByWebview('firstOpenRemoteFolder', {
host: `${devContainerHost}`,
username: `${devContainerUsername}`,
port: `${devContainerPort}`,
path: `${devContainerWorkDir}`
})
} else if (response.ok) {
// TODO: 等待的code
console.warn(`打开容器失败code: ${responseCode}\nError: ${data.data.ErrorMsg}`)
showErrorNotification(`打开容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`打开容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`打开容器失败!\nError: ${data.message}`)
throw new Error(`打开容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
async function hasDevContainer(repoId) {
return new Promise(async (resolve) => {
url = DEVSTAR_DOMAIN + "/api/devcontainer/user"
token = USERTOKEN
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
if (response.ok) {
let found = false
const devContainers = data.data.devContainers;
devContainers.forEach((c, index) => {
if (String(c.repoId) === String(repoId)) {
// has devContainer
found = true
resolve(true)
}
});
if (!found) {
resolve(false)
}
} else {
throw new Error(`Failed to fetch devContainer list.\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
})
}
async function createDevContainer(repoId) {
showInformationNotification("正在创建开发容器...")
// request creating container
const url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const postData = {
"repoId": repoId.toString(),
// "sshPublicKeyList": [publicKey]
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
showInformationNotification(`创建容器成功!`)
adjustBtnForRepoItem(repoId, true)
console.log('Successfully create dev container!')
} else if (response.ok) {
console.warn(responseCode)
showErrorNotification(`创建容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`创建容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`创建容器失败!\nError: ${data.message}`)
throw new Error(`创建容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
function deleteDevContainer(repoId) {
var url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const queryParams = new URLSearchParams({
repoId: repoId,
}).toString();
const urlWithParams = `${url}?${queryParams}`;
fetch(urlWithParams, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
showInformationNotification("删除容器成功!")
adjustBtnForRepoItem(repoId, false)
console.log('Successfully delete dev container belong to repoId:', repoId, data)
} else if (response.ok) {
showErrorNotification(`删除容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`删除容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`删除容器失败!\nError: ${data.message}`)
throw new Error(`删除容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error)
})
}
function firstOpenRemoteFolder(host, username, password, port, path) {
const message = {
action: 'firstOpenRemoteFolder',
host: host,
username: username,
password: password,
port: port,
path: path,
}
// 向iframe父页面发送消息
window.parent.postMessage(message, '*');
}
function openRemoteFolder(host, path) {
const message = {
action: 'openRemoteFolder',
host: host,
path: path,
}
// 向iframe父页面发送消息
window.parent.postMessage(message, '*');
}
// ===================================== Utils ===========================
// 统一通知
function showInformationNotification(message) {
communicateVSCodeByWebviewNoReturn('showInformationNotification', { message: message })
}
function showWarningNotification(message) {
communicateVSCodeByWebviewNoReturn('showWarningNotification', { message: message })
}
function showErrorNotification(message) {
communicateVSCodeByWebviewNoReturn('showErrorNotification', { message: message })
}
async function communicateVSCodeByWebviewNoReturn(action, data) {
window.parent.postMessage({ target: "vscode_no_return", action: action, data: data }, "*");
}
async function communicateVSCodeByWebview(action, data) {
return new Promise((resolve, reject) => {
// request to webview
window.parent.postMessage({ target: "vscode", action: action, data: data }, '*');
// response from webview
function handleResponse(event) {
const jsonData = event.data
if (jsonData.action === action) {
// return webview response
console.log("dataFromVSCodeByWebview", jsonData.data)
window.removeEventListener('message', handleResponse) // 清理监听器
resolve(jsonData.data)
}
}
window.addEventListener('message', handleResponse)
setTimeout(() => {
window.removeEventListener('message', handleResponse)
reject('timeout')
}, 10000); // 10秒超时
})
}
</script>

repo.diff.view_file

@@ -373,923 +373,4 @@
</body>
</html>
{{template "base/footer" .}}
<!-- ======================================= Script ==================================-->
<script>
// ===================================== Initialization ===========================
// Global variables
DEVSTAR_DOMAIN = ""
DEVSTAR_DOMAIN_TEST_URL = "" // It is empty by default in the production environment
var USERTOKEN = null
var USERNAME = null
var SIGNED = false
window.onload = async function () {
if ("" === DEVSTAR_DOMAIN_TEST_URL) {
await getDevstarDomainFromVSCode()
.then(async devstarDomain => {
DEVSTAR_DOMAIN = devstarDomain.endsWith("/") ? devstarDomain.substring(0, devstarDomain.length-1) : devstarDomain
})
.catch(error => {
DEVSTAR_DOMAIN = "https://devstar.cn"
console.error('Failed to get devstar domain: ', error, '. Use default domain: ', DEVSTAR_DOMAIN)
})
} else {
DEVSTAR_DOMAIN = DEVSTAR_DOMAIN_TEST_URL
}
// related to login status
await getUserTokenFromVSCode()
.then(async userToken => {
// verify user token
await verifyToken(userToken)
.then(result => {
// initialize user token
USERTOKEN = userToken
})
.catch(error => {
console.error('Error in verifying token:', error)
})
})
.catch(error => {
console.error("Failed to get user token from vscode: ", error)
})
await getUsernameFromVSCode()
.then(async username => {
USERNAME = username
})
.catch(error => {
console.error('Failed to get user name from vscode: ', error)
})
SIGNED = true ? USERTOKEN && USERNAME : false;
await reloadPageModules()
}
async function loadCreatingRepoForm() {
// fetch template data
const issueLabels = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/label/templates2`)
const gitignores = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/gitignore/templates`)
const licenses = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/licenses`)
const readmes = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/readme/templates`).then(data => {return data.data})
const object_formats = await fetchTemplateData(DEVSTAR_DOMAIN + `/api/v1/object_formats`).then(data => {return data.data})
// console.log('issueLabels, gitignores, licenses, readmes, object_formats', issueLabels, gitignores, licenses, readmes, object_formats)
// generate menus
const issue_label_menu = document.getElementById('issue_label_menu');
issueLabels.forEach(label => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', label.DisplayName);
newItem.innerHTML = `${label.DisplayName}<br><i>(${label.Description})</i>`;
issue_label_menu.appendChild(newItem);
});
const gitignore_menu = document.getElementById('gitignore_menu');
gitignores.forEach(gitignore => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', gitignore)
newItem.innerHTML = `${gitignore}`;
gitignore_menu.appendChild(newItem);
})
const license_menu = document.getElementById('license_menu');
licenses.forEach(license => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', license.name)
newItem.innerHTML = `${license.name}`;
license_menu.appendChild(newItem);
})
const readme_menu = document.getElementById('readme_menu');
readmes.forEach(readme => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', readme.name)
newItem.innerHTML = `${readme.name}`;
readme_menu.appendChild(newItem);
})
const defaultBranchElement = document.getElementById('default_branch');
if (defaultBranchElement) {
defaultBranchElement.value = 'main'; // Set the value of the input field to "main"
defaultBranchElement.placeholder = 'main'; // Optionally update the placeholder as well
}
const defaultObjectFormatElement = document.getElementById('object_format_name');
if (defaultObjectFormatElement) {
defaultObjectFormatElement.value = object_formats[0].name
}
const default_object_format_name = document.getElementById('default_object_format_name')
if(default_object_format_name) {
default_object_format_name.innerHTML = object_formats[0].name
}
const object_format_menu = document.getElementById('object_format_menu')
object_formats.forEach(object_format => {
const newItem = document.createElement('div');
newItem.classList.add('item');
newItem.setAttribute('data-value', object_format.name)
newItem.innerHTML = `${object_format.name}`;
object_format_menu.appendChild(newItem);
})
}
async function fetchTemplateData(url) {
return new Promise((resolve, reject) => {
try {
fetch(url, {
method: 'GET',
})
.then(response => {
response.json().then(data => {
if (response.ok) {
resolve(data)
} else {
throw new Error(`Failed to fetch template data from url: ${url} Error: ${data.message}`)
}
})
})
} catch (error) {
reject(error)
console.error(error)
}
})
}
async function getDevstarDomainFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getDevstarDomain', null)
.then(async data => {
const devstarDomain = data.devstarDomain
if (undefined === devstarDomain) {
reject("devstar domain is undefined")
}
resolve(devstarDomain)
})
.catch(error => {
reject(error)
})
})
}
async function getUserTokenFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUserToken', null)
.then(async data => {
const userToken = data.userToken
if (userToken === undefined) {
reject("userToken is undefined")
}
resolve(userToken)
})
.catch(error => {
reject(error)
})
})
}
async function getUsernameFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUsername', null)
.then(async data => {
const username = data.username
if (username === undefined) {
reject('username is undefined')
}
resolve(username)
})
.catch(error => {
reject(error)
})
})
}
function verifyToken(token) {
return new Promise((resolve, reject) => {
fetch(DEVSTAR_DOMAIN + '/api/devcontainer/user', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
if (response.ok) {
resolve(response.status)
} else {
reject(new Error("Error code", response.status))
}
})
})
}
async function reloadPageModules() {
// everytime login status change, call this function
if (SIGNED) {
// head
// document.getElementById('signup-link').style.display = 'none'
document.getElementById('signin-link').style.display = 'none'
document.getElementById('logout-link').style.display = 'flex'
// html content
document.getElementById('contentBeforeSigned').style.display = 'none'
document.getElementById('contentAfterSigned').style.display = 'block'
// load data for page
loadRepositories() // repo list
await loadCreatingRepoForm() // form for creating repo
} else {
// document.getElementById('signup-link').style.display = 'flex'
document.getElementById('signin-link').style.display = 'flex'
document.getElementById('logout-link').style.display = 'none'
// html content
document.getElementById('contentBeforeSigned').style.display = 'block'
document.getElementById('contentAfterSigned').style.display = 'none'
}
}
// ===================================== login and logout ===========================
// login model
function openLoginModal() {
// check if user has logged in, only show login modal when user has not logged in
if (SIGNED) {
console.log('User has logged in')
showInformationNotification('')
} else {
document.getElementById('loginModal').style.display = 'block';
}
}
function closeLoginModal() {
document.getElementById('loginModal').style.display = 'none';
}
async function login() {
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
const url = DEVSTAR_DOMAIN + `/api/v1/users/${username}/tokens`;
// Base64编码用户名和密码
const base64Credentials = btoa(username + ':' + password);
const tokenName = generateTokenName(10);
postData = {
"name": tokenName,
"scopes": ["write:user", "write:repository"]
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + base64Credentials
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(async data => {
if (response.ok) {
// store token in global variable and vscode global state
USERTOKEN = data.sha1;
USERNAME = username;
setUserTokenToVSCode(USERTOKEN);
setUsernameToVSCode(username);
SIGNED = true
await reloadPageModules()
closeLoginModal()
// if user public key exist, meaning that public key has been uploaded
await getUserPublicKeyFromVSCode()
.then(async userPublicKey => {
if (userPublicKey === '') {
await createUserPublicKeyByVSCode()
.then(async () => {
// only upload new created public key
await getUserPublicKeyFromVSCode()
.then(async userPublicKey => {
const dataFromResp = await communicateVSCodeByWebview('getMachineName', {});
const machineName = dataFromResp.machineName;
// key title: username-machine-timestamp
const timestamp = Date.now();
const keyTitle = `${USERNAME}-${machineName}-${timestamp}`
await uploadNewCreatedPublicKey(userPublicKey, keyTitle);
})
.catch(error => {
console.error("Failed to get NEW CREATED user public key from vscode: ", error)
})
})
}
})
.catch(error => {
console.error("Failed to get user public key from vscode: ", error)
})
} else {
showErrorNotification(`登录失败!\nError: ${data.message}`)
throw new Error(`登录失败!\nError: ${data.message}`);
}
})
})
.catch(error => {
console.error('There has been a problem when logging', error);
});
}
function generateTokenName(length = 10) {
// tokenName is random string and number and _ combination (10 characters)
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_';
let name = 'vscode_login_token_';
const charactersLength = characters.length;
for (let i = 0; i < length; i++) {
name += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return name
}
async function logout() {
// remove token and username from global variable and vscode global state
USERTOKEN = null
USERNAME = null
await setUserTokenToVSCode('')
await setUsernameToVSCode('')
SIGNED = false
await reloadPageModules()
}
async function setUserTokenToVSCode(userToken) {
var removeUserToken = false;
if ('' === userToken) {
removeUserToken = true
}
communicateVSCodeByWebview('setUserToken', {userToken: userToken})
.then(result => {
if (result.ok) {
console.log(removeUserToken ? 'User token has been removed from vscode' : 'User token has been stored in vscode')
} else {
console.error(removeUserToken ? 'Failed to remove user token from vscode' : 'Failed to store user token into vscode')
}
})
.catch(error => {
console.error('Failed to set user token into vscode:', error)
})
}
async function setUsernameToVSCode(username) {
var removeUsername = false;
if ('' === username) {
removeUsername = true;
}
await communicateVSCodeByWebview('setUsername', {username: username})
.then(result => {
if (result.ok) {
console.log(removeUsername ? 'User name has been removed from vscode' : 'User name has been stored in vscode')
} else {
console.error(removeUsername ? 'Failed to removing user name from vscode' : 'Failed to store user name in vscode')
}
})
.catch(error => {
console.error("Error happened when setting user name: ", error)
})
}
async function getUserPublicKeyFromVSCode() {
return new Promise(async (resolve, reject) => {
await communicateVSCodeByWebview('getUserPublicKey', {})
.then(data => {
const publicKey = data.userPublicKey;
resolve(publicKey)
})
.catch(error => {
reject(error)
})
})
}
async function createUserPublicKeyByVSCode() {
await communicateVSCodeByWebview('createUserPublicKey', {})
.then(res => {
if (res.ok) {
console.log('User public key has been created')
} else {
console.error('Failed to create user public key')
}
})
.catch(error => {
console.error('Failed to request to create user public key: ', erro)
})
}
async function uploadNewCreatedPublicKey(userPublicKey, keyTitle) {
const postData = {
"key": userPublicKey,
"title": keyTitle
}
const uploadUrl = DEVSTAR_DOMAIN + `/api/v1/user/keys`;
fetch(uploadUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + USERTOKEN
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
if (response.ok) {
console.log("Successfully upload new created public key.\n", data)
} else {
throw new Error(`Failed to upload new created public key!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
// ===================================== Repo ===========================
function loadRepositories() {
// clear old data
const repoList = document.getElementById('repo_list')
repoList.innerHTML = '';
// load new data
var url = DEVSTAR_DOMAIN + "/api/v1/user/repos?page=1&limit=20"
var token = USERTOKEN
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
if (response.ok) {
let repos = data;
repos.forEach((repo, index) => {
const repoFullName = repo.full_name;
const repoURL = repo.html_url;
const repoID = repo.id;
hasDevContainer(repoID)
.then(found => {
addItemForRepoList(repoFullName, repoURL, repoID, found)
})
})
} else {
throw new Error(`Failed to load repos, Error: ${data.message}`);
}
})
})
.catch(error => {
console.error(error);
});
}
function addItemForRepoList(repoFullName, repoURL, repoID, hasDevContainer) {
const repoList = document.getElementById('repo_list');
// 创建新的 flex-item 元素
const flexItem = document.createElement('div');
flexItem.classList.add('flex-item');
// 创建 flex-item-main
const flexItemMain = document.createElement('div');
flexItemMain.classList.add('flex-item-main');
// 创建 flex-item-header
const flexItemHeader = document.createElement('div');
flexItemHeader.classList.add('flex-item-header');
// 创建 flex-item-title
const flexItemTitle = document.createElement('div');
flexItemTitle.classList.add('flex-item-title');
// 创建名称部分
const repoNameDiv = document.createElement('div');
repoNameDiv.classList.add('text', 'name');
repoNameDiv.textContent = repoFullName;
// 创建 flex-item-trailing
const flexItemTrailing = document.createElement('div');
flexItemTrailing.classList.add('flex-item-trailing');
// 创建按钮
const createContainerBtn = document.createElement('button');
createContainerBtn.id = `${repoID}_createContainerBtn`
createContainerBtn.classList.add('ui', 'green', 'button');
createContainerBtn.textContent = 'Create Container';
createContainerBtn.onclick = function () {
createDevContainer(repoID)
}
const openProjectBtn = document.createElement('button');
openProjectBtn.id = `${repoID}_openProjectBtn`
openProjectBtn.classList.add('ui', 'primary', 'button');
openProjectBtn.textContent = 'Open Project';
openProjectBtn.onclick = function () {
openDevContainer(repoID)
}
const deleteContainerBtn = document.createElement('button');
deleteContainerBtn.id = `${repoID}_deleteContainerBtn`
deleteContainerBtn.classList.add('ui', 'red', 'button');
deleteContainerBtn.textContent = 'Delete Container';
deleteContainerBtn.onclick = function () {
deleteDevContainer(repoID)
}
// 创建 flex-item-body
const flexItemBody = document.createElement('div');
flexItemBody.classList.add('flex-item-body');
flexItemBody.textContent = repoURL;
// 组合所有部分
flexItemTitle.appendChild(repoNameDiv);
flexItemTrailing.appendChild(createContainerBtn);
flexItemTrailing.appendChild(openProjectBtn);
flexItemTrailing.appendChild(deleteContainerBtn);
flexItemHeader.appendChild(flexItemTitle);
flexItemHeader.appendChild(flexItemTrailing);
flexItemMain.appendChild(flexItemHeader);
flexItemMain.appendChild(flexItemBody);
flexItem.appendChild(flexItemMain);
// 将新创建的 flex-item 添加到 repo_list 中
repoList.appendChild(flexItem);
// 调整按钮显示
adjustBtnForRepoItem(repoID, hasDevContainer)
}
function adjustBtnForRepoItem(repoId, hasDevContainer) {
const createContainerBtn = document.getElementById(`${repoId}_createContainerBtn`);
const openProjectBtn = document.getElementById(`${repoId}_openProjectBtn`);
const deleteContainerBtn = document.getElementById(`${repoId}_deleteContainerBtn`);
if (hasDevContainer) {
createContainerBtn.style.display = 'none';
openProjectBtn.style.display = 'block';
deleteContainerBtn.style.display = 'block';
} else {
createContainerBtn.style.display = 'block';
openProjectBtn.style.display = 'none';
deleteContainerBtn.style.display = 'none';
}
}
// 创建仓库modal
function openModalForCreateRepository() {
document.getElementById('modalForCreatingRepo').style.display = "block";
}
function closeModalForCreatingRepo() {
document.getElementById('modalForCreatingRepo').style.display = "none";
}
// 点击窗外关闭弹窗
window.onclick = function (event) {
if (event.target == document.getElementById('modalForCreatingRepo')) {
closeModalForCreatingRepo();
}
if (event.target == document.getElementById('loginModal')) {
closeLoginModal();
}
}
function createRepo() {
// 这里添加实际的表单提交逻辑
// 模拟表单处理
var repo_name = document.getElementById('repo_name').value;
// 暂时不支持private
// var is_private = document.getElementById('is_private').checked;
var description = document.getElementById('description').value;
var template_url = document.getElementById('devstar_template').value;
var issue_labels = document.getElementById('issue_label').value
var gitignore = document.getElementById('gitignore').value
var license = document.getElementById('license').value
var readme = document.getElementById('readme').value
var default_branch = document.getElementById('default_branch').value;
var object_format_name = document.getElementById('object_format_name').value;
var is_template = document.getElementById('is_template').checked
const url = DEVSTAR_DOMAIN + "/api/v1/user/repos"
var token = USERTOKEN
const postData = {
'name': repo_name,
// 'private': is_private,
'trust_model': 'default',
"auto_init": true
}
if (description) {
postData.description = description
}
if (issue_labels) {
postData.issue_labels = issue_labels
}
if (gitignore) {
postData.gitignore = gitignore
}
if (license) {
postData.license = license
}
if (readme) {
postData.readme = readme
}
if (default_branch) {
postData.default_branch = default_branch
}
if (object_format_name) {
postData.object_format_name = object_format_name
}
if (is_template) {
postData.template = is_template
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
if (response.ok) {
showInformationNotification('')
console.log('')
loadRepositories()
closeModalForCreatingRepo(); // 关闭创建项目弹窗
} else {
showErrorNotification(`项目创建失败!\nError: ${data.message}`)
throw new Error(`项目创建失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
return false;
}
// ===================================== Projects ===========================
async function openDevContainer(repoId) {
console.log("opening project")
// open devcontainer through repoId
var url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const queryParams = new URLSearchParams({
repoId: repoId,
wait: true
}).toString();
const urlWithParams = `${url}?${queryParams}`;
fetch(urlWithParams, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
// container start successfully
// get devContainer ssh connection information
const devContainerHost = data.data.devContainerHost
const devContainerUsername = data.data.devContainerUsername
const devContainerPort = data.data.devContainerPort
const devContainerWorkDir = data.data.devContainerWorkDir
// default: open with key
communicateVSCodeByWebview('firstOpenRemoteFolder', {
host: `${devContainerHost}`,
username: `${devContainerUsername}`,
port: `${devContainerPort}`,
path: `${devContainerWorkDir}`
})
} else if (response.ok) {
// TODO: 等待的code
console.warn(`打开容器失败code: ${responseCode}\nError: ${data.data.ErrorMsg}`)
showErrorNotification(`打开容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`打开容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`打开容器失败!\nError: ${data.message}`)
throw new Error(`打开容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
async function hasDevContainer(repoId) {
return new Promise(async (resolve) => {
url = DEVSTAR_DOMAIN + "/api/devcontainer/user"
token = USERTOKEN
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
if (response.ok) {
let found = false
const devContainers = data.data.devContainers;
devContainers.forEach((c, index) => {
if (String(c.repoId) === String(repoId)) {
// has devContainer
found = true
resolve(true)
}
});
if (!found) {
resolve(false)
}
} else {
throw new Error(`Failed to fetch devContainer list.\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
})
}
async function createDevContainer(repoId) {
showInformationNotification("正在创建开发容器...")
// request creating container
const url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const postData = {
"repoId": repoId.toString(),
// "sshPublicKeyList": [publicKey]
}
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
body: JSON.stringify(postData)
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
showInformationNotification(`创建容器成功!`)
adjustBtnForRepoItem(repoId, true)
console.log('Successfully create dev container!')
} else if (response.ok){
console.warn(responseCode)
showErrorNotification(`创建容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`创建容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`创建容器失败!\nError: ${data.message}`)
throw new Error(`创建容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error);
});
}
function deleteDevContainer(repoId) {
var url = DEVSTAR_DOMAIN + "/api/devcontainer"
var token = USERTOKEN
const queryParams = new URLSearchParams({
repoId: repoId,
}).toString();
const urlWithParams = `${url}?${queryParams}`;
fetch(urlWithParams, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
'Authorization': 'token ' + token
},
})
.then(response => {
response.json().then(data => {
const responseCode = data.code
if (response.ok && responseCode === 0) {
showInformationNotification("删除容器成功!")
adjustBtnForRepoItem(repoId, false)
console.log('Successfully delete dev container belong to repoId:', repoId, data)
} else if (response.ok) {
showErrorNotification(`删除容器失败!\nError: ${data.data.ErrorMsg}`)
throw new Error(`删除容器失败!\nError: ${data.data.ErrorMsg}`)
} else {
showErrorNotification(`删除容器失败!\nError: ${data.message}`)
throw new Error(`删除容器失败!\nError: ${data.message}`)
}
})
})
.catch(error => {
console.error(error)
})
}
function firstOpenRemoteFolder(host, username, password, port, path) {
const message = {
action: 'firstOpenRemoteFolder',
host: host,
username: username,
password: password,
port: port,
path: path,
}
// 向iframe父页面发送消息
window.parent.postMessage(message, '*');
}
function openRemoteFolder(host, path) {
const message = {
action: 'openRemoteFolder',
host: host,
path: path,
}
// 向iframe父页面发送消息
window.parent.postMessage(message, '*');
}
// ===================================== Utils ===========================
// 统一通知
function showInformationNotification(message) {
communicateVSCodeByWebviewNoReturn('showInformationNotification', {message: message})
}
function showWarningNotification(message) {
communicateVSCodeByWebviewNoReturn('showWarningNotification', {message: message})
}
function showErrorNotification(message) {
communicateVSCodeByWebviewNoReturn('showErrorNotification', {message: message})
}
async function communicateVSCodeByWebviewNoReturn(action, data) {
window.parent.postMessage({target: "vscode_no_return", action: action, data: data}, "*");
}
async function communicateVSCodeByWebview(action, data) {
return new Promise((resolve, reject) => {
// request to webview
window.parent.postMessage({target: "vscode", action: action, data: data}, '*');
// response from webview
function handleResponse(event) {
const jsonData = event.data
if (jsonData.action === action) {
// return webview response
console.log("dataFromVSCodeByWebview", jsonData.data)
window.removeEventListener('message', handleResponse) // 清理监听器
resolve(jsonData.data)
}
}
window.addEventListener('message', handleResponse)
setTimeout(() => {
window.removeEventListener('message', handleResponse)
reject('timeout')
}, 5000); // 5
})
}
</script>
{{template "devstar-home-vscode-js" .}}