mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-21 16:38:23 +01:00
metadata updates (frontend typing) (#174)
This commit is contained in:
parent
9233b3dbd7
commit
cfc875bc21
@ -47,9 +47,9 @@ func viewRun(cmd *cobra.Command, args []string) {
|
|||||||
wshutil.SetTermRawModeAndInstallShutdownHandlers(true)
|
wshutil.SetTermRawModeAndInstallShutdownHandlers(true)
|
||||||
viewWshCmd := &wshrpc.CommandCreateBlockData{
|
viewWshCmd := &wshrpc.CommandCreateBlockData{
|
||||||
BlockDef: &wstore.BlockDef{
|
BlockDef: &wstore.BlockDef{
|
||||||
View: "preview",
|
|
||||||
Meta: map[string]interface{}{
|
Meta: map[string]interface{}{
|
||||||
"file": absFile,
|
wstore.MetaKey_View: "preview",
|
||||||
|
wstore.MetaKey_File: absFile,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -313,11 +313,11 @@ const AppInner = () => {
|
|||||||
|
|
||||||
function handleKeyDown(waveEvent: WaveKeyboardEvent): boolean {
|
function handleKeyDown(waveEvent: WaveKeyboardEvent): boolean {
|
||||||
// global key handler for now (refactor later)
|
// global key handler for now (refactor later)
|
||||||
if (keyutil.checkKeyPressed(waveEvent, "Cmd:]")) {
|
if (keyutil.checkKeyPressed(waveEvent, "Cmd:]") || keyutil.checkKeyPressed(waveEvent, "Shift:Cmd:]")) {
|
||||||
switchTab(1);
|
switchTab(1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (keyutil.checkKeyPressed(waveEvent, "Cmd:[")) {
|
if (keyutil.checkKeyPressed(waveEvent, "Cmd:[") || keyutil.checkKeyPressed(waveEvent, "Shift:Cmd:[")) {
|
||||||
switchTab(-1);
|
switchTab(-1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -153,8 +153,8 @@ function getBlockHeaderText(blockIcon: string, blockData: Block, settings: Setti
|
|||||||
return [blockIconElem, blockData.meta.title + blockIdStr];
|
return [blockIconElem, blockData.meta.title + blockIdStr];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let viewString = blockData?.view;
|
let viewString = blockData?.meta?.view;
|
||||||
if (blockData.controller == "cmd") {
|
if (blockData?.meta?.controller == "cmd") {
|
||||||
viewString = "cmd";
|
viewString = "cmd";
|
||||||
}
|
}
|
||||||
return [blockIconElem, viewString + blockIdStr];
|
return [blockIconElem, viewString + blockIdStr];
|
||||||
@ -259,8 +259,8 @@ const BlockFrame_Default_Component = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
let isFocused = jotai.useAtomValue(isFocusedAtom);
|
let isFocused = jotai.useAtomValue(isFocusedAtom);
|
||||||
const viewIconUnion = util.useAtomValueSafe(viewModel.viewIcon) ?? blockViewToIcon(blockData?.view);
|
const viewIconUnion = util.useAtomValueSafe(viewModel.viewIcon) ?? blockViewToIcon(blockData?.meta?.view);
|
||||||
const viewName = util.useAtomValueSafe(viewModel.viewName) ?? blockViewToName(blockData?.view);
|
const viewName = util.useAtomValueSafe(viewModel.viewName) ?? blockViewToName(blockData?.meta?.view);
|
||||||
const headerTextUnion = util.useAtomValueSafe(viewModel.viewText);
|
const headerTextUnion = util.useAtomValueSafe(viewModel.viewText);
|
||||||
const preIconButton = util.useAtomValueSafe(viewModel.preIconButton);
|
const preIconButton = util.useAtomValueSafe(viewModel.preIconButton);
|
||||||
const endIconButtons = util.useAtomValueSafe(viewModel.endIconButtons);
|
const endIconButtons = util.useAtomValueSafe(viewModel.endIconButtons);
|
||||||
@ -377,6 +377,10 @@ const BlockFrame_Default_Component = ({
|
|||||||
headerTextElems.push(...renderHeaderElements(headerTextUnion));
|
headerTextElems.push(...renderHeaderElements(headerTextUnion));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleDoubleClick() {
|
||||||
|
layoutModel?.onMagnifyToggle();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={clsx(
|
className={clsx(
|
||||||
@ -397,6 +401,7 @@ const BlockFrame_Default_Component = ({
|
|||||||
<div
|
<div
|
||||||
className="block-frame-default-header"
|
className="block-frame-default-header"
|
||||||
ref={layoutModel?.dragHandleRef}
|
ref={layoutModel?.dragHandleRef}
|
||||||
|
onDoubleClick={handleDoubleClick}
|
||||||
onContextMenu={(e) =>
|
onContextMenu={(e) =>
|
||||||
handleHeaderContextMenu(
|
handleHeaderContextMenu(
|
||||||
e,
|
e,
|
||||||
@ -456,6 +461,9 @@ function blockViewToIcon(view: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function blockViewToName(view: string): string {
|
function blockViewToName(view: string): string {
|
||||||
|
if (util.isBlank(view)) {
|
||||||
|
return "(No View)";
|
||||||
|
}
|
||||||
if (view == "term") {
|
if (view == "term") {
|
||||||
return "Terminal";
|
return "Terminal";
|
||||||
}
|
}
|
||||||
@ -476,12 +484,12 @@ function getViewElemAndModel(
|
|||||||
blockView: string,
|
blockView: string,
|
||||||
blockRef: React.RefObject<HTMLDivElement>
|
blockRef: React.RefObject<HTMLDivElement>
|
||||||
): { viewModel: ViewModel; viewElem: JSX.Element } {
|
): { viewModel: ViewModel; viewElem: JSX.Element } {
|
||||||
if (blockView == null) {
|
|
||||||
return { viewElem: null, viewModel: null };
|
|
||||||
}
|
|
||||||
let viewElem: JSX.Element = null;
|
let viewElem: JSX.Element = null;
|
||||||
let viewModel: ViewModel = null;
|
let viewModel: ViewModel = null;
|
||||||
if (blockView === "term") {
|
if (util.isBlank(blockView)) {
|
||||||
|
viewElem = <CenteredDiv>No View</CenteredDiv>;
|
||||||
|
viewModel = makeDefaultViewModel(blockId);
|
||||||
|
} else if (blockView === "term") {
|
||||||
const termViewModel = makeTerminalModel(blockId);
|
const termViewModel = makeTerminalModel(blockId);
|
||||||
viewElem = <TerminalView key={blockId} blockId={blockId} model={termViewModel} />;
|
viewElem = <TerminalView key={blockId} blockId={blockId} model={termViewModel} />;
|
||||||
viewModel = termViewModel;
|
viewModel = termViewModel;
|
||||||
@ -501,6 +509,7 @@ function getViewElemAndModel(
|
|||||||
viewModel = waveAiModel;
|
viewModel = waveAiModel;
|
||||||
}
|
}
|
||||||
if (viewModel == null) {
|
if (viewModel == null) {
|
||||||
|
viewElem = <CenteredDiv>Invalid View "{blockView}"</CenteredDiv>;
|
||||||
viewModel = makeDefaultViewModel(blockId);
|
viewModel = makeDefaultViewModel(blockId);
|
||||||
}
|
}
|
||||||
return { viewElem, viewModel };
|
return { viewElem, viewModel };
|
||||||
@ -511,11 +520,11 @@ function makeDefaultViewModel(blockId: string): ViewModel {
|
|||||||
let viewModel: ViewModel = {
|
let viewModel: ViewModel = {
|
||||||
viewIcon: jotai.atom((get) => {
|
viewIcon: jotai.atom((get) => {
|
||||||
const blockData = get(blockDataAtom);
|
const blockData = get(blockDataAtom);
|
||||||
return blockViewToIcon(blockData?.view);
|
return blockViewToIcon(blockData?.meta?.view);
|
||||||
}),
|
}),
|
||||||
viewName: jotai.atom((get) => {
|
viewName: jotai.atom((get) => {
|
||||||
const blockData = get(blockDataAtom);
|
const blockData = get(blockDataAtom);
|
||||||
return blockViewToName(blockData?.view);
|
return blockViewToName(blockData?.meta?.view);
|
||||||
}),
|
}),
|
||||||
viewText: jotai.atom((get) => {
|
viewText: jotai.atom((get) => {
|
||||||
const blockData = get(blockDataAtom);
|
const blockData = get(blockDataAtom);
|
||||||
@ -532,7 +541,7 @@ const BlockPreview = React.memo(({ blockId, layoutModel }: BlockProps) => {
|
|||||||
if (!blockData) {
|
if (!blockData) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let { viewModel } = getViewElemAndModel(blockId, blockData?.view, null);
|
let { viewModel } = getViewElemAndModel(blockId, blockData?.meta?.view, null);
|
||||||
return (
|
return (
|
||||||
<BlockFrame
|
<BlockFrame
|
||||||
key={blockId}
|
key={blockId}
|
||||||
@ -588,8 +597,8 @@ const BlockFull = React.memo(({ blockId, layoutModel }: BlockProps) => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
let { viewElem, viewModel } = React.useMemo(
|
let { viewElem, viewModel } = React.useMemo(
|
||||||
() => getViewElemAndModel(blockId, blockData?.view, blockRef),
|
() => getViewElemAndModel(blockId, blockData?.meta?.view, blockRef),
|
||||||
[blockId, blockData?.view, blockRef]
|
[blockId, blockData?.meta?.view, blockRef]
|
||||||
);
|
);
|
||||||
|
|
||||||
const determineFocusedChild = React.useCallback(
|
const determineFocusedChild = React.useCallback(
|
||||||
|
@ -284,10 +284,10 @@ export class PreviewModel implements ViewModel {
|
|||||||
label: "Open Terminal in New Block",
|
label: "Open Terminal in New Block",
|
||||||
click: async () => {
|
click: async () => {
|
||||||
const termBlockDef: BlockDef = {
|
const termBlockDef: BlockDef = {
|
||||||
controller: "shell",
|
|
||||||
view: "term",
|
|
||||||
meta: {
|
meta: {
|
||||||
cwd: globalStore.get(this.fileName),
|
view: "term",
|
||||||
|
controller: "shell",
|
||||||
|
"cmd:cwd": globalStore.get(this.fileName),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
await createBlock(termBlockDef);
|
await createBlock(termBlockDef);
|
||||||
@ -579,4 +579,4 @@ function PreviewView({ blockId, model }: { blockId: string; model: PreviewModel
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export { PreviewView, makePreviewModel };
|
export { makePreviewModel, PreviewView };
|
||||||
|
@ -143,7 +143,7 @@ class TermViewModel {
|
|||||||
});
|
});
|
||||||
this.viewName = jotai.atom((get) => {
|
this.viewName = jotai.atom((get) => {
|
||||||
const blockData = get(this.blockAtom);
|
const blockData = get(this.blockAtom);
|
||||||
if (blockData.controller == "cmd") {
|
if (blockData?.meta?.controller == "cmd") {
|
||||||
return "Command";
|
return "Command";
|
||||||
}
|
}
|
||||||
return "Terminal";
|
return "Terminal";
|
||||||
@ -219,7 +219,7 @@ const TerminalView = ({ blockId, model }: TerminalViewProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const settings = globalStore.get(atoms.settingsConfigAtom);
|
const settings = globalStore.get(atoms.settingsConfigAtom);
|
||||||
const termTheme = computeTheme(settings, blockData?.meta?.termtheme);
|
const termTheme = computeTheme(settings, blockData?.meta?.["term:theme"]);
|
||||||
const termWrap = new TermWrap(
|
const termWrap = new TermWrap(
|
||||||
blockId,
|
blockId,
|
||||||
connectElemRef.current,
|
connectElemRef.current,
|
||||||
|
@ -99,7 +99,7 @@ function TermSticker({ sticker, config }: { sticker: StickerType; config: Sticke
|
|||||||
console.log("clickHandler", sticker.clickcmd, sticker.clickblockdef);
|
console.log("clickHandler", sticker.clickcmd, sticker.clickblockdef);
|
||||||
if (sticker.clickcmd) {
|
if (sticker.clickcmd) {
|
||||||
const b64data = btoa(sticker.clickcmd);
|
const b64data = btoa(sticker.clickcmd);
|
||||||
WshServer.BlockInputCommand({ blockid: config.blockId, inputdata64: b64data });
|
WshServer.ControllerInputCommand({ blockid: config.blockId, inputdata64: b64data });
|
||||||
}
|
}
|
||||||
if (sticker.clickblockdef) {
|
if (sticker.clickblockdef) {
|
||||||
createBlock(sticker.clickblockdef);
|
createBlock(sticker.clickblockdef);
|
||||||
@ -183,7 +183,7 @@ export function TermStickers({ config }: { config: StickerTermConfig }) {
|
|||||||
imgsrc: "~/Downloads/natureicon.png",
|
imgsrc: "~/Downloads/natureicon.png",
|
||||||
opacity: 0.8,
|
opacity: 0.8,
|
||||||
pointerevents: true,
|
pointerevents: true,
|
||||||
clickblockdef: { view: "preview", meta: { file: "~/" } },
|
clickblockdef: { meta: { file: "~/", view: "preview" } },
|
||||||
});
|
});
|
||||||
stickers.push({
|
stickers.push({
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
|
@ -16,7 +16,7 @@ const TermThemeUpdater = ({ blockId, termRef }: TermThemeProps) => {
|
|||||||
const { termthemes } = useAtomValue(atoms.settingsConfigAtom);
|
const { termthemes } = useAtomValue(atoms.settingsConfigAtom);
|
||||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||||
let defaultThemeName = "default-dark";
|
let defaultThemeName = "default-dark";
|
||||||
let themeName = blockData.meta?.termtheme ?? "default-dark";
|
let themeName = blockData.meta?.["term:theme"] ?? "default-dark";
|
||||||
|
|
||||||
const defaultTheme: TermThemeType = termthemes?.[defaultThemeName] || ({} as any);
|
const defaultTheme: TermThemeType = termthemes?.[defaultThemeName] || ({} as any);
|
||||||
const theme: TermThemeType = termthemes?.[themeName] || ({} as any);
|
const theme: TermThemeType = termthemes?.[themeName] || ({} as any);
|
||||||
|
@ -24,11 +24,6 @@
|
|||||||
.filler {
|
.filler {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
|
||||||
cursor: default;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.chat-msg {
|
.chat-msg {
|
||||||
|
@ -20,16 +20,18 @@ const Widgets = React.memo(() => {
|
|||||||
const newWidgetModalVisible = React.useState(false);
|
const newWidgetModalVisible = React.useState(false);
|
||||||
async function clickTerminal() {
|
async function clickTerminal() {
|
||||||
const termBlockDef: BlockDef = {
|
const termBlockDef: BlockDef = {
|
||||||
controller: "shell",
|
meta: {
|
||||||
view: "term",
|
controller: "shell",
|
||||||
|
view: "term",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
createBlock(termBlockDef);
|
createBlock(termBlockDef);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function clickHome() {
|
async function clickHome() {
|
||||||
const editDef: BlockDef = {
|
const editDef: BlockDef = {
|
||||||
view: "preview",
|
|
||||||
meta: {
|
meta: {
|
||||||
|
view: "preview",
|
||||||
file: "~",
|
file: "~",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -37,8 +39,8 @@ const Widgets = React.memo(() => {
|
|||||||
}
|
}
|
||||||
async function clickWeb() {
|
async function clickWeb() {
|
||||||
const editDef: BlockDef = {
|
const editDef: BlockDef = {
|
||||||
view: "web",
|
|
||||||
meta: {
|
meta: {
|
||||||
|
view: "web",
|
||||||
url: "https://waveterm.dev/",
|
url: "https://waveterm.dev/",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
59
frontend/types/gotypes.d.ts
vendored
59
frontend/types/gotypes.d.ts
vendored
@ -24,11 +24,8 @@ declare global {
|
|||||||
// wstore.Block
|
// wstore.Block
|
||||||
type Block = WaveObj & {
|
type Block = WaveObj & {
|
||||||
blockdef: BlockDef;
|
blockdef: BlockDef;
|
||||||
controller: string;
|
|
||||||
view: string;
|
|
||||||
runtimeopts?: RuntimeOpts;
|
runtimeopts?: RuntimeOpts;
|
||||||
stickers?: StickerType[];
|
stickers?: StickerType[];
|
||||||
meta: MetaType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// blockcontroller.BlockControllerRuntimeStatus
|
// blockcontroller.BlockControllerRuntimeStatus
|
||||||
@ -40,8 +37,6 @@ declare global {
|
|||||||
|
|
||||||
// wstore.BlockDef
|
// wstore.BlockDef
|
||||||
type BlockDef = {
|
type BlockDef = {
|
||||||
controller?: string;
|
|
||||||
view?: string;
|
|
||||||
files?: {[key: string]: FileDef};
|
files?: {[key: string]: FileDef};
|
||||||
meta?: MetaType;
|
meta?: MetaType;
|
||||||
};
|
};
|
||||||
@ -60,9 +55,7 @@ declare global {
|
|||||||
|
|
||||||
// wstore.Client
|
// wstore.Client
|
||||||
type Client = WaveObj & {
|
type Client = WaveObj & {
|
||||||
mainwindowid: string;
|
|
||||||
windowids: string[];
|
windowids: string[];
|
||||||
meta: MetaType;
|
|
||||||
tosagreed?: number;
|
tosagreed?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,7 +63,7 @@ declare global {
|
|||||||
type CommandAppendIJsonData = {
|
type CommandAppendIJsonData = {
|
||||||
zoneid: string;
|
zoneid: string;
|
||||||
filename: string;
|
filename: string;
|
||||||
data: MetaType;
|
data: {[key: string]: any};
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshrpc.CommandBlockInputData
|
// wshrpc.CommandBlockInputData
|
||||||
@ -144,7 +137,7 @@ declare global {
|
|||||||
path?: string;
|
path?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
content?: string;
|
content?: string;
|
||||||
meta?: MetaType;
|
meta?: {[key: string]: any};
|
||||||
};
|
};
|
||||||
|
|
||||||
// fileservice.FileInfo
|
// fileservice.FileInfo
|
||||||
@ -178,10 +171,42 @@ declare global {
|
|||||||
type LayoutNode = WaveObj & {
|
type LayoutNode = WaveObj & {
|
||||||
node?: any;
|
node?: any;
|
||||||
magnifiednodeid?: string;
|
magnifiednodeid?: string;
|
||||||
meta?: MetaType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type MetaType = {[key: string]: any}
|
// wstore.MetaTSType
|
||||||
|
type MetaType = {
|
||||||
|
view?: string;
|
||||||
|
controller?: string;
|
||||||
|
title?: string;
|
||||||
|
file?: string;
|
||||||
|
url?: string;
|
||||||
|
connection?: string;
|
||||||
|
icon?: string;
|
||||||
|
"icon:color"?: string;
|
||||||
|
frame?: boolean;
|
||||||
|
"frame:*"?: boolean;
|
||||||
|
"frame:bordercolor"?: string;
|
||||||
|
"frame:bordercolor:focused"?: string;
|
||||||
|
cmd?: string;
|
||||||
|
"cmd:*"?: boolean;
|
||||||
|
"cmd:interactive"?: boolean;
|
||||||
|
"cmd:login"?: boolean;
|
||||||
|
"cmd:runonstart"?: boolean;
|
||||||
|
"cmd:clearonstart"?: boolean;
|
||||||
|
"cmd:clearonrestart"?: boolean;
|
||||||
|
"cmd:env"?: {[key: string]: string};
|
||||||
|
"cmd:cwd"?: string;
|
||||||
|
"cmd:nowsh"?: boolean;
|
||||||
|
bg?: string;
|
||||||
|
"bg:*"?: boolean;
|
||||||
|
"bg:opacity"?: number;
|
||||||
|
"bg:blendmode"?: string;
|
||||||
|
"term:*"?: boolean;
|
||||||
|
"term:fontsize"?: number;
|
||||||
|
"term:fontfamily"?: string;
|
||||||
|
"term:mode"?: string;
|
||||||
|
"term:theme"?: string;
|
||||||
|
};
|
||||||
|
|
||||||
// tsgenmeta.MethodMeta
|
// tsgenmeta.MethodMeta
|
||||||
type MethodMeta = {
|
type MethodMeta = {
|
||||||
@ -284,6 +309,8 @@ declare global {
|
|||||||
autoupdate: AutoUpdateOpts;
|
autoupdate: AutoUpdateOpts;
|
||||||
termthemes: {[key: string]: TermThemeType};
|
termthemes: {[key: string]: TermThemeType};
|
||||||
window: WindowSettingsType;
|
window: WindowSettingsType;
|
||||||
|
defaultmeta?: MetaType;
|
||||||
|
presets?: {[key: string]: MetaType};
|
||||||
};
|
};
|
||||||
|
|
||||||
// wstore.StickerClickOptsType
|
// wstore.StickerClickOptsType
|
||||||
@ -302,7 +329,7 @@ declare global {
|
|||||||
// wstore.StickerType
|
// wstore.StickerType
|
||||||
type StickerType = {
|
type StickerType = {
|
||||||
stickertype: string;
|
stickertype: string;
|
||||||
style: MetaType;
|
style: {[key: string]: any};
|
||||||
clickopts?: StickerClickOptsType;
|
clickopts?: StickerClickOptsType;
|
||||||
display: StickerDisplayOptsType;
|
display: StickerDisplayOptsType;
|
||||||
};
|
};
|
||||||
@ -319,7 +346,6 @@ declare global {
|
|||||||
name: string;
|
name: string;
|
||||||
layoutnode: string;
|
layoutnode: string;
|
||||||
blockids: string[];
|
blockids: string[];
|
||||||
meta: MetaType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// shellexec.TermSize
|
// shellexec.TermSize
|
||||||
@ -392,7 +418,7 @@ declare global {
|
|||||||
type VDomElem = {
|
type VDomElem = {
|
||||||
id?: string;
|
id?: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
props?: MetaType;
|
props?: {[key: string]: any};
|
||||||
children?: VDomElem[];
|
children?: VDomElem[];
|
||||||
text?: string;
|
text?: string;
|
||||||
};
|
};
|
||||||
@ -465,7 +491,7 @@ declare global {
|
|||||||
createdts: number;
|
createdts: number;
|
||||||
size: number;
|
size: number;
|
||||||
modts: number;
|
modts: number;
|
||||||
meta: MetaType;
|
meta: {[key: string]: any};
|
||||||
};
|
};
|
||||||
|
|
||||||
// waveobj.WaveObj
|
// waveobj.WaveObj
|
||||||
@ -473,6 +499,7 @@ declare global {
|
|||||||
otype: string;
|
otype: string;
|
||||||
oid: string;
|
oid: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
meta: MetaType;
|
||||||
};
|
};
|
||||||
|
|
||||||
// wstore.WaveObjUpdate
|
// wstore.WaveObjUpdate
|
||||||
@ -492,7 +519,6 @@ declare global {
|
|||||||
pos: Point;
|
pos: Point;
|
||||||
winsize: WinSize;
|
winsize: WinSize;
|
||||||
lastfocusts: number;
|
lastfocusts: number;
|
||||||
meta: MetaType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// service.WebCallType
|
// service.WebCallType
|
||||||
@ -538,7 +564,6 @@ declare global {
|
|||||||
type Workspace = WaveObj & {
|
type Workspace = WaveObj & {
|
||||||
name: string;
|
name: string;
|
||||||
tabids: string[];
|
tabids: string[];
|
||||||
meta: MetaType;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// wshrpc.WshRpcCommandOpts
|
// wshrpc.WshRpcCommandOpts
|
||||||
|
@ -186,7 +186,7 @@ func (bc *BlockController) resetTerminalState() {
|
|||||||
var shouldTruncate bool
|
var shouldTruncate bool
|
||||||
blockData, getBlockDataErr := wstore.DBMustGet[*wstore.Block](ctx, bc.BlockId)
|
blockData, getBlockDataErr := wstore.DBMustGet[*wstore.Block](ctx, bc.BlockId)
|
||||||
if getBlockDataErr == nil {
|
if getBlockDataErr == nil {
|
||||||
shouldTruncate = getBoolFromMeta(blockData.Meta, wstore.MetaKey_CmdClearOnRestart, false)
|
shouldTruncate = blockData.Meta.GetBool(wstore.MetaKey_CmdClearOnRestart, false)
|
||||||
}
|
}
|
||||||
if shouldTruncate {
|
if shouldTruncate {
|
||||||
err := HandleTruncateBlockFile(bc.BlockId, BlockFile_Main)
|
err := HandleTruncateBlockFile(bc.BlockId, BlockFile_Main)
|
||||||
@ -208,34 +208,6 @@ func (bc *BlockController) resetTerminalState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMetaBool(meta map[string]any, key string, def bool) bool {
|
|
||||||
val, found := meta[key]
|
|
||||||
if !found {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
if val == nil {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
if bval, ok := val.(bool); ok {
|
|
||||||
return bval
|
|
||||||
}
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMetaStr(meta map[string]any, key string, def string) string {
|
|
||||||
val, found := meta[key]
|
|
||||||
if !found {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
if val == nil {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
if sval, ok := val.(string); ok {
|
|
||||||
return sval
|
|
||||||
}
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
|
|
||||||
// every byte is 4-bits of randomness
|
// every byte is 4-bits of randomness
|
||||||
func randomHexString(numHexDigits int) (string, error) {
|
func randomHexString(numHexDigits int) (string, error) {
|
||||||
numBytes := (numHexDigits + 1) / 2 // Calculate the number of bytes needed
|
numBytes := (numHexDigits + 1) / 2 // Calculate the number of bytes needed
|
||||||
@ -248,7 +220,7 @@ func randomHexString(numHexDigits int) (string, error) {
|
|||||||
return hexStr[:numHexDigits], nil // Return the exact number of hex digits
|
return hexStr[:numHexDigits], nil // Return the exact number of hex digits
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[string]any) error {
|
func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta waveobj.MetaMapType) error {
|
||||||
// create a circular blockfile for the output
|
// create a circular blockfile for the output
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
|
ctx, cancelFn := context.WithTimeout(context.Background(), 2*time.Second)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
@ -276,7 +248,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
|||||||
return shellProcErr
|
return shellProcErr
|
||||||
}
|
}
|
||||||
var remoteDomainSocketName string
|
var remoteDomainSocketName string
|
||||||
remoteName := getMetaStr(blockMeta, "connection", "")
|
remoteName := blockMeta.GetString(wstore.MetaKey_Connection, "")
|
||||||
isRemote := remoteName != ""
|
isRemote := remoteName != ""
|
||||||
if isRemote {
|
if isRemote {
|
||||||
randStr, err := randomHexString(16) // 64-bits of randomness
|
randStr, err := randomHexString(16) // 64-bits of randomness
|
||||||
@ -289,7 +261,7 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
|||||||
cmdOpts := shellexec.CommandOptsType{
|
cmdOpts := shellexec.CommandOptsType{
|
||||||
Env: make(map[string]string),
|
Env: make(map[string]string),
|
||||||
}
|
}
|
||||||
if !getMetaBool(blockMeta, "nowsh", false) {
|
if !blockMeta.GetBool(wstore.MetaKey_CmdNoWsh, false) {
|
||||||
if isRemote {
|
if isRemote {
|
||||||
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId}, remoteDomainSocketName)
|
jwtStr, err := wshutil.MakeClientJWTToken(wshrpc.RpcContext{TabId: bc.TabId, BlockId: bc.BlockId}, remoteDomainSocketName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -307,44 +279,31 @@ func (bc *BlockController) DoRunShellCommand(rc *RunShellOpts, blockMeta map[str
|
|||||||
if bc.ControllerType == BlockController_Shell {
|
if bc.ControllerType == BlockController_Shell {
|
||||||
cmdOpts.Interactive = true
|
cmdOpts.Interactive = true
|
||||||
cmdOpts.Login = true
|
cmdOpts.Login = true
|
||||||
cmdOpts.Cwd, _ = blockMeta["cmd:cwd"].(string)
|
cmdOpts.Cwd = blockMeta.GetString(wstore.MetaKey_CmdCwd, "")
|
||||||
if cmdOpts.Cwd != "" {
|
if cmdOpts.Cwd != "" {
|
||||||
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
||||||
}
|
}
|
||||||
} else if bc.ControllerType == BlockController_Cmd {
|
} else if bc.ControllerType == BlockController_Cmd {
|
||||||
if _, ok := blockMeta["cmd"].(string); ok {
|
cmdStr = blockMeta.GetString(wstore.MetaKey_Cmd, "")
|
||||||
cmdStr = blockMeta["cmd"].(string)
|
if cmdStr == "" {
|
||||||
} else {
|
|
||||||
return fmt.Errorf("missing cmd in block meta")
|
return fmt.Errorf("missing cmd in block meta")
|
||||||
}
|
}
|
||||||
if _, ok := blockMeta["cmd:cwd"].(string); ok {
|
cmdOpts.Cwd = blockMeta.GetString(wstore.MetaKey_CmdCwd, "")
|
||||||
cmdOpts.Cwd = blockMeta["cmd:cwd"].(string)
|
if cmdOpts.Cwd != "" {
|
||||||
if cmdOpts.Cwd != "" {
|
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
||||||
cmdOpts.Cwd = wavebase.ExpandHomeDir(cmdOpts.Cwd)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if _, ok := blockMeta["cmd:interactive"]; ok {
|
cmdOpts.Interactive = blockMeta.GetBool(wstore.MetaKey_CmdInteractive, false)
|
||||||
if blockMeta["cmd:interactive"].(bool) {
|
cmdOpts.Login = blockMeta.GetBool(wstore.MetaKey_CmdLogin, false)
|
||||||
cmdOpts.Interactive = true
|
cmdEnv := blockMeta.GetMap(wstore.MetaKey_CmdEnv)
|
||||||
|
for k, v := range cmdEnv {
|
||||||
|
if v == nil {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
if _, ok := v.(string); ok {
|
||||||
if _, ok := blockMeta["cmd:login"]; ok {
|
cmdOpts.Env[k] = v.(string)
|
||||||
if blockMeta["cmd:login"].(bool) {
|
|
||||||
cmdOpts.Login = true
|
|
||||||
}
|
}
|
||||||
}
|
if _, ok := v.(float64); ok {
|
||||||
if _, ok := blockMeta["cmd:env"].(map[string]any); ok {
|
cmdOpts.Env[k] = fmt.Sprintf("%v", v)
|
||||||
cmdEnv := blockMeta["cmd:env"].(map[string]any)
|
|
||||||
for k, v := range cmdEnv {
|
|
||||||
if v == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if _, ok := v.(string); ok {
|
|
||||||
cmdOpts.Env[k] = v.(string)
|
|
||||||
}
|
|
||||||
if _, ok := v.(float64); ok {
|
|
||||||
cmdOpts.Env[k] = fmt.Sprintf("%v", v)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -477,8 +436,9 @@ func (bc *BlockController) run(bdata *wstore.Block, blockMeta map[string]any) {
|
|||||||
bc.Status = Status_Running
|
bc.Status = Status_Running
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
if bdata.Controller != BlockController_Shell && bdata.Controller != BlockController_Cmd {
|
controllerName := bdata.Meta.GetString(wstore.MetaKey_Controller, "")
|
||||||
log.Printf("unknown controller %q\n", bdata.Controller)
|
if controllerName != BlockController_Shell && controllerName != BlockController_Cmd {
|
||||||
|
log.Printf("unknown controller %q\n", controllerName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if getBoolFromMeta(blockMeta, wstore.MetaKey_CmdClearOnStart, false) {
|
if getBoolFromMeta(blockMeta, wstore.MetaKey_CmdClearOnStart, false) {
|
||||||
@ -527,12 +487,13 @@ func StartBlockController(ctx context.Context, tabId string, blockId string) err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting block: %w", err)
|
return fmt.Errorf("error getting block: %w", err)
|
||||||
}
|
}
|
||||||
if blockData.Controller == "" {
|
controllerName := blockData.Meta.GetString(wstore.MetaKey_Controller, "")
|
||||||
|
if controllerName == "" {
|
||||||
// nothing to start
|
// nothing to start
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if blockData.Controller != BlockController_Shell && blockData.Controller != BlockController_Cmd {
|
if controllerName != BlockController_Shell && controllerName != BlockController_Cmd {
|
||||||
return fmt.Errorf("unknown controller %q", blockData.Controller)
|
return fmt.Errorf("unknown controller %q", controllerName)
|
||||||
}
|
}
|
||||||
globalLock.Lock()
|
globalLock.Lock()
|
||||||
defer globalLock.Unlock()
|
defer globalLock.Unlock()
|
||||||
@ -542,7 +503,7 @@ func StartBlockController(ctx context.Context, tabId string, blockId string) err
|
|||||||
}
|
}
|
||||||
bc := &BlockController{
|
bc := &BlockController{
|
||||||
Lock: &sync.Mutex{},
|
Lock: &sync.Mutex{},
|
||||||
ControllerType: blockData.Controller,
|
ControllerType: controllerName,
|
||||||
TabId: tabId,
|
TabId: tabId,
|
||||||
BlockId: blockId,
|
BlockId: blockId,
|
||||||
Status: Status_Init,
|
Status: Status_Init,
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ijson values are built out of standard go building blocks:
|
// ijson values are built out of standard go building blocks:
|
||||||
@ -56,6 +57,45 @@ func MakeAppendCommand(path Path, value any) Command {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var pathPartKeyRe = regexp.MustCompile(`^[a-zA-Z0-9:_#-]+`)
|
||||||
|
|
||||||
|
func ParseSimplePath(input string) ([]any, error) {
|
||||||
|
var path []any
|
||||||
|
// Scan the input string character by character
|
||||||
|
for i := 0; i < len(input); {
|
||||||
|
if input[i] == '[' {
|
||||||
|
// Handle the index
|
||||||
|
end := strings.Index(input[i:], "]")
|
||||||
|
if end == -1 {
|
||||||
|
return nil, fmt.Errorf("unmatched bracket at position %d", i)
|
||||||
|
}
|
||||||
|
index, err := strconv.Atoi(input[i+1 : i+end])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid index at position %d: %v", i, err)
|
||||||
|
}
|
||||||
|
path = append(path, index)
|
||||||
|
i += end + 1
|
||||||
|
} else {
|
||||||
|
// Handle the key
|
||||||
|
j := i
|
||||||
|
for j < len(input) && input[j] != '.' && input[j] != '[' {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
key := input[i:j]
|
||||||
|
if !pathPartKeyRe.MatchString(key) {
|
||||||
|
return nil, fmt.Errorf("invalid key at position %d: %s", i, key)
|
||||||
|
}
|
||||||
|
path = append(path, key)
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
if i < len(input) && input[i] == '.' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return path, nil
|
||||||
|
}
|
||||||
|
|
||||||
type PathError struct {
|
type PathError struct {
|
||||||
Err string
|
Err string
|
||||||
}
|
}
|
||||||
|
@ -66,8 +66,9 @@ func (bs *BlockService) SaveWaveAiData(ctx context.Context, blockId string, hist
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if block.View != "waveai" {
|
viewName := block.Meta.GetString(wstore.MetaKey_View, "")
|
||||||
return fmt.Errorf("invalid view type: %s", block.View)
|
if viewName != "waveai" {
|
||||||
|
return fmt.Errorf("invalid view type: %s", viewName)
|
||||||
}
|
}
|
||||||
historyBytes, err := json.Marshal(history)
|
historyBytes, err := json.Marshal(history)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -189,7 +189,8 @@ func (svc *ObjectService) CreateBlock(uiContext wstore.UIContext, blockDef *wsto
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("error creating block: %w", err)
|
return "", nil, fmt.Errorf("error creating block: %w", err)
|
||||||
}
|
}
|
||||||
if blockData.Controller != "" {
|
controllerName := blockData.Meta.GetString(wstore.MetaKey_Controller, "")
|
||||||
|
if controllerName != "" {
|
||||||
err = blockcontroller.StartBlockController(ctx, uiContext.ActiveTabId, blockData.OID)
|
err = blockcontroller.StartBlockController(ctx, uiContext.ActiveTabId, blockData.OID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, fmt.Errorf("error starting block controller: %w", err)
|
return "", nil, fmt.Errorf("error starting block controller: %w", err)
|
||||||
@ -228,7 +229,7 @@ func (svc *ObjectService) UpdateObjectMeta_Meta() tsgenmeta.MethodMeta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svc *ObjectService) UpdateObjectMeta(uiContext wstore.UIContext, orefStr string, meta map[string]any) (wstore.UpdatesRtnType, error) {
|
func (svc *ObjectService) UpdateObjectMeta(uiContext wstore.UIContext, orefStr string, meta wstore.MetaMapType) (wstore.UpdatesRtnType, error) {
|
||||||
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
|
ctx, cancelFn := context.WithTimeout(context.Background(), DefaultTimeout)
|
||||||
defer cancelFn()
|
defer cancelFn()
|
||||||
ctx = wstore.ContextWithUpdates(ctx)
|
ctx = wstore.ContextWithUpdates(ctx)
|
||||||
|
@ -89,7 +89,7 @@ func convertNumber(argType reflect.Type, jsonArg float64) (any, error) {
|
|||||||
|
|
||||||
func convertComplex(argType reflect.Type, jsonArg any) (any, error) {
|
func convertComplex(argType reflect.Type, jsonArg any) (any, error) {
|
||||||
nativeArgVal := reflect.New(argType)
|
nativeArgVal := reflect.New(argType)
|
||||||
err := utilfn.DoMapStucture(nativeArgVal.Interface(), jsonArg)
|
err := utilfn.DoMapStructure(nativeArgVal.Interface(), jsonArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ var ExtraTypes = []any{
|
|||||||
vdom.Elem{},
|
vdom.Elem{},
|
||||||
vdom.VDomFuncType{},
|
vdom.VDomFuncType{},
|
||||||
vdom.VDomRefType{},
|
vdom.VDomRefType{},
|
||||||
|
wstore.MetaTSType{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// add extra type unions to generate here
|
// add extra type unions to generate here
|
||||||
@ -55,7 +56,7 @@ var TypeUnions = []tsgenmeta.TypeUnionMeta{
|
|||||||
var contextRType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
var contextRType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
||||||
var errorRType = reflect.TypeOf((*error)(nil)).Elem()
|
var errorRType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
var anyRType = reflect.TypeOf((*interface{})(nil)).Elem()
|
var anyRType = reflect.TypeOf((*interface{})(nil)).Elem()
|
||||||
var metaRType = reflect.TypeOf((*map[string]any)(nil)).Elem()
|
var metaRType = reflect.TypeOf((*wstore.MetaMapType)(nil)).Elem()
|
||||||
var uiContextRType = reflect.TypeOf((*wstore.UIContext)(nil)).Elem()
|
var uiContextRType = reflect.TypeOf((*wstore.UIContext)(nil)).Elem()
|
||||||
var waveObjRType = reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
|
var waveObjRType = reflect.TypeOf((*waveobj.WaveObj)(nil)).Elem()
|
||||||
var updatesRtnRType = reflect.TypeOf(wstore.UpdatesRtnType{})
|
var updatesRtnRType = reflect.TypeOf(wstore.UpdatesRtnType{})
|
||||||
@ -86,6 +87,9 @@ func getTSFieldName(field reflect.StructField) string {
|
|||||||
if namePart == "-" {
|
if namePart == "-" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
if strings.Contains(namePart, ":") {
|
||||||
|
return "\"" + namePart + "\""
|
||||||
|
}
|
||||||
return namePart
|
return namePart
|
||||||
}
|
}
|
||||||
// if namePart is empty, still uses default
|
// if namePart is empty, still uses default
|
||||||
@ -155,8 +159,9 @@ func TypeToTSType(t reflect.Type, tsTypesMap map[reflect.Type]string) (string, [
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tsRenameMap = map[string]string{
|
var tsRenameMap = map[string]string{
|
||||||
"Window": "WaveWindow",
|
"Window": "WaveWindow",
|
||||||
"Elem": "VDomElem",
|
"Elem": "VDomElem",
|
||||||
|
"MetaTSType": "MetaType",
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateTSTypeInternal(rtype reflect.Type, tsTypesMap map[reflect.Type]string) (string, []reflect.Type) {
|
func generateTSTypeInternal(rtype reflect.Type, tsTypesMap map[reflect.Type]string) (string, []reflect.Type) {
|
||||||
@ -183,7 +188,7 @@ func generateTSTypeInternal(rtype reflect.Type, tsTypesMap map[reflect.Type]stri
|
|||||||
if fieldName == "" {
|
if fieldName == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if isWaveObj && (fieldName == waveobj.OTypeKeyName || fieldName == waveobj.OIDKeyName || fieldName == waveobj.VersionKeyName) {
|
if isWaveObj && (fieldName == waveobj.OTypeKeyName || fieldName == waveobj.OIDKeyName || fieldName == waveobj.VersionKeyName || fieldName == waveobj.MetaKeyName) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
optMarker := ""
|
optMarker := ""
|
||||||
@ -192,6 +197,9 @@ func generateTSTypeInternal(rtype reflect.Type, tsTypesMap map[reflect.Type]stri
|
|||||||
}
|
}
|
||||||
tsTypeTag := field.Tag.Get("tstype")
|
tsTypeTag := field.Tag.Get("tstype")
|
||||||
if tsTypeTag != "" {
|
if tsTypeTag != "" {
|
||||||
|
if tsTypeTag == "-" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
|
buf.WriteString(fmt.Sprintf(" %s%s: %s;\n", fieldName, optMarker, tsTypeTag))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -216,14 +224,11 @@ func GenerateWaveObjTSType() string {
|
|||||||
buf.WriteString(" otype: string;\n")
|
buf.WriteString(" otype: string;\n")
|
||||||
buf.WriteString(" oid: string;\n")
|
buf.WriteString(" oid: string;\n")
|
||||||
buf.WriteString(" version: number;\n")
|
buf.WriteString(" version: number;\n")
|
||||||
|
buf.WriteString(" meta: MetaType;\n")
|
||||||
buf.WriteString("};\n")
|
buf.WriteString("};\n")
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateMetaType() string {
|
|
||||||
return "type MetaType = {[key: string]: any}\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateTSTypeUnion(unionMeta tsgenmeta.TypeUnionMeta, tsTypeMap map[reflect.Type]string) {
|
func GenerateTSTypeUnion(unionMeta tsgenmeta.TypeUnionMeta, tsTypeMap map[reflect.Type]string) {
|
||||||
rtn := generateTSTypeUnionInternal(unionMeta)
|
rtn := generateTSTypeUnionInternal(unionMeta)
|
||||||
tsTypeMap[unionMeta.BaseType] = rtn
|
tsTypeMap[unionMeta.BaseType] = rtn
|
||||||
@ -257,10 +262,6 @@ func GenerateTSType(rtype reflect.Type, tsTypesMap map[reflect.Type]string) {
|
|||||||
if rtype.Kind() == reflect.Chan {
|
if rtype.Kind() == reflect.Chan {
|
||||||
rtype = rtype.Elem()
|
rtype = rtype.Elem()
|
||||||
}
|
}
|
||||||
if rtype == metaRType {
|
|
||||||
tsTypesMap[metaRType] = GenerateMetaType()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if rtype == contextRType || rtype == errorRType || rtype == anyRType {
|
if rtype == contextRType || rtype == errorRType || rtype == anyRType {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -747,7 +747,7 @@ func ReUnmarshal(out any, in any) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// does a mapstructure using "json" tags
|
// does a mapstructure using "json" tags
|
||||||
func DoMapStucture(out any, input any) error {
|
func DoMapStructure(out any, input any) error {
|
||||||
dconfig := &mapstructure.DecoderConfig{
|
dconfig := &mapstructure.DecoderConfig{
|
||||||
Result: out,
|
Result: out,
|
||||||
TagName: "json",
|
TagName: "json",
|
||||||
@ -826,3 +826,14 @@ func StarMatchString(pattern string, s string, delimiter string) bool {
|
|||||||
// Check if both pattern and string are fully matched
|
// Check if both pattern and string are fully matched
|
||||||
return pLen == sLen
|
return pLen == sLen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MergeStrMaps[T any](m1 map[string]T, m2 map[string]T) map[string]T {
|
||||||
|
rtn := make(map[string]T)
|
||||||
|
for key, val := range m1 {
|
||||||
|
rtn[key] = val
|
||||||
|
}
|
||||||
|
for key, val := range m2 {
|
||||||
|
rtn[key] = val
|
||||||
|
}
|
||||||
|
return rtn
|
||||||
|
}
|
||||||
|
74
pkg/waveobj/metamap.go
Normal file
74
pkg/waveobj/metamap.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package waveobj
|
||||||
|
|
||||||
|
type MetaMapType map[string]any
|
||||||
|
|
||||||
|
func (m MetaMapType) GetString(key string, def string) string {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MetaMapType) GetBool(key string, def bool) bool {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
if b, ok := v.(bool); ok {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MetaMapType) GetInt(key string, def int) int {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
if fval, ok := v.(float64); ok {
|
||||||
|
return int(fval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MetaMapType) GetFloat(key string, def float64) float64 {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
if fval, ok := v.(float64); ok {
|
||||||
|
return fval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MetaMapType) GetMap(key string) MetaMapType {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
if mval, ok := v.(map[string]any); ok {
|
||||||
|
return MetaMapType(mval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MetaMapType) GetArray(key string) []any {
|
||||||
|
if v, ok := m[key]; ok {
|
||||||
|
if aval, ok := v.([]any); ok {
|
||||||
|
return aval
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m MetaMapType) GetStringArray(key string) []string {
|
||||||
|
arr := m.GetArray(key)
|
||||||
|
if len(arr) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rtn := make([]string, 0, len(arr))
|
||||||
|
for _, v := range arr {
|
||||||
|
if s, ok := v.(string); ok {
|
||||||
|
rtn = append(rtn, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rtn
|
||||||
|
}
|
@ -106,6 +106,7 @@ type waveObjDesc struct {
|
|||||||
|
|
||||||
var waveObjMap = sync.Map{}
|
var waveObjMap = sync.Map{}
|
||||||
var waveObjRType = reflect.TypeOf((*WaveObj)(nil)).Elem()
|
var waveObjRType = reflect.TypeOf((*WaveObj)(nil)).Elem()
|
||||||
|
var metaMapRType = reflect.TypeOf(MetaMapType{})
|
||||||
|
|
||||||
func RegisterType(rtype reflect.Type) {
|
func RegisterType(rtype reflect.Type) {
|
||||||
if rtype.Kind() != reflect.Ptr {
|
if rtype.Kind() != reflect.Ptr {
|
||||||
@ -143,10 +144,8 @@ func RegisterType(rtype reflect.Type) {
|
|||||||
if !found {
|
if !found {
|
||||||
panic(fmt.Sprintf("missing Meta field for %v", rtype))
|
panic(fmt.Sprintf("missing Meta field for %v", rtype))
|
||||||
}
|
}
|
||||||
if metaField.Type.Kind() != reflect.Map ||
|
if metaField.Type != metaMapRType {
|
||||||
metaField.Type.Elem().Kind() != reflect.Interface ||
|
panic(fmt.Sprintf("Meta field must be MetaMapType for %v", rtype))
|
||||||
metaField.Type.Key().Kind() != reflect.String {
|
|
||||||
panic(fmt.Sprintf("Meta field must be map[string]any for %v", rtype))
|
|
||||||
}
|
}
|
||||||
_, found = waveObjMap.Load(otype)
|
_, found = waveObjMap.Load(otype)
|
||||||
if found {
|
if found {
|
||||||
@ -200,12 +199,16 @@ func SetVersion(waveObj WaveObj, version int) {
|
|||||||
reflect.ValueOf(waveObj).Elem().FieldByIndex(desc.VersionField.Index).SetInt(int64(version))
|
reflect.ValueOf(waveObj).Elem().FieldByIndex(desc.VersionField.Index).SetInt(int64(version))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMeta(waveObj WaveObj) map[string]any {
|
func GetMeta(waveObj WaveObj) MetaMapType {
|
||||||
desc := getWaveObjDesc(waveObj.GetOType())
|
desc := getWaveObjDesc(waveObj.GetOType())
|
||||||
if desc == nil {
|
if desc == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(waveObj).Elem().FieldByIndex(desc.MetaField.Index).Interface().(map[string]any)
|
mval := reflect.ValueOf(waveObj).Elem().FieldByIndex(desc.MetaField.Index).Interface()
|
||||||
|
if mval == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return mval.(MetaMapType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetMeta(waveObj WaveObj, meta map[string]any) {
|
func SetMeta(waveObj WaveObj, meta map[string]any) {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wavebase"
|
"github.com/wavetermdev/thenextwave/pkg/wavebase"
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||||
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
"github.com/wavetermdev/thenextwave/pkg/wstore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -98,6 +99,9 @@ type SettingsConfigType struct {
|
|||||||
AutoUpdate *AutoUpdateOpts `json:"autoupdate"`
|
AutoUpdate *AutoUpdateOpts `json:"autoupdate"`
|
||||||
TermThemes TermThemesConfigType `json:"termthemes"`
|
TermThemes TermThemesConfigType `json:"termthemes"`
|
||||||
WindowSettings WindowSettingsType `json:"window"`
|
WindowSettings WindowSettingsType `json:"window"`
|
||||||
|
|
||||||
|
DefaultMeta *waveobj.MetaMapType `json:"defaultmeta,omitempty"`
|
||||||
|
Presets map[string]*waveobj.MetaMapType `json:"presets,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var DefaultTermDarkTheme = TermThemeType{
|
var DefaultTermDarkTheme = TermThemeType{
|
||||||
@ -181,30 +185,38 @@ func applyDefaultSettings(settings *SettingsConfigType) {
|
|||||||
Icon: "files",
|
Icon: "files",
|
||||||
Label: "files",
|
Label: "files",
|
||||||
BlockDef: wstore.BlockDef{
|
BlockDef: wstore.BlockDef{
|
||||||
View: "preview",
|
Meta: map[string]any{
|
||||||
Meta: map[string]any{"file": wavebase.GetHomeDir()},
|
wstore.MetaKey_View: "preview",
|
||||||
|
wstore.MetaKey_File: wavebase.GetHomeDir(),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Icon: "chart-simple",
|
Icon: "chart-simple",
|
||||||
Label: "chart",
|
Label: "chart",
|
||||||
BlockDef: wstore.BlockDef{
|
BlockDef: wstore.BlockDef{
|
||||||
View: "plot",
|
Meta: map[string]any{
|
||||||
|
wstore.MetaKey_View: "plot",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Icon: "globe",
|
Icon: "globe",
|
||||||
Label: "web",
|
Label: "web",
|
||||||
BlockDef: wstore.BlockDef{
|
BlockDef: wstore.BlockDef{
|
||||||
View: "web",
|
Meta: map[string]any{
|
||||||
Meta: map[string]any{"url": "https://waveterm.dev/"},
|
wstore.MetaKey_View: "web",
|
||||||
|
wstore.MetaKey_Url: "https://waveterm.dev/",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Icon: "sparkles",
|
Icon: "sparkles",
|
||||||
Label: "waveai",
|
Label: "waveai",
|
||||||
BlockDef: wstore.BlockDef{
|
BlockDef: wstore.BlockDef{
|
||||||
View: "waveai",
|
Meta: map[string]any{
|
||||||
|
wstore.MetaKey_View: "waveai",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -72,21 +72,21 @@ func ParseWSCommandMap(cmdMap map[string]any) (WSCommandType, error) {
|
|||||||
switch cmdType {
|
switch cmdType {
|
||||||
case WSCommand_SetBlockTermSize:
|
case WSCommand_SetBlockTermSize:
|
||||||
var cmd SetBlockTermSizeWSCommand
|
var cmd SetBlockTermSizeWSCommand
|
||||||
err := utilfn.DoMapStucture(&cmd, cmdMap)
|
err := utilfn.DoMapStructure(&cmd, cmdMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error decoding SetBlockTermSizeWSCommand: %w", err)
|
return nil, fmt.Errorf("error decoding SetBlockTermSizeWSCommand: %w", err)
|
||||||
}
|
}
|
||||||
return &cmd, nil
|
return &cmd, nil
|
||||||
case WSCommand_BlockInput:
|
case WSCommand_BlockInput:
|
||||||
var cmd BlockInputWSCommand
|
var cmd BlockInputWSCommand
|
||||||
err := utilfn.DoMapStucture(&cmd, cmdMap)
|
err := utilfn.DoMapStructure(&cmd, cmdMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error decoding BlockInputWSCommand: %w", err)
|
return nil, fmt.Errorf("error decoding BlockInputWSCommand: %w", err)
|
||||||
}
|
}
|
||||||
return &cmd, nil
|
return &cmd, nil
|
||||||
case WSCommand_Rpc:
|
case WSCommand_Rpc:
|
||||||
var cmd WSRpcCommand
|
var cmd WSRpcCommand
|
||||||
err := utilfn.DoMapStucture(&cmd, cmdMap)
|
err := utilfn.DoMapStructure(&cmd, cmdMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error decoding WSRpcCommand: %w", err)
|
return nil, fmt.Errorf("error decoding WSRpcCommand: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -96,8 +96,8 @@ func FileWriteCommand(w *wshutil.WshRpc, data wshrpc.CommandFileData, opts *wshr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// command "getmeta", wshserver.GetMetaCommand
|
// command "getmeta", wshserver.GetMetaCommand
|
||||||
func GetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandGetMetaData, opts *wshrpc.WshRpcCommandOpts) (map[string]interface {}, error) {
|
func GetMetaCommand(w *wshutil.WshRpc, data wshrpc.CommandGetMetaData, opts *wshrpc.WshRpcCommandOpts) (waveobj.MetaMapType, error) {
|
||||||
resp, err := sendRpcRequestCallHelper[map[string]interface {}](w, "getmeta", data, opts)
|
resp, err := sendRpcRequestCallHelper[waveobj.MetaMapType](w, "getmeta", data, opts)
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +45,6 @@ const (
|
|||||||
Command_StreamWaveAi = "streamwaveai"
|
Command_StreamWaveAi = "streamwaveai"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MetaDataType = map[string]any
|
|
||||||
|
|
||||||
type RespOrErrorUnion[T any] struct {
|
type RespOrErrorUnion[T any] struct {
|
||||||
Response T
|
Response T
|
||||||
Error error
|
Error error
|
||||||
@ -55,7 +53,7 @@ type RespOrErrorUnion[T any] struct {
|
|||||||
type WshRpcInterface interface {
|
type WshRpcInterface interface {
|
||||||
AuthenticateCommand(ctx context.Context, data string) error
|
AuthenticateCommand(ctx context.Context, data string) error
|
||||||
MessageCommand(ctx context.Context, data CommandMessageData) error
|
MessageCommand(ctx context.Context, data CommandMessageData) error
|
||||||
GetMetaCommand(ctx context.Context, data CommandGetMetaData) (MetaDataType, error)
|
GetMetaCommand(ctx context.Context, data CommandGetMetaData) (wstore.MetaMapType, error)
|
||||||
SetMetaCommand(ctx context.Context, data CommandSetMetaData) error
|
SetMetaCommand(ctx context.Context, data CommandSetMetaData) error
|
||||||
SetViewCommand(ctx context.Context, data CommandBlockSetViewData) error
|
SetViewCommand(ctx context.Context, data CommandBlockSetViewData) error
|
||||||
ControllerInputCommand(ctx context.Context, data CommandBlockInputData) error
|
ControllerInputCommand(ctx context.Context, data CommandBlockInputData) error
|
||||||
@ -130,8 +128,8 @@ type CommandGetMetaData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CommandSetMetaData struct {
|
type CommandSetMetaData struct {
|
||||||
ORef waveobj.ORef `json:"oref" wshcontext:"BlockORef"`
|
ORef waveobj.ORef `json:"oref" wshcontext:"BlockORef"`
|
||||||
Meta MetaDataType `json:"meta"`
|
Meta wstore.MetaMapType `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommandResolveIdsData struct {
|
type CommandResolveIdsData struct {
|
||||||
|
@ -67,7 +67,7 @@ func (ws *WshServer) StreamWaveAiCommand(ctx context.Context, request wshrpc.Ope
|
|||||||
return waveai.RunLocalCompletionStream(ctx, request)
|
return waveai.RunLocalCompletionStream(ctx, request)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *WshServer) GetMetaCommand(ctx context.Context, data wshrpc.CommandGetMetaData) (wshrpc.MetaDataType, error) {
|
func (ws *WshServer) GetMetaCommand(ctx context.Context, data wshrpc.CommandGetMetaData) (waveobj.MetaMapType, error) {
|
||||||
log.Printf("calling meta: %s\n", data.ORef)
|
log.Printf("calling meta: %s\n", data.ORef)
|
||||||
obj, err := wstore.DBGetORef(ctx, data.ORef)
|
obj, err := wstore.DBGetORef(ctx, data.ORef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -82,31 +82,9 @@ func (ws *WshServer) GetMetaCommand(ctx context.Context, data wshrpc.CommandGetM
|
|||||||
func (ws *WshServer) SetMetaCommand(ctx context.Context, data wshrpc.CommandSetMetaData) error {
|
func (ws *WshServer) SetMetaCommand(ctx context.Context, data wshrpc.CommandSetMetaData) error {
|
||||||
log.Printf("SETMETA: %s | %v\n", data.ORef, data.Meta)
|
log.Printf("SETMETA: %s | %v\n", data.ORef, data.Meta)
|
||||||
oref := data.ORef
|
oref := data.ORef
|
||||||
if oref.IsEmpty() {
|
err := wstore.UpdateObjectMeta(ctx, oref, data.Meta)
|
||||||
return fmt.Errorf("no oref")
|
|
||||||
}
|
|
||||||
obj, err := wstore.DBGetORef(ctx, oref)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting object: %w", err)
|
return fmt.Errorf("error updating object meta: %w", err)
|
||||||
}
|
|
||||||
if obj == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
meta := waveobj.GetMeta(obj)
|
|
||||||
if meta == nil {
|
|
||||||
meta = make(map[string]any)
|
|
||||||
}
|
|
||||||
for k, v := range data.Meta {
|
|
||||||
if v == nil {
|
|
||||||
delete(meta, k)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
meta[k] = v
|
|
||||||
}
|
|
||||||
waveobj.SetMeta(obj, meta)
|
|
||||||
err = wstore.DBUpdate(ctx, obj)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error updating block: %w", err)
|
|
||||||
}
|
}
|
||||||
sendWaveObjUpdate(oref)
|
sendWaveObjUpdate(oref)
|
||||||
return nil
|
return nil
|
||||||
@ -177,7 +155,8 @@ func (ws *WshServer) CreateBlockCommand(ctx context.Context, data wshrpc.Command
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating block: %w", err)
|
return nil, fmt.Errorf("error creating block: %w", err)
|
||||||
}
|
}
|
||||||
if blockData.Controller != "" {
|
controllerName := blockData.Meta.GetString(wstore.MetaKey_Controller, "")
|
||||||
|
if controllerName != "" {
|
||||||
// TODO
|
// TODO
|
||||||
err = blockcontroller.StartBlockController(ctx, data.TabId, blockData.OID)
|
err = blockcontroller.StartBlockController(ctx, data.TabId, blockData.OID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -211,7 +190,7 @@ func (ws *WshServer) SetViewCommand(ctx context.Context, data wshrpc.CommandBloc
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting block: %w", err)
|
return fmt.Errorf("error getting block: %w", err)
|
||||||
}
|
}
|
||||||
block.View = data.View
|
block.Meta[wstore.MetaKey_View] = data.View
|
||||||
err = wstore.DBUpdate(ctx, block)
|
err = wstore.DBUpdate(ctx, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error updating block: %w", err)
|
return fmt.Errorf("error updating block: %w", err)
|
||||||
|
@ -236,8 +236,6 @@ func CreateBlock(ctx context.Context, tabId string, blockDef *BlockDef, rtOpts *
|
|||||||
blockData := &Block{
|
blockData := &Block{
|
||||||
OID: blockId,
|
OID: blockId,
|
||||||
BlockDef: blockDef,
|
BlockDef: blockDef,
|
||||||
Controller: blockDef.Controller,
|
|
||||||
View: blockDef.View,
|
|
||||||
RuntimeOpts: rtOpts,
|
RuntimeOpts: rtOpts,
|
||||||
Meta: blockDef.Meta,
|
Meta: blockDef.Meta,
|
||||||
}
|
}
|
||||||
@ -299,36 +297,21 @@ func DeleteTab(ctx context.Context, workspaceId string, tabId string) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateMeta(ctx context.Context, oref waveobj.ORef, meta map[string]any) error {
|
func UpdateObjectMeta(ctx context.Context, oref waveobj.ORef, meta MetaMapType) error {
|
||||||
return WithTx(ctx, func(tx *TxWrap) error {
|
return WithTx(ctx, func(tx *TxWrap) error {
|
||||||
obj, _ := DBGetORef(tx.Context(), oref)
|
if oref.IsEmpty() {
|
||||||
if obj == nil {
|
return fmt.Errorf("empty object reference")
|
||||||
return fmt.Errorf("object not found: %q", oref)
|
|
||||||
}
|
}
|
||||||
// obj.SetMeta(meta)
|
|
||||||
DBUpdate(tx.Context(), obj)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateObjectMeta(ctx context.Context, oref waveobj.ORef, meta map[string]any) error {
|
|
||||||
return WithTx(ctx, func(tx *TxWrap) error {
|
|
||||||
obj, _ := DBGetORef(tx.Context(), oref)
|
obj, _ := DBGetORef(tx.Context(), oref)
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return fmt.Errorf("object not found: %q", oref)
|
return ErrNotFound
|
||||||
}
|
}
|
||||||
objMeta := waveobj.GetMeta(obj)
|
objMeta := waveobj.GetMeta(obj)
|
||||||
if objMeta == nil {
|
if objMeta == nil {
|
||||||
objMeta = make(map[string]any)
|
objMeta = make(map[string]any)
|
||||||
}
|
}
|
||||||
for k, v := range meta {
|
newMeta := MergeMeta(objMeta, meta)
|
||||||
if v == nil {
|
waveobj.SetMeta(obj, newMeta)
|
||||||
delete(objMeta, k)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
objMeta[k] = v
|
|
||||||
}
|
|
||||||
waveobj.SetMeta(obj, objMeta)
|
|
||||||
DBUpdate(tx.Context(), obj)
|
DBUpdate(tx.Context(), obj)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@ -441,19 +424,6 @@ func EnsureInitialData() error {
|
|||||||
return fmt.Errorf("error creating client: %w", err)
|
return fmt.Errorf("error creating client: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if client.MainWindowId != "" {
|
|
||||||
// convert to windowIds
|
|
||||||
client.WindowIds = []string{client.MainWindowId}
|
|
||||||
client.MainWindowId = ""
|
|
||||||
err = DBUpdate(ctx, client)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error updating client: %w", err)
|
|
||||||
}
|
|
||||||
client, err = DBGetSingleton[*Client](ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("error getting client (after main window update): %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(client.WindowIds) > 0 {
|
if len(client.WindowIds) > 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
153
pkg/wstore/wstore_meta.go
Normal file
153
pkg/wstore/wstore_meta.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
// Copyright 2024, Command Line Inc.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package wstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Entity_Any = "any"
|
||||||
|
|
||||||
|
type MetaMapType = waveobj.MetaMapType
|
||||||
|
|
||||||
|
// well known meta keys
|
||||||
|
// to add a new key, add it here and add it to MetaTSType (make sure the keys match)
|
||||||
|
// TODO: will code generate one side of this so we don't need to add the keys in two places
|
||||||
|
// will probably drive this off the meta decls so we can add more information and validate the keys/values
|
||||||
|
const (
|
||||||
|
MetaKey_View = "view"
|
||||||
|
MetaKey_Controller = "controller"
|
||||||
|
MetaKey_Title = "title"
|
||||||
|
MetaKey_File = "file"
|
||||||
|
MetaKey_Url = "url"
|
||||||
|
MetaKey_Connection = "connection"
|
||||||
|
|
||||||
|
MetaKey_Icon = "icon"
|
||||||
|
MetaKey_IconColor = "icon:color"
|
||||||
|
|
||||||
|
MetaKey_Frame = "frame"
|
||||||
|
MetaKey_FrameBorderColor = "frame:bordercolor"
|
||||||
|
MetaKey_FrameBorderColor_Focused = "frame:bordercolor:focused"
|
||||||
|
|
||||||
|
MetaKey_Cmd = "cmd"
|
||||||
|
MetaKey_CmdInteractive = "cmd:interactive"
|
||||||
|
MetaKey_CmdLogin = "cmd:login"
|
||||||
|
MetaKey_CmdRunOnStart = "cmd:runonstart"
|
||||||
|
MetaKey_CmdClearOnStart = "cmd:clearonstart"
|
||||||
|
MetaKey_CmdClearOnRestart = "cmd:clearonrestart"
|
||||||
|
MetaKey_CmdEnv = "cmd:env"
|
||||||
|
MetaKey_CmdCwd = "cmd:cwd"
|
||||||
|
MetaKey_CmdNoWsh = "cmd:nowsh"
|
||||||
|
|
||||||
|
MetaKey_Bg = "bg"
|
||||||
|
MetaKey_BgOpacity = "bg:opacity"
|
||||||
|
MetaKey_BgBlendMode = "bg:blendmode"
|
||||||
|
|
||||||
|
MetaKey_TermFontSize = "term:fontsize"
|
||||||
|
MetaKey_TermFontFamily = "term:fontfamily"
|
||||||
|
MetaKey_TermMode = "term:mode"
|
||||||
|
MetaKey_TermTheme = "term:theme"
|
||||||
|
)
|
||||||
|
|
||||||
|
// for typescript typing
|
||||||
|
type MetaTSType struct {
|
||||||
|
// shared
|
||||||
|
View string `json:"view,omitempty"`
|
||||||
|
Controller string `json:"controller,omitempty"`
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
File string `json:"file,omitempty"`
|
||||||
|
Url string `json:"url,omitempty"`
|
||||||
|
Connection string `json:"connection,omitempty"`
|
||||||
|
|
||||||
|
Icon string `json:"icon,omitempty"`
|
||||||
|
IconColor string `json:"icon:color,omitempty"`
|
||||||
|
|
||||||
|
Frame bool `json:"frame,omitempty"`
|
||||||
|
FrameClear bool `json:"frame:*,omitempty"`
|
||||||
|
FrameBorderColor string `json:"frame:bordercolor,omitempty"`
|
||||||
|
FrameBorderColor_Focused string `json:"frame:bordercolor:focused,omitempty"`
|
||||||
|
|
||||||
|
Cmd string `json:"cmd,omitempty"`
|
||||||
|
CmdClear bool `json:"cmd:*,omitempty"`
|
||||||
|
CmdInteractive bool `json:"cmd:interactive,omitempty"`
|
||||||
|
CmdLogin bool `json:"cmd:login,omitempty"`
|
||||||
|
CmdRunOnStart bool `json:"cmd:runonstart,omitempty"`
|
||||||
|
CmdClearOnStart bool `json:"cmd:clearonstart,omitempty"`
|
||||||
|
CmdClearOnRestart bool `json:"cmd:clearonrestart,omitempty"`
|
||||||
|
CmdEnv map[string]string `json:"cmd:env,omitempty"`
|
||||||
|
CmdCwd string `json:"cmd:cwd,omitempty"`
|
||||||
|
CmdNoWsh bool `json:"cmd:nowsh,omitempty"`
|
||||||
|
|
||||||
|
// for tabs
|
||||||
|
Bg string `json:"bg,omitempty"`
|
||||||
|
BgClear bool `json:"bg:*,omitempty"`
|
||||||
|
BgOpacity float64 `json:"bg:opacity,omitempty"`
|
||||||
|
BgBlendMode string `json:"bg:blendmode,omitempty"`
|
||||||
|
|
||||||
|
TermClear bool `json:"term:*,omitempty"`
|
||||||
|
TermFontSize int `json:"term:fontsize,omitempty"`
|
||||||
|
TermFontFamily string `json:"term:fontfamily,omitempty"`
|
||||||
|
TermMode string `json:"term:mode,omitempty"`
|
||||||
|
TermTheme string `json:"term:theme,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetaDataDecl struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Desc string `json:"desc,omitempty"`
|
||||||
|
Type string `json:"type"` // string, int, float, bool, array, object
|
||||||
|
Default any `json:"default,omitempty"`
|
||||||
|
StrOptions []string `json:"stroptions,omitempty"`
|
||||||
|
NumRange []*int `json:"numrange,omitempty"` // inclusive, null means no limit
|
||||||
|
Entity []string `json:"entity"` // what entities this applies to, e.g. "block", "tab", "any", etc.
|
||||||
|
Special []string `json:"special,omitempty"` // special handling. things that need to happen if this gets updated
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetaPresetDecl struct {
|
||||||
|
Preset string `json:"preset"`
|
||||||
|
Desc string `json:"desc,omitempty"`
|
||||||
|
Keys []string `json:"keys"`
|
||||||
|
Entity []string `json:"entity"` // what entities this applies to, e.g. "block", "tab", etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a clean copy of meta with mergeMeta merged in
|
||||||
|
func MergeMeta(meta MetaMapType, metaUpdate MetaMapType) MetaMapType {
|
||||||
|
rtn := make(MetaMapType)
|
||||||
|
for k, v := range meta {
|
||||||
|
rtn[k] = v
|
||||||
|
}
|
||||||
|
// deal with "section:*" keys
|
||||||
|
for k := range metaUpdate {
|
||||||
|
if !strings.HasSuffix(k, ":*") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !metaUpdate.GetBool(k, false) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
prefix := strings.TrimSuffix(k, ":*")
|
||||||
|
if prefix == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// delete "[prefix]" and all keys that start with "[prefix]:"
|
||||||
|
prefixColon := prefix + ":"
|
||||||
|
for k2 := range rtn {
|
||||||
|
if k2 == prefix || strings.HasPrefix(k2, prefixColon) {
|
||||||
|
delete(rtn, k2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now deal with regular keys
|
||||||
|
for k, v := range metaUpdate {
|
||||||
|
if strings.HasSuffix(k, ":*") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if v == nil {
|
||||||
|
delete(rtn, k)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rtn[k] = v
|
||||||
|
}
|
||||||
|
return rtn
|
||||||
|
}
|
@ -12,54 +12,6 @@ import (
|
|||||||
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
"github.com/wavetermdev/thenextwave/pkg/waveobj"
|
||||||
)
|
)
|
||||||
|
|
||||||
// well known meta keys
|
|
||||||
const (
|
|
||||||
MetaKey_Title = "title"
|
|
||||||
MetaKey_File = "file"
|
|
||||||
MetaKey_Url = "url"
|
|
||||||
MetaKey_Icon = "icon"
|
|
||||||
MetaKey_IconColor = "icon:color"
|
|
||||||
MetaKey_Frame = "frame"
|
|
||||||
MetaKey_FrameBorderColor = "frame:bordercolor"
|
|
||||||
MetaKey_FrameBorderColor_Focused = "frame:bordercolor:focused"
|
|
||||||
MetaKey_Cmd = "cmd"
|
|
||||||
MetaKey_CmdInteractive = "cmd:interactive"
|
|
||||||
MetaKey_CmdLogin = "cmd:login"
|
|
||||||
MetaKey_CmdRunOnStart = "cmd:runonstart"
|
|
||||||
MetaKey_CmdClearOnStart = "cmd:clearonstart"
|
|
||||||
MetaKey_CmdClearOnRestart = "cmd:clearonrestart"
|
|
||||||
MetaKey_CmdEnv = "cmd:env"
|
|
||||||
MetaKey_CmdCwd = "cmd:cwd"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MetaType struct {
|
|
||||||
View string `json:"view,omitempty"`
|
|
||||||
Controller string `json:"controller,omitempty"`
|
|
||||||
Title string `json:"title,omitempty"`
|
|
||||||
File string `json:"file,omitempty"`
|
|
||||||
Url string `json:"url,omitempty"`
|
|
||||||
|
|
||||||
Icon string `json:"icon,omitempty"`
|
|
||||||
IconColor string `json:"icon:color,omitempty"`
|
|
||||||
|
|
||||||
Frame bool `json:"frame,omitempty"`
|
|
||||||
FrameBorderColor string `json:"frame:bordercolor,omitempty"`
|
|
||||||
FrameBorderColor_Focused string `json:"frame:bordercolor:focused,omitempty"`
|
|
||||||
|
|
||||||
Cmd string `json:"cmd,omitempty"`
|
|
||||||
CmdInteractive bool `json:"cmd:interactive,omitempty"`
|
|
||||||
CmdLogin bool `json:"cmd:login,omitempty"`
|
|
||||||
CmdRunOnStart bool `json:"cmd:runonstart,omitempty"`
|
|
||||||
CmdClearOnStart bool `json:"cmd:clearonstart,omitempty"`
|
|
||||||
CmdClearOnRestart bool `json:"cmd:clearonrestart,omitempty"`
|
|
||||||
CmdEnv map[string]string `json:"cmd:env,omitempty"`
|
|
||||||
CmdCwd string `json:"cmd:cwd,omitempty"`
|
|
||||||
|
|
||||||
Bg string `json:"bg,omitempty"`
|
|
||||||
BgOpacity float64 `json:"bg:opacity,omitempty"`
|
|
||||||
BgBlendMode string `json:"bg:blendmode,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UIContext struct {
|
type UIContext struct {
|
||||||
WindowId string `json:"windowid"`
|
WindowId string `json:"windowid"`
|
||||||
ActiveTabId string `json:"activetabid"`
|
ActiveTabId string `json:"activetabid"`
|
||||||
@ -161,12 +113,11 @@ func (update *WaveObjUpdate) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
OID string `json:"oid"`
|
OID string `json:"oid"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
MainWindowId string `json:"mainwindowid"` // deprecated
|
WindowIds []string `json:"windowids"`
|
||||||
WindowIds []string `json:"windowids"`
|
Meta MetaMapType `json:"meta"`
|
||||||
Meta map[string]any `json:"meta"`
|
TosAgreed int64 `json:"tosagreed,omitempty"`
|
||||||
TosAgreed int64 `json:"tosagreed,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Client) GetOType() string {
|
func (*Client) GetOType() string {
|
||||||
@ -185,7 +136,7 @@ type Window struct {
|
|||||||
Pos Point `json:"pos"`
|
Pos Point `json:"pos"`
|
||||||
WinSize WinSize `json:"winsize"`
|
WinSize WinSize `json:"winsize"`
|
||||||
LastFocusTs int64 `json:"lastfocusts"`
|
LastFocusTs int64 `json:"lastfocusts"`
|
||||||
Meta map[string]any `json:"meta"`
|
Meta MetaMapType `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Window) GetOType() string {
|
func (*Window) GetOType() string {
|
||||||
@ -193,11 +144,11 @@ func (*Window) GetOType() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Workspace struct {
|
type Workspace struct {
|
||||||
OID string `json:"oid"`
|
OID string `json:"oid"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
TabIds []string `json:"tabids"`
|
TabIds []string `json:"tabids"`
|
||||||
Meta map[string]any `json:"meta"`
|
Meta MetaMapType `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Workspace) GetOType() string {
|
func (*Workspace) GetOType() string {
|
||||||
@ -205,12 +156,12 @@ func (*Workspace) GetOType() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Tab struct {
|
type Tab struct {
|
||||||
OID string `json:"oid"`
|
OID string `json:"oid"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
LayoutNode string `json:"layoutnode"`
|
LayoutNode string `json:"layoutnode"`
|
||||||
BlockIds []string `json:"blockids"`
|
BlockIds []string `json:"blockids"`
|
||||||
Meta map[string]any `json:"meta"`
|
Meta MetaMapType `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Tab) GetOType() string {
|
func (*Tab) GetOType() string {
|
||||||
@ -226,11 +177,11 @@ func (t *Tab) GetBlockORefs() []waveobj.ORef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type LayoutNode struct {
|
type LayoutNode struct {
|
||||||
OID string `json:"oid"`
|
OID string `json:"oid"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
Node any `json:"node,omitempty"`
|
Node any `json:"node,omitempty"`
|
||||||
MagnifiedNodeId string `json:"magnifiednodeid,omitempty"`
|
MagnifiedNodeId string `json:"magnifiednodeid,omitempty"`
|
||||||
Meta map[string]any `json:"meta,omitempty"`
|
Meta MetaMapType `json:"meta,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*LayoutNode) GetOType() string {
|
func (*LayoutNode) GetOType() string {
|
||||||
@ -246,10 +197,8 @@ type FileDef struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type BlockDef struct {
|
type BlockDef struct {
|
||||||
Controller string `json:"controller,omitempty"`
|
Files map[string]*FileDef `json:"files,omitempty"`
|
||||||
View string `json:"view,omitempty"`
|
Meta MetaMapType `json:"meta,omitempty"`
|
||||||
Files map[string]*FileDef `json:"files,omitempty"`
|
|
||||||
Meta map[string]any `json:"meta,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type StickerClickOptsType struct {
|
type StickerClickOptsType struct {
|
||||||
@ -289,11 +238,9 @@ type Block struct {
|
|||||||
OID string `json:"oid"`
|
OID string `json:"oid"`
|
||||||
Version int `json:"version"`
|
Version int `json:"version"`
|
||||||
BlockDef *BlockDef `json:"blockdef"`
|
BlockDef *BlockDef `json:"blockdef"`
|
||||||
Controller string `json:"controller"`
|
|
||||||
View string `json:"view"`
|
|
||||||
RuntimeOpts *RuntimeOpts `json:"runtimeopts,omitempty"`
|
RuntimeOpts *RuntimeOpts `json:"runtimeopts,omitempty"`
|
||||||
Stickers []*StickerType `json:"stickers,omitempty"`
|
Stickers []*StickerType `json:"stickers,omitempty"`
|
||||||
Meta map[string]any `json:"meta"`
|
Meta MetaMapType `json:"meta"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Block) GetOType() string {
|
func (*Block) GetOType() string {
|
||||||
|
Loading…
Reference in New Issue
Block a user