dsh-claude-slides 2.11.0 → 2.11.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/lib/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// dsh-claude-slides 2.11.
|
|
1
|
+
// dsh-claude-slides 2.11.1: built from baoyu-design.
|
|
2
2
|
var define_PLUGIN_default = { name: "claude-slides", provider: "dsh-claude-slides", skills: [{ name: "claude-slides", description: "Make and revise slide decks in the Slides artifact format (project/deck.json plus one HTML file per slide) as local files, then open them in a local slide editor where the user can present, retype, drag, and export PDF, HTML or PowerPoint. Use when the user asks for an editable slide deck or presentation in this format, names claude-slides or the Slides type, or wants to revise a folder that holds project/deck.json. Not for a one-off single-file HTML deck or prototype.", comments: true }] };
|
|
3
3
|
|
|
4
4
|
import { readFile } from "node:fs/promises";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-claude-slides",
|
|
3
|
-
"version": "2.11.
|
|
3
|
+
"version": "2.11.1",
|
|
4
4
|
"description": "DeepSeek Harness plugin: the claude-slides skill from baoyu-design. The agent writes editable slide decks as files and opens them in a local slide editor.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"dsh",
|
|
@@ -23,6 +23,12 @@
|
|
|
23
23
|
"icon": "./icon.svg",
|
|
24
24
|
"baoyuDesign": {
|
|
25
25
|
"releaseNotes": [
|
|
26
|
+
{
|
|
27
|
+
"version": "2.11.1",
|
|
28
|
+
"date": "2026-11-05",
|
|
29
|
+
"en": "### Features\n- **DeepSeek Harness: the Artifacts page offers the plugins you haven't installed.** Beside the tiles for the skills you have, each other baoyu-design plugin gets a dimmed tile marked **Not installed**. Click it to see what the plugin makes and install it from there, at the same version as the ones you have, usually without a restart. Once it's in, **Start** opens a session with the skill. In an app that can't install from the page, the tile points you to the Plugins page. A plugin you installed and switched off still gets no tile.\n\n### Fixes\n- With `--agent-hook`, a comment could reach the agent twice: a second send could start the hook again after the first run had finished but before the comment was marked sent.",
|
|
30
|
+
"zh": "### 新功能\n- **DeepSeek Harness:「作品」页会列出你还没装的插件。** 已装技能的入口旁边,其他 baoyu-design 插件也各有一个入口,颜色淡一些,标着 **未安装**。点开会说明这个插件做什么,可以直接在这里安装,版本和你已装的一致,多数情况不用重启。装好后点 **新建** 就用这个技能开一个会话。应用不支持在页面里安装时,会引导你去插件页。装了但被你关掉的插件,仍然没有入口。\n\n### 修复\n- 用 `--agent-hook` 时,一条评论可能发给 agent 两次:第一次运行已经结束、评论还没标成已发送时,第二次发送会再启动一次 hook。"
|
|
31
|
+
},
|
|
26
32
|
{
|
|
27
33
|
"version": "2.11.0",
|
|
28
34
|
"date": "2026-11-05",
|
|
@@ -64,12 +70,6 @@
|
|
|
64
70
|
"date": "2026-11-02",
|
|
65
71
|
"en": "### Fixes\n- `claude-slides`: in a PowerPoint export, Chinese, Japanese or Korean text could show as boxes when its East Asian font was missing on the reader's computer or held none of those characters: Arial in the basic-fonts export, a Latin family, or an embedded subset that left characters out of a long deck. That text now names a font Windows and Office have instead (Microsoft YaHei, Microsoft JhengHei, Yu Gothic or Malgun Gothic). Text the embedded font holds, text without these characters and the Google Slides export are unchanged ([#29](https://github.com/JimLiu/baoyu-design/issues/29)).",
|
|
66
72
|
"zh": "### 修复\n- `claude-slides`:导出 PowerPoint 时,如果中日韩文字用的东亚字体在对方电脑上没有,或者这个字体本身不含这些字(basic 字体导出里的 Arial、拉丁字体、长 deck 里漏了字的内嵌子集),文字可能显示成方块。这类文字现在改用 Windows 和 Office 常见的字体(Microsoft YaHei、Microsoft JhengHei、Yu Gothic 或 Malgun Gothic)。内嵌字体装得下的文字、不含这些字的文字和 Google Slides 导出都不变([#29](https://github.com/JimLiu/baoyu-design/issues/29))。"
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
"version": "2.8.1",
|
|
70
|
-
"date": "2026-11-02",
|
|
71
|
-
"en": "### Fixes\n- `claude-slides`, `claude-design`, `claude-design-system`, `claude-dashboard`: their descriptions no longer send one-off HTML work to the `baoyu-design` skill, which is archived and no longer installed. They now only say what each skill is not for. The DeepSeek Harness plugins carry the same descriptions.",
|
|
72
|
-
"zh": "### 修复\n- `claude-slides`、`claude-design`、`claude-design-system`、`claude-dashboard`:说明里不再把一次性的 HTML 产出交给 `baoyu-design` 技能。这个技能已经归档,不再随仓库安装。现在只写各技能不适用于什么。DeepSeek Harness 插件里的说明也一样。"
|
|
73
73
|
}
|
|
74
74
|
]
|
|
75
75
|
},
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"dsh": ">=0.2.0-0"
|
|
96
96
|
},
|
|
97
97
|
"dependencies": {
|
|
98
|
-
"dsh-claude-artifacts": "^2.11.
|
|
98
|
+
"dsh-claude-artifacts": "^2.11.1"
|
|
99
99
|
},
|
|
100
100
|
"dsh": {
|
|
101
101
|
"bundle": {
|
|
@@ -85,7 +85,7 @@ h2 { flex: 1; min-width: 0; margin: 0; font-size: 17px; font-weight: 600; letter
|
|
|
85
85
|
.toast p { margin: 0; font-size: 13.5px; overflow-wrap: anywhere; }
|
|
86
86
|
.toast .path { margin-top: 4px; font: 12.5px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--fg); user-select: text; -webkit-user-select: text; overflow-wrap: anywhere; }
|
|
87
87
|
.toast.err p { color: var(--danger); }
|
|
88
|
-
`;function V(){if(/\bElectron\/\d/.test(navigator.userAgent))return!0;if(window.top===window)return!1;try{return window.top.location.origin!==location.origin}catch{return!0}}function F(t){let e=/filename\*=UTF-8''([^;]+)/i.exec(t??"");if(e)try{return decodeURIComponent(e[1])}catch{}return/filename="([^"]+)"/i.exec(t??"")?.[1]??""}async function q(t){let e=await t.json().catch(()=>null);if(!t.ok||!e||typeof e.path!="string")throw new Error(e?.message||`HTTP ${t.status}`);return e}var Y=class{layer=o("div",{"data-baoyu-save-layer":""});wrap=o("div",{class:"wrap"});node=null;timer;constructor(){this.layer.attachShadow({mode:"open"}).append(o("style",{},E+Ce),this.wrap)}saved(e,n=N.hint){this.show([o("p",{},N.saved(e.name)),o("div",{class:"path"},e.shown),o("div",{class:"hint"},n)],!1)}failed(e){this.show([o("p",{},e)],!0)}show(e,n){this.layer.isConnected||document.body.append(this.layer),window.clearTimeout(this.timer),this.node?.remove();let i=o("div",{class:`toast${n?" err":""}`,role:n?"alert":"status","data-baoyu-save-toast":""},...e);this.wrap.append(i),this.node=i,this.timer=window.setTimeout(()=>{i.remove(),this.node===i&&(this.node=null)},n?1e4:12e3)}},He=null;function U(){return He??=new Y}async function Ne(t,e){return q(await fetch(`${Oe}?name=${encodeURIComponent(e)}`,{method:"POST",body:t,headers:{"Content-Type":t.type||"application/octet-stream"}}))}function _e(t){for(let e of t.composedPath())if(e instanceof HTMLAnchorElement&&e.hasAttribute("download")&&e.href)return e;return null}function ze(t){try{let e=new URL(t.href,location.href);return e.protocol==="blob:"||e.protocol==="data:"||e.origin===location.origin}catch{return!1}}function ce(t){if(!V())return!1;let e=U(),n=async(a,c,u={})=>{try{let f=await Ne(a,c);return u.quiet||e.saved(f),f}catch(f){throw u.quiet||e.failed(N.failed(c,f instanceof Error?f.message:String(f))),f}},i=a=>{let c=a.getAttribute("download")??"",u=a.href,f=fetch(u,{cache:"no-store"});(async()=>{let h=c||"download",g;try{let m=await f;if(!m.ok)throw new Error(`HTTP ${m.status}`);h=c||F(m.headers.get("Content-Disposition"))||"download",g=await m.blob()}catch(m){return e.failed(N.failed(h,m instanceof Error?m.message:String(m)))}await n(g,h).catch(()=>{})})()},r=a=>a!==null&&(!t||ze(a)),s=a=>{t?i(a):e.failed(N.guest)};window.addEventListener("click",a=>{let c=_e(a);a.defaultPrevented||!r(c)||(a.preventDefault(),s(c))},!0);let p=HTMLAnchorElement.prototype.click;return HTMLAnchorElement.prototype.click=function(){if(!this.isConnected&&this.hasAttribute("download")&&this.href&&r(this))return s(this);p.call(this)},t&&(window.__baoyuSave=n),!0}var J="/__share/archive",je={label:"Whole folder (.zip)",description:"Everything here, to back up or move"},R=y?{started:t=>`\u5F00\u59CB\u4E0B\u8F7D ${t}`,hint:"\u6574\u4E2A\u6587\u4EF6\u5939\u6253\u6210\u4E86\u4E00\u4E2A zip\uFF0C\u53EF\u4EE5\u5907\u4EFD\u3001\u6362\u7535\u8111\u6216\u53D1\u7ED9\u522B\u4EBA\u3002\u89E3\u538B\u540E\u7528\u540C\u4E00\u4E2A skill \u6253\u5F00\u5C31\u80FD\u63A5\u7740\u7528\uFF1B\u5206\u4EAB\u94FE\u63A5\u548C\u8FD9\u53F0\u7535\u8111\u4E0A\u7684\u670D\u52A1\u5668\u72B6\u6001\u4E0D\u5728\u91CC\u9762\u3002",beside:"\u8FD9\u4E2A\u6D4F\u89C8\u5668\u4E0D\u80FD\u4E0B\u8F7D\u6587\u4EF6\uFF0Czip \u5B58\u5728\u4E86\u6587\u4EF6\u5939\u65C1\u8FB9\u3002\u89E3\u538B\u540E\u7528\u540C\u4E00\u4E2A skill \u6253\u5F00\u5C31\u80FD\u63A5\u7740\u7528\u3002",tooLarge:"\u6587\u4EF6\u5939\u8D85\u8FC7\u4E86 zip \u7684\u4E0A\u9650\uFF084 GiB \u6216 65535 \u4E2A\u6587\u4EF6\uFF09\uFF0C\u8BF7\u76F4\u63A5\u590D\u5236\u6587\u4EF6\u5939\u3002",failed:"\u6CA1\u80FD\u6253\u5305\uFF1A"}:{started:t=>`Downloading ${t}`,hint:"The whole folder in one zip, to back up, move to another computer or send to someone. Unzip it and open it with the same skill to carry on; share links and this computer's server state are left out.",beside:"This browser doesn't download files, so the zip went beside the folder. Unzip it and open it with the same skill to carry on.",tooLarge:"The folder is over what a zip holds (4 GiB or 65,535 files): copy the folder itself instead.",failed:"Could not zip the folder: "},Be=`
|
|
88
|
+
`;function V(){if(window.__baoyuNativeDownloads===!0)return!1;if(/\bElectron\/\d/.test(navigator.userAgent))return!0;if(window.top===window)return!1;try{return window.top.location.origin!==location.origin}catch{return!0}}function F(t){let e=/filename\*=UTF-8''([^;]+)/i.exec(t??"");if(e)try{return decodeURIComponent(e[1])}catch{}return/filename="([^"]+)"/i.exec(t??"")?.[1]??""}async function q(t){let e=await t.json().catch(()=>null);if(!t.ok||!e||typeof e.path!="string")throw new Error(e?.message||`HTTP ${t.status}`);return e}var Y=class{layer=o("div",{"data-baoyu-save-layer":""});wrap=o("div",{class:"wrap"});node=null;timer;constructor(){this.layer.attachShadow({mode:"open"}).append(o("style",{},E+Ce),this.wrap)}saved(e,n=N.hint){this.show([o("p",{},N.saved(e.name)),o("div",{class:"path"},e.shown),o("div",{class:"hint"},n)],!1)}failed(e){this.show([o("p",{},e)],!0)}show(e,n){this.layer.isConnected||document.body.append(this.layer),window.clearTimeout(this.timer),this.node?.remove();let i=o("div",{class:`toast${n?" err":""}`,role:n?"alert":"status","data-baoyu-save-toast":""},...e);this.wrap.append(i),this.node=i,this.timer=window.setTimeout(()=>{i.remove(),this.node===i&&(this.node=null)},n?1e4:12e3)}},He=null;function U(){return He??=new Y}async function Ne(t,e){return q(await fetch(`${Oe}?name=${encodeURIComponent(e)}`,{method:"POST",body:t,headers:{"Content-Type":t.type||"application/octet-stream"}}))}function _e(t){for(let e of t.composedPath())if(e instanceof HTMLAnchorElement&&e.hasAttribute("download")&&e.href)return e;return null}function ze(t){try{let e=new URL(t.href,location.href);return e.protocol==="blob:"||e.protocol==="data:"||e.origin===location.origin}catch{return!1}}function ce(t){if(!V())return!1;let e=U(),n=async(a,c,u={})=>{try{let f=await Ne(a,c);return u.quiet||e.saved(f),f}catch(f){throw u.quiet||e.failed(N.failed(c,f instanceof Error?f.message:String(f))),f}},i=a=>{let c=a.getAttribute("download")??"",u=a.href,f=fetch(u,{cache:"no-store"});(async()=>{let h=c||"download",g;try{let m=await f;if(!m.ok)throw new Error(`HTTP ${m.status}`);h=c||F(m.headers.get("Content-Disposition"))||"download",g=await m.blob()}catch(m){return e.failed(N.failed(h,m instanceof Error?m.message:String(m)))}await n(g,h).catch(()=>{})})()},r=a=>a!==null&&(!t||ze(a)),s=a=>{t?i(a):e.failed(N.guest)};window.addEventListener("click",a=>{let c=_e(a);a.defaultPrevented||!r(c)||(a.preventDefault(),s(c))},!0);let p=HTMLAnchorElement.prototype.click;return HTMLAnchorElement.prototype.click=function(){if(!this.isConnected&&this.hasAttribute("download")&&this.href&&r(this))return s(this);p.call(this)},t&&(window.__baoyuSave=n),!0}var J="/__share/archive",je={label:"Whole folder (.zip)",description:"Everything here, to back up or move"},R=y?{started:t=>`\u5F00\u59CB\u4E0B\u8F7D ${t}`,hint:"\u6574\u4E2A\u6587\u4EF6\u5939\u6253\u6210\u4E86\u4E00\u4E2A zip\uFF0C\u53EF\u4EE5\u5907\u4EFD\u3001\u6362\u7535\u8111\u6216\u53D1\u7ED9\u522B\u4EBA\u3002\u89E3\u538B\u540E\u7528\u540C\u4E00\u4E2A skill \u6253\u5F00\u5C31\u80FD\u63A5\u7740\u7528\uFF1B\u5206\u4EAB\u94FE\u63A5\u548C\u8FD9\u53F0\u7535\u8111\u4E0A\u7684\u670D\u52A1\u5668\u72B6\u6001\u4E0D\u5728\u91CC\u9762\u3002",beside:"\u8FD9\u4E2A\u6D4F\u89C8\u5668\u4E0D\u80FD\u4E0B\u8F7D\u6587\u4EF6\uFF0Czip \u5B58\u5728\u4E86\u6587\u4EF6\u5939\u65C1\u8FB9\u3002\u89E3\u538B\u540E\u7528\u540C\u4E00\u4E2A skill \u6253\u5F00\u5C31\u80FD\u63A5\u7740\u7528\u3002",tooLarge:"\u6587\u4EF6\u5939\u8D85\u8FC7\u4E86 zip \u7684\u4E0A\u9650\uFF084 GiB \u6216 65535 \u4E2A\u6587\u4EF6\uFF09\uFF0C\u8BF7\u76F4\u63A5\u590D\u5236\u6587\u4EF6\u5939\u3002",failed:"\u6CA1\u80FD\u6253\u5305\uFF1A"}:{started:t=>`Downloading ${t}`,hint:"The whole folder in one zip, to back up, move to another computer or send to someone. Unzip it and open it with the same skill to carry on; share links and this computer's server state are left out.",beside:"This browser doesn't download files, so the zip went beside the folder. Unzip it and open it with the same skill to carry on.",tooLarge:"The folder is over what a zip holds (4 GiB or 65,535 files): copy the folder itself instead.",failed:"Could not zip the folder: "},Be=`
|
|
89
89
|
.toast { position: fixed; right: 16px; bottom: 16px; z-index: 2147483003; width: min(380px, calc(100vw - 32px)); background: var(--bg); border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow); padding: 12px 14px; }
|
|
90
90
|
.toast p { margin: 0; font-size: 13.5px; overflow-wrap: anywhere; }
|
|
91
91
|
.toast.err p { color: var(--danger); }
|
|
@@ -451,6 +451,7 @@ data: ${JSON.stringify(snap)}
|
|
|
451
451
|
if (agentHook) await runHook(agentHook);
|
|
452
452
|
}
|
|
453
453
|
async function runHook(command) {
|
|
454
|
+
if (typeof command !== "string") return runHandler(command);
|
|
454
455
|
const doc = await readComments(root);
|
|
455
456
|
for (const thread of pendingForAgent(doc)) {
|
|
456
457
|
if (hookRunning.has(thread.id)) continue;
|
|
@@ -475,15 +476,15 @@ data: ${JSON.stringify(snap)}
|
|
|
475
476
|
log(`comments: the agent hook failed to start: ${error.message}`);
|
|
476
477
|
});
|
|
477
478
|
child.on("exit", (code) => {
|
|
478
|
-
hookRunning.delete(thread.id);
|
|
479
479
|
if (code !== 0) {
|
|
480
|
+
hookRunning.delete(thread.id);
|
|
480
481
|
log(`comments: the agent hook exited with ${code} for ${thread.id}; the comment stays waiting${stderr ? `: ${stderr.trim()}` : ""}`);
|
|
481
482
|
return;
|
|
482
483
|
}
|
|
483
484
|
void updateComments(root, (d) => {
|
|
484
485
|
const t = d.threads.find((x) => x.id === thread.id);
|
|
485
486
|
if (t) markDelivered(t, now(), "hook");
|
|
486
|
-
}).then(() => {
|
|
487
|
+
}).finally(() => hookRunning.delete(thread.id)).then(() => {
|
|
487
488
|
log(`comments: sent to the agent (hook): ${thread.id}`);
|
|
488
489
|
return broadcast();
|
|
489
490
|
}).catch((error) => log(`comments: ${error.message}`));
|
|
@@ -524,6 +525,44 @@ data: ${JSON.stringify(snap)}
|
|
|
524
525
|
});
|
|
525
526
|
}
|
|
526
527
|
}
|
|
528
|
+
async function runHandler(handler) {
|
|
529
|
+
for (const { id } of pendingForAgent(await readComments(root))) {
|
|
530
|
+
if (hookRunning.has(id)) continue;
|
|
531
|
+
hookRunning.add(id);
|
|
532
|
+
try {
|
|
533
|
+
const doc = await readComments(root);
|
|
534
|
+
const thread = pendingForAgent(doc).find((t) => t.id === id);
|
|
535
|
+
if (!thread) continue;
|
|
536
|
+
try {
|
|
537
|
+
await handler.thread(agentViews(doc, [thread])[0]);
|
|
538
|
+
} catch (error) {
|
|
539
|
+
log(`comments: the agent handler failed for ${id}; the comment stays waiting: ${error.message}`);
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
542
|
+
await updateComments(root, (d) => {
|
|
543
|
+
const t = d.threads.find((x) => x.id === id);
|
|
544
|
+
if (t) markDelivered(t, now(), "hook");
|
|
545
|
+
});
|
|
546
|
+
log(`comments: sent to the agent (handler): ${id}`);
|
|
547
|
+
await broadcast();
|
|
548
|
+
} finally {
|
|
549
|
+
hookRunning.delete(id);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const failed = [];
|
|
553
|
+
for (const request of requests.splice(0)) {
|
|
554
|
+
setRequestState(request.id, "working");
|
|
555
|
+
try {
|
|
556
|
+
await handler.request(request);
|
|
557
|
+
log(`comments: sent to the agent (handler): ${request.id}`);
|
|
558
|
+
} catch (error) {
|
|
559
|
+
failed.push(request);
|
|
560
|
+
setRequestState(request.id, "queued");
|
|
561
|
+
log(`comments: the agent handler failed for ${request.id}; the request stays waiting: ${error.message}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
requests.unshift(...failed);
|
|
565
|
+
}
|
|
527
566
|
let debounce;
|
|
528
567
|
const changed = () => {
|
|
529
568
|
clearTimeout(debounce);
|
|
@@ -3235,8 +3274,11 @@ var ShareHost = class {
|
|
|
3235
3274
|
* their own artifact; a guest comes over a LAN address (this computer's own included: that is how the owner sees
|
|
3236
3275
|
* what a role sees).
|
|
3237
3276
|
*/
|
|
3277
|
+
isLocal(request) {
|
|
3278
|
+
return this.options.local ? this.options.local(request) : isLoopback(request.socket.remoteAddress);
|
|
3279
|
+
}
|
|
3238
3280
|
viewerOf(request) {
|
|
3239
|
-
if (
|
|
3281
|
+
if (this.isLocal(request)) return { role: "owner", user: this.options.owner };
|
|
3240
3282
|
const guest = this.on ? this.sharing.guest(this.cookie(request)) : null;
|
|
3241
3283
|
if (!guest) return null;
|
|
3242
3284
|
this.sharing.seen(guest);
|
|
@@ -3368,7 +3410,7 @@ var ShareHost = class {
|
|
|
3368
3410
|
async join(request, response, method, token2) {
|
|
3369
3411
|
const t = this.words(request);
|
|
3370
3412
|
const role = this.sharing.linkRole(token2);
|
|
3371
|
-
const local =
|
|
3413
|
+
const local = this.isLocal(request);
|
|
3372
3414
|
const form = method === "POST" ? await readBody(request, 8 << 10).then((body) => body.toString("utf8"), () => "") : "";
|
|
3373
3415
|
const title = this.options.title();
|
|
3374
3416
|
if (!role || !local && !this.on) return this.sendPage(response, 404, t.linkGoneTitle, `<h1>${esc2(t.linkGoneTitle)}</h1><p>${esc2(t.linkGone)}</p>`);
|
|
@@ -4523,7 +4565,7 @@ function watchArtifact(store, onChange, onError) {
|
|
|
4523
4565
|
};
|
|
4524
4566
|
}
|
|
4525
4567
|
var publishError = (response, status, code, message) => sendJson2(response, status, { code, message });
|
|
4526
|
-
async function mountSite(root, { log: stamp2, network, agentHook }) {
|
|
4568
|
+
async function mountSite(root, { log: stamp2, network, agentHook, local }) {
|
|
4527
4569
|
const project = path12.join(root, "project");
|
|
4528
4570
|
const editorPath = pagePathOf(KIND, root);
|
|
4529
4571
|
const projectDir = await fsp4.realpath(project);
|
|
@@ -4540,6 +4582,7 @@ async function mountSite(root, { log: stamp2, network, agentHook }) {
|
|
|
4540
4582
|
title: () => artifactTitle(root, projectDir),
|
|
4541
4583
|
log: stamp2,
|
|
4542
4584
|
uiFile: path12.join(EDITOR_DIR, "share-ui.js"),
|
|
4585
|
+
...local ? { local } : {},
|
|
4543
4586
|
// The History panel's address and what this person may do there; on a version's page (?at=n), which version.
|
|
4544
4587
|
globals: (viewer, query) => {
|
|
4545
4588
|
const at = Number(query.get("at") ?? "");
|