A discovery while experimenting with AnyType
revealed a conceptual equivalence to SilverBullet:
promoting a plain paragraph in AnyType to a named object mirrors
converting an ordinary text in SilverBullet’s editor into an [[aspiring-page]].
Opening that created object in AT (or [[aspiring-page]] in SB) shows backlinks
— one of which (back)links to the originating article at line level (at least),
while others point to other pages at line level (as well).
From a higher-level perspective,
this pattern constitutes a cursor–page–cursor bidirectional link:
the page functions as an intermediary that permits cursor-to-cursor connections.
Conclusion: a single [[aspiring-page]] suffices to establish cursor–cursor links;
nevertheless, the earlier code pattern required n+1 distinct [[aspiring-pages]] for 1 forward + n inverse labels.
Inspired by this, I refactored the final code snippet above to minimize anchors/labels, thereby reducing SB’s indexing overhead and better reflecting some essence of graph theory.
function getSelectedText()
local sel = editor.getSelection()
if not sel or sel.from == sel.to then return nil end
local text = editor.getText()
return text:sub(sel.from + 1, sel.to)
end
function setSelectedText(newText)
local sel = editor.getSelection()
if not sel or sel.from == sel.to then return nil end
editor.replaceRange(sel.from, sel.to, newText)
end
function usrPrompt(hinText, iniText)
local iniText = iniText or ""
local input = editor.prompt(hinText, iniText)
if not input then
editor.flashNotification("Cancelled", "warn")
end
return input
end
local anchorSymbol = "⚓"
local suffixFlabel = "➡️"
local suffixBlabel = "🔙"
local siblings = "🧑🤝🧑"
-- =========== Forth Anchor + Back Refs ==================
local function tableBack(Flabel)
local aspiringPage = Flabel .. anchorSymbol
return query[[
from index.tag "link"
where toPage == aspiringPage and alias:find(suffixFlabel, 1, true)
order by _.thBlabel
select {ref=_.ref, thBlabel=_.thBlabel}
]]
end
function backRefs(Flabel)
local str = template.each(tableBack(Flabel), template.new[==[[[${_.ref}|${_.thBlabel}]]]==])
if #str == 0 then return "No BackRef" end
return str
end
command.define {
name = "Insert: ForthAnchor + BackRefs",
key = "Ctrl-,",
run = function()
local iniText = getSelectedText()
-- local Flabel = usrPrompt('Enter: label (to be Referred)', iniText)
local Flabel
if iniText and iniText ~= "" then
Flabel = iniText
else
Flabel = usrPrompt('Enter: label (to be Referred)', '')
end
if not Flabel then return end
local aspiringPage = Flabel .. anchorSymbol
local forthAnchor = "[[" .. aspiringPage .. "||^|" .. suffixBlabel .. "]]"
local backRefs = '${backRefs("' .. Flabel .. '")}'
local fullText = forthAnchor .. backRefs
if iniText and iniText ~= "" then
setSelectedText("") -- Delete selected iniText
end
editor.insertAtPos(fullText, editor.getCursor(), true)
editor.copyToClipboard(Flabel)
end
}
-- =========== Back Anchor + Forth Ref ==================
local function tableBack_noSelf(Flabel, thBlabelNum)
local aspiringPage = Flabel .. anchorSymbol
return query[[
from index.tag "link"
where toPage == aspiringPage and alias:find(suffixFlabel, 1, true) and thBlabelNum ~= _.thBlabel
order by _.thBlabel
select {ref=_.ref, thBlabel=_.thBlabel}
]]
end
function backRefs_noSelf(Flabel, thBlabelNum)
local str = template.each(tableBack_noSelf(Flabel, thBlabelNum), template.new[==[[[${_.ref}|${_.thBlabel}]]]==])
if #str == 0 then return "No Sibling" end
return str
end
local function tableForth(Flabel)
local aspiringPage = Flabel .. anchorSymbol
return query[[
from index.tag "link"
where toPage == aspiringPage and alias:find(suffixBlabel, 1, true)
select {ref=_.ref}
]]
end
function forthRef(Flabel)
local str = template.each(tableForth(Flabel), template.new("[[${_.ref}|" .. siblings .. "]]"))
if #str == 0 then return "No such Anchor" end
return str
end
command.define {
name = "Insert: BackAnchor + ForthRef",
key = "Ctrl-.",
run = function()
local alias = getSelectedText()
local iniText = js.window.navigator.clipboard.readText()
-- local Flabel = usrPrompt('Jump to: label', iniText)
local Flabel
if iniText and iniText ~= "" then
Flabel = iniText
else
Flabel = usrPrompt('Jump to: label', '')
end
if not Flabel then return end
local thBlabelNum = #tableBack(Flabel) + 1
local aspiringPage = Flabel .. anchorSymbol
local backAnchor = "[[" .. aspiringPage .. "||^|" .. suffixFlabel .. thBlabelNum .. "]]"
local forthRef = '${forthRef("' .. Flabel .. '")}'
local backRefs_noSelf = '${backRefs_noSelf("' .. Flabel .. '",' .. thBlabelNum .. ')}'
local fullText = backAnchor .. forthRef .. backRefs_noSelf
if alias and alias ~= "" then
setSelectedText("") -- Delete selected alias
else
alias = ''
end
editor.insertAtPos(fullText, editor.getCursor(), true)
editor.insertAtCursor(alias, false) -- scrollIntoView?
end
}
index.defineTag {
name = "link",
metatable = {
__index = function(self, attr)
if attr == "thBlabel" then
return tonumber(string.match(self.alias, suffixFlabel .. "([0-9]+)"))
end
end
}
}
Now the forward jump is be triggered through the siblings emoji
(previously numbers ahead of
) — an undeniably odd interaction — but the trade-off is a likely improvement in indexing performance.
While experimenting with these apps, I drafted a comparison table that is neither rigorous[1] nor entirely fair[2]:
| App | Core RAM | Tech |
|---|---|---|
| SilverBullet | ~ 24 MB | Go + TS + Lua |
| RoamEdit | ~ 44 MB | Custom Web Stack ? |
| Workflowy | ~ 73 MB ? | Custom Web Stack ? |
| RoamResearch | -[3] | Electron + … |
| RemNote | -[4] | Electron + … |
| Tana | ~ 233 MB | Electron ? + … |
| SiYuan | ~ 231 MB | Electron + Go + TS |
| AnyType | 170 ~ 330 MB | Electron + TS |
| LogSeq | ~ 230 MB | Electron + Clojure + TS |
| Orca | ~ 200 MB | Electron + … |
| Obsidian | ~ 160 MB | Electron + TS ? |
My fondness for Sublime and Zed over VS Code has left me slightly biased against Electron, which influenced the table above; my apologies if it seems offensive. Note that the above is not a ranking (i.e. an ordered list) but an unordered list.
