refactor: 优化终端交互.
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
### 获取主机终端主题
|
||||
GET {{baseUrl}}/asset/host-terminal/themes
|
||||
Authorization: {{token}}
|
||||
|
||||
|
||||
### 获取主机终端连接 token
|
||||
GET {{baseUrl}}/asset/host-terminal/access
|
||||
Authorization: {{token}}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orion.ops.module.asset.controller;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.orion.ops.framework.web.core.annotation.RestWrapper;
|
||||
import com.orion.ops.module.asset.service.HostTerminalService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
@@ -32,11 +33,17 @@ public class HostTerminalController {
|
||||
@Resource
|
||||
private HostTerminalService hostTerminalService;
|
||||
|
||||
@GetMapping("/themes")
|
||||
@Operation(summary = "获取主机终端主题")
|
||||
public JSONArray getTerminalThemes() {
|
||||
return hostTerminalService.getTerminalThemes();
|
||||
}
|
||||
|
||||
@GetMapping("/access")
|
||||
@Operation(summary = "获取主机终端 accessToken")
|
||||
@PreAuthorize("@ss.hasPermission('asset:host-terminal:access')")
|
||||
public String getHostTerminalAccessToken() {
|
||||
return hostTerminalService.getHostTerminalAccessToken();
|
||||
public String getTerminalAccessToken() {
|
||||
return hostTerminalService.getTerminalAccessToken();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.orion.ops.module.asset.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.ops.module.asset.entity.domain.HostDO;
|
||||
import com.orion.ops.module.asset.entity.dto.HostTerminalAccessDTO;
|
||||
@@ -14,12 +15,19 @@ import com.orion.ops.module.asset.entity.dto.HostTerminalConnectDTO;
|
||||
*/
|
||||
public interface HostTerminalService {
|
||||
|
||||
/**
|
||||
* 获取主机终端主题
|
||||
*
|
||||
* @return themes
|
||||
*/
|
||||
JSONArray getTerminalThemes();
|
||||
|
||||
/**
|
||||
* 获取主机终端访问 accessToken
|
||||
*
|
||||
* @return accessToken
|
||||
*/
|
||||
String getHostTerminalAccessToken();
|
||||
String getTerminalAccessToken();
|
||||
|
||||
/**
|
||||
* 通过 accessToken 获取主机终端访问信息
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package com.orion.ops.module.asset.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.orion.lang.exception.AuthenticationException;
|
||||
import com.orion.lang.id.UUIds;
|
||||
import com.orion.lang.utils.Exceptions;
|
||||
import com.orion.lang.utils.Strings;
|
||||
import com.orion.lang.utils.io.StreamReaders;
|
||||
import com.orion.net.host.SessionHolder;
|
||||
import com.orion.net.host.SessionStore;
|
||||
import com.orion.ops.framework.common.constant.Const;
|
||||
@@ -40,6 +42,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -54,6 +57,8 @@ import java.util.Optional;
|
||||
@Service
|
||||
public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
|
||||
private static final String TERMINAL_PATH = "/template/theme/terminal.theme.json";
|
||||
|
||||
@Resource
|
||||
private HostConfigService hostConfigService;
|
||||
|
||||
@@ -82,7 +87,17 @@ public class HostTerminalServiceImpl implements HostTerminalService {
|
||||
private SystemUserApi systemUserApi;
|
||||
|
||||
@Override
|
||||
public String getHostTerminalAccessToken() {
|
||||
public JSONArray getTerminalThemes() {
|
||||
try (InputStream in = HostTerminalService.class.getResourceAsStream(TERMINAL_PATH)) {
|
||||
byte[] bytes = StreamReaders.readAllBytes(in);
|
||||
return JSONArray.parseArray(new String(bytes));
|
||||
} catch (Exception e) {
|
||||
throw Exceptions.ioRuntime(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTerminalAccessToken() {
|
||||
LoginUser user = SecurityUtils.getLoginUser();
|
||||
log.info("HostConnectService.getHostAccessToken userId: {}", user.getId());
|
||||
String accessToken = UUIds.random19();
|
||||
|
||||
@@ -0,0 +1,290 @@
|
||||
[
|
||||
{
|
||||
"name": "catppuccin-macchiato",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#24273A",
|
||||
"foreground": "#CAD3F5",
|
||||
"cursor": "#F4DBD6",
|
||||
"selectionBackground": "#5B6078",
|
||||
"black": "#494D64",
|
||||
"red": "#ED8796",
|
||||
"green": "#A6DA95",
|
||||
"yellow": "#EED49F",
|
||||
"blue": "#8AADF4",
|
||||
"cyan": "#8BD5CA",
|
||||
"white": "#B8C0E0",
|
||||
"brightBlack": "#5B6078",
|
||||
"brightRed": "#ED8796",
|
||||
"brightGreen": "#A6DA95",
|
||||
"brightYellow": "#EED49F",
|
||||
"brightBlue": "#8AADF4",
|
||||
"brightCyan": "#8BD5CA",
|
||||
"brightWhite": "#A5ADCB"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "catppuccin-mocha",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#1E1E2E",
|
||||
"foreground": "#CDD6F4",
|
||||
"cursor": "#F5E0DC",
|
||||
"selectionBackground": "#585B70",
|
||||
"black": "#45475A",
|
||||
"red": "#F38BA8",
|
||||
"green": "#A6E3A1",
|
||||
"yellow": "#F9E2AF",
|
||||
"blue": "#89B4FA",
|
||||
"cyan": "#94E2D5",
|
||||
"white": "#BAC2DE",
|
||||
"brightBlack": "#585B70",
|
||||
"brightRed": "#F38BA8",
|
||||
"brightGreen": "#A6E3A1",
|
||||
"brightYellow": "#F9E2AF",
|
||||
"brightBlue": "#89B4FA",
|
||||
"brightCyan": "#94E2D5",
|
||||
"brightWhite": "#A6ADC8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "OneHalfDark",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#282C34",
|
||||
"foreground": "#DCDFE4",
|
||||
"cursor": "#A3B3CC",
|
||||
"selectionBackground": "#474E5D",
|
||||
"black": "#282C34",
|
||||
"red": "#E06C75",
|
||||
"green": "#98C379",
|
||||
"yellow": "#E5C07B",
|
||||
"blue": "#61AFEF",
|
||||
"cyan": "#56B6C2",
|
||||
"white": "#DCDFE4",
|
||||
"brightBlack": "#282C34",
|
||||
"brightRed": "#E06C75",
|
||||
"brightGreen": "#98C379",
|
||||
"brightYellow": "#E5C07B",
|
||||
"brightBlue": "#61AFEF",
|
||||
"brightCyan": "#56B6C2",
|
||||
"brightWhite": "#DCDFE4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "MaterialDesignColors",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#1D262A",
|
||||
"foreground": "#E7EBED",
|
||||
"cursor": "#EAEAEA",
|
||||
"selectionBackground": "#4E6A78",
|
||||
"black": "#435B67",
|
||||
"red": "#FC3841",
|
||||
"green": "#5CF19E",
|
||||
"yellow": "#FED032",
|
||||
"blue": "#37B6FF",
|
||||
"cyan": "#59FFD1",
|
||||
"white": "#FFFFFF",
|
||||
"brightBlack": "#A1B0B8",
|
||||
"brightRed": "#FC746D",
|
||||
"brightGreen": "#ADF7BE",
|
||||
"brightYellow": "#FEE16C",
|
||||
"brightBlue": "#70CFFF",
|
||||
"brightCyan": "#9AFFE6",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Dracula",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#1E1F29",
|
||||
"foreground": "#F8F8F2",
|
||||
"cursor": "#BBBBBB",
|
||||
"selectionBackground": "#44475A",
|
||||
"black": "#000000",
|
||||
"red": "#FF5555",
|
||||
"green": "#50FA7B",
|
||||
"yellow": "#F1FA8C",
|
||||
"blue": "#BD93F9",
|
||||
"cyan": "#8BE9FD",
|
||||
"white": "#BBBBBB",
|
||||
"brightBlack": "#555555",
|
||||
"brightRed": "#FF5555",
|
||||
"brightGreen": "#50FA7B",
|
||||
"brightYellow": "#F1FA8C",
|
||||
"brightBlue": "#BD93F9",
|
||||
"brightCyan": "#8BE9FD",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Dracula+",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#212121",
|
||||
"foreground": "#F8F8F2",
|
||||
"cursor": "#ECEFF4",
|
||||
"selectionBackground": "#F8F8F2",
|
||||
"black": "#21222C",
|
||||
"red": "#FF5555",
|
||||
"green": "#50FA7B",
|
||||
"yellow": "#FFCB6B",
|
||||
"blue": "#82AAFF",
|
||||
"cyan": "#8BE9FD",
|
||||
"white": "#F8F8F2",
|
||||
"brightBlack": "#545454",
|
||||
"brightRed": "#FF6E6E",
|
||||
"brightGreen": "#69FF94",
|
||||
"brightYellow": "#FFCB6B",
|
||||
"brightBlue": "#D6ACFF",
|
||||
"brightCyan": "#A4FFFF",
|
||||
"brightWhite": "#F8F8F2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Apple System Colors",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#1E1E1E",
|
||||
"foreground": "#FFFFFF",
|
||||
"cursor": "#98989D",
|
||||
"selectionBackground": "#3F638B",
|
||||
"black": "#1A1A1A",
|
||||
"red": "#CC372E",
|
||||
"green": "#26A439",
|
||||
"yellow": "#CDAC08",
|
||||
"blue": "#0869CB",
|
||||
"cyan": "#479EC2",
|
||||
"white": "#98989D",
|
||||
"brightBlack": "#464646",
|
||||
"brightRed": "#FF453A",
|
||||
"brightGreen": "#32D74B",
|
||||
"brightYellow": "#FFD60A",
|
||||
"brightBlue": "#0A84FF",
|
||||
"brightCyan": "#76D6FF",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Builtin Tango Light",
|
||||
"dark": false,
|
||||
"schema": {
|
||||
"background": "#FFFFFF",
|
||||
"foreground": "#000000",
|
||||
"cursor": "#000000",
|
||||
"selectionBackground": "#B5D5FF",
|
||||
"black": "#000000",
|
||||
"red": "#CC0000",
|
||||
"green": "#4E9A06",
|
||||
"yellow": "#C4A000",
|
||||
"blue": "#3465A4",
|
||||
"cyan": "#06989A",
|
||||
"white": "#D3D7CF",
|
||||
"brightBlack": "#555753",
|
||||
"brightRed": "#EF2929",
|
||||
"brightGreen": "#8AE234",
|
||||
"brightYellow": "#FCE94F",
|
||||
"brightBlue": "#729FCF",
|
||||
"brightCyan": "#34E2E2",
|
||||
"brightWhite": "#EEEEEC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Duotone Dark",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#1F1D27",
|
||||
"foreground": "#B7A1FF",
|
||||
"cursor": "#FF9839",
|
||||
"selectionBackground": "#353147",
|
||||
"black": "#1F1D27",
|
||||
"red": "#D9393E",
|
||||
"green": "#2DCD73",
|
||||
"yellow": "#D9B76E",
|
||||
"blue": "#FFC284",
|
||||
"cyan": "#2488FF",
|
||||
"white": "#B7A1FF",
|
||||
"brightBlack": "#353147",
|
||||
"brightRed": "#D9393E",
|
||||
"brightGreen": "#2DCD73",
|
||||
"brightYellow": "#D9B76E",
|
||||
"brightBlue": "#FFC284",
|
||||
"brightCyan": "#2488FF",
|
||||
"brightWhite": "#EAE5FF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "BlulocoLight",
|
||||
"dark": false,
|
||||
"schema": {
|
||||
"background": "#F9F9F9",
|
||||
"foreground": "#373A41",
|
||||
"cursor": "#F32759",
|
||||
"selectionBackground": "#DAF0FF",
|
||||
"black": "#373A41",
|
||||
"red": "#D52753",
|
||||
"green": "#23974A",
|
||||
"yellow": "#DF631C",
|
||||
"blue": "#275FE4",
|
||||
"cyan": "#27618D",
|
||||
"white": "#BABBC2",
|
||||
"brightBlack": "#676A77",
|
||||
"brightRed": "#FF6480",
|
||||
"brightGreen": "#3CBC66",
|
||||
"brightYellow": "#C5A332",
|
||||
"brightBlue": "#0099E1",
|
||||
"brightCyan": "#6D93BB",
|
||||
"brightWhite": "#D3D3D3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Chester",
|
||||
"dark": true,
|
||||
"schema": {
|
||||
"background": "#2C3643",
|
||||
"foreground": "#FFFFFF",
|
||||
"cursor": "#B4B1B1",
|
||||
"selectionBackground": "#67747C",
|
||||
"black": "#080200",
|
||||
"red": "#FA5E5B",
|
||||
"green": "#16C98D",
|
||||
"yellow": "#FFC83F",
|
||||
"blue": "#288AD6",
|
||||
"cyan": "#28DDDE",
|
||||
"white": "#E7E7E7",
|
||||
"brightBlack": "#6F6B68",
|
||||
"brightRed": "#FA5E5B",
|
||||
"brightGreen": "#16C98D",
|
||||
"brightYellow": "#FEEF6D",
|
||||
"brightBlue": "#278AD6",
|
||||
"brightCyan": "#27DEDE",
|
||||
"brightWhite": "#FFFFFF"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "CLRS",
|
||||
"dark": false,
|
||||
"schema": {
|
||||
"background": "#FFFFFF",
|
||||
"foreground": "#262626",
|
||||
"cursor": "#6FD3FC",
|
||||
"selectionBackground": "#6FD3FC",
|
||||
"black": "#000000",
|
||||
"red": "#F8282A",
|
||||
"green": "#328A5D",
|
||||
"yellow": "#FA701D",
|
||||
"blue": "#135CD0",
|
||||
"cyan": "#33C3C1",
|
||||
"white": "#B3B3B3",
|
||||
"brightBlack": "#555753",
|
||||
"brightRed": "#FB0416",
|
||||
"brightGreen": "#2CC631",
|
||||
"brightYellow": "#FDD727",
|
||||
"brightBlue": "#1670FF",
|
||||
"brightCyan": "#3AD5CE",
|
||||
"brightWhite": "#EEEEEC"
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -5,16 +5,13 @@ import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.alibaba.fastjson.serializer.ValueFilter;
|
||||
import com.orion.lang.utils.Colors;
|
||||
import com.orion.lang.utils.awt.Clipboards;
|
||||
import com.orion.lang.utils.collect.Lists;
|
||||
import com.orion.lang.utils.io.FileReaders;
|
||||
import com.orion.lang.utils.io.Files1;
|
||||
import com.orion.lang.utils.reflect.Fields;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -30,9 +27,14 @@ public class TerminalThemeFetcher {
|
||||
public static void main(String[] args) {
|
||||
List<File> files = Files1.listFiles("D:\\idea-project\\iTerm2-Color-Schemes\\vhs");
|
||||
// 过滤的 theme
|
||||
List<String> schemaFilter = new ArrayList<>();
|
||||
// List<String> schemaFilter = Lists.of("oneHalf", "material", "github", "iTerm2", "JetBrains");
|
||||
// List<String> schemaFilter = Lists.of("catppuccin", "3024", "OneHalfDark", "OneHalfLight", "MaterialDesignColors", "MaterialOcean", "Solarized Light");
|
||||
List<String> schemaFilter = Lists.of(
|
||||
"catppuccin-macchiato", "catppuccin-mocha",
|
||||
"OneHalfDark", "MaterialDesignColors",
|
||||
"Dracula", "Dracula+",
|
||||
"Apple System Colors", "Builtin Tango Light",
|
||||
"Duotone Dark", "BlulocoLight",
|
||||
"Chester", "CLRS"
|
||||
);
|
||||
// 颜色大写
|
||||
ValueFilter colorFilter = (Object object, String name, Object value) -> {
|
||||
if (value instanceof String && value.toString().contains("#")) {
|
||||
@@ -44,32 +46,26 @@ public class TerminalThemeFetcher {
|
||||
// 转换
|
||||
List<TerminalTheme> arr = files.stream()
|
||||
.filter(f -> Lists.isEmpty(schemaFilter) || schemaFilter.stream()
|
||||
.anyMatch(s -> f.getName().toLowerCase().contains(s.toLowerCase())))
|
||||
.limit(200)
|
||||
.map(s -> s + ".json")
|
||||
.anyMatch(s -> f.getName().equalsIgnoreCase(s)))
|
||||
.map(f -> {
|
||||
JSONObject schema = JSONObject.parseObject(new String(FileReaders.readAllBytes(f)));
|
||||
schema.put("dark", Colors.isDarkColor(schema.getString("background")));
|
||||
schema.put("selectionBackground", schema.getString("selection"));
|
||||
// 转为对象
|
||||
return JSON.parseObject(JSON.toJSONString(schema, colorFilter), TerminalTheme.class);
|
||||
TerminalTheme theme = new TerminalTheme();
|
||||
theme.setName(schema.getString("name"));
|
||||
theme.setDark(Colors.isDarkColor(schema.getString("background")));
|
||||
theme.setSchema(JSON.parseObject(JSON.toJSONString(schema), TerminalThemeSchema.class));
|
||||
return theme;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
// 排序
|
||||
if (!Lists.isEmpty(schemaFilter)) {
|
||||
arr.sort(Comparator.comparing(s -> schemaFilter.indexOf(s.getName())));
|
||||
}
|
||||
// 打印 json
|
||||
String json = JSON.toJSONString(arr, colorFilter);
|
||||
System.out.println("\n\n" + json);
|
||||
// 转为 jsCode
|
||||
List<String> formatter = Fields.getFields(TerminalTheme.class)
|
||||
.stream()
|
||||
.map(Field::getName)
|
||||
.collect(Collectors.toList());
|
||||
for (String s : formatter) {
|
||||
json = json.replaceAll("\"" + s + "\"", s);
|
||||
}
|
||||
Clipboards.setString(json);
|
||||
System.out.println("\n\njsCode 已复制到剪切板");
|
||||
}
|
||||
|
||||
//
|
||||
/*
|
||||
var term = new Terminal();
|
||||
var doc = document.getElementById('themes');
|
||||
@@ -110,44 +106,50 @@ public class TerminalThemeFetcher {
|
||||
@JSONField(ordinal = 1)
|
||||
private Boolean dark;
|
||||
@JSONField(ordinal = 2)
|
||||
private TerminalThemeSchema schema;
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class TerminalThemeSchema {
|
||||
@JSONField(ordinal = 0)
|
||||
private String background;
|
||||
@JSONField(ordinal = 3)
|
||||
@JSONField(ordinal = 1)
|
||||
private String foreground;
|
||||
@JSONField(ordinal = 4)
|
||||
@JSONField(ordinal = 2)
|
||||
private String cursor;
|
||||
@JSONField(ordinal = 5)
|
||||
@JSONField(ordinal = 3)
|
||||
private String selectionBackground;
|
||||
@JSONField(ordinal = 6)
|
||||
@JSONField(ordinal = 4)
|
||||
private String black;
|
||||
@JSONField(ordinal = 7)
|
||||
@JSONField(ordinal = 5)
|
||||
private String red;
|
||||
@JSONField(ordinal = 8)
|
||||
@JSONField(ordinal = 6)
|
||||
private String green;
|
||||
@JSONField(ordinal = 9)
|
||||
@JSONField(ordinal = 7)
|
||||
private String yellow;
|
||||
@JSONField(ordinal = 10)
|
||||
@JSONField(ordinal = 8)
|
||||
private String blue;
|
||||
@JSONField(ordinal = 11)
|
||||
@JSONField(ordinal = 9)
|
||||
private String magenta;
|
||||
@JSONField(ordinal = 12)
|
||||
@JSONField(ordinal = 10)
|
||||
private String cyan;
|
||||
@JSONField(ordinal = 13)
|
||||
@JSONField(ordinal = 11)
|
||||
private String white;
|
||||
@JSONField(ordinal = 14)
|
||||
@JSONField(ordinal = 12)
|
||||
private String brightBlack;
|
||||
@JSONField(ordinal = 15)
|
||||
@JSONField(ordinal = 13)
|
||||
private String brightRed;
|
||||
@JSONField(ordinal = 16)
|
||||
@JSONField(ordinal = 14)
|
||||
private String brightGreen;
|
||||
@JSONField(ordinal = 17)
|
||||
@JSONField(ordinal = 15)
|
||||
private String brightYellow;
|
||||
@JSONField(ordinal = 18)
|
||||
@JSONField(ordinal = 16)
|
||||
private String brightBlue;
|
||||
@JSONField(ordinal = 19)
|
||||
@JSONField(ordinal = 17)
|
||||
private String brightMagenta;
|
||||
@JSONField(ordinal = 20)
|
||||
@JSONField(ordinal = 18)
|
||||
private String brightCyan;
|
||||
@JSONField(ordinal = 21)
|
||||
@JSONField(ordinal = 19)
|
||||
private String brightWhite;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,51 @@
|
||||
import axios from 'axios';
|
||||
|
||||
// 终端主题
|
||||
export interface TerminalTheme {
|
||||
name: string;
|
||||
dark: boolean;
|
||||
schema: TerminalThemeSchema;
|
||||
}
|
||||
|
||||
// 终端主题 schema
|
||||
export interface TerminalThemeSchema {
|
||||
background: string;
|
||||
foreground: string;
|
||||
cursor: string;
|
||||
cursorAccent?: string;
|
||||
selectionBackground?: string;
|
||||
selectionForeground?: string;
|
||||
selectionInactiveBackground?: string;
|
||||
black: string;
|
||||
red: string;
|
||||
green: string;
|
||||
yellow: string;
|
||||
blue: string;
|
||||
magenta: string;
|
||||
cyan: string;
|
||||
white: string;
|
||||
brightBlack: string;
|
||||
brightRed: string;
|
||||
brightGreen: string;
|
||||
brightYellow: string;
|
||||
brightBlue: string;
|
||||
brightMagenta: string;
|
||||
brightCyan: string;
|
||||
brightWhite: string;
|
||||
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机终端主题
|
||||
*/
|
||||
export function getTerminalThemes() {
|
||||
return axios.get<Array<TerminalTheme>>('/asset/host-terminal/themes');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主机终端 accessToken
|
||||
*/
|
||||
export function getHostTerminalAccessToken() {
|
||||
export function getTerminalAccessToken() {
|
||||
return axios.get<string>('/asset/host-terminal/access');
|
||||
}
|
||||
|
||||
@@ -43,6 +43,10 @@
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
autoFocus: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
default: 'json',
|
||||
@@ -76,6 +80,10 @@
|
||||
};
|
||||
// 创建编辑器
|
||||
editor = monaco.editor.create(editorContainer.value, options);
|
||||
// 自动聚焦
|
||||
if (props.autoFocus) {
|
||||
editor.focus();
|
||||
}
|
||||
// 监听值的变化
|
||||
editor.onDidChangeModelContent(() => {
|
||||
const value = editor.getValue();
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<div :style="{ width: '100%', 'height': height }">
|
||||
<editor v-model="value"
|
||||
language="shell"
|
||||
:auto-focus="true"
|
||||
:theme="dark ? 'vs-dark' : 'vs'" />
|
||||
</div>
|
||||
</a-modal>
|
||||
|
||||
@@ -3,9 +3,10 @@ import { defineStore } from 'pinia';
|
||||
import { getPreference, updatePreference } from '@/api/user/preference';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { useDark } from '@vueuse/core';
|
||||
import { DEFAULT_SCHEMA } from '@/views/host/terminal/types/terminal.theme';
|
||||
import TerminalTabManager from '@/views/host/terminal/handler/terminal-tab-manager';
|
||||
import TerminalSessionManager from '@/views/host/terminal/handler/terminal-session-manager';
|
||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||
import { getTerminalThemes } from '@/api/asset/host-terminal';
|
||||
|
||||
// 暗色主题
|
||||
export const DarkTheme = {
|
||||
@@ -28,7 +29,8 @@ export default defineStore('terminal', {
|
||||
darkTheme: 'auto',
|
||||
newConnectionType: 'group',
|
||||
displaySetting: {} as TerminalDisplaySetting,
|
||||
themeSchema: {} as TerminalThemeSchema
|
||||
themeSchema: {} as TerminalThemeSchema,
|
||||
theme: {} as TerminalTheme
|
||||
},
|
||||
tabManager: new TerminalTabManager(),
|
||||
sessionManager: new TerminalSessionManager()
|
||||
@@ -38,25 +40,21 @@ export default defineStore('terminal', {
|
||||
// 加载终端偏好
|
||||
async fetchPreference() {
|
||||
try {
|
||||
// 加载偏好
|
||||
const { data } = await getPreference<TerminalPreference>('TERMINAL');
|
||||
// 设置默认终端主题
|
||||
if (!data.themeSchema?.name) {
|
||||
data.themeSchema = DEFAULT_SCHEMA;
|
||||
// theme 不存在则默认加载第一个
|
||||
if (!data.theme) {
|
||||
const { data: themes } = await getTerminalThemes();
|
||||
data.theme = themes[0];
|
||||
}
|
||||
this.preference = data;
|
||||
// 设置暗色主题
|
||||
const userDarkTheme = data.darkTheme;
|
||||
if (userDarkTheme === DarkTheme.AUTO) {
|
||||
this.isDarkTheme = data.themeSchema?.dark === true;
|
||||
} else {
|
||||
this.isDarkTheme = userDarkTheme === DarkTheme.DARK;
|
||||
}
|
||||
} catch (e) {
|
||||
Message.error('配置加载失败');
|
||||
}
|
||||
},
|
||||
|
||||
// 修改暗色主题
|
||||
// FIXME 删除 terminalDarkTheme
|
||||
async changeDarkTheme(darkTheme: string) {
|
||||
this.preference.darkTheme = darkTheme;
|
||||
if (darkTheme === DarkTheme.DARK) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { Ref } from 'vue';
|
||||
import type { ITerminalTabManager, ITerminalSessionManager } from '@/views/host/terminal/types/terminal.type';
|
||||
import type { ITerminalSessionManager, ITerminalTabManager } from '@/views/host/terminal/types/terminal.type';
|
||||
import type { TerminalTheme } from '@/api/asset/host-terminal';
|
||||
|
||||
export interface TerminalState {
|
||||
isDarkTheme: Ref<boolean>;
|
||||
@@ -13,6 +14,7 @@ export interface TerminalPreference {
|
||||
darkTheme: string;
|
||||
newConnectionType: string;
|
||||
displaySetting: TerminalDisplaySetting;
|
||||
theme: TerminalTheme;
|
||||
themeSchema: TerminalThemeSchema;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,30 @@
|
||||
<script lang="ts" setup>
|
||||
import { TabType, InnerTabs } from '../../types/terminal.const';
|
||||
import { useTerminalStore } from '@/store';
|
||||
import { watch } from 'vue';
|
||||
import TerminalViewSetting from '../view-setting/terminal-view-setting.vue';
|
||||
import NewConnectionView from '../new-connection/new-connection-view.vue';
|
||||
import TerminalView from '../xterm/terminal-view.vue';
|
||||
|
||||
const { tabManager } = useTerminalStore();
|
||||
import TerminalView from '../xterm/terminal-view.vue';
|
||||
const { tabManager, sessionManager } = useTerminalStore();
|
||||
|
||||
// 监听 tab 修改
|
||||
watch(() => tabManager.active, active => {
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
// 获取 tab
|
||||
const tab = tabManager.items.find(s => s.key === active);
|
||||
if (!tab) {
|
||||
return;
|
||||
}
|
||||
// 修改标题
|
||||
document.title = tab.title;
|
||||
// terminal 自动聚焦
|
||||
if (tab?.type === TabType.TERMINAL) {
|
||||
sessionManager.getSession(active)?.focus();
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
@@ -31,11 +31,11 @@
|
||||
term.value.open(terminal.value);
|
||||
term.value.write(
|
||||
'[1;94m[root[0m@[1;96mOrionServer usr]#[0m\r\n' +
|
||||
'[92mdr-xr-xr-x.[0m 2 root root [96mbin[0m\r\n' +
|
||||
'[92mdr-xr-xr-x.[0m 2 root root [96msbin[0m\r\n' +
|
||||
'[92mdr-xr-xr-x.[0m 43 root root [96mlib[0m\r\n' +
|
||||
'[92mdr-xr-xr-x.[0m 62 root root [96mlib64[0m\r\n' +
|
||||
'[92mlrwxrwxrwx.[0m 1 root root [90;102mtmp[0m'
|
||||
'dr-xr-xr-x. 2 root root [0m[01;34mbin[0m\r\n' +
|
||||
'dr-xr-xr-x. 2 root root [01;34msbin[0m\r\n' +
|
||||
'drwxr-xr-x. 89 root root [01;34mshare[0m\r\n' +
|
||||
'drwxr-xr-x. 4 root root [01;34msrc[0m\r\n' +
|
||||
'lrwxrwxrwx. 1 root root [01;36mtmp[0m -> [30;42m../var/tmp[0m'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@
|
||||
:body-style="{ padding: '16px 16px 16px 0' }"
|
||||
:dark="themeSchema.dark"
|
||||
cancel-text="关闭"
|
||||
@ok="writeCommand(modal.getValue())" />
|
||||
@ok="writeCommand(modal.getValue())"
|
||||
@cancel="focus" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -83,17 +84,18 @@
|
||||
const session = ref<ITerminalSession>();
|
||||
|
||||
// FIXME
|
||||
// 卸载 最外层 terminal 组件, 卸载 style
|
||||
// 调教 theme
|
||||
// terminal themes 改成非同步 style
|
||||
// 从后端获取 theme
|
||||
// (改成可配置/拆分)
|
||||
// 自定义 font siderBar 颜色, 集成到主题里面, 现在的问题是切换主题字体颜色就变了
|
||||
// 是否开启 link, url 匹配策略
|
||||
// 是否开启 link
|
||||
// 是否开启 image
|
||||
// search color 配置
|
||||
// 右键菜单补充
|
||||
// 搜索
|
||||
// 搜索插件, link插件
|
||||
// 截屏
|
||||
// 最近连接逻辑 偏好逻辑
|
||||
|
||||
// 发送命令
|
||||
const writeCommandInput = async (e: KeyboardEvent) => {
|
||||
@@ -111,6 +113,11 @@
|
||||
}
|
||||
};
|
||||
|
||||
// 聚焦
|
||||
const focus = () => {
|
||||
session.value?.focus();
|
||||
};
|
||||
|
||||
// 右侧操作
|
||||
const rightActions = computed<Array<SidebarAction>>(() => [
|
||||
{
|
||||
@@ -310,7 +317,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
::-webkit-scrollbar-track {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
import type {
|
||||
InputPayload,
|
||||
ITerminalChannel,
|
||||
ITerminalOutputProcessor,
|
||||
ITerminalSessionManager,
|
||||
OutputPayload,
|
||||
Protocol,
|
||||
} from '../types/terminal.type';
|
||||
import type { InputPayload, ITerminalChannel, ITerminalOutputProcessor, ITerminalSessionManager, OutputPayload, Protocol, } from '../types/terminal.type';
|
||||
import { OutputProtocol } from '../types/terminal.protocol';
|
||||
import { getHostTerminalAccessToken } from '@/api/asset/host-terminal';
|
||||
import { getTerminalAccessToken } from '@/api/asset/host-terminal';
|
||||
import { Message } from '@arco-design/web-vue';
|
||||
import { sleep } from '@/utils';
|
||||
import TerminalOutputProcessor from './terminal-output-processor';
|
||||
@@ -28,7 +21,7 @@ export default class TerminalChannel implements ITerminalChannel {
|
||||
// 初始化
|
||||
async init() {
|
||||
// 获取 access
|
||||
const { data: accessToken } = await getHostTerminalAccessToken();
|
||||
const { data: accessToken } = await getTerminalAccessToken();
|
||||
// 打开会话
|
||||
this.client = new WebSocket(`${wsBase}/host/terminal/${accessToken}`);
|
||||
this.client.onerror = event => {
|
||||
|
||||
@@ -70,6 +70,7 @@ export default class TerminalSession implements ITerminalSession {
|
||||
connect(): void {
|
||||
this.status = TerminalStatus.CONNECTED;
|
||||
this.connected = true;
|
||||
this.inst.focus();
|
||||
// 注册输入事件
|
||||
this.inst.onData(s => {
|
||||
if (!this.canWrite) {
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
const dictStore = useDictStore();
|
||||
const cacheStore = useCacheStore();
|
||||
|
||||
const originTitle = document.title;
|
||||
const render = ref(false);
|
||||
|
||||
// 关闭视口处理
|
||||
@@ -54,6 +55,9 @@
|
||||
// 加载用户终端偏好
|
||||
onBeforeMount(async () => {
|
||||
await terminalStore.fetchPreference();
|
||||
// 设置系统主题配色
|
||||
const dark = terminalStore.preference.theme.dark;
|
||||
document.body.setAttribute('terminal-theme', dark ? 'dark' : 'light');
|
||||
render.value = true;
|
||||
});
|
||||
|
||||
@@ -73,6 +77,10 @@
|
||||
cacheStore.reset('authorizedHostKeys', 'authorizedHostIdentities');
|
||||
// 移除关闭视口事件
|
||||
window.removeEventListener('beforeunload', handleBeforeUnload);
|
||||
// 去除 body style
|
||||
document.body.removeAttribute('terminal-theme');
|
||||
// 重置 title
|
||||
document.title = originTitle;
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user