1
0
This commit is contained in:
Zef Hemel 2024-01-24 14:44:39 +01:00
parent a9ce68860e
commit 8a89d69c22
16 changed files with 69 additions and 54 deletions

View File

@ -9,6 +9,7 @@ import {
} from "$sb/lib/tree.ts"; } from "$sb/lib/tree.ts";
import { parsePageRef } from "$sb/lib/page.ts"; import { parsePageRef } from "$sb/lib/page.ts";
import { Fragment, renderHtml, Tag } from "./html_render.ts"; import { Fragment, renderHtml, Tag } from "./html_render.ts";
import { encodePageRef } from "$sb/lib/page.ts";
export type MarkdownRenderOptions = { export type MarkdownRenderOptions = {
failOnUnknown?: true; failOnUnknown?: true;
@ -246,11 +247,11 @@ function render(
if (aliasNode) { if (aliasNode) {
linkText = aliasNode.children![0].text!; linkText = aliasNode.children![0].text!;
} }
const { page: pageName, anchor } = parsePageRef(ref); const pageRef = parsePageRef(ref);
return { return {
name: "a", name: "a",
attrs: { attrs: {
href: `/${pageName}${anchor ? "#" + anchor : ""}`, href: `/${encodePageRef(pageRef)}`,
class: "wiki-link", class: "wiki-link",
"data-ref": ref, "data-ref": ref,
}, },

View File

@ -13,7 +13,11 @@ import { FilterOption } from "./types.ts";
import { ensureSettingsAndIndex } from "../common/util.ts"; import { ensureSettingsAndIndex } 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 { PageState, PathPageNavigator } from "./navigator.ts"; import {
PageState,
parsePageRefFromURI,
PathPageNavigator,
} from "./navigator.ts";
import { AppViewState, BuiltinSettings } from "./types.ts"; import { AppViewState, BuiltinSettings } from "./types.ts";
@ -110,6 +114,7 @@ export class Client {
// Used by the "wiki link" highlighter to check if a page exists // Used by the "wiki link" highlighter to check if a page exists
public allKnownPages = new Set<string>(); public allKnownPages = new Set<string>();
onLoadPageRef: PageRef;
constructor( constructor(
private parent: Element, private parent: Element,
@ -120,6 +125,7 @@ export class Client {
} }
// Generate a semi-unique prefix for the database so not to reuse databases for different space paths // Generate a semi-unique prefix for the database so not to reuse databases for different space paths
this.dbPrefix = "" + simpleHash(window.silverBulletConfig.spaceFolderPath); this.dbPrefix = "" + simpleHash(window.silverBulletConfig.spaceFolderPath);
this.onLoadPageRef = parsePageRefFromURI();
} }
/** /**
@ -224,7 +230,7 @@ export class Client {
setInterval(() => { setInterval(() => {
try { try {
this.syncService.syncFile(`${this.currentPage!}.md`).catch((e: any) => { this.syncService.syncFile(`${this.currentPage}.md`).catch((e: any) => {
console.error("Interval sync error", e); console.error("Interval sync error", e);
}); });
} catch (e: any) { } catch (e: any) {
@ -305,7 +311,11 @@ export class Client {
let adjustedPosition = false; let adjustedPosition = false;
// Was a particular scroll position persisted? // Was a particular scroll position persisted?
if (pageState.scrollTop !== undefined) { if (
pageState.scrollTop !== undefined &&
!(pageState.scrollTop === 0 &&
(pageState.pos !== undefined || pageState.anchor !== undefined))
) {
setTimeout(() => { setTimeout(() => {
console.log("Kicking off scroll to", pageState.scrollTop); console.log("Kicking off scroll to", pageState.scrollTop);
this.editorView.scrollDOM.scrollTop = pageState.scrollTop!; this.editorView.scrollDOM.scrollTop = pageState.scrollTop!;
@ -592,8 +602,10 @@ export class Client {
return localSpacePrimitives; return localSpacePrimitives;
} }
get currentPage(): string | undefined { get currentPage(): string {
return this.ui.viewState.currentPage; return this.ui.viewState.currentPage !== undefined
? this.ui.viewState.currentPage
: this.onLoadPageRef.page; // best effort
} }
dispatchAppEvent(name: AppEvent, ...args: any[]): Promise<any[]> { dispatchAppEvent(name: AppEvent, ...args: any[]): Promise<any[]> {
@ -814,7 +826,7 @@ export class Client {
} }
const results = await this.dispatchAppEvent(eventName, { const results = await this.dispatchAppEvent(eventName, {
pageName: this.currentPage!, pageName: this.currentPage,
linePrefix, linePrefix,
pos: selection.from, pos: selection.from,
parentNodes, parentNodes,
@ -851,7 +863,7 @@ export class Client {
async reloadPage() { async reloadPage() {
console.log("Reloading page"); console.log("Reloading page");
clearTimeout(this.saveTimeout); clearTimeout(this.saveTimeout);
await this.loadPage(this.currentPage!); await this.loadPage(this.currentPage);
} }
focus() { focus() {
@ -889,6 +901,10 @@ export class Client {
} }
if (newWindow) { if (newWindow) {
console.log(
"Navigating to new page in new window",
`${location.origin}/${encodePageRef(pageRef)}`,
);
const win = window.open( const win = window.open(
`${location.origin}/${encodePageRef(pageRef)}`, `${location.origin}/${encodePageRef(pageRef)}`,
"_blank", "_blank",

View File

@ -28,7 +28,7 @@ export function cleanModePlugins(client: Client) {
// TODO: Move this logic elsewhere? // TODO: Move this logic elsewhere?
onCheckboxClick: (pos) => { onCheckboxClick: (pos) => {
const clickEvent: ClickEvent = { const clickEvent: ClickEvent = {
page: client.currentPage!, page: client.currentPage,
altKey: false, altKey: false,
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,

View File

@ -55,7 +55,7 @@ export function cleanCommandLinkPlugin(editor: Client) {
} }
// Dispatch click event to navigate there without moving the cursor // Dispatch click event to navigate there without moving the cursor
const clickEvent: ClickEvent = { const clickEvent: ClickEvent = {
page: editor.currentPage!, page: editor.currentPage,
ctrlKey: e.ctrlKey, ctrlKey: e.ctrlKey,
metaKey: e.metaKey, metaKey: e.metaKey,
altKey: e.altKey, altKey: e.altKey,

View File

@ -215,7 +215,7 @@ export function attachmentExtension(editor: Client) {
return; return;
} }
await editor.space.writeAttachment( await editor.space.writeAttachment(
resolve(folderName(editor.currentPage!), finalFileName), resolve(folderName(editor.currentPage), finalFileName),
new Uint8Array(data), new Uint8Array(data),
); );
let attachmentMarkdown = `[${finalFileName}](${encodeURI(finalFileName)})`; let attachmentMarkdown = `[${finalFileName}](${encodeURI(finalFileName)})`;

View File

@ -22,7 +22,7 @@ export class IFrameWidget extends WidgetType {
const iframe = createWidgetSandboxIFrame( const iframe = createWidgetSandboxIFrame(
this.client, this.client,
this.bodyText, this.bodyText,
this.codeWidgetCallback(this.bodyText, this.client.currentPage!), this.codeWidgetCallback(this.bodyText, this.client.currentPage),
(message) => { (message) => {
switch (message.type) { switch (message.type) {
case "blur": case "blur":
@ -33,7 +33,7 @@ export class IFrameWidget extends WidgetType {
break; break;
case "reload": case "reload":
this.codeWidgetCallback(this.bodyText, this.client.currentPage!) this.codeWidgetCallback(this.bodyText, this.client.currentPage)
.then( .then(
(widgetContent: WidgetContent | null) => { (widgetContent: WidgetContent | null) => {
if (widgetContent === null) { if (widgetContent === null) {

View File

@ -33,7 +33,7 @@ class InlineImageWidget extends WidgetType {
toDOM() { toDOM() {
const img = document.createElement("img"); const img = document.createElement("img");
let url = this.url; let url = this.url;
url = resolvePath(this.client.currentPage!, url, true); url = resolvePath(this.client.currentPage, url, true);
// console.log("Creating DOM", this.url); // console.log("Creating DOM", this.url);
const cachedImageHeight = this.client.getCachedWidgetHeight( const cachedImageHeight = this.client.getCachedWidgetHeight(
`image:${this.url}`, `image:${this.url}`,
@ -79,7 +79,7 @@ export function inlineImagesPlugin(client: Client) {
const title = imageRexexResult.groups.title; const title = imageRexexResult.groups.title;
if (url.indexOf("://") === -1 && !url.startsWith("/")) { if (url.indexOf("://") === -1 && !url.startsWith("/")) {
url = resolveAttachmentPath(client.currentPage!, decodeURI(url)); url = resolveAttachmentPath(client.currentPage, decodeURI(url));
} }
widgets.push( widgets.push(
Decoration.widget({ Decoration.widget({

View File

@ -37,7 +37,7 @@ export function linkPlugin(client: Client) {
if (!cleanLink.includes("://")) { if (!cleanLink.includes("://")) {
cleanLink = resolveAttachmentPath( cleanLink = resolveAttachmentPath(
client.currentPage!, client.currentPage,
decodeURI(cleanLink), decodeURI(cleanLink),
); );
} }

View File

@ -11,7 +11,7 @@ export function plugLinter(client: Client) {
view.state.sliceDoc(), view.state.sliceDoc(),
); );
const results = (await client.dispatchAppEvent("editor:lint", { const results = (await client.dispatchAppEvent("editor:lint", {
name: client.currentPage!, name: client.currentPage,
tree: tree, tree: tree,
} as LintEvent)).flat(); } as LintEvent)).flat();
return results; return results;

View File

@ -48,7 +48,7 @@ export class MarkdownWidget extends WidgetType {
) { ) {
const widgetContent = await this.codeWidgetCallback( const widgetContent = await this.codeWidgetCallback(
this.bodyText, this.bodyText,
this.client.currentPage!, this.client.currentPage,
); );
activeWidgets.add(this); activeWidgets.add(this);
if (!widgetContent) { if (!widgetContent) {
@ -79,7 +79,7 @@ export class MarkdownWidget extends WidgetType {
translateUrls: (url) => { translateUrls: (url) => {
if (!url.includes("://")) { if (!url.includes("://")) {
url = resolveAttachmentPath( url = resolveAttachmentPath(
this.client.currentPage!, this.client.currentPage,
decodeURI(url), decodeURI(url),
); );
} }

View File

@ -42,7 +42,7 @@ class TableViewWidget extends WidgetType {
annotationPositions: true, annotationPositions: true,
translateUrls: (url) => { translateUrls: (url) => {
if (!url.includes("://")) { if (!url.includes("://")) {
url = resolveAttachmentPath(this.client.currentPage!, decodeURI(url)); url = resolveAttachmentPath(this.client.currentPage, decodeURI(url));
} }
return url; return url;

View File

@ -9,7 +9,7 @@ import {
LinkWidget, LinkWidget,
} from "./util.ts"; } from "./util.ts";
import { resolvePath } from "$sb/lib/resolve.ts"; import { resolvePath } from "$sb/lib/resolve.ts";
import { parsePageRef } from "$sb/lib/page.ts"; import { encodePageRef, parsePageRef } from "$sb/lib/page.ts";
/** /**
* Plugin to hide path prefix when the cursor is not inside. * Plugin to hide path prefix when the cursor is not inside.
@ -30,10 +30,9 @@ export function cleanWikiLinkPlugin(client: Client) {
const [_fullMatch, page, pipePart, alias] = match; const [_fullMatch, page, pipePart, alias] = match;
let pageExists = !client.fullSyncCompleted; let pageExists = !client.fullSyncCompleted;
let cleanPage = page; const pageRef = parsePageRef(page);
cleanPage = parsePageRef(page).page; pageRef.page = resolvePath(client.currentPage, pageRef.page);
cleanPage = resolvePath(client.currentPage!, cleanPage); const lowerCasePageName = pageRef.page.toLowerCase();
const lowerCasePageName = cleanPage.toLowerCase();
for (const pageName of client.allKnownPages) { for (const pageName of client.allKnownPages) {
if (pageName.toLowerCase() === lowerCasePageName) { if (pageName.toLowerCase() === lowerCasePageName) {
pageExists = true; pageExists = true;
@ -41,8 +40,8 @@ export function cleanWikiLinkPlugin(client: Client) {
} }
} }
if ( if (
cleanPage === "" || pageRef.page === "" ||
client.plugSpaceRemotePrimitives.isLikelyHandled(cleanPage) client.plugSpaceRemotePrimitives.isLikelyHandled(pageRef.page)
) { ) {
// Empty page name with local @anchor use or a link to a page that dynamically generated by a plug // Empty page name with local @anchor use or a link to a page that dynamically generated by a plug
pageExists = true; pageExists = true;
@ -81,9 +80,9 @@ export function cleanWikiLinkPlugin(client: Client) {
{ {
text: linkText, text: linkText,
title: pageExists title: pageExists
? `Navigate to ${cleanPage}` ? `Navigate to ${encodePageRef(pageRef)}`
: `Create ${cleanPage}`, : `Create ${pageRef.page}`,
href: `/${cleanPage}`, href: `/${encodePageRef(pageRef)}`,
cssClass: pageExists cssClass: pageExists
? "sb-wiki-link-page" ? "sb-wiki-link-page"
: "sb-wiki-link-page-missing", : "sb-wiki-link-page-missing",
@ -96,7 +95,7 @@ export function cleanWikiLinkPlugin(client: Client) {
} }
// Dispatch click event to navigate there without moving the cursor // Dispatch click event to navigate there without moving the cursor
const clickEvent: ClickEvent = { const clickEvent: ClickEvent = {
page: client.currentPage!, page: client.currentPage,
ctrlKey: e.ctrlKey, ctrlKey: e.ctrlKey,
metaKey: e.metaKey, metaKey: e.metaKey,
altKey: e.altKey, altKey: e.altKey,

View File

@ -14,7 +14,6 @@ export type PageState = PageRef & {
export class PathPageNavigator { export class PathPageNavigator {
navigationResolve?: () => void; navigationResolve?: () => void;
root: string;
indexPage: string; indexPage: string;
openPages = new Map<string, PageState>(); openPages = new Map<string, PageState>();
@ -22,7 +21,6 @@ export class PathPageNavigator {
constructor( constructor(
private client: Client, private client: Client,
) { ) {
this.root = "";
this.indexPage = cleanPageRef( this.indexPage = cleanPageRef(
renderHandlebarsTemplate(client.settings.indexPage, {}, {}), renderHandlebarsTemplate(client.settings.indexPage, {}, {}),
); );
@ -52,20 +50,20 @@ export class PathPageNavigator {
window.history.replaceState( window.history.replaceState(
cleanState, cleanState,
"", "",
`${this.root}/${currentState.page}`, `/${currentState.page}`,
); );
console.log("Pushing new state", pageRef); console.log("Pushing new state", pageRef);
window.history.pushState( window.history.pushState(
pageRef, pageRef,
"", "",
`${this.root}/${pageRef.page}`, `/${pageRef.page}`,
); );
} else { } else {
// console.log("Replacing state", pageRef); // console.log("Replacing state", pageRef);
window.history.replaceState( window.history.replaceState(
pageRef, pageRef,
"", "",
`${this.root}/${pageRef.page}`, `/${pageRef.page}`,
); );
} }
// console.log("Explicitly dispatching the popstate", pageRef); // console.log("Explicitly dispatching the popstate", pageRef);
@ -81,7 +79,7 @@ export class PathPageNavigator {
} }
buildCurrentPageState(): PageState { buildCurrentPageState(): PageState {
const pageState: PageState = this.parseURI(); const pageState: PageState = parsePageRefFromURI();
const mainSelection = this.client.editorView.state.selection.main; const mainSelection = this.client.editorView.state.selection.main;
pageState.scrollTop = this.client.editorView.scrollDOM.scrollTop; pageState.scrollTop = this.client.editorView.scrollDOM.scrollTop;
pageState.selection = { pageState.selection = {
@ -122,7 +120,7 @@ export class PathPageNavigator {
} else { } else {
// This occurs when the page is loaded completely fresh with no browser history around it // This occurs when the page is loaded completely fresh with no browser history around it
// console.log("Got null state so using", this.parseURI()); // console.log("Got null state so using", this.parseURI());
const pageRef = this.parseURI(); const pageRef = parsePageRefFromURI();
if (!pageRef.page) { if (!pageRef.page) {
pageRef.page = this.indexPage; pageRef.page = this.indexPage;
} }
@ -141,16 +139,12 @@ export class PathPageNavigator {
}), }),
); );
} }
}
parseURI(): PageRef {
const pageRef = parsePageRef(decodeURI( export function parsePageRefFromURI(): PageRef {
location.pathname.substring(this.root.length + 1), const pageRef = parsePageRef(decodeURI(
)); location.pathname.substring(1),
));
// if (!pageRef.page) {
// pageRef.page = this.indexPage; return pageRef;
// }
return pageRef;
}
} }

View File

@ -18,7 +18,7 @@ import { PageRef } from "$sb/lib/page.ts";
export function editorSyscalls(client: Client): SysCallMapping { export function editorSyscalls(client: Client): SysCallMapping {
const syscalls: SysCallMapping = { const syscalls: SysCallMapping = {
"editor.getCurrentPage": (): string => { "editor.getCurrentPage": (): string => {
return client.currentPage!; return client.currentPage;
}, },
"editor.getText": () => { "editor.getText": () => {
return client.editorView.state.sliceDoc(); return client.editorView.state.sliceDoc();

View File

@ -6,6 +6,9 @@ release.
## Edge ## Edge
_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)._
* Bug fixes:
* 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
* Internal changes: * Internal changes:
* Big refactor: of navigation and browser history, fixed some {[Page: Rename]} bugs along the way * Big refactor: of navigation and browser history, fixed some {[Page: Rename]} bugs along the way
* Plugs now can no longer define their own markdown syntax, migrated all plug-specific syntax into the main parser. This should remove a bunch of editor “flashing” especially during sync. * Plugs now can no longer define their own markdown syntax, migrated all plug-specific syntax into the main parser. This should remove a bunch of editor “flashing” especially during sync.
@ -118,8 +121,8 @@ _The changes below are not yet released “properly”. To them out early, check
--- ---
## 0.5.3 ## 0.5.3
* Changes to [[Objects]]: * Changes to [[Objects]]:
* Paragraphs are now indexed, see [[Objects@paragraph]] (thanks to [Ian Shehadeh](https://github.com/silverbulletmd/silverbullet/pull/528)) * Paragraphs are now indexed, see [[Objects$paragraph]] (thanks to [Ian Shehadeh](https://github.com/silverbulletmd/silverbullet/pull/528))
* For consistency, list items are now always indexed as well (whether they contain a [[Tags|tag]] or not) see [[Objects@item]]. * For consistency, list items are now always indexed as well (whether they contain a [[Tags|tag]] or not) see [[Objects$item]].
* The {[Directive: Convert to Live Query/Template]} now also converts `#use` and `#include` directives * The {[Directive: Convert to Live Query/Template]} now also converts `#use` and `#include` directives
* Styling improvements for Linked Mentions * Styling improvements for Linked Mentions
* SilverBullet now fully works when added as PWA on Safari 17 (via the “Add to Dock” option). * SilverBullet now fully works when added as PWA on Safari 17 (via the “Add to Dock” option).

View File

@ -16,6 +16,8 @@ shortcuts:
key: "Alt-x" key: "Alt-x"
- command: "{[Upload: File]}" - command: "{[Upload: File]}"
priority: 1 # Make sure this appears at the top of the list in the command palette priority: 1 # Make sure this appears at the top of the list in the command palette
- command: "{[Open Command Palette]}"
key: "Ctrl-."
# Defines files to ignore in a format compatible with .gitignore # Defines files to ignore in a format compatible with .gitignore
spaceIgnore: | spaceIgnore: |