Mobile app PoC (#281)

Initial checkin of mobile "native" app
pull/301/head
Zef Hemel 2023-01-08 12:24:12 +01:00 committed by GitHub
parent dc7de9d8f9
commit 0365673c41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 5190 additions and 155 deletions

View File

@ -13,28 +13,25 @@ import { bundle as plugOsBundle } from "./plugos/bin/plugos-bundle.ts";
import * as flags from "https://deno.land/std@0.165.0/flags/mod.ts";
// @ts-ignore trust me
const esbuild: typeof esbuildWasm = Deno.run === undefined
export const esbuild: typeof esbuildWasm = Deno.run === undefined
? esbuildWasm
: esbuildNative;
async function prepareAssets(dist: string) {
await copy("web/fonts", `${dist}/web`, { overwrite: true });
await copy("web/index.html", `${dist}/web/index.html`, {
export async function prepareAssets(dist: string) {
await copy("web/fonts", `${dist}`, { overwrite: true });
await copy("web/index.html", `${dist}/index.html`, {
overwrite: true,
});
await copy("web/auth.html", `${dist}/web/auth.html`, {
await copy("web/auth.html", `${dist}/auth.html`, {
overwrite: true,
});
await copy("web/images/favicon.png", `${dist}/web/favicon.png`, {
await copy("web/images/favicon.png", `${dist}/favicon.png`, {
overwrite: true,
});
await copy("web/images/logo.png", `${dist}/web/logo.png`, {
await copy("web/images/logo.png", `${dist}/logo.png`, {
overwrite: true,
});
await copy("web/manifest.json", `${dist}/web/manifest.json`, {
overwrite: true,
});
await copy("server/SETTINGS_template.md", `${dist}/SETTINGS_template.md`, {
await copy("web/manifest.json", `${dist}/manifest.json`, {
overwrite: true,
});
const compiler = sass(
@ -44,53 +41,61 @@ async function prepareAssets(dist: string) {
},
);
await Deno.writeTextFile(
`${dist}/web/main.css`,
`${dist}/main.css`,
compiler.to_string("expanded") as string,
);
const globalManifest = await plugOsBundle("./plugs/global.plug.yaml");
await Deno.writeTextFile(
`${dist}/web/global.plug.json`,
`${dist}/global.plug.json`,
JSON.stringify(globalManifest, null, 2),
);
// HACK: Patch the JS by removing an invalid regex
let bundleJs = await Deno.readTextFile(`${dist}/web/client.js`);
let bundleJs = await Deno.readTextFile(`${dist}/client.js`);
bundleJs = patchDenoLibJS(bundleJs);
await Deno.writeTextFile(`${dist}/web/client.js`, bundleJs);
await bundleFolder(dist, "dist/asset_bundle.json");
await Deno.writeTextFile(`${dist}/client.js`, bundleJs);
}
async function bundle(watch: boolean): Promise<void> {
export async function bundle(
watch: boolean,
type: "web" | "mobile",
distDir: string,
): Promise<void> {
let building = false;
await doBuild();
await doBuild(`${type}/boot.ts`);
let timer;
if (watch) {
const watcher = Deno.watchFs(["web", "dist_bundle/_plug"]);
const watcher = Deno.watchFs([type, "dist_bundle/_plug"]);
for await (const _event of watcher) {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
console.log("Change detected, rebuilding...");
doBuild();
doBuild(`${type}/boot.ts`);
}, 1000);
}
}
async function doBuild() {
async function doBuild(
mainScript: string,
) {
if (building) {
return;
}
building = true;
if (type === "mobile") {
await bundleFolder("dist_bundle", "dist/asset_bundle.json");
}
await Promise.all([
esbuild.build({
entryPoints: {
client: "web/boot.ts",
client: mainScript,
service_worker: "web/service_worker.ts",
worker: "plugos/environments/sandbox_worker.ts",
},
outdir: "./dist_bundle/web",
outdir: distDir,
absWorkingDir: Deno.cwd(),
bundle: true,
treeShaking: true,
@ -107,12 +112,18 @@ async function bundle(watch: boolean): Promise<void> {
],
}),
]);
await prepareAssets("dist_bundle");
await prepareAssets(distDir);
if (type === "web") {
await bundleFolder("dist_bundle", "dist/asset_bundle.json");
}
building = false;
console.log("Built!");
}
}
if (import.meta.main) {
const args = flags.parse(Deno.args, {
boolean: ["watch"],
alias: { w: "watch" },
@ -120,8 +131,8 @@ const args = flags.parse(Deno.args, {
watch: false,
},
});
await bundle(args.watch);
await bundle(args.watch, "web", "dist_bundle/web");
if (!args.watch) {
esbuild.stop();
}
}

20
build_mobile.ts Normal file
View File

@ -0,0 +1,20 @@
import { bundle, esbuild } from "./build.ts";
import * as flags from "https://deno.land/std@0.165.0/flags/mod.ts";
import { copy } from "https://deno.land/std@0.165.0/fs/copy.ts";
if (import.meta.main) {
const args = flags.parse(Deno.args, {
boolean: ["watch"],
alias: { w: "watch" },
default: {
watch: false,
},
});
await bundle(args.watch, "mobile", "mobile/dist");
await copy("mobile/index.html", `mobile/dist/index.html`, {
overwrite: true,
});
if (!args.watch) {
esbuild.stop();
}
}

View File

@ -0,0 +1,7 @@
export const SETTINGS_TEMPLATE =
`This page contains settings for configuring SilverBullet and its plugs. Any changes outside of the yaml block will be overwritten.
\`\`\`yaml
indexPage: index
\`\`\`
`;

View File

@ -26,7 +26,6 @@ export class Space extends EventEmitter<SpaceEvents> {
public async updatePageList() {
const newPageList = await this.fetchPageList();
// console.log("Updating page list", newPageList);
const deletedPages = new Set<string>(this.pageMetaCache.keys());
newPageList.forEach((meta) => {
const pageName = meta.name;

View File

@ -1,4 +1,6 @@
import { SETTINGS_TEMPLATE } from "./settings_template.ts";
import { YAML } from "./deps.ts";
import { Space } from "./spaces/space.ts";
export function safeRun(fn: () => Promise<void>) {
fn().catch((e) => {
@ -42,3 +44,32 @@ export function parseYamlSettings(settingsMarkdown: string): {
return {};
}
}
export async function ensureAndLoadSettings(space: Space) {
try {
await space.getPageMeta("SETTINGS");
} catch {
await space.writePage(
"SETTINGS",
SETTINGS_TEMPLATE,
true,
);
}
const { text: settingsText } = await space.readPage("SETTINGS");
const settings = parseYamlSettings(settingsText);
if (!settings.indexPage) {
settings.indexPage = "index";
}
try {
await space.getPageMeta(settings.indexPage);
} catch {
await space.writePage(
settings.indexPage,
`Welcome to your new space!`,
);
}
return settings;
}

View File

@ -4,7 +4,9 @@
"install": "deno install -f -A --unstable silverbullet.ts",
"test": "deno test -A --unstable",
"build": "deno run -A --unstable --check build_plugs.ts && deno run -A --unstable --check build.ts",
"plugs": "deno run -A --unstable --check build_plugs.ts",
"watch-web": "deno run -A --unstable --check build.ts --watch",
"watch-mobile": "deno run -A --unstable --check build_mobile.ts --watch",
"watch-server": "deno run -A --unstable --check --watch silverbullet.ts",
// The only reason to run a shell script is that deno task doesn't support globs yet (e.g. *.plug.yaml)
"watch-plugs": "deno run -A --unstable --check build_plugs.ts -w",
@ -13,11 +15,14 @@
// Install lezer-generator with "npm install -g @lezer/generator"
"generate": "deno run -A plugos/gen.ts && lezer-generator common/markdown_parser/query.grammar -o common/markdown_parser/parse-query.js",
// Install npm dependencies for desktop app
"desktop:install": "cd desktop && npm install",
"desktop:deps": "cd desktop && npm install",
// Run the desktop app for local development
"desktop:run": "cd desktop && npm start",
// Build the desktop app as a package for this platform
"desktop:build": "deno task build && deno task bundle && cd desktop && npm run make"
"desktop:build": "deno task build && deno task bundle && cd desktop && npm run make",
// Mobile
"mobile:deps": "cd mobile && npm install && npx cap sync",
"mobile:build": "deno run -A --unstable --check build_mobile.ts && cd mobile && npx cap copy && npx cap open ios"
},
"compilerOptions": {

View File

@ -17,6 +17,9 @@
"$sb/": "./plug-api/",
"handlebars": "https://esm.sh/handlebars",
"@lezer/lr": "https://esm.sh/@lezer/lr@1.2.5?external=@lezer/common",
"yaml": "https://deno.land/std/encoding/yaml.ts"
"yaml": "https://deno.land/std/encoding/yaml.ts",
"@capacitor/core": "https://esm.sh/@capacitor/core@4.6.1",
"@capacitor/filesystem": "https://esm.sh/@capacitor/filesystem@4.1.4?external=@capacitor/core"
}
}

5
mobile/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
node_modules/
*.map
.DS_Store
.sourcemaps
dist/

12
mobile/README.md Normal file
View File

@ -0,0 +1,12 @@
## Created with Capacitor Create App
This app was created using [`@capacitor/create-app`](https://github.com/ionic-team/create-capacitor-app),
and comes with a very minimal shell for building an app.
### Running this example
To run the provided example, you can use `npm start` command.
```bash
npm start
```

102
mobile/boot.ts Normal file
View File

@ -0,0 +1,102 @@
import { Editor } from "../web/editor.tsx";
import { ensureAndLoadSettings, safeRun } from "../common/util.ts";
import { Space } from "../common/spaces/space.ts";
import { PlugSpacePrimitives } from "../server/hooks/plug_space_primitives.ts";
import { PageNamespaceHook } from "../server/hooks/page_namespace.ts";
import { SilverBulletHooks } from "../common/manifest.ts";
import { System } from "../plugos/system.ts";
import { BuiltinSettings } from "../web/types.ts";
import { Directory } from "./deps.ts";
import { CapacitorSpacePrimitives } from "./spaces/capacitor_space_primitives.ts";
import { AssetBundlePlugSpacePrimitives } from "../common/spaces/asset_bundle_space_primitives.ts";
import assetBundle from "../dist/asset_bundle.json" assert { type: "json" };
import { AssetBundle } from "../plugos/asset_bundle/bundle.ts";
import {
ensureTable as ensureStoreTable,
storeSyscalls,
} from "../plugos/syscalls/store.sqlite.ts";
import { CapacitorDb } from "../plugos/sqlite/capacitor_sqlite.ts";
import {
ensureTable as ensurePageIndexTable,
pageIndexSyscalls,
} from "../server/syscalls/index.ts";
import {
ensureFTSTable,
fullTextSearchSyscalls,
} from "../plugos/syscalls/fulltext.sqlite.ts";
import { FileMetaSpacePrimitives } from "../common/spaces/file_meta_space_primitives.ts";
import { EventedSpacePrimitives } from "../common/spaces/evented_space_primitives.ts";
import { EventHook } from "../plugos/hooks/event.ts";
import { clientStoreSyscalls } from "./syscalls/clientStore.ts";
import { sandboxFetchSyscalls } from "../plugos/syscalls/fetch.ts";
safeRun(async () => {
// Instantiate a PlugOS system for the client
const system = new System<SilverBulletHooks>();
// Attach the page namespace hook
const namespaceHook = new PageNamespaceHook();
system.addHook(namespaceHook);
// Event hook
const eventHook = new EventHook();
system.addHook(eventHook);
const db = new CapacitorDb("data.db");
await db.init();
// for store
await ensureStoreTable(db, "store");
// for clientStore
await ensureStoreTable(db, "localData");
await ensurePageIndexTable(db);
await ensureFTSTable(db, "fts");
const indexSyscalls = pageIndexSyscalls(db);
const spacePrimitives = new FileMetaSpacePrimitives(
new AssetBundlePlugSpacePrimitives(
new EventedSpacePrimitives(
new PlugSpacePrimitives(
new CapacitorSpacePrimitives(
Directory.Documents,
"",
),
namespaceHook,
),
eventHook,
),
new AssetBundle(assetBundle),
),
indexSyscalls,
);
const serverSpace = new Space(spacePrimitives);
serverSpace.watch();
const settings = await ensureAndLoadSettings(serverSpace) as BuiltinSettings;
// Register some mobile-specific syscall implementations
system.registerSyscalls(
[],
storeSyscalls(db, "store"),
indexSyscalls,
clientStoreSyscalls(db),
fullTextSearchSyscalls(db, "fts"),
sandboxFetchSyscalls(),
);
console.log("Booting...");
const editor = new Editor(
serverSpace,
system,
eventHook,
document.getElementById("sb-root")!,
"",
settings,
);
await editor.init();
});

View File

@ -0,0 +1,21 @@
{
"appId": "md.silverbullet",
"appName": "SilverBullet",
"bundledWebRuntime": false,
"webDir": "dist",
"backgroundColor": "#000",
"ios": {
"contentInset": "always"
},
"plugins": {
"SplashScreen": {
"launchShowDuration": 0
},
"CapacitorSQLite": {
"iosDatabaseLocation": "Library"
},
"CapacitorHttp": {
"enabled": true
}
}
}

3
mobile/deps.ts Normal file
View File

@ -0,0 +1,3 @@
export { Capacitor } from "@capacitor/core";
export { Directory, Encoding, Filesystem } from "@capacitor/filesystem";
export type { WriteFileResult } from "@capacitor/filesystem";

47
mobile/index.html Normal file
View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<base href="/" />
<title>Silver Bullet</title>
<script>
Deno = {
args: [],
build: {
arch: "x86_64",
},
env: {
get(key) {
// return undefined;
},
},
errors: {
AlreadyExists: class extends Error { },
}
};
</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>
</body>
</html>

13
mobile/ios/.gitignore vendored Normal file
View File

@ -0,0 +1,13 @@
App/build
App/Pods
App/Podfile.lock
App/App/public
DerivedData
xcuserdata
# Cordova plugins for Capacitor
capacitor-cordova-ios-plugins
# Generated Config files
App/App/capacitor.config.json
App/App/config.xml

View File

@ -0,0 +1,422 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 48;
objects = {
/* Begin PBXBuildFile section */
2493DD8E3237F110A3166EDB /* Pods_SilverBullet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B2AB7E92525621074064EB5 /* Pods_SilverBullet.framework */; };
2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
0B2AB7E92525621074064EB5 /* Pods_SilverBullet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SilverBullet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
2E06859990AF304187A3CBA8 /* Pods-SilverBullet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SilverBullet.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SilverBullet/Pods-SilverBullet.debug.xcconfig"; sourceTree = "<group>"; };
2E4EFA801479B66C1B2AD68F /* Pods-SilverBullet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SilverBullet.release.xcconfig"; path = "Pods/Target Support Files/Pods-SilverBullet/Pods-SilverBullet.release.xcconfig"; sourceTree = "<group>"; };
2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
504EC3041FED79650016851F /* SilverBullet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SilverBullet.app; sourceTree = BUILT_PRODUCTS_DIR; };
504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
504EC3011FED79650016851F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2493DD8E3237F110A3166EDB /* Pods_SilverBullet.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {
isa = PBXGroup;
children = (
0B2AB7E92525621074064EB5 /* Pods_SilverBullet.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
504EC2FB1FED79650016851F = {
isa = PBXGroup;
children = (
504EC3061FED79650016851F /* App */,
504EC3051FED79650016851F /* Products */,
7F8756D8B27F46E3366F6CEA /* Pods */,
27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
);
sourceTree = "<group>";
};
504EC3051FED79650016851F /* Products */ = {
isa = PBXGroup;
children = (
504EC3041FED79650016851F /* SilverBullet.app */,
);
name = Products;
sourceTree = "<group>";
};
504EC3061FED79650016851F /* App */ = {
isa = PBXGroup;
children = (
50379B222058CBB4000EE86E /* capacitor.config.json */,
504EC3071FED79650016851F /* AppDelegate.swift */,
504EC30B1FED79650016851F /* Main.storyboard */,
504EC30E1FED79650016851F /* Assets.xcassets */,
504EC3101FED79650016851F /* LaunchScreen.storyboard */,
504EC3131FED79650016851F /* Info.plist */,
2FAD9762203C412B000D30F8 /* config.xml */,
50B271D01FEDC1A000F3C39B /* public */,
);
path = App;
sourceTree = "<group>";
};
7F8756D8B27F46E3366F6CEA /* Pods */ = {
isa = PBXGroup;
children = (
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,
2E06859990AF304187A3CBA8 /* Pods-SilverBullet.debug.xcconfig */,
2E4EFA801479B66C1B2AD68F /* Pods-SilverBullet.release.xcconfig */,
);
name = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
504EC3031FED79650016851F /* SilverBullet */ = {
isa = PBXNativeTarget;
buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "SilverBullet" */;
buildPhases = (
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
504EC3001FED79650016851F /* Sources */,
504EC3011FED79650016851F /* Frameworks */,
504EC3021FED79650016851F /* Resources */,
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = SilverBullet;
productName = App;
productReference = 504EC3041FED79650016851F /* SilverBullet.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
504EC2FC1FED79650016851F /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 0920;
TargetAttributes = {
504EC3031FED79650016851F = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 504EC2FB1FED79650016851F;
productRefGroup = 504EC3051FED79650016851F /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
504EC3031FED79650016851F /* SilverBullet */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
504EC3021FED79650016851F /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
50B271D11FEDC1A000F3C39B /* public in Resources */,
504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-SilverBullet-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SilverBullet/Pods-SilverBullet-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
504EC3001FED79650016851F /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
504EC30B1FED79650016851F /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC30C1FED79650016851F /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
504EC3111FED79650016851F /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
504EC3141FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
504EC3151FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
504EC3171FED79650016851F /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 2E06859990AF304187A3CBA8 /* Pods-SilverBullet.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = Z92J6WM6X8;
INFOPLIST_FILE = App/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SilverBullet;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
PRODUCT_BUNDLE_IDENTIFIER = md.silverbullet;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
504EC3181FED79650016851F /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 2E4EFA801479B66C1B2AD68F /* Pods-SilverBullet.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = Z92J6WM6X8;
INFOPLIST_FILE = App/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = SilverBullet;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.productivity";
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = md.silverbullet;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3141FED79650016851F /* Debug */,
504EC3151FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "SilverBullet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
504EC3171FED79650016851F /* Debug */,
504EC3181FED79650016851F /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 504EC2FC1FED79650016851F /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:App.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:App.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,49 @@
import UIKit
import Capacitor
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
// Called when the app was launched with a url. Feel free to add additional processing here,
// but if you want the App API to support tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
}
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
// Called when the app was launched with an activity, including Universal Links.
// Feel free to add additional processing here, but if you want the App API to support
// tracking app url opens, make sure to keep this call
return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
}
}

View File

@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "splash-2732x2732-2.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "splash-2732x2732-1.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "splash-2732x2732.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<imageView key="view" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" id="snD-IY-ifK">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</imageView>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="Splash" width="1366" height="1366"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14111" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
</dependencies>
<scenes>
<!--Bridge View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="CAPBridgeViewController" customModule="Capacitor" sceneMemberID="viewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>SilverBullet</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>WKAppBoundDomains</key>
<array>
<string>192.168.0.3</string>
<string>127.0.0.1</string>
<string>localhost</string>
</array>
</dict>
</plist>

28
mobile/ios/App/Podfile Normal file
View File

@ -0,0 +1,28 @@
require_relative '../../node_modules/@capacitor/ios/scripts/pods_helpers'
platform :ios, '13.0'
use_frameworks!
# workaround to avoid Xcode caching of Pods that requires
# Product -> Clean Build Folder after new Cordova plugins installed
# Requires CocoaPods 1.6 or newer
install! 'cocoapods', :disable_input_output_paths => true
def capacitor_pods
pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
pod 'CapacitorCommunitySqlite', :path => '../../node_modules/@capacitor-community/sqlite'
pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
pod 'CapacitorFilesystem', :path => '../../node_modules/@capacitor/filesystem'
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
end
target 'SilverBullet' do
capacitor_pods
# Add your Pods here
end
post_install do |installer|
assertDeploymentTarget(installer)
end

3691
mobile/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
mobile/package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "capacitor-app",
"version": "1.0.0",
"description": "An Amazing Capacitor App",
"main": "index.js",
"keywords": [
"capacitor",
"mobile"
],
"scripts": {
"start": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@capacitor-community/sqlite": "^4.6.0",
"@capacitor/android": "^4.6.1",
"@capacitor/app": "^4.1.1",
"@capacitor/core": "latest",
"@capacitor/filesystem": "^4.1.4",
"@capacitor/ios": "^4.6.1",
"@capacitor/keyboard": "^4.1.0",
"@capacitor/splash-screen": "latest",
"cordova-res": "^0.15.4"
},
"devDependencies": {
"@capacitor/cli": "latest",
"vite": "^2.9.13"
},
"author": "",
"license": "ISC"
}

BIN
mobile/resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 KiB

View File

@ -0,0 +1,158 @@
import {
FileData,
FileEncoding,
SpacePrimitives,
} from "../../common/spaces/space_primitives.ts";
import type { FileMeta } from "../../common/types.ts";
import {
base64Decode,
base64Encode,
} from "../../plugos/asset_bundle/base64.ts";
import type { Plug } from "../../plugos/plug.ts";
import { Directory, Encoding, Filesystem } from "../deps.ts";
import { mime } from "../../plugos/deps.ts";
export class CapacitorSpacePrimitives implements SpacePrimitives {
constructor(readonly source: Directory, readonly root: string) {
}
async fetchFileList(): Promise<FileMeta[]> {
const allFiles: FileMeta[] = [];
const directory = this.source;
const root = this.root;
async function readAllFiles(dir: string) {
const files = await Filesystem.readdir({
path: `${root}/${dir}`,
directory,
});
for (const file of files.files) {
if (file.type === "file") {
allFiles.push({
name: `${dir}/${file.name}`.substring(1),
lastModified: file.mtime,
perm: "rw",
contentType: mime.getType(file.name) || "application/octet-stream",
size: file.size,
});
} else { // Directory
await readAllFiles(`${dir}/${file.name}`);
}
}
}
await readAllFiles("");
console.log("allFiles", allFiles);
return allFiles;
}
async readFile(
name: string,
encoding: FileEncoding,
): Promise<{ data: FileData; meta: FileMeta }> {
let data: FileData | undefined;
try {
switch (encoding) {
case "string":
data = (await Filesystem.readFile({
path: this.root + name,
directory: this.source,
encoding: Encoding.UTF8,
})).data;
break;
case "arraybuffer": {
const b64Data = (await Filesystem.readFile({
path: this.root + name,
directory: this.source,
})).data;
data = base64Decode(b64Data);
break;
}
case "dataurl": {
const b64Data = (await Filesystem.readFile({
path: this.root + name,
directory: this.source,
})).data;
data = `data:${
mime.getType(name) || "application/octet-stream"
};base64,${b64Data}`;
break;
}
}
return {
data,
meta: await this.getFileMeta(name),
};
} catch (e: any) {
throw new Error(`Page not found`);
}
}
async getFileMeta(name: string): Promise<FileMeta> {
try {
const statResult = await Filesystem.stat({
path: this.root + name,
directory: this.source,
});
return {
name,
contentType: mime.getType(name) || "application/octet-stream",
lastModified: statResult.mtime,
perm: "rw",
size: statResult.size,
};
} catch (e: any) {
console.error("Error getting file meta", e.message);
throw new Error(`Page not found`);
}
}
async writeFile(
name: string,
encoding: FileEncoding,
data: FileData,
): Promise<FileMeta> {
switch (encoding) {
case "string":
await Filesystem.writeFile({
path: this.root + name,
directory: this.source,
encoding: Encoding.UTF8,
data: data as string,
recursive: true,
});
break;
case "arraybuffer":
await Filesystem.writeFile({
path: this.root + name,
directory: this.source,
data: base64Encode(new Uint8Array(data as ArrayBuffer)),
recursive: true,
});
break;
case "dataurl":
await Filesystem.writeFile({
path: this.root + name,
directory: this.source,
data: (data as string).split(";base64,")[1],
recursive: true,
});
break;
}
return this.getFileMeta(name);
}
async deleteFile(name: string): Promise<void> {
await Filesystem.deleteFile({
path: this.root + name,
directory: this.source,
});
}
proxySyscall(plug: Plug<any>, name: string, args: any[]): Promise<any> {
return plug.syscall(name, args);
}
invokeFunction(
plug: Plug<any>,
_env: string,
name: string,
args: any[],
): Promise<any> {
return plug.invoke(name, args);
}
}

View File

@ -0,0 +1,14 @@
import { proxySyscalls } from "../../plugos/syscalls/transport.ts";
import { SysCallMapping } from "../../plugos/system.ts";
import { storeSyscalls } from "../../plugos/syscalls/store.sqlite.ts";
import { ISQLite } from "../../plugos/sqlite/sqlite_interface.ts";
export function clientStoreSyscalls(db: ISQLite): SysCallMapping {
const storeCalls = storeSyscalls(db, "localData");
return proxySyscalls(
["clientStore.get", "clientStore.set", "clientStore.delete"],
(ctx, name, ...args) => {
return storeCalls[name.replace("clientStore.", "store.")](ctx, ...args);
},
);
}

View File

@ -0,0 +1,42 @@
import { base64Decode } from "../../plugos/asset_bundle/base64.ts";
export type SandboxFetchRequest = {
method?: string;
headers?: Record<string, string>;
};
export type SandboxFetchResponse = {
ok: boolean;
status: number;
headers: Record<string, string>;
// We base64 encode the body because the body can be binary data that we have to push through the worker boundary
base64Body: string;
};
export function sandboxFetch(
url: string,
options?: SandboxFetchRequest,
): Promise<SandboxFetchResponse> {
// @ts-ignore: monkey patching fetch
return syscall("sandboxFetch.fetch", url, options);
}
export function monkeyPatchFetch() {
// @ts-ignore: monkey patching fetch
globalThis.fetch = async function (
url: string,
init?: RequestInit,
): Promise<Response> {
const r = await sandboxFetch(
url,
init && {
method: init.method,
headers: init.headers as Record<string, string>,
},
);
return new Response(base64Decode(r.base64Body), {
status: r.status,
headers: r.headers,
});
};
}

View File

@ -5,3 +5,5 @@ export { expandGlobSync } from "https://deno.land/std@0.165.0/fs/mod.ts";
export { mime } from "https://deno.land/x/mimetypes@v1.0.0/mod.ts";
export { default as cacheDir } from "https://deno.land/x/cache_dir@0.2.0/mod.ts";
export * as flags from "https://deno.land/std@0.165.0/flags/mod.ts";
export { CapacitorSQLite } from "https://esm.sh/@capacitor-community/sqlite@4.6.0?external=@capacitor/core";

View File

@ -48,8 +48,8 @@ export function createSandbox(plug: Plug<any>) {
type: "module",
deno: {
permissions: {
// Allow network access and servers (main use case: fetch)
net: true,
// Disallow network access
net: false,
// This is required for console logging to work, apparently?
env: true,
// No talking to native code

View File

@ -159,3 +159,7 @@ self.addEventListener("message", (event: { data: WorkerMessage }) => {
}
});
});
import { monkeyPatchFetch } from "../../plug-api/plugos-syscall/fetch.ts";
monkeyPatchFetch();

View File

@ -1,3 +1,3 @@
{
"worker.js": "data:application/javascript;base64,KCgpID0+IHsgdmFyIG1vZD0oKCk9PntmdW5jdGlvbiBjKHMpe3MoKS5jYXRjaChlPT57Y29uc29sZS5lcnJvcigiQ2F1Z2h0IGVycm9yIixlLm1lc3NhZ2UpfSl9dmFyIGE9Y2xhc3N7Y29uc3RydWN0b3IoZSxuPSEwKXt0aGlzLnByaW50PW4sdGhpcy5jYWxsYmFjaz1lfWxvZyguLi5lKXt0aGlzLnB1c2goImxvZyIsZSl9d2FybiguLi5lKXt0aGlzLnB1c2goIndhcm4iLGUpfWVycm9yKC4uLmUpe3RoaXMucHVzaCgiZXJyb3IiLGUpfWluZm8oLi4uZSl7dGhpcy5wdXNoKCJpbmZvIixlKX1wdXNoKGUsbil7dGhpcy5jYWxsYmFjayhlLHRoaXMubG9nTWVzc2FnZShuKSksdGhpcy5wcmludCYmY29uc29sZVtlXSguLi5uKX1sb2dNZXNzYWdlKGUpe2xldCBuPVtdO2ZvcihsZXQgciBvZiBlKXN3aXRjaCh0eXBlb2Ygcil7Y2FzZSJzdHJpbmciOmNhc2UibnVtYmVyIjpuLnB1c2goIiIrcik7YnJlYWs7Y2FzZSJ1bmRlZmluZWQiOm4ucHVzaCgidW5kZWZpbmVkIik7YnJlYWs7ZGVmYXVsdDp0cnl7bGV0IG89SlNPTi5zdHJpbmdpZnkocixudWxsLDIpO28ubGVuZ3RoPjUwMCYmKG89by5zdWJzdHJpbmcoMCw1MDApKyIuLi4iKSxuLnB1c2gobyl9Y2F0Y2h7bi5wdXNoKCJbY2lyY3VsYXIgb2JqZWN0XSIpfX1yZXR1cm4gbi5qb2luKCIgIil9fTt0eXBlb2YgRGVubz4idSImJihzZWxmLkRlbm89e2FyZ3M6W10sYnVpbGQ6e2FyY2g6Ing4Nl82NCJ9LGVudjp7Z2V0KCl7fX19KTt2YXIgZD1uZXcgTWFwLGk9bmV3IE1hcDtmdW5jdGlvbiB0KHMpe3R5cGVvZiB3aW5kb3c8InUiJiZ3aW5kb3cucGFyZW50IT09d2luZG93P3dpbmRvdy5wYXJlbnQucG9zdE1lc3NhZ2UocywiKiIpOnNlbGYucG9zdE1lc3NhZ2Uocyl9dmFyIGw9MDtzZWxmLnN5c2NhbGw9YXN5bmMocywuLi5lKT0+YXdhaXQgbmV3IFByb21pc2UoKG4scik9PntsKyssaS5zZXQobCx7cmVzb2x2ZTpuLHJlamVjdDpyfSksdCh7dHlwZToic3lzY2FsbCIsaWQ6bCxuYW1lOnMsYXJnczplfSl9KTt2YXIgdT1uZXcgTWFwO3NlbGYucmVxdWlyZT1zPT57bGV0IGU9dS5nZXQocyk7aWYoIWUpdGhyb3cgbmV3IEVycm9yKGBEeW5hbWljYWxseSBpbXBvcnRpbmcgbm9uLXByZWxvYWRlZCBsaWJyYXJ5ICR7c31gKTtyZXR1cm4gZX07c2VsZi5jb25zb2xlPW5ldyBhKChzLGUpPT57dCh7dHlwZToibG9nIixsZXZlbDpzLG1lc3NhZ2U6ZX0pfSwhMSk7ZnVuY3Rpb24gZyhzKXtyZXR1cm5gcmV0dXJuICgke3N9KVsiZGVmYXVsdCJdYH1zZWxmLmFkZEV2ZW50TGlzdGVuZXIoIm1lc3NhZ2UiLHM9PntjKGFzeW5jKCk9PntsZXQgZT1zLmRhdGE7c3dpdGNoKGUudHlwZSl7Y2FzZSJsb2FkIjp7bGV0IG49bmV3IEZ1bmN0aW9uKGcoZS5jb2RlKSk7ZC5zZXQoZS5uYW1lLG4oKSksdCh7dHlwZToiaW5pdGVkIixuYW1lOmUubmFtZX0pfWJyZWFrO2Nhc2UibG9hZC1kZXBlbmRlbmN5Ijp7bGV0IHI9bmV3IEZ1bmN0aW9uKGByZXR1cm4gJHtlLmNvZGV9YCkoKTt1LnNldChlLm5hbWUsciksdCh7dHlwZToiZGVwZW5kZW5jeS1pbml0ZWQiLG5hbWU6ZS5uYW1lfSl9YnJlYWs7Y2FzZSJpbnZva2UiOntsZXQgbj1kLmdldChlLm5hbWUpO2lmKCFuKXRocm93IG5ldyBFcnJvcihgRnVuY3Rpb24gbm90IGxvYWRlZDogJHtlLm5hbWV9YCk7dHJ5e2xldCByPWF3YWl0IFByb21pc2UucmVzb2x2ZShuKC4uLmUuYXJnc3x8W10pKTt0KHt0eXBlOiJyZXN1bHQiLGlkOmUuaWQscmVzdWx0OnJ9KX1jYXRjaChyKXt0KHt0eXBlOiJyZXN1bHQiLGlkOmUuaWQsZXJyb3I6ci5tZXNzYWdlLHN0YWNrOnIuc3RhY2t9KX19YnJlYWs7Y2FzZSJzeXNjYWxsLXJlc3BvbnNlIjp7bGV0IG49ZS5pZCxyPWkuZ2V0KG4pO2lmKCFyKXRocm93IGNvbnNvbGUubG9nKCJDdXJyZW50IG91dHN0YW5kaW5nIHJlcXVlc3RzIixpLCJsb29raW5nIHVwIixuKSxFcnJvcigiSW52YWxpZCByZXF1ZXN0IGlkIik7aS5kZWxldGUobiksZS5lcnJvcj9yLnJlamVjdChuZXcgRXJyb3IoZS5lcnJvcikpOnIucmVzb2x2ZShlLnJlc3VsdCl9YnJlYWt9fSl9KTt9KSgpOwogcmV0dXJuIG1vZDt9KSgp"
"worker.js": "data:application/javascript;base64,KCgpID0+IHsgdmFyIG1vZD0oKCk9PntmdW5jdGlvbiBsKHQpe3QoKS5jYXRjaChlPT57Y29uc29sZS5lcnJvcigiQ2F1Z2h0IGVycm9yIixlLm1lc3NhZ2UpfSl9dmFyIGE9Y2xhc3N7Y29uc3RydWN0b3IoZSxuPSEwKXt0aGlzLnByaW50PW4sdGhpcy5jYWxsYmFjaz1lfWxvZyguLi5lKXt0aGlzLnB1c2goImxvZyIsZSl9d2FybiguLi5lKXt0aGlzLnB1c2goIndhcm4iLGUpfWVycm9yKC4uLmUpe3RoaXMucHVzaCgiZXJyb3IiLGUpfWluZm8oLi4uZSl7dGhpcy5wdXNoKCJpbmZvIixlKX1wdXNoKGUsbil7dGhpcy5jYWxsYmFjayhlLHRoaXMubG9nTWVzc2FnZShuKSksdGhpcy5wcmludCYmY29uc29sZVtlXSguLi5uKX1sb2dNZXNzYWdlKGUpe2xldCBuPVtdO2ZvcihsZXQgciBvZiBlKXN3aXRjaCh0eXBlb2Ygcil7Y2FzZSJzdHJpbmciOmNhc2UibnVtYmVyIjpuLnB1c2goIiIrcik7YnJlYWs7Y2FzZSJ1bmRlZmluZWQiOm4ucHVzaCgidW5kZWZpbmVkIik7YnJlYWs7ZGVmYXVsdDp0cnl7bGV0IHM9SlNPTi5zdHJpbmdpZnkocixudWxsLDIpO3MubGVuZ3RoPjUwMCYmKHM9cy5zdWJzdHJpbmcoMCw1MDApKyIuLi4iKSxuLnB1c2gocyl9Y2F0Y2h7bi5wdXNoKCJbY2lyY3VsYXIgb2JqZWN0XSIpfX1yZXR1cm4gbi5qb2luKCIgIil9fTtmdW5jdGlvbiBkKHQpe2xldCBlPWF0b2IodCksbj1lLmxlbmd0aCxyPW5ldyBVaW50OEFycmF5KG4pO2ZvcihsZXQgcz0wO3M8bjtzKyspcltzXT1lLmNoYXJDb2RlQXQocyk7cmV0dXJuIHJ9ZnVuY3Rpb24geSh0LGUpe3JldHVybiBzeXNjYWxsKCJzYW5kYm94RmV0Y2guZmV0Y2giLHQsZSl9ZnVuY3Rpb24gdSgpe2dsb2JhbFRoaXMuZmV0Y2g9YXN5bmMgZnVuY3Rpb24odCxlKXtsZXQgbj1hd2FpdCB5KHQsZSYme21ldGhvZDplLm1ldGhvZCxoZWFkZXJzOmUuaGVhZGVyc30pO3JldHVybiBuZXcgUmVzcG9uc2UoZChuLmJhc2U2NEJvZHkpLHtzdGF0dXM6bi5zdGF0dXMsaGVhZGVyczpuLmhlYWRlcnN9KX19dHlwZW9mIERlbm8+InUiJiYoc2VsZi5EZW5vPXthcmdzOltdLGJ1aWxkOnthcmNoOiJ4ODZfNjQifSxlbnY6e2dldCgpe319fSk7dmFyIGc9bmV3IE1hcCxpPW5ldyBNYXA7ZnVuY3Rpb24gbyh0KXt0eXBlb2Ygd2luZG93PCJ1IiYmd2luZG93LnBhcmVudCE9PXdpbmRvdz93aW5kb3cucGFyZW50LnBvc3RNZXNzYWdlKHQsIioiKTpzZWxmLnBvc3RNZXNzYWdlKHQpfXZhciBjPTA7c2VsZi5zeXNjYWxsPWFzeW5jKHQsLi4uZSk9PmF3YWl0IG5ldyBQcm9taXNlKChuLHIpPT57YysrLGkuc2V0KGMse3Jlc29sdmU6bixyZWplY3Q6cn0pLG8oe3R5cGU6InN5c2NhbGwiLGlkOmMsbmFtZTp0LGFyZ3M6ZX0pfSk7dmFyIHA9bmV3IE1hcDtzZWxmLnJlcXVpcmU9dD0+e2xldCBlPXAuZ2V0KHQpO2lmKCFlKXRocm93IG5ldyBFcnJvcihgRHluYW1pY2FsbHkgaW1wb3J0aW5nIG5vbi1wcmVsb2FkZWQgbGlicmFyeSAke3R9YCk7cmV0dXJuIGV9O3NlbGYuY29uc29sZT1uZXcgYSgodCxlKT0+e28oe3R5cGU6ImxvZyIsbGV2ZWw6dCxtZXNzYWdlOmV9KX0sITEpO2Z1bmN0aW9uIGgodCl7cmV0dXJuYHJldHVybiAoJHt0fSlbImRlZmF1bHQiXWB9c2VsZi5hZGRFdmVudExpc3RlbmVyKCJtZXNzYWdlIix0PT57bChhc3luYygpPT57bGV0IGU9dC5kYXRhO3N3aXRjaChlLnR5cGUpe2Nhc2UibG9hZCI6e2xldCBuPW5ldyBGdW5jdGlvbihoKGUuY29kZSkpO2cuc2V0KGUubmFtZSxuKCkpLG8oe3R5cGU6ImluaXRlZCIsbmFtZTplLm5hbWV9KX1icmVhaztjYXNlImxvYWQtZGVwZW5kZW5jeSI6e2xldCByPW5ldyBGdW5jdGlvbihgcmV0dXJuICR7ZS5jb2RlfWApKCk7cC5zZXQoZS5uYW1lLHIpLG8oe3R5cGU6ImRlcGVuZGVuY3ktaW5pdGVkIixuYW1lOmUubmFtZX0pfWJyZWFrO2Nhc2UiaW52b2tlIjp7bGV0IG49Zy5nZXQoZS5uYW1lKTtpZighbil0aHJvdyBuZXcgRXJyb3IoYEZ1bmN0aW9uIG5vdCBsb2FkZWQ6ICR7ZS5uYW1lfWApO3RyeXtsZXQgcj1hd2FpdCBQcm9taXNlLnJlc29sdmUobiguLi5lLmFyZ3N8fFtdKSk7byh7dHlwZToicmVzdWx0IixpZDplLmlkLHJlc3VsdDpyfSl9Y2F0Y2gocil7byh7dHlwZToicmVzdWx0IixpZDplLmlkLGVycm9yOnIubWVzc2FnZSxzdGFjazpyLnN0YWNrfSl9fWJyZWFrO2Nhc2Uic3lzY2FsbC1yZXNwb25zZSI6e2xldCBuPWUuaWQscj1pLmdldChuKTtpZighcil0aHJvdyBjb25zb2xlLmxvZygiQ3VycmVudCBvdXRzdGFuZGluZyByZXF1ZXN0cyIsaSwibG9va2luZyB1cCIsbiksRXJyb3IoIkludmFsaWQgcmVxdWVzdCBpZCIpO2kuZGVsZXRlKG4pLGUuZXJyb3I/ci5yZWplY3QobmV3IEVycm9yKGUuZXJyb3IpKTpyLnJlc29sdmUoZS5yZXN1bHQpfWJyZWFrfX0pfSk7dSgpO30pKCk7CiByZXR1cm4gbW9kO30pKCk="
}

View File

@ -9,7 +9,7 @@ export class Plug<HookT> {
public manifest?: Manifest<HookT>;
public assets?: AssetBundle;
private sandboxFactory: (plug: Plug<HookT>) => Sandbox;
readonly runtimeEnv: RuntimeEnvironment;
readonly runtimeEnv?: RuntimeEnvironment;
grantedPermissions: string[] = [];
name: string;
version: number;
@ -23,7 +23,7 @@ export class Plug<HookT> {
this.name = name;
this.sandboxFactory = sandboxFactory;
// this.sandbox = sandboxFactory(this);
this.runtimeEnv = system.runtimeEnv;
this.runtimeEnv = system.env;
this.version = new Date().getTime();
}
@ -69,7 +69,7 @@ export class Plug<HookT> {
if (!funDef) {
throw new Error(`Function ${name} not found in manifest`);
}
return !funDef.env || funDef.env === this.runtimeEnv;
return !funDef.env || !this.runtimeEnv || funDef.env === this.runtimeEnv;
}
async invoke(name: string, args: any[]): Promise<any> {

View File

@ -1,9 +1,10 @@
import { AssetBundle } from "../asset_bundle/bundle.ts";
import { ISQLite } from "./sqlite_interface.ts";
import workerBundleJson from "./worker_bundle.json" assert { type: "json" };
const workerBundle = new AssetBundle(workerBundleJson);
export class AsyncSQLite {
export class AsyncSQLite implements ISQLite {
worker: Worker;
requestId = 0;
outstandingRequests = new Map<

View File

@ -0,0 +1,36 @@
import { Capacitor } from "../../mobile/deps.ts";
import { CapacitorSQLite } from "../deps.ts";
import { ISQLite } from "./sqlite_interface.ts";
export class CapacitorDb implements ISQLite {
constructor(readonly name: string) {
}
async init() {
await CapacitorSQLite.createConnection({
database: this.name,
});
await CapacitorSQLite.open({
database: this.name,
});
}
async query(sql: string, ...args: any[]) {
const result = await CapacitorSQLite.query({
statement: sql,
database: this.name,
values: args,
});
if (Capacitor.getPlatform() === "ios") {
return result.values!.slice(1);
}
return result.values!;
}
async execute(sql: string, ...args: any[]): Promise<number> {
return (await CapacitorSQLite.run({
statement: sql,
database: this.name,
values: args,
})).changes!.changes!;
}
}

View File

@ -0,0 +1,4 @@
export interface ISQLite {
execute(query: string, ...params: any[]): Promise<number>;
query(query: string, ...params: any[]): Promise<any[]>;
}

38
plugos/syscalls/fetch.ts Normal file
View File

@ -0,0 +1,38 @@
import type {
SandboxFetchRequest,
SandboxFetchResponse,
} from "../../plug-api/plugos-syscall/fetch.ts";
import { base64Encode } from "../asset_bundle/base64.ts";
import { SysCallMapping } from "../system.ts";
export async function sandboxFetch(
url: string,
req?: SandboxFetchRequest,
): Promise<SandboxFetchResponse> {
const result = await fetch(
url,
req && {
method: req.method,
headers: req.headers,
},
);
const body = await (await result.blob()).arrayBuffer();
return {
ok: result.ok,
status: result.status,
headers: Object.fromEntries(result.headers.entries()),
base64Body: base64Encode(new Uint8Array(body)),
};
}
export function sandboxFetchSyscalls(): SysCallMapping {
return {
"sandboxFetch.fetch": (
_ctx,
url: string,
options?: SandboxFetchRequest,
): Promise<SandboxFetchResponse> => {
return sandboxFetch(url, options);
},
};
}

View File

@ -1,9 +1,9 @@
import { FullTextSearchOptions } from "../../plug-api/plugos-syscall/fulltext.ts";
import { AsyncSQLite } from "../../plugos/sqlite/async_sqlite.ts";
import { ISQLite } from "../sqlite/sqlite_interface.ts";
import { SysCallMapping } from "../system.ts";
export async function ensureFTSTable(
db: AsyncSQLite,
db: ISQLite,
tableName: string,
) {
const result = await db.query(
@ -20,7 +20,7 @@ export async function ensureFTSTable(
}
export function fullTextSearchSyscalls(
db: AsyncSQLite,
db: ISQLite,
tableName: string,
): SysCallMapping {
return {

View File

@ -1,7 +1,7 @@
import { assertEquals } from "../../test_deps.ts";
import { createSandbox } from "../environments/deno_sandbox.ts";
import { System } from "../system.ts";
import { ensureTable, storeSyscalls } from "./store.deno.ts";
import { ensureTable, storeSyscalls } from "./store.sqlite.ts";
import { AsyncSQLite } from "../sqlite/async_sqlite.ts";
Deno.test("Test store", async () => {

View File

@ -1,4 +1,4 @@
import { AsyncSQLite } from "../sqlite/async_sqlite.ts";
import { ISQLite } from "../sqlite/sqlite_interface.ts";
import { SysCallMapping } from "../system.ts";
export type Item = {
@ -12,7 +12,7 @@ export type KV = {
value: any;
};
export async function ensureTable(db: AsyncSQLite, tableName: string) {
export async function ensureTable(db: ISQLite, tableName: string) {
const result = await db.query(
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
tableName,
@ -72,7 +72,7 @@ export function queryToSql(
}
export function storeSyscalls(
db: AsyncSQLite,
db: ISQLite,
tableName: string,
): SysCallMapping {
const apiObj: SysCallMapping = {

View File

@ -30,14 +30,12 @@ type Syscall = {
};
export class System<HookT> extends EventEmitter<SystemEvents<HookT>> {
readonly runtimeEnv: RuntimeEnvironment;
protected plugs = new Map<string, Plug<HookT>>();
protected registeredSyscalls = new Map<string, Syscall>();
protected enabledHooks = new Set<Hook<HookT>>();
constructor(env: RuntimeEnvironment) {
constructor(readonly env?: RuntimeEnvironment) {
super();
this.runtimeEnv = env;
}
get loadedPlugs(): Map<string, Plug<HookT>> {

View File

@ -6,6 +6,8 @@ import { renderToText, replaceNodesMatching } from "$sb/lib/tree.ts";
import type { FileMeta } from "../../common/types.ts";
import { parseMarkdown } from "$sb/silverbullet-syscall/markdown.ts";
import { base64EncodedDataUrl } from "../../plugos/asset_bundle/base64.ts";
import { CapacitorHttp } from "@capacitor/core";
import { sandboxFetch } from "../../plug-api/plugos-syscall/fetch.ts";
const pagePrefix = "💭 ";
@ -27,15 +29,16 @@ export async function readFileCloud(
url = `http://${url}`;
}
let text = "";
try {
const r = await fetch(`${url}.md`);
text = await r.text();
if (r.status !== 200) {
if (!r.ok) {
text = `ERROR: ${text}`;
}
} catch (e: any) {
console.error("ERROR", e.message);
text = e.message;
console.error("ERROR thrown", e.message);
text = `ERROR: ${e.message}`;
}
text = await translateLinksWithPrefix(
text,

View File

@ -1,6 +0,0 @@
This page contains settings for configuring SilverBullet and its plugs. Any
changes outside of the yaml block will be overwritten.
```yaml
indexPage: index
```

View File

@ -12,7 +12,7 @@ export class PlugSpacePrimitives implements SpacePrimitives {
constructor(
private wrapped: SpacePrimitives,
private hook: PageNamespaceHook,
private env: string,
private env?: string,
) {}
performOperation(
@ -25,7 +25,7 @@ export class PlugSpacePrimitives implements SpacePrimitives {
) {
if (
operation === type && pageName.match(pattern) &&
(env ? env === this.env : true)
(!this.env || (env && env === this.env))
) {
return plug.invoke(name, [pageName, ...args]);
}

View File

@ -4,7 +4,7 @@ import { SpacePrimitives } from "../common/spaces/space_primitives.ts";
import { EndpointHook } from "../plugos/hooks/endpoint.ts";
import { AssetBundle } from "../plugos/asset_bundle/bundle.ts";
import { SpaceSystem } from "./space_system.ts";
import { parseYamlSettings } from "../common/util.ts";
import { ensureAndLoadSettings } from "../common/util.ts";
export type ServerOptions = {
hostname: string;
@ -66,7 +66,7 @@ export class HttpServer {
async start() {
await this.systemBoot.start();
await this.systemBoot.ensureSpaceIndex();
await this.ensureAndLoadSettings();
await ensureAndLoadSettings(this.systemBoot.space);
this.addPasswordAuth(this.app);
@ -149,34 +149,6 @@ export class HttpServer {
);
}
async ensureAndLoadSettings() {
const space = this.systemBoot.space;
try {
await space.getPageMeta("SETTINGS");
} catch {
await space.writePage(
"SETTINGS",
this.systemBoot.assetBundle.readTextFileSync("SETTINGS_template.md"),
true,
);
}
const { text: settingsText } = await space.readPage("SETTINGS");
const settings = parseYamlSettings(settingsText);
if (!settings.indexPage) {
settings.indexPage = "index";
}
try {
await space.getPageMeta(settings.indexPage);
} catch {
await space.writePage(
settings.indexPage,
`Welcome to your new space!`,
);
}
}
private addPasswordAuth(app: Application) {
const excludedPaths = [
"/manifest.json",

View File

@ -21,7 +21,7 @@ import shellSyscalls from "../plugos/syscalls/shell.deno.ts";
import {
ensureTable as ensureStoreTable,
storeSyscalls,
} from "../plugos/syscalls/store.deno.ts";
} from "../plugos/syscalls/store.sqlite.ts";
import { System } from "../plugos/system.ts";
import { PageNamespaceHook } from "./hooks/page_namespace.ts";
import { PlugSpacePrimitives } from "./hooks/plug_space_primitives.ts";
@ -36,6 +36,7 @@ import assetSyscalls from "../plugos/syscalls/asset.ts";
import { AssetBundle } from "../plugos/asset_bundle/bundle.ts";
import { AsyncSQLite } from "../plugos/sqlite/async_sqlite.ts";
import { FileMetaSpacePrimitives } from "../common/spaces/file_meta_space_primitives.ts";
import { sandboxFetchSyscalls } from "../plugos/syscalls/fetch.ts";
export const indexRequiredKey = "$spaceIndexed";
// A composition of a PlugOS system attached to a Space for server-side use
@ -116,6 +117,7 @@ export class SpaceSystem {
systemSyscalls(this.loadPlugsFromSpace.bind(this), this.system),
sandboxSyscalls(this.system),
assetSyscalls(this.system),
sandboxFetchSyscalls(),
);
// Danger zone, these syscalls require requesting specific permissions

View File

@ -1,7 +1,7 @@
// import { Knex } from "knex";
import { SysCallMapping } from "../../plugos/system.ts";
import { Query, queryToSql } from "../../plugos/syscalls/store.deno.ts";
import { AsyncSQLite } from "../../plugos/sqlite/async_sqlite.ts";
import { Query, queryToSql } from "../../plugos/syscalls/store.sqlite.ts";
import { ISQLite } from "../../plugos/sqlite/sqlite_interface.ts";
type Item = {
page: string;
@ -16,7 +16,7 @@ export type KV = {
const tableName = "page_index";
export async function ensureTable(db: AsyncSQLite): Promise<void> {
export async function ensureTable(db: ISQLite): Promise<void> {
const result = await db.query(
`SELECT name FROM sqlite_master WHERE type='table' AND name=?`,
tableName,
@ -32,7 +32,7 @@ export async function ensureTable(db: AsyncSQLite): Promise<void> {
}
}
export function pageIndexSyscalls(db: AsyncSQLite): SysCallMapping {
export function pageIndexSyscalls(db: ISQLite): SysCallMapping {
const apiObj: SysCallMapping = {
"index.set": async (_ctx, page: string, key: string, value: any) => {
await db.execute(

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,

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),
);
},

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();
},
},
{

View File

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
@ -34,10 +35,13 @@
</style>
<link rel="stylesheet" href="/main.css" />
<script type="module" src="/client.js"></script>
<link rel="manifest" href="/manifest.json" -->
<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
web/syscalls/fetch.ts Normal file
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),
);
}

View File

@ -2,12 +2,18 @@ An attempt at documenting the changes/new features introduced in each
release.
---
## Next
* New {[Extract text to new page]} command
* Improvement to listify commands by [Tristan Sokol](https://github.com/silverbulletmd/silverbullet/pull/290)
* {[Extract text to new page]} command by [Tristan Sokol](https://github.com/silverbulletmd/silverbullet/pull/286)
* SQL syntax highlighting in fenced code blocks by [Martin Kraft](https://github.com/silverbulletmd/silverbullet/pull/292)
```sql
select * from my_table;
```
* Merged code for experimental mobile app (iOS only for now)
---