waveterm/frontend/app/workspace/workspace.tsx
Evan Simkowitz e2bd3cd94a
Add per-block WebView homepage setting, help view is now just a skinned webview (#982)
Adds a meta field `pinnedurl` that can be set to override the
`web:defaulturl` setting for a given block. Also adds a home button to
the webview to reset the block url to the homepage

The help view is now an extension of the webview with some of the chrome
removed.

Also updates the cookie dependency to resolve a vulnerability
2024-10-07 17:20:18 -07:00

122 lines
3.9 KiB
TypeScript

// Copyright 2024, Command Line Inc.
// SPDX-License-Identifier: Apache-2.0
import { ErrorBoundary } from "@/app/element/errorboundary";
import { CenteredDiv } from "@/app/element/quickelems";
import { ModalsRenderer } from "@/app/modals/modalsrenderer";
import { TabBar } from "@/app/tab/tabbar";
import { TabContent } from "@/app/tab/tabcontent";
import { atoms, createBlock } from "@/store/global";
import { isBlank, makeIconClass } from "@/util/util";
import { useAtomValue } from "jotai";
import { memo } from "react";
import "./workspace.less";
const iconRegex = /^[a-z0-9-]+$/;
function keyLen(obj: Object): number {
if (obj == null) {
return 0;
}
return Object.keys(obj).length;
}
function sortByDisplayOrder(wmap: { [key: string]: WidgetConfigType }): WidgetConfigType[] {
if (wmap == null) {
return [];
}
const wlist = Object.values(wmap);
wlist.sort((a, b) => {
return a["display:order"] - b["display:order"];
});
return wlist;
}
const Widgets = memo(() => {
const fullConfig = useAtomValue(atoms.fullConfigAtom);
const helpWidget: WidgetConfigType = {
icon: "circle-question",
label: "help",
blockdef: {
meta: {
view: "help",
},
},
};
const tipsWidget: WidgetConfigType = {
icon: "lightbulb",
label: "tips",
blockdef: {
meta: {
view: "tips",
},
},
};
const showHelp = fullConfig?.settings?.["widget:showhelp"] ?? true;
const showDivider = keyLen(fullConfig?.defaultwidgets) > 0 && keyLen(fullConfig?.widgets) > 0;
const defaultWidgets = sortByDisplayOrder(fullConfig?.defaultwidgets);
const widgets = sortByDisplayOrder(fullConfig?.widgets);
return (
<div className="workspace-widgets">
{defaultWidgets.map((data, idx) => (
<Widget key={`defwidget-${idx}`} widget={data} />
))}
{showDivider ? <div className="widget-divider" /> : null}
{widgets?.map((data, idx) => <Widget key={`widget-${idx}`} widget={data} />)}
{showHelp ? (
<>
<div className="widget-spacer" />
<Widget key="tips" widget={tipsWidget} />
<Widget key="help" widget={helpWidget} />
</>
) : null}
</div>
);
});
async function handleWidgetSelect(blockDef: BlockDef) {
createBlock(blockDef);
}
const Widget = memo(({ widget }: { widget: WidgetConfigType }) => {
return (
<div
className="widget"
onClick={() => handleWidgetSelect(widget.blockdef)}
title={widget.description || widget.label}
>
<div className="widget-icon" style={{ color: widget.color }}>
<i className={makeIconClass(widget.icon, true, { defaultIcon: "browser" })}></i>
</div>
{!isBlank(widget.label) ? <div className="widget-label">{widget.label}</div> : null}
</div>
);
});
const WorkspaceElem = memo(() => {
const windowData = useAtomValue(atoms.waveWindow);
const activeTabId = windowData?.activetabid;
const ws = useAtomValue(atoms.workspace);
return (
<div className="workspace">
<TabBar key={ws.oid} workspace={ws} />
<div className="workspace-tabcontent">
<ErrorBoundary key={activeTabId}>
{activeTabId == "" ? (
<CenteredDiv>No Active Tab</CenteredDiv>
) : (
<>
<TabContent key={activeTabId} tabId={activeTabId} />
<Widgets />
<ModalsRenderer />
</>
)}
</ErrorBoundary>
</div>
</div>
);
});
export { WorkspaceElem as Workspace };