mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
add block icon
This commit is contained in:
parent
8683105f70
commit
e7550c0a3e
@ -12,6 +12,10 @@
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
|
||||
.block-frame-icon {
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.block-content {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
@ -25,17 +25,20 @@ interface BlockProps {
|
||||
onClose?: () => void;
|
||||
dragHandleRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
function processTextString(iconString: string): React.ReactNode {
|
||||
if (iconString == null) {
|
||||
|
||||
const colorRegex = /^((#[0-9a-f]{6,8})|([a-z]+))$/;
|
||||
|
||||
function processTitleString(titleString: string): React.ReactNode[] {
|
||||
if (titleString == null) {
|
||||
return null;
|
||||
}
|
||||
const tagRegex = /<(\/)?([a-z]+)(?::([#a-z0-9-]+))?>/g;
|
||||
let lastIdx = 0;
|
||||
let match;
|
||||
let partsStack = [[]];
|
||||
while ((match = tagRegex.exec(iconString)) != null) {
|
||||
while ((match = tagRegex.exec(titleString)) != null) {
|
||||
const lastPart = partsStack[partsStack.length - 1];
|
||||
const before = iconString.substring(lastIdx, match.index);
|
||||
const before = titleString.substring(lastIdx, match.index);
|
||||
lastPart.push(before);
|
||||
lastIdx = match.index + match[0].length;
|
||||
const [_, isClosing, tagName, tagParam] = match;
|
||||
@ -51,9 +54,6 @@ function processTextString(iconString: string): React.ReactNode {
|
||||
continue;
|
||||
}
|
||||
if (tagName == "c" || tagName == "color") {
|
||||
if (tagParam == null) {
|
||||
continue;
|
||||
}
|
||||
if (isClosing) {
|
||||
if (partsStack.length <= 1) {
|
||||
continue;
|
||||
@ -61,6 +61,12 @@ function processTextString(iconString: string): React.ReactNode {
|
||||
partsStack.pop();
|
||||
continue;
|
||||
}
|
||||
if (tagParam == null) {
|
||||
continue;
|
||||
}
|
||||
if (!tagParam.match(colorRegex)) {
|
||||
continue;
|
||||
}
|
||||
let children = [];
|
||||
const rtag = React.createElement("span", { key: match.index, style: { color: tagParam } }, children);
|
||||
lastPart.push(rtag);
|
||||
@ -82,7 +88,7 @@ function processTextString(iconString: string): React.ReactNode {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
partsStack[partsStack.length - 1].push(iconString.substring(lastIdx));
|
||||
partsStack[partsStack.length - 1].push(titleString.substring(lastIdx));
|
||||
return partsStack[0];
|
||||
}
|
||||
|
||||
@ -90,15 +96,34 @@ function getBlockHeaderText(blockData: Block): React.ReactNode {
|
||||
if (!blockData) {
|
||||
return "no block data";
|
||||
}
|
||||
if (!util.isBlank(blockData?.meta?.title)) {
|
||||
try {
|
||||
return processTextString(blockData.meta.title);
|
||||
} catch (e) {
|
||||
console.error("error processing title", blockData.meta.title, e);
|
||||
return blockData.meta.title;
|
||||
let blockIcon: React.ReactNode = null;
|
||||
if (!util.isBlank(blockData?.meta?.["icon"])) {
|
||||
const iconName = blockData.meta.icon;
|
||||
let iconColor = blockData.meta["icon:color"];
|
||||
if (iconColor && !iconColor.match(colorRegex)) {
|
||||
iconColor = null;
|
||||
}
|
||||
let iconStyle = null;
|
||||
if (!util.isBlank(iconColor)) {
|
||||
iconStyle = { color: iconColor };
|
||||
}
|
||||
if (iconName.match(/^[a-z0-9-]+$/)) {
|
||||
blockIcon = <i style={iconStyle} className={`block-frame-icon fa fa-solid fa-${blockData.meta.icon}`} />;
|
||||
}
|
||||
}
|
||||
return `${blockData?.view} [${blockData.oid.substring(0, 8)}]`;
|
||||
if (!util.isBlank(blockData?.meta?.title)) {
|
||||
try {
|
||||
const rtn = processTitleString(blockData.meta.title) ?? [];
|
||||
if (blockIcon) {
|
||||
rtn.unshift(blockIcon);
|
||||
}
|
||||
return rtn;
|
||||
} catch (e) {
|
||||
console.error("error processing title", blockData.meta.title, e);
|
||||
return [blockIcon, blockData.meta.title];
|
||||
}
|
||||
}
|
||||
return [blockIcon, `${blockData?.view} [${blockData.oid.substring(0, 8)}]`];
|
||||
}
|
||||
|
||||
interface FramelessBlockHeaderProps {
|
||||
@ -276,12 +301,18 @@ const BlockFrame = (props: BlockFrameProps) => {
|
||||
if (!blockId || !blockData) {
|
||||
return null;
|
||||
}
|
||||
let FrameElem = BlockFrame_Tech;
|
||||
// if 0 or 1 blocks, use frameless, otherwise use tech
|
||||
const numBlocks = tabData?.blockids?.length ?? 0;
|
||||
if (numBlocks <= 1) {
|
||||
return <BlockFrame_Frameless {...props} />;
|
||||
FrameElem = BlockFrame_Frameless;
|
||||
}
|
||||
return <BlockFrame_Tech {...props} />;
|
||||
if (blockData?.meta?.["frame"] === "tech") {
|
||||
FrameElem = BlockFrame_Tech;
|
||||
} else if (blockData?.meta?.["frame"] === "frameless") {
|
||||
FrameElem = BlockFrame_Frameless;
|
||||
}
|
||||
return <FrameElem {...props} />;
|
||||
};
|
||||
|
||||
const Block = ({ blockId, onClose, dragHandleRef }: BlockProps) => {
|
||||
|
@ -12,11 +12,13 @@ import { CenteredDiv } from "../element/quickelems";
|
||||
|
||||
import "./workspace.less";
|
||||
|
||||
const iconRegex = /^[a-z0-9-]+$/;
|
||||
|
||||
function Widgets() {
|
||||
const settingsConfig = jotai.useAtomValue(atoms.settingsConfigAtom);
|
||||
const newWidgetModalVisible = React.useState(false);
|
||||
async function clickTerminal() {
|
||||
const termBlockDef = {
|
||||
const termBlockDef: BlockDef = {
|
||||
controller: "shell",
|
||||
view: "term",
|
||||
};
|
||||
@ -44,6 +46,20 @@ function Widgets() {
|
||||
await services.FileService.RemoveWidget(idx);
|
||||
}
|
||||
|
||||
function isIconValid(icon: string): boolean {
|
||||
if (util.isBlank(icon)) {
|
||||
return false;
|
||||
}
|
||||
return icon.match(iconRegex) != null;
|
||||
}
|
||||
|
||||
function getIconClass(icon: string): string {
|
||||
if (!isIconValid(icon)) {
|
||||
return "fa fa-solid fa-question fa-fw";
|
||||
}
|
||||
return `fa fa-solid fa-${icon} fa-fw`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="workspace-widgets">
|
||||
<div className="widget" onClick={() => clickTerminal()}>
|
||||
@ -54,20 +70,19 @@ function Widgets() {
|
||||
</div>
|
||||
<div className="widget" onClick={() => clickHome()}>
|
||||
<div className="widget-icon">
|
||||
<i className="fa-sharp fa-solid fa-house"></i>
|
||||
<i className="fa-sharp fa-solid fa-home"></i>
|
||||
</div>
|
||||
<div className="widget-label">home</div>
|
||||
</div>
|
||||
{settingsConfig.widgets.map((data, idx) => (
|
||||
<div
|
||||
className="widget"
|
||||
style={{ color: data.color }}
|
||||
onClick={() => handleWidgetSelect(data.blockdef)}
|
||||
key={`widget-${idx}`}
|
||||
title={data.description || data.label}
|
||||
>
|
||||
<div className="widget-icon">
|
||||
<i className={data.icon}></i>
|
||||
<div className="widget-icon" style={{ color: data.color }}>
|
||||
<i className={getIconClass(data.icon)}></i>
|
||||
</div>
|
||||
{!util.isBlank(data.label) ? <div className="widget-label">{data.label}</div> : null}
|
||||
</div>
|
||||
|
@ -54,6 +54,7 @@ type RunCmdFnType = func(ctx context.Context, cmd wshutil.BlockCommand, cmdCtx w
|
||||
|
||||
type BlockController struct {
|
||||
Lock *sync.Mutex
|
||||
ControllerType string
|
||||
TabId string
|
||||
BlockId string
|
||||
BlockDef *wstore.BlockDef
|
||||
@ -308,12 +309,13 @@ func StartBlockController(ctx context.Context, tabId string, blockId string, run
|
||||
return nil
|
||||
}
|
||||
bc := &BlockController{
|
||||
Lock: &sync.Mutex{},
|
||||
TabId: tabId,
|
||||
BlockId: blockId,
|
||||
Status: "init",
|
||||
InputCh: make(chan wshutil.BlockCommand),
|
||||
RunCmdFn: runCmdFn,
|
||||
Lock: &sync.Mutex{},
|
||||
ControllerType: blockData.Controller,
|
||||
TabId: tabId,
|
||||
BlockId: blockId,
|
||||
Status: "init",
|
||||
InputCh: make(chan wshutil.BlockCommand),
|
||||
RunCmdFn: runCmdFn,
|
||||
}
|
||||
blockControllerMap[blockId] = bc
|
||||
go bc.Run(blockData)
|
||||
|
Loading…
Reference in New Issue
Block a user