@jtfmumm/patchwork-standalone-frame 0.3.0 → 0.3.1
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/README.md +167 -17
- package/dist/index.d.ts +2 -0
- package/dist/index.js +34 -34
- package/package.json +1 -1
- package/src/frame.tsx +1 -1
- package/src/index.ts +2 -0
package/README.md
CHANGED
|
@@ -1,34 +1,184 @@
|
|
|
1
1
|
# @jtfmumm/patchwork-standalone-frame
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A standalone frame for patchwork tools. Provides repo setup, doc history, top bar UI, sharing modals, and optional keyhive access control.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Quick start: standalone app from an automerge tool
|
|
6
6
|
|
|
7
|
+
This guide walks through building a standalone web app from a patchwork tool published as an automerge FolderDoc.
|
|
8
|
+
|
|
9
|
+
### Prerequisites
|
|
10
|
+
|
|
11
|
+
- Node.js 18+
|
|
12
|
+
- pnpm
|
|
13
|
+
|
|
14
|
+
### 1. Create the project
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
mkdir my-standalone-app && cd my-standalone-app
|
|
18
|
+
pnpm init
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Install dependencies
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
pnpm add @jtfmumm/patchwork-standalone-frame \
|
|
25
|
+
@automerge/automerge @automerge/automerge-repo \
|
|
26
|
+
@automerge/automerge-repo-network-websocket \
|
|
27
|
+
@automerge/automerge-repo-storage-indexeddb \
|
|
28
|
+
solid-js
|
|
29
|
+
|
|
30
|
+
pnpm add -D @jtfmumm/automerge-deps @jtfmumm/patchwork-standalone-vite vite
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 3. Configure automerge-deps
|
|
34
|
+
|
|
35
|
+
Create `automerge-deps.json` to specify the tool and sync server:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"syncServers": ["wss://your-sync-server.example.com"],
|
|
40
|
+
"dependencies": [
|
|
41
|
+
{ "name": "my-tool", "url": "automerge:<tool-url>" }
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 4. Fetch the tool
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
npx automerge-deps install
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This downloads the tool's FolderDoc from the sync server and writes it to `node_modules/my-tool/`.
|
|
53
|
+
|
|
54
|
+
### 5. Create the entry point
|
|
55
|
+
|
|
56
|
+
**`src/main.ts`**
|
|
57
|
+
```typescript
|
|
58
|
+
import { Repo } from "@automerge/automerge-repo";
|
|
59
|
+
import { IndexedDBStorageAdapter } from "@automerge/automerge-repo-storage-indexeddb";
|
|
60
|
+
import { BrowserWebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket";
|
|
61
|
+
import { mountStandaloneApp } from "@jtfmumm/patchwork-standalone-frame";
|
|
62
|
+
import { plugins } from "my-tool";
|
|
63
|
+
|
|
64
|
+
const repo = new Repo({
|
|
65
|
+
storage: new IndexedDBStorageAdapter(),
|
|
66
|
+
network: [new BrowserWebSocketClientAdapter("wss://your-sync-server.example.com")],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const root = document.getElementById("root");
|
|
70
|
+
if (root) {
|
|
71
|
+
mountStandaloneApp(root, plugins, { legacyMode: true, repo });
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 6. Create the HTML shell
|
|
76
|
+
|
|
77
|
+
**`index.html`**
|
|
78
|
+
```html
|
|
79
|
+
<!doctype html>
|
|
80
|
+
<html lang="en">
|
|
81
|
+
<head>
|
|
82
|
+
<meta charset="UTF-8" />
|
|
83
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
84
|
+
<title>My Tool</title>
|
|
85
|
+
<style>
|
|
86
|
+
* { margin: 0; padding: 0; }
|
|
87
|
+
html, body, #root { height: 100%; }
|
|
88
|
+
</style>
|
|
89
|
+
</head>
|
|
90
|
+
<body>
|
|
91
|
+
<div id="root"></div>
|
|
92
|
+
<script type="module" src="/src/main.ts"></script>
|
|
93
|
+
</body>
|
|
94
|
+
</html>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 7. Configure Vite
|
|
98
|
+
|
|
99
|
+
**`vite.config.ts`**
|
|
7
100
|
```typescript
|
|
8
|
-
import {
|
|
101
|
+
import { defineConfig } from "vite";
|
|
102
|
+
import { patchworkStandalone } from "@jtfmumm/patchwork-standalone-vite";
|
|
103
|
+
|
|
104
|
+
export default defineConfig({
|
|
105
|
+
plugins: [patchworkStandalone({ tools: ["my-tool"] })],
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The `tools` option lists tool packages fetched by `automerge-deps`. This enables serving of the tool's code-split chunks during development.
|
|
110
|
+
|
|
111
|
+
### 8. Add scripts to package.json
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"scripts": {
|
|
116
|
+
"fetch-deps": "automerge-deps install",
|
|
117
|
+
"dev": "vite",
|
|
118
|
+
"build": "pnpm fetch-deps && vite build",
|
|
119
|
+
"preview": "vite preview"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 9. Run
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
pnpm dev
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
For production builds:
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
pnpm build
|
|
134
|
+
pnpm preview
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## API
|
|
9
138
|
|
|
139
|
+
### `mountStandaloneApp(root, toolOrPlugins, config?)`
|
|
140
|
+
|
|
141
|
+
Mounts the standalone frame into a DOM element.
|
|
142
|
+
|
|
143
|
+
- **`root`** — the HTML element to mount into
|
|
144
|
+
- **`toolOrPlugins`** — either a `ToolRegistration<D>` object or a `Plugin<D>[]` array (patchwork plugin convention)
|
|
145
|
+
- **`config`** — optional `StandaloneFrameConfig`
|
|
146
|
+
|
|
147
|
+
### ToolRegistration
|
|
148
|
+
|
|
149
|
+
For tools that don't use the patchwork plugin convention:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
10
152
|
const myTool: ToolRegistration<MyDoc> = {
|
|
11
|
-
id: "my-tool",
|
|
12
|
-
name: "My Tool",
|
|
13
|
-
defaultTitle: "Untitled",
|
|
14
|
-
init: (doc, repo) => {
|
|
15
|
-
// Initialize a blank document
|
|
16
|
-
},
|
|
153
|
+
id: "my-tool",
|
|
154
|
+
name: "My Tool",
|
|
155
|
+
defaultTitle: "Untitled",
|
|
156
|
+
init: (doc, repo) => { /* initialize a blank document */ },
|
|
17
157
|
getTitle: (doc) => doc.title || "Untitled",
|
|
18
158
|
setTitle: (doc, title) => { doc.title = title; },
|
|
19
|
-
|
|
20
|
-
render: (handle, element) => {
|
|
21
|
-
// Mount your UI into `element`, return a cleanup function
|
|
22
|
-
},
|
|
159
|
+
render: (handle, element) => { /* mount UI, return cleanup function */ },
|
|
23
160
|
};
|
|
24
161
|
|
|
25
|
-
|
|
26
|
-
if (root) mountStandaloneApp(root, myTool);
|
|
162
|
+
mountStandaloneApp(root, myTool);
|
|
27
163
|
```
|
|
28
164
|
|
|
29
|
-
|
|
165
|
+
### Plugin convention
|
|
166
|
+
|
|
167
|
+
Tools that export a `plugins` array with `patchwork:tool` and `patchwork:datatype` entries can be passed directly:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
import { plugins } from "my-tool";
|
|
171
|
+
mountStandaloneApp(root, plugins);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Legacy mode
|
|
175
|
+
|
|
176
|
+
Pass `{ legacyMode: true, repo }` to use a plain automerge repo without keyhive. You construct and configure the repo yourself. This is the recommended approach for standalone deployments.
|
|
177
|
+
|
|
178
|
+
### Keyhive mode (default)
|
|
179
|
+
|
|
180
|
+
When `legacyMode` is not set, the frame initializes keyhive automatically, including WASM loading, repo creation, and access control. The sync server URL is resolved in this order:
|
|
30
181
|
|
|
31
|
-
The sync server URL is resolved in this order:
|
|
32
182
|
1. `VITE_SYNC_URL` environment variable
|
|
33
183
|
2. `tool.syncUrl` from the registration
|
|
34
184
|
3. `ws://localhost:3030` (default)
|
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ export interface StandaloneFrameConfig {
|
|
|
34
34
|
legacyMode?: boolean;
|
|
35
35
|
/** Pre-built repo for legacy mode. Required when legacyMode is true. */
|
|
36
36
|
repo?: FrameRepo;
|
|
37
|
+
/** WebSocket sync server URL. Overrides tool.syncUrl. */
|
|
38
|
+
syncUrl?: string;
|
|
37
39
|
}
|
|
38
40
|
export interface PluginDescription {
|
|
39
41
|
id: string;
|
package/dist/index.js
CHANGED
|
@@ -85,17 +85,17 @@ const Ze = {
|
|
|
85
85
|
"border-radius": "4px",
|
|
86
86
|
cursor: "pointer",
|
|
87
87
|
"font-size": "12px"
|
|
88
|
-
},
|
|
88
|
+
}, Ue = {
|
|
89
89
|
"font-size": "13px",
|
|
90
90
|
color: "#6b7280",
|
|
91
91
|
margin: "16px 0 8px"
|
|
92
|
-
},
|
|
92
|
+
}, ze = {
|
|
93
93
|
border: "none",
|
|
94
94
|
"border-top": "1px solid #2a323c",
|
|
95
95
|
margin: "12px 0"
|
|
96
96
|
};
|
|
97
97
|
function et(e) {
|
|
98
|
-
const [t, l] = x(""), [g, u] = x({}), [p, k] = x(!0), [$, v] = x(void 0), [N,
|
|
98
|
+
const [t, l] = x(""), [g, u] = x({}), [p, k] = x(!0), [$, v] = x(void 0), [N, U] = x(!1), [oe, H] = x(null), [A, ae] = x(null), [ee, te] = x(null), [G, be] = x("");
|
|
99
99
|
ie(() => {
|
|
100
100
|
if (!e.isOpen) return;
|
|
101
101
|
let a = !1;
|
|
@@ -105,9 +105,9 @@ function et(e) {
|
|
|
105
105
|
H(s.docIdFromAutomergeUrl(e.docUrl));
|
|
106
106
|
const C = e.hive.active.individual.id;
|
|
107
107
|
ae(C ? s.uint8ArrayToHex(C.toBytes()) : null);
|
|
108
|
-
const
|
|
109
|
-
if (
|
|
110
|
-
const T = s.ContactCard.fromJson(
|
|
108
|
+
const z = e.hive.syncServer;
|
|
109
|
+
if (z) {
|
|
110
|
+
const T = s.ContactCard.fromJson(z.contactCard.toJson());
|
|
111
111
|
T && te(s.uint8ArrayToHex(T.individualId.bytes));
|
|
112
112
|
}
|
|
113
113
|
const P = s.Identifier.publicId();
|
|
@@ -132,10 +132,10 @@ function et(e) {
|
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
try {
|
|
135
|
-
const
|
|
136
|
-
s || v(
|
|
137
|
-
} catch (
|
|
138
|
-
s || (console.error("[ShareModal] Error checking access:",
|
|
135
|
+
const z = await e.hive.accessForDoc(C, a);
|
|
136
|
+
s || v(z ? z.toString() : void 0);
|
|
137
|
+
} catch (z) {
|
|
138
|
+
s || (console.error("[ShareModal] Error checking access:", z), v(void 0));
|
|
139
139
|
}
|
|
140
140
|
})(), q(() => {
|
|
141
141
|
s = !0;
|
|
@@ -165,18 +165,18 @@ function et(e) {
|
|
|
165
165
|
a.preventDefault();
|
|
166
166
|
const s = t().trim();
|
|
167
167
|
if (s) {
|
|
168
|
-
|
|
168
|
+
U(!0);
|
|
169
169
|
try {
|
|
170
|
-
const C = await ye(),
|
|
171
|
-
if (!
|
|
170
|
+
const C = await ye(), z = C.ContactCard.fromJson(s);
|
|
171
|
+
if (!z) throw new Error("Invalid ContactCard JSON");
|
|
172
172
|
const P = C.Access.tryFromString("write");
|
|
173
173
|
if (!P) throw new Error("Invalid access level");
|
|
174
|
-
await e.hive.addMemberToDoc(e.docUrl,
|
|
174
|
+
await e.hive.addMemberToDoc(e.docUrl, z, P), l("");
|
|
175
175
|
} catch (C) {
|
|
176
176
|
console.error("[ShareModal]", C);
|
|
177
177
|
} finally {
|
|
178
178
|
const C = await de(e.hive, e.docUrl);
|
|
179
|
-
u(C),
|
|
179
|
+
u(C), U(!1);
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
}, he = async (a) => {
|
|
@@ -214,7 +214,7 @@ function et(e) {
|
|
|
214
214
|
return e.isOpen;
|
|
215
215
|
},
|
|
216
216
|
get children() {
|
|
217
|
-
var a = Ge(), s = a.firstChild, C = s.firstChild,
|
|
217
|
+
var a = Ge(), s = a.firstChild, C = s.firstChild, z = C.firstChild, P = z.nextSibling, T = C.nextSibling, L = T.firstChild, F = L.nextSibling, j = T.nextSibling, ce = j.firstChild;
|
|
218
218
|
return a.$$click = (i) => {
|
|
219
219
|
i.target === i.currentTarget && e.onClose();
|
|
220
220
|
}, s.$$click = (i) => i.stopPropagation(), P.$$click = () => e.onClose(), f(F, m(_, {
|
|
@@ -254,7 +254,7 @@ function et(e) {
|
|
|
254
254
|
get children() {
|
|
255
255
|
return [(() => {
|
|
256
256
|
var i = Le();
|
|
257
|
-
return D((S) => E(i,
|
|
257
|
+
return D((S) => E(i, ze, S)), i;
|
|
258
258
|
})(), (() => {
|
|
259
259
|
var i = We(), S = i.firstChild, K = S.nextSibling, V = K.firstChild;
|
|
260
260
|
return i.addEventListener("submit", fe), S.$$input = (M) => l(M.currentTarget.value), f(V, () => N() ? "Adding..." : "Add Member"), D((M) => {
|
|
@@ -269,7 +269,7 @@ function et(e) {
|
|
|
269
269
|
}), D(() => S.value = t()), i;
|
|
270
270
|
})(), (() => {
|
|
271
271
|
var i = Le();
|
|
272
|
-
return D((S) => E(i,
|
|
272
|
+
return D((S) => E(i, ze, S)), i;
|
|
273
273
|
})()];
|
|
274
274
|
}
|
|
275
275
|
}), j), f(j, m(_, {
|
|
@@ -316,7 +316,7 @@ function et(e) {
|
|
|
316
316
|
}), null), D((i) => {
|
|
317
317
|
var S = Ce, K = Ze, V = {
|
|
318
318
|
...ue
|
|
319
|
-
}, M =
|
|
319
|
+
}, M = Ue, n = Ue;
|
|
320
320
|
return i.e = E(a, S, i.e), i.t = E(s, K, i.t), i.a = E(P, V, i.a), i.o = E(L, M, i.o), i.i = E(ce, n, i.i), i;
|
|
321
321
|
}, {
|
|
322
322
|
e: void 0,
|
|
@@ -346,10 +346,10 @@ function nt(e) {
|
|
|
346
346
|
return t.$$click = (v) => {
|
|
347
347
|
v.target === v.currentTarget && e.onCancel();
|
|
348
348
|
}, f(g, () => e.title), f(u, () => e.message), k.$$click = () => e.onCancel(), $.$$click = () => e.onConfirm(), f($, () => e.confirmLabel ?? "Confirm"), D((v) => {
|
|
349
|
-
var N = Ce,
|
|
349
|
+
var N = Ce, U = {
|
|
350
350
|
...Se
|
|
351
351
|
};
|
|
352
|
-
return v.e = E(t, N, v.e), v.t = E(l,
|
|
352
|
+
return v.e = E(t, N, v.e), v.t = E(l, U, v.t), v;
|
|
353
353
|
}, {
|
|
354
354
|
e: void 0,
|
|
355
355
|
t: void 0
|
|
@@ -379,14 +379,14 @@ function it(e) {
|
|
|
379
379
|
return e.isOpen;
|
|
380
380
|
},
|
|
381
381
|
get children() {
|
|
382
|
-
var p = rt(), k = p.firstChild, $ = k.firstChild, v = $.nextSibling, N = v.nextSibling,
|
|
382
|
+
var p = rt(), k = p.firstChild, $ = k.firstChild, v = $.nextSibling, N = v.nextSibling, U = N.firstChild, oe = U.nextSibling;
|
|
383
383
|
p.$$click = (A) => {
|
|
384
384
|
A.target === A.currentTarget && e.onCancel();
|
|
385
385
|
}, v.$$keydown = (A) => {
|
|
386
386
|
A.key === "Enter" && u();
|
|
387
387
|
}, v.$$input = (A) => l(A.currentTarget.value);
|
|
388
388
|
var H = g;
|
|
389
|
-
return typeof H == "function" ? Ee(H, v) : g = v,
|
|
389
|
+
return typeof H == "function" ? Ee(H, v) : g = v, U.$$click = () => e.onCancel(), oe.$$click = () => u(), D((A) => {
|
|
390
390
|
var ae = Ce, ee = {
|
|
391
391
|
...Se
|
|
392
392
|
}, te = e.defaultTitle;
|
|
@@ -403,7 +403,7 @@ xe(["click", "input", "keydown"]);
|
|
|
403
403
|
const ot = {};
|
|
404
404
|
var at = /* @__PURE__ */ w('<div style="padding:8px 10px;color:#6b7280;font-style:italic">No documents yet'), lt = /* @__PURE__ */ w('<div style="position:absolute;top:34px;left:0;background:#191e24;border:1px solid #2a323c;border-radius:4px;max-height:400px;overflow-y:auto;min-width:280px;max-width:420px;z-index:1000;box-shadow:0 4px 12px rgba(0,0,0,0.4)">'), ct = /* @__PURE__ */ w('<button style="background:none;border:1px solid #2a323c;color:#edf2f7;font-size:13px;padding:4px 10px;border-radius:4px;cursor:pointer;white-space:nowrap">Share'), st = /* @__PURE__ */ w('<button title="Copy automerge URL"style="background:none;border:1px solid #2a323c;font-size:13px;padding:4px 10px;border-radius:4px;cursor:pointer;white-space:nowrap">'), dt = /* @__PURE__ */ w('<button style="background:none;border:1px solid #2a323c;font-size:13px;padding:4px 10px;border-radius:4px;cursor:pointer;white-space:nowrap">'), ut = /* @__PURE__ */ w('<button style="background:none;border:1px solid #944;color:#c66;font-size:13px;padding:4px 10px;border-radius:4px;cursor:pointer;white-space:nowrap">Remove Doc'), ft = /* @__PURE__ */ w('<div style=display:flex;flex-direction:column;height:100vh;background:#1d232a><div style="display:flex;align-items:center;height:52px;min-height:52px;background:#191e24;color:#edf2f7;font-size:14px;font-family:system-ui, sans-serif;padding:0 12px;border-bottom:1px solid #15191e;box-sizing:border-box"><button style="background:none;border:1px solid #2a323c;color:#edf2f7;font-size:13px;padding:4px 10px;border-radius:4px;cursor:pointer;flex-shrink:0;margin-right:12px;white-space:nowrap">+ New</button><input type=text placeholder="Paste automerge:… URL"style="background:#15191e;border:1px solid #2a323c;color:#edf2f7;font-size:13px;padding:4px 10px;border-radius:4px;width:200px;min-width:80px;flex-shrink:1;margin-right:12px;outline:none"><div data-doc-switcher style=position:relative;flex:1;min-width:120px><button style="background:none;border:none;color:#edf2f7;font-size:14px;cursor:pointer;padding:4px 8px;border-radius:4px;max-width:100%;display:flex;align-items:center;gap:4px"><span style=overflow:hidden;text-overflow:ellipsis;white-space:nowrap></span><span style=flex-shrink:0>▾</span></button></div><div style=display:flex;align-items:center;flex-shrink:0;gap:8px></div></div><div style=flex:1;min-height:0;overflow:hidden>'), ht = /* @__PURE__ */ w('<div style="padding:8px 12px;cursor:pointer;border-bottom:1px solid #15191e;overflow:hidden;text-overflow:ellipsis;white-space:nowrap"> <span style=color:#6b7280;font-size:12px>'), gt = /* @__PURE__ */ w('<div style="width:24px;height:24px;border:2px solid #2a323c;border-top-color:#6b7280;border-radius:50%;animation:spin 0.8s linear infinite">'), pt = /* @__PURE__ */ w('<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;height:100%;background:#1d232a;font-family:system-ui, sans-serif;font-size:14px">'), mt = /* @__PURE__ */ w("<div style=height:100%>");
|
|
405
405
|
function yt(e) {
|
|
406
|
-
const t = e.tool, [l, g] = x(null), [u, p] = x("..."), [k, $] = x([]), [v, N] = x(!1), [
|
|
406
|
+
const t = e.tool, [l, g] = x(null), [u, p] = x("..."), [k, $] = x([]), [v, N] = x(!1), [U, oe] = x(null), [H, A] = x(""), [ae, ee] = x(!1), [te, G] = x(!1), [be, Q] = x(!1), [le, fe] = x(!1), [he, ge] = x(!1), [ne, pe] = x(!1), [re, a] = x(!1), [s, C] = x(null), [z, P] = x(!1);
|
|
407
407
|
let T, L = null, F = 0;
|
|
408
408
|
function j(n) {
|
|
409
409
|
L && (L(), L = null);
|
|
@@ -419,7 +419,7 @@ function yt(e) {
|
|
|
419
419
|
n.on("change", c), L = () => n.off("change", c);
|
|
420
420
|
}
|
|
421
421
|
async function ce(n) {
|
|
422
|
-
const r =
|
|
422
|
+
const r = U();
|
|
423
423
|
if (!r) return !0;
|
|
424
424
|
try {
|
|
425
425
|
const {
|
|
@@ -479,7 +479,7 @@ function yt(e) {
|
|
|
479
479
|
t.init(r, T), t.setTitle && t.setTitle(r, n), r["@patchwork"] = {
|
|
480
480
|
type: t.id
|
|
481
481
|
};
|
|
482
|
-
const h =
|
|
482
|
+
const h = U();
|
|
483
483
|
let c;
|
|
484
484
|
h ? (c = await T.create2(r), console.log(`[${t.name}] Created document (keyhive): ${c.url}`), await h.addSyncServerPullToDoc(c.url), await h.keyhiveStorage.saveKeyhiveWithHash(h.keyhive)) : (c = T.create(r), console.log(`[${t.name}] Created document (legacy): ${c.url}`)), g(c), se(c.url), localStorage.setItem(me(t.id, H()), c.url), j(c);
|
|
485
485
|
}
|
|
@@ -493,7 +493,7 @@ function yt(e) {
|
|
|
493
493
|
K(), T.delete(r), $(Ne(t.id, H(), r));
|
|
494
494
|
}
|
|
495
495
|
async function M() {
|
|
496
|
-
const n =
|
|
496
|
+
const n = U();
|
|
497
497
|
if (n)
|
|
498
498
|
try {
|
|
499
499
|
const r = n.active.contactCard.toJson();
|
|
@@ -521,7 +521,7 @@ function yt(e) {
|
|
|
521
521
|
BrowserWebSocketClientAdapter: b
|
|
522
522
|
}, y] = await Promise.all([import("@automerge/automerge-repo"), import("@automerge/automerge-repo-storage-indexeddb"), import("@automerge/automerge-repo-network-websocket"), import("@automerge/automerge-repo-keyhive")]);
|
|
523
523
|
y.initKeyhiveWasm();
|
|
524
|
-
const J = new c(`${t.id}-keyhive`), R = ot?.VITE_SYNC_URL, W = new b(R || t.syncUrl || "ws://localhost:3030"), o = `${t.id}-${Math.random().toString(36).slice(2)}`, d = await y.initializeAutomergeRepoKeyhive({
|
|
524
|
+
const J = new c(`${t.id}-keyhive`), R = ot?.VITE_SYNC_URL, W = new b(e.config?.syncUrl || R || t.syncUrl || "ws://localhost:3030"), o = `${t.id}-${Math.random().toString(36).slice(2)}`, d = await y.initializeAutomergeRepoKeyhive({
|
|
525
525
|
storage: J,
|
|
526
526
|
peerIdSuffix: o,
|
|
527
527
|
networkAdapter: W,
|
|
@@ -620,7 +620,7 @@ function yt(e) {
|
|
|
620
620
|
}
|
|
621
621
|
}), null), f(R, m(_, {
|
|
622
622
|
get when() {
|
|
623
|
-
return Z(() => !!l())() &&
|
|
623
|
+
return Z(() => !!l())() && U();
|
|
624
624
|
},
|
|
625
625
|
get children() {
|
|
626
626
|
var o = ct();
|
|
@@ -639,7 +639,7 @@ function yt(e) {
|
|
|
639
639
|
}
|
|
640
640
|
}), null), f(R, m(_, {
|
|
641
641
|
get when() {
|
|
642
|
-
return
|
|
642
|
+
return U();
|
|
643
643
|
},
|
|
644
644
|
get children() {
|
|
645
645
|
var o = dt();
|
|
@@ -663,14 +663,14 @@ function yt(e) {
|
|
|
663
663
|
var o = pt();
|
|
664
664
|
return f(o, m(_, {
|
|
665
665
|
get when() {
|
|
666
|
-
return
|
|
666
|
+
return z() || !ne();
|
|
667
667
|
},
|
|
668
668
|
get children() {
|
|
669
669
|
return gt();
|
|
670
670
|
}
|
|
671
671
|
}), null), f(o, (() => {
|
|
672
672
|
var d = Z(() => !!re());
|
|
673
|
-
return () => d() ? "Document unavailable: you may not have access" : Z(() => !!
|
|
673
|
+
return () => d() ? "Document unavailable: you may not have access" : Z(() => !!z())() ? "Loading document..." : Z(() => !!ne())() ? "No document open" : e.config?.legacyMode ? "Initializing..." : "Initializing keyhive...";
|
|
674
674
|
})(), null), D((d) => X(o, "color", re() ? "#c66" : "#6b7280")), o;
|
|
675
675
|
})();
|
|
676
676
|
},
|
|
@@ -685,7 +685,7 @@ function yt(e) {
|
|
|
685
685
|
}
|
|
686
686
|
})), f(n, m(_, {
|
|
687
687
|
get when() {
|
|
688
|
-
return Z(() => !!
|
|
688
|
+
return Z(() => !!U())() && l();
|
|
689
689
|
},
|
|
690
690
|
get children() {
|
|
691
691
|
return m(et, {
|
|
@@ -696,7 +696,7 @@ function yt(e) {
|
|
|
696
696
|
return l().url;
|
|
697
697
|
},
|
|
698
698
|
get hive() {
|
|
699
|
-
return
|
|
699
|
+
return U();
|
|
700
700
|
},
|
|
701
701
|
onClose: () => ee(!1)
|
|
702
702
|
});
|
package/package.json
CHANGED
package/src/frame.tsx
CHANGED
|
@@ -257,7 +257,7 @@ export function StandaloneApp<D>(props: { tool: ToolRegistration<D>; config?: St
|
|
|
257
257
|
const keyhiveStorage = new IndexedDBStorageAdapter(`${tool.id}-keyhive`);
|
|
258
258
|
const envSyncUrl = (import.meta as unknown as Record<string, Record<string, string>>).env?.VITE_SYNC_URL;
|
|
259
259
|
const networkAdapter = new BrowserWebSocketClientAdapter(
|
|
260
|
-
envSyncUrl || tool.syncUrl || "ws://localhost:3030"
|
|
260
|
+
props.config?.syncUrl || envSyncUrl || tool.syncUrl || "ws://localhost:3030"
|
|
261
261
|
);
|
|
262
262
|
const peerIdSuffix = `${tool.id}-${Math.random().toString(36).slice(2)}`;
|
|
263
263
|
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,8 @@ export interface StandaloneFrameConfig {
|
|
|
39
39
|
legacyMode?: boolean;
|
|
40
40
|
/** Pre-built repo for legacy mode. Required when legacyMode is true. */
|
|
41
41
|
repo?: FrameRepo;
|
|
42
|
+
/** WebSocket sync server URL. Overrides tool.syncUrl. */
|
|
43
|
+
syncUrl?: string;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export interface PluginDescription {
|