1
0
This commit is contained in:
Zef Hemel 2024-01-25 11:42:36 +01:00
parent 232a0d8df8
commit 8404256ccb
22 changed files with 188 additions and 91 deletions

View File

@ -25,5 +25,8 @@ jobs:
- name: Run build - name: Run build
run: deno task build run: deno task build
- name: Run type check
run: deno task check
- name: Run tests - name: Run tests
run: deno task test --trace-ops run: deno task test --trace-ops

24
common/command.test.ts Normal file
View File

@ -0,0 +1,24 @@
import { assertEquals } from "../test_deps.ts";
import { parseCommand } from "./command.ts";
Deno.test("Command parser", () => {
assertEquals(parseCommand("Hello world"), { name: "Hello world", args: [] });
assertEquals(parseCommand("{[Hello world]}"), {
name: "Hello world",
args: [],
});
assertEquals(parseCommand("{[Hello world|sup]}"), {
name: "Hello world",
alias: "sup",
args: [],
});
assertEquals(parseCommand("{[Hello world](1, 2, 3)}"), {
name: "Hello world",
args: [1, 2, 3],
});
assertEquals(parseCommand("{[Hello world|sup](1, 2, 3)}"), {
name: "Hello world",
alias: "sup",
args: [1, 2, 3],
});
});

23
common/command.ts Normal file
View File

@ -0,0 +1,23 @@
export const commandLinkRegex =
/^\{\[([^\]\|]+)(\|([^\]]+))?\](\(([^\)]+)\))?\}/;
export type ParsedCommand = {
name: string;
args: any[];
alias?: string;
};
export function parseCommand(command: string): ParsedCommand {
const parsedCommand: ParsedCommand = { name: command, args: [] };
const commandMatch = commandLinkRegex.exec(command);
if (commandMatch) {
parsedCommand.name = commandMatch[1];
if (commandMatch[3]) {
parsedCommand.alias = commandMatch[3];
}
parsedCommand.args = commandMatch[5]
? JSON.parse(`[${commandMatch[5]}]`)
: [];
}
return parsedCommand;
}

View File

@ -1,3 +1,4 @@
import { commandLinkRegex } from "../command.ts";
import { import {
BlockContext, BlockContext,
LeafBlock, LeafBlock,
@ -13,7 +14,6 @@ import {
yamlLanguage, yamlLanguage,
} from "../deps.ts"; } from "../deps.ts";
import * as ct from "./customtags.ts"; import * as ct from "./customtags.ts";
import { HashtagTag, TaskDeadlineTag } from "./customtags.ts";
import { NakedURLTag } from "./customtags.ts"; import { NakedURLTag } from "./customtags.ts";
import { TaskList } from "./extended_task.ts"; import { TaskList } from "./extended_task.ts";
@ -65,9 +65,6 @@ const WikiLink: MarkdownConfig = {
], ],
}; };
export const commandLinkRegex =
/^\{\[([^\]\|]+)(\|([^\]]+))?\](\(([^\)]+)\))?\}/;
const CommandLink: MarkdownConfig = { const CommandLink: MarkdownConfig = {
defineNodes: [ defineNodes: [
{ name: "CommandLink", style: { "CommandLink/...": ct.CommandLinkTag } }, { name: "CommandLink", style: { "CommandLink/...": ct.CommandLinkTag } },

View File

@ -48,13 +48,35 @@ export function parseYamlSettings(settingsMarkdown: string): {
} }
} }
export const defaultSettings: BuiltinSettings = {
indexPage: "index",
hideSyncButton: false,
actionButtons: [
{
icon: "Home",
description: "Go to the index page",
command: "Navigate: Home",
},
{
icon: "Book",
description: `Open page`,
command: "Navigate: Page Picker",
},
{
icon: "Terminal",
description: `Run command`,
command: "Open Command Palette",
},
],
};
/** /**
* Ensures that the settings and index page exist in the given space. * Ensures that the settings and index page exist in the given space.
* If they don't exist, default settings and index page will be created. * If they don't exist, default settings and index page will be created.
* @param space - The SpacePrimitives object representing the space. * @param space - The SpacePrimitives object representing the space.
* @returns A promise that resolves to the built-in settings. * @returns A promise that resolves to the built-in settings.
*/ */
export async function ensureSettingsAndIndex( export async function ensureAndLoadSettingsAndIndex(
space: SpacePrimitives, space: SpacePrimitives,
): Promise<BuiltinSettings> { ): Promise<BuiltinSettings> {
let settingsText: string | undefined; let settingsText: string | undefined;
@ -73,9 +95,7 @@ export async function ensureSettingsAndIndex(
} else { } else {
console.error("Error reading settings", e.message); console.error("Error reading settings", e.message);
console.warn("Falling back to default settings"); console.warn("Falling back to default settings");
return { return defaultSettings;
indexPage: "index",
};
} }
settingsText = SETTINGS_TEMPLATE; settingsText = SETTINGS_TEMPLATE;
// Ok, then let's also check the index page // Ok, then let's also check the index page
@ -95,5 +115,5 @@ export async function ensureSettingsAndIndex(
const settings: any = parseYamlSettings(settingsText); const settings: any = parseYamlSettings(settingsText);
expandPropertyNames(settings); expandPropertyNames(settings);
return settings; return { ...defaultSettings, ...settings };
} }

View File

@ -4,7 +4,7 @@
"deep-clean-mac": "rm -f deno.lock && rm -rf $HOME/Library/Caches/deno && deno task clean", "deep-clean-mac": "rm -f deno.lock && rm -rf $HOME/Library/Caches/deno && deno task clean",
"install": "deno install -f --unstable -A --importmap import_map.json silverbullet.ts", "install": "deno install -f --unstable -A --importmap import_map.json silverbullet.ts",
"check": "find . -name '*.ts*' | xargs deno check", "check": "find . -name '*.ts*' | xargs deno check",
"test": "deno task check && deno test -A --unstable", "test": "deno test -A --unstable",
"build": "deno run -A build_plugs.ts && deno run -A --unstable build_web.ts", "build": "deno run -A build_plugs.ts && deno run -A --unstable build_web.ts",
"plugs": "deno run -A build_plugs.ts", "plugs": "deno run -A build_plugs.ts",
"server": "deno run -A --unstable --check silverbullet.ts", "server": "deno run -A --unstable --check silverbullet.ts",

View File

@ -59,7 +59,7 @@ functions:
linkNavigate: linkNavigate:
path: "./navigate.ts:linkNavigate" path: "./navigate.ts:linkNavigate"
command: command:
name: Navigate To page name: "Navigate: To This Page"
key: Ctrl-Enter key: Ctrl-Enter
mac: Cmd-Enter mac: Cmd-Enter
clickNavigate: clickNavigate:
@ -76,6 +76,11 @@ functions:
path: "./editor.ts:moveToPosCommand" path: "./editor.ts:moveToPosCommand"
command: command:
name: "Navigate: Move Cursor to Position" name: "Navigate: Move Cursor to Position"
navigateToPage:
path: "./navigate.ts:navigateToPage"
command:
name: "Navigate: To Page"
hide: true
# Text editing commands # Text editing commands
quoteSelectionCommand: quoteSelectionCommand:

View File

@ -132,3 +132,7 @@ export async function clickNavigate(event: ClickEvent) {
export async function navigateCommand(cmdDef: any) { export async function navigateCommand(cmdDef: any) {
await editor.navigate({ page: cmdDef.page, pos: 0 }); await editor.navigate({ page: cmdDef.page, pos: 0 });
} }
export async function navigateToPage(_cmdDef: any, pageName: string) {
await editor.navigate({ page: pageName, pos: 0 });
}

View File

@ -2,7 +2,7 @@ import { SilverBulletHooks } from "../common/manifest.ts";
import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_space_primitives.ts"; import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_space_primitives.ts";
import { FilteredSpacePrimitives } from "../common/spaces/filtered_space_primitives.ts"; import { FilteredSpacePrimitives } from "../common/spaces/filtered_space_primitives.ts";
import { SpacePrimitives } from "../common/spaces/space_primitives.ts"; import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
import { ensureSettingsAndIndex } from "../common/util.ts"; import { ensureAndLoadSettingsAndIndex } from "../common/util.ts";
import { AssetBundle } from "../plugos/asset_bundle/bundle.ts"; import { AssetBundle } from "../plugos/asset_bundle/bundle.ts";
import { KvPrimitives } from "../plugos/lib/kv_primitives.ts"; import { KvPrimitives } from "../plugos/lib/kv_primitives.ts";
import { System } from "../plugos/system.ts"; import { System } from "../plugos/system.ts";
@ -113,10 +113,11 @@ export class SpaceServer {
async reloadSettings() { async reloadSettings() {
if (!this.clientEncryption) { if (!this.clientEncryption) {
// Only attempt this when the space is not encrypted // Only attempt this when the space is not encrypted
this.settings = await ensureSettingsAndIndex(this.spacePrimitives); this.settings = await ensureAndLoadSettingsAndIndex(this.spacePrimitives);
} else { } else {
this.settings = { this.settings = {
indexPage: "index", indexPage: "index",
actionButtons: [],
}; };
} }
} }

View File

@ -10,7 +10,7 @@ import {
} from "../common/deps.ts"; } from "../common/deps.ts";
import { Space } from "./space.ts"; import { Space } from "./space.ts";
import { FilterOption } from "./types.ts"; import { FilterOption } from "./types.ts";
import { ensureSettingsAndIndex } from "../common/util.ts"; import { ensureAndLoadSettingsAndIndex } from "../common/util.ts";
import { EventHook } from "../plugos/hooks/event.ts"; import { EventHook } from "../plugos/hooks/event.ts";
import { AppCommand } from "./hooks/command.ts"; import { AppCommand } from "./hooks/command.ts";
import { import {
@ -242,7 +242,13 @@ export class Client {
} }
async loadSettings() { async loadSettings() {
this.settings = await ensureSettingsAndIndex(this.space.spacePrimitives); this.settings = await ensureAndLoadSettingsAndIndex(
this.space.spacePrimitives,
);
this.ui.viewDispatch({
type: "settings-loaded",
settings: this.settings,
});
} }
private async initSync() { private async initSync() {

View File

@ -1,4 +1,3 @@
import { commandLinkRegex } from "../../common/markdown_parser/parser.ts";
import { ClickEvent } from "$sb/app_event.ts"; import { ClickEvent } from "$sb/app_event.ts";
import { Decoration, syntaxTree } from "../deps.ts"; import { Decoration, syntaxTree } from "../deps.ts";
import { Client } from "../client.ts"; import { Client } from "../client.ts";
@ -8,6 +7,7 @@ import {
invisibleDecoration, invisibleDecoration,
isCursorInRange, isCursorInRange,
} from "./util.ts"; } from "./util.ts";
import { commandLinkRegex } from "../../common/command.ts";
/** /**
* Plugin to hide path prefix when the cursor is not inside. * Plugin to hide path prefix when the cursor is not inside.

View File

@ -1,9 +1,9 @@
import { isMacLike } from "../../common/util.ts"; import { isMacLike } from "../../common/util.ts";
import { FilterList } from "./filter.tsx"; import { FilterList } from "./filter.tsx";
import { CompletionContext, CompletionResult, TerminalIcon } from "../deps.ts"; import { CompletionContext, CompletionResult, featherIcons } from "../deps.ts";
import { AppCommand } from "../hooks/command.ts"; import { AppCommand } from "../hooks/command.ts";
import { BuiltinSettings, FilterOption } from "../types.ts"; import { BuiltinSettings, FilterOption } from "../types.ts";
import { commandLinkRegex } from "../../common/markdown_parser/parser.ts"; import { parseCommand } from "../../common/command.ts";
export function CommandPalette({ export function CommandPalette({
commands, commands,
@ -25,6 +25,9 @@ export function CommandPalette({
const options: FilterOption[] = []; const options: FilterOption[] = [];
const isMac = isMacLike(); const isMac = isMacLike();
for (const [name, def] of commands.entries()) { for (const [name, def] of commands.entries()) {
if (def.command.hide) {
continue;
}
let shortcut: { key?: string; mac?: string; priority?: number } = let shortcut: { key?: string; mac?: string; priority?: number } =
def.command; def.command;
// Let's see if there's a shortcut override // Let's see if there's a shortcut override
@ -32,11 +35,9 @@ export function CommandPalette({
const commandOverride = settings.shortcuts.find(( const commandOverride = settings.shortcuts.find((
shortcut, shortcut,
) => { ) => {
const commandMatch = commandLinkRegex.exec(shortcut.command); const parsedCommand = parseCommand(shortcut.command);
// If this is a command link, we want to match the command name but also make sure no arguments were set // If this is a command link, we want to match the command name but also make sure no arguments were set
return commandMatch && commandMatch[1] === name && !commandMatch[5] || return parsedCommand.name === name && parsedCommand.args.length === 0;
// or if it's not a command link, let's match exactly
shortcut.command === name;
}); });
if (commandOverride) { if (commandOverride) {
shortcut = commandOverride; shortcut = commandOverride;
@ -58,7 +59,7 @@ export function CommandPalette({
placeholder="Command" placeholder="Command"
options={options} options={options}
allowNew={false} allowNew={false}
icon={TerminalIcon} icon={featherIcons.Terminal}
completer={completer} completer={completer}
vimMode={vimMode} vimMode={vimMode}
darkMode={darkMode} darkMode={darkMode}

View File

@ -1,9 +1,4 @@
import { import { CompletionContext, CompletionResult, useEffect } from "../deps.ts";
CompletionContext,
CompletionResult,
useEffect,
useRef,
} from "../deps.ts";
import type { ComponentChildren, FunctionalComponent } from "../deps.ts"; import type { ComponentChildren, FunctionalComponent } from "../deps.ts";
import { Notification } from "../types.ts"; import { Notification } from "../types.ts";
import { FeatherProps } from "https://esm.sh/v99/preact-feather@4.2.1/dist/types"; import { FeatherProps } from "https://esm.sh/v99/preact-feather@4.2.1/dist/types";
@ -65,8 +60,9 @@ export function TopBar({
const innerDiv = currentPageElement.parentElement!.parentElement!; const innerDiv = currentPageElement.parentElement!.parentElement!;
// Then calculate a new width // Then calculate a new width
const substract = 60 + actionButtons.length * 31;
currentPageElement.style.width = `${ currentPageElement.style.width = `${
Math.min(editorWidth - 170, innerDiv.clientWidth - 170) Math.min(editorWidth - substract, innerDiv.clientWidth - substract)
}px`; }px`;
} }
} }

View File

@ -9,13 +9,7 @@ export {
useState, useState,
} from "https://esm.sh/preact@10.11.1/hooks"; } from "https://esm.sh/preact@10.11.1/hooks";
export { export * as featherIcons from "https://esm.sh/preact-feather@4.2.1?external=preact";
Book as BookIcon,
Home as HomeIcon,
RefreshCw as RefreshCwIcon,
Terminal as TerminalIcon,
Type as TemplateIcon,
} from "https://esm.sh/preact-feather@4.2.1?external=preact";
// Vim mode // Vim mode
export { export {

View File

@ -1,4 +1,3 @@
import { commandLinkRegex } from "../common/markdown_parser/parser.ts";
import { readonlyMode } from "./cm_plugins/readonly.ts"; import { readonlyMode } from "./cm_plugins/readonly.ts";
import customMarkdownStyle from "./style.ts"; import customMarkdownStyle from "./style.ts";
import { import {
@ -45,6 +44,7 @@ import { languageFor } from "../common/languages.ts";
import { plugLinter } from "./cm_plugins/lint.ts"; import { plugLinter } from "./cm_plugins/lint.ts";
import { Compartment, Extension } from "@codemirror/state"; import { Compartment, Extension } from "@codemirror/state";
import { extendedMarkdownLanguage } from "../common/markdown_parser/parser.ts"; import { extendedMarkdownLanguage } from "../common/markdown_parser/parser.ts";
import { parseCommand } from "../common/command.ts";
export function createEditorState( export function createEditorState(
client: Client, client: Client,
@ -270,28 +270,24 @@ export function createCommandKeyBindings(client: Client): KeyBinding[] {
if (client.settings?.shortcuts) { if (client.settings?.shortcuts) {
for (const shortcut of client.settings.shortcuts) { for (const shortcut of client.settings.shortcuts) {
// Figure out if we're using the command link syntax here, if so: parse it out // Figure out if we're using the command link syntax here, if so: parse it out
const commandMatch = commandLinkRegex.exec(shortcut.command); const parsedCommand = parseCommand(shortcut.command);
let cleanCommandName = shortcut.command; if (parsedCommand.args.length === 0) {
let args: any[] = [];
if (commandMatch) {
cleanCommandName = commandMatch[1];
args = commandMatch[5] ? JSON.parse(`[${commandMatch[5]}]`) : [];
}
if (args.length === 0) {
// If there was no "specialization" of this command (that is, we effectively created a keybinding for an existing command but with arguments), let's add it to the overridden command set: // If there was no "specialization" of this command (that is, we effectively created a keybinding for an existing command but with arguments), let's add it to the overridden command set:
overriddenCommands.add(cleanCommandName); overriddenCommands.add(parsedCommand.name);
} }
commandKeyBindings.push({ commandKeyBindings.push({
key: shortcut.key, key: shortcut.key,
mac: shortcut.mac, mac: shortcut.mac,
run: (): boolean => { run: (): boolean => {
client.runCommandByName(cleanCommandName, args).catch((e: any) => { client.runCommandByName(parsedCommand.name, parsedCommand.args).catch(
console.error(e); (e: any) => {
client.flashNotification( console.error(e);
`Error running command: ${e.message}`, client.flashNotification(
"error", `Error running command: ${e.message}`,
); "error",
}).then(() => { );
},
).then(() => {
// Always be focusing the editor after running a command // Always be focusing the editor after running a command
client.focus(); client.focus();
}); });

View File

@ -1,4 +1,4 @@
import { isMacLike, safeRun } from "../common/util.ts"; import { safeRun } from "../common/util.ts";
import { Confirm, Prompt } from "./components/basic_modals.tsx"; import { Confirm, Prompt } from "./components/basic_modals.tsx";
import { CommandPalette } from "./components/command_palette.tsx"; import { CommandPalette } from "./components/command_palette.tsx";
import { FilterList } from "./components/filter.tsx"; import { FilterList } from "./components/filter.tsx";
@ -7,12 +7,9 @@ import { TopBar } from "./components/top_bar.tsx";
import reducer from "./reducer.ts"; import reducer from "./reducer.ts";
import { Action, AppViewState, initialViewState } from "./types.ts"; import { Action, AppViewState, initialViewState } from "./types.ts";
import { import {
BookIcon, featherIcons,
HomeIcon,
preactRender, preactRender,
RefreshCwIcon,
runScopeHandlers, runScopeHandlers,
TerminalIcon,
useEffect, useEffect,
useReducer, useReducer,
} from "./deps.ts"; } from "./deps.ts";
@ -20,6 +17,7 @@ import type { Client } from "./client.ts";
import { Panel } from "./components/panel.tsx"; import { Panel } from "./components/panel.tsx";
import { h } from "./deps.ts"; import { h } from "./deps.ts";
import { sleep } from "$sb/lib/async.ts"; import { sleep } from "$sb/lib/async.ts";
import { parseCommand } from "../common/command.ts";
export class MainUI { export class MainUI {
viewState: AppViewState = initialViewState; viewState: AppViewState = initialViewState;
@ -210,10 +208,11 @@ export class MainUI {
client.focus(); client.focus();
}} }}
actionButtons={[ actionButtons={[
...!window.silverBulletConfig.syncOnly ...(!window.silverBulletConfig.syncOnly &&
!viewState.settings.hideSyncButton)
// If we support syncOnly, don't show this toggle button // If we support syncOnly, don't show this toggle button
? [{ ? [{
icon: RefreshCwIcon, icon: featherIcons.RefreshCw,
description: this.client.syncMode description: this.client.syncMode
? "Currently in Sync mode, click to switch to Online mode" ? "Currently in Sync mode, click to switch to Online mode"
: "Currently in Online mode, click to switch to Sync mode", : "Currently in Online mode, click to switch to Sync mode",
@ -241,33 +240,20 @@ export class MainUI {
}, },
}] }]
: [], : [],
{ ...viewState.settings.actionButtons.map((button) => {
icon: HomeIcon, const parsedCommand = parseCommand(button.command);
description: `Go to the index page (Alt-h)`, return {
callback: () => { icon: (featherIcons as any)[button.icon],
client.navigate({ page: "", pos: 0 }); description: button.description || "",
// And let's make sure all panels are closed callback: () => {
dispatch({ type: "hide-filterbox" }); client.runCommandByName(
}, parsedCommand.name,
href: "", parsedCommand.args,
}, );
{ },
icon: BookIcon, href: "",
description: `Open page (${isMacLike() ? "Cmd-k" : "Ctrl-k"})`, };
callback: () => { }),
client.startPageNavigate("page");
},
},
{
icon: TerminalIcon,
description: `Run command (${isMacLike() ? "Cmd-/" : "Ctrl-/"})`,
callback: () => {
dispatch({
type: "show-palette",
context: client.getContext(),
});
},
},
]} ]}
rhs={!!viewState.panels.rhs.mode && ( rhs={!!viewState.panels.rhs.mode && (
<div <div

View File

@ -20,6 +20,8 @@ export type CommandDef = {
// Bind to keyboard shortcut // Bind to keyboard shortcut
key?: string; key?: string;
mac?: string; mac?: string;
hide?: boolean;
}; };
export type AppCommand = { export type AppCommand = {

View File

@ -45,6 +45,11 @@ export default function reducer(
...state, ...state,
syncFailures: action.syncSuccess ? 0 : state.syncFailures + 1, syncFailures: action.syncSuccess ? 0 : state.syncFailures + 1,
}; };
case "settings-loaded":
return {
...state,
settings: action.settings,
};
case "update-page-list": { case "update-page-list": {
// Let's move over any "lastOpened" times to the "allPages" list // Let's move over any "lastOpened" times to the "allPages" list
const oldPageMeta = new Map( const oldPageMeta = new Map(

View File

@ -1,6 +1,7 @@
import { Manifest } from "../common/manifest.ts"; import { Manifest } from "../common/manifest.ts";
import { PageMeta } from "$sb/types.ts"; import { PageMeta } from "$sb/types.ts";
import { AppCommand } from "./hooks/command.ts"; import { AppCommand } from "./hooks/command.ts";
import { defaultSettings } from "../common/util.ts";
// Used by FilterBox // Used by FilterBox
export type FilterOption = { export type FilterOption = {
@ -26,11 +27,20 @@ export type Shortcut = {
command: string; command: string;
}; };
export type ActionButton = {
icon: string;
description?: string;
command: string;
args?: any[];
};
export type BuiltinSettings = { export type BuiltinSettings = {
indexPage: string; indexPage: string;
customStyles?: string | string[]; customStyles?: string | string[];
plugOverrides?: Record<string, Partial<Manifest>>; plugOverrides?: Record<string, Partial<Manifest>>;
shortcuts?: Shortcut[]; shortcuts?: Shortcut[];
hideSyncButton?: boolean;
actionButtons: ActionButton[];
// Format: compatible with docker ignore // Format: compatible with docker ignore
spaceIgnore?: string; spaceIgnore?: string;
}; };
@ -44,6 +54,8 @@ export type PanelConfig = {
export type AppViewState = { export type AppViewState = {
currentPage?: string; currentPage?: string;
currentPageMeta?: PageMeta; currentPageMeta?: PageMeta;
allPages: PageMeta[];
isLoading: boolean; isLoading: boolean;
showPageNavigator: boolean; showPageNavigator: boolean;
showCommandPalette: boolean; showCommandPalette: boolean;
@ -52,11 +64,12 @@ export type AppViewState = {
syncFailures: number; // Reset everytime a sync succeeds syncFailures: number; // Reset everytime a sync succeeds
progressPerc?: number; progressPerc?: number;
panels: { [key: string]: PanelConfig }; panels: { [key: string]: PanelConfig };
allPages: PageMeta[];
commands: Map<string, AppCommand>; commands: Map<string, AppCommand>;
notifications: Notification[]; notifications: Notification[];
recentCommands: Map<string, Date>; recentCommands: Map<string, Date>;
settings: BuiltinSettings;
uiOptions: { uiOptions: {
vimMode: boolean; vimMode: boolean;
darkMode: boolean; darkMode: boolean;
@ -104,6 +117,7 @@ export const initialViewState: AppViewState = {
bhs: {}, bhs: {},
modal: {}, modal: {},
}, },
settings: defaultSettings,
allPages: [], allPages: [],
commands: new Map(), commands: new Map(),
recentCommands: new Map(), recentCommands: new Map(),
@ -126,6 +140,7 @@ export type Action =
| { type: "page-saved" } | { type: "page-saved" }
| { type: "sync-change"; syncSuccess: boolean } | { type: "sync-change"; syncSuccess: boolean }
| { type: "update-page-list"; allPages: PageMeta[] } | { type: "update-page-list"; allPages: PageMeta[] }
| { type: "settings-loaded"; settings: BuiltinSettings }
| { type: "start-navigate"; mode: "page" | "template" } | { type: "start-navigate"; mode: "page" | "template" }
| { type: "stop-navigate" } | { type: "stop-navigate" }
| { | {

View File

@ -7,6 +7,7 @@ release.
_The changes below are not yet released “properly”. To them out early, check out [the docs on edge](https://community.silverbullet.md/t/living-on-the-edge-builds/27)._ _The changes below are not yet released “properly”. To them out early, check out [the docs on edge](https://community.silverbullet.md/t/living-on-the-edge-builds/27)._
* Tag pages: when you click on a #tag you will now be directed to a page that shows all pages, tasks, items and paragraphs tagged with that tag. * Tag pages: when you click on a #tag you will now be directed to a page that shows all pages, tasks, items and paragraphs tagged with that tag.
* Action buttons (top right buttons) can now be configured, see [[SETTINGS]] how to do this.
* Bug fixes: * Bug fixes:
* Improved Ctrl/Cmd-click (to open links in a new window) behavior: now actually follow `@pos` and `$anchor` links. * Improved Ctrl/Cmd-click (to open links in a new window) behavior: now actually follow `@pos` and `$anchor` links.
* Right-clicking links now opens browser native context menu again * Right-clicking links now opens browser native context menu again

View File

@ -17,7 +17,7 @@ The `editor` plug implements foundational editor functionality for SilverBullet.
## Navigation ## Navigation
* {[Navigate: Home]}: navigate to the home (index) page * {[Navigate: Home]}: navigate to the home (index) page
* {[Navigate To page]}: navigate to the page under the cursor * {[Navigate: To This Page]}: navigate to the page under the cursor
* {[Navigate: Center Cursor]}: center the cursor at the center of the screen * {[Navigate: Center Cursor]}: center the cursor at the center of the screen
* {[Navigate: Move Cursor to Position]}: move cursor to a specific (numeric) cursor position (# of characters from the start of the document) * {[Navigate: Move Cursor to Position]}: move cursor to a specific (numeric) cursor position (# of characters from the start of the document)

View File

@ -7,7 +7,25 @@ indexPage: "[[SilverBullet]]"
# Load custom CSS styles from the following page, can also be an array # Load custom CSS styles from the following page, can also be an array
customStyles: "[[STYLES]]" customStyles: "[[STYLES]]"
# It is possible to override keyboard shortcuts and command priority # Hide the sync button
hideSyncButton: false
# Configure the shown action buttons (top right bar)
actionButtons:
- icon: Home # Capitalized version of an icon from https://feathericons.com
command: "{[Navigate: Home]}"
description: "Go to the index page"
- icon: Activity
description: "What's new"
command: '{[Navigate: To Page]("CHANGELOG")}'
- icon: Book
command: "{[Navigate: Page Picker]}"
description: Open page
- icon: Terminal
command: "{[Open Command Palette]}"
description: Run command
# Override keyboard shortcuts and command priority
shortcuts: shortcuts:
- command: "{[Stats: Show]}" # Using the command link syntax here - command: "{[Stats: Show]}" # Using the command link syntax here
mac: "Cmd-s" # Mac-specific keyboard shortcut mac: "Cmd-s" # Mac-specific keyboard shortcut