deno fmt
parent
9f6063998f
commit
64e398fd90
|
@ -1,4 +1,3 @@
|
|||
|
||||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
|
||||
|
|
|
@ -14,5 +14,6 @@
|
|||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "denoland.vscode-deno"
|
||||
}
|
||||
},
|
||||
"editor.tabSize": 2
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import {
|
|||
LuaNativeJSFunction,
|
||||
LuaRuntimeError,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import { luaBuildStandardEnv } from "$common/space_lua/stdlib.ts";
|
||||
import { parse as parseLua } from "$common/space_lua/parse.ts";
|
||||
import { evalStatement } from "$common/space_lua/eval.ts";
|
||||
import { jsToLuaValue } from "$common/space_lua/runtime.ts";
|
||||
|
@ -19,6 +18,9 @@ import {
|
|||
import type { ScriptEnvironment } from "$common/space_script.ts";
|
||||
import { luaValueToJS } from "$common/space_lua/runtime.ts";
|
||||
import type { ASTCtx } from "$common/space_lua/ast.ts";
|
||||
import { lua } from "@silverbulletmd/silverbullet/syscalls";
|
||||
import type { ObjectQuery } from "@silverbulletmd/silverbullet/types";
|
||||
import { buildLuaEnv } from "$common/space_lua_api.ts";
|
||||
|
||||
export class SpaceLuaEnvironment {
|
||||
env: LuaEnv = new LuaEnv();
|
||||
|
@ -30,71 +32,17 @@ export class SpaceLuaEnvironment {
|
|||
async reload(system: System<any>, scriptEnv: ScriptEnvironment) {
|
||||
const allScripts: ScriptObject[] = await system.invokeFunction(
|
||||
"index.queryObjects",
|
||||
["space-lua", {}],
|
||||
["space-lua", {
|
||||
// This is a bit silly, but at least makes the order deterministic
|
||||
orderBy: [{ expr: ["attr", "ref"] }],
|
||||
} as ObjectQuery],
|
||||
);
|
||||
// We start from scratch
|
||||
this.env = new LuaEnv(luaBuildStandardEnv());
|
||||
const env = this.env;
|
||||
|
||||
// Expose all syscalls to Lua
|
||||
for (const syscallName of system.registeredSyscalls.keys()) {
|
||||
const [ns, fn] = syscallName.split(".");
|
||||
if (!env.get(ns)) {
|
||||
env.set(ns, new LuaTable());
|
||||
}
|
||||
env.get(ns).set(
|
||||
fn,
|
||||
new LuaNativeJSFunction((...args) => {
|
||||
return system.localSyscall(syscallName, args);
|
||||
}),
|
||||
);
|
||||
}
|
||||
env.set(
|
||||
"command",
|
||||
new LuaBuiltinFunction(
|
||||
(def: LuaTable) => {
|
||||
if (def.get(1) === undefined) {
|
||||
throw new Error("Callback is required");
|
||||
}
|
||||
console.log("Registering Lua command", def.get("name"));
|
||||
scriptEnv.registerCommand(
|
||||
def.toJSObject() as any,
|
||||
async (...args: any[]) => {
|
||||
try {
|
||||
return await def.get(1).call(...args.map(jsToLuaValue));
|
||||
} catch (e: any) {
|
||||
console.error("Lua eval exception", e.message, e.context);
|
||||
if (e.context && e.context.ref) {
|
||||
// We got an error and actually know where it came from, let's navigate there to help debugging
|
||||
const pageRef = parsePageRef(e.context.ref);
|
||||
await system.localSyscall("editor.flashNotification", [
|
||||
`Lua error: ${e.message}`,
|
||||
"error",
|
||||
]);
|
||||
await system.localSyscall("editor.flashNotification", [
|
||||
`Navigating to the place in the code where this error occurred in ${pageRef.page}`,
|
||||
"info",
|
||||
]);
|
||||
await system.localSyscall("editor.navigate", [
|
||||
{
|
||||
page: pageRef.page,
|
||||
pos: pageRef.pos + e.context.from +
|
||||
"```space-lua\n".length,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
this.env = buildLuaEnv(system, scriptEnv);
|
||||
for (const script of allScripts) {
|
||||
try {
|
||||
const ast = parseLua(script.script, { ref: script.ref });
|
||||
// We create a local scope for each script
|
||||
const scriptEnv = new LuaEnv(env);
|
||||
const scriptEnv = new LuaEnv(this.env);
|
||||
await evalStatement(ast, scriptEnv);
|
||||
} catch (e: any) {
|
||||
if (e instanceof LuaRuntimeError) {
|
||||
|
@ -111,9 +59,10 @@ export class SpaceLuaEnvironment {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Find all functions and register them
|
||||
for (const globalName of env.keys()) {
|
||||
const value = env.get(globalName);
|
||||
for (const globalName of this.env.keys()) {
|
||||
const value = this.env.get(globalName);
|
||||
if (value instanceof LuaFunction) {
|
||||
console.log("Now registering Lua function", globalName);
|
||||
scriptEnv.registerFunction({ name: globalName }, (...args: any[]) => {
|
||||
|
|
|
@ -295,7 +295,7 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|||
}
|
||||
}
|
||||
|
||||
toJSObject(): Record<string, any> {
|
||||
asJSObject(): Record<string, any> {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key of this.keys()) {
|
||||
result[key] = luaValueToJS(this.get(key));
|
||||
|
@ -303,6 +303,10 @@ export class LuaTable implements ILuaSettable, ILuaGettable {
|
|||
return result;
|
||||
}
|
||||
|
||||
asJSArray(): any[] {
|
||||
return this.arrayPart.map(luaValueToJS);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
if (this.metatable?.has("__tostring")) {
|
||||
const metaValue = this.metatable.get("__tostring");
|
||||
|
|
|
@ -54,8 +54,7 @@ export const osApi = new LuaTable({
|
|||
"%e": () => date.getDate().toString(),
|
||||
// Hour
|
||||
"%H": () => date.getHours().toString().padStart(2, "0"),
|
||||
"%I": () =>
|
||||
(date.getHours() % 12 || 12).toString().padStart(2, "0"),
|
||||
"%I": () => (date.getHours() % 12 || 12).toString().padStart(2, "0"),
|
||||
// Minute
|
||||
"%M": () => date.getMinutes().toString().padStart(2, "0"),
|
||||
// Second
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import { luaBuildStandardEnv } from "$common/space_lua/stdlib.ts";
|
||||
import { parsePageRef } from "@silverbulletmd/silverbullet/lib/page_ref";
|
||||
import {
|
||||
jsToLuaValue,
|
||||
LuaBuiltinFunction,
|
||||
LuaEnv,
|
||||
LuaNativeJSFunction,
|
||||
LuaTable,
|
||||
} from "$common/space_lua/runtime.ts";
|
||||
import type { System } from "$lib/plugos/system.ts";
|
||||
import type { ScriptEnvironment } from "$common/space_script.ts";
|
||||
|
||||
export function buildLuaEnv(system: System<any>, scriptEnv: ScriptEnvironment) {
|
||||
const env = new LuaEnv(luaBuildStandardEnv());
|
||||
|
||||
// Expose all syscalls to Lua
|
||||
for (const syscallName of system.registeredSyscalls.keys()) {
|
||||
const [ns, fn] = syscallName.split(".");
|
||||
if (!env.get(ns)) {
|
||||
env.set(ns, new LuaTable());
|
||||
}
|
||||
const luaFn = new LuaNativeJSFunction((...args) => {
|
||||
return system.localSyscall(syscallName, args);
|
||||
});
|
||||
// Register the function with the same name as the syscall both in regular and snake_case
|
||||
env.get(ns).set(fn, luaFn);
|
||||
env.get(ns).set(snakeCase(fn), luaFn);
|
||||
}
|
||||
|
||||
// Expose the command registration function to Lua
|
||||
env.set(
|
||||
"command",
|
||||
new LuaBuiltinFunction(
|
||||
(def: LuaTable) => {
|
||||
if (def.get(1) === undefined) {
|
||||
throw new Error("Callback is required");
|
||||
}
|
||||
if (!def.get("name")) {
|
||||
throw new Error("Name is required");
|
||||
}
|
||||
console.log("Registering Lua command", def.get("name"));
|
||||
scriptEnv.registerCommand(
|
||||
def.asJSObject() as any,
|
||||
async (...args: any[]) => {
|
||||
try {
|
||||
return await def.get(1).call(
|
||||
...args.map(jsToLuaValue),
|
||||
);
|
||||
} catch (e: any) {
|
||||
console.error(
|
||||
"Lua eval exception",
|
||||
e.message,
|
||||
e.context,
|
||||
);
|
||||
if (e.context && e.context.ref) {
|
||||
// We got an error and actually know where it came from, let's navigate there to help debugging
|
||||
const pageRef = parsePageRef(e.context.ref);
|
||||
await system.localSyscall(
|
||||
"editor.flashNotification",
|
||||
[
|
||||
`Lua error: ${e.message}`,
|
||||
"error",
|
||||
],
|
||||
);
|
||||
await system.localSyscall(
|
||||
"editor.flashNotification",
|
||||
[
|
||||
`Navigating to the place in the code where this error occurred in ${pageRef.page}`,
|
||||
"info",
|
||||
],
|
||||
);
|
||||
await system.localSyscall("editor.navigate", [
|
||||
{
|
||||
page: pageRef.page,
|
||||
pos: pageRef.pos + e.context.from +
|
||||
"```space-lua\n".length,
|
||||
},
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
return env;
|
||||
}
|
||||
|
||||
function snakeCase(s: string) {
|
||||
return s.replace(/([A-Z])/g, "_$1").toLowerCase();
|
||||
}
|
|
@ -6,4 +6,3 @@ functions:
|
|||
name: "Sync: Now"
|
||||
key: "Alt-Shift-s"
|
||||
mac: "Cmd-Shift-s"
|
||||
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1"
|
||||
/>
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.png" />
|
||||
<title>Login to SilverBullet</title>
|
||||
<style>
|
||||
html,
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-family:
|
||||
-apple-system,
|
||||
BlinkMacSystemFont,
|
||||
"Segoe UI",
|
||||
Roboto,
|
||||
"Helvetica Neue",
|
||||
Arial,
|
||||
"Noto Sans",
|
||||
sans-serif,
|
||||
"Apple Color Emoji",
|
||||
"Segoe UI Emoji",
|
||||
"Segoe UI Symbol",
|
||||
"Noto Color Emoji";
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -50,21 +64,36 @@
|
|||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header>
|
||||
<h1>Login to <img src="/.client/logo.png" style="height: 1ch;" /> SilverBullet</h1>
|
||||
<h1>
|
||||
Login to <img src="/.client/logo.png" style="height: 1ch" />
|
||||
SilverBullet
|
||||
</h1>
|
||||
</header>
|
||||
<form action="/.auth" method="POST" id="login">
|
||||
<div class="error-message"></div>
|
||||
<div>
|
||||
<input type="text" name="username" id="username" autocomplete="off" autocorrect="off" autocapitalize="off"
|
||||
autofocus placeholder="Username" />
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
id="username"
|
||||
autocomplete="off"
|
||||
autocorrect="off"
|
||||
autocapitalize="off"
|
||||
autofocus
|
||||
placeholder="Username"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="password" name="password" id="password" placeholder="Password" />
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
id="password"
|
||||
placeholder="Password"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Login" />
|
||||
|
@ -77,11 +106,13 @@
|
|||
<script>
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
||||
const error = params.get('error');
|
||||
const error = params.get("error");
|
||||
if (error === "0") {
|
||||
document.querySelector('.error-message').innerText = "The sent data was invalid";
|
||||
document.querySelector(".error-message").innerText =
|
||||
"The sent data was invalid";
|
||||
} else if (error === "1") {
|
||||
document.querySelector('.error-message').innerText = "Invalid username or password";
|
||||
document.querySelector(".error-message").innerText =
|
||||
"Invalid username or password";
|
||||
}
|
||||
|
||||
const from = params.get("from");
|
||||
|
@ -95,5 +126,4 @@
|
|||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -147,7 +147,8 @@ export class Client implements ConfigContainer {
|
|||
this.fullSyncCompleted = true;
|
||||
}
|
||||
// Generate a semi-unique prefix for the database so not to reuse databases for different space paths
|
||||
this.dbPrefix = "" + simpleHash(globalThis.silverBulletConfig.spaceFolderPath);
|
||||
this.dbPrefix = "" +
|
||||
simpleHash(globalThis.silverBulletConfig.spaceFolderPath);
|
||||
this.onLoadPageRef = parsePageRefFromURI();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" prefix="og: https://ogp.me/ns#">
|
||||
|
||||
<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" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<base href="/" />
|
||||
<link rel="apple-touch-icon" href="/.client/logo.png">
|
||||
<meta name="theme-color" content="#e1e1e1" media="(prefers-color-scheme: light)">
|
||||
<meta name="theme-color" content="#262626" media="(prefers-color-scheme: dark)">
|
||||
<link rel="apple-touch-icon" href="/.client/logo.png" />
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#e1e1e1"
|
||||
media="(prefers-color-scheme: light)"
|
||||
/>
|
||||
<meta
|
||||
name="theme-color"
|
||||
content="#262626"
|
||||
media="(prefers-color-scheme: dark)"
|
||||
/>
|
||||
<meta name="referrer" content="no-referrer" />
|
||||
|
||||
<title>{{TITLE}}</title>
|
||||
<meta property="og:image" content="/.client/logo.png">
|
||||
<meta property="og:description" content={{DESCRIPTION}}>
|
||||
<meta property="og:image" content="/.client/logo.png" />
|
||||
<meta property="og:description" content="{{DESCRIPTION}}" />
|
||||
|
||||
<script>
|
||||
// Some global variables we need to make this work
|
||||
|
@ -29,7 +38,7 @@
|
|||
},
|
||||
setHasTickScheduled() {
|
||||
// console.log("Not supported");
|
||||
}
|
||||
},
|
||||
},
|
||||
env: {
|
||||
get(key) {
|
||||
|
@ -38,7 +47,7 @@
|
|||
},
|
||||
errors: {
|
||||
AlreadyExists: class extends Error {},
|
||||
}
|
||||
},
|
||||
};
|
||||
window.silverBulletConfig = {
|
||||
// These {{VARIABLES}} are replaced by http_server.ts
|
||||
|
@ -66,7 +75,6 @@
|
|||
|
||||
<body>
|
||||
<div id="sb-root">
|
||||
|
||||
<div id="sb-main">
|
||||
<div id="sb-editor">
|
||||
<div class="cm-editor">
|
||||
|
@ -78,5 +86,4 @@
|
|||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -361,7 +361,11 @@ tbody tr:nth-of-type(even) {
|
|||
}
|
||||
|
||||
.sb-admonition-title {
|
||||
background-color: color-mix(in srgb, var(--admonition-color), transparent 90%)
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--admonition-color),
|
||||
transparent 90%
|
||||
);
|
||||
}
|
||||
|
||||
.sb-admonition-type::before {
|
||||
|
|
|
@ -360,7 +360,7 @@
|
|||
}
|
||||
|
||||
.sb-task-deadline {
|
||||
background-color: rgba(22, 22, 22, 0.07)
|
||||
background-color: rgba(22, 22, 22, 0.07);
|
||||
}
|
||||
|
||||
.sb-line-frontmatter-outside,
|
||||
|
@ -420,7 +420,6 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
// dont apply background color twice for (fenced) code blocks
|
||||
.sb-line-code .sb-code {
|
||||
background-color: transparent;
|
||||
|
@ -592,7 +591,6 @@
|
|||
button:last-of-type {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -90,9 +90,7 @@ body {
|
|||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#sb-current-page {
|
||||
|
@ -169,7 +167,6 @@ body {
|
|||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
#sb-main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
|
@ -50,7 +50,11 @@ html {
|
|||
--button-color: black;
|
||||
--button-border-color: #6c6c6c;
|
||||
--primary-button-background-color: var(--ui-accent-color);
|
||||
--primary-button-hover-background-color: color-mix(in srgb, var(--ui-accent-color), black 35%);
|
||||
--primary-button-hover-background-color: color-mix(
|
||||
in srgb,
|
||||
var(--ui-accent-color),
|
||||
black 35%
|
||||
);
|
||||
--primary-button-color: var(--ui-accent-contrast-color);
|
||||
--primary-button-border-color: transparent;
|
||||
|
||||
|
@ -120,10 +124,10 @@ html {
|
|||
--editor-directive-color: #696969;
|
||||
--editor-directive-background-color: #ebebeb7d;
|
||||
|
||||
|
||||
--ui-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji",
|
||||
"Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
--ui-font:
|
||||
-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
|
||||
Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji",
|
||||
"Segoe UI Symbol", "Noto Color Emoji";
|
||||
--editor-font: "iA-Mono", "Menlo";
|
||||
--editor-width: 800px;
|
||||
}
|
||||
|
@ -186,7 +190,11 @@ html[data-theme="dark"] {
|
|||
--button-color: white;
|
||||
--button-border-color: #666;
|
||||
--primary-button-background-color: var(--ui-accent-color);
|
||||
--primary-button-hover-background-color: color-mix(in srgb, var(--ui-accent-color), black 35%);
|
||||
--primary-button-hover-background-color: color-mix(
|
||||
in srgb,
|
||||
var(--ui-accent-color),
|
||||
black 35%
|
||||
);
|
||||
--primary-button-color: var(--ui-accent-contrast-color);
|
||||
--primary-button-border-color: transparent;
|
||||
|
||||
|
|
Loading…
Reference in New Issue