Skip to content

launcher block #1948

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 85 additions & 4 deletions docs/docs/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ id: "config"
title: "Configuration"
---

import { Kbd } from "@site/src/components/kbd.tsx";
import { PlatformProvider, PlatformSelectorButton } from "@site/src/components/platformcontext.tsx";

<PlatformProvider>

<PlatformSelectorButton />
<div style={{ marginBottom: 20 }}></div>

Wave's configuration files are located at `~/.config/waveterm/`.

The main configuration file is `settings.json` (`~/.config/waveterm/settings.json`).
Expand All @@ -27,6 +35,7 @@ wsh editconfig
| ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| app:globalhotkey | string | A systemwide keybinding to open your most recent wave window. This is a set of key names separated by `:`. For more info, see [Customizable Systemwide Global Hotkey](#customizable-systemwide-global-hotkey) |
| app:dismissarchitecturewarning | bool | Disable warnings on app start when you are using a non-native architecture for Wave. For more info, see [Why does Wave warn me about ARM64 translation when it launches?](./faq#why-does-wave-warn-me-about-arm64-translation-when-it-launches). |
| app:defaultnewblock | string | Sets the default new block (Cmd:n, Cmd:d). "term" for terminal block, "launcher" for launcher block (default = "term") |
| ai:preset | string | the default AI preset to use |
| ai:baseurl | string | Set the AI Base Url (must be OpenAI compatible) |
| ai:apitoken | string | your AI api token |
Expand Down Expand Up @@ -91,6 +100,7 @@ For reference, this is the current default configuration (v0.10.4):
"ai:model": "gpt-4o-mini",
"ai:maxtokens": 2048,
"ai:timeoutms": 60000,
"app:defaultnewblock": "term",
"autoupdate:enabled": true,
"autoupdate:installonquit": true,
"autoupdate:intervalms": 3600000,
Expand Down Expand Up @@ -121,7 +131,76 @@ files as well: `termthemes.json`, `presets.json`, and `widgets.json`.

:::

### Terminal Theming
## WebBookmarks Configuration

WebBookmarks allows you to store and manage web links with customizable display preferences. The bookmarks are stored in a JSON file (`bookmarks.json`) as a key-value map where the key (`id`) is an arbitrary identifier for the bookmark. By convention, you should start your ids with "bookmark@". In the web widget, you can pull up your bookmarks using <Kbd k="Cmd:o"/>

### Bookmark Structure

Each bookmark follows this structure (only `url` is required):

```json
{
"url": "https://example.com",
"title": "Example Site",
"iconurl": "https://example.com/custom-icon.png",
"display:order": 1
}
```

### Fields

| Field | Type | Description |
| ------------- | ------- | ----------------------------------------------------------------------------------------------------------------- |
| url | string | **Required.** The URL of the bookmark. |
| title | string | **Optional.** A display title for the bookmark. |
| icon | string | **Optional, rarely used.** Overrides the default favicon with an icon name. |
| iconcolor | string | **Optional, rarely used.** Sets a custom color for the specified icon. |
| iconurl | string | **Optional.** Provides a custom icon URL, useful if the favicon is incorrect (e.g., for dark mode compatibility). |
| display:order | float64 | **Optional.** Defines the order in which bookmarks appear. |

### Example `bookmarks.json`

```json
{
"bookmark@google": {
"url": "https://www.google.com",
"title": "Google"
},
"bookmark@claude": {
"url": "https://claude.ai",
"title": "Claude AI"
},
"bookmark@wave": {
"url": "https://waveterm.dev",
"title": "Wave Terminal",
"display:order": -1
},
"bookmark@wave-github": {
"url": "https://github.com/wavetermdev/waveterm",
"title": "Wave Github",
"iconurl": "https://github.githubassets.com/favicons/favicon-dark.png"
},
"bookmark@chatgpt": {
"url": "https://chatgpt.com",
"iconurl": "https://cdn.oaistatic.com/assets/favicon-miwirzcw.ico"
},
"bookmark@wave-pulls": {
"url": "https://github.com/wavetermdev/waveterm/pulls",
"title": "Wave Pull Requests",
"iconurl": "https://github.githubassets.com/favicons/favicon-dark.png"
}
}
```

### Behavior

- If `iconurl` is set, it fetches the icon from the specified URL instead of the site's default favicon.
- Bookmarks are sorted based on `display:order` (if provided), otherwise by id.
- `icon` and `iconcolor` are rarely needed since the default behavior fetches the site's favicon.
- favicons are refreshed every 24-hours

## Terminal Theming

User-defined terminal themes are located in `~/.config/waveterm/termthemes.json`.

Expand Down Expand Up @@ -203,17 +282,17 @@ wsh editconfig termthemes.json
| cursorAccent | CSS color | | | color for cursor |
| selectionBackground | CSS color | | | background color for selected text |

### Customizable Systemwide Global Hotkey
## Customizable Systemwide Global Hotkey

Wave allows settings a custom global hotkey to open your most recent window from anywhere in your computer. This has the name `"app:globalhotkey"` in the `settings.json` file and takes the form of a series of key names separated by the `:` character.

#### Examples
### Examples

As a practical example, suppose you want a value of `F5` as your global hotkey. Then you can simply set the value of `"app:globalhotkey"` to `"F5"` and reboot Wave to make that your global hotkey.

As a less practical example, suppose you use the combination of the keys `Ctrl`, `Option`, and `e`. Then the value for this keybinding would be `"Ctrl:Option:e"`.

#### Allowed Key Names
### Allowed Key Names

We support the following key names:

Expand Down Expand Up @@ -251,3 +330,5 @@ We support the following key names:
- The numpad minus/subtract represented by `Subtract`
- The numpad star/multiply represented by `Multiply`
- The numpad slash/divide represented by `Divide`

</PlatformProvider>
41 changes: 22 additions & 19 deletions docs/docs/keybindings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,27 @@ replace "Cmd" with "Alt" (note that "Ctrl" is "Ctrl" on both Mac, Windows, and L
<PlatformSelectorButton />
<div style={{ marginBottom: 20 }}></div>

| Key | Function |
| ---------------------------- | --------------------------------------------------------------------------------- |
| <Kbd k="Cmd:t"/> | Open a new tab |
| <Kbd k="Cmd:n"/> | Open a new terminal block (defaults to the same connection and working directory) |
| <Kbd k="Cmd:Shift:n"/> | Open a new window |
| <Kbd k="Cmd:w"/> | Close the current block |
| <Kbd k="Cmd:Shift:w"/> | Close the current tab |
| <Kbd k="Cmd:m"/> | Magnify / Un-Magnify the current block |
| <Kbd k="Cmd:g"/> | Open the "connection" switcher |
| <Kbd k="Cmd:i"/> | Refocus the current block (useful if the block has lost input focus) |
| <Kbd k="Ctrl:Shift"/> | Show block numbers |
| <Kbd k="Ctrl:Shift:1-9"/> | Switch to block number |
| <Kbd k="Ctrl:Shift:Arrows"/> | Move left, right, up, down between blocks |
| <Kbd k="Cmd:1-9"/> | Switch to tab number |
| <Kbd k="Cmd:["/> | Switch tab left |
| <Kbd k="Cmd:]"/> | Switch tab right |
| <Kbd k="Cmd:Ctrl:1-9"/> | Switch to workspace number |
| <Kbd k="Cmd:Shift:r"/> | Refresh the UI |
| <Kbd k="Ctrl:Shift:i"/> | Toggle terminal multi-input mode |
| Key | Function |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| <Kbd k="Cmd:t"/> | Open a new tab |
| <Kbd k="Cmd:n"/> | Open a new block (defaults to a terminal block with the same connection and working directory). Switch to launcher using `app:defaultnewblock` setting |
| <Kbd k="Cmd:d"/> | Split horizontally, open a new block to the right |
| <Kbd k="Cmd:Shift:d"/> | Split vertically, open a new block below |
| <Kbd k="Cmd:Shift:n"/> | Open a new window |
| <Kbd k="Cmd:w"/> | Close the current block |
| <Kbd k="Cmd:Shift:w"/> | Close the current tab |
| <Kbd k="Cmd:m"/> | Magnify / Un-Magnify the current block |
| <Kbd k="Cmd:g"/> | Open the "connection" switcher |
| <Kbd k="Cmd:i"/> | Refocus the current block (useful if the block has lost input focus) |
| <Kbd k="Ctrl:Shift"/> | Show block numbers |
| <Kbd k="Ctrl:Shift:1-9"/> | Switch to block number |
| <Kbd k="Ctrl:Shift:Arrows"/> | Move left, right, up, down between blocks |
| <Kbd k="Cmd:1-9"/> | Switch to tab number |
| <Kbd k="Cmd:["/> | Switch tab left |
| <Kbd k="Cmd:]"/> | Switch tab right |
| <Kbd k="Cmd:Ctrl:1-9"/> | Switch to workspace number |
| <Kbd k="Cmd:Shift:r"/> | Refresh the UI |
| <Kbd k="Ctrl:Shift:i"/> | Toggle terminal multi-input mode |

## File Preview Keybindings

Expand Down Expand Up @@ -66,6 +68,7 @@ replace "Cmd" with "Alt" (note that "Ctrl" is "Ctrl" on both Mac, Windows, and L
| <Kbd k="Cmd:ArrowLeft"/> | Back |
| <Kbd k="Cmd:ArrowRight"/> | Forward |
| <Kbd k="Cmd:f"/> | Find in webpage |
| <Kbd k="Cmd:o"/> | Open a bookmark |

## WaveAI Keybindings

Expand Down
2 changes: 2 additions & 0 deletions frontend/app/block/block.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
FullSubBlockProps,
SubBlockProps,
} from "@/app/block/blocktypes";
import { LauncherViewModel } from "@/app/view/launcher/launcher";
import { PreviewModel } from "@/app/view/preview/preview";
import { SysinfoViewModel } from "@/app/view/sysinfo/sysinfo";
import { VDomModel } from "@/app/view/vdom/vdom-model";
Expand Down Expand Up @@ -44,6 +45,7 @@ BlockRegistry.set("cpuplot", SysinfoViewModel);
BlockRegistry.set("sysinfo", SysinfoViewModel);
BlockRegistry.set("vdom", VDomModel);
BlockRegistry.set("help", HelpViewModel);
BlockRegistry.set("launcher", LauncherViewModel);

function makeViewModel(blockId: string, blockView: string, nodeModel: BlockNodeModel): ViewModel {
const ctor = BlockRegistry.get(blockView);
Expand Down
4 changes: 3 additions & 1 deletion frontend/app/block/blockframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,8 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
const [magnifiedBlockOpacityAtom] = React.useState(() => getSettingsKeyAtom("window:magnifiedblockopacity"));
const magnifiedBlockOpacity = jotai.useAtomValue(magnifiedBlockOpacityAtom);
const connBtnRef = React.useRef<HTMLDivElement>();
const noHeader = util.useAtomValueSafe(viewModel?.noHeader);

React.useEffect(() => {
if (!manageConnection) {
return;
Expand Down Expand Up @@ -618,7 +620,7 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => {
/>
)}
<div className="block-frame-default-inner" style={innerStyle}>
<ErrorBoundary fallback={headerElemNoView}>{headerElem}</ErrorBoundary>
{noHeader || <ErrorBoundary fallback={headerElemNoView}>{headerElem}</ErrorBoundary>}
{preview ? previewElem : children}
</div>
{preview || viewModel == null || !connModalOpen ? null : (
Expand Down
26 changes: 25 additions & 1 deletion frontend/app/store/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
newLayoutNode,
} from "@/layout/index";
import { getLayoutModelForStaticTab } from "@/layout/lib/layoutModelHooks";
import { LayoutTreeSplitHorizontalAction, LayoutTreeSplitVerticalAction } from "@/layout/lib/types";
import {
LayoutTreeReplaceNodeAction,
LayoutTreeSplitHorizontalAction,
LayoutTreeSplitVerticalAction,
} from "@/layout/lib/types";
import { getWebServerEndpoint } from "@/util/endpoints";
import { fetch } from "@/util/fetchutil";
import { deepCompareReturnPrev, getPrefixedSettings, isBlank } from "@/util/util";
Expand Down Expand Up @@ -447,6 +451,25 @@ async function createBlock(blockDef: BlockDef, magnified = false, ephemeral = fa
return blockId;
}

async function replaceBlock(blockId: string, blockDef: BlockDef): Promise<string> {
const tabId = globalStore.get(atoms.staticTabId);
const layoutModel = getLayoutModelForTabById(tabId);
const rtOpts: RuntimeOpts = { termsize: { rows: 25, cols: 80 } };
const newBlockId = await ObjectService.CreateBlock(blockDef, rtOpts);
const targetNodeId = layoutModel.getNodeByBlockId(blockId)?.id;
if (targetNodeId == null) {
throw new Error(`targetNodeId not found for blockId: ${blockId}`);
}
const replaceNodeAction: LayoutTreeReplaceNodeAction = {
type: LayoutTreeActionType.ReplaceNode,
targetNodeId: targetNodeId,
newNode: newLayoutNode(undefined, undefined, undefined, { blockId: newBlockId }),
focused: true,
};
layoutModel.treeReducer(replaceNodeAction);
return newBlockId;
}

// when file is not found, returns {data: null, fileInfo: null}
async function fetchWaveFile(
zoneId: string,
Expand Down Expand Up @@ -761,6 +784,7 @@ export {
removeFlashError,
removeNotification,
removeNotificationById,
replaceBlock,
setActiveTab,
setNodeFocus,
setPlatform,
Expand Down
Loading
Loading