dsh-claude-data 0.0.0-stage → 2.13.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 +26 -0
- package/NOTICE.md +62 -0
- package/README.md +43 -2
- package/cordis.patch.yml +13 -0
- package/docs/DETAIL.md +27 -0
- package/docs/DETAIL.zh.md +16 -0
- package/icon.svg +9 -0
- package/lib/index.js +167 -0
- package/locale/en.json +7 -0
- package/locale/zh.json +7 -0
- package/package.json +104 -4
- package/skills/claude-data/SKILL.md +536 -0
- package/skills/claude-data/artifact-type/reference/admin-setup.md +64 -0
- package/skills/claude-data/artifact-type/reference/dependent-calls.md +33 -0
- package/skills/claude-data/artifact-type/reference/design-defaults.md +51 -0
- package/skills/claude-data/artifact-type/reference/design-system.md +65 -0
- package/skills/claude-data/artifact-type/reference/exporting.md +23 -0
- package/skills/claude-data/artifact-type/reference/features.md +54 -0
- package/skills/claude-data/artifact-type/reference/looks.md +55 -0
- package/skills/claude-data/artifact-type/reference/one-writer.md +51 -0
- package/skills/claude-data/artifact-type/reference/questions.md +45 -0
- package/skills/claude-data/artifact-type/reference/read-aloud.md +61 -0
- package/skills/claude-data/artifact-type/reference/remix.md +43 -0
- package/skills/claude-data/artifact-type/reference/sources.md +63 -0
- package/skills/claude-data/artifact-type/reference/visual-short-script.md +146 -0
- package/skills/claude-data/artifact-type/reference/visual-short-skeleton.md +131 -0
- package/skills/claude-data/artifact-type/reference/visual-short.md +63 -0
- package/skills/claude-data/scripts/artifact.mjs +5688 -0
- package/skills/claude-data/scripts/editor/assets/anthropicons-fizzy-lake-CPTdBYLV.woff2 +0 -0
- package/skills/claude-data/scripts/editor/assets/index-B1CKpH_P.css +1 -0
- package/skills/claude-data/scripts/editor/assets/index-BWg-0IkN.js +569 -0
- package/skills/claude-data/scripts/editor/assets/working-mark-sheets-dAm26n99.js +1 -0
- package/skills/claude-data/scripts/editor/comments.js +103 -0
- package/skills/claude-data/scripts/editor/index.html +27 -0
- package/skills/claude-data/scripts/editor/local-host.js +481 -0
- package/skills/claude-data/scripts/editor/share-ui.js +208 -0
- package/skills/claude-data/scripts/editor/third-party-licenses.txt +1213 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="cds-root" data-density="comfortable" data-thumb-defer>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<script>
|
|
6
|
+
(function(){var values={"dashboard_design_system":true,"dashboard_edit":true,"appifact_icon_set":true};function kind(v){return v===null?"null":Array.isArray(v)?"array":typeof v}function copy(v){return JSON.parse(JSON.stringify(v))}function get(key,fallback){if(!Object.prototype.hasOwnProperty.call(values,key)||kind(values[key])!==kind(fallback))return fallback;return copy(values[key])}globalThis.__appifact_flags={version:"appifact-flags:v2",ready:function(){return Promise.resolve(true)},get:get,latest:get,onLatest:function(){return function(){}},askAgain:function(){},all:function(){return copy(values)}};})();
|
|
7
|
+
</script>
|
|
8
|
+
<title>Dashboard</title>
|
|
9
|
+
<!--
|
|
10
|
+
claude-data: the claude.ai Dashboard type runtime, served locally by scripts/artifact.mjs serve.
|
|
11
|
+
A dashboard's content is its store, <root>/db/<collection>/<doc_id>.json; local-host.js gives the runtime
|
|
12
|
+
globalThis.claude (db, assets, user, downloads) over HTTP to that server; comments.js draws the comments
|
|
13
|
+
layer (threads in <root>/comments.json). Prebuilt; see SKILL.md.
|
|
14
|
+
-->
|
|
15
|
+
<meta name="appifact-capabilities" content="{"downloads":{},"db":{"rules":[{"path":"","write":"admin"},{"path":"data/users/{self}","read":"view","write":"interact"},{"path":"versions","read":"admin"},{"path":"version-docs","read":"admin"},{"path":"drafts","read":"admin"}]},"assets":{},"user":{"scopes":["profile"]}}">
|
|
16
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
17
|
+
<script id="appifact-mode">(function(){var h=document.documentElement;function s(){var t=h.getAttribute('data-theme');if(t==='light'||t==='dark'){h.setAttribute('data-mode',t)}else{h.removeAttribute('data-mode')}}s();if(typeof MutationObserver==='function'){new MutationObserver(s).observe(h,{attributes:true,attributeFilter:['data-theme']})}})();</script>
|
|
18
|
+
|
|
19
|
+
<script src="./comments.js"></script>
|
|
20
|
+
<script src="./local-host.js"></script>
|
|
21
|
+
<script type="module" crossorigin src="./assets/index-BWg-0IkN.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B1CKpH_P.css">
|
|
23
|
+
</head>
|
|
24
|
+
<body>
|
|
25
|
+
<div id="root"></div>
|
|
26
|
+
</body>
|
|
27
|
+
</html>
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
// claude-data local host: globalThis.claude for the Dashboard runtime (prebuilt; do not edit).
|
|
2
|
+
"use strict";
|
|
3
|
+
(() => {
|
|
4
|
+
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
5
|
+
var isDeleteMarker = (v, nullStored) => v === null && !nullStored || isObject(v) && v.__delete__ === true && Object.keys(v).length === 1;
|
|
6
|
+
function patched(target, patch, nullStored) {
|
|
7
|
+
const out = { ...target };
|
|
8
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
9
|
+
if (k === "__proto__") continue;
|
|
10
|
+
if (isDeleteMarker(v, nullStored)) delete out[k];
|
|
11
|
+
else if (isObject(v)) out[k] = patched(isObject(out[k]) ? out[k] : {}, v, nullStored);
|
|
12
|
+
else out[k] = v;
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
var EXPECT_MS = 3e4;
|
|
17
|
+
function writeBases(store2, known = () => false, { nullStored = false } = {}) {
|
|
18
|
+
const wrote = /* @__PURE__ */ new Map();
|
|
19
|
+
const writing = /* @__PURE__ */ new Map();
|
|
20
|
+
const expected = /* @__PURE__ */ new Map();
|
|
21
|
+
return {
|
|
22
|
+
begin(path, op, data) {
|
|
23
|
+
const seen = store2.get(path);
|
|
24
|
+
const told = expected.get(path);
|
|
25
|
+
expected.delete(path);
|
|
26
|
+
const fresh = told !== void 0 && Date.now() - told.at < EXPECT_MS;
|
|
27
|
+
const from = fresh ? told.base : wrote.get(path);
|
|
28
|
+
const base = from ?? (seen !== void 0 ? JSON.parse(seen) : known() ? null : void 0);
|
|
29
|
+
const body = JSON.stringify({ op, path, data, base, ...fresh && told.chosen ? { chosen: true } : {} });
|
|
30
|
+
const sent = JSON.parse(body);
|
|
31
|
+
const after = !isObject(sent.data) ? void 0 : op === "set" ? sent.data : isObject(sent.base) ? patched(sent.base, sent.data, nullStored) : sent.base === null ? patched({}, sent.data, nullStored) : void 0;
|
|
32
|
+
const before = wrote.get(path);
|
|
33
|
+
if (after) wrote.set(path, after);
|
|
34
|
+
else wrote.delete(path);
|
|
35
|
+
writing.set(path, (writing.get(path) ?? 0) + 1);
|
|
36
|
+
return {
|
|
37
|
+
body,
|
|
38
|
+
done(ok) {
|
|
39
|
+
const left = (writing.get(path) ?? 1) - 1;
|
|
40
|
+
if (left > 0) writing.set(path, left);
|
|
41
|
+
else writing.delete(path);
|
|
42
|
+
if (!ok && wrote.get(path) === after) {
|
|
43
|
+
if (before) wrote.set(path, before);
|
|
44
|
+
else wrote.delete(path);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
changed(path) {
|
|
50
|
+
for (const p of path === void 0 ? [...wrote.keys()] : [path]) if (!writing.has(p)) wrote.delete(p);
|
|
51
|
+
},
|
|
52
|
+
expect(path, base, chosen) {
|
|
53
|
+
if (isObject(base)) expected.set(path, { base: JSON.parse(JSON.stringify(base)), at: Date.now(), chosen: chosen === true });
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var MAX_PRESENCE_BYTES = 4096;
|
|
59
|
+
var EMPTY = Object.freeze([]);
|
|
60
|
+
var bytesOf = (value) => new TextEncoder().encode(JSON.stringify(value)).length;
|
|
61
|
+
function createRoomClient(options) {
|
|
62
|
+
const serverMe = options.serverMe ?? options.me;
|
|
63
|
+
const local = (by) => by === serverMe ? options.me : by;
|
|
64
|
+
const doFetch = options.fetch ?? ((...args) => fetch(...args));
|
|
65
|
+
let views = [];
|
|
66
|
+
let snapshot = EMPTY;
|
|
67
|
+
let byPeer = /* @__PURE__ */ new Map();
|
|
68
|
+
let heard = false;
|
|
69
|
+
let open = false;
|
|
70
|
+
const handlers = /* @__PURE__ */ new Set();
|
|
71
|
+
const connectionListeners = /* @__PURE__ */ new Set();
|
|
72
|
+
const pageListeners = /* @__PURE__ */ new Set();
|
|
73
|
+
let published = {};
|
|
74
|
+
let queued = null;
|
|
75
|
+
let waiters = [];
|
|
76
|
+
let sending = false;
|
|
77
|
+
let frame = false;
|
|
78
|
+
const peerOf = (view, previous) => {
|
|
79
|
+
const presence2 = view.peer === options.peer ? published : view.presence;
|
|
80
|
+
const json = JSON.stringify(presence2);
|
|
81
|
+
const isMe = view.kind !== "agent" && view.by === serverMe;
|
|
82
|
+
if (previous && previous.json === json && previous.peer.isMe === isMe) return previous;
|
|
83
|
+
const peer = Object.freeze({
|
|
84
|
+
peer: view.peer,
|
|
85
|
+
by: view.kind === "agent" ? null : local(view.by),
|
|
86
|
+
isMe,
|
|
87
|
+
sameTab: view.peer === options.peer,
|
|
88
|
+
kind: view.kind === "agent" ? "agent" : "viewer",
|
|
89
|
+
guest: false,
|
|
90
|
+
presence: Object.freeze({ ...presence2 }),
|
|
91
|
+
updatedAt: Date.now()
|
|
92
|
+
});
|
|
93
|
+
return { peer, json };
|
|
94
|
+
};
|
|
95
|
+
const rebuild = () => {
|
|
96
|
+
const next = /* @__PURE__ */ new Map();
|
|
97
|
+
let changed = views.length !== byPeer.size;
|
|
98
|
+
for (const view of views) {
|
|
99
|
+
const before = byPeer.get(view.peer);
|
|
100
|
+
const entry = peerOf(view, before);
|
|
101
|
+
if (entry !== before) changed = true;
|
|
102
|
+
next.set(view.peer, entry);
|
|
103
|
+
}
|
|
104
|
+
byPeer = next;
|
|
105
|
+
if (changed) snapshot = Object.freeze([...next.values()].map((e) => e.peer));
|
|
106
|
+
return changed;
|
|
107
|
+
};
|
|
108
|
+
const deliver2 = () => {
|
|
109
|
+
frame = false;
|
|
110
|
+
for (const entry of handlers) {
|
|
111
|
+
const before = entry.delivered;
|
|
112
|
+
const now = new Map(snapshot.map((p) => [p.peer, p]));
|
|
113
|
+
const joined = [];
|
|
114
|
+
const updated = [];
|
|
115
|
+
const left = [];
|
|
116
|
+
for (const p of snapshot) {
|
|
117
|
+
const was = before?.get(p.peer);
|
|
118
|
+
if (!was) joined.push(p);
|
|
119
|
+
else if (was !== p) updated.push(p);
|
|
120
|
+
}
|
|
121
|
+
if (before) {
|
|
122
|
+
for (const [id, p] of before) if (!now.has(id)) left.push(p);
|
|
123
|
+
}
|
|
124
|
+
if (before && !joined.length && !updated.length && !left.length) continue;
|
|
125
|
+
entry.delivered = now;
|
|
126
|
+
entry.handler({ peers: snapshot, joined, left, updated });
|
|
127
|
+
}
|
|
128
|
+
for (const listener of pageListeners) listener();
|
|
129
|
+
};
|
|
130
|
+
const schedule = () => {
|
|
131
|
+
if (frame) return;
|
|
132
|
+
frame = true;
|
|
133
|
+
nextFrame(deliver2);
|
|
134
|
+
};
|
|
135
|
+
const flush2 = async () => {
|
|
136
|
+
if (sending || !queued || !open) return;
|
|
137
|
+
sending = true;
|
|
138
|
+
const patch = queued;
|
|
139
|
+
const done = waiters;
|
|
140
|
+
queued = null;
|
|
141
|
+
waiters = [];
|
|
142
|
+
try {
|
|
143
|
+
const res = await doFetch(`${options.base}__presence`, {
|
|
144
|
+
method: "POST",
|
|
145
|
+
headers: { "Content-Type": "application/json" },
|
|
146
|
+
body: JSON.stringify({ peer: options.peer, patch })
|
|
147
|
+
});
|
|
148
|
+
if (res.status === 409) {
|
|
149
|
+
for (const w of done) w.resolve();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!res.ok) throw { code: "upstream_error", message: `presence: HTTP ${res.status}` };
|
|
153
|
+
for (const w of done) w.resolve();
|
|
154
|
+
} catch (error) {
|
|
155
|
+
for (const w of done) w.reject(typeof error === "object" && error !== null && "code" in error ? error : { code: "upstream_error", message: String(error) });
|
|
156
|
+
} finally {
|
|
157
|
+
sending = false;
|
|
158
|
+
if (queued && open) void flush2();
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
const room = {
|
|
162
|
+
presence(patch) {
|
|
163
|
+
if (typeof patch !== "object" || patch === null || Array.isArray(patch)) {
|
|
164
|
+
return Promise.reject({ code: "invalid_argument", message: "presence(patch) takes an object." });
|
|
165
|
+
}
|
|
166
|
+
const merged = { ...published };
|
|
167
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
168
|
+
if (value === null || value === void 0) delete merged[key];
|
|
169
|
+
else merged[key] = value;
|
|
170
|
+
}
|
|
171
|
+
if (bytesOf(merged) > MAX_PRESENCE_BYTES) {
|
|
172
|
+
return Promise.reject({ code: "invalid_argument", message: `Presence is at most ${MAX_PRESENCE_BYTES} bytes of JSON.` });
|
|
173
|
+
}
|
|
174
|
+
published = merged;
|
|
175
|
+
if (rebuild()) schedule();
|
|
176
|
+
queued = { ...queued ?? {}, ...patch };
|
|
177
|
+
if (!open) return Promise.resolve();
|
|
178
|
+
const promise = new Promise((resolve, reject) => waiters.push({ resolve, reject }));
|
|
179
|
+
void flush2();
|
|
180
|
+
return promise;
|
|
181
|
+
},
|
|
182
|
+
peers: () => snapshot,
|
|
183
|
+
onPeers(handler, _onError) {
|
|
184
|
+
const entry = { handler, delivered: null };
|
|
185
|
+
handlers.add(entry);
|
|
186
|
+
if (heard) queueMicrotask(() => handlers.has(entry) && entry.delivered === null && schedule());
|
|
187
|
+
return () => void handlers.delete(entry);
|
|
188
|
+
},
|
|
189
|
+
connected: () => open,
|
|
190
|
+
onConnection(onChange) {
|
|
191
|
+
connectionListeners.add(onChange);
|
|
192
|
+
return () => void connectionListeners.delete(onChange);
|
|
193
|
+
},
|
|
194
|
+
emit: () => Promise.resolve(),
|
|
195
|
+
on: () => () => {
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
const page = {
|
|
199
|
+
me: { peer: options.peer, by: serverMe },
|
|
200
|
+
peers: () => views.map((v) => v.peer === options.peer ? { ...v, presence: published } : v),
|
|
201
|
+
subscribe(onChange) {
|
|
202
|
+
pageListeners.add(onChange);
|
|
203
|
+
return () => void pageListeners.delete(onChange);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
if (typeof window !== "undefined") {
|
|
207
|
+
window.__baoyuPresence = page;
|
|
208
|
+
window.dispatchEvent(new Event("baoyu-presence"));
|
|
209
|
+
}
|
|
210
|
+
const setOpen = (next) => {
|
|
211
|
+
if (open === next) return;
|
|
212
|
+
open = next;
|
|
213
|
+
for (const listener of connectionListeners) listener(next);
|
|
214
|
+
if (next) {
|
|
215
|
+
if (Object.keys(published).length) queued = { ...published };
|
|
216
|
+
void flush2();
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
return {
|
|
220
|
+
room,
|
|
221
|
+
page,
|
|
222
|
+
nameOf: (by) => views.find((v) => v.kind !== "agent" && local(v.by) === by)?.name,
|
|
223
|
+
attach(source2) {
|
|
224
|
+
source2.addEventListener("peers", (event) => {
|
|
225
|
+
let list;
|
|
226
|
+
try {
|
|
227
|
+
const body = JSON.parse(event.data);
|
|
228
|
+
if (!Array.isArray(body.peers)) return;
|
|
229
|
+
list = body.peers.filter(
|
|
230
|
+
(p) => typeof p === "object" && p !== null && typeof p.peer === "string" && typeof p.by === "string"
|
|
231
|
+
);
|
|
232
|
+
} catch {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
views = list;
|
|
236
|
+
heard = true;
|
|
237
|
+
setOpen(true);
|
|
238
|
+
rebuild();
|
|
239
|
+
schedule();
|
|
240
|
+
});
|
|
241
|
+
source2.addEventListener("error", () => {
|
|
242
|
+
if (source2.readyState !== EventSource.OPEN) setOpen(false);
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function shareIdentity() {
|
|
248
|
+
const share = typeof window === "undefined" ? void 0 : window.__baoyuShare;
|
|
249
|
+
if (!share || typeof share.id !== "string" || typeof share.name !== "string") return null;
|
|
250
|
+
return { id: share.id, name: share.name, role: typeof share.role === "string" ? share.role : "" };
|
|
251
|
+
}
|
|
252
|
+
var onPreview = () => typeof window !== "undefined" && Boolean(window.__baoyuHistory?.preview);
|
|
253
|
+
function pageRoom(base, peer) {
|
|
254
|
+
const share = shareIdentity();
|
|
255
|
+
if (!share || onPreview()) return null;
|
|
256
|
+
return createRoomClient({ base, peer, me: share.id });
|
|
257
|
+
}
|
|
258
|
+
function nextFrame(run) {
|
|
259
|
+
let done = false;
|
|
260
|
+
const once = () => {
|
|
261
|
+
if (done) return;
|
|
262
|
+
done = true;
|
|
263
|
+
run();
|
|
264
|
+
};
|
|
265
|
+
if (typeof requestAnimationFrame === "function" && typeof document !== "undefined" && document.visibilityState === "visible") requestAnimationFrame(once);
|
|
266
|
+
setTimeout(once, typeof document !== "undefined" && document.visibilityState === "visible" ? 100 : 16);
|
|
267
|
+
}
|
|
268
|
+
function newPeerId() {
|
|
269
|
+
const bytes = new Uint8Array(12);
|
|
270
|
+
crypto.getRandomValues(bytes);
|
|
271
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
var AT = /^\d{1,9}$/.test(new URLSearchParams(location.search).get("at") ?? "") ? new URLSearchParams(location.search).get("at") : null;
|
|
275
|
+
var api = (route) => AT === null ? route : `${route}${route.includes("?") ? "&" : "?"}at=${AT}`;
|
|
276
|
+
var UNAVAILABLE = Object.freeze({ available: false });
|
|
277
|
+
var user = { id: "local", name: "You", canEdit: true, owner: true };
|
|
278
|
+
var sessionReady = fetch(api("/_api/session"), { cache: "no-store" }).then((r) => r.ok ? r.json() : null).then((s) => {
|
|
279
|
+
if (!s) return;
|
|
280
|
+
if (typeof s.userId === "string" && s.userId) user.id = s.userId;
|
|
281
|
+
if (typeof s.userName === "string" && s.userName) user.name = s.userName;
|
|
282
|
+
if (typeof s.canEdit === "boolean") user.canEdit = s.canEdit;
|
|
283
|
+
if (typeof s.role === "string") user.owner = s.role === "owner";
|
|
284
|
+
}).catch(() => {
|
|
285
|
+
});
|
|
286
|
+
var clone = (v) => v === void 0 ? void 0 : JSON.parse(JSON.stringify(v));
|
|
287
|
+
var dbError = (code, message) => Object.assign(new Error(message), { code, message });
|
|
288
|
+
async function call(url, init) {
|
|
289
|
+
let res;
|
|
290
|
+
try {
|
|
291
|
+
res = await fetch(url, { cache: "no-store", ...init });
|
|
292
|
+
} catch {
|
|
293
|
+
throw dbError("unavailable", "The local dashboard server is not running.");
|
|
294
|
+
}
|
|
295
|
+
const body = await res.json().catch(() => null);
|
|
296
|
+
if (!res.ok) throw dbError(body && body.code || "unavailable", body && body.message || `HTTP ${res.status}`);
|
|
297
|
+
return body;
|
|
298
|
+
}
|
|
299
|
+
var store = /* @__PURE__ */ new Map();
|
|
300
|
+
var bases = writeBases(store, () => live, { nullStored: true });
|
|
301
|
+
window.__baoyuWriteBase = (path, base, chosen) => bases.expect(path, base, chosen);
|
|
302
|
+
var live = false;
|
|
303
|
+
var subs = /* @__PURE__ */ new Set();
|
|
304
|
+
var source = null;
|
|
305
|
+
var PEER = newPeerId();
|
|
306
|
+
var presence = AT === null ? pageRoom(new URL("./", location.href).href, PEER) : null;
|
|
307
|
+
var parentOf = (path) => path.slice(0, path.lastIndexOf("/"));
|
|
308
|
+
var idOf = (path) => path.slice(path.lastIndexOf("/") + 1);
|
|
309
|
+
function deliver(sub, initial) {
|
|
310
|
+
const changes = [];
|
|
311
|
+
const seen = /* @__PURE__ */ new Set();
|
|
312
|
+
for (const [path, text] of store) {
|
|
313
|
+
if (parentOf(path) !== sub.collection) continue;
|
|
314
|
+
seen.add(path);
|
|
315
|
+
const before = sub.delivered.get(path);
|
|
316
|
+
if (before === text) continue;
|
|
317
|
+
sub.delivered.set(path, text);
|
|
318
|
+
changes.push({ type: before === void 0 ? "added" : "modified", path, text });
|
|
319
|
+
}
|
|
320
|
+
for (const [path, text] of sub.delivered) {
|
|
321
|
+
if (seen.has(path)) continue;
|
|
322
|
+
sub.delivered.delete(path);
|
|
323
|
+
changes.push({ type: "removed", path, text });
|
|
324
|
+
}
|
|
325
|
+
if (!initial && changes.length === 0) return;
|
|
326
|
+
const docChanges = changes.map((c) => {
|
|
327
|
+
const data = JSON.parse(c.text);
|
|
328
|
+
return { type: c.type, doc: { id: idOf(c.path), data: () => clone(data) } };
|
|
329
|
+
});
|
|
330
|
+
try {
|
|
331
|
+
sub.next({ docChanges: () => docChanges, metadata: { fromCache: false, hasPendingWrites: false } });
|
|
332
|
+
} catch (error) {
|
|
333
|
+
console.error("local host: snapshot listener threw", error);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function flush() {
|
|
337
|
+
for (const sub of subs) {
|
|
338
|
+
const initial = !sub.started;
|
|
339
|
+
sub.started = true;
|
|
340
|
+
deliver(sub, initial);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function connect() {
|
|
344
|
+
if (source) return;
|
|
345
|
+
source = new EventSource(api(presence ? `/_api/events?peer=${PEER}` : "/_api/events"));
|
|
346
|
+
presence?.attach(source);
|
|
347
|
+
source.addEventListener("snapshot", (event) => {
|
|
348
|
+
const { docs } = JSON.parse(event.data);
|
|
349
|
+
store.clear();
|
|
350
|
+
for (const [path, data] of Object.entries(docs)) store.set(path, JSON.stringify(data));
|
|
351
|
+
bases.changed();
|
|
352
|
+
live = true;
|
|
353
|
+
flush();
|
|
354
|
+
});
|
|
355
|
+
source.addEventListener("change", (event) => {
|
|
356
|
+
const { changes } = JSON.parse(event.data);
|
|
357
|
+
for (const { path, data } of changes) {
|
|
358
|
+
if (data === null) store.delete(path);
|
|
359
|
+
else store.set(path, JSON.stringify(data));
|
|
360
|
+
bases.changed(path);
|
|
361
|
+
}
|
|
362
|
+
flush();
|
|
363
|
+
});
|
|
364
|
+
source.addEventListener("error", () => {
|
|
365
|
+
if (!live) return;
|
|
366
|
+
live = false;
|
|
367
|
+
const failed = [...subs];
|
|
368
|
+
subs.clear();
|
|
369
|
+
for (const sub of failed) {
|
|
370
|
+
try {
|
|
371
|
+
sub.error(dbError("unavailable", "Lost the local dashboard server; retrying."));
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
function collection(path) {
|
|
378
|
+
const ref = {
|
|
379
|
+
limit: () => ref,
|
|
380
|
+
onSnapshot(next, error) {
|
|
381
|
+
const sub = { collection: path, next, error: error || (() => {
|
|
382
|
+
}), delivered: /* @__PURE__ */ new Map(), started: false };
|
|
383
|
+
subs.add(sub);
|
|
384
|
+
connect();
|
|
385
|
+
if (live) setTimeout(() => subs.has(sub) && !sub.started && (sub.started = true, deliver(sub, true)), 0);
|
|
386
|
+
return () => subs.delete(sub);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
return ref;
|
|
390
|
+
}
|
|
391
|
+
function doc(path) {
|
|
392
|
+
const write = (op, data) => {
|
|
393
|
+
const { body, done } = bases.begin(path, op, data);
|
|
394
|
+
return call(api("/_api/write"), { method: "POST", headers: { "Content-Type": "application/json" }, body }).then(
|
|
395
|
+
() => done(true),
|
|
396
|
+
(error) => {
|
|
397
|
+
done(false);
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
};
|
|
402
|
+
return {
|
|
403
|
+
async get() {
|
|
404
|
+
const body = await call(api(`/_api/doc?path=${encodeURIComponent(path)}`));
|
|
405
|
+
const data = body.exists ? body.data : void 0;
|
|
406
|
+
return { exists: body.exists === true, data: () => clone(data), metadata: { fromCache: false, hasPendingWrites: false } };
|
|
407
|
+
},
|
|
408
|
+
set: (data) => write("set", data),
|
|
409
|
+
update: (data) => write("update", data),
|
|
410
|
+
delete: () => write("delete")
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
var db = { doc, collection };
|
|
414
|
+
var assets = {
|
|
415
|
+
async upload(file) {
|
|
416
|
+
const name = file && typeof file.name === "string" && file.name || "upload";
|
|
417
|
+
const res = await fetch(api("/_api/upload"), {
|
|
418
|
+
method: "POST",
|
|
419
|
+
headers: { "Content-Type": file && file.type || "application/octet-stream", "X-Filename": encodeURIComponent(name) },
|
|
420
|
+
body: file
|
|
421
|
+
});
|
|
422
|
+
const body = await res.json().catch(() => null);
|
|
423
|
+
if (!res.ok || !body) throw dbError(body && body.code || "unavailable", body && body.message || `Upload failed (HTTP ${res.status}).`);
|
|
424
|
+
return { id: body.id, url: body.url };
|
|
425
|
+
},
|
|
426
|
+
// Uploads are content-addressed and small; the agent removes one with `artifact.mjs delete-asset`.
|
|
427
|
+
async delete() {
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var EDIT_NAMES = /* @__PURE__ */ new Set(["data.write", "files.write", "assets.write"]);
|
|
431
|
+
var users = {
|
|
432
|
+
id: async () => (await sessionReady, user.id),
|
|
433
|
+
name: async () => (await sessionReady, user.name),
|
|
434
|
+
isOwner: async () => (await sessionReady, user.owner),
|
|
435
|
+
// The owner and editors hold the db's `admin` level: they write the shared documents, the page and its files.
|
|
436
|
+
canEdit: async () => (await sessionReady, user.canEdit),
|
|
437
|
+
can: async (name) => (await sessionReady, EDIT_NAMES.has(name) && user.canEdit)
|
|
438
|
+
};
|
|
439
|
+
var downloads = {
|
|
440
|
+
async save({ filename, data }) {
|
|
441
|
+
const blob = data instanceof Blob ? data : new Blob([data]);
|
|
442
|
+
const toFolder = globalThis.__baoyuSave;
|
|
443
|
+
if (toFolder) {
|
|
444
|
+
await toFolder(blob, filename || "download");
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const url = URL.createObjectURL(blob);
|
|
448
|
+
const a = document.createElement("a");
|
|
449
|
+
a.href = url;
|
|
450
|
+
a.download = filename || "download";
|
|
451
|
+
a.style.display = "none";
|
|
452
|
+
document.body.appendChild(a);
|
|
453
|
+
a.click();
|
|
454
|
+
a.remove();
|
|
455
|
+
setTimeout(() => URL.revokeObjectURL(url), 6e4);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
var self = { publish: async () => ({}) };
|
|
459
|
+
var capabilities = { db, assets, user: users, downloads, self };
|
|
460
|
+
globalThis.claude = {
|
|
461
|
+
...capabilities,
|
|
462
|
+
use: async (name) => {
|
|
463
|
+
if (name === "assets" && !(await sessionReady, user.canEdit)) return UNAVAILABLE;
|
|
464
|
+
return Object.hasOwn(capabilities, name) ? capabilities[name] : UNAVAILABLE;
|
|
465
|
+
}
|
|
466
|
+
};
|
|
467
|
+
var NativeFontFace = globalThis.FontFace;
|
|
468
|
+
if (typeof NativeFontFace === "function") {
|
|
469
|
+
const LocalFontFace = function FontFace(family, src, descriptors) {
|
|
470
|
+
if (typeof src === "string" && src.includes("/_runtime/")) {
|
|
471
|
+
const face = new NativeFontFace(family, `local(${JSON.stringify(String(family))})`, descriptors);
|
|
472
|
+
face.load = () => new Promise(() => {
|
|
473
|
+
});
|
|
474
|
+
return face;
|
|
475
|
+
}
|
|
476
|
+
return new NativeFontFace(family, src, descriptors);
|
|
477
|
+
};
|
|
478
|
+
LocalFontFace.prototype = NativeFontFace.prototype;
|
|
479
|
+
globalThis.FontFace = LocalFontFace;
|
|
480
|
+
}
|
|
481
|
+
})();
|