Big refactors and fixes

* Query regen
* Fix anchor completion
* Dependency fixes
* Changelog update
This commit is contained in:
Zef Hemel
2023-07-02 11:25:32 +02:00
committed by GitHub
parent fee2c5928e
commit 7c825348b2
74 changed files with 935 additions and 567 deletions
+6 -5
View File
@@ -180,12 +180,13 @@ export function attachmentExtension(editor: Editor) {
if (!finalFileName) {
return;
}
await editor.space.writeAttachment(finalFileName, new Uint8Array(data));
let attachmentMarkdown = `[${finalFileName}](${
encodeURIComponent(finalFileName)
})`;
await editor.space.writeAttachment(
finalFileName,
new Uint8Array(data),
);
let attachmentMarkdown = `[${finalFileName}](${encodeURI(finalFileName)})`;
if (mimeType.startsWith("image/")) {
attachmentMarkdown = `![](${encodeURIComponent(finalFileName)})`;
attachmentMarkdown = `![](${encodeURI(finalFileName)})`;
}
editor.editorView!.dispatch({
changes: [
+8 -10
View File
@@ -8,6 +8,7 @@ import {
import { decoratorStateField } from "./util.ts";
import type { Space } from "../space.ts";
import type { Editor } from "../editor.tsx";
class InlineImageWidget extends WidgetType {
constructor(
@@ -39,13 +40,7 @@ class InlineImageWidget extends WidgetType {
this.space.setCachedImageHeight(this.url, img.height);
}
};
if (this.url.startsWith("http")) {
img.src = this.url;
} else {
// This is an attachment image, rewrite the URL a little
img.src = `/.fs/${decodeURIComponent(this.url)}`;
}
img.src = this.url;
img.alt = this.title;
img.title = this.title;
img.style.display = "block";
@@ -58,7 +53,7 @@ class InlineImageWidget extends WidgetType {
}
}
export function inlineImagesPlugin(space: Space) {
export function inlineImagesPlugin(editor: Editor) {
return decoratorStateField((state: EditorState) => {
const widgets: Range<Decoration>[] = [];
const imageRegex = /!\[(?<title>[^\]]*)\]\((?<url>.+)\)/;
@@ -76,11 +71,14 @@ export function inlineImagesPlugin(space: Space) {
return;
}
const url = imageRexexResult.groups.url;
let url = imageRexexResult.groups.url;
const title = imageRexexResult.groups.title;
if (url.indexOf("://") === -1) {
url = `/.fs/${url}`;
}
widgets.push(
Decoration.widget({
widget: new InlineImageWidget(url, title, space),
widget: new InlineImageWidget(url, title, editor.space),
block: true,
}).range(node.to),
);
+1 -1
View File
@@ -37,7 +37,7 @@ class TableViewWidget extends WidgetType {
// Annotate every element with its position so we can use it to put
// the cursor there when the user clicks on the table.
annotationPositions: true,
inlineAttachments: (url) => {
translateUrls: (url) => {
if (!url.includes("://")) {
return `/.fs/${url}`;
}
+5 -2
View File
@@ -33,6 +33,7 @@ export function cleanWikiLinkPlugin(editor: Editor) {
if (page.includes("@")) {
cleanPage = page.split("@")[0];
}
// console.log("Resolved page", resolvedPage);
for (const pageMeta of allPages) {
if (pageMeta.name === cleanPage) {
pageExists = true;
@@ -76,8 +77,10 @@ export function cleanWikiLinkPlugin(editor: Editor) {
widget: new LinkWidget(
{
text: linkText,
title: pageExists ? `Navigate to ${page}` : `Create ${page}`,
href: `/${page}`,
title: pageExists
? `Navigate to ${cleanPage}`
: `Create ${cleanPage}`,
href: `/${cleanPage}`,
cssClass: pageExists
? "sb-wiki-link-page"
: "sb-wiki-link-page-missing",
+8 -13
View File
@@ -15,7 +15,7 @@ export class CollabManager {
"editor:pageLoaded",
(pageName, previousPage) => {
console.log("Page loaded", pageName, previousPage);
this.updatePresence(pageName, previousPage).catch(console.error);
this.updatePresence(pageName).catch(console.error);
},
);
}
@@ -26,25 +26,20 @@ export class CollabManager {
}, collabPingInterval);
}
async updatePresence(currentPage?: string, previousPage?: string) {
async updatePresence(currentPage: string) {
try {
// This is signaled through an OPTIONS call on the file we have open
const resp = await this.editor.remoteSpacePrimitives.authenticatedFetch(
this.editor.remoteSpacePrimitives.url,
`${this.editor.remoteSpacePrimitives.url}/${currentPage}.md`,
{
method: "POST",
method: "OPTIONS",
headers: {
"Content-Type": "application/json",
"X-Client-Id": this.clientId,
},
body: JSON.stringify({
operation: "presence",
clientId: this.clientId,
previousPage,
currentPage,
}),
keepalive: true, // important for beforeunload event
},
);
const { collabId } = await resp.json();
const collabId = resp.headers.get("X-Collab-Id");
// Not reading body at all, is that a problem?
if (this.editor.collabState && !this.editor.collabState.isLocalCollab) {
// We're in a remote collab mode, don't do anything
-1
View File
@@ -8,7 +8,6 @@ import type { ComponentChildren, FunctionalComponent } from "../deps.ts";
import { Notification } from "../types.ts";
import { FeatherProps } from "https://esm.sh/v99/preact-feather@4.2.1/dist/types";
import { MiniEditor } from "./mini_editor.tsx";
import process from "https://deno.land/std@0.177.1/node/process.ts";
export type ActionButton = {
icon: FunctionalComponent<FeatherProps>;
+1 -1
View File
@@ -21,7 +21,7 @@ export {
yCollab,
yUndoManagerKeymap,
} from "https://esm.sh/y-codemirror.next@0.3.2?external=yjs,@codemirror/state,@codemirror/commands,@codemirror/history,@codemirror/view";
export { HocuspocusProvider } from "https://esm.sh/@hocuspocus/provider@2.1.0?external=yjs,ws&target=es2022";
export { HocuspocusProvider } from "https://esm.sh/@hocuspocus/provider@2.2.0?deps=lib0@0.2.70&external=yjs,ws&target=es2022";
// Vim mode
export {
+19 -13
View File
@@ -242,15 +242,8 @@ export class Editor {
true,
);
const plugSpacePrimitives = new PlugSpacePrimitives(
// Using fallback space primitives here to allow (by default) local reads to "fall through" to HTTP when files aren't synced yet
new FallbackSpacePrimitives(
new IndexedDBSpacePrimitives(
`${dbPrefix}_space`,
globalThis.indexedDB,
),
this.remoteSpacePrimitives,
),
const plugSpaceRemotePrimitives = new PlugSpacePrimitives(
this.remoteSpacePrimitives,
namespaceHook,
);
@@ -258,7 +251,14 @@ export class Editor {
const localSpacePrimitives = new FilteredSpacePrimitives(
new FileMetaSpacePrimitives(
new EventedSpacePrimitives(
plugSpacePrimitives,
// Using fallback space primitives here to allow (by default) local reads to "fall through" to HTTP when files aren't synced yet
new FallbackSpacePrimitives(
new IndexedDBSpacePrimitives(
`${dbPrefix}_space`,
globalThis.indexedDB,
),
plugSpaceRemotePrimitives,
),
this.eventHook,
),
indexSyscalls,
@@ -279,12 +279,16 @@ export class Editor {
this.syncService = new SyncService(
localSpacePrimitives,
this.remoteSpacePrimitives,
plugSpaceRemotePrimitives,
this.kvStore,
this.eventHook,
(path) => {
// TODO: At some point we should remove the data.db exception here
return path !== "data.db" && !plugSpacePrimitives.isLikelyHandled(path);
return path !== "data.db" &&
// Exclude all plug space primitives paths
!plugSpaceRemotePrimitives.isLikelyHandled(path) ||
// Except federated ones
path.startsWith("!");
},
);
@@ -892,7 +896,7 @@ export class Editor {
),
],
}),
inlineImagesPlugin(this.space),
inlineImagesPlugin(this),
highlightSpecialChars(),
history(),
drawSelection(),
@@ -1117,6 +1121,7 @@ export class Editor {
const linePrefix = line.text.slice(0, selection.from - line.from);
const results = await this.dispatchAppEvent(eventName, {
pageName: this.currentPage!,
linePrefix,
pos: selection.from,
} as CompleteEvent);
@@ -1127,6 +1132,7 @@ export class Editor {
console.error(
"Got completion results from multiple sources, cannot deal with that",
);
console.error("Previously had", actualResult, "now also got", result);
return null;
}
actualResult = result;
+9 -4
View File
@@ -82,10 +82,13 @@ self.addEventListener("fetch", (event: any) => {
}
const requestUrl = new URL(event.request.url);
const pathname = requestUrl.pathname;
// console.log("In service worker, pathname is", pathname);
// Are we fetching a URL from the same origin as the app? If not, we don't handle it here
const fetchingLocal = location.host === requestUrl.host;
// If this is a /.fs request, this can either be a plug worker load or an attachment load
if (pathname.startsWith("/.fs")) {
if (fetchingLocal && pathname.startsWith("/.fs")) {
if (fileContentTable && !event.request.headers.has("x-sync-mode")) {
// console.log(
// "Attempting to serve file from locally synced space:",
@@ -101,8 +104,10 @@ self.addEventListener("fetch", (event: any) => {
// console.log("Serving from space", path);
return new Response(data.data, {
headers: {
"Content-type": mime.getType(path) ||
"application/octet-stream",
"Content-type": data.meta.contentType,
"Content-Length": "" + data.meta.size,
"X-Permission": data.meta.perm,
"X-Last-Modified": "" + data.meta.lastModified,
},
});
} else {
@@ -120,7 +125,7 @@ self.addEventListener("fetch", (event: any) => {
// Just fetch the file directly
return fetch(event.request);
}
} else if (pathname !== "/.auth") {
} else if (fetchingLocal && pathname !== "/.auth") {
// Must be a page URL, let's serve index.html which will handle it
return caches.match(precacheFiles["/"]).then((response) => {
// This shouldnt't happen, index.html not in the cache for some reason
+3 -3
View File
@@ -88,8 +88,8 @@ export class SyncService {
async hasInitialSyncCompleted(): Promise<boolean> {
// Initial sync has happened when sync progress has been reported at least once, but the syncStartTime has been reset (which happens after sync finishes)
return !!(!(await this.kvStore.get(syncStartTimeKey)) &&
(await this.kvStore.get(syncLastActivityKey)));
return !(await this.kvStore.has(syncStartTimeKey)) &&
(await this.kvStore.has(syncLastActivityKey));
}
async registerSyncStart(): Promise<void> {
@@ -371,7 +371,7 @@ export class SyncService {
name,
data,
false,
meta.lastModified,
meta,
);
// Update snapshot
snapshot.set(name, [
+4
View File
@@ -182,6 +182,10 @@ export function editorSyscalls(editor: Editor): SysCallMapping {
const cm = vimGetCm(editor.editorView!)!;
return Vim.handleEx(cm, exCommand);
},
// Sync
"editor.syncSpace": () => {
return editor.syncService.syncSpace();
},
// Folding
"editor.fold": () => {
foldCode(editor.editorView!);