Complete redo of content indexing and querying (#517)
Complete redo of data store Introduces live queries and live templates
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { FilterList } from "./filter.tsx";
|
||||
import { FilterOption, PageMeta } from "../types.ts";
|
||||
import { FilterOption } from "../types.ts";
|
||||
import { CompletionContext, CompletionResult } from "../deps.ts";
|
||||
import { PageMeta } from "$sb/types.ts";
|
||||
|
||||
export function PageNavigator({
|
||||
allPages,
|
||||
@@ -51,7 +52,7 @@ export function PageNavigator({
|
||||
darkMode={darkMode}
|
||||
completer={completer}
|
||||
allowNew={true}
|
||||
helpText="Start typing the page name to filter results, press <code>Return</code> to open."
|
||||
helpText="Press <code>Enter</code> to open the selected page, or <code>Shift-Enter</code> to create a new page."
|
||||
newHint="Create page"
|
||||
completePrefix={completePrefix}
|
||||
onSelect={(opt) => {
|
||||
|
||||
@@ -1,97 +1,7 @@
|
||||
import { useEffect, useRef } from "../deps.ts";
|
||||
import { Client } from "../client.ts";
|
||||
import { PanelConfig } from "../types.ts";
|
||||
|
||||
export const panelHtml = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<base target="_top">
|
||||
<script>
|
||||
const pendingRequests = new Map();
|
||||
let syscallReqId = 0;
|
||||
|
||||
self.syscall = async (name, ...args) => {
|
||||
return await new Promise((resolve, reject) => {
|
||||
syscallReqId++;
|
||||
pendingRequests.set(syscallReqId, { resolve, reject });
|
||||
window.parent.postMessage({
|
||||
type: "syscall",
|
||||
id: syscallReqId,
|
||||
name,
|
||||
args,
|
||||
}, "*");
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("message", (message) => {
|
||||
const data = message.data;
|
||||
switch (data.type) {
|
||||
case "html":
|
||||
document.body.innerHTML = data.html;
|
||||
if (data.script) {
|
||||
try {
|
||||
eval(data.script);
|
||||
} catch (e) {
|
||||
console.error("Error evaling script", e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "syscall-response":
|
||||
{
|
||||
const syscallId = data.id;
|
||||
const lookup = pendingRequests.get(syscallId);
|
||||
if (!lookup) {
|
||||
console.log(
|
||||
"Current outstanding requests",
|
||||
pendingRequests,
|
||||
"looking up",
|
||||
syscallId,
|
||||
);
|
||||
throw Error("Invalid request id");
|
||||
}
|
||||
pendingRequests.delete(syscallId);
|
||||
if (data.error) {
|
||||
lookup.reject(new Error(data.error));
|
||||
} else {
|
||||
lookup.resolve(data.result);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
// DEPRECATED: Use syscall("event.dispatch", ...) instead
|
||||
function sendEvent(name, ...args) {
|
||||
window.parent.postMessage({ type: "event", name, args, }, "*");
|
||||
}
|
||||
function api(obj) {
|
||||
window.parent.postMessage(obj, "*");
|
||||
}
|
||||
function updateHeight() {
|
||||
api({
|
||||
type: "setHeight",
|
||||
height: document.documentElement.offsetHeight,
|
||||
});
|
||||
}
|
||||
|
||||
function loadJsByUrl(url) {
|
||||
const script = document.createElement("script");
|
||||
script.src = url;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
script.onload = resolve;
|
||||
|
||||
document.documentElement.firstChild.appendChild(script);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
Loading...
|
||||
</body>
|
||||
</html>`;
|
||||
import { panelHtml } from "./panel_html.ts";
|
||||
|
||||
export function Panel({
|
||||
config,
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
export const panelHtml = `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<base target="_top">
|
||||
<script>
|
||||
const pendingRequests = new Map();
|
||||
let syscallReqId = 0;
|
||||
|
||||
self.syscall = async (name, ...args) => {
|
||||
return await new Promise((resolve, reject) => {
|
||||
syscallReqId++;
|
||||
pendingRequests.set(syscallReqId, { resolve, reject });
|
||||
window.parent.postMessage({
|
||||
type: "syscall",
|
||||
id: syscallReqId,
|
||||
name,
|
||||
args,
|
||||
}, "*");
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("message", (message) => {
|
||||
const data = message.data;
|
||||
switch (data.type) {
|
||||
case "html":
|
||||
document.body.innerHTML = data.html;
|
||||
if(data.theme) {
|
||||
document.getElementsByTagName("html")[0].setAttribute("data-theme", data.theme);
|
||||
}
|
||||
if (data.script) {
|
||||
try {
|
||||
eval(data.script);
|
||||
} catch (e) {
|
||||
console.error("Error evaling script", e);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "syscall-response":
|
||||
{
|
||||
const syscallId = data.id;
|
||||
const lookup = pendingRequests.get(syscallId);
|
||||
if (!lookup) {
|
||||
console.log(
|
||||
"Current outstanding requests",
|
||||
pendingRequests,
|
||||
"looking up",
|
||||
syscallId,
|
||||
);
|
||||
throw Error("Invalid request id");
|
||||
}
|
||||
pendingRequests.delete(syscallId);
|
||||
if (data.error) {
|
||||
lookup.reject(new Error(data.error));
|
||||
} else {
|
||||
lookup.resolve(data.result);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function api(obj) {
|
||||
window.parent.postMessage(obj, "*");
|
||||
}
|
||||
|
||||
let oldHeight = undefined;
|
||||
let heightChecks = 0;
|
||||
function updateHeight() {
|
||||
const body = document.body, html = document.documentElement;
|
||||
let height = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight);
|
||||
heightChecks++;
|
||||
if(height !== oldHeight) {
|
||||
oldHeight = height;
|
||||
api({
|
||||
type: "setHeight",
|
||||
height: height,
|
||||
});
|
||||
}
|
||||
if(heightChecks < 25) {
|
||||
setTimeout(updateHeight, 100);
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
updateHeight();
|
||||
});
|
||||
|
||||
function loadJsByUrl(url) {
|
||||
const script = document.createElement("script");
|
||||
script.src = url;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
script.onload = resolve;
|
||||
document.documentElement.firstChild.appendChild(script);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>`;
|
||||
@@ -0,0 +1,96 @@
|
||||
import { WidgetContent } from "$sb/app_event.ts";
|
||||
import { Client } from "../client.ts";
|
||||
import { panelHtml } from "./panel_html.ts";
|
||||
|
||||
export function createWidgetSandboxIFrame(
|
||||
client: Client,
|
||||
widgetHeightCacheKey: string | null,
|
||||
content: WidgetContent | Promise<WidgetContent>,
|
||||
onMessage?: (message: any) => void,
|
||||
) {
|
||||
const iframe = document.createElement("iframe");
|
||||
iframe.srcdoc = panelHtml;
|
||||
// iframe.style.height = "150px";
|
||||
|
||||
const messageListener = (evt: any) => {
|
||||
(async () => {
|
||||
if (evt.source !== iframe.contentWindow) {
|
||||
return;
|
||||
}
|
||||
const data = evt.data;
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
switch (data.type) {
|
||||
case "syscall": {
|
||||
const { id, name, args } = data;
|
||||
try {
|
||||
const result = await client.system.localSyscall(name, args);
|
||||
if (!iframe.contentWindow) {
|
||||
// iFrame already went away
|
||||
return;
|
||||
}
|
||||
iframe.contentWindow!.postMessage({
|
||||
type: "syscall-response",
|
||||
id,
|
||||
result,
|
||||
});
|
||||
} catch (e: any) {
|
||||
if (!iframe.contentWindow) {
|
||||
// iFrame already went away
|
||||
return;
|
||||
}
|
||||
iframe.contentWindow!.postMessage({
|
||||
type: "syscall-response",
|
||||
id,
|
||||
error: e.message,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "setHeight":
|
||||
iframe.style.height = data.height + "px";
|
||||
if (widgetHeightCacheKey) {
|
||||
client.space.setCachedWidgetHeight(
|
||||
widgetHeightCacheKey,
|
||||
data.height,
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (onMessage) {
|
||||
onMessage(data);
|
||||
}
|
||||
}
|
||||
})().catch((e) => {
|
||||
console.error("Message listener error", e);
|
||||
});
|
||||
};
|
||||
|
||||
iframe.onload = () => {
|
||||
// Subscribe to message event on global object (to receive messages from iframe)
|
||||
globalThis.addEventListener("message", messageListener);
|
||||
// Only run this code once
|
||||
iframe.onload = null;
|
||||
Promise.resolve(content).then((content) => {
|
||||
if (content.html) {
|
||||
iframe.contentWindow!.postMessage({
|
||||
type: "html",
|
||||
html: content.html,
|
||||
script: content.script,
|
||||
theme: document.getElementsByTagName("html")[0].dataset.theme,
|
||||
});
|
||||
} else if (content.url) {
|
||||
iframe.contentWindow!.location.href = content.url;
|
||||
if (content.height) {
|
||||
iframe.style.height = content.height + "px";
|
||||
}
|
||||
if (content.width) {
|
||||
iframe.style.width = content.width + "px";
|
||||
}
|
||||
}
|
||||
}).catch(console.error);
|
||||
};
|
||||
|
||||
return iframe;
|
||||
}
|
||||
Reference in New Issue
Block a user