Mobile app PoC (#281)

Initial checkin of mobile "native" app
This commit is contained in:
Zef Hemel
2023-01-08 12:24:12 +01:00
committed by GitHub
parent dc7de9d8f9
commit 0365673c41
61 changed files with 5190 additions and 155 deletions
+20
View File
@@ -7,6 +7,12 @@ import { PageNamespaceHook } from "../server/hooks/page_namespace.ts";
import { SilverBulletHooks } from "../common/manifest.ts";
import { System } from "../plugos/system.ts";
import { BuiltinSettings } from "./types.ts";
import { fulltextSyscalls } from "./syscalls/fulltext.ts";
import { indexerSyscalls } from "./syscalls/index.ts";
import { storeSyscalls } from "./syscalls/store.ts";
import { EventHook } from "../plugos/hooks/event.ts";
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
import { sandboxFetchSyscalls } from "./syscalls/fetch.ts";
safeRun(async () => {
const httpPrimitives = new HttpSpacePrimitives("");
@@ -35,6 +41,16 @@ safeRun(async () => {
const serverSpace = new Space(spacePrimitives);
serverSpace.watch();
// Register some web-specific syscall implementations
system.registerSyscalls(
[],
storeSyscalls(serverSpace),
indexerSyscalls(serverSpace),
clientStoreSyscalls(),
fulltextSyscalls(serverSpace),
sandboxFetchSyscalls(serverSpace),
);
console.log("Booting...");
const settings = parseYamlSettings(settingsPageText) as BuiltinSettings;
@@ -42,10 +58,14 @@ safeRun(async () => {
if (!settings.indexPage) {
settings.indexPage = "index";
}
// Event hook
const eventHook = new EventHook();
system.addHook(eventHook);
const editor = new Editor(
serverSpace,
system,
eventHook,
document.getElementById("sb-root")!,
"",
settings,
+16 -4
View File
@@ -7,8 +7,14 @@ import {
} from "../deps.ts";
import { decoratorStateField } from "./util.ts";
import type { Space } from "../../common/spaces/space.ts";
class InlineImageWidget extends WidgetType {
constructor(readonly url: string, readonly title: string) {
constructor(
readonly url: string,
readonly title: string,
readonly space: Space,
) {
super();
}
@@ -21,8 +27,14 @@ class InlineImageWidget extends WidgetType {
if (this.url.startsWith("http")) {
img.src = this.url;
} else {
img.src = `fs/${this.url}`;
// Specific to mobile
this.space.readAttachment(decodeURI(this.url), "dataurl").then(
({ data }) => {
img.src = data as string;
},
);
}
img.alt = this.title;
img.title = this.title;
img.style.display = "block";
@@ -32,7 +44,7 @@ class InlineImageWidget extends WidgetType {
}
}
export function inlineImagesPlugin() {
export function inlineImagesPlugin(space: Space) {
return decoratorStateField((state: EditorState) => {
const widgets: Range<Decoration>[] = [];
const imageRegex = /!\[(?<title>[^\]]*)\]\((?<url>.+)\)/;
@@ -54,7 +66,7 @@ export function inlineImagesPlugin() {
const title = imageRexexResult.groups.title;
widgets.push(
Decoration.widget({
widget: new InlineImageWidget(url, title),
widget: new InlineImageWidget(url, title, space),
}).range(node.to),
);
},
+30 -9
View File
@@ -24,6 +24,7 @@ import {
markdown,
runScopeHandlers,
searchKeymap,
sqlLanguage,
standardKeymap,
StreamLanguage,
syntaxHighlighting,
@@ -32,7 +33,6 @@ import {
ViewPlugin,
ViewUpdate,
yamlLanguage,
sqlLanguage,
} from "../common/deps.ts";
import { SilverBulletHooks } from "../common/manifest.ts";
import {
@@ -96,6 +96,7 @@ import type {
CompleteEvent,
} from "../plug-api/app_event.ts";
import { CodeWidgetHook } from "./hooks/code_widget.ts";
import { sandboxFetchSyscalls } from "../plugos/syscalls/fetch.ts";
const frontMatterRegex = /^---\n(.*?)---\n/ms;
@@ -139,6 +140,7 @@ export class Editor {
constructor(
space: Space,
system: System<SilverBulletHooks>,
eventHook: EventHook,
parent: Element,
urlPrefix: string,
readonly builtinSettings: BuiltinSettings,
@@ -150,9 +152,7 @@ export class Editor {
this.viewDispatch = () => {};
this.indexPage = builtinSettings.indexPage;
// Event hook
this.eventHook = new EventHook();
this.system.addHook(this.eventHook);
this.eventHook = eventHook;
// Code widget hook
this.codeWidgetHook = new CodeWidgetHook();
@@ -191,12 +191,8 @@ export class Editor {
eventSyscalls(this.eventHook),
editorSyscalls(this),
spaceSyscalls(this),
indexerSyscalls(this.space),
fulltextSyscalls(this.space),
systemSyscalls(this, this.system),
markdownSyscalls(buildMarkdown(this.mdExtensions)),
clientStoreSyscalls(),
storeSyscalls(this.space),
sandboxSyscalls(this.system),
assetSyscalls(this.system),
collabSyscalls(this),
@@ -484,6 +480,7 @@ export class Editor {
}
// deno-lint-ignore no-this-alias
const editor = this;
let touchCount = 0;
return EditorState.create({
doc: this.collabState ? this.collabState.ytext.toString() : text,
@@ -528,7 +525,7 @@ export class Editor {
),
],
}),
inlineImagesPlugin(),
inlineImagesPlugin(this.space),
highlightSpecialChars(),
history(),
drawSelection(),
@@ -601,6 +598,29 @@ export class Editor {
},
]),
EditorView.domEventHandlers({
// This may result in duplicated touch events on mobile devices
touchmove: (event: TouchEvent, view: EditorView) => {
touchCount++;
},
touchend: (event: TouchEvent, view: EditorView) => {
if (touchCount === 0) {
safeRun(async () => {
const touch = event.changedTouches.item(0)!;
const clickEvent: ClickEvent = {
page: pageName,
ctrlKey: event.ctrlKey,
metaKey: event.metaKey,
altKey: event.altKey,
pos: view.posAtCoords({
x: touch.clientX,
y: touch.clientY,
})!,
};
await this.dispatchAppEvent("page:click", clickEvent);
});
}
touchCount = 0;
},
click: (event: MouseEvent, view: EditorView) => {
safeRun(async () => {
const clickEvent: ClickEvent = {
@@ -1011,6 +1031,7 @@ export class Editor {
description: `Open page (${isMacLike() ? "Cmd-k" : "Ctrl-k"})`,
callback: () => {
dispatch({ type: "start-navigate" });
this.space.updatePageList();
},
},
{
+42 -38
View File
@@ -1,43 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<base href="/" />
<title>Silver Bullet</title>
<script>
Deno = {
args: [],
build: {
arch: "x86_64",
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<base href="/" />
<title>Silver Bullet</title>
<script>
Deno = {
args: [],
build: {
arch: "x86_64",
},
env: {
get(key) {
// return undefined;
},
env: {
get(key) {
// return undefined;
},
},
errors: {
AlreadyExists: class extends Error {},
}
};
</script>
<style>
html,
body {
margin: 0;
height: 100%;
padding: 0;
width: 100%;
overflow: hidden;
},
errors: {
AlreadyExists: class extends Error { },
}
</style>
<link rel="stylesheet" href="/main.css" />
<script type="module" src="/client.js"></script>
<link rel="manifest" href="/manifest.json" -->
<link rel="icon" type="image/x-icon" href="/favicon.png" />
</head>
<body>
<div id="sb-root"></div>
</body>
</html>
};
</script>
<style>
html,
body {
margin: 0;
height: 100%;
padding: 0;
width: 100%;
overflow: hidden;
}
</style>
<link rel="stylesheet" href="/main.css" />
<script type="module" src="/client.js"></script>
<link rel="manifest" href="/manifest.json" />
<link rel="icon" type="image/x-icon" href="/favicon.png" />
</head>
<body>
<div id="sb-root"></div>
<h1>SUPPP</h1>
</body>
</html>
+12
View File
@@ -0,0 +1,12 @@
import type { SysCallMapping } from "../../plugos/system.ts";
import { proxySyscalls } from "../../plugos/syscalls/transport.ts";
import type { Space } from "../../common/spaces/space.ts";
export function sandboxFetchSyscalls(space: Space): SysCallMapping {
return proxySyscalls(
[
"sandboxFetch.fetch",
],
(ctx, name, ...args) => space.proxySyscall(ctx.plug, name, args),
);
}