Skip to content

Commit

Permalink
launcher block (#1948)
Browse files Browse the repository at this point in the history
  • Loading branch information
sawka authored Feb 12, 2025
1 parent af65c2c commit 539559c
Show file tree
Hide file tree
Showing 16 changed files with 692 additions and 68 deletions.
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

0 comments on commit 539559c

Please sign in to comment.