@mcp-b/interactive-components 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/dist/code-editor-B94tewLC.js +425 -0
- package/dist/code-preview-gKk0nBKP.js +269 -0
- package/dist/components/code-editor/code-editor.d.ts +65 -0
- package/dist/components/code-editor/code-editor.js +2 -0
- package/dist/components/code-preview/code-preview.d.ts +58 -0
- package/dist/components/code-preview/code-preview.js +3 -0
- package/dist/components/interactive-editor/interactive-editor.d.ts +77 -0
- package/dist/components/interactive-editor/interactive-editor.js +3 -0
- package/dist/components/r-editor/r-editor.d.ts +68 -0
- package/dist/components/r-editor/r-editor.js +2 -0
- package/dist/components/sql-editor/sql-editor.d.ts +78 -0
- package/dist/components/sql-editor/sql-editor.js +2 -0
- package/dist/decorate-DcF3lt7P.js +9 -0
- package/dist/docs/custom-elements.json +2807 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/interactive-editor-WfTWxJGF.js +1454 -0
- package/dist/lib/runner-channel.d.ts +25 -0
- package/dist/lib/runner-channel.js +55 -0
- package/dist/lib/runner-url.d.ts +15 -0
- package/dist/lib/runner-url.js +19 -0
- package/dist/lib/transform.d.ts +2 -0
- package/dist/lib/transform.js +210 -0
- package/dist/r-editor-DwSyDo2i.js +743 -0
- package/dist/runners/ephemeral-indexeddb.js +114 -0
- package/dist/runners/pglite-runner.js +175 -0
- package/dist/runners/pyscript-runner.html +189 -0
- package/dist/runners/webr-runner.html +282 -0
- package/dist/sql-editor-CsNrjJ2_.js +968 -0
- package/dist/themes/interactive.css +79 -0
- package/dist/transform-DWhHlnkw.d.ts +53 -0
- package/dist/vite.d.ts +14 -0
- package/dist/vite.js +62 -0
- package/package.json +96 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
// PyScript's filesystem bridge instantiates IDBMap at module load for file
|
|
2
|
+
// handle metadata. Opaque sandbox origins are intentionally denied browser
|
|
3
|
+
// storage, so this implements only that IDBMap surface in runner-local memory.
|
|
4
|
+
|
|
5
|
+
export function createEphemeralIndexedDB() {
|
|
6
|
+
const databases = new Map();
|
|
7
|
+
|
|
8
|
+
function clone(value) {
|
|
9
|
+
try {
|
|
10
|
+
return structuredClone(value);
|
|
11
|
+
} catch {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolvedRequest(result) {
|
|
17
|
+
const request = { result, error: null, onsuccess: null, onerror: null };
|
|
18
|
+
queueMicrotask(() => request.onsuccess?.({ target: request }));
|
|
19
|
+
return request;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function createObjectStore(entries) {
|
|
23
|
+
return {
|
|
24
|
+
count: () => resolvedRequest(entries.size),
|
|
25
|
+
clear: () => {
|
|
26
|
+
entries.clear();
|
|
27
|
+
return resolvedRequest(undefined);
|
|
28
|
+
},
|
|
29
|
+
delete: (key) => {
|
|
30
|
+
entries.delete(key);
|
|
31
|
+
return resolvedRequest(undefined);
|
|
32
|
+
},
|
|
33
|
+
get: (key) => resolvedRequest(entries.has(key) ? clone(entries.get(key)) : undefined),
|
|
34
|
+
getAllKeys: () => resolvedRequest(Array.from(entries.keys())),
|
|
35
|
+
getKey: (key) => resolvedRequest(entries.has(key) ? key : undefined),
|
|
36
|
+
put: (value, key) => {
|
|
37
|
+
entries.set(key, clone(value));
|
|
38
|
+
return resolvedRequest(key);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function createDatabase(stores) {
|
|
44
|
+
return {
|
|
45
|
+
objectStoreNames: {
|
|
46
|
+
get length() {
|
|
47
|
+
return stores.size;
|
|
48
|
+
},
|
|
49
|
+
contains: (name) => stores.has(name),
|
|
50
|
+
},
|
|
51
|
+
createObjectStore: (name) => {
|
|
52
|
+
const entries = new Map();
|
|
53
|
+
stores.set(name, entries);
|
|
54
|
+
return createObjectStore(entries);
|
|
55
|
+
},
|
|
56
|
+
transaction: (name) => ({
|
|
57
|
+
objectStore: () => {
|
|
58
|
+
const entries = stores.get(name);
|
|
59
|
+
if (!entries) throw new Error(`Object store does not exist: ${name}`);
|
|
60
|
+
return createObjectStore(entries);
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
close: () => {},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
open(name) {
|
|
69
|
+
const request = {
|
|
70
|
+
result: null,
|
|
71
|
+
error: null,
|
|
72
|
+
transaction: null,
|
|
73
|
+
onupgradeneeded: null,
|
|
74
|
+
onsuccess: null,
|
|
75
|
+
onerror: null,
|
|
76
|
+
};
|
|
77
|
+
queueMicrotask(() => {
|
|
78
|
+
let stores = databases.get(name);
|
|
79
|
+
const isNew = !stores;
|
|
80
|
+
if (!stores) {
|
|
81
|
+
stores = new Map();
|
|
82
|
+
databases.set(name, stores);
|
|
83
|
+
}
|
|
84
|
+
const database = createDatabase(stores);
|
|
85
|
+
request.result = database;
|
|
86
|
+
if (isNew && request.onupgradeneeded) {
|
|
87
|
+
const upgrade = { oncomplete: null };
|
|
88
|
+
request.transaction = upgrade;
|
|
89
|
+
request.onupgradeneeded({ target: request });
|
|
90
|
+
queueMicrotask(() => {
|
|
91
|
+
upgrade.oncomplete?.({ target: upgrade });
|
|
92
|
+
request.onsuccess?.({ target: request });
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
request.onsuccess?.({ target: request });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return request;
|
|
99
|
+
},
|
|
100
|
+
deleteDatabase(name) {
|
|
101
|
+
databases.delete(name);
|
|
102
|
+
return resolvedRequest(undefined);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function installEphemeralIndexedDB() {
|
|
108
|
+
const ephemeralIndexedDB = createEphemeralIndexedDB();
|
|
109
|
+
Object.defineProperty(globalThis, "indexedDB", {
|
|
110
|
+
configurable: true,
|
|
111
|
+
value: ephemeralIndexedDB,
|
|
112
|
+
});
|
|
113
|
+
return ephemeralIndexedDB;
|
|
114
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { PGlite } from "./pglite/index.js";
|
|
2
|
+
|
|
3
|
+
let db = null;
|
|
4
|
+
|
|
5
|
+
function isRecord(value) {
|
|
6
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function errorMessage(error) {
|
|
10
|
+
return error instanceof Error ? error.message : String(error);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function post(message) {
|
|
14
|
+
self.postMessage(message);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function initialize({ dbName, postgis }) {
|
|
18
|
+
if (db) return;
|
|
19
|
+
post({ __sigvelo_pglite_loading: true, message: "Initializing PostgreSQL..." });
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const options = { relaxedDurability: true };
|
|
23
|
+
if (dbName) options.dataDir = `idb://${dbName}`;
|
|
24
|
+
if (postgis) {
|
|
25
|
+
const { postgis: extension } = await import("./pglite-postgis/index.js");
|
|
26
|
+
options.extensions = { postgis: extension };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
db = await PGlite.create(options);
|
|
30
|
+
if (postgis) {
|
|
31
|
+
post({ __sigvelo_pglite_loading: true, message: "Loading PostGIS extension..." });
|
|
32
|
+
await db.exec("CREATE EXTENSION IF NOT EXISTS postgis;");
|
|
33
|
+
}
|
|
34
|
+
post({ __sigvelo_pglite_ready: true });
|
|
35
|
+
} catch (error) {
|
|
36
|
+
post({
|
|
37
|
+
__sigvelo_pglite_error: true,
|
|
38
|
+
id: -1,
|
|
39
|
+
message: `Init failed: ${errorMessage(error)}`,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function execute(sql, id) {
|
|
45
|
+
if (!db) {
|
|
46
|
+
post({
|
|
47
|
+
__sigvelo_pglite_error: true,
|
|
48
|
+
id,
|
|
49
|
+
message: "Database not initialized",
|
|
50
|
+
});
|
|
51
|
+
post({ __sigvelo_pglite_done: true });
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const start = performance.now();
|
|
56
|
+
try {
|
|
57
|
+
const results = await db.exec(sql);
|
|
58
|
+
const elapsed = performance.now() - start;
|
|
59
|
+
let result = results.at(-1);
|
|
60
|
+
for (let index = results.length - 1; index >= 0; index--) {
|
|
61
|
+
if (results[index].fields.length > 0) {
|
|
62
|
+
result = results[index];
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
post({
|
|
67
|
+
__sigvelo_pglite_result: true,
|
|
68
|
+
id,
|
|
69
|
+
rows: result?.rows ?? [],
|
|
70
|
+
fields: result?.fields ?? [],
|
|
71
|
+
rowCount: result ? (result.affectedRows ?? result.rows.length) : 0,
|
|
72
|
+
executionTimeMs: Math.round(elapsed),
|
|
73
|
+
});
|
|
74
|
+
} catch (error) {
|
|
75
|
+
post({
|
|
76
|
+
__sigvelo_pglite_error: true,
|
|
77
|
+
id,
|
|
78
|
+
message: errorMessage(error),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
post({ __sigvelo_pglite_done: true });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function setup(sql) {
|
|
85
|
+
if (!db) return;
|
|
86
|
+
post({ __sigvelo_pglite_loading: true, message: "Loading dataset..." });
|
|
87
|
+
try {
|
|
88
|
+
await db.exec(sql);
|
|
89
|
+
post({ __sigvelo_pglite_setup_done: true });
|
|
90
|
+
} catch (error) {
|
|
91
|
+
post({
|
|
92
|
+
__sigvelo_pglite_error: true,
|
|
93
|
+
id: -1,
|
|
94
|
+
message: `Setup failed: ${errorMessage(error)}`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function sendSchema() {
|
|
100
|
+
if (!db) return;
|
|
101
|
+
try {
|
|
102
|
+
const result = await db.query(
|
|
103
|
+
"SELECT table_name, column_name, data_type, is_nullable " +
|
|
104
|
+
"FROM information_schema.columns " +
|
|
105
|
+
"WHERE table_schema = 'public' " +
|
|
106
|
+
"ORDER BY table_name, ordinal_position",
|
|
107
|
+
);
|
|
108
|
+
const tableMap = {};
|
|
109
|
+
for (const row of result.rows) {
|
|
110
|
+
tableMap[row.table_name] ??= [];
|
|
111
|
+
tableMap[row.table_name].push({
|
|
112
|
+
name: row.column_name,
|
|
113
|
+
type: row.data_type,
|
|
114
|
+
nullable: row.is_nullable === "YES",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
const tables = Object.entries(tableMap).map(([name, columns]) => ({ name, columns }));
|
|
118
|
+
post({ __sigvelo_pglite_schema: true, tables });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
post({
|
|
121
|
+
__sigvelo_pglite_error: true,
|
|
122
|
+
id: -1,
|
|
123
|
+
message: `Schema query failed: ${errorMessage(error)}`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function reset(setupSql) {
|
|
129
|
+
if (!db) return;
|
|
130
|
+
post({ __sigvelo_pglite_loading: true, message: "Resetting database..." });
|
|
131
|
+
try {
|
|
132
|
+
const result = await db.query("SELECT tablename FROM pg_tables WHERE schemaname = 'public'");
|
|
133
|
+
for (const row of result.rows) {
|
|
134
|
+
const tableName = String(row.tablename).replaceAll('"', '""');
|
|
135
|
+
await db.exec(`DROP TABLE IF EXISTS "${tableName}" CASCADE`);
|
|
136
|
+
}
|
|
137
|
+
if (setupSql) await db.exec(setupSql);
|
|
138
|
+
post({ __sigvelo_pglite_reset_done: true });
|
|
139
|
+
} catch (error) {
|
|
140
|
+
post({
|
|
141
|
+
__sigvelo_pglite_error: true,
|
|
142
|
+
id: -1,
|
|
143
|
+
message: `Reset failed: ${errorMessage(error)}`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
self.addEventListener("message", (event) => {
|
|
149
|
+
const data = event.data;
|
|
150
|
+
if (!isRecord(data) || typeof data.type !== "string") return;
|
|
151
|
+
|
|
152
|
+
switch (data.type) {
|
|
153
|
+
case "pglite-init":
|
|
154
|
+
if (typeof data.dbName === "string" && typeof data.postgis === "boolean") {
|
|
155
|
+
void initialize(data);
|
|
156
|
+
}
|
|
157
|
+
break;
|
|
158
|
+
case "pglite-setup":
|
|
159
|
+
if (typeof data.sql === "string") void setup(data.sql);
|
|
160
|
+
break;
|
|
161
|
+
case "pglite-exec":
|
|
162
|
+
if (typeof data.sql === "string" && Number.isInteger(data.id)) {
|
|
163
|
+
void execute(data.sql, data.id);
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
case "pglite-schema":
|
|
167
|
+
void sendSchema();
|
|
168
|
+
break;
|
|
169
|
+
case "pglite-reset":
|
|
170
|
+
if (data.setupSql === undefined || typeof data.setupSql === "string") {
|
|
171
|
+
void reset(data.setupSql);
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="referrer" content="no-referrer" />
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
font-family: system-ui, sans-serif;
|
|
12
|
+
}
|
|
13
|
+
* {
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
</head>
|
|
18
|
+
<body>
|
|
19
|
+
<div id="pyscript-body"></div>
|
|
20
|
+
<script type="module">
|
|
21
|
+
import { installEphemeralIndexedDB } from "./ephemeral-indexeddb.js";
|
|
22
|
+
|
|
23
|
+
function isRecord(value) {
|
|
24
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
installEphemeralIndexedDB();
|
|
28
|
+
|
|
29
|
+
function createRunnerEndpoint(onMessage) {
|
|
30
|
+
var port = null;
|
|
31
|
+
var pending = [];
|
|
32
|
+
var params = new URLSearchParams(window.location.hash.slice(1));
|
|
33
|
+
var channelId = params.get("sigvelo-channel");
|
|
34
|
+
var parentOrigin = params.get("sigvelo-parent-origin");
|
|
35
|
+
|
|
36
|
+
function connect(event) {
|
|
37
|
+
if (
|
|
38
|
+
event.source !== window.parent ||
|
|
39
|
+
!parentOrigin ||
|
|
40
|
+
event.origin !== parentOrigin ||
|
|
41
|
+
!isRecord(event.data) ||
|
|
42
|
+
event.data.type !== "sigvelo-runner-connect-v1" ||
|
|
43
|
+
event.data.channelId !== channelId ||
|
|
44
|
+
event.data.parentOrigin !== parentOrigin ||
|
|
45
|
+
event.ports.length !== 1
|
|
46
|
+
)
|
|
47
|
+
return;
|
|
48
|
+
|
|
49
|
+
port = event.ports[0];
|
|
50
|
+
port.onmessage = function (message) {
|
|
51
|
+
onMessage(message.data);
|
|
52
|
+
};
|
|
53
|
+
port.start();
|
|
54
|
+
pending.splice(0).forEach(function (message) {
|
|
55
|
+
port.postMessage(message);
|
|
56
|
+
});
|
|
57
|
+
window.removeEventListener("message", connect);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
window.addEventListener("message", connect);
|
|
61
|
+
return {
|
|
62
|
+
postMessage(message) {
|
|
63
|
+
if (port) port.postMessage(message);
|
|
64
|
+
else if (pending.length < 100) pending.push(message);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
var runnerEndpoint = createRunnerEndpoint(handleRunnerMessage);
|
|
70
|
+
|
|
71
|
+
function postToParent(message) {
|
|
72
|
+
runnerEndpoint.postMessage(message);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Console capture — forwards console calls to the parent frame.
|
|
76
|
+
(function () {
|
|
77
|
+
var _post = function (level, args) {
|
|
78
|
+
try {
|
|
79
|
+
postToParent({
|
|
80
|
+
__sigvelo_console: true,
|
|
81
|
+
level: level,
|
|
82
|
+
args: args.map(function (a) {
|
|
83
|
+
try {
|
|
84
|
+
return typeof a === "object" ? JSON.stringify(a, null, 2) : String(a);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
return String(a);
|
|
87
|
+
}
|
|
88
|
+
}),
|
|
89
|
+
ts: Date.now(),
|
|
90
|
+
});
|
|
91
|
+
} catch (e) {}
|
|
92
|
+
};
|
|
93
|
+
["log", "warn", "error", "info"].forEach(function (m) {
|
|
94
|
+
var orig = console[m];
|
|
95
|
+
console[m] = function () {
|
|
96
|
+
var args = Array.prototype.slice.call(arguments);
|
|
97
|
+
_post(m, args);
|
|
98
|
+
orig.apply(console, args);
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
window.addEventListener("error", function (e) {
|
|
102
|
+
_post("error", [e.message + " at " + e.filename + ":" + e.lineno]);
|
|
103
|
+
});
|
|
104
|
+
})();
|
|
105
|
+
|
|
106
|
+
function handleRunnerMessage(payload) {
|
|
107
|
+
if (
|
|
108
|
+
!isRecord(payload) ||
|
|
109
|
+
payload.type !== "pyscript-run" ||
|
|
110
|
+
typeof payload.code !== "string" ||
|
|
111
|
+
typeof payload.bodyHtml !== "string" ||
|
|
112
|
+
typeof payload.css !== "string" ||
|
|
113
|
+
(payload.config !== null && !isRecord(payload.config))
|
|
114
|
+
) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
var container = document.getElementById("pyscript-body");
|
|
118
|
+
|
|
119
|
+
// Clear previous run
|
|
120
|
+
container.replaceChildren();
|
|
121
|
+
var oldScripts = document.querySelectorAll(
|
|
122
|
+
'script[type="py"], py-config, link[data-pyscript], script[data-pyscript]',
|
|
123
|
+
);
|
|
124
|
+
oldScripts.forEach(function (el) {
|
|
125
|
+
el.remove();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Inject body HTML (from index.html file in the editor)
|
|
129
|
+
if (payload.bodyHtml) {
|
|
130
|
+
var parsed = new DOMParser().parseFromString(payload.bodyHtml, "text/html");
|
|
131
|
+
container.replaceChildren(
|
|
132
|
+
...Array.from(parsed.body.childNodes, function (node) {
|
|
133
|
+
return document.importNode(node, true);
|
|
134
|
+
}),
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Inject user CSS
|
|
139
|
+
if (payload.css) {
|
|
140
|
+
var style = document.createElement("style");
|
|
141
|
+
style.textContent = payload.css;
|
|
142
|
+
style.setAttribute("data-pyscript", "");
|
|
143
|
+
document.head.appendChild(style);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Load PyScript if not already loaded.
|
|
147
|
+
// Uses the package-local copy from @pyscript/core.
|
|
148
|
+
if (!document.querySelector("script[data-pyscript-core]")) {
|
|
149
|
+
var cssLink = document.createElement("link");
|
|
150
|
+
cssLink.rel = "stylesheet";
|
|
151
|
+
cssLink.href = "./pyscript/core.css";
|
|
152
|
+
cssLink.setAttribute("data-pyscript-core", "");
|
|
153
|
+
document.head.appendChild(cssLink);
|
|
154
|
+
|
|
155
|
+
var coreScript = document.createElement("script");
|
|
156
|
+
coreScript.type = "module";
|
|
157
|
+
coreScript.src = "./pyscript/core.js";
|
|
158
|
+
coreScript.setAttribute("data-pyscript-core", "");
|
|
159
|
+
document.head.appendChild(coreScript);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Inject py-config if provided
|
|
163
|
+
if (payload.config) {
|
|
164
|
+
var pyConfig = document.createElement("py-config");
|
|
165
|
+
pyConfig.textContent = JSON.stringify(payload.config);
|
|
166
|
+
container.appendChild(pyConfig);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Wait briefly for PyScript to register, then inject the py script
|
|
170
|
+
function injectPy() {
|
|
171
|
+
var pyScript = document.createElement("script");
|
|
172
|
+
pyScript.type = "py";
|
|
173
|
+
pyScript.textContent = payload.code;
|
|
174
|
+
container.appendChild(pyScript);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Give PyScript a moment to initialize on first load
|
|
178
|
+
if (document.querySelector("script[data-pyscript-core]")) {
|
|
179
|
+
setTimeout(injectPy, 500);
|
|
180
|
+
} else {
|
|
181
|
+
injectPy();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Signal ready
|
|
186
|
+
postToParent({ __sigvelo_pyscript_ready: true });
|
|
187
|
+
</script>
|
|
188
|
+
</body>
|
|
189
|
+
</html>
|