Implement new slash commands and enhance editor functionality

- Added slash commands for inserting today's, yesterday's, and tomorrow's dates.
- Introduced new admonition commands for notes and warnings.
- Implemented a table command for quick table insertion.
- Enhanced the `insertAtCursor` function to support a cursor placeholder option.
- Updated `getSelection` to return the selected text along with cursor positions.
- Added a new widget function to display linked tasks in the editor.
This commit is contained in:
Zef Hemel
2025-03-30 13:03:57 +02:00
parent a0807b052f
commit be86e53398
24 changed files with 142 additions and 126 deletions
@@ -1,3 +1,5 @@
#meta
This is the start of the standard library distributed as part of SilverBullet core. All pages under this prefix are read-only.
${template.each(query[[
+21
View File
@@ -27,6 +27,27 @@ end
function date.time()
return os.date(date.time_format)
end
slashcommand.define {
name = "today",
run = function()
editor.insertAtCursor(date.today())
end
}
slashcommand.define {
name = "yesterday",
run = function()
editor.insertAtCursor(date.yesterday())
end
}
slashcommand.define {
name = "tomorrow",
run = function()
editor.insertAtCursor(date.tomorrow())
end
}
```
# Examples
+7
View File
@@ -0,0 +1,7 @@
This is your ${widgets.commandButton("Quick Note")} inbox.
${template.each(query[[
from index.tag "page"
where _.name:startsWith("Inbox/")
order by _.lastModified desc
]], templates.fullPageItem)}
+5
View File
@@ -8,6 +8,11 @@ templates.pageItem = template.new([==[
* [[${name}]]
]==])
-- Renders a page object as a linked list item with full path
templates.fullPageItem = template.new([==[
* [[${name}|${name}]]
]==])
-- Renders a task object as a togglable task
templates.taskItem = template.new([==[
* [${state}] [[${ref}]] ${name}
+40
View File
@@ -58,4 +58,44 @@ slashcommand.define {
editor.replaceRange(line.from, line.to, ws .. "* [ ] " .. rest, true)
end
}
slashcommand.define {
name = "hr",
run = function()
editor.insertAtCursor("---\n")
end
}
slashcommand.define {
name = "note-admonition",
run = function()
editor.insertAtCursor([==[
> **note** Note
> |^|
]==], false, true)
end
}
slashcommand.define {
name = "warning-admonition",
run = function()
editor.insertAtCursor([==[
> **warning** Warning
> |^|
]==], false, true)
end
}
slashcommand.define {
name = "table",
exceptContexts = {"FencedCode"},
run = function()
editor.insertAtCursor([==[
| Header A | Header B |
|----------|----------|
| Cell A|^| | Cell B |
]==], false, true)
end
}
```
+32 -2
View File
@@ -123,8 +123,8 @@ local mentionTemplate = template.new [==[
* [[${_.ref}]]: “${_.snippet}”
]==]
function widgets.linkedMentions()
local pageName = editor.getCurrentPage()
function widgets.linkedMentions(pageName)
pageName = pageName or editor.getCurrentPage()
local linkedMentions = query[[
from index.tag "link"
where _.page != pageName and _.toPage == pageName
@@ -145,3 +145,33 @@ event.listen {
end
}
```
# Linked tasks
```space-lua
function widgets.linkedTasks(pageName)
pageName = pageName or editor.getCurrentPage()
local tasks = query[[
from index.tag "task"
where not _.done
and string.find(_.name, "[[" .. pageName .. "]]", 1, true)
]]
local md = ""
if #tasks > 0 then
md = "# Linked Tasks\n"
.. template.each(tasks, templates.taskItem)
else
md = ""
end
return widget.new {
markdown = md
}
end
event.listen {
name = "hooks:renderTopWidgets",
run = function(e)
print("Linked tasks")
return widgets.linkedTasks()
end
}
```
+7 -5
View File
@@ -282,13 +282,15 @@ export class LuaNativeJSFunction implements ILuaFunction {
constructor(readonly fn: (...args: JSValue[]) => JSValue) {
}
// Performs automatic conversion between Lua and JS values
// Performs automatic conversion between Lua and JS values for arguments, but not for return values
call(sf: LuaStackFrame, ...args: LuaValue[]): Promise<LuaValue> | LuaValue {
const result = this.fn(...args.map((v) => luaValueToJS(v, sf)));
if (result instanceof Promise) {
return result.then(jsToLuaValue);
const evaluatedArgs = evalPromiseValues(
args.map((v) => luaValueToJS(v, sf)),
);
if (evaluatedArgs instanceof Promise) {
return evaluatedArgs.then((args) => this.fn(...args));
} else {
return jsToLuaValue(result);
return this.fn(...evaluatedArgs);
}
}
+7
View File
@@ -1,6 +1,7 @@
import {
jsToLuaValue,
LuaBuiltinFunction,
LuaNativeJSFunction,
LuaTable,
luaValueToJS,
} from "$common/space_lua/runtime.ts";
@@ -75,4 +76,10 @@ export const jsApi = new LuaTable({
createTextNode: new LuaBuiltinFunction((_sf, text) =>
document.createTextNode(text)
),
Blob: new LuaNativeJSFunction((
blobParts: any[],
options: BlobPropertyBag,
) => {
return new Blob(blobParts, options);
}),
});
+10 -2
View File
@@ -70,7 +70,9 @@ export function getCursor(): Promise<number> {
/**
* Returns the line number and column number of the cursor in the editor
*/
export function getSelection(): Promise<{ from: number; to: number }> {
export function getSelection(): Promise<
{ from: number; to: number; text: string }
> {
return syscall("editor.getSelection");
}
@@ -299,8 +301,14 @@ export function moveCursorToLine(
export function insertAtCursor(
text: string,
scrollIntoView = false,
cursorPlaceHolder = false,
): Promise<void> {
return syscall("editor.insertAtCursor", text, scrollIntoView);
return syscall(
"editor.insertAtCursor",
text,
scrollIntoView,
cursorPlaceHolder,
);
}
/**
+11 -2
View File
@@ -59,8 +59,17 @@ export function editorSyscalls(client: Client): SysCallMapping {
"editor.getCursor": (): number => {
return client.editorView.state.selection.main.from;
},
"editor.getSelection": (): { from: number; to: number } => {
return client.editorView.state.selection.main;
"editor.getSelection": (): { from: number; to: number; text: string } => {
const selection = client.editorView.state.selection.main;
const text = client.editorView.state.sliceDoc(
selection.from,
selection.to,
);
return {
from: selection.from,
to: selection.to,
text,
};
},
"editor.save": () => {
return client.save(true);
@@ -1,12 +0,0 @@
---
description: Create a New Page template
tags: template
hooks.newPage:
suggestedName: "Library/Personal/New Page/"
confirmName: true
frontmatter: |
tags: template
displayName: "|^|"
hooks.newPage:
suggestedName: ""
---
@@ -1,11 +0,0 @@
---
tags: template
description: Create a new Snippet template
hooks.newPage:
suggestedName: Library/Personal/Snippet/
confirmName: true
frontmatter: |
tags: template
hooks.snippet:
slashCommand: "|^|"
---
-7
View File
@@ -1,7 +0,0 @@
#meta
This page lists all `#meta` tagged pages currently available in your space. You can also navigate to them via the {[Navigate: Meta Picker]}.
```query
page where tags = "meta" render [[Library/Core/Query/Page]]
```
-8
View File
@@ -1,8 +0,0 @@
This is your {[Quick Note]} inbox.
```query
page
where name =~ /^Inbox\//
render [[Library/Core/Query/Full Page]]
order by lastModified desc
```
-9
View File
@@ -1,9 +0,0 @@
---
tags: template
description: Insert a horizontal rule
hooks.snippet:
slashCommand: hr
exceptContexts:
- FencedCode
---
---
@@ -1,7 +0,0 @@
---
tags: template
description: Insert a "note" admonition
hooks.snippet.slashCommand: note-admonition
---
> **note** Note
> |^|
-11
View File
@@ -1,11 +0,0 @@
---
tags: template
description: Insert a table
hooks.snippet:
slashCommand: table
exceptContexts:
- FencedCode
---
| Header A | Header B |
|----------|----------|
| Cell A|^| | Cell B |
-6
View File
@@ -1,6 +0,0 @@
---
tags: template
description: "Today's date"
hooks.snippet.slashCommand: today
---
{{today}}
-6
View File
@@ -1,6 +0,0 @@
---
tags: template
description: "Tomorrow's date"
hooks.snippet.slashCommand: tomorrow
---
{{tomorrow}}
@@ -1,7 +0,0 @@
---
description: Insert a "warning" admonition
tags: template
hooks.snippet.slashCommand: warning-admonition
---
> **warning** Warning
> |^|
@@ -1,6 +0,0 @@
---
tags: template
description: "Yesterday's date"
hooks.snippet.slashCommand: yesterday
---
{{yesterday}}
@@ -1,16 +0,0 @@
---
tags: template
description: |
Shows all tasks that contain a link the current page. For instance a task that references `[[John]]` in its name, would appear on the `John` page.
hooks.top:
where: 'true'
order: 1
---
{{#let @linkedTasks = {task where not done and (contains(name, "[[" + @page.name + "]]") or contains(name, "[[" + @page.name + "|"))}}}
{{#if @linkedTasks}}
# Linked Tasks
{{#each @linkedTasks}}
{{template([[Library/Core/Query/Task]], .)}}
{{/each}}
{{/if}}
{{/let}}
@@ -1,9 +0,0 @@
---
tags: template
description: Queries all tasks tagged with a specific tag.
usage: Pass in the tag to filter on as the `value` of this template
---
```query
task where tags = "{{.}}" and not done render [[Library/Core/Query/Task]]
```