From 56ffe0cd1c320cde9f6101c6b2e49ec60ad53a17 Mon Sep 17 00:00:00 2001 From: Aisland Date: Tue, 27 Oct 2026 23:09:34 +0800 Subject: [PATCH] feat(v0.5.0): secure project previews and fix host UI compatibility --- 00_index/README.md | 1 + 01_content/README.md | 29 +- 01_content/dsh.plugin.json | 2 +- 01_content/lib/client.js | 2516 ++++++++++++---------- 01_content/lib/index.js | 287 ++- 01_content/package-lock.json | 7 +- 01_content/package.json | 14 +- 01_content/release-prep.mjs | 25 +- 01_content/src/client/SandboxedFrame.tsx | 57 + 01_content/src/client/changelog.ts | 28 +- 01_content/src/client/hostTransport.ts | 18 + 01_content/src/client/index.tsx | 253 +-- 01_content/src/client/locales.ts | 8 +- 01_content/src/client/split.tsx | 14 +- 01_content/src/index.ts | 122 +- 01_content/src/previewFiles.ts | 93 + 01_content/src/requestAuth.ts | 23 + 02_process/PRD.md | 21 +- 04_test/anchor-dom.test.mjs | 36 + 04_test/host-transport.test.mjs | 12 +- 04_test/preview-files.test.mjs | 107 + 04_test/request-auth.test.mjs | 132 ++ 04_test/sandboxed-frame.test.mjs | 226 ++ 04_test/server-home.test.mjs | 4 +- 04_test/sidebar-popup.test.mjs | 244 +++ 04_test/split-margin.test.mjs | 197 ++ AGENTS.md | 55 +- README.md | 20 +- README.zh.md | 18 +- docs/compatibility-and-security.md | 80 + 30 files changed, 3202 insertions(+), 1447 deletions(-) create mode 100644 01_content/src/client/SandboxedFrame.tsx create mode 100644 01_content/src/previewFiles.ts create mode 100644 01_content/src/requestAuth.ts create mode 100644 04_test/preview-files.test.mjs create mode 100644 04_test/request-auth.test.mjs create mode 100644 04_test/sandboxed-frame.test.mjs create mode 100644 04_test/sidebar-popup.test.mjs create mode 100644 04_test/split-margin.test.mjs create mode 100644 docs/compatibility-and-security.md diff --git a/00_index/README.md b/00_index/README.md index 2e04133..59595d9 100644 --- a/00_index/README.md +++ b/00_index/README.md @@ -2,6 +2,7 @@ - [README](../README.md) — 项目概述与目录结构 - [PRD](../02_process/PRD.md) — 需求与设计(含接手分界线 §11) +- [兼容与安全](../docs/compatibility-and-security.md) — 当前 0.5.0 范围、接口与隔离限制 - [AGENTS](../AGENTS.md) — 项目规则 - [01_content](../01_content) — 插件包 - [02_process](../02_process) — 过程文档 diff --git a/01_content/README.md b/01_content/README.md index e765c7a..d907c0d 100644 --- a/01_content/README.md +++ b/01_content/README.md @@ -17,7 +17,30 @@ - 状态监控为宿主会话运行时快照的事件订阅镜像;视图/项目/绑定状态存 localStorage。 - 客户端:TypeScript + React(host externals)+ 原生 CSS;服务端:Node。 -## v0.4.0:兼容与验证范围 +## 0.5.0:兼容与安全说明 + +公开发布状态以 GitHub Release 为准。 + +- HTTP 文件/目录/写入/Git/工作区与终端握手先通过宿主身份验证,仅放行 owner(0.2.0 使用单 operator 身份);缺少可确认身份的服务时返回 503,不降级开放。health 仍可公开读取状态。以前直接用无登录 curl 调用这些接口的方式将被拒绝;不提供跳过鉴权开关。 +- 候选 dev.1 已在 Windows 隔离数据目录验证 **DSH 0.2.0-rc.2 / 0.2.1-rc.3 网页端**的冷启动、加载、分栏、文件与网页资源;0.2.1 另测创建/写入、控制室绑定和终端,未发送模型请求。用户已在官方 Windows Desktop 0.2.1-rc.3 验证 dev.2 的项目打开、分栏缩放与侧栏设置弹窗正常;前轮新项目也已点测。正式包另做安装与回归检查,这些证据不等于双端全功能验收。 +- 本版的 `engines.dsh` 与兼容矩阵只列上述两个 0.2 版本。旧 0.1.x 不在本版支持范围;下方 v0.4.0 的记录是历史范围,不代表 0.5.0 继续支持旧宿主。 +- 折叠侧栏与展开侧栏共用一份弹窗层,使控制室新建项目、绑定、图标与删除弹窗不因侧栏收起而消失,避免重复渲染。 +- 修复分栏 margin 的小数值被浏览器规范化后误判为外部改写,以及会话根更换后观察器仍盯着旧元素的问题。 + +### 网页隔离的影响与边界 + +- 浏览器/动画/自定义 HTML 窗格使用无 `allow-same-origin` 的 sandbox;网页不能直接读取 DSH 主页面及其同源存储;私有文件操作和终端接口仍需宿主身份校验。工作台自己的项目、布局和媒体存储不迁移、不删除。 +- **可能受影响**:嵌入页面的 localStorage、IndexedDB、Cookie 登录、依赖同源的请求、弹出新窗口/顶层跳转,以及标注时自动读取 iframe 内文字。项目页内嵌 iframe/object 被限制;依赖这些能力的页面可能需要单独适配。网页窗口级标注仍可使用,但不能承诺保留逐元素文字提取。 +- **已观察到的兼容限制**:Cesium 等库的 WebGL 纹理可能需要显式跨源加载。StarMap 的月球纹理已在独立项目中修复并验证,改动不包含在工作台安装包里;其他旧网页仍需各自检查。不会为恢复旧网页取消安全隔离。 +- 本地 HTML 通过登录后创建的随机只读地址加载,允许读取该次选择目录内的资源(包括子目录),支持相对 CSS、JS 模块与 fetch;它不是“只能读当前 HTML 文件”。不要给不可信页面挂载含敏感文件的目录;页面仍可主动联网,隔离不等于阻止数据外发。 +- 临时地址不写入项目存档,单资源最多 40 MiB,最多 128 个挂载,连续 30 分钟无读取后失效;失效后点击窗格刷新重新创建。关闭网页标签、刷新替换、组件卸载或插件卸载会撤销;仅关闭/切换项目仍沿用最多 6 个项目的保活池,不立即撤销仍挂载的页面。 +- 预览使用同一文件句柄限量读取,读取前后复核真实路径、文件身份与状态;检测到替换、改写或撤销时不返回内容。此措施不是针对恶意本地写入者精确竞态的操作系统沙箱;40 MiB 是单资源限制,不是所有并发请求的总内存额度。 +- 终端插件卸载时关闭本插件连接及 Shell。鉴权检查发生在每次 HTTP 请求/WS 建连;本轮没有实现宿主身份撤销后即时断开已有 WS 的机制。 +- `npm run test:security` 覆盖鉴权、预览目录越界/生命周期、终端卸载;`npm run pack` 同时执行真实 Chromium 小数 margin 回归。需要本机安装 Edge/Chrome,或用 `DSH_TEST_CHROMIUM` 指定可执行文件。 + +完整路由、错误码、预览生命周期与验收边界见源码仓库的 `docs/compatibility-and-security.md`;该文档随源码交付,不额外放入七文件安装包。 + +## v0.4.0:兼容与验证范围(历史发布记录) - DSH 网页端兼容声明仅列 **0.1.1-rc.2 / 0.1.2-rc.1 / 0.2.0-rc.2**,不是对任意新旧版本的保证。 - Windows + 0.2.0-rc.2 网页端已有冷启动、工作台/控制室、新建绑定与短文本回复、关闭回切等验收基础;桌面主要页面与交互已点测,共目录串挂修复获用户确认。最终包经过 98 项源码回归、独立安装与客户端加载门禁;这些检查不等于完整桌面端业务验收。旧版沿用此前页面验收并补定向代码回归,未重跑整套旧版 GUI。 @@ -37,7 +60,7 @@ ## 权限与外部服务(如实披露) - **文件访问**:服务端读写工作区与用户指定的目录——文件窗目录列表、MD 编辑保存、项目文件夹 mkdir、站点静态托管、widget 产物读取。除用户当次操作外,插件在加载时、项目状态变化时、已登记窗口任务完成时会自动读取当前关联的 `.dsh-worktable/` 结果文件及其指定产物,并读取宿主提供的工作区数据(workspaces 路由)。不扫描根目录旧 `widget-result.json`;产物路径可来自任务结果,这不是文件系统沙箱。失败返回错误或降级。 -- **网络**:① 更新检查向 api.github.com 发只读 GET(无凭据,请求仅含公开仓库名,不携带会话内容);② 插件与宿主的通信走宿主自身的 API / WebSocket 通道(由宿主管理);③ 「浏览器」「动画」等网页窗加载用户指定的页面,页面自身可能发起任意网络请求——等同于用户在自己的浏览器中打开这些页面。 +- **网络**:① 更新检查向 api.github.com 发只读 GET(无凭据,请求仅含公开仓库名,不携带会话内容);② 插件与宿主的通信走宿主自身的 API / WebSocket 通道;③ 网页窗加载用户指定的页面,页面自身可能发起任意网络请求;本版的隔离与存储限制见上节,不等同于完整浏览器页面的功能。 - **命令执行**:① 「源代码管理」窗运行 git 只读状态命令;② 「终端」窗经 node-pty 启动交互式 Shell(Windows 下为 PowerShell)——这是用户主动使用的完整终端:终端内执行的任何命令都可能读写文件、联网,行为等同用户本人操作。 - **凭据与环境**:插件代码不专门提取、存储或上传任何 API Key / Token / Cookie;但终端窗把宿主进程的完整环境变量传给子 Shell(其中可能包含敏感信息),终端内命令的行为不受插件约束。插件对 process.env 的读取用于运行参数(数据目录 DSH_HOME、终端默认 Shell SHELL 等),不将其外发。以上声明均有对应源码可查;本说明不是安全保证,高权限能力如实列出,使用风险由用户在自身环境评估。 - **外部依赖与边界**:无生产依赖;终端窗依赖宿主的 ws / node-pty(缺失时该窗降级提示,不影响其余功能)。不替换、不禁用任何官方插件。本插件不声称解决宿主自身的全部启动问题。 @@ -61,7 +84,7 @@ 先保存任务,从应用菜单/托盘完整退出(只关窗口不够);将占位路径替换为实际桌面端安装目录,在 PowerShell 执行,再手动重新打开应用: - & "<桌面端安装目录>\resources\runtime\cli\bin\dsh.cmd" plugin --profile desktop add "https://github.com/Aisland-SJL/dsh-worktable/releases/download/v0.4.0/dsh-worktable-0.4.0.tgz" + & "<桌面端安装目录>\resources\runtime\cli\bin\dsh.cmd" plugin --profile desktop add "https://github.com/Aisland-SJL/dsh-worktable/releases/download/v0.5.0/dsh-worktable-0.5.0.tgz" 不要以不固定版本的 npx 或网页端 CLI 代替桌面端自带 CLI;保留实际 DSH_HOME,勿把测试数据目录覆盖正式数据。新建控制室管理对话时,DSH 0.2 用户请选择分组,或直接绑定可用的现有对话。 diff --git a/01_content/dsh.plugin.json b/01_content/dsh.plugin.json index 971c6a6..79613df 100644 --- a/01_content/dsh.plugin.json +++ b/01_content/dsh.plugin.json @@ -1,7 +1,7 @@ { "name": "dsh-worktable", "description": "工作台:DSH 侧边栏的 agent 级项目容器,收纳如 TravelAtlas 等自装项目", - "version": "0.4.0", + "version": "0.5.0", "entry": { "name": "dsh-worktable", "inject": [] diff --git a/01_content/lib/client.js b/01_content/lib/client.js index f5e631c..e7ab575 100644 --- a/01_content/lib/client.js +++ b/01_content/lib/client.js @@ -7542,7 +7542,7 @@ __export(index_exports, { sendCustomToSession: () => sendCustomToSession }); module.exports = __toCommonJS(index_exports); -var import_react2 = require("react"); +var import_react3 = require("react"); // node_modules/xterm/css/xterm.css var xterm_default = `/** @@ -8494,7 +8494,7 @@ var zh = { "custom.groupNew": "\uFF0B \u65B0\u5EFA\u4E00\u4E2A\u5206\u7EC4\u2026", "custom.groupNewParent": "\u5206\u7EC4\u7236\u76EE\u5F55", "custom.groupNewName": "\u65B0\u5206\u7EC4\u540D", - "custom.groupNewParentPh": "\u4F8B\u5982 E:\\AI_Workspace\\DeepseekHarness", + "custom.groupNewParentPh": "\u4F8B\u5982 C:\\Projects", "custom.groupNewNamePh": "\u4F8B\u5982 MyProject\uFF08\u5C06\u65B0\u5EFA\u6B64\u6587\u4EF6\u5939\u5E76\u6CE8\u518C\u4E3A\u5206\u7EC4\uFF09", "custom.groupNeedPath": "\u8BF7\u586B\u5199\u5206\u7EC4\u7236\u76EE\u5F55\u548C\u65B0\u5206\u7EC4\u540D", "bind.title": "\u7ED1\u5B9A\u5BF9\u8BDD", @@ -8572,7 +8572,7 @@ var zh = { "console.groupNew": "\u65B0\u5EFA\u5206\u7EC4", "console.chooseGroup": "\u9009\u62E9\u5206\u7EC4\u2026", "console.blankNeedsGroup": "\u5F53\u524D DSH \u7684\u672A\u5206\u7EC4\u7A7A\u5BF9\u8BDD\u65E0\u6CD5\u8F93\u5165\uFF0C\u8BF7\u9009\u62E9\u5206\u7EC4\u6216\u52A0\u5165\u73B0\u6709\u5BF9\u8BDD\u3002", - "console.newParentPh": "\u5206\u7EC4\u7236\u76EE\u5F55\uFF0C\u4F8B\u5982 E:\\AI_Workspace", + "console.newParentPh": "\u5206\u7EC4\u7236\u76EE\u5F55\uFF0C\u4F8B\u5982 C:\\Projects", "console.newNamePh": "\u5206\u7EC4\u6587\u4EF6\u5939\u540D\uFF08\u5C06\u521B\u5EFA\u5E76\u6CE8\u518C\uFF09", "console.createBind": "\u65B0\u5EFA\u5E76\u7ED1\u5B9A", "console.bindFail": "\u521B\u5EFA\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5", @@ -8727,7 +8727,7 @@ var en = { "custom.groupNew": "\uFF0B Create a new group\u2026", "custom.groupNewParent": "Group parent folder", "custom.groupNewName": "New group name", - "custom.groupNewParentPh": "e.g. E:\\AI_Workspace\\DeepseekHarness", + "custom.groupNewParentPh": "e.g. C:\\Projects", "custom.groupNewNamePh": "e.g. MyProject (folder will be created and registered)", "custom.groupNeedPath": "Fill in the parent folder and new group name", "bind.title": "Bind conversation", @@ -8805,7 +8805,7 @@ var en = { "console.groupNew": "New group", "console.chooseGroup": "Choose a group\u2026", "console.blankNeedsGroup": "Blank ungrouped chats cannot accept input in this DSH. Choose a group or join an existing chat.", - "console.newParentPh": "Group parent folder, e.g. E:\\AI_Workspace", + "console.newParentPh": "Group parent folder, e.g. C:\\Projects", "console.newNamePh": "Group folder name (created & registered)", "console.createBind": "Create & bind", "console.bindFail": "Creation failed, try again", @@ -8900,7 +8900,7 @@ function basenameOf(p) { } // src/client/split.tsx -var import_react = require("react"); +var import_react2 = require("react"); // src/client/hostTransport.ts function hostWebSocketUrl(path, surface = { @@ -8917,6 +8917,17 @@ function hostWebSocketUrl(path, surface = { url.protocol = url.protocol === "https:" ? "wss:" : "ws:"; return url.href; } +function hostPreviewUrl(path, surface = { + protocol: location.protocol, + streamBaseUrl: globalThis.__DSH_TRANSPORT__?.streamBaseUrl +}) { + if (!path.startsWith("/api/worktable/preview-static/")) throw new Error("Invalid preview path"); + if (surface.protocol !== "dsh-app:") return path; + if (!surface.streamBaseUrl) throw new Error("Desktop Host preview URL unavailable"); + const url = new URL(path, surface.streamBaseUrl); + if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error("Unsupported Host preview protocol"); + return url.href; +} function worktableUpgrade(protocol = typeof location === "undefined" ? "http:" : location.protocol) { const desktop = protocol === "dsh-app:"; const command = "dsh plugin --profile " + (desktop ? "desktop" : "web") + ' add "https://github.com/Aisland-SJL/dsh-worktable/releases/latest/download/dsh-worktable.tgz"'; @@ -8924,8 +8935,114 @@ function worktableUpgrade(protocol = typeof location === "undefined" ? "http:" : return { command, prompt, desktop }; } +// src/client/SandboxedFrame.tsx +var import_react = require("react"); +var import_jsx_runtime = require("react/jsx-runtime"); +var SANDBOX = "allow-scripts allow-forms allow-downloads"; +var SITE = "/api/worktable/site/"; +function previewTarget(input, base2) { + const url = new URL(input, base2); + const own = new URL(base2); + const sameHost = url.protocol === own.protocol && url.host === own.host; + if (!["http:", "https:"].includes(url.protocol) && !(sameHost && url.protocol === "dsh-app:")) throw new Error("Unsupported preview URL"); + if (!sameHost || !url.pathname.startsWith(SITE)) return { url: url.href }; + const segments = url.pathname.slice(SITE.length).split("/"); + const root = decodeURIComponent(segments.shift() ?? ""); + if (!root) throw new Error("Missing preview directory"); + const relative = segments.map(decodeURIComponent); + if (relative.some((s) => s === ".." || /[\\/\0]/.test(s))) throw new Error("Invalid preview path"); + return { root, relative: relative.map(encodeURIComponent).join("/"), suffix: url.search + url.hash }; +} +function SandboxedFrame(props) { + const [src, setSrc] = (0, import_react.useState)("about:blank"); + const [error2, setError] = (0, import_react.useState)(""); + (0, import_react.useEffect)(() => { + let alive = true; + let token; + const revoke = (value) => { + void fetch("/api/worktable/preview", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ revoke: value }), + keepalive: true + }).catch(() => { + }); + }; + setSrc("about:blank"); + setError(""); + void (async () => { + if (!props.url || props.url === "about:blank") return; + const target = previewTarget(props.url, location.href); + if ("url" in target) { + if (alive) setSrc(target.url); + return; + } + const response = await fetch("/api/worktable/preview", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ root: target.root }) + }); + if (!response.ok) throw new Error("Preview HTTP " + response.status); + const grant = await response.json(); + if (typeof grant.token !== "string" || !/^[\w-]{32}$/.test(grant.token) || grant.base !== "/api/worktable/preview-static/" + grant.token + "/") throw new Error("Invalid preview grant"); + token = grant.token; + if (!alive) { + revoke(token); + token = void 0; + return; + } + setSrc(hostPreviewUrl(grant.base + target.relative + target.suffix)); + })().catch((err) => { + if (token) { + revoke(token); + token = void 0; + } + if (alive) setError(String(err)); + }); + return () => { + alive = false; + if (token) revoke(token); + }; + }, [props.url, props.reloadKey]); + if (error2) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_paneWipText", children: error2 }) }); + return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + "iframe", + { + className: "dsh-wt_paneFrame", + src, + "data-source-url": props.url, + title: props.title ?? "", + sandbox: SANDBOX, + referrerPolicy: "no-referrer" + }, + props.reloadKey + ); +} + // src/client/changelog.ts -var CHANGELOG_V030 = `\u66F4\u65B0\u516C\u544A \xB7 v0.4.0 +var CHANGELOG_V030 = `\u66F4\u65B0\u516C\u544A \xB7 v0.5.0 + +\u{1F6E1}\uFE0F \u65B0\u7248\u5BBF\u4E3B\u517C\u5BB9\u3001\u5B89\u5168\u9694\u79BB\u4E0E\u4FA7\u680F\u4FEE\u590D + +\u3010\u63A5\u53E3\u8EAB\u4EFD\u6821\u9A8C\u3011\u6587\u4EF6\u3001\u76EE\u5F55\u3001\u5199\u5165\u3001Git\u3001\u5DE5\u4F5C\u533A\u63A5\u53E3\u548C\u7EC8\u7AEF\u63E1\u624B\u590D\u7528\u5BBF\u4E3B\u8EAB\u4EFD\u9A8C\u8BC1\u3002\u65B0\u7248\u53EA\u5141\u8BB8 owner\uFF0C0.2.0 \u4F7F\u7528\u5355 operator \u8EAB\u4EFD\uFF1B\u4E0D\u80FD\u786E\u8BA4\u8EAB\u4EFD\u65F6\u62D2\u7EDD\u8BBF\u95EE\uFF0C\u4E0D\u9759\u9ED8\u5F00\u653E\u3002\u5065\u5EB7\u63A5\u53E3\u4ECD\u53EF\u8BFB\u72B6\u6001\uFF0C\u4E0D\u4EE3\u8868\u5168\u90E8\u529F\u80FD\u53EF\u7528\u3002 + +\u3010\u9879\u76EE\u7F51\u9875\u9694\u79BB\u3011\u6D4F\u89C8\u5668\u3001\u52A8\u753B\u53CA\u81EA\u5B9A\u4E49 HTML \u7A97\u683C\u4E0D\u518D\u62E5\u6709 DSH \u4E3B\u754C\u9762\u7684\u540C\u6E90\u6743\u9650\u3002\u672C\u5730 HTML \u901A\u8FC7\u4E34\u65F6\u53EA\u8BFB\u5730\u5740\u52A0\u8F7D\u8BE5\u76EE\u5F55\u5185\u7684\u8D44\u6E90\uFF1B\u5730\u5740\u4E0D\u5199\u5165\u9879\u76EE\u5B58\u6863\uFF0C\u5173\u95ED\u7F51\u9875\u6807\u7B7E\u3001\u5237\u65B0\u66FF\u6362\u6216\u5378\u8F7D\u4F1A\u64A4\u9500\u3002\u5207\u6362\u9879\u76EE\u4ECD\u4FDD\u7559\u539F\u6709\u6700\u591A 6 \u4E2A\u9879\u76EE\u4FDD\u6D3B\uFF0C\u4E0D\u7B49\u4E8E\u7ACB\u5373\u64A4\u9500\u3002\u7F51\u9875\u4ECD\u80FD\u8054\u7F51\uFF0C\u4E0D\u5E94\u7ED9\u4E0D\u53EF\u4FE1\u9875\u9762\u6302\u8F7D\u542B\u654F\u611F\u6587\u4EF6\u7684\u76EE\u5F55\u3002 + +\u3010\u9694\u79BB\u5E26\u6765\u7684\u5F71\u54CD\u3011\u90E8\u5206\u7F51\u9875\u7684\u6D4F\u89C8\u5668\u5B58\u50A8\u3001Cookie \u767B\u5F55\u3001\u5D4C\u5957\u9875\u9762\u3001WebGL \u8D34\u56FE\u53CA\u6807\u6CE8\u81EA\u52A8\u53D6\u5B57\u53EF\u80FD\u53D7\u9650\uFF0C\u65E7\u7F51\u9875\u53EF\u80FD\u9700\u8981\u9879\u76EE\u4FA7\u9002\u914D\u3002\u5DE5\u4F5C\u53F0\u4E0D\u81EA\u52A8\u6539\u5199\u9879\u76EE\u7F51\u9875\uFF0C\u4E5F\u4E0D\u901A\u8FC7\u53D6\u6D88\u9694\u79BB\u6062\u590D\u8FD9\u4E9B\u80FD\u529B\u3002\u7A97\u53E3\u7EA7\u6807\u6CE8\u4ECD\u53EF\u7528\uFF0C\u5FC5\u8981\u65F6\u8865\u622A\u56FE\u3002\u5DE5\u4F5C\u53F0\u81EA\u5DF1\u7684\u9879\u76EE\u3001\u5E03\u5C40\u4E0E\u5A92\u4F53\u4E0D\u6E05\u7A7A\u3001\u4E0D\u8FC1\u79FB\u3002 + +\u3010\u5206\u680F\u4E0E\u4FA7\u680F\u3011\u4FEE\u590D\u5C0F\u6570\u5C3A\u5BF8\u7ECF\u6D4F\u89C8\u5668\u89C4\u8303\u5316\u540E\u8BEF\u5224\u3001\u53CD\u590D\u91CD\u6392\uFF0C\u4EE5\u53CA\u4F1A\u8BDD\u6839\u66F4\u6362\u540E\u4ECD\u89C2\u5BDF\u65E7\u5143\u7D20\u7684\u95EE\u9898\u3002\u5C55\u5F00\u548C\u6298\u53E0\u4FA7\u680F\u5171\u7528\u4E00\u4EFD\u5F39\u7A97\u5C42\uFF0C\u63A7\u5236\u5BA4\u7684\u65B0\u5EFA\u5165\u53E3\u53CA\u7ED1\u5B9A\u3001\u56FE\u6807\u3001\u5220\u9664\u5F39\u7A97\u4E0D\u518D\u56E0\u4FA7\u680F\u6536\u8D77\u800C\u7F3A\u5931\uFF0C\u907F\u514D\u91CD\u590D\u6E32\u67D3\u3002 + +\u3010\u9884\u89C8\u8BFB\u53D6\u3011\u5355\u8D44\u6E90\u4E0A\u9650 40 MiB\uFF0C\u8BFB\u53D6\u671F\u95F4\u4E5F\u6267\u884C\u9650\u5236\uFF1B\u4F7F\u7528\u540C\u4E00\u6587\u4EF6\u53E5\u67C4\u5E76\u590D\u6838\u8DEF\u5F84\u4E0E\u6587\u4EF6\u8EAB\u4EFD\uFF0C\u9047\u5230\u6587\u4EF6\u66FF\u6362\u3001\u53D8\u5316\u6216\u6302\u8F7D\u64A4\u9500\u5219\u62D2\u7EDD\u8FD4\u56DE\u5185\u5BB9\u3002\u5B83\u4E0D\u662F\u9488\u5BF9\u6076\u610F\u672C\u5730\u8FDB\u7A0B\u7684\u64CD\u4F5C\u7CFB\u7EDF\u7EA7\u6587\u4EF6\u6C99\u7BB1\u3002 + +\u3010\u517C\u5BB9\u4E0E\u9A8C\u6536\u8FB9\u754C\u3011\u672C\u7248\u53EA\u5217 DSH 0.2.0-rc.2 / 0.2.1-rc.3\u3002\u5019\u9009\u5DF2\u5728 Windows \u9694\u79BB\u7F51\u9875\u7AEF\u9A8C\u8BC1\u4E3B\u8981\u9875\u9762\u3001\u6587\u4EF6\u3001\u7F51\u9875\u8D44\u6E90\u4E0E\u8FDE\u63A5\uFF1B\u7528\u6237\u5728\u5B98\u65B9 Windows Desktop 0.2.1-rc.3 \u9A8C\u8BC1\u5019\u9009\u7684\u65B0\u9879\u76EE\u3001\u9879\u76EE\u6253\u5F00\u3001\u5206\u680F\u7F29\u653E\u53CA\u4FA7\u680F\u8BBE\u7F6E\u5F39\u7A97\u6B63\u5E38\u3002\u6B63\u5F0F\u5305\u53E6\u505A\u5B89\u88C5\u4E0E\u56DE\u5F52\u68C0\u67E5\uFF0C\u4E0D\u5C06\u5019\u9009\u9875\u9762\u7ED3\u679C\u6269\u5927\u4E3A\u6B63\u5F0F\u5305\u5168\u529F\u80FD\u9A8C\u6536\u3002\u672A\u9A8C\u8BC1\u6A21\u578B\u751F\u6210\u5168\u94FE\u8DEF\u3001\u771F\u5B9E\u5BA1\u6279/\u5B50\u4EE3\u7406\u53CA macOS\uFF0C\u4E0D\u4FDD\u8BC1\u672A\u6D4B\u7248\u672C\u517C\u5BB9\uFF1B\u65E7 0.1.x \u4E0D\u5728\u672C\u7248\u652F\u6301\u8303\u56F4\u3002 + +\u3010\u53D1\u5E03\u68C0\u67E5\u3011\u6D41\u6C34\u7EBF\u5305\u542B\u6E90\u7801\u56DE\u5F52\u3001\u72EC\u7ACB\u5B89\u88C5\u3001\u5DE5\u5382\u95E8\u7981\u300114 \u9879\u5206\u680F\u951A\u70B9\u3001\u4FA7\u680F\u5B9E\u9645\u7EC4\u4EF6\u56DE\u5F52\u30015 \u9879 Chromium \u51E0\u4F55\u56DE\u5F52\u4E0E\u6570\u636E\u76EE\u5F55\u68C0\u67E5\u3002\u793E\u533A PR \u7684\u4FEE\u590D\u601D\u8DEF\u6309\u5F53\u524D\u6E90\u7801\u5438\u6536\uFF0C\u8D21\u732E\u8303\u56F4\u4E0E\u6765\u6E90\u89C1\u53D1\u5E03\u8BF4\u660E\uFF1B\u91C7\u7528\u4FEE\u6CD5\u4E0D\u7B49\u4E8E\u539F PR \u5DF2\u5408\u5E76\u3002 + +\u3010\u5347\u7EA7\u63D0\u9192\u3011\u516C\u5F00\u53D1\u5E03\u4EE5 GitHub Release \u4E3A\u51C6\u3002\u5B89\u88C5\u524D\u5907\u4EFD\u91CD\u8981\u6570\u636E\u3001\u6838\u5BF9\u5176\u4ED6\u63D2\u4EF6\u517C\u5BB9\u6027\uFF1B\u7F51\u9875\u7AEF\u91CD\u542F\u540E\u5237\u65B0\uFF0C\u684C\u9762\u7AEF\u5B8C\u6574\u9000\u51FA\u540E\u901A\u8FC7\u81EA\u5E26 CLI \u5B89\u88C5\u3001\u518D\u624B\u52A8\u6253\u5F00\u3002\u82E5\u4ECD\u52A0\u8F7D\u5931\u8D25\uFF0C\u4FDD\u7559\u7B2C\u4E00\u6761\u5B8C\u6574\u9519\u8BEF\u3002\u8FD9\u4E2A\u5305\u4E0D\u5305\u542B\u5BBF\u4E3B\u6A21\u578B HTTP 400 \u4FEE\u590D\u3002 + +\u2500\u2500\u2500\u2500\u2500\u2500\u2500 + +\u6B64\u524D\u7248\u672C\u8BF4\u660E\uFF08v0.4.0\uFF09 \u{1F5A5}\uFE0F \u7F51\u9875\u7AEF\u4E0E Windows \u684C\u9762\u7AEF\u9002\u914D\uFF0C\u4FEE\u590D\u9879\u76EE\u5185\u5BB9\u4E32\u6302 @@ -9027,7 +9144,7 @@ var CHANGELOG_V030 = `\u66F4\u65B0\u516C\u544A \xB7 v0.4.0 \u30103.1 \u6587\u5B57\u4E0E\u7EC6\u8282\u6253\u78E8\u3011\u5168\u5C40\u5B57\u4F53\u4E0E\u5B57\u53F7\u7EDF\u4E00\u4F18\u5316\u3001\u4E0B\u62C9\u9762\u677F\u73BB\u7483\u5316\u4E0E\u5BF9\u9F50\u3001\u83DC\u5355\u70B9\u9009\u540E\u4FDD\u6301\u6253\u5F00\u4FBF\u4E8E\u8FDE\u7EED\u9884\u89C8\u3001\u6309\u94AE\u63CF\u8FB9\u4E0E\u60AC\u505C\u53CD\u9988\u7B49\u4EA4\u4E92\u7EC6\u8282\uFF1B\u540C\u65F6\u4FEE\u590D\u4E86\u591A\u9879\u4F53\u9A8C\u95EE\u9898\uFF08\u7167\u7247\u4E0A\u4F20\u6E05\u6670\u5EA6\u3001\u80CC\u666F\u7F51\u683C\u7EBF\u5728\u7167\u7247\u6A21\u5F0F\u4E0B\u4E0D\u751F\u6548\u3001\u6D45\u8272\u4E3B\u9898\u4E0B\u5DE5\u4F5C\u72B6\u6001\u5149\u6548\u4E0D\u53EF\u89C1\u7B49\uFF09\u3002`; // src/client/updateCheck.ts -var LOCAL_VERSION = false ? "dev" : "0.4.0"; +var LOCAL_VERSION = false ? "dev" : "0.5.0"; var UPDATE_REPO = "Aisland-SJL/dsh-worktable"; var K_UPDATE_CHECK = "dsh.worktable.updateCheck.v1"; var K_LAST_CHECK = "dsh.worktable.lastUpdateCheck.v1"; @@ -16899,7 +17016,7 @@ function json(hljs) { } // src/client/split.tsx -var import_jsx_runtime = require("react/jsx-runtime"); +var import_jsx_runtime2 = require("react/jsx-runtime"); core_default.registerLanguage("typescript", typescript); core_default.registerLanguage("javascript", javascript2); core_default.registerLanguage("css", css2); @@ -17135,7 +17252,7 @@ function boxPayload(x0, y0, x1, y1) { const r = iframe.getBoundingClientRect(); if (r.width <= 0 || r.height <= 0) continue; if (cx < r.left || cx > r.right || cy < r.top || cy > r.bottom) continue; - src = iframe.getAttribute("src") ?? ""; + src = iframe.getAttribute("data-source-url") ?? iframe.getAttribute("src") ?? ""; let inner = null; try { inner = iframe.contentDocument; @@ -17200,10 +17317,10 @@ function confirmAnnot() { cancelAnnot(); } function AnnotationOverlay() { - const [, setTick] = (0, import_react.useState)(0); - (0, import_react.useEffect)(() => subscribeAnnot(() => setTick((t) => t + 1)), []); + const [, setTick] = (0, import_react2.useState)(0); + (0, import_react2.useEffect)(() => subscribeAnnot(() => setTick((t) => t + 1)), []); const s = annotState; - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { if (!s.on || s.started) return; let sx = 0; let sy = 0; @@ -17281,15 +17398,15 @@ function AnnotationOverlay() { window.removeEventListener("keydown", onKey, true); }; }, [s.on, s.started]); - const [hintOnce] = (0, import_react.useState)(() => { + const [hintOnce] = (0, import_react2.useState)(() => { try { return localStorage.getItem("dsh.worktable.annotHint.v1") !== "1"; } catch { return false; } }); - const [hintVisible, setHintVisible] = (0, import_react.useState)(false); - (0, import_react.useEffect)(() => { + const [hintVisible, setHintVisible] = (0, import_react2.useState)(false); + (0, import_react2.useEffect)(() => { if (!s.on || !hintOnce) return; setHintVisible(true); try { @@ -17301,20 +17418,20 @@ function AnnotationOverlay() { }, [s.on, hintOnce]); if (!s.on) return null; { - hintVisible && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_annotUi dsh-wt_annotHint", children: T("annot.hint") }); + hintVisible && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_annotUi dsh-wt_annotHint", children: T("annot.hint") }); } if (s.drawing) { const L = Math.min(s.bx0, s.bx1); const T2 = Math.min(s.by0, s.by1); const R = Math.max(s.bx0, s.bx1); const B = Math.max(s.by0, s.by1); - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_annotUi dsh-wt_annotSel", style: { left: L, top: T2, width: R - L, height: B - T2 } }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_annotUi dsh-wt_annotSel", style: { left: L, top: T2, width: R - L, height: B - T2 } }); } if (!s.started) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_annotUi dsh-wt_annotBubble", style: { left: s.px, top: s.py }, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 3.2v9.6M3.2 8h9.6", fill: "none", stroke: "#fff", strokeWidth: "1.8", strokeLinecap: "round" }) }) }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_annotUi dsh-wt_annotBubble", style: { left: s.px, top: s.py }, "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 3.2v9.6M3.2 8h9.6", fill: "none", stroke: "#fff", strokeWidth: "1.8", strokeLinecap: "round" }) }) }); } - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_annotUi dsh-wt_annotEditor", style: { left: s.ex, top: s.ey }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_annotUi dsh-wt_annotEditor", style: { left: s.ex, top: s.ey }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "textarea", { className: "dsh-wt_annotInput", @@ -17331,9 +17448,9 @@ function AnnotationOverlay() { } } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_annotBtns", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_annotOk", title: T("annot.ok"), "aria-label": T("annot.ok"), onClick: confirmAnnot, children: "\u2713" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_annotNo", title: T("annot.cancel"), "aria-label": T("annot.cancel"), onClick: cancelAnnot, children: "\u2715" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_annotBtns", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_annotOk", title: T("annot.ok"), "aria-label": T("annot.ok"), onClick: confirmAnnot, children: "\u2713" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_annotNo", title: T("annot.cancel"), "aria-label": T("annot.cancel"), onClick: cancelAnnot, children: "\u2715" }) ] }) ] }); } @@ -17593,6 +17710,8 @@ var splitStore = { this.savedMarginLeft = viewArea.style.marginLeft; this.savedMarginRight = viewArea.style.marginRight; this.savedMarginTop = viewArea.style.marginTop; + this.yieldObserver?.disconnect(); + this.yieldObserver?.observe(viewArea, { attributes: true, attributeFilter: ["style"] }); } this.observer?.disconnect(); this.observer.observe(next); @@ -17633,6 +17752,9 @@ var splitStore = { viewArea.style.marginLeft = this.lastMarginLeft; viewArea.style.marginRight = this.lastMarginRight; viewArea.style.marginTop = mt; + this.lastMarginLeft = viewArea.style.marginLeft; + this.lastMarginRight = viewArea.style.marginRight; + this.lastMarginTop = viewArea.style.marginTop; }, setChatW(w) { const g = this.geom; @@ -18041,8 +18163,8 @@ function makeDividerHandler(kind, index) { } function BrowserPane(props) { const initial = props.content?.url || "https://example.com"; - const [url, setUrl] = (0, import_react.useState)(initial); - const [src, setSrc] = (0, import_react.useState)(initial); + const [url, setUrl] = (0, import_react2.useState)(initial); + const [src, setSrc] = (0, import_react2.useState)(initial); const go = () => { const u = url.trim(); const ok = /^(\/|https?:\/\/)/i.test(u) ? u : "about:blank"; @@ -18051,9 +18173,9 @@ function BrowserPane(props) { splitStore.setTabContent(props.row, props.index, props.tabId, { kind: "builtin", type: "browser", url: ok }); } }; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_browserBar", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_browserBar", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { className: "dsh-wt_browserInput", @@ -18065,18 +18187,18 @@ function BrowserPane(props) { } } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_browserGo", onClick: go, children: "\u2197" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_browserGo", onClick: go, children: "\u2197" }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("iframe", { className: "dsh-wt_paneFrame", src, title: "browser" }, props.reloadKey) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SandboxedFrame, { url: src, title: "browser", reloadKey: props.reloadKey }) ] }); } function IframePane(props) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("iframe", { className: "dsh-wt_paneFrame", src: props.url, title: props.title ?? "" }, props.reloadKey); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SandboxedFrame, { ...props }); } function AnimPane(props) { const initial = props.content?.url || ""; - const [url, setUrl] = (0, import_react.useState)(initial); - const [src, setSrc] = (0, import_react.useState)(initial || "about:blank"); + const [url, setUrl] = (0, import_react2.useState)(initial); + const [src, setSrc] = (0, import_react2.useState)(initial || "about:blank"); const go = () => { const u = url.trim(); const ok = /^(\/|https?:\/\/)/i.test(u) ? u : "about:blank"; @@ -18085,9 +18207,9 @@ function AnimPane(props) { splitStore.setTabContent(props.row, props.index, props.tabId, { kind: "builtin", type: "anim", url: ok }); } }; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_browserBar", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_browserBar", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { className: "dsh-wt_browserInput", @@ -18099,36 +18221,36 @@ function AnimPane(props) { } } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_browserGo", onClick: go, children: "\u2197" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_browserGo", onClick: go, children: "\u2197" }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("iframe", { className: "dsh-wt_paneFrame", src, title: "anim" }, props.reloadKey) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SandboxedFrame, { url: src, title: "anim", reloadKey: props.reloadKey }) ] }); } function ThemeIcon({ mode, size }) { const s = size ?? 18; - if (mode === "dark") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M14 8.53A6 6 0 1 1 7.47 2 4.67 4.67 0 0 0 14 8.53z", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinejoin: "round" }) }); - if (mode === "light") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "3.1", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M8 1.6v1.7M8 12.7v1.7M1.6 8h1.7M12.7 8h1.7M3.5 3.5l1.2 1.2M11.3 11.3l1.2 1.2M12.5 3.5l-1.2 1.2M4.7 11.3l-1.2 1.2", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinecap: "round" }) + if (mode === "dark") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M14 8.53A6 6 0 1 1 7.47 2 4.67 4.67 0 0 0 14 8.53z", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinejoin: "round" }) }); + if (mode === "light") return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "3.1", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M8 1.6v1.7M8 12.7v1.7M1.6 8h1.7M12.7 8h1.7M3.5 3.5l1.2 1.2M11.3 11.3l1.2 1.2M12.5 3.5l-1.2 1.2M4.7 11.3l-1.2 1.2", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinecap: "round" }) ] }); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2", y: "3.2", width: "12", height: "8.8", rx: "1.6", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5.4 14.4h5.2M8 12v2.4", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinecap: "round" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2", y: "3.2", width: "12", height: "8.8", rx: "1.6", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M5.4 14.4h5.2M8 12v2.4", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinecap: "round" }) ] }); } function SliderIcon({ size }) { const s = size ?? 14; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.5 5.6h11M2.5 10.4h11", fill: "none", stroke: "currentColor", strokeWidth: "1.1" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10.4", cy: "5.6", r: "1.8", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5.8", cy: "10.4", r: "1.8", fill: "currentColor" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: s, height: s, viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.5 5.6h11M2.5 10.4h11", fill: "none", stroke: "currentColor", strokeWidth: "1.1" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "10.4", cy: "5.6", r: "1.8", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5.8", cy: "10.4", r: "1.8", fill: "currentColor" }) ] }); } function HslValInput(props) { const lo = props.min ?? 0; - const [draft, setDraft] = (0, import_react.useState)(String(props.value)); - const [focused, setFocused] = (0, import_react.useState)(false); - (0, import_react.useEffect)(() => { + const [draft, setDraft] = (0, import_react2.useState)(String(props.value)); + const [focused, setFocused] = (0, import_react2.useState)(false); + (0, import_react2.useEffect)(() => { if (!focused) setDraft(String(props.value)); }, [props.value, focused]); const commit = () => { @@ -18137,7 +18259,7 @@ function HslValInput(props) { n = Math.min(Math.max(n, lo), props.max); props.onCommit(n); }; - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { className: "dsh-wt_hslInput", @@ -18167,11 +18289,11 @@ function HslValInput(props) { var PLAIN_HSL_DARK = { h: -140, s: 31, l: 6 }; var PLAIN_HSL_LIGHT = { h: -146, s: 26, l: 95 }; function ConsoleVideo(props) { - const aRef = (0, import_react.useRef)(null); - const bRef = (0, import_react.useRef)(null); - const roleRef = (0, import_react.useRef)("a"); - const firedRef = (0, import_react.useRef)(false); - (0, import_react.useEffect)(() => { + const aRef = (0, import_react2.useRef)(null); + const bRef = (0, import_react2.useRef)(null); + const roleRef = (0, import_react2.useRef)("a"); + const firedRef = (0, import_react2.useRef)(false); + (0, import_react2.useEffect)(() => { const role = () => roleRef.current === "a" ? aRef.current : bRef.current; const spare = () => roleRef.current === "a" ? bRef.current : aRef.current; firedRef.current = false; @@ -18247,46 +18369,46 @@ function ConsoleVideo(props) { bRef.current?.removeEventListener("ended", onEnded); }; }, []); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { ref: aRef, className: "dsh-wt_consoleMedia", src: props.src, muted: true, autoPlay: true, playsInline: true, style: props.style }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { ref: bRef, className: "dsh-wt_consoleMedia", src: props.src, muted: true, playsInline: true, style: { ...props.style, opacity: 0 } }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("video", { ref: aRef, className: "dsh-wt_consoleMedia", src: props.src, muted: true, autoPlay: true, playsInline: true, style: props.style }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("video", { ref: bRef, className: "dsh-wt_consoleMedia", src: props.src, muted: true, playsInline: true, style: { ...props.style, opacity: 0 } }) ] }); } function ConsolePane() { - const [, setTick] = (0, import_react.useState)(0); - const [now, setNow] = (0, import_react.useState)(() => Date.now()); - const [cols, setColsState] = (0, import_react.useState)(() => splitEnv?.console?.getCols?.() ?? 3); - const [shape, setShapeState] = (0, import_react.useState)(() => splitEnv?.console?.getShape?.() ?? "square"); - const [bg, setBgState] = (0, import_react.useState)(() => splitEnv?.console?.getBg?.() ?? "glow"); - const [bgPhoto, setBgPhoto] = (0, import_react.useState)(""); - const [photoId, setPhotoIdLocal] = (0, import_react.useState)(""); - const [photoList, setPhotoList] = (0, import_react.useState)([]); - const [bgVideo, setBgVideo] = (0, import_react.useState)(""); - const [photoGrid, setPhotoGridState] = (0, import_react.useState)(() => splitEnv?.console?.getPhotoGrid?.() ?? true); - const [gridOpacity, setGridOpacityState] = (0, import_react.useState)(() => splitEnv?.console?.getGridOpacity?.() ?? 8); - const [cardBlur, setCardBlurState] = (0, import_react.useState)(() => splitEnv?.console?.getCardBlur?.() ?? 8); - const [plainBlur, setPlainBlurState] = (0, import_react.useState)(() => splitEnv?.console?.getPlainBlur?.() ?? 0); - const [glowBlur, setGlowBlurState] = (0, import_react.useState)(() => splitEnv?.console?.getGlowBlur?.() ?? 8); - const [plainGrid, setPlainGridState] = (0, import_react.useState)(() => splitEnv?.console?.getPlainGrid?.() ?? 5); - const [glowGrid, setGlowGridState] = (0, import_react.useState)(() => splitEnv?.console?.getGlowGrid?.() ?? 8); - const [glowSpeed, setGlowSpeedState] = (0, import_react.useState)(() => splitEnv?.console?.getGlowSpeed?.() ?? 50); - const [annOpen, setAnnOpen] = (0, import_react.useState)(false); - const [updStatus, setUpdStatus] = (0, import_react.useState)(() => readCache().status); - const [updInfo, setUpdInfo] = (0, import_react.useState)(() => readCache().info); - const [autoCheckOn, setAutoCheckOn] = (0, import_react.useState)(() => getAutoCheck()); - const updBusyRef = (0, import_react.useRef)(false); - const photoUrlRef = (0, import_react.useRef)(""); - const [openMenu, setOpenMenu] = (0, import_react.useState)(null); - const [bgEdit, setBgEdit] = (0, import_react.useState)(null); - const [plainHsl, setPlainHslState] = (0, import_react.useState)(() => { + const [, setTick] = (0, import_react2.useState)(0); + const [now, setNow] = (0, import_react2.useState)(() => Date.now()); + const [cols, setColsState] = (0, import_react2.useState)(() => splitEnv?.console?.getCols?.() ?? 3); + const [shape, setShapeState] = (0, import_react2.useState)(() => splitEnv?.console?.getShape?.() ?? "square"); + const [bg, setBgState] = (0, import_react2.useState)(() => splitEnv?.console?.getBg?.() ?? "glow"); + const [bgPhoto, setBgPhoto] = (0, import_react2.useState)(""); + const [photoId, setPhotoIdLocal] = (0, import_react2.useState)(""); + const [photoList, setPhotoList] = (0, import_react2.useState)([]); + const [bgVideo, setBgVideo] = (0, import_react2.useState)(""); + const [photoGrid, setPhotoGridState] = (0, import_react2.useState)(() => splitEnv?.console?.getPhotoGrid?.() ?? true); + const [gridOpacity, setGridOpacityState] = (0, import_react2.useState)(() => splitEnv?.console?.getGridOpacity?.() ?? 8); + const [cardBlur, setCardBlurState] = (0, import_react2.useState)(() => splitEnv?.console?.getCardBlur?.() ?? 8); + const [plainBlur, setPlainBlurState] = (0, import_react2.useState)(() => splitEnv?.console?.getPlainBlur?.() ?? 0); + const [glowBlur, setGlowBlurState] = (0, import_react2.useState)(() => splitEnv?.console?.getGlowBlur?.() ?? 8); + const [plainGrid, setPlainGridState] = (0, import_react2.useState)(() => splitEnv?.console?.getPlainGrid?.() ?? 5); + const [glowGrid, setGlowGridState] = (0, import_react2.useState)(() => splitEnv?.console?.getGlowGrid?.() ?? 8); + const [glowSpeed, setGlowSpeedState] = (0, import_react2.useState)(() => splitEnv?.console?.getGlowSpeed?.() ?? 50); + const [annOpen, setAnnOpen] = (0, import_react2.useState)(false); + const [updStatus, setUpdStatus] = (0, import_react2.useState)(() => readCache().status); + const [updInfo, setUpdInfo] = (0, import_react2.useState)(() => readCache().info); + const [autoCheckOn, setAutoCheckOn] = (0, import_react2.useState)(() => getAutoCheck()); + const updBusyRef = (0, import_react2.useRef)(false); + const photoUrlRef = (0, import_react2.useRef)(""); + const [openMenu, setOpenMenu] = (0, import_react2.useState)(null); + const [bgEdit, setBgEdit] = (0, import_react2.useState)(null); + const [plainHsl, setPlainHslState] = (0, import_react2.useState)(() => { const saved = splitEnv?.console?.getPlainHsl?.(); if (saved) return saved; return splitEnv?.console?.getTheme?.() === "light" ? { ...PLAIN_HSL_LIGHT } : { ...PLAIN_HSL_DARK }; }); - const [glowHsl, setGlowHslState] = (0, import_react.useState)(() => splitEnv?.console?.getGlowHsl?.() ?? { h: 0, s: 100, l: 100 }); - const [photoHsl, setPhotoHslState] = (0, import_react.useState)({ h: 0, s: 100, l: 100 }); - const gridRef = (0, import_react.useRef)(null); - const firstRectsRef = (0, import_react.useRef)(null); + const [glowHsl, setGlowHslState] = (0, import_react2.useState)(() => splitEnv?.console?.getGlowHsl?.() ?? { h: 0, s: 100, l: 100 }); + const [photoHsl, setPhotoHslState] = (0, import_react2.useState)({ h: 0, s: 100, l: 100 }); + const gridRef = (0, import_react2.useRef)(null); + const firstRectsRef = (0, import_react2.useRef)(null); const onCols = (n) => { const grid = gridRef.current; if (grid) { @@ -18427,7 +18549,7 @@ function ConsolePane() { updBusyRef.current = false; } }; - const [updCopied, setUpdCopied] = (0, import_react.useState)(false); + const [updCopied, setUpdCopied] = (0, import_react2.useState)(false); const onCopyUpgrade = async () => { const ok = await copyTextSafe(UPGRADE_AI); if (ok) { @@ -18448,11 +18570,11 @@ function ConsolePane() { setAutoCheck(next); if (next) void runUpdateCheck(false); }; - const dragRowRef = (0, import_react.useRef)(null); - const dragFlipRef = (0, import_react.useRef)(null); - const lastTargetRef = (0, import_react.useRef)(-1); - const [dragRowId, setDragRowId] = (0, import_react.useState)(null); - (0, import_react.useLayoutEffect)(() => { + const dragRowRef = (0, import_react2.useRef)(null); + const dragFlipRef = (0, import_react2.useRef)(null); + const lastTargetRef = (0, import_react2.useRef)(-1); + const [dragRowId, setDragRowId] = (0, import_react2.useState)(null); + (0, import_react2.useLayoutEffect)(() => { const first = dragFlipRef.current; if (!first) return; dragFlipRef.current = null; @@ -18588,7 +18710,7 @@ function ConsolePane() { splitEnv?.console?.setPhotoGrid?.(true); } }; - (0, import_react.useLayoutEffect)(() => { + (0, import_react2.useLayoutEffect)(() => { const first = firstRectsRef.current; if (!first) return; const grid = gridRef.current; @@ -18606,15 +18728,15 @@ function ConsolePane() { }); firstRectsRef.current = null; }, [cols]); - const [themeMode, setThemeMode] = (0, import_react.useState)(() => splitEnv?.console?.getTheme?.() ?? "system"); - const [sysDark, setSysDark] = (0, import_react.useState)(() => { + const [themeMode, setThemeMode] = (0, import_react2.useState)(() => splitEnv?.console?.getTheme?.() ?? "system"); + const [sysDark, setSysDark] = (0, import_react2.useState)(() => { try { return window.matchMedia("(prefers-color-scheme: dark)").matches; } catch { return true; } }); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { const env2 = splitEnv?.console; if (!env2) return; const bump = () => { @@ -18625,7 +18747,7 @@ function ConsolePane() { bump(); return un; }, []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { let mq = null; try { mq = window.matchMedia("(prefers-color-scheme: dark)"); @@ -18652,13 +18774,13 @@ function ConsolePane() { } }; }, []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { splitEnv?.console?.refreshPreviews?.(); }, []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { if (getAutoCheck()) void runUpdateCheck(false); }, []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { let alive = true; splitEnv?.console?.getPhotoLib?.().then((lib2) => { if (!alive) return; @@ -18676,11 +18798,11 @@ function ConsolePane() { alive = false; }; }, []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { if (!photoId) return; setPhotoHslState(splitEnv?.console?.getPhotoHsl?.(photoId) ?? { h: 0, s: 100, l: 100 }); }, [photoId]); - (0, import_react.useEffect)(() => () => { + (0, import_react2.useEffect)(() => () => { if (photoUrlRef.current) { try { URL.revokeObjectURL(photoUrlRef.current); @@ -18688,7 +18810,7 @@ function ConsolePane() { } } }, []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { const iv = window.setInterval(() => setNow(Date.now()), 1e3); return () => window.clearInterval(iv); }, []); @@ -18709,8 +18831,8 @@ function ConsolePane() { setThemeMode(th); env?.setTheme?.(th); }; - const prevPlainThemeRef = (0, import_react.useRef)(null); - (0, import_react.useEffect)(() => { + const prevPlainThemeRef = (0, import_react2.useRef)(null); + (0, import_react2.useEffect)(() => { const prev = prevPlainThemeRef.current; prevPlainThemeRef.current = resolvedTheme; if (!prev || prev === resolvedTheme) return; @@ -18735,49 +18857,49 @@ function ConsolePane() { { mode: "light", key: "console.themeLight" }, { mode: "system", key: "console.themeSystem" } ]; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_console", "data-wt-theme": resolvedTheme, "data-wt-shape": shape, "data-wt-bg": bg, "data-wt-grid": bg === "photo" && !photoGrid ? "off" : "on", style: { ...bg === "plain" ? { ["--wt-bg"]: "hsl(" + plainHsl.h + ", " + plainHsl.s + "%, " + plainHsl.l + "%)", ["--wt-gridPlain"]: "rgba(255,255,255," + plainGrid / 100 + ")", ["--wt-gridPlainLight"]: "rgba(27,31,36," + plainGrid / 100 + ")" } : {}, ...bg === "glow" ? { ["--wt-gridGlow"]: "rgba(255,255,255," + glowGrid / 100 + ")", ["--wt-gridGlowLight"]: "rgba(27,31,36," + glowGrid / 100 + ")", ["--wt-glowScale"]: glowSpeed === 0 ? "100000000" : String(50 / glowSpeed) } : {}, ...bg === "photo" ? { ["--wt-gridPhoto"]: "rgba(255,255,255," + gridOpacity / 100 + ")" } : {}, ["--wt-cardBlur"]: (bg === "plain" ? plainBlur : bg === "glow" ? glowBlur : cardBlur) + "px" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-wt_consoleBg", "aria-hidden": true, style: bg === "glow" && (glowHsl.h !== 0 || glowHsl.s !== 100 || glowHsl.l !== 100) ? { filter: "hue-rotate(" + glowHsl.h + "deg) saturate(" + glowHsl.s + "%) brightness(" + glowHsl.l + "%)" } : void 0, children: [ - bg === "photo" && (bgVideo ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConsoleVideo, { src: bgVideo, style: photoHsl.h !== 0 || photoHsl.s !== 100 || photoHsl.l !== 100 ? { filter: "hue-rotate(" + photoHsl.h + "deg) saturate(" + photoHsl.s + "%) brightness(" + photoHsl.l + "%)" } : void 0 }) : bgPhoto ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { className: "dsh-wt_consoleMedia", src: bgPhoto, alt: "", style: photoHsl.h !== 0 || photoHsl.s !== 100 || photoHsl.l !== 100 ? { filter: "hue-rotate(" + photoHsl.h + "deg) saturate(" + photoHsl.s + "%) brightness(" + photoHsl.l + "%)" } : void 0 }) : null), - bg === "glow" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob1" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob2" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob3" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob4" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_console", "data-wt-theme": resolvedTheme, "data-wt-shape": shape, "data-wt-bg": bg, "data-wt-grid": bg === "photo" && !photoGrid ? "off" : "on", style: { ...bg === "plain" ? { ["--wt-bg"]: "hsl(" + plainHsl.h + ", " + plainHsl.s + "%, " + plainHsl.l + "%)", ["--wt-gridPlain"]: "rgba(255,255,255," + plainGrid / 100 + ")", ["--wt-gridPlainLight"]: "rgba(27,31,36," + plainGrid / 100 + ")" } : {}, ...bg === "glow" ? { ["--wt-gridGlow"]: "rgba(255,255,255," + glowGrid / 100 + ")", ["--wt-gridGlowLight"]: "rgba(27,31,36," + glowGrid / 100 + ")", ["--wt-glowScale"]: glowSpeed === 0 ? "100000000" : String(50 / glowSpeed) } : {}, ...bg === "photo" ? { ["--wt-gridPhoto"]: "rgba(255,255,255," + gridOpacity / 100 + ")" } : {}, ["--wt-cardBlur"]: (bg === "plain" ? plainBlur : bg === "glow" ? glowBlur : cardBlur) + "px" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_consoleBg", "aria-hidden": true, style: bg === "glow" && (glowHsl.h !== 0 || glowHsl.s !== 100 || glowHsl.l !== 100) ? { filter: "hue-rotate(" + glowHsl.h + "deg) saturate(" + glowHsl.s + "%) brightness(" + glowHsl.l + "%)" } : void 0, children: [ + bg === "photo" && (bgVideo ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ConsoleVideo, { src: bgVideo, style: photoHsl.h !== 0 || photoHsl.s !== 100 || photoHsl.l !== 100 ? { filter: "hue-rotate(" + photoHsl.h + "deg) saturate(" + photoHsl.s + "%) brightness(" + photoHsl.l + "%)" } : void 0 }) : bgPhoto ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { className: "dsh-wt_consoleMedia", src: bgPhoto, alt: "", style: photoHsl.h !== 0 || photoHsl.s !== 100 || photoHsl.l !== 100 ? { filter: "hue-rotate(" + photoHsl.h + "deg) saturate(" + photoHsl.s + "%) brightness(" + photoHsl.l + "%)" } : void 0 }) : null), + bg === "glow" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob1" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob2" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob3" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("i", { className: "dsh-wt_blob dsh-wt_blob4" }) ] }) ] }), - openMenu !== null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_dropMask", onClick: () => setOpenMenu(null) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_consoleScroll", children: annOpen ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_announceCtr", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_announce", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_announceHead", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-wt_announceVer", children: [ + openMenu !== null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_dropMask", onClick: () => setOpenMenu(null) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_consoleScroll", children: annOpen ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_announceCtr", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_announce", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_announceHead", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_announceVer", children: [ T("annot.curVer"), " v", LOCAL_VERSION ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_announceBtn", disabled: updStatus === "checking", onClick: () => runUpdateCheck(true), children: updStatus === "checking" ? T("annot.checking") : T("annot.checkNow") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_announceAuto", children: T("annot.autoCheck") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_switch" + (autoCheckOn ? " dsh-wt_switchOn" : ""), "aria-pressed": autoCheckOn, "aria-label": T("annot.autoCheck"), onClick: onAutoCheckToggle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_switchKnob" }) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_announceBtn", disabled: updStatus === "checking", onClick: () => runUpdateCheck(true), children: updStatus === "checking" ? T("annot.checking") : T("annot.checkNow") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_announceAuto", children: T("annot.autoCheck") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_switch" + (autoCheckOn ? " dsh-wt_switchOn" : ""), "aria-pressed": autoCheckOn, "aria-label": T("annot.autoCheck"), onClick: onAutoCheckToggle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_switchKnob" }) }) ] }), - updInfo && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_announceUpdate", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-wt_announceNewVer", children: [ + updInfo && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_announceUpdate", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_announceNewVer", children: [ T("annot.newVer"), " v", updInfo.latest ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_announceUpg" + (updCopied ? " dsh-wt_announceUpgOk" : ""), onClick: onCopyUpgrade, children: updCopied ? T("annot.copied") : T("annot.copyUpgrade") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_announceSkip dsh-wt_announceSkipGo", onClick: () => { + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_announceUpg" + (updCopied ? " dsh-wt_announceUpgOk" : ""), onClick: onCopyUpgrade, children: updCopied ? T("annot.copied") : T("annot.copyUpgrade") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_announceSkip dsh-wt_announceSkipGo", onClick: () => { try { window.open(updInfo.url, "_blank"); } catch { } }, children: T("annot.gotoRelease") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_announceSkip", onClick: onSkipVersion, children: T("annot.skipVer") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_announceHow", children: T("annot.howUpdate") }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_announceSkip", onClick: onSkipVersion, children: T("annot.skipVer") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_announceHow", children: T("annot.howUpdate") }) ] }), - updStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_announceStatus", children: T("annot.checkFail") }), - updStatus === "uptodate" && !updInfo && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_announceStatus", children: T("annot.latest") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_announceBody", children: CHANGELOG_V030 }) - ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: gridRef, className: "dsh-wt_consoleGrid", style: { ["--wt-cols"]: cols }, children: [ - cards.map((c) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( + updStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_announceStatus", children: T("annot.checkFail") }), + updStatus === "uptodate" && !updInfo && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_announceStatus", children: T("annot.latest") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_announceBody", children: CHANGELOG_V030 }) + ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: gridRef, className: "dsh-wt_consoleGrid", style: { ["--wt-cols"]: cols }, children: [ + cards.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "div", { "data-flip": c.id, @@ -18791,22 +18913,22 @@ function ConsolePane() { env.onOpen(c.id); }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_consoleCardHead", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_consoleIcon", "aria-hidden": true, children: c.icon }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_consoleName", children: c.name }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_consoleCardHead", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_consoleIcon", "aria-hidden": true, children: c.icon }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_consoleName", children: c.name }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_consoleDivider", "aria-hidden": true }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_consoleStatusRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_consoleStatus dsh-wt_consoleStatus-" + c.status, children: statusLabel[c.status] }), - c.runtimeMs != null && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_consoleRuntime", children: fmtDur(c.runtimeMs) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_consoleDivider", "aria-hidden": true }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_consoleStatusRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_consoleStatus dsh-wt_consoleStatus-" + c.status, children: statusLabel[c.status] }), + c.runtimeMs != null && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_consoleRuntime", children: fmtDur(c.runtimeMs) }) ] }), - c.status === "busy" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_consoleSweep", "aria-hidden": true }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_consolePreview" + (c.preview ? "" : " dsh-wt_consolePreviewNone"), title: c.preview, children: c.preview || (c.bound ? T("console.noPreview") : T("console.unboundShort")) }) + c.status === "busy" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_consoleSweep", "aria-hidden": true }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_consolePreview" + (c.preview ? "" : " dsh-wt_consolePreviewNone"), title: c.preview, children: c.preview || (c.bound ? T("console.noPreview") : T("console.unboundShort")) }) ] }, c.id )), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "div", { role: "button", @@ -18815,195 +18937,195 @@ function ConsolePane() { title: T("console.addProject"), onClick: () => env?.onAdd?.(), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_consoleAddPlus", "aria-hidden": true, children: "\uFF0B" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_consoleAddLabel", children: T("console.addProject") }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_consoleAddPlus", "aria-hidden": true, children: "\uFF0B" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_consoleAddLabel", children: T("console.addProject") }) ] } ), - cards.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_consoleEmpty", children: T("console.empty") }) + cards.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_consoleEmpty", children: T("console.empty") }) ] }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_consoleDockWrap", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_consoleDock", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "theme" ? " dsh-wt_dockBtnOn" : ""), title: T("console.themeLabel"), "aria-label": T("console.themeLabel"), onClick: () => setOpenMenu(openMenu === "theme" ? null : "theme"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeIcon, { mode: themeMode }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "shape" ? " dsh-wt_dockBtnOn" : ""), title: T("console.shapeLabel"), "aria-label": T("console.shapeLabel"), onClick: () => setOpenMenu(openMenu === "shape" ? null : "shape"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.2", y: "2.2", width: "7", height: "7", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "10.6", cy: "10.6", r: "3.8", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_consoleDockWrap", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_consoleDock", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "theme" ? " dsh-wt_dockBtnOn" : ""), title: T("console.themeLabel"), "aria-label": T("console.themeLabel"), onClick: () => setOpenMenu(openMenu === "theme" ? null : "theme"), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeIcon, { mode: themeMode }) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "shape" ? " dsh-wt_dockBtnOn" : ""), title: T("console.shapeLabel"), "aria-label": T("console.shapeLabel"), onClick: () => setOpenMenu(openMenu === "shape" ? null : "shape"), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.2", y: "2.2", width: "7", height: "7", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "10.6", cy: "10.6", r: "3.8", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }) ] }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "bg" ? " dsh-wt_dockBtnOn" : ""), title: T("console.bgLabel"), "aria-label": T("console.bgLabel"), onClick: () => { + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "bg" ? " dsh-wt_dockBtnOn" : ""), title: T("console.bgLabel"), "aria-label": T("console.bgLabel"), onClick: () => { setOpenMenu(openMenu === "bg" ? null : "bg"); setBgEdit(null); - }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.2", y: "3.2", width: "11.6", height: "9.6", rx: "1.6", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5.9", cy: "6.7", r: "1.05", fill: "none", stroke: "currentColor", strokeWidth: "0.8" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.4 11.4l3-3 2.3 2.3 1.9-1.9 3 2.6", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }) + }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.2", y: "3.2", width: "11.6", height: "9.6", rx: "1.6", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5.9", cy: "6.7", r: "1.05", fill: "none", stroke: "currentColor", strokeWidth: "0.8" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.4 11.4l3-3 2.3 2.3 1.9-1.9 3 2.6", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }) ] }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "cols" ? " dsh-wt_dockBtnOn" : ""), title: T("console.colsLabel"), "aria-label": T("console.colsLabel"), onClick: () => setOpenMenu(openMenu === "cols" ? null : "cols"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.2", y: "3.4", width: "2.7", height: "9.2", rx: "0.9", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "6.65", y: "3.4", width: "2.7", height: "9.2", rx: "0.9", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "11.1", y: "3.4", width: "2.7", height: "9.2", rx: "0.9", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dockBtn" + (openMenu === "cols" ? " dsh-wt_dockBtnOn" : ""), title: T("console.colsLabel"), "aria-label": T("console.colsLabel"), onClick: () => setOpenMenu(openMenu === "cols" ? null : "cols"), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.2", y: "3.4", width: "2.7", height: "9.2", rx: "0.9", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "6.65", y: "3.4", width: "2.7", height: "9.2", rx: "0.9", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "11.1", y: "3.4", width: "2.7", height: "9.2", rx: "0.9", fill: "none", stroke: "currentColor", strokeWidth: "0.9" }) ] }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dockBtn" + (annOpen ? " dsh-wt_dockBtnOn" : ""), title: T("annot.updateTitle"), "aria-label": T("annot.updateTitle"), onClick: () => { + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dockBtn" + (annOpen ? " dsh-wt_dockBtnOn" : ""), title: T("annot.updateTitle"), "aria-label": T("annot.updateTitle"), onClick: () => { setOpenMenu(null); setAnnOpen((v) => !v); }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.6 7c0-2.9 2.4-4.9 5.4-4.9s5.4 2 5.4 4.9v2.9l1.2 1.7H1.4l1.2-1.7z", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinejoin: "round" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M6.2 12.9c.3.7 1 1.2 1.8 1.2s1.5-.5 1.8-1.2", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinecap: "round" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "18", height: "18", viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.6 7c0-2.9 2.4-4.9 5.4-4.9s5.4 2 5.4 4.9v2.9l1.2 1.7H1.4l1.2-1.7z", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinejoin: "round" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M6.2 12.9c.3.7 1 1.2 1.8 1.2s1.5-.5 1.8-1.2", fill: "none", stroke: "currentColor", strokeWidth: "0.9", strokeLinecap: "round" }) ] }), - updInfo && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_dockBadge", "aria-hidden": true }) + updInfo && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_dockBadge", "aria-hidden": true }) ] }) ] }), - openMenu === "theme" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_drop", children: themeOpts.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (themeMode === o.mode ? " dsh-wt_dropItemOn" : ""), onClick: () => setTheme(o.mode), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeIcon, { mode: o.mode, size: 13 }), + openMenu === "theme" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_drop", children: themeOpts.map((o) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (themeMode === o.mode ? " dsh-wt_dropItemOn" : ""), onClick: () => setTheme(o.mode), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ThemeIcon, { mode: o.mode, size: 13 }), T(o.key) ] }, o.mode)) }), - openMenu === "shape" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_drop", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (shape === "square" ? " dsh-wt_dropItemOn" : ""), onClick: () => onShape("square"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) }), + openMenu === "shape" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_drop", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (shape === "square" ? " dsh-wt_dropItemOn" : ""), onClick: () => onShape("square"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "4", y: "4", width: "8", height: "8", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) }), T("console.shapeSquare") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (shape === "circle" ? " dsh-wt_dropItemOn" : ""), onClick: () => onShape("circle"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "8", r: "4.5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (shape === "circle" ? " dsh-wt_dropItemOn" : ""), onClick: () => onShape("circle"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "4.5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) }), T("console.shapeCircle") ] }) ] }), - openMenu === "bg" && (bgEdit === null ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_drop", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_dropRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (bg === "plain" ? " dsh-wt_dropItemOn" : ""), onClick: () => onBg("plain"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "3", width: "10", height: "10", rx: "2", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }) }), + openMenu === "bg" && (bgEdit === null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_drop", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_dropRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (bg === "plain" ? " dsh-wt_dropItemOn" : ""), onClick: () => onBg("plain"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "3", y: "3", width: "10", height: "10", rx: "2", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }) }), T("console.bgPlain") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dropGear", title: T("console.bgEdit"), "aria-label": T("console.bgEdit"), onClick: () => setBgEdit("plain"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SliderIcon, {}) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dropGear", title: T("console.bgEdit"), "aria-label": T("console.bgEdit"), onClick: () => setBgEdit("plain"), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SliderIcon, {}) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_dropRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (bg === "glow" ? " dsh-wt_dropItemOn" : ""), onClick: () => onBg("glow"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.4 5.6c2-1.5 3.9-1.5 5.6 0 1.9 1.6 3.6 1.6 5.6 0M2.4 10.4c2-1.5 3.9-1.5 5.6 0 1.9 1.6 3.6 1.6 5.6 0", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_dropRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (bg === "glow" ? " dsh-wt_dropItemOn" : ""), onClick: () => onBg("glow"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.4 5.6c2-1.5 3.9-1.5 5.6 0 1.9 1.6 3.6 1.6 5.6 0M2.4 10.4c2-1.5 3.9-1.5 5.6 0 1.9 1.6 3.6 1.6 5.6 0", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round" }) }), T("console.bgGlow") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dropGear", title: T("console.bgEdit"), "aria-label": T("console.bgEdit"), onClick: () => setBgEdit("glow"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SliderIcon, {}) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dropGear", title: T("console.bgEdit"), "aria-label": T("console.bgEdit"), onClick: () => setBgEdit("glow"), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SliderIcon, {}) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_dropRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (bg === "photo" ? " dsh-wt_dropItemOn" : ""), onClick: () => onBg("photo"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.5", y: "3.5", width: "11", height: "9", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5.8", cy: "6.6", r: "1.1", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.2 11.2l2.8-2.8 2.2 2.2 1.8-1.8 2.8 2.4", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_dropRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dropItem" + (bg === "photo" ? " dsh-wt_dropItemOn" : ""), onClick: () => onBg("photo"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.5", y: "3.5", width: "11", height: "9", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5.8", cy: "6.6", r: "1.1", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.2 11.2l2.8-2.8 2.2 2.2 1.8-1.8 2.8 2.4", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }) ] }), T("console.bgCustom") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dropGear", title: T("console.bgEdit"), "aria-label": T("console.bgEdit"), onClick: () => setBgEdit("photo"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SliderIcon, {}) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dropGear", title: T("console.bgEdit"), "aria-label": T("console.bgEdit"), onClick: () => setBgEdit("photo"), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SliderIcon, {}) }) ] }) - ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_drop" + (bgEdit === "photo" ? " dsh-wt_dropWide" : ""), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hslHead", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_hslBack", title: T("console.bgEditBack"), "aria-label": T("console.bgEditBack"), onClick: () => setBgEdit(null), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10.2 3.2 5.4 8l4.8 4.8", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hslTitle", children: bgEdit === "plain" ? T("console.bgEditPlain") : bgEdit === "glow" ? T("console.bgEditGlow") : T("console.bgEditPhoto") }) + ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_drop" + (bgEdit === "photo" ? " dsh-wt_dropWide" : ""), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hslHead", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_hslBack", title: T("console.bgEditBack"), "aria-label": T("console.bgEditBack"), onClick: () => setBgEdit(null), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M10.2 3.2 5.4 8l4.8 4.8", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hslTitle", children: bgEdit === "plain" ? T("console.bgEditPlain") : bgEdit === "glow" ? T("console.bgEditGlow") : T("console.bgEditPhoto") }) ] }), - bgEdit === "photo" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_dropRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_dropItem", onClick: pickPhotoFile, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.5", y: "3.5", width: "11", height: "9", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "5.8", cy: "6.6", r: "1.1", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.2 11.2l2.8-2.8 2.2 2.2 1.8-1.8 2.8 2.4", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }) + bgEdit === "photo" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_dropRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_dropItem", onClick: pickPhotoFile, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "13", height: "13", viewBox: "0 0 16 16", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.5", y: "3.5", width: "11", height: "9", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "5.8", cy: "6.6", r: "1.1", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.2 11.2l2.8-2.8 2.2 2.2 1.8-1.8 2.8 2.4", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }) ] }), T("console.bgPhoto") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_gridHalf", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_gridLabel", children: T("console.bgGridLabel") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_switch" + (photoGrid ? " dsh-wt_switchOn" : ""), "aria-pressed": photoGrid, "aria-label": T("console.bgGridLabel"), onClick: togglePhotoGrid, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_switchKnob" }) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_gridHalf", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_gridLabel", children: T("console.bgGridLabel") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_switch" + (photoGrid ? " dsh-wt_switchOn" : ""), "aria-pressed": photoGrid, "aria-label": T("console.bgGridLabel"), onClick: togglePhotoGrid, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_switchKnob" }) }) ] }) ] }), - photoList.length > 0 ? photoList.slice(0, 4).map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-rid": p.id, className: "dsh-wt_dropRow dsh-wt_photoRow" + (photoId === p.id ? " dsh-wt_photoRowOn" : ""), onClick: () => selectPhoto(p), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-wt_thumbWrap", children: [ - p.kind === "video" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("video", { className: "dsh-wt_photoThumb", src: p.url, muted: true, playsInline: true, preload: "metadata" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { className: "dsh-wt_photoThumb", src: p.url, alt: "" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_thumbBadge", "aria-hidden": true, children: p.kind === "video" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "8", height: "8", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4.5 3.8l8 4.2-8 4.2z", fill: "currentColor" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "8", height: "8", viewBox: "0 0 16 16", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "2.5", y: "3.5", width: "11", height: "9", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.4 11l2.6-2.6 2.1 2.1 1.7-1.7 2.8 2.4", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) + photoList.length > 0 ? photoList.slice(0, 4).map((p) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { "data-rid": p.id, className: "dsh-wt_dropRow dsh-wt_photoRow" + (photoId === p.id ? " dsh-wt_photoRowOn" : ""), onClick: () => selectPhoto(p), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbWrap", children: [ + p.kind === "video" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("video", { className: "dsh-wt_photoThumb", src: p.url, muted: true, playsInline: true, preload: "metadata" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { className: "dsh-wt_photoThumb", src: p.url, alt: "" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbBadge", "aria-hidden": true, children: p.kind === "video" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "8", height: "8", viewBox: "0 0 16 16", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4.5 3.8l8 4.2-8 4.2z", fill: "currentColor" }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "8", height: "8", viewBox: "0 0 16 16", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: "2.5", y: "3.5", width: "11", height: "9", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.4 11l2.6-2.6 2.1 2.1 1.7-1.7 2.8 2.4", fill: "none", stroke: "currentColor", strokeWidth: "1.5" }) ] }) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_photoName", children: photoId === p.id ? T("console.bgPhotoCurrent") : T("console.bgPhotoUse") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dragHandle" + (dragRowId === p.id ? " dsh-wt_dragHandleOn" : ""), title: T("console.bgMediaDrag"), "aria-label": T("console.bgMediaDrag"), onPointerDown: (e) => onHandleDown(p.id, e), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { width: "10", height: "12", viewBox: "0 0 10 12", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "2.5", cy: "2", r: "1.1", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7.5", cy: "2", r: "1.1", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "2.5", cy: "6", r: "1.1", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7.5", cy: "6", r: "1.1", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "2.5", cy: "10", r: "1.1", fill: "currentColor" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "7.5", cy: "10", r: "1.1", fill: "currentColor" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_photoName", children: photoId === p.id ? T("console.bgPhotoCurrent") : T("console.bgPhotoUse") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dragHandle" + (dragRowId === p.id ? " dsh-wt_dragHandleOn" : ""), title: T("console.bgMediaDrag"), "aria-label": T("console.bgMediaDrag"), onPointerDown: (e) => onHandleDown(p.id, e), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { width: "10", height: "12", viewBox: "0 0 10 12", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "2.5", cy: "2", r: "1.1", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "7.5", cy: "2", r: "1.1", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "2.5", cy: "6", r: "1.1", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "7.5", cy: "6", r: "1.1", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "2.5", cy: "10", r: "1.1", fill: "currentColor" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "7.5", cy: "10", r: "1.1", fill: "currentColor" }) ] }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_dropTrash", title: T("console.bgPhotoDelete"), "aria-label": T("console.bgPhotoDelete"), onClick: (e) => { + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_dropTrash", title: T("console.bgPhotoDelete"), "aria-label": T("console.bgPhotoDelete"), onClick: (e) => { e.stopPropagation(); removePhotoById(p); - }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: "11", height: "11", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2.5 4.2h11M6.5 4.2V2.9c0-.6.4-1 .9-1h1.2c.5 0 .9.4.9 1v1.3M4.2 4.2l.5 8.1c0 .7.5 1.2 1.2 1.2h4.2c.7 0 1.2-.5 1.2-1.2l.5-8.1", fill: "none", stroke: "currentColor", strokeWidth: "1.1", strokeLinecap: "round" }) }) }) - ] }, p.id)) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_photoEmpty", children: T("console.bgPhotoNone") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_hslDivider" }) + }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "11", height: "11", viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2.5 4.2h11M6.5 4.2V2.9c0-.6.4-1 .9-1h1.2c.5 0 .9.4.9 1v1.3M4.2 4.2l.5 8.1c0 .7.5 1.2 1.2 1.2h4.2c.7 0 1.2-.5 1.2-1.2l.5-8.1", fill: "none", stroke: "currentColor", strokeWidth: "1.1", strokeLinecap: "round" }) }) }) + ] }, p.id)) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_photoEmpty", children: T("console.bgPhotoNone") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_hslDivider" }) ] }), (() => { const v = bgEdit === "plain" ? plainHsl : bgEdit === "glow" ? glowHsl : photoHsl; const sMax = bgEdit === "plain" ? 100 : 200; const lMax = bgEdit === "plain" ? 100 : 200; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - bgEdit === "glow" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipSpeed"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hslLabel", children: "S" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: 100, step: 5, value: glowSpeed, onChange: (e) => onGlowSpeed(Number(e.target.value)) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HslValInput, { value: glowSpeed, min: 0, max: 100, onCommit: onGlowSpeed }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + bgEdit === "glow" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipSpeed"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hslLabel", children: "S" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: 100, step: 5, value: glowSpeed, onChange: (e) => onGlowSpeed(Number(e.target.value)) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HslValInput, { value: glowSpeed, min: 0, max: 100, onCommit: onGlowSpeed }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipB"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hslLabel", children: "B" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: 20, step: 1, value: bgEdit === "plain" ? plainBlur : bgEdit === "glow" ? glowBlur : cardBlur, onChange: (e) => onModeBlur(bgEdit, Number(e.target.value)) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HslValInput, { value: bgEdit === "plain" ? plainBlur : bgEdit === "glow" ? glowBlur : cardBlur, min: 0, max: 20, onCommit: (n) => onModeBlur(bgEdit, n) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipB"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hslLabel", children: "B" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: 20, step: 1, value: bgEdit === "plain" ? plainBlur : bgEdit === "glow" ? glowBlur : cardBlur, onChange: (e) => onModeBlur(bgEdit, Number(e.target.value)) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HslValInput, { value: bgEdit === "plain" ? plainBlur : bgEdit === "glow" ? glowBlur : cardBlur, min: 0, max: 20, onCommit: (n) => onModeBlur(bgEdit, n) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipT"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hslLabel", children: "T" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: 30, step: 1, value: bgEdit === "plain" ? plainGrid : bgEdit === "glow" ? glowGrid : gridOpacity, onChange: (e) => bgEdit === "photo" ? onGridOpacity(Number(e.target.value)) : onModeGrid(bgEdit, Number(e.target.value)) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HslValInput, { value: bgEdit === "plain" ? plainGrid : bgEdit === "glow" ? glowGrid : gridOpacity, min: 0, max: 30, onCommit: (n) => bgEdit === "photo" ? onGridOpacity(n) : onModeGrid(bgEdit, n) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipT"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hslLabel", children: "T" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: 30, step: 1, value: bgEdit === "plain" ? plainGrid : bgEdit === "glow" ? glowGrid : gridOpacity, onChange: (e) => bgEdit === "photo" ? onGridOpacity(Number(e.target.value)) : onModeGrid(bgEdit, Number(e.target.value)) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HslValInput, { value: bgEdit === "plain" ? plainGrid : bgEdit === "glow" ? glowGrid : gridOpacity, min: 0, max: 30, onCommit: (n) => bgEdit === "photo" ? onGridOpacity(n) : onModeGrid(bgEdit, n) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipH"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hslLabel", children: "H" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: -180, max: 180, step: 1, value: v.h, onChange: (e) => editHsl(bgEdit, { h: Number(e.target.value) }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HslValInput, { value: v.h, min: -180, max: 180, onCommit: (n) => editHsl(bgEdit, { h: n }) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipH"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hslLabel", children: "H" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: -180, max: 180, step: 1, value: v.h, onChange: (e) => editHsl(bgEdit, { h: Number(e.target.value) }) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HslValInput, { value: v.h, min: -180, max: 180, onCommit: (n) => editHsl(bgEdit, { h: n }) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipS"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hslLabel", children: "S" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: sMax, step: 1, value: v.s, onChange: (e) => editHsl(bgEdit, { s: Number(e.target.value) }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HslValInput, { value: v.s, max: sMax, onCommit: (n) => editHsl(bgEdit, { s: n }) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipS"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hslLabel", children: "S" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: sMax, step: 1, value: v.s, onChange: (e) => editHsl(bgEdit, { s: Number(e.target.value) }) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HslValInput, { value: v.s, max: sMax, onCommit: (n) => editHsl(bgEdit, { s: n }) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipL"), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hslLabel", children: "L" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: lMax, step: 1, value: v.l, onChange: (e) => editHsl(bgEdit, { l: Number(e.target.value) }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HslValInput, { value: v.l, max: lMax, onCommit: (n) => editHsl(bgEdit, { l: n }) }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hslRow", "data-tip": T("console.bgTipL"), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hslLabel", children: "L" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_hslSlider", type: "range", min: 0, max: lMax, step: 1, value: v.l, onChange: (e) => editHsl(bgEdit, { l: Number(e.target.value) }) }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(HslValInput, { value: v.l, max: lMax, onCommit: (n) => editHsl(bgEdit, { l: n }) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_hslReset", onClick: () => resetHsl(bgEdit), children: T("console.bgEditReset") }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_hslReset", onClick: () => resetHsl(bgEdit), children: T("console.bgEditReset") }) ] }); })() ] })), - openMenu === "cols" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_drop", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_hbar", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hbarFill", style: { width: 8 + (cols - 1) * 21 + "%" } }), - [1, 2, 3, 4, 5].map((v) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_hbarLine", style: { left: 8 + (v - 1) * 21 + "%" }, "aria-label": String(v), title: String(v), onClick: () => onCols(v) }, v)), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_hbarKnob", style: { left: 8 + (cols - 1) * 21 + "%" } }) + openMenu === "cols" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_drop", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_hbar", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hbarFill", style: { width: 8 + (cols - 1) * 21 + "%" } }), + [1, 2, 3, 4, 5].map((v) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_hbarLine", style: { left: 8 + (v - 1) * 21 + "%" }, "aria-label": String(v), title: String(v), onClick: () => onCols(v) }, v)), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_hbarKnob", style: { left: 8 + (cols - 1) * 21 + "%" } }) ] }) }) ] }) ] }); } function FolderIcon() { - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "dsh-wt_treeIcon", width: "13", height: "13", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M1.75 3.25A1.75 1.75 0 0 1 3.5 1.5h2.63a1.75 1.75 0 0 1 1.34.66l.62.79a1.75 1.75 0 0 0 1.34.66H12.5a1.75 1.75 0 0 1 1.75 1.75v7.39A1.75 1.75 0 0 1 12.5 14.5h-9a1.75 1.75 0 0 1-1.75-1.75V3.25Z", fill: "var(--dsw-alias-state-accent-primary,#4f8ef7)", opacity: "0.9" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M1.75 5.75h12.5v7a1.75 1.75 0 0 1-1.75 1.75h-9a1.75 1.75 0 0 1-1.75-1.75v-7Z", fill: "var(--dsw-alias-state-accent-primary,#4f8ef7)", opacity: "0.4" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "dsh-wt_treeIcon", width: "13", height: "13", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1.75 3.25A1.75 1.75 0 0 1 3.5 1.5h2.63a1.75 1.75 0 0 1 1.34.66l.62.79a1.75 1.75 0 0 0 1.34.66H12.5a1.75 1.75 0 0 1 1.75 1.75v7.39A1.75 1.75 0 0 1 12.5 14.5h-9a1.75 1.75 0 0 1-1.75-1.75V3.25Z", fill: "var(--dsw-alias-state-accent-primary,#4f8ef7)", opacity: "0.9" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1.75 5.75h12.5v7a1.75 1.75 0 0 1-1.75 1.75h-9a1.75 1.75 0 0 1-1.75-1.75v-7Z", fill: "var(--dsw-alias-state-accent-primary,#4f8ef7)", opacity: "0.4" }) ] }); } function FileIcon() { - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { className: "dsh-wt_treeIcon", width: "13", height: "13", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 1.5h5.25a1 1 0 0 1 .71.29l3.25 3.25a1 1 0 0 1 .29.71V13.5a1 1 0 0 1-1 1h-8.5a1 1 0 0 1-1-1v-11a1 1 0 0 1 1-1Z", fill: "var(--dsw-alias-fill-l1,rgba(255,255,255,.06))", stroke: "var(--dsw-alias-label-secondary,#9aa4b2)", strokeWidth: "1.1" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M9.25 1.5V4.75h3.25", fill: "none", stroke: "var(--dsw-alias-label-secondary,#9aa4b2)", strokeWidth: "1.1" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("svg", { className: "dsh-wt_treeIcon", width: "13", height: "13", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 1.5h5.25a1 1 0 0 1 .71.29l3.25 3.25a1 1 0 0 1 .29.71V13.5a1 1 0 0 1-1 1h-8.5a1 1 0 0 1-1-1v-11a1 1 0 0 1 1-1Z", fill: "var(--dsw-alias-fill-l1,rgba(255,255,255,.06))", stroke: "var(--dsw-alias-label-secondary,#9aa4b2)", strokeWidth: "1.1" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M9.25 1.5V4.75h3.25", fill: "none", stroke: "var(--dsw-alias-label-secondary,#9aa4b2)", strokeWidth: "1.1" }) ] }); } function ExplorerPane(props) { - const cacheRef = (0, import_react.useRef)({}); - const expandedRef = (0, import_react.useRef)(/* @__PURE__ */ new Set()); - const [rootPath, setRootPath] = (0, import_react.useState)(""); - const [error2, setError] = (0, import_react.useState)(""); - const [, setTick] = (0, import_react.useState)(0); + const cacheRef = (0, import_react2.useRef)({}); + const expandedRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set()); + const [rootPath, setRootPath] = (0, import_react2.useState)(""); + const [error2, setError] = (0, import_react2.useState)(""); + const [, setTick] = (0, import_react2.useState)(0); const rerender = () => setTick((t) => t + 1); - const fetchDir = (0, import_react.useCallback)(async (path, force = false) => { + const fetchDir = (0, import_react2.useCallback)(async (path, force = false) => { if (!force && cacheRef.current[path]) return { path, entries: cacheRef.current[path] }; try { const d = await postJson("/api/worktable/fs", { @@ -19022,12 +19144,12 @@ function ExplorerPane(props) { rerender(); } }, []); - const initRoot = (0, import_react.useCallback)(async () => { + const initRoot = (0, import_react2.useCallback)(async () => { const r = await fetchDir(splitEnv?.getScope()?.cwd ?? ""); setRootPath(r.path); rerender(); }, [fetchDir]); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { initRoot(); }, [initRoot]); const toggle = (path) => { @@ -19063,8 +19185,8 @@ function ExplorerPane(props) { for (const e of entries) { const isOpen = expandedRef.current.has(e.path); nodes.push( - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "button", { type: "button", @@ -19089,9 +19211,9 @@ function ExplorerPane(props) { } }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_treeArrow" + (e.isDir && isOpen ? " dsh-wt_treeArrowOpen" : ""), "aria-hidden": true, children: e.isDir ? "\u25B8" : "" }), - e.isDir ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FolderIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileIcon, {}), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_treeName", children: e.name }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_treeArrow" + (e.isDir && isOpen ? " dsh-wt_treeArrowOpen" : ""), "aria-hidden": true, children: e.isDir ? "\u25B8" : "" }), + e.isDir ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FolderIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FileIcon, {}), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_treeName", children: e.name }) ] } ), @@ -19101,76 +19223,76 @@ function ExplorerPane(props) { } return nodes; }; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subBar", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_subBtn", title: "\u4E0A\u4E00\u7EA7", onClick: goUp, children: "\u2B06" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_subBtn", title: "\u5237\u65B0", onClick: refresh, children: "\u21BB" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_subPath", children: rootPath || "\u2026" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subBar", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_subBtn", title: "\u4E0A\u4E00\u7EA7", onClick: goUp, children: "\u2B06" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_subBtn", title: "\u5237\u65B0", onClick: refresh, children: "\u21BB" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_subPath", children: rootPath || "\u2026" }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subList", children: [ - error2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subEmpty", children: error2 }), - !error2 && cacheRef.current[rootPath]?.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subEmpty", children: "\u2014" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subList", children: [ + error2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subEmpty", children: error2 }), + !error2 && cacheRef.current[rootPath]?.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subEmpty", children: "\u2014" }), renderLevel(rootPath, 0) ] }) ] }); } function GitPane() { - const [snap, setSnap] = (0, import_react.useState)(null); - const [error2, setError] = (0, import_react.useState)(""); - const load = (0, import_react.useCallback)(() => { + const [snap, setSnap] = (0, import_react2.useState)(null); + const [error2, setError] = (0, import_react2.useState)(""); + const load = (0, import_react2.useCallback)(() => { postJson("/api/worktable/git", { sessionId: splitEnv?.getScope()?.sessionId ?? "", cwd: splitEnv?.getScope()?.cwd ?? "" }).then(setSnap).catch((e) => setError(String(e))); }, []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { load(); }, [load]); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subBar", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_subBtn", title: "\u5237\u65B0", onClick: load, children: "\u21BB" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_subPath", children: snap?.isRepo ? "\u2387 " + snap.branch : "" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subBar", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_subBtn", title: "\u5237\u65B0", onClick: load, children: "\u21BB" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_subPath", children: snap?.isRepo ? "\u2387 " + snap.branch : "" }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subList", children: [ - error2 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subEmpty", children: error2 }), - !error2 && snap && !snap.isRepo && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.gitNotRepo") }), - !error2 && snap?.isRepo && snap.entries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.gitClean") }), - !error2 && snap?.isRepo && snap.entries.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subRow dsh-wt_subRowStatic", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_gitXY dsh-wt_gitXY" + (e.xy.includes("A") || e.xy.includes("M") ? "Mod" : "New"), children: e.xy.trim() }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_subName", children: e.path }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subList", children: [ + error2 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subEmpty", children: error2 }), + !error2 && snap && !snap.isRepo && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.gitNotRepo") }), + !error2 && snap?.isRepo && snap.entries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.gitClean") }), + !error2 && snap?.isRepo && snap.entries.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subRow dsh-wt_subRowStatic", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_gitXY dsh-wt_gitXY" + (e.xy.includes("A") || e.xy.includes("M") ? "Mod" : "New"), children: e.xy.trim() }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_subName", children: e.path }) ] }, i)) ] }) ] }); } function JobsPane() { - const [, setTick] = (0, import_react.useState)(0); - (0, import_react.useEffect)(() => { + const [, setTick] = (0, import_react2.useState)(0); + (0, import_react2.useEffect)(() => { const timer = window.setInterval(() => setTick((t) => t + 1), 2e3); return () => window.clearInterval(timer); }, []); const jobs = splitEnv?.getJobs?.() ?? []; const subagents = splitEnv?.getSubagents?.() ?? []; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subList", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subSection", children: T("pane.jobsTitle") }), - jobs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.jobsEmpty") }), - jobs.map((j) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subRow dsh-wt_subRowStatic", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_jobDot dsh-wt_jobDot-" + j.status, "aria-hidden": true, children: "\u25CF" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_subName", children: j.label }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_subTag", children: j.kind }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subList", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subSection", children: T("pane.jobsTitle") }), + jobs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.jobsEmpty") }), + jobs.map((j) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subRow dsh-wt_subRowStatic", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_jobDot dsh-wt_jobDot-" + j.status, "aria-hidden": true, children: "\u25CF" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_subName", children: j.label }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_subTag", children: j.kind }) ] }, j.id)), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subSection", children: T("pane.subagents") }), - subagents.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.subagentsEmpty") }), - subagents.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_subRow dsh-wt_subRowStatic", style: { paddingLeft: 8 + (s?.depth ?? 0) * 12 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_jobDot dsh-wt_jobDot-" + (s?.status ?? "stopping"), "aria-hidden": true, children: "\u25CF" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_subName", children: s?.label ?? s?.title ?? s?.name ?? "\u2014" }), - s?.status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_subTag", children: s.status }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subSection", children: T("pane.subagents") }), + subagents.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_subEmpty", children: T("pane.subagentsEmpty") }), + subagents.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_subRow dsh-wt_subRowStatic", style: { paddingLeft: 8 + (s?.depth ?? 0) * 12 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_jobDot dsh-wt_jobDot-" + (s?.status ?? "stopping"), "aria-hidden": true, children: "\u25CF" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_subName", children: s?.label ?? s?.title ?? s?.name ?? "\u2014" }), + s?.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_subTag", children: s.status }) ] }, s?.id ?? i)) ] }); } function TerminalPane() { - const hostRef = (0, import_react.useRef)(null); - const [failed, setFailed] = (0, import_react.useState)(""); - (0, import_react.useEffect)(() => { + const hostRef = (0, import_react2.useRef)(null); + const [failed, setFailed] = (0, import_react2.useState)(""); + (0, import_react2.useEffect)(() => { const el = hostRef.current; if (!el) return; let term = null; @@ -19261,9 +19383,9 @@ function TerminalPane() { }; }, []); if (failed) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_paneWipText", children: failed }) }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_paneWipText", children: failed }) }); } - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: hostRef, className: "dsh-wt_termHost" }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { ref: hostRef, className: "dsh-wt_termHost" }); } var mdRenderer = new MarkdownItCallable({ linkify: true }); var IMAGE_EXTS = /[.](png|jpe?g|gif|webp|svg|bmp|ico)$/i; @@ -19272,12 +19394,12 @@ function FileViewer(props) { const ext = (props.path.split(".").pop() || "").toLowerCase(); const fileUrl = "/api/worktable/file?path=" + encodeURIComponent(props.path); if (ext === "pdf") { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("iframe", { className: "dsh-wt_paneFrame", src: fileUrl, title: basenameOf2(props.path) }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("iframe", { className: "dsh-wt_paneFrame", src: fileUrl, title: basenameOf2(props.path) }); } if (IMAGE_EXTS.test("." + ext)) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_imgView", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { src: fileUrl, alt: basenameOf2(props.path) }) }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_imgView", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { src: fileUrl, alt: basenameOf2(props.path) }) }); } - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TextViewer, { path: props.path, fileUrl, isMd: MD_EXTS.test("." + ext) }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TextViewer, { path: props.path, fileUrl, isMd: MD_EXTS.test("." + ext) }); } var CODE_LANGS = { ts: "typescript", tsx: "typescript", js: "javascript", jsx: "javascript", css: "css", json: "json" }; var CODE_EXTS = /[.](tsx|ts|jsx|js|css|json)$/i; @@ -19296,13 +19418,13 @@ function codeHtml(text2, ext) { return escapeHtml2(text2); } function TextViewer(props) { - const [text2, setText] = (0, import_react.useState)(null); - const [error2, setError] = (0, import_react.useState)(""); - const [mode, setMode] = (0, import_react.useState)("preview"); - const [draft, setDraft] = (0, import_react.useState)(""); - const [saving, setSaving] = (0, import_react.useState)(false); - const [saveFail, setSaveFail] = (0, import_react.useState)(false); - (0, import_react.useEffect)(() => { + const [text2, setText] = (0, import_react2.useState)(null); + const [error2, setError] = (0, import_react2.useState)(""); + const [mode, setMode] = (0, import_react2.useState)("preview"); + const [draft, setDraft] = (0, import_react2.useState)(""); + const [saving, setSaving] = (0, import_react2.useState)(false); + const [saveFail, setSaveFail] = (0, import_react2.useState)(false); + (0, import_react2.useEffect)(() => { let dead = false; setText(null); setError(""); @@ -19342,25 +19464,25 @@ function TextViewer(props) { } }; if (error2) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-wt_paneWipText", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_paneWipText", children: [ T("file.fail"), "\uFF1A", error2 ] }) }); } if (text2 == null) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_paneWipText", children: T("file.loading") }) }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_paneWipText", children: T("file.loading") }) }); } const ext = (props.path.split(".").pop() || "").toLowerCase(); const isCode = CODE_EXTS.test("." + ext); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_mdBar", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_mdBtn" + (mode === "preview" ? " dsh-wt_mdBtnOn" : ""), onClick: () => setMode("preview"), children: T("file.preview") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_mdBtn" + (mode === "edit" ? " dsh-wt_mdBtnOn" : ""), onClick: enterEdit, children: T("file.edit") }), - mode === "edit" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_mdSave", disabled: saving, onClick: save, children: saving ? "\u2026" : T("file.save") }), - saveFail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_mdMsg", children: T("file.saveFail") }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_mdBar", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_mdBtn" + (mode === "preview" ? " dsh-wt_mdBtnOn" : ""), onClick: () => setMode("preview"), children: T("file.preview") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_mdBtn" + (mode === "edit" ? " dsh-wt_mdBtnOn" : ""), onClick: enterEdit, children: T("file.edit") }), + mode === "edit" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_mdSave", disabled: saving, onClick: save, children: saving ? "\u2026" : T("file.save") }), + saveFail && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_mdMsg", children: T("file.saveFail") }) ] }), - mode === "edit" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { className: "dsh-wt_mdEdit", value: draft, spellCheck: false, onChange: (e) => setDraft(e.target.value) }) : props.isMd ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_fileView", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + mode === "edit" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("textarea", { className: "dsh-wt_mdEdit", value: draft, spellCheck: false, onChange: (e) => setDraft(e.target.value) }) : props.isMd ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_fileView", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: "dsh-wt_md", @@ -19373,27 +19495,27 @@ function TextViewer(props) { if (/^(https?:|mailto:)/i.test(href)) window.open(href, "_blank", "noopener"); } } - ) }) : isCode ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_fileView", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "dsh-wt_code", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("code", { dangerouslySetInnerHTML: { __html: codeHtml(text2, ext) } }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_fileView", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", { className: "dsh-wt_txt", children: text2 }) }) + ) }) : isCode ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_fileView", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { className: "dsh-wt_code", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("code", { dangerouslySetInnerHTML: { __html: codeHtml(text2, ext) } }) }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_fileView", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("pre", { className: "dsh-wt_txt", children: text2 }) }) ] }); } function SelectPop(props) { - const [open, setOpen] = (0, import_react.useState)(false); + const [open, setOpen] = (0, import_react2.useState)(false); const flat = props.groups.flatMap((g) => g.items); const selected = flat.find((i) => i.id === props.value); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_select", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_selectBtn", onClick: () => setOpen((v) => !v), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_selectVal" + (selected ? "" : " dsh-wt_selectPh"), children: selected?.label ?? props.placeholder ?? "" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_selectCaret", "aria-hidden": true, children: "\u25BE" }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_select", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_selectBtn", onClick: () => setOpen((v) => !v), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectVal" + (selected ? "" : " dsh-wt_selectPh"), children: selected?.label ?? props.placeholder ?? "" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectCaret", "aria-hidden": true, children: "\u25BE" }) ] }), - open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_selectList", children: props.groups.map((g, gi) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Fragment, { children: [ - g.title && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_selectDivider" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_selectGroup", children: [ + open && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_selectList", children: props.groups.map((g, gi) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react2.Fragment, { children: [ + g.title && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_selectDivider" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_selectGroup", children: [ "\u{1F4C1} ", g.title ] }) ] }), - g.items.map((it) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( + g.items.map((it) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "button", { type: "button", @@ -19403,8 +19525,8 @@ function SelectPop(props) { setOpen(false); }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_selectItemTitle", children: it.label }), - it.isCurrent && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_selectCurrent", children: T("custom.sessionCurrent") }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectItemTitle", children: it.label }), + it.isCurrent && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectCurrent", children: T("custom.sessionCurrent") }) ] }, it.id @@ -19419,23 +19541,23 @@ function CustomPane(props) { } catch { } const custom = splitEnv?.custom; - const [requirement, setRequirement] = (0, import_react.useState)(""); - const [projectId, setProjectId] = (0, import_react.useState)(null); - const [projects, setProjects] = (0, import_react.useState)(() => custom?.getProjects?.() ?? []); - const [mode, setMode] = (0, import_react.useState)("existing"); - const [sessionGroups, setSessionGroups] = (0, import_react.useState)([]); - const [sessionId, setSessionId] = (0, import_react.useState)(null); - const [wsGroups, setWsGroups] = (0, import_react.useState)([]); - const [groupMode, setGroupMode] = (0, import_react.useState)("none"); - const [groupId, setGroupId] = (0, import_react.useState)(null); - const groupTouchedRef = (0, import_react.useRef)(false); - const [newGroupParent, setNewGroupParent] = (0, import_react.useState)(""); - const [newGroupName, setNewGroupName] = (0, import_react.useState)(""); - const [busy, setBusy] = (0, import_react.useState)(false); - const [done, setDone] = (0, import_react.useState)(false); - const [fail, setFail] = (0, import_react.useState)(""); - const [bindNote, setBindNote] = (0, import_react.useState)("none"); - (0, import_react.useEffect)(() => { + const [requirement, setRequirement] = (0, import_react2.useState)(""); + const [projectId, setProjectId] = (0, import_react2.useState)(null); + const [projects, setProjects] = (0, import_react2.useState)(() => custom?.getProjects?.() ?? []); + const [mode, setMode] = (0, import_react2.useState)("existing"); + const [sessionGroups, setSessionGroups] = (0, import_react2.useState)([]); + const [sessionId, setSessionId] = (0, import_react2.useState)(null); + const [wsGroups, setWsGroups] = (0, import_react2.useState)([]); + const [groupMode, setGroupMode] = (0, import_react2.useState)("none"); + const [groupId, setGroupId] = (0, import_react2.useState)(null); + const groupTouchedRef = (0, import_react2.useRef)(false); + const [newGroupParent, setNewGroupParent] = (0, import_react2.useState)(""); + const [newGroupName, setNewGroupName] = (0, import_react2.useState)(""); + const [busy, setBusy] = (0, import_react2.useState)(false); + const [done, setDone] = (0, import_react2.useState)(false); + const [fail, setFail] = (0, import_react2.useState)(""); + const [bindNote, setBindNote] = (0, import_react2.useState)("none"); + (0, import_react2.useEffect)(() => { const list2 = custom?.getProjects?.() ?? []; setProjects(list2); const cur = custom?.currentProjectId?.() ?? null; @@ -19488,27 +19610,27 @@ function CustomPane(props) { } }; if (!custom) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_paneWipText", children: T("pane.wip") }) }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_paneWip", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_paneWipText", children: T("pane.wip") }) }); } if (done) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customBox", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_customDone", "aria-hidden": true, children: "\u2705" }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-wt_customDoneText", children: mode === "new" ? T("custom.done") : T("custom.sent") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-wt_customDoneHint", children: T("custom.doneHint") }), - bindNote !== "none" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-wt_customDoneBind", children: bindNote === "auto" ? T("custom.autoBound") : T("custom.keptBinding") }) + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customBox", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_customDone", "aria-hidden": true, children: "\u2705" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_customDoneText", children: mode === "new" ? T("custom.done") : T("custom.sent") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_customDoneHint", children: T("custom.doneHint") }), + bindNote !== "none" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_customDoneBind", children: bindNote === "auto" ? T("custom.autoBound") : T("custom.keptBinding") }) ] }); } - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_customBox", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customCard", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "dsh-wt_customTitle", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_customBox", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customCard", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_customTitle", children: [ "\u2728 ", T("custom.title") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customModes", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_customModeBtn" + (mode === "existing" ? " dsh-wt_customModeBtnOn" : ""), onClick: () => setMode("existing"), children: T("custom.modeSend") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_customModeBtn" + (mode === "new" ? " dsh-wt_customModeBtnOn" : ""), onClick: () => setMode("new"), children: T("custom.modeNew") }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customModes", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_customModeBtn" + (mode === "existing" ? " dsh-wt_customModeBtnOn" : ""), onClick: () => setMode("existing"), children: T("custom.modeSend") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_customModeBtn" + (mode === "new" ? " dsh-wt_customModeBtnOn" : ""), onClick: () => setMode("new"), children: T("custom.modeNew") }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-wt_customHint", children: mode === "new" ? T("custom.hint") : T("custom.hintSend") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_customHint", children: mode === "new" ? T("custom.hint") : T("custom.hintSend") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "textarea", { className: "dsh-wt_customInput", @@ -19518,9 +19640,9 @@ function CustomPane(props) { onChange: (e) => setRequirement(e.target.value) } ), - mode === "existing" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.session") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + mode === "existing" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.session") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( SelectPop, { value: sessionId, @@ -19533,9 +19655,9 @@ function CustomPane(props) { } ) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.project") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.project") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( SelectPop, { value: projectId, @@ -19545,9 +19667,9 @@ function CustomPane(props) { } ) ] }), - mode === "new" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.group") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + mode === "new" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.group") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( SelectPop, { value: groupMode === "none" ? "__none" : groupMode === "new" ? "__new" : groupId, @@ -19572,10 +19694,10 @@ function CustomPane(props) { } ) ] }), - mode === "new" && groupMode === "new" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.groupNewParent") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + mode === "new" && groupMode === "new" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.groupNewParent") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { className: "dsh-wt_customPathInput", @@ -19585,9 +19707,9 @@ function CustomPane(props) { } ) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_customRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.groupNewName") }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_customRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_customLabel", children: T("custom.groupNewName") }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "input", { className: "dsh-wt_customPathInput", @@ -19598,7 +19720,7 @@ function CustomPane(props) { ) ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", @@ -19608,7 +19730,7 @@ function CustomPane(props) { children: busy ? "\u2026" : mode === "new" ? T("custom.send") : T("custom.sendToSession") } ), - fail && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { className: "dsh-wt_customFail", children: [ + fail && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "dsh-wt_customFail", children: [ T("custom.fail"), "\uFF1A", fail @@ -19618,22 +19740,22 @@ function CustomPane(props) { function PaneTabBody(props) { const content = props.tab.content; if (content.kind === "iframe") { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(IframePane, { url: content.url, title: content.title ?? props.tab.title, reloadKey: props.reloadKey }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(IframePane, { url: content.url, title: content.title ?? props.tab.title, reloadKey: props.reloadKey }); } if (content.kind === "file") { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FileViewer, { path: content.path }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(FileViewer, { path: content.path }); } - if (content.type === "browser") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BrowserPane, { row: props.row, index: props.index, tabId: props.tab.id, content, reloadKey: props.reloadKey }); - if (content.type === "anim") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnimPane, { row: props.row, index: props.index, tabId: props.tab.id, content, reloadKey: props.reloadKey }); - if (content.type === "console") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConsolePane, {}); - if (content.type === "explorer") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExplorerPane, { row: props.row, index: props.index }); - if (content.type === "scm") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(GitPane, {}); - if (content.type === "tasks") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(JobsPane, {}); - if (content.type === "terminal") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TerminalPane, {}); - if (content.type === "custom") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CustomPane, { paneTitle: props.paneTitle ?? "", row: props.row, index: props.index }); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_paneWip", children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_paneWipIcon", "aria-hidden": true, children: BUILTIN_ICONS[content.type] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_paneWipText", children: T("pane.wip") }) + if (content.type === "browser") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BrowserPane, { row: props.row, index: props.index, tabId: props.tab.id, content, reloadKey: props.reloadKey }); + if (content.type === "anim") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnimPane, { row: props.row, index: props.index, tabId: props.tab.id, content, reloadKey: props.reloadKey }); + if (content.type === "console") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ConsolePane, {}); + if (content.type === "explorer") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ExplorerPane, { row: props.row, index: props.index }); + if (content.type === "scm") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(GitPane, {}); + if (content.type === "tasks") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(JobsPane, {}); + if (content.type === "terminal") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(TerminalPane, {}); + if (content.type === "custom") return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CustomPane, { paneTitle: props.paneTitle ?? "", row: props.row, index: props.index }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_paneWip", children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_paneWipIcon", "aria-hidden": true, children: BUILTIN_ICONS[content.type] }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_paneWipText", children: T("pane.wip") }) ] }); } function refreshableTab(t) { @@ -19644,19 +19766,19 @@ function PaneBody(props) { const { pane, row, index } = props; const tabs = pane.tabs ?? []; const active = Math.min(pane.active ?? 0, Math.max(0, tabs.length - 1)); - const [reloadKeys, setReloadKeys] = (0, import_react.useState)({}); + const [reloadKeys, setReloadKeys] = (0, import_react2.useState)({}); if (tabs.length === 0) { - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PanePicker, { row, index }); + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PanePicker, { row, index }); } const refreshTab = (t) => { setReloadKeys((m) => ({ ...m, [t.id]: (m[t.id] ?? 0) + 1 })); splitStore.setActiveTab(row, index, t.id); }; const singleConsole = tabs.length === 1 && tabs[0].content?.kind === "builtin" && tabs[0].content.type === "console"; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - !singleConsole && !pane.collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-wt_tabBar", children: tabs.map((t, i) => { + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + !singleConsole && !pane.collapsed && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_tabBar", children: tabs.map((t, i) => { const locked = t.content?.kind === "builtin" && t.content.type === "console"; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "span", { className: "dsh-wt_tab" + (i === active ? " dsh-wt_tabOn" : ""), @@ -19675,7 +19797,7 @@ function PaneBody(props) { }, onClick: () => splitStore.setActiveTab(row, index, t.id), children: [ - refreshableTab(t) && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + refreshableTab(t) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", @@ -19689,8 +19811,8 @@ function PaneBody(props) { children: "\u21BB" } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_tabTitle", children: t.title }), - !locked && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_tabTitle", children: t.title }), + !locked && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", @@ -19708,13 +19830,13 @@ function PaneBody(props) { t.id ); }) }), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PaneTabBody, { tab: tabs[active], row, index, paneTitle: pane.title, reloadKey: reloadKeys[tabs[active].id] ?? 0 }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PaneTabBody, { tab: tabs[active], row, index, paneTitle: pane.title, reloadKey: reloadKeys[tabs[active].id] ?? 0 }) ] }); } function PanePicker(props) { - const [mode, setMode] = (0, import_react.useState)("grid"); - const hostRef = (0, import_react.useRef)(null); - (0, import_react.useEffect)(() => { + const [mode, setMode] = (0, import_react2.useState)("grid"); + const hostRef = (0, import_react2.useRef)(null); + (0, import_react2.useEffect)(() => { const el = hostRef.current; if (!el) return; const update = () => { @@ -19729,25 +19851,25 @@ function PanePicker(props) { return () => ro.disconnect(); }, []); const pick = (content) => splitStore.openTab(props.row, props.index, content); - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { ref: hostRef, className: "dsh-wt_panePicker dsh-wt_panePicker-" + mode, children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "browser" }), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, children: "\u{1F310}" }), + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: hostRef, className: "dsh-wt_panePicker dsh-wt_panePicker-" + mode, children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "browser" }), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, children: "\u{1F310}" }), T("pane.browser") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "anim" }), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, children: "\u{1F3AC}" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "anim" }), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, children: "\u{1F3AC}" }), T("pane.anim") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "explorer" }), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, children: "\u{1F4C1}" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "explorer" }), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, children: "\u{1F4C1}" }), T("pane.explorer") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "terminal" }), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, children: "\u25B8_" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "terminal" }), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, children: "\u25B8_" }), T("pane.terminal") ] }), - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "custom" }), children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "aria-hidden": true, children: "\u2728" }), + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_panePick", onClick: () => pick({ kind: "builtin", type: "custom" }), children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, children: "\u2728" }), T("pane.custom") ] }) ] }); @@ -19780,7 +19902,7 @@ function WorkspaceLayer(props) { const topY = barTop + BAR_H; const renderPane = (it, row, index, x, y, h) => { const singleConsole = (it.pane.tabs ?? []).length === 1 && it.pane.tabs[0].content?.kind === "builtin" && it.pane.tabs[0].content.type === "console"; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( "div", { className: "dsh-wt_pane", @@ -19804,7 +19926,7 @@ function WorkspaceLayer(props) { } }, children: [ - !it.pane.collapsed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + !it.pane.collapsed && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: "dsh-wt_paneBar", @@ -19827,12 +19949,12 @@ function WorkspaceLayer(props) { onDragEnd: () => { dragPane = null; }, - children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_paneTitle", children: it.pane.title }) + children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_paneTitle", children: it.pane.title }) } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PaneBody, { pane: it.pane, row, index }), - !singleConsole && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(PaneBody, { pane: it.pane, row, index }), + !singleConsole && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", @@ -19840,10 +19962,10 @@ function WorkspaceLayer(props) { title: T("annot.label"), "aria-label": T("annot.label"), onClick: () => startAnnot(windowLabelOf(row, index)), - children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M2 5.6c0-1.2 1-2.2 2.2-2.2h7.6c1.2 0 2.2 1 2.2 2.2v3.6c0 1.2-1 2.2-2.2 2.2H7.5L5 13.6l.3-2.4H4.2c-1.2 0-2.2-1-2.2-2.2z", fill: "none", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }) }) + children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M2 5.6c0-1.2 1-2.2 2.2-2.2h7.6c1.2 0 2.2 1 2.2 2.2v3.6c0 1.2-1 2.2-2.2 2.2H7.5L5 13.6l.3-2.4H4.2c-1.2 0-2.2-1-2.2-2.2z", fill: "none", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }) }) } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", @@ -19851,7 +19973,7 @@ function WorkspaceLayer(props) { title: it.pane.collapsed ? T("pane.expand") : T("pane.collapse"), "aria-label": it.pane.collapsed ? T("pane.expand") : T("pane.collapse"), onClick: () => splitStore.toggleCollapsed(row, index), - children: it.pane.collapsed ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 6l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 10l4-4 4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) + children: it.pane.collapsed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 6l4 4 4-4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { viewBox: "0 0 16 16", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M4 10l4-4 4 4", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }) }) } ) ] }) @@ -19860,10 +19982,10 @@ function WorkspaceLayer(props) { it.pane.id ); }; - return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-wt_splitBar", style: { position: "fixed", left: g.left, top: barTop, width: hasLeft || chatFull ? contentW : hasTop ? colW : contentW, zIndex: 70 }, children: [ - spec.id !== "wt-console" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-wt_splitTitle", children: spec.title }), - !hasLeft && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_splitBar", style: { position: "fixed", left: g.left, top: barTop, width: hasLeft || chatFull ? contentW : hasTop ? colW : contentW, zIndex: 70 }, children: [ + spec.id !== "wt-console" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_splitTitle", children: spec.title }), + !hasLeft && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "button", { type: "button", @@ -19873,12 +19995,12 @@ function WorkspaceLayer(props) { children: "\u21C4" } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-wt_splitClose", "aria-label": "\u9000\u51FA\u5206\u680F\uFF08Esc\uFF09", onClick: () => splitStore.close(), children: "\u2715" }) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_splitClose", "aria-label": "\u9000\u51FA\u5206\u680F\uFF08Esc\uFF09", onClick: () => splitStore.close(), children: "\u2715" }) ] }), leftItem && renderPane(leftItem, "left", 0, g.left, barTop + BAR_H, g.bottom - barTop - BAR_H), hasTop && topItems.map((it, i) => renderPane(it, "top", i, topRowX, topY, topH)), mainItems.map((it, i) => renderPane(it, "main", i, contentX, bodyTop, mainH)), - hasTop && /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + hasTop && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: "dsh-wt_splitDivider dsh-wt_splitDividerH", @@ -19888,7 +20010,7 @@ function WorkspaceLayer(props) { onPointerDown: makeDividerHandler("top") } ), - hasTop && topItems.slice(0, -1).map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + hasTop && topItems.slice(0, -1).map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: "dsh-wt_splitDivider", @@ -19899,7 +20021,7 @@ function WorkspaceLayer(props) { }, "tv" + it.pane.id )), - mainItems.slice(0, -1).map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + mainItems.slice(0, -1).map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: "dsh-wt_splitDivider", @@ -19910,7 +20032,7 @@ function WorkspaceLayer(props) { }, "v" + it.pane.id )), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( "div", { className: "dsh-wt_splitDivider", @@ -19927,11 +20049,11 @@ function WorkspaceLayer(props) { onPointerDown: makeDividerHandler(hasLeft ? "left" : "chat") } ), - /* @__PURE__ */ (0, import_jsx_runtime.jsx)(AnnotationOverlay, {}) + /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(AnnotationOverlay, {}) ] }); } function SplitWorkspace() { - const [snap, setSnap] = (0, import_react.useState)({ + const [snap, setSnap] = (0, import_react2.useState)({ active: splitStore.active, spec: splitStore.spec, geom: splitStore.geom, @@ -19941,9 +20063,9 @@ function SplitWorkspace() { paneWs: [...splitStore.paneWs], topWs: [...splitStore.topWs] }); - const poolRef = (0, import_react.useRef)(/* @__PURE__ */ new Map()); - const [, setPoolTick] = (0, import_react.useState)(0); - (0, import_react.useEffect)(() => splitStore.subscribe(() => { + const poolRef = (0, import_react2.useRef)(/* @__PURE__ */ new Map()); + const [, setPoolTick] = (0, import_react2.useState)(0); + (0, import_react2.useEffect)(() => splitStore.subscribe(() => { const spec = splitStore.spec; if (splitStore.active && spec) { poolRef.current.set(spec.id, { @@ -19971,7 +20093,7 @@ function SplitWorkspace() { }); setPoolTick((t) => t + 1); }), []); - (0, import_react.useEffect)(() => { + (0, import_react2.useEffect)(() => { if (!snap.active) return; const onKey = (event) => { if (event.key === "Escape") splitStore.close(); @@ -19979,8 +20101,8 @@ function SplitWorkspace() { window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [snap.active]); - const [, setDropTick] = (0, import_react.useState)(0); - (0, import_react.useEffect)(() => { + const [, setDropTick] = (0, import_react2.useState)(0); + (0, import_react2.useEffect)(() => { const fn = () => setDropTick((t) => t + 1); dropTargetListeners.add(fn); return () => { @@ -19990,9 +20112,9 @@ function SplitWorkspace() { const activeId = snap.active && snap.spec ? snap.spec.id : null; const entries = Array.from(poolRef.current.entries()); if (entries.length === 0) return null; - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: entries.map(([id, item]) => { + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: entries.map(([id, item]) => { const isActive = id === activeId; - return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: isActive ? void 0 : { visibility: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: isActive ? void 0 : { visibility: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( WorkspaceLayer, { spec: item.spec, @@ -20540,19 +20662,19 @@ var DEFAULT_BG_SVG = ' d.id === presetId) ?? PRESET_DEFS[0]; @@ -20628,94 +20750,94 @@ function buildLayout(presetId, name) { }; } function presetThumb(defId) { - const cell = (chat, key) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbCell" + (chat ? " dsh-wt_thumbChat" : ""), children: chat ? "\u{1F4AC}" : "" }, key); + const cell = (chat, key) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbCell" + (chat ? " dsh-wt_thumbChat" : ""), children: chat ? "\u{1F4AC}" : "" }, key); if (defId === "2h") { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumb", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumb", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "a"), cell(true, "b") ] }) }); } if (defId === "3h") { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumb", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumb", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "a"), cell(false, "b"), cell(true, "c") ] }) }); } if (defId === "g4") { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "a"), cell(false, "b") ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "c"), cell(false, "d") ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "e") }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "e") }) ] }); } if (defId === "l23") { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "a"), cell(false, "b") ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "c"), cell(false, "d"), cell(false, "e") ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "f") }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "f") }) ] }); } if (defId === "l13") { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "a") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "a") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "b"), cell(false, "c"), cell(false, "d") ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "e") }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "e") }) ] }); } if (defId === "grid") { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumb", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumb", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "a"), cell(false, "b") ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "c"), cell(true, "d") ] }) ] }); } if (defId === "l2") { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "a") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "b") }) + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "a") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "b") }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "c") }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "c") }) ] }); } - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "a") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumb dsh-wt_thumbCols", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbCol", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbRow", children: cell(false, "a") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_thumbRow", children: [ cell(false, "b"), cell(false, "c") ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "d") }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_thumbCol", children: cell(true, "d") }) ] }); } var DEFAULT_VIEW = { @@ -21383,17 +21505,17 @@ function syncSessionScope(list2) { } } function EyeIcon(props) { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: props.closed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1.8 8s2.6-3.1 6.2-3.1S14.2 8 14.2 8s-2.6 3.1-6.2 3.1S1.8 8 1.8 8Z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M3.6 8h8.8", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }) - ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M1.8 8s2.6-3.1 6.2-3.1S14.2 8 14.2 8s-2.6 3.1-6.2 3.1S1.8 8 1.8 8Z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "1.7", fill: "currentColor" }) + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": true, children: props.closed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M1.8 8s2.6-3.1 6.2-3.1S14.2 8 14.2 8s-2.6 3.1-6.2 3.1S1.8 8 1.8 8Z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M3.6 8h8.8", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" }) + ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M1.8 8s2.6-3.1 6.2-3.1S14.2 8 14.2 8s-2.6 3.1-6.2 3.1S1.8 8 1.8 8Z", stroke: "currentColor", strokeWidth: "1.2", strokeLinejoin: "round" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "8", cy: "8", r: "1.7", fill: "currentColor" }) ] }) }); } function RenameInput(props) { - const [val, setVal] = (0, import_react2.useState)(props.initial); - (0, import_react2.useEffect)(() => { + const [val, setVal] = (0, import_react3.useState)(props.initial); + (0, import_react3.useEffect)(() => { setVal(props.initial); }, [props.initial]); const commit = () => { @@ -21401,7 +21523,7 @@ function RenameInput(props) { props.onCommit(v); if (!v.trim()) setVal(props.initial); }; - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "input", { className: "dsh-wt_manageInput", @@ -21432,23 +21554,23 @@ function WorktableSection(props) { if (params) for (const [k, v] of Object.entries(params)) s = s.replace("{" + k + "}", v); return s; }; - const [view, setView] = (0, import_react2.useState)(loadView); - const [notifyTick, setNotifyTick] = (0, import_react2.useState)(0); - const [projects, setProjects] = (0, import_react2.useState)(loadProjects); - const [metas, setMetas] = (0, import_react2.useState)({}); - const [registeredIds, setRegisteredIds] = (0, import_react2.useState)(() => [...registryStore.ids]); - const [addOpen, setAddOpen] = (0, import_react2.useState)(false); - const [viewOptionsOpen, setViewOptionsOpen] = (0, import_react2.useState)(false); - const [updateInfo, setUpdateInfo] = (0, import_react2.useState)(null); - const [updateCheckOn, setUpdateCheckOn] = (0, import_react2.useState)(() => localStorage.getItem("dsh.worktable.updateCheck.v1") !== "0"); - const [updateCopied, setUpdateCopied] = (0, import_react2.useState)(false); - const [updateStatus, setUpdateStatus] = (0, import_react2.useState)("idle"); - const updateCheckingRef = (0, import_react2.useRef)(false); - const updateAliveRef = (0, import_react2.useRef)(true); - (0, import_react2.useEffect)(() => () => { + const [view, setView] = (0, import_react3.useState)(loadView); + const [notifyTick, setNotifyTick] = (0, import_react3.useState)(0); + const [projects, setProjects] = (0, import_react3.useState)(loadProjects); + const [metas, setMetas] = (0, import_react3.useState)({}); + const [registeredIds, setRegisteredIds] = (0, import_react3.useState)(() => [...registryStore.ids]); + const [addOpen, setAddOpen] = (0, import_react3.useState)(false); + const [viewOptionsOpen, setViewOptionsOpen] = (0, import_react3.useState)(false); + const [updateInfo, setUpdateInfo] = (0, import_react3.useState)(null); + const [updateCheckOn, setUpdateCheckOn] = (0, import_react3.useState)(() => localStorage.getItem("dsh.worktable.updateCheck.v1") !== "0"); + const [updateCopied, setUpdateCopied] = (0, import_react3.useState)(false); + const [updateStatus, setUpdateStatus] = (0, import_react3.useState)("idle"); + const updateCheckingRef = (0, import_react3.useRef)(false); + const updateAliveRef = (0, import_react3.useRef)(true); + (0, import_react3.useEffect)(() => () => { updateAliveRef.current = false; }, []); - const checkUpdates = (0, import_react2.useCallback)(async (force = false) => { + const checkUpdates = (0, import_react3.useCallback)(async (force = false) => { if (updateCheckingRef.current) return; const last = Number(localStorage.getItem("dsh.worktable.lastUpdateCheck.v1") ?? "0"); if (!force && Date.now() - last < 24 * 3600 * 1e3) return; @@ -21489,7 +21611,7 @@ function WorktableSection(props) { updateCheckingRef.current = false; } }, []); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { if (updateCheckOn) void checkUpdates(); }, [updateCheckOn, checkUpdates]); const copyUpgradeAi = async () => { @@ -21511,60 +21633,60 @@ function WorktableSection(props) { return !v; }); }; - const [wsPreset, setWsPreset] = (0, import_react2.useState)("2h"); - const [wsName, setWsName] = (0, import_react2.useState)(""); - const [wsError, setWsError] = (0, import_react2.useState)(false); - const [wsFolderParent, setWsFolderParent] = (0, import_react2.useState)(""); - const [wsFolderError, setWsFolderError] = (0, import_react2.useState)(false); - const [pickBusy, setPickBusy] = (0, import_react2.useState)(false); - const [pickErr, setPickErr] = (0, import_react2.useState)({ add: "", bind: "" }); - const [manualPathFor, setManualPathFor] = (0, import_react2.useState)(null); - const [manualPathText, setManualPathText] = (0, import_react2.useState)(""); - const pickSeqRef = (0, import_react2.useRef)(0); - const pickBusyRef = (0, import_react2.useRef)(false); - const mountedRef = (0, import_react2.useRef)(true); - (0, import_react2.useEffect)(() => () => { + const [wsPreset, setWsPreset] = (0, import_react3.useState)("2h"); + const [wsName, setWsName] = (0, import_react3.useState)(""); + const [wsError, setWsError] = (0, import_react3.useState)(false); + const [wsFolderParent, setWsFolderParent] = (0, import_react3.useState)(""); + const [wsFolderError, setWsFolderError] = (0, import_react3.useState)(false); + const [pickBusy, setPickBusy] = (0, import_react3.useState)(false); + const [pickErr, setPickErr] = (0, import_react3.useState)({ add: "", bind: "" }); + const [manualPathFor, setManualPathFor] = (0, import_react3.useState)(null); + const [manualPathText, setManualPathText] = (0, import_react3.useState)(""); + const pickSeqRef = (0, import_react3.useRef)(0); + const pickBusyRef = (0, import_react3.useRef)(false); + const mountedRef = (0, import_react3.useRef)(true); + (0, import_react3.useEffect)(() => () => { mountedRef.current = false; }, []); - const [iconPick, setIconPick] = (0, import_react2.useState)(null); - const [bindPick, setBindPick] = (0, import_react2.useState)(null); - const [bindGroups, setBindGroups] = (0, import_react2.useState)([]); - const [bindListOpen, setBindListOpen] = (0, import_react2.useState)(false); - const [consoleBind, setConsoleBind] = (0, import_react2.useState)(null); - const [consoleGroups, setConsoleGroups] = (0, import_react2.useState)([]); - const [consoleMode, setConsoleMode] = (0, import_react2.useState)("none"); - const [consoleWsId, setConsoleWsId] = (0, import_react2.useState)(""); - const [consoleParent, setConsoleParent] = (0, import_react2.useState)(""); - const [consoleName, setConsoleName] = (0, import_react2.useState)(""); - const [consoleErr, setConsoleErr] = (0, import_react2.useState)(false); - const [consoleBusy, setConsoleBusy] = (0, import_react2.useState)(false); + const [iconPick, setIconPick] = (0, import_react3.useState)(null); + const [bindPick, setBindPick] = (0, import_react3.useState)(null); + const [bindGroups, setBindGroups] = (0, import_react3.useState)([]); + const [bindListOpen, setBindListOpen] = (0, import_react3.useState)(false); + const [consoleBind, setConsoleBind] = (0, import_react3.useState)(null); + const [consoleGroups, setConsoleGroups] = (0, import_react3.useState)([]); + const [consoleMode, setConsoleMode] = (0, import_react3.useState)("none"); + const [consoleWsId, setConsoleWsId] = (0, import_react3.useState)(""); + const [consoleParent, setConsoleParent] = (0, import_react3.useState)(""); + const [consoleName, setConsoleName] = (0, import_react3.useState)(""); + const [consoleErr, setConsoleErr] = (0, import_react3.useState)(false); + const [consoleBusy, setConsoleBusy] = (0, import_react3.useState)(false); const consoleNeedsWorkspace = blankSessionNeedsWorkspace(sessionBridge?.sessions, consoleMode === "none" ? "none" : "auto"); - const [customOpen, setCustomOpen] = (0, import_react2.useState)(false); - const [customLayoutText, setCustomLayoutText] = (0, import_react2.useState)(""); - const [copiedToast, setCopiedToast] = (0, import_react2.useState)(null); - const copyToastTimerRef = (0, import_react2.useRef)(null); - const [requestDelete, setRequestDelete] = (0, import_react2.useState)(null); - const [viewPickFor, setViewPickFor] = (0, import_react2.useState)(null); - const [float, setFloat] = (0, import_react2.useState)( + const [customOpen, setCustomOpen] = (0, import_react3.useState)(false); + const [customLayoutText, setCustomLayoutText] = (0, import_react3.useState)(""); + const [copiedToast, setCopiedToast] = (0, import_react3.useState)(null); + const copyToastTimerRef = (0, import_react3.useRef)(null); + const [requestDelete, setRequestDelete] = (0, import_react3.useState)(null); + const [viewPickFor, setViewPickFor] = (0, import_react3.useState)(null); + const [float, setFloat] = (0, import_react3.useState)( () => view.dock === "float" && view.floatTop != null ? { top: view.floatTop } : null ); - const rootRef = (0, import_react2.useRef)(null); - const dragRef = (0, import_react2.useRef)(null); - const dragIdRef = (0, import_react2.useRef)(null); - const [railRect, setRailRect] = (0, import_react2.useState)(null); - const [bottomInset, setBottomInset] = (0, import_react2.useState)(0); - const bottomInsetRef = (0, import_react2.useRef)(0); - const [floatGeo, setFloatGeo] = (0, import_react2.useState)(null); - const [sidebarRight, setSidebarRight] = (0, import_react2.useState)(null); - const [activeSplitId, setActiveSplitId] = (0, import_react2.useState)( + const rootRef = (0, import_react3.useRef)(null); + const dragRef = (0, import_react3.useRef)(null); + const dragIdRef = (0, import_react3.useRef)(null); + const [railRect, setRailRect] = (0, import_react3.useState)(null); + const [bottomInset, setBottomInset] = (0, import_react3.useState)(0); + const bottomInsetRef = (0, import_react3.useRef)(0); + const [floatGeo, setFloatGeo] = (0, import_react3.useState)(null); + const [sidebarRight, setSidebarRight] = (0, import_react3.useState)(null); + const [activeSplitId, setActiveSplitId] = (0, import_react3.useState)( () => splitStore.active && splitStore.spec ? splitStore.spec.id : null ); - const projectsRef = (0, import_react2.useRef)( + const projectsRef = (0, import_react3.useRef)( { projects, metas, aliveRegisteredIds: registeredIds } ); - const actionsRef = (0, import_react2.useRef)(null); - const ackRef = (0, import_react2.useRef)(null); - const viewRef = (0, import_react2.useRef)(view); + const actionsRef = (0, import_react3.useRef)(null); + const ackRef = (0, import_react3.useRef)(null); + const viewRef = (0, import_react3.useRef)(view); viewRef.current = view; const getConsoleCards = () => { const pr = projectsRef.current; @@ -21633,7 +21755,7 @@ function WorktableSection(props) { if (cwd) setWsFolderParent(cwd); } }; - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const env = { fillHostInput: (text2) => appendHostInput(sessionBridge, text2), getScope: () => { @@ -21808,7 +21930,7 @@ function WorktableSection(props) { } }; }, []); - const floatRef = (0, import_react2.useRef)(null); + const floatRef = (0, import_react3.useRef)(null); const persistView = (patch) => { setView((prev) => { const next = { ...prev, ...patch }; @@ -21829,21 +21951,21 @@ function WorktableSection(props) { return next; }); }; - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const saved = projects.views[CONSOLE_ID]; if (!specHasConsoleTab(saved)) { const spec = buildConsoleSpec((k) => t(k)); persistProjects((prev) => ({ ...prev, views: { ...prev.views, [CONSOLE_ID]: spec } })); } }, [projects.views[CONSOLE_ID]]); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const sync = () => setRegisteredIds([...registryStore.ids]); registryStore.listeners.add(sync); return () => { registryStore.listeners.delete(sync); }; }, []); - (0, import_react2.useEffect)(() => splitStore.subscribe(() => { + (0, import_react3.useEffect)(() => splitStore.subscribe(() => { setActiveSplitId(splitStore.active && splitStore.spec ? splitStore.spec.id : null); if (!splitStore.active) { if (!suppressRestoreRef.current) { @@ -21860,7 +21982,7 @@ function WorktableSection(props) { } suppressRestoreRef.current = false; }), []); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { splitStore.onSpecMutated = (spec) => { persistProjects((prev) => { if (prev.layouts.some((l) => l.id === spec.id)) { @@ -21881,11 +22003,11 @@ function WorktableSection(props) { splitStore.onPaneContentEdited = null; }; }, []); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { setSplitT((k, p) => t(k, p)); return () => setSplitT(null); }, [t]); - const measureRailRect = (0, import_react2.useCallback)(() => { + const measureRailRect = (0, import_react3.useCallback)(() => { const el = rootRef.current; if (!el) return; const r = el.getBoundingClientRect(); @@ -21893,7 +22015,7 @@ function WorktableSection(props) { setRailRect((prev) => prev && Math.abs(prev.left - r.left) < 1 && Math.abs(prev.width - r.width) < 1 ? prev : { left: r.left, width: r.width }); } }, []); - (0, import_react2.useLayoutEffect)(() => { + (0, import_react3.useLayoutEffect)(() => { if (!wide) { const t1 = window.setTimeout(measureRailRect, 320); const t2 = window.setTimeout(measureRailRect, 750); @@ -21903,7 +22025,7 @@ function WorktableSection(props) { }; } }, [wide, measureRailRect]); - const measureFloatGeo = (0, import_react2.useCallback)(() => { + const measureFloatGeo = (0, import_react3.useCallback)(() => { const sidebar = findSidebar(rootRef.current); if (!sidebar) { setFloatGeo({ left: 14, width: null }); @@ -21922,7 +22044,7 @@ function WorktableSection(props) { setFloatGeo({ left: 14, width: null }); } }, []); - (0, import_react2.useLayoutEffect)(() => { + (0, import_react3.useLayoutEffect)(() => { measureFloatGeo(); const sidebar = findSidebar(rootRef.current); if (!sidebar) return; @@ -21930,7 +22052,7 @@ function WorktableSection(props) { ro.observe(sidebar); return () => ro.disconnect(); }, [measureFloatGeo]); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const onResize = () => { setFloat(null); persistView({ dock: "footer" }); @@ -21938,7 +22060,7 @@ function WorktableSection(props) { window.addEventListener("resize", onResize); return () => window.removeEventListener("resize", onResize); }, []); - const reportMeta = (0, import_react2.useCallback)((meta) => { + const reportMeta = (0, import_react3.useCallback)((meta) => { if (!meta || typeof meta.id !== "string" || !meta.id) return; setMetas((prev) => { const cur = prev[meta.id]; @@ -21946,9 +22068,9 @@ function WorktableSection(props) { return { ...prev, [meta.id]: { id: meta.id, name: typeof meta.name === "string" ? meta.name : meta.id, icon: meta.icon } }; }); }, []); - const engineIdsRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set()); - const lastLegacyBumpRef = (0, import_react2.useRef)({}); - const reportUsed = (0, import_react2.useCallback)((id) => { + const engineIdsRef = (0, import_react3.useRef)(/* @__PURE__ */ new Set()); + const lastLegacyBumpRef = (0, import_react3.useRef)({}); + const reportUsed = (0, import_react3.useCallback)((id) => { if (typeof id !== "string" || !id) return; const now = Date.now(); const bump = () => { @@ -22011,7 +22133,7 @@ function WorktableSection(props) { req ].join("\n"); } - const openSplit = (0, import_react2.useCallback)((spec) => { + const openSplit = (0, import_react3.useCallback)((spec) => { engineIdsRef.current.add(spec.id); splitStore.open(projects.views[spec.id] ?? spec); if (splitStore.active && splitStore.spec?.id === spec.id) { @@ -22036,7 +22158,7 @@ function WorktableSection(props) { } } }, [projects.views]); - const openConsole = (0, import_react2.useCallback)((explicitBound) => { + const openConsole = (0, import_react3.useCallback)((explicitBound) => { const saved = projects.views[CONSOLE_ID]; const broken = !specHasConsoleTab(saved); const spec = broken ? buildConsoleSpec((k) => t(k)) : saved; @@ -22144,7 +22266,7 @@ function WorktableSection(props) { } }; actionsRef.current = { openSplit, openConsole }; - const openBindPick = (0, import_react2.useCallback)((id, anchor) => { + const openBindPick = (0, import_react3.useCallback)((id, anchor) => { invalidatePickState(); const r = anchor.getBoundingClientRect(); const x = clamp2(Math.round(r.right + 8), 8, window.innerWidth - 300); @@ -22292,7 +22414,7 @@ function WorktableSection(props) { if (copyToastTimerRef.current != null) window.clearTimeout(copyToastTimerRef.current); copyToastTimerRef.current = window.setTimeout(() => setCopiedToast(null), 6e3); }; - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const l = () => { setNotifyTick((t2) => t2 + 1); notifyConsole(); @@ -22303,12 +22425,12 @@ function WorktableSection(props) { sessionsSnapshotStore.listeners.delete(l); }; }, []); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { notifyConsole(); }, [projects]); - const [widgetTick, setWidgetTick] = (0, import_react2.useState)(0); - (0, import_react2.useEffect)(() => widgetRegistry.subscribe(() => setWidgetTick((n) => n + 1)), []); - const applyWidgetManifest = (0, import_react2.useCallback)(async (binding, rawManifest, force = false) => { + const [widgetTick, setWidgetTick] = (0, import_react3.useState)(0); + (0, import_react3.useEffect)(() => widgetRegistry.subscribe(() => setWidgetTick((n) => n + 1)), []); + const applyWidgetManifest = (0, import_react3.useCallback)(async (binding, rawManifest, force = false) => { let current = widgetProjectOf(binding.projectId); if (!mountedRef.current || !widgetRegistry.isCurrent(binding, current.spec, current.folder)) return; const readSeq = (widgetReadSeqRef.current[binding.bindingId] ?? 0) + 1; @@ -22339,7 +22461,7 @@ function WorktableSection(props) { } catch { } }, []); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const byId = sessionsSnapshotStore.snapshot?.byId ?? {}; const scopePairs = new Map(widgetRegistry.entries().map((b) => [JSON.stringify([b.projectId, b.sessionId]), b])); for (const key of mountConsumedRef.current) if (!scopePairs.has(key)) mountConsumedRef.current.delete(key); @@ -22367,7 +22489,7 @@ function WorktableSection(props) { } } }, [notifyTick, projects.bindings, widgetTick, applyWidgetManifest]); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { let cancelled = false; (async () => { for (const binding of widgetRegistry.entries()) { @@ -22379,10 +22501,10 @@ function WorktableSection(props) { cancelled = true; }; }, [projects.folders, widgetTick, applyWidgetManifest]); - const collectKids = (0, import_react2.useCallback)((sid) => { + const collectKids = (0, import_react3.useCallback)((sid) => { return childSessionIdsOf(sessionsSnapshotStore.snapshot, sid); }, []); - const bindNotifyMap = (0, import_react2.useMemo)(() => { + const bindNotifyMap = (0, import_react3.useMemo)(() => { const map2 = {}; const byId = sessionsSnapshotStore.snapshot?.byId ?? {}; const ack = loadNotifyAck(); @@ -22433,8 +22555,8 @@ function WorktableSection(props) { } }; ackRef.current = ackProjectNotify; - const layoutIds = (0, import_react2.useMemo)(() => projects.layouts.map((l) => l.id), [projects.layouts]); - const aliveRegisteredIds = (0, import_react2.useMemo)( + const layoutIds = (0, import_react3.useMemo)(() => projects.layouts.map((l) => l.id), [projects.layouts]); + const aliveRegisteredIds = (0, import_react3.useMemo)( () => registeredIds.filter((id) => !projects.removed.includes(id)), [registeredIds, projects.removed] ); @@ -22444,8 +22566,8 @@ function WorktableSection(props) { spec: (splitStore.active && splitStore.spec?.id === id ? splitStore.spec : void 0) ?? projectsRef.current.projects.views[id] ?? projectsRef.current.projects.layouts.find((l) => l.id === id) }); projectBindingsRef.current = projects.bindings; - const allIds = (0, import_react2.useMemo)(() => [...aliveRegisteredIds, ...layoutIds], [aliveRegisteredIds, layoutIds]); - const effectiveOrder = (0, import_react2.useMemo)(() => { + const allIds = (0, import_react3.useMemo)(() => [...aliveRegisteredIds, ...layoutIds], [aliveRegisteredIds, layoutIds]); + const effectiveOrder = (0, import_react3.useMemo)(() => { const known = new Set(allIds); const stored = projects.order.filter((id) => known.has(id)); const rest = allIds.filter((id) => !stored.includes(id)); @@ -22692,7 +22814,7 @@ function WorktableSection(props) { const setProjectIcon = (id, icon) => { persistProjects((prev) => ({ ...prev, iconOverrides: { ...prev.iconOverrides, [id]: icon } })); }; - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const sync = () => { const box = document.querySelector(".dsh-wt_projects"); if (!box) return; @@ -22746,7 +22868,7 @@ function WorktableSection(props) { mo.observe(document.body, { childList: true, subtree: true }); return () => mo.disconnect(); }, [aliveRegisteredIds, projects.iconOverrides, projects.views, activeSplitId, projects.bindings, bindNotifyMap]); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const onDocClick = (e) => { const target = e.target; const card = target && target.closest ? target.closest(".dsh-wt_projects [data-wt-id]") : null; @@ -22806,13 +22928,13 @@ function WorktableSection(props) { ...floatGeo?.width != null ? { width: floatGeo.width } : {}, zIndex: 70 } : void 0; - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { bottomInsetRef.current = bottomInset; }, [bottomInset]); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { floatRef.current = float; }, [float]); - const measureBottomOverlay = (0, import_react2.useCallback)(() => { + const measureBottomOverlay = (0, import_react3.useCallback)(() => { if (isFloat) return; const root = rootRef.current; if (!root) return; @@ -22841,10 +22963,10 @@ function WorktableSection(props) { } setBottomInset((prev) => Math.abs(prev - needed) < 2 ? prev : needed); }, [isFloat]); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { if (!isFloat) measureBottomOverlay(); }, [isFloat, wide, measureBottomOverlay]); - (0, import_react2.useEffect)(() => { + (0, import_react3.useEffect)(() => { const timer = window.setInterval(() => { if (!floatRef.current) measureBottomOverlay(); }, 2e3); @@ -22855,9 +22977,9 @@ function WorktableSection(props) { const popLeft = sidebarRight != null ? Math.min(sidebarRight + 8, Math.max(16, window.innerWidth - 344)) : 16; const popTop = clamp2(sectionTop, MIN_TOP, Math.max(MIN_TOP, window.innerHeight - 540)); const POP_BOTTOM_MARGIN = 12; - const settingsRef = (0, import_react2.useRef)(null); - const [settingsTop, setSettingsTop] = (0, import_react2.useState)(null); - (0, import_react2.useLayoutEffect)(() => { + const settingsRef = (0, import_react3.useRef)(null); + const [settingsTop, setSettingsTop] = (0, import_react3.useState)(null); + (0, import_react3.useLayoutEffect)(() => { const recalc = () => { if (!viewOptionsOpen || !settingsRef.current) return; const h = settingsRef.current.offsetHeight; @@ -22884,6 +23006,547 @@ function WorktableSection(props) { } } }; + const popupLayer = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + addOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_popBackdrop", onClick: () => { + invalidatePickState(); + setAddOpen(false); + } }), + addOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_menu dsh-wt_add dsh-wt_pop", style: { position: "fixed", left: popLeft, top: popTop, width: 360, zIndex: 80 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_menuLabel", children: t("add.chooseLayout") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_presets", children: [ + PRESET_DEFS.map((def) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_preset", + "data-on": wsPreset === def.id ? "true" : "false", + onClick: () => { + setWsPreset(def.id); + setWsError(false); + }, + children: presetThumb(def.id) + }, + def.id + )), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( + "button", + { + type: "button", + className: "dsh-wt_preset dsh-wt_presetAdd", + title: t("customLayout.addTitle"), + onClick: () => { + invalidatePickState(); + setAddOpen(false); + setCustomOpen(true); + }, + children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_presetAddIcon", "aria-hidden": true, children: "\uFF0B" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_presetAddText", children: t("customLayout.add") }) + ] + } + ) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_addForm", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "input", + { + type: "text", + placeholder: t("add.layoutNamePh"), + value: wsName, + onChange: (e) => { + setWsName(e.target.value); + setWsError(false); + } + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_addFolderRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_customLabel", children: t("add.folderParent") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_addFolderPath" + (wsFolderParent ? "" : " dsh-wt_addFolderPathNone"), title: wsFolderParent || "", children: wsFolderParent || t("add.folderNone") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_bindFolderChange", + disabled: pickBusy, + onClick: () => { + pickFolder("add", (p) => { + setWsFolderParent(p); + setWsFolderError(false); + }); + setWsError(false); + }, + children: pickBusy ? t("add.folderPicking") : t("add.folderPick") + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_bindFolderChange", + title: t("add.folderManual"), + onClick: () => { + setManualPathFor(manualPathFor === "add" ? null : "add"); + setPickErr((prev) => ({ ...prev, add: "" })); + }, + children: t("add.folderManual") + } + ) + ] }), + pickBusy && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_folderPickHint", children: t("add.folderPickerHint") }), + manualPathFor === "add" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_addFolderRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "input", + { + type: "text", + className: "dsh-wt_manualPathInput", + placeholder: t("add.folderManualPh"), + value: manualPathText, + onChange: (e) => setManualPathText(e.target.value), + onKeyDown: (e) => { + if (e.key === "Enter") applyManualPath("add"); + } + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_bindFolderChange", onClick: () => applyManualPath("add"), children: t("add.folderManualOk") }) + ] }), + pickErr.add && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_addError", children: pickErr.add }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_addBtn", onClick: saveLayout, children: t("add.layoutSave") }) + ] }), + wsError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_addError", children: t("add.layoutInvalid") }), + wsFolderError && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_addError", children: t("add.folderRequired") }) + ] }), + iconPick && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_popBackdrop", onClick: () => setIconPick(null) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_iconPop", style: { left: iconPick.x, top: iconPick.y }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_iconPopTitle", children: t("icons.title") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_iconGrid", children: EMOJI_SET.map((em) => { + const cur = iconPick.kind === "layout" ? projects.layouts.find((l) => l.id === iconPick.id)?.icon ?? "\u{1F9F1}" : iconPick.kind === "shortcut" ? projects.shortcuts.find((s) => s.id === iconPick.id)?.icon ?? "\u{1F517}" : projects.iconOverrides[iconPick.id] ?? metas[iconPick.id]?.icon ?? "\u{1F4E6}"; + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_iconCell", + "data-on": cur === em ? "true" : "false", + onClick: () => { + if (iconPick.kind === "layout") setLayoutIcon(iconPick.id, em); + else if (iconPick.kind === "shortcut") setShortcutIcon(iconPick.id, em); + else setProjectIcon(iconPick.id, em); + setIconPick(null); + }, + children: em + }, + em + ); + }) }) + ] }) + ] }), + viewOptionsOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_popBackdrop", onClick: () => setViewOptionsOpen(false) }), + viewOptionsOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: settingsRef, className: "dsh-wt_manage dsh-wt_pop dsh-wt_settings", style: { position: "fixed", left: popLeft, top: settingsTop ?? popTop, width: 280, zIndex: 80 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_settingsClose", + "aria-label": t("manage.done"), + title: t("manage.done"), + onClick: () => setViewOptionsOpen(false), + children: "\u2715" + } + ), + updateInfo && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_updateCard", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_updateHead", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_updateDot" }), + t("update.available"), + " \xB7 v", + updateInfo.latest + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_updateVers", children: [ + t("update.current"), + " v", + LOCAL_VERSION2, + " \u2192 v", + updateInfo.latest + ] }), + updateInfo.notes && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_updateNotes", children: updateInfo.notes }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_updateCmd", children: UPGRADE_CMD }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_updateBtns", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_updateBtn dsh-wt_updateBtnCopy", onClick: () => void copyUpgradeAi(), children: updateCopied ? "\u2713 " + t("update.copied") : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + ICON_SPARK, + " ", + t("update.copyAi") + ] }) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_updateBtn", onClick: skipUpdate, children: t("update.skip") }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_updateHint", children: t(DESKTOP_HOST ? "update.upgradeHintDesktop" : "update.upgradeHint") }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_manageHead", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_manageTitle", children: t("sort.label") }) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_sortRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_sortBtn", + "data-on": view.orderBy === "manual", + onClick: () => persistView({ orderBy: "manual" }), + children: t("sort.manual") + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_sortBtn", + "data-on": view.orderBy === "recent", + onClick: () => persistView({ orderBy: "recent" }), + children: t("sort.recent") + } + ) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_menuSep" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_manageHead", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_manageTitle", children: t("manage.title") }) }), + effectiveOrder.map((id) => { + const meta = metas[id]; + const layout = projects.layouts.find((l) => l.id === id); + const display = projects.nameOverrides[id] ?? layout?.title ?? meta?.name ?? id; + const isHidden = projects.hidden.includes(id); + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( + "div", + { + className: "dsh-wt_manageRow" + (isHidden ? " dsh-wt_manageRowOff" : ""), + draggable: true, + onDragStart: (e) => { + dragIdRef.current = id; + try { + e.dataTransfer.effectAllowed = "move"; + } catch { + } + }, + onDragOver: (e) => { + e.preventDefault(); + const dragId = dragIdRef.current; + if (dragId && dragId !== id) moveTo(dragId, id); + }, + onDrop: (e) => e.preventDefault(), + onDragEnd: () => { + dragIdRef.current = null; + }, + children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_manageGrip", "aria-hidden": true, children: "\u2261" }), + layout ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "span", + { + className: "dsh-wt_manageIcon dsh-wt_iconPick", + role: "button", + tabIndex: 0, + title: t("icons.change"), + onClick: (e) => { + e.stopPropagation(); + openIconPick("layout", id, e.currentTarget); + }, + children: layout.icon ?? "\u{1F9F1}" + } + ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "span", + { + className: "dsh-wt_manageIcon dsh-wt_iconPick", + role: "button", + tabIndex: 0, + title: t("icons.change"), + onClick: (e) => { + e.stopPropagation(); + openIconPick("project", id, e.currentTarget); + }, + children: projects.iconOverrides[id] ?? meta?.icon ?? "\u{1F4E6}" + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RenameInput, { initial: display, placeholder: t("manage.renamePh"), onCommit: (v) => renameProject(id, v) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_manageBtn", title: isHidden ? t("manage.show") : t("manage.hide"), onClick: () => toggleHidden(id), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(EyeIcon, { closed: isHidden }) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_manageBtn", title: t("manage.changeView"), onClick: () => setViewPickFor(id), children: "\u{1F9E9}" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_manageBtn", + title: layout ? t("manage.deleteLayout") : t("manage.deleteProject"), + onClick: () => askDelete(layout ? "layout" : "project", id, display), + children: "\u2715" + } + ) + ] + }, + id + ); + }), + projects.shortcuts.map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_manageRow dsh-wt_manageRowSc", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_manageGrip", "aria-hidden": true, children: "\u{1F517}" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "span", + { + className: "dsh-wt_manageIcon dsh-wt_iconPick", + role: "button", + tabIndex: 0, + title: t("icons.change"), + onClick: (e) => { + e.stopPropagation(); + openIconPick("shortcut", s.id, e.currentTarget); + }, + children: s.icon + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_manageScName", children: s.name }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_manageBtn", title: t("manage.deleteShortcut"), onClick: () => askDelete("shortcut", s.id, s.name), children: "\u2715" }) + ] }, s.id)), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_versionRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { children: [ + "v", + LOCAL_VERSION2, + updateStatus === "uptodate" && !updateInfo ? " \xB7 " + t("update.upToDate") : "", + updateStatus === "failed" && !updateInfo ? " \xB7 " + t("update.checkFail") : "" + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_versionActions", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_updateBtn", disabled: updateStatus === "checking", onClick: () => void checkUpdates(true), children: updateStatus === "checking" ? t("update.checking") : t("update.checkNow") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_updateToggle", onClick: toggleUpdateCheck, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("update.autoCheck") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_updateSwitch", "data-off": updateCheckOn ? void 0 : "true" }) + ] }) + ] }) + ] }) + ] }), + viewPickFor && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 81 }, onClick: () => setViewPickFor(null) }), + viewPickFor && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_menu dsh-wt_pop", style: { position: "fixed", left: popLeft, top: popTop, width: 320, zIndex: 82 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_menuLabel", children: t("viewPick.title") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_presets", children: [ + PRESET_DEFS.map((def) => { + const cur = projects.layouts.find((l) => l.id === viewPickFor) ?? projects.views[viewPickFor]; + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_preset", + "data-on": cur && presetOf(cur) === def.id ? "true" : "false", + onClick: () => applyLayoutChange(viewPickFor, def.id), + children: presetThumb(def.id) + }, + def.id + ); + }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( + "button", + { + type: "button", + className: "dsh-wt_preset dsh-wt_presetAdd", + title: t("customLayout.addTitle"), + onClick: () => { + setViewPickFor(null); + setCustomOpen(true); + }, + children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_presetAddIcon", "aria-hidden": true, children: "\uFF0B" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_presetAddText", children: t("customLayout.add") }) + ] + } + ) + ] }) + ] }), + bindPick && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 83 }, onClick: () => { + invalidatePickState(); + setBindPick(null); + setBindListOpen(false); + } }), + bindPick && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_menu dsh-wt_pop dsh-wt_bindPop", style: { position: "fixed", left: bindPick.x, top: bindPick.y, width: 280, zIndex: 84 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_bindFolderBox", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_bindFolderRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_bindFolderLabel", children: [ + "\u{1F4C1} ", + t("bind.folder") + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_bindFolderChange", disabled: pickBusy, onClick: changeBindFolder, children: pickBusy ? t("add.folderPicking") : t("bind.folderChange") + " \u21BB" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "button", + { + type: "button", + className: "dsh-wt_bindFolderChange", + title: t("add.folderManual"), + onClick: () => { + setManualPathFor(manualPathFor === "bind" ? null : "bind"); + setPickErr((prev) => ({ ...prev, bind: "" })); + }, + children: t("add.folderManual") + } + ) + ] }), + pickBusy && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_folderPickHint", children: t("add.folderPickerHint") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_bindFolderPath" + (projects.folders[bindPick.id] ? "" : " dsh-wt_bindFolderPathNone"), title: projects.folders[bindPick.id] ?? "", children: projects.folders[bindPick.id] ?? t("bind.folderNone") }), + manualPathFor === "bind" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_addFolderRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "input", + { + type: "text", + className: "dsh-wt_manualPathInput", + placeholder: t("add.folderManualPh"), + value: manualPathText, + onChange: (e) => setManualPathText(e.target.value), + onKeyDown: (e) => { + if (e.key === "Enter") applyManualPath("bind"); + } + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_bindFolderChange", onClick: () => applyManualPath("bind"), children: t("add.folderManualOk") }) + ] }), + pickErr.bind && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_addError", children: pickErr.bind }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_bindFolderBox", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_bindFolderRow", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_bindFolderLabel", children: [ + "\u{1F4AC} ", + t("bind.title") + ] }), + projects.bindings[bindPick.id] && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: "dsh-wt_bindUnbind", onClick: () => setProjectBinding(bindPick.id, null), children: [ + t("bind.unbind"), + " \u2715" + ] }) + ] }), + (() => { + const sid = projects.bindings[bindPick.id]; + if (!sid) { + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_bindConvRow dsh-wt_bindConvRowNone", title: t("bind.tipUnbound"), onClick: () => setBindListOpen((v) => !v), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_bindNoneText", children: [ + t("bind.unbound"), + " \xB7 ", + t("bind.clickPick") + ] }) }); + } + const info = bindInfoOf(bindGroups, sid); + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("button", { type: "button", className: "dsh-wt_bindConvRow", title: t("bind.tipBound", { name: info.title }), onClick: () => setBindListOpen((v) => !v), children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_bindFolder", title: info.folder, children: [ + "\u{1F4C2} ", + info.folder + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_bindSep", "aria-hidden": true, children: "|" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_bindConvName", title: info.title, children: info.title }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_bindConvChevron", "aria-hidden": true, children: "\u25BE" }) + ] }); + })(), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_bindHint", children: t("bind.hint") }) + ] }) + ] }), + bindPick && bindListOpen && (() => { + const rightFits = bindPick.x + 300 + 260 <= window.innerWidth - 8; + const listLeft = rightFits ? bindPick.x + 300 : Math.max(8, bindPick.x - 268); + return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_menu dsh-wt_pop dsh-wt_bindListPop", style: { position: "fixed", left: listLeft, top: clamp2(bindPick.y, 8, window.innerHeight - 348), width: 260, zIndex: 86 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_selectList dsh-wt_bindList", children: bindGroups.map((g, gi) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react3.Fragment, { children: [ + g.title && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_selectDivider" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_selectGroup", children: [ + "\u{1F4C1} ", + g.title + ] }) + ] }), + g.sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( + "button", + { + type: "button", + className: "dsh-wt_selectItem" + (projects.bindings[bindPick.id] === s.id ? " dsh-wt_selectItemOn" : ""), + onClick: () => { + setProjectBinding(bindPick.id, s.id); + setBindListOpen(false); + }, + children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_selectItemTitle", children: s.title }), + s.isCurrent && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_selectCurrent", children: t("custom.sessionCurrent") }) + ] + }, + s.id + )) + ] }, g.title || "g" + gi)) }) }); + })(), + consoleBind && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 85 }, onClick: () => setConsoleBind(null) }), + consoleBind && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_menu dsh-wt_pop dsh-wt_consoleBindPop", style: { position: "fixed", left: consoleBind.x, top: consoleBind.y, width: 560, zIndex: 86 }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_consoleBindCols", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_consoleBindCol", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_consoleBindLabel", children: [ + "\u2795 ", + t("console.joinExisting") + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_selectList dsh-wt_consoleBindList", children: [ + consoleGroups.map((g, gi) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react3.Fragment, { children: [ + g.title && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_selectDivider" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_selectGroup", children: [ + "\u{1F4C1} ", + g.title + ] }) + ] }), + g.sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( + "button", + { + type: "button", + className: "dsh-wt_selectItem" + (projects.bindings[CONSOLE_ID] === s.id ? " dsh-wt_selectItemOn" : ""), + onClick: () => bindConsoleExisting(s.id), + children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_selectItemTitle", children: s.title }), + s.isCurrent && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_selectCurrent", children: t("custom.sessionCurrent") }) + ] + }, + s.id + )) + ] }, g.title || "g" + gi)), + consoleGroups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_consoleBindEmpty", children: t("console.noSessions") }) + ] }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_consoleBindCol dsh-wt_consoleBindColNew", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_consoleBindLabel", children: [ + "\u2728 ", + t("console.newConv") + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { className: "dsh-wt_consoleSelect", value: consoleMode, onChange: (e) => setConsoleMode(e.target.value), children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "none", children: t("console.groupNone") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "existing", children: t("console.groupExisting") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "new", children: t("console.groupNew") }) + ] }), + consoleMode === "existing" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { className: "dsh-wt_consoleSelect", value: consoleWsId, onChange: (e) => setConsoleWsId(e.target.value), children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: t("console.chooseGroup") }), + listWorkspaces().map((w) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: w.id, children: w.title }, w.id)) + ] }), + consoleMode === "new" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "dsh-wt_consoleInput", placeholder: t("console.newParentPh"), value: consoleParent, onChange: (e) => setConsoleParent(e.target.value) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { className: "dsh-wt_consoleInput", placeholder: t("console.newNamePh"), value: consoleName, onChange: (e) => setConsoleName(e.target.value) }) + ] }), + consoleNeedsWorkspace && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_consoleHint", children: t("console.blankNeedsGroup") }), + consoleErr && !consoleNeedsWorkspace && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_consoleErr", children: t("console.bindFail") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_consoleCreateBtn", disabled: consoleBusy || consoleNeedsWorkspace || consoleMode === "existing" && !consoleWsId, onClick: bindConsoleNew, children: consoleBusy ? "\u2026" : t("console.createBind") }) + ] }) + ] }) }), + customOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 83 }, onClick: () => setCustomOpen(false) }), + customOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_menu dsh-wt_pop", style: { position: "fixed", left: popLeft, top: popTop, width: 340, zIndex: 84 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "dsh-wt_menuLabel", children: [ + "\u2728 ", + t("customLayout.title") + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( + "textarea", + { + className: "dsh-wt_customLayoutInput", + autoFocus: true, + rows: 6, + placeholder: t("customLayout.placeholder"), + value: customLayoutText, + onChange: (e) => setCustomLayoutText(e.target.value) + } + ), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_customLayoutBtn", onClick: copyCustomLayout, children: t("customLayout.copy") }), + copiedToast && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "dsh-wt_customLayoutToast" + (copiedToast === "fail" ? " dsh-wt_customLayoutToastFail" : ""), children: copiedToast === "ok" ? "\u2705 " + t("customLayout.copied") : "\u26A0\uFE0F " + t("customLayout.copyFail") }) + ] }), + requestDelete && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_confirmBackdrop", onClick: () => setRequestDelete(null) }), + requestDelete && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_confirm", role: "alertdialog", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_confirmTitle", children: [ + "\u26A0\uFE0F ", + t("confirm.title") + ] }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_confirmBody", children: requestDelete.kind === "layout" ? t("confirm.layoutBody", { name: requestDelete.name }) : requestDelete.kind === "shortcut" ? t("confirm.shortcutBody", { name: requestDelete.name }) : t("confirm.projectBody", { name: requestDelete.name }) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_confirmActions", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_confirmCancel", onClick: () => setRequestDelete(null), children: t("confirm.cancel") }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_confirmDelete", onClick: doDelete, children: t("confirm.delete") }) + ] }) + ] }) + ] }); if (!wide) { const railItems = [ { icon: CONSOLE_ICON, name: t("console.name"), onClick: (e) => clickConsoleCard(e.currentTarget) }, @@ -22909,15 +23572,18 @@ function WorktableSection(props) { })) ]; const railStyle = isFloat && railRect ? { position: "fixed", top: float.top, left: railRect.left, width: railRect.width, zIndex: 70 } : bottomInset > 0 ? { marginBottom: bottomInset } : void 0; - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: rootRef, className: "dsh-wt_section dsh-wt_rail", style: railStyle, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_divider" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_railBox", children: railItems.length > 0 ? railItems.map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_railBtn", title: it.name, "aria-label": it.name, onClick: it.onClick, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { "aria-hidden": true, children: it.icon }) }, i)) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_railIcon", children: "\u2261" }) }) + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: rootRef, className: "dsh-wt_section dsh-wt_rail", style: railStyle, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_divider" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_railBox", children: railItems.length > 0 ? railItems.map((it, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "dsh-wt_railBtn", title: it.name, "aria-label": it.name, onClick: it.onClick, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { "aria-hidden": true, children: it.icon }) }, i)) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_railIcon", children: "\u2261" }) }) + ] }), + popupLayer ] }); } - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: rootRef, className: "dsh-wt_section" + (isFloat ? " dsh-wt_float" : ""), style: isFloat ? floatStyle : dockedStyle, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_divider" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_header", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { ref: rootRef, className: "dsh-wt_section" + (isFloat ? " dsh-wt_float" : ""), style: isFloat ? floatStyle : dockedStyle, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_divider" }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_header", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { type: "button", @@ -22933,7 +23599,7 @@ function WorktableSection(props) { children: "\u2261" } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "span", { className: "dsh-wt_title", @@ -22946,7 +23612,7 @@ function WorktableSection(props) { children: t("title") } ), - updateInfo && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + updateInfo && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { type: "button", @@ -22957,8 +23623,8 @@ function WorktableSection(props) { children: ICON_SYNC } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_actions", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_actions", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { type: "button", @@ -22969,7 +23635,7 @@ function WorktableSection(props) { children: ICON_SEARCH } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { type: "button", @@ -22984,7 +23650,7 @@ function WorktableSection(props) { children: ICON_VIEW_OPTIONS } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { type: "button", @@ -23005,8 +23671,8 @@ function WorktableSection(props) { ) ] }) ] }), - view.searchOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_search", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + view.searchOpen && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_search", children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "input", { autoFocus: true, @@ -23017,7 +23683,7 @@ function WorktableSection(props) { onKeyDown: onSearchKeyDown } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "button", { type: "button", @@ -23028,547 +23694,9 @@ function WorktableSection(props) { } ) ] }), - addOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_popBackdrop", onClick: () => { - invalidatePickState(); - setAddOpen(false); - } }), - addOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_menu dsh-wt_add dsh-wt_pop", style: { position: "fixed", left: popLeft, top: popTop, width: 360, zIndex: 80 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_menuLabel", children: t("add.chooseLayout") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_presets", children: [ - PRESET_DEFS.map((def) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_preset", - "data-on": wsPreset === def.id ? "true" : "false", - onClick: () => { - setWsPreset(def.id); - setWsError(false); - }, - children: presetThumb(def.id) - }, - def.id - )), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( - "button", - { - type: "button", - className: "dsh-wt_preset dsh-wt_presetAdd", - title: t("customLayout.addTitle"), - onClick: () => { - invalidatePickState(); - setAddOpen(false); - setCustomOpen(true); - }, - children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_presetAddIcon", "aria-hidden": true, children: "\uFF0B" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_presetAddText", children: t("customLayout.add") }) - ] - } - ) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_addForm", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "input", - { - type: "text", - placeholder: t("add.layoutNamePh"), - value: wsName, - onChange: (e) => { - setWsName(e.target.value); - setWsError(false); - } - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_addFolderRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_customLabel", children: t("add.folderParent") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_addFolderPath" + (wsFolderParent ? "" : " dsh-wt_addFolderPathNone"), title: wsFolderParent || "", children: wsFolderParent || t("add.folderNone") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_bindFolderChange", - disabled: pickBusy, - onClick: () => { - pickFolder("add", (p) => { - setWsFolderParent(p); - setWsFolderError(false); - }); - setWsError(false); - }, - children: pickBusy ? t("add.folderPicking") : t("add.folderPick") - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_bindFolderChange", - title: t("add.folderManual"), - onClick: () => { - setManualPathFor(manualPathFor === "add" ? null : "add"); - setPickErr((prev) => ({ ...prev, add: "" })); - }, - children: t("add.folderManual") - } - ) - ] }), - pickBusy && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_folderPickHint", children: t("add.folderPickerHint") }), - manualPathFor === "add" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_addFolderRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "input", - { - type: "text", - className: "dsh-wt_manualPathInput", - placeholder: t("add.folderManualPh"), - value: manualPathText, - onChange: (e) => setManualPathText(e.target.value), - onKeyDown: (e) => { - if (e.key === "Enter") applyManualPath("add"); - } - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_bindFolderChange", onClick: () => applyManualPath("add"), children: t("add.folderManualOk") }) - ] }), - pickErr.add && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_addError", children: pickErr.add }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_addBtn", onClick: saveLayout, children: t("add.layoutSave") }) - ] }), - wsError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_addError", children: t("add.layoutInvalid") }), - wsFolderError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_addError", children: t("add.folderRequired") }) - ] }), - iconPick && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_popBackdrop", onClick: () => setIconPick(null) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_iconPop", style: { left: iconPick.x, top: iconPick.y }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_iconPopTitle", children: t("icons.title") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_iconGrid", children: EMOJI_SET.map((em) => { - const cur = iconPick.kind === "layout" ? projects.layouts.find((l) => l.id === iconPick.id)?.icon ?? "\u{1F9F1}" : iconPick.kind === "shortcut" ? projects.shortcuts.find((s) => s.id === iconPick.id)?.icon ?? "\u{1F517}" : projects.iconOverrides[iconPick.id] ?? metas[iconPick.id]?.icon ?? "\u{1F4E6}"; - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_iconCell", - "data-on": cur === em ? "true" : "false", - onClick: () => { - if (iconPick.kind === "layout") setLayoutIcon(iconPick.id, em); - else if (iconPick.kind === "shortcut") setShortcutIcon(iconPick.id, em); - else setProjectIcon(iconPick.id, em); - setIconPick(null); - }, - children: em - }, - em - ); - }) }) - ] }) - ] }), - viewOptionsOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_popBackdrop", onClick: () => setViewOptionsOpen(false) }), - viewOptionsOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { ref: settingsRef, className: "dsh-wt_manage dsh-wt_pop dsh-wt_settings", style: { position: "fixed", left: popLeft, top: settingsTop ?? popTop, width: 280, zIndex: 80 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_settingsClose", - "aria-label": t("manage.done"), - title: t("manage.done"), - onClick: () => setViewOptionsOpen(false), - children: "\u2715" - } - ), - updateInfo && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_updateCard", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_updateHead", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_updateDot" }), - t("update.available"), - " \xB7 v", - updateInfo.latest - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_updateVers", children: [ - t("update.current"), - " v", - LOCAL_VERSION2, - " \u2192 v", - updateInfo.latest - ] }), - updateInfo.notes && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_updateNotes", children: updateInfo.notes }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_updateCmd", children: UPGRADE_CMD }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_updateBtns", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_updateBtn dsh-wt_updateBtnCopy", onClick: () => void copyUpgradeAi(), children: updateCopied ? "\u2713 " + t("update.copied") : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ - ICON_SPARK, - " ", - t("update.copyAi") - ] }) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_updateBtn", onClick: skipUpdate, children: t("update.skip") }) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_updateHint", children: t(DESKTOP_HOST ? "update.upgradeHintDesktop" : "update.upgradeHint") }) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_manageHead", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_manageTitle", children: t("sort.label") }) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_sortRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_sortBtn", - "data-on": view.orderBy === "manual", - onClick: () => persistView({ orderBy: "manual" }), - children: t("sort.manual") - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_sortBtn", - "data-on": view.orderBy === "recent", - onClick: () => persistView({ orderBy: "recent" }), - children: t("sort.recent") - } - ) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_menuSep" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_manageHead", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_manageTitle", children: t("manage.title") }) }), - effectiveOrder.map((id) => { - const meta = metas[id]; - const layout = projects.layouts.find((l) => l.id === id); - const display = projects.nameOverrides[id] ?? layout?.title ?? meta?.name ?? id; - const isHidden = projects.hidden.includes(id); - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( - "div", - { - className: "dsh-wt_manageRow" + (isHidden ? " dsh-wt_manageRowOff" : ""), - draggable: true, - onDragStart: (e) => { - dragIdRef.current = id; - try { - e.dataTransfer.effectAllowed = "move"; - } catch { - } - }, - onDragOver: (e) => { - e.preventDefault(); - const dragId = dragIdRef.current; - if (dragId && dragId !== id) moveTo(dragId, id); - }, - onDrop: (e) => e.preventDefault(), - onDragEnd: () => { - dragIdRef.current = null; - }, - children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_manageGrip", "aria-hidden": true, children: "\u2261" }), - layout ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "span", - { - className: "dsh-wt_manageIcon dsh-wt_iconPick", - role: "button", - tabIndex: 0, - title: t("icons.change"), - onClick: (e) => { - e.stopPropagation(); - openIconPick("layout", id, e.currentTarget); - }, - children: layout.icon ?? "\u{1F9F1}" - } - ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "span", - { - className: "dsh-wt_manageIcon dsh-wt_iconPick", - role: "button", - tabIndex: 0, - title: t("icons.change"), - onClick: (e) => { - e.stopPropagation(); - openIconPick("project", id, e.currentTarget); - }, - children: projects.iconOverrides[id] ?? meta?.icon ?? "\u{1F4E6}" - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RenameInput, { initial: display, placeholder: t("manage.renamePh"), onCommit: (v) => renameProject(id, v) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_manageBtn", title: isHidden ? t("manage.show") : t("manage.hide"), onClick: () => toggleHidden(id), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(EyeIcon, { closed: isHidden }) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_manageBtn", title: t("manage.changeView"), onClick: () => setViewPickFor(id), children: "\u{1F9E9}" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_manageBtn", - title: layout ? t("manage.deleteLayout") : t("manage.deleteProject"), - onClick: () => askDelete(layout ? "layout" : "project", id, display), - children: "\u2715" - } - ) - ] - }, - id - ); - }), - projects.shortcuts.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_manageRow dsh-wt_manageRowSc", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_manageGrip", "aria-hidden": true, children: "\u{1F517}" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "span", - { - className: "dsh-wt_manageIcon dsh-wt_iconPick", - role: "button", - tabIndex: 0, - title: t("icons.change"), - onClick: (e) => { - e.stopPropagation(); - openIconPick("shortcut", s.id, e.currentTarget); - }, - children: s.icon - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_manageScName", children: s.name }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_manageBtn", title: t("manage.deleteShortcut"), onClick: () => askDelete("shortcut", s.id, s.name), children: "\u2715" }) - ] }, s.id)), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_versionRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [ - "v", - LOCAL_VERSION2, - updateStatus === "uptodate" && !updateInfo ? " \xB7 " + t("update.upToDate") : "", - updateStatus === "failed" && !updateInfo ? " \xB7 " + t("update.checkFail") : "" - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_versionActions", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_updateBtn", disabled: updateStatus === "checking", onClick: () => void checkUpdates(true), children: updateStatus === "checking" ? t("update.checking") : t("update.checkNow") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_updateToggle", onClick: toggleUpdateCheck, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: t("update.autoCheck") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_updateSwitch", "data-off": updateCheckOn ? void 0 : "true" }) - ] }) - ] }) - ] }) - ] }), - viewPickFor && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 81 }, onClick: () => setViewPickFor(null) }), - viewPickFor && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_menu dsh-wt_pop", style: { position: "fixed", left: popLeft, top: popTop, width: 320, zIndex: 82 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_menuLabel", children: t("viewPick.title") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_presets", children: [ - PRESET_DEFS.map((def) => { - const cur = projects.layouts.find((l) => l.id === viewPickFor) ?? projects.views[viewPickFor]; - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_preset", - "data-on": cur && presetOf(cur) === def.id ? "true" : "false", - onClick: () => applyLayoutChange(viewPickFor, def.id), - children: presetThumb(def.id) - }, - def.id - ); - }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( - "button", - { - type: "button", - className: "dsh-wt_preset dsh-wt_presetAdd", - title: t("customLayout.addTitle"), - onClick: () => { - setViewPickFor(null); - setCustomOpen(true); - }, - children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_presetAddIcon", "aria-hidden": true, children: "\uFF0B" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_presetAddText", children: t("customLayout.add") }) - ] - } - ) - ] }) - ] }), - bindPick && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 83 }, onClick: () => { - invalidatePickState(); - setBindPick(null); - setBindListOpen(false); - } }), - bindPick && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_menu dsh-wt_pop dsh-wt_bindPop", style: { position: "fixed", left: bindPick.x, top: bindPick.y, width: 280, zIndex: 84 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_bindFolderBox", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_bindFolderRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_bindFolderLabel", children: [ - "\u{1F4C1} ", - t("bind.folder") - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_bindFolderChange", disabled: pickBusy, onClick: changeBindFolder, children: pickBusy ? t("add.folderPicking") : t("bind.folderChange") + " \u21BB" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "button", - { - type: "button", - className: "dsh-wt_bindFolderChange", - title: t("add.folderManual"), - onClick: () => { - setManualPathFor(manualPathFor === "bind" ? null : "bind"); - setPickErr((prev) => ({ ...prev, bind: "" })); - }, - children: t("add.folderManual") - } - ) - ] }), - pickBusy && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_folderPickHint", children: t("add.folderPickerHint") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_bindFolderPath" + (projects.folders[bindPick.id] ? "" : " dsh-wt_bindFolderPathNone"), title: projects.folders[bindPick.id] ?? "", children: projects.folders[bindPick.id] ?? t("bind.folderNone") }), - manualPathFor === "bind" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_addFolderRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "input", - { - type: "text", - className: "dsh-wt_manualPathInput", - placeholder: t("add.folderManualPh"), - value: manualPathText, - onChange: (e) => setManualPathText(e.target.value), - onKeyDown: (e) => { - if (e.key === "Enter") applyManualPath("bind"); - } - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_bindFolderChange", onClick: () => applyManualPath("bind"), children: t("add.folderManualOk") }) - ] }), - pickErr.bind && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_addError", children: pickErr.bind }) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_bindFolderBox", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_bindFolderRow", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_bindFolderLabel", children: [ - "\u{1F4AC} ", - t("bind.title") - ] }), - projects.bindings[bindPick.id] && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_bindUnbind", onClick: () => setProjectBinding(bindPick.id, null), children: [ - t("bind.unbind"), - " \u2715" - ] }) - ] }), - (() => { - const sid = projects.bindings[bindPick.id]; - if (!sid) { - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_bindConvRow dsh-wt_bindConvRowNone", title: t("bind.tipUnbound"), onClick: () => setBindListOpen((v) => !v), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_bindNoneText", children: [ - t("bind.unbound"), - " \xB7 ", - t("bind.clickPick") - ] }) }); - } - const info = bindInfoOf(bindGroups, sid); - return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("button", { type: "button", className: "dsh-wt_bindConvRow", title: t("bind.tipBound", { name: info.title }), onClick: () => setBindListOpen((v) => !v), children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_bindFolder", title: info.folder, children: [ - "\u{1F4C2} ", - info.folder - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_bindSep", "aria-hidden": true, children: "|" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_bindConvName", title: info.title, children: info.title }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_bindConvChevron", "aria-hidden": true, children: "\u25BE" }) - ] }); - })(), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_bindHint", children: t("bind.hint") }) - ] }) - ] }), - bindPick && bindListOpen && (() => { - const rightFits = bindPick.x + 300 + 260 <= window.innerWidth - 8; - const listLeft = rightFits ? bindPick.x + 300 : Math.max(8, bindPick.x - 268); - return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_menu dsh-wt_pop dsh-wt_bindListPop", style: { position: "fixed", left: listLeft, top: clamp2(bindPick.y, 8, window.innerHeight - 348), width: 260, zIndex: 86 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_selectList dsh-wt_bindList", children: bindGroups.map((g, gi) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react2.Fragment, { children: [ - g.title && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_selectDivider" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_selectGroup", children: [ - "\u{1F4C1} ", - g.title - ] }) - ] }), - g.sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( - "button", - { - type: "button", - className: "dsh-wt_selectItem" + (projects.bindings[bindPick.id] === s.id ? " dsh-wt_selectItemOn" : ""), - onClick: () => { - setProjectBinding(bindPick.id, s.id); - setBindListOpen(false); - }, - children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectItemTitle", children: s.title }), - s.isCurrent && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectCurrent", children: t("custom.sessionCurrent") }) - ] - }, - s.id - )) - ] }, g.title || "g" + gi)) }) }); - })(), - consoleBind && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 85 }, onClick: () => setConsoleBind(null) }), - consoleBind && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_menu dsh-wt_pop dsh-wt_consoleBindPop", style: { position: "fixed", left: consoleBind.x, top: consoleBind.y, width: 560, zIndex: 86 }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_consoleBindCols", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_consoleBindCol", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_consoleBindLabel", children: [ - "\u2795 ", - t("console.joinExisting") - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_selectList dsh-wt_consoleBindList", children: [ - consoleGroups.map((g, gi) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react2.Fragment, { children: [ - g.title && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_selectDivider" }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_selectGroup", children: [ - "\u{1F4C1} ", - g.title - ] }) - ] }), - g.sessions.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( - "button", - { - type: "button", - className: "dsh-wt_selectItem" + (projects.bindings[CONSOLE_ID] === s.id ? " dsh-wt_selectItemOn" : ""), - onClick: () => bindConsoleExisting(s.id), - children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectItemTitle", children: s.title }), - s.isCurrent && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_selectCurrent", children: t("custom.sessionCurrent") }) - ] - }, - s.id - )) - ] }, g.title || "g" + gi)), - consoleGroups.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_consoleBindEmpty", children: t("console.noSessions") }) - ] }) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_consoleBindCol dsh-wt_consoleBindColNew", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_consoleBindLabel", children: [ - "\u2728 ", - t("console.newConv") - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { className: "dsh-wt_consoleSelect", value: consoleMode, onChange: (e) => setConsoleMode(e.target.value), children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "none", children: t("console.groupNone") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "existing", children: t("console.groupExisting") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "new", children: t("console.groupNew") }) - ] }), - consoleMode === "existing" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("select", { className: "dsh-wt_consoleSelect", value: consoleWsId, onChange: (e) => setConsoleWsId(e.target.value), children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "", children: t("console.chooseGroup") }), - listWorkspaces().map((w) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: w.id, children: w.title }, w.id)) - ] }), - consoleMode === "new" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_consoleInput", placeholder: t("console.newParentPh"), value: consoleParent, onChange: (e) => setConsoleParent(e.target.value) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("input", { className: "dsh-wt_consoleInput", placeholder: t("console.newNamePh"), value: consoleName, onChange: (e) => setConsoleName(e.target.value) }) - ] }), - consoleNeedsWorkspace && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_consoleHint", children: t("console.blankNeedsGroup") }), - consoleErr && !consoleNeedsWorkspace && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_consoleErr", children: t("console.bindFail") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_consoleCreateBtn", disabled: consoleBusy || consoleNeedsWorkspace || consoleMode === "existing" && !consoleWsId, onClick: bindConsoleNew, children: consoleBusy ? "\u2026" : t("console.createBind") }) - ] }) - ] }) }), - customOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_popBackdrop", style: { zIndex: 83 }, onClick: () => setCustomOpen(false) }), - customOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_menu dsh-wt_pop", style: { position: "fixed", left: popLeft, top: popTop, width: 340, zIndex: 84 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "dsh-wt_menuLabel", children: [ - "\u2728 ", - t("customLayout.title") - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( - "textarea", - { - className: "dsh-wt_customLayoutInput", - autoFocus: true, - rows: 6, - placeholder: t("customLayout.placeholder"), - value: customLayoutText, - onChange: (e) => setCustomLayoutText(e.target.value) - } - ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_customLayoutBtn", onClick: copyCustomLayout, children: t("customLayout.copy") }), - copiedToast && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "dsh-wt_customLayoutToast" + (copiedToast === "fail" ? " dsh-wt_customLayoutToastFail" : ""), children: copiedToast === "ok" ? "\u2705 " + t("customLayout.copied") : "\u26A0\uFE0F " + t("customLayout.copyFail") }) - ] }), - requestDelete && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_confirmBackdrop", onClick: () => setRequestDelete(null) }), - requestDelete && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_confirm", role: "alertdialog", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_confirmTitle", children: [ - "\u26A0\uFE0F ", - t("confirm.title") - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_confirmBody", children: requestDelete.kind === "layout" ? t("confirm.layoutBody", { name: requestDelete.name }) : requestDelete.kind === "shortcut" ? t("confirm.shortcutBody", { name: requestDelete.name }) : t("confirm.projectBody", { name: requestDelete.name }) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_confirmActions", children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_confirmCancel", onClick: () => setRequestDelete(null), children: t("confirm.cancel") }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { type: "button", className: "dsh-wt_confirmDelete", onClick: doDelete, children: t("confirm.delete") }) - ] }) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dsh-wt_projects", "data-managing": viewOptionsOpen ? "true" : void 0, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( + popupLayer, + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "dsh-wt_projects", "data-managing": viewOptionsOpen ? "true" : void 0, children: [ + /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( "button", { type: "button", @@ -23578,9 +23706,9 @@ function WorktableSection(props) { title: t("console.name"), onClick: (e) => clickConsoleCard(e.currentTarget), children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_layoutIcon", children: CONSOLE_ICON }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_layoutText", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_layoutName", children: t("console.name") }) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_layoutIcon", children: CONSOLE_ICON }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_layoutText", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_layoutName", children: t("console.name") }) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "span", { className: "dsh-wt_bindBtn", @@ -23593,15 +23721,15 @@ function WorktableSection(props) { e.stopPropagation(); openBindPick(CONSOLE_ID, e.currentTarget); }, - children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_bindCircles", "aria-hidden": true }) + children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_bindCircles", "aria-hidden": true }) } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_layoutArrow", "aria-hidden": true, children: "\u203A" }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_layoutArrow", "aria-hidden": true, children: "\u203A" }) ] } ), - renderProjectSlot ? renderProjectSlot("sidebar.worktable.project", ownerProps) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_empty", children: t("empty") }), - visibleLayouts.map((l) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( + renderProjectSlot ? renderProjectSlot("sidebar.worktable.project", ownerProps) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_empty", children: t("empty") }), + visibleLayouts.map((l) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( "button", { type: "button", @@ -23613,7 +23741,7 @@ function WorktableSection(props) { reportUsed(l.id); }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "span", { className: "dsh-wt_layoutIcon dsh-wt_iconPick", @@ -23627,8 +23755,8 @@ function WorktableSection(props) { children: l.icon ?? "\u{1F9F1}" } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_layoutText", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_layoutName", children: projects.nameOverrides[l.id] ?? l.title }) }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_layoutText", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_layoutName", children: projects.nameOverrides[l.id] ?? l.title }) }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "span", { className: "dsh-wt_bindBtn", @@ -23641,16 +23769,16 @@ function WorktableSection(props) { e.stopPropagation(); openBindPick(l.id, e.currentTarget); }, - children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_bindCircles", "aria-hidden": true }) + children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_bindCircles", "aria-hidden": true }) } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_layoutArrow", "aria-hidden": true, children: "\u203A" }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_layoutArrow", "aria-hidden": true, children: "\u203A" }) ] }, l.id )) ] }), - visibleShortcuts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dsh-wt_shortcuts", children: visibleShortcuts.map((s) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)( + visibleShortcuts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "dsh-wt_shortcuts", children: visibleShortcuts.map((s) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)( "a", { className: "dsh-wt_shortcut", @@ -23659,7 +23787,7 @@ function WorktableSection(props) { rel: "noreferrer noopener", title: s.href, children: [ - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)( "span", { className: "dsh-wt_shortcutIcon dsh-wt_iconPick", @@ -23674,8 +23802,8 @@ function WorktableSection(props) { children: s.icon } ), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_shortcutName", children: s.name }), - /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "dsh-wt_shortcutBadge", children: t("shortcut.badge") }) + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_shortcutName", children: s.name }), + /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "dsh-wt_shortcutBadge", children: t("shortcut.badge") }) ] }, s.id diff --git a/01_content/lib/index.js b/01_content/lib/index.js index 6d89de7..fcc4678 100644 --- a/01_content/lib/index.js +++ b/01_content/lib/index.js @@ -2,11 +2,121 @@ import { execFile } from "node:child_process"; import { readdirSync, realpathSync } from "node:fs"; import { readdir, readFile } from "node:fs/promises"; -import { dirname, resolve as pathResolve, sep } from "node:path"; +import { dirname, resolve as pathResolve, sep as sep2 } from "node:path"; import { createRequire } from "node:module"; import { homedir } from "node:os"; import { fileURLToPath, pathToFileURL } from "node:url"; +// src/requestAuth.ts +async function requestRejection(ctx, req) { + try { + const connection = typeof ctx.get === "function" ? ctx.get("connection") : void 0; + if (!connection) return 503; + if (typeof connection.admit === "function") { + const admission = await connection.admit(req); + if (!admission || typeof admission !== "object") return 503; + if ("rejection" in admission) return admission.rejection === 401 ? 401 : 403; + const peer = admission.peer; + if (peer?.principal?.kind === "owner") return void 0; + if (peer && peer === connection.operator && peer.principal === void 0) return void 0; + return 403; + } + } catch { + } + return 503; +} +function rejectionText(status) { + return status === 401 ? "unauthorized" : status === 403 ? "forbidden" : "Host authentication unavailable"; +} + +// src/previewFiles.ts +import { randomBytes } from "node:crypto"; +import { open, realpath, stat } from "node:fs/promises"; +import { isAbsolute, relative, resolve, sep } from "node:path"; +var PREVIEW_PREFIX = "/api/worktable/preview-static"; +var PREVIEW_SANDBOX = "allow-scripts allow-forms allow-downloads"; +var PREVIEW_CSP = `sandbox ${PREVIEW_SANDBOX}; base-uri 'none'; object-src 'none'; frame-src 'none'`; +var PREVIEW_MAX_BYTES = 40 * 1024 * 1024; +var IDLE_MS = 30 * 60 * 1e3; +var MAX_MOUNTS = 128; +function inside(root, file) { + const rel = relative(root, file); + return rel === "" || !isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + sep); +} +function createPreviewFiles(now = () => Date.now()) { + const mounts = /* @__PURE__ */ new Map(); + const prune = () => { + for (const [key, item] of mounts) if (now() - item.touched >= IDLE_MS) mounts.delete(key); + }; + return { + async mount(root) { + if (!isAbsolute(root)) throw new Error("Preview root must be absolute"); + const canonical = await realpath(root); + if (!(await stat(canonical)).isDirectory()) throw new Error("Preview root is not a directory"); + prune(); + if (mounts.size >= MAX_MOUNTS) throw new Error("Too many open previews; close an unused preview first"); + const token = randomBytes(24).toString("base64url"); + mounts.set(token, { root: canonical, touched: now() }); + return token; + }, + revoke(token) { + mounts.delete(token); + }, + clear() { + mounts.clear(); + }, + async file(token, segments, headOnly = false) { + prune(); + const mount = mounts.get(token); + if (!mount) return null; + if (segments.some((s) => s === ".." || s.includes("/") || s.includes("\\") || s.includes("\0"))) return null; + let target = resolve(mount.root, ...segments); + if (!inside(mount.root, target)) return null; + let info = await stat(target).catch(() => null); + if (info?.isDirectory()) target = resolve(target, "index.html"); + const canonical = await realpath(target).catch(() => null); + if (!canonical || !inside(mount.root, canonical)) return null; + const handle = await open(canonical, "r").catch(() => null); + if (!handle) return null; + try { + const before = await handle.stat({ bigint: true }); + if (!before.isFile()) return null; + const sameFile = (other) => other.isFile() && other.dev === before.dev && other.ino === before.ino && other.size === before.size && other.mtimeNs === before.mtimeNs && other.ctimeNs === before.ctimeNs; + const stillValid = async () => { + const current = await realpath(canonical); + if (current !== canonical || !inside(mount.root, current)) return false; + const named = await stat(current, { bigint: true }); + const opened = await handle.stat({ bigint: true }); + return sameFile(named) && sameFile(opened) && mounts.get(token) === mount && now() - mount.touched < IDLE_MS; + }; + const tooLarge = () => Object.assign(new Error("file too large"), { status: 413 }); + if (before.size > BigInt(PREVIEW_MAX_BYTES)) throw tooLarge(); + if (!await stillValid()) return null; + const chunks = []; + let total = 0; + if (!headOnly) { + while (total <= PREVIEW_MAX_BYTES) { + const chunk = Buffer.alloc(Math.min(64 * 1024, PREVIEW_MAX_BYTES + 1 - total)); + const { bytesRead } = await handle.read(chunk, 0, chunk.length, total); + if (!bytesRead) break; + total += bytesRead; + if (total > PREVIEW_MAX_BYTES) throw tooLarge(); + chunks.push(chunk.subarray(0, bytesRead)); + } + } + if (!await stillValid() || !headOnly && BigInt(total) !== before.size) return null; + mount.touched = now(); + return { path: canonical, size: Number(before.size), data: headOnly ? void 0 : Buffer.concat(chunks, total) }; + } catch (error) { + if (error?.status === 413) throw error; + return null; + } finally { + await handle.close(); + } + } + }; +} + // template/dshell.css var dshell_default = `/* dsh-worktable \u539F\u751F\u76AE\u80A4 \xB7 DSH \u8BBE\u8BA1\u7CFB\u7EDF\u7EC4\u4EF6\u5E93 \u7528\u6CD5\uFF1A @@ -115,7 +225,7 @@ function resolveDshHomeSafe() { cachedDshHome = baseDshHome(); return cachedDshHome; } -var PLUGIN_VERSION = false ? "dev" : "0.4.0"; +var PLUGIN_VERSION = false ? "dev" : "0.5.0"; var name = "dsh-worktable"; var inject = ["webServer", "sessions"]; var HEALTH_PATH = "/api/worktable/health"; @@ -212,7 +322,13 @@ function json(res, status, body) { } async function readJsonBody(req) { const chunks = []; - for await (const chunk of req) chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk); + let bytes = 0; + for await (const chunk of req) { + const data = typeof chunk === "string" ? Buffer.from(chunk) : chunk; + bytes += data.length; + if (bytes > 22 * 1024 * 1024) throw Object.assign(new Error("request too large"), { status: 413 }); + chunks.push(data); + } const text = Buffer.concat(chunks).toString("utf8"); if (!text) return {}; try { @@ -224,7 +340,7 @@ async function readJsonBody(req) { async function listDirectory(path) { const abs = pathResolve(path); const dirents = await readdir(abs, { withFileTypes: true }); - const entries = dirents.map((d) => ({ name: d.name, path: abs + sep + d.name, isDir: d.isDirectory(), hidden: d.name.startsWith(".") })).sort((a, b) => { + const entries = dirents.map((d) => ({ name: d.name, path: abs + sep2 + d.name, isDir: d.isDirectory(), hidden: d.name.startsWith(".") })).sort((a, b) => { if (a.isDir !== b.isDir) return a.isDir ? -1 : 1; return a.name.localeCompare(b.name, void 0, { sensitivity: "base" }); }); @@ -262,11 +378,43 @@ function setupTerminal(webServer, ctx) { if (!WebSocketServer) return; const pty = ptyMod.default ?? ptyMod; const wss = new WebSocketServer({ noServer: true }); + let disposed = false; + const terminals = /* @__PURE__ */ new Set(); + ctx.effect(() => () => { + disposed = true; + for (const ws of wss.clients ?? []) { + try { + ws.terminate(); + } catch { + } + } + for (const term of terminals) { + try { + term.kill(); + } catch { + } + } + terminals.clear(); + try { + wss.close(); + } catch { + } + }, "dsh-worktable: terminal sessions"); const spawnShell = () => process.platform === "win32" ? { cmd: "powershell.exe", args: ["-NoLogo", "-NoProfile"] } : { cmd: process.env.SHELL || "/bin/bash", args: [] }; const clampDim = (v, fallback) => Math.min(1024, Math.max(2, Number.isFinite(v) ? v : fallback)); ctx.effect(() => webServer.registerUpgrade({ path: "/api/worktable/term", - handler: (req, socket, head) => { + handler: async (req, socket, head) => { + let rejected = await requestRejection(ctx, req); + if (disposed) rejected = 503; + if (rejected !== void 0) { + socket.end(`HTTP/1.1 ${rejected} ${rejectionText(rejected)}\r +Connection: close\r +Content-Length: 0\r +\r +`); + return; + } wss.handleUpgrade(req, socket, head, (ws) => { const u = new URL(req.url ?? "/", "http://dsh.internal"); const cwd = serverCwd(ctx, u.searchParams.get("sessionId") || void 0, u.searchParams.get("cwd") || void 0); @@ -276,6 +424,7 @@ function setupTerminal(webServer, ctx) { try { const shell = spawnShell(); term = pty.spawn(shell.cmd, shell.args, { name: "xterm-256color", cols, rows, cwd, env: process.env }); + terminals.add(term); } catch (err) { try { ws.send("\r\n[worktable] \u7EC8\u7AEF\u542F\u52A8\u5931\u8D25\uFF1A" + String(err)); @@ -294,6 +443,7 @@ function setupTerminal(webServer, ctx) { } }); term.onExit(() => { + terminals.delete(term); try { ws.close(); } catch { @@ -315,6 +465,7 @@ function setupTerminal(webServer, ctx) { } }); ws.on("close", () => { + terminals.delete(term); try { term.kill(); } catch { @@ -330,14 +481,85 @@ function apply(ctx) { ctx.logger?.warn("[dsh-worktable] ctx.webServer \u4E0D\u53EF\u7528\uFF08headless profile\uFF1F\uFF09\uFF0C\u8DF3\u8FC7\u670D\u52A1\u7AEF\u8DEF\u7531"); return; } - webServer.register({ + const previews = createPreviewFiles(); + ctx.effect(() => () => previews.clear(), "dsh-worktable: preview capabilities"); + const register = (route, publicAsset = false) => ctx.effect(() => webServer.register({ + ...route, + handler: async (req, res) => { + if (!publicAsset && route.path !== HEALTH_PATH) { + const rejected = await requestRejection(ctx, req); + if (rejected !== void 0) { + json(res, rejected, { error: rejectionText(rejected) }); + return; + } + } + return route.handler(req, res); + } + }), "dsh-worktable: " + route.path); + register({ + kind: "exact", + path: "/api/worktable/preview", + handler: async (req, res) => { + if (req.method !== "POST") { + json(res, 405, { error: "method not allowed" }); + return; + } + try { + const body = await readJsonBody(req); + if (typeof body.revoke === "string") { + previews.revoke(body.revoke); + json(res, 200, { ok: true }); + return; + } + if (typeof body.root !== "string") { + json(res, 400, { error: "missing root" }); + return; + } + const token = await previews.mount(body.root); + json(res, 200, { token, base: PREVIEW_PREFIX + "/" + token + "/" }); + } catch (err) { + json(res, err.status ?? 400, { error: String(err) }); + } + } + }); + register({ + kind: "prefix", + path: PREVIEW_PREFIX, + handler: async (req, res) => { + if (req.method !== "GET" && req.method !== "HEAD") { + json(res, 405, { error: "method not allowed" }); + return; + } + try { + const parts = new URL(req.url ?? "/", "http://dsh.internal").pathname.slice(PREVIEW_PREFIX.length + 1).split("/").map(decodeURIComponent); + const file = await previews.file(parts.shift() ?? "", parts.filter(Boolean), req.method === "HEAD"); + if (!file) { + json(res, 404, { error: "preview unavailable; refresh to reopen" }); + return; + } + const ext = (file.path.split(".").pop() || "").toLowerCase(); + res.writeHead(200, { + "content-type": FILE_TYPES[ext] ?? "application/octet-stream", + "cache-control": "no-store", + "referrer-policy": "no-referrer", + "content-security-policy": PREVIEW_CSP, + "x-content-type-options": "nosniff", + "access-control-allow-origin": "*" + }); + res.end(file.data); + } catch (err) { + json(res, err?.status === 413 ? 413 : 404, { error: err?.status === 413 ? "file too large" : "preview unavailable" }); + } + } + }, true); + register({ kind: "exact", path: HEALTH_PATH, handler: (_req, res) => { json(res, 200, { plugin: "dsh-worktable", version: PLUGIN_VERSION, ok: true }); } }); - webServer.register({ + register({ kind: "exact", path: "/api/worktable/file", handler: async (req, res) => { @@ -349,8 +571,8 @@ function apply(ctx) { return; } const abs = pathResolve(p); - const stat = await import("node:fs/promises").then((m) => m.stat(abs)); - if (stat.size > 20 * 1024 * 1024) { + const stat2 = await import("node:fs/promises").then((m) => m.stat(abs)); + if (stat2.size > 20 * 1024 * 1024) { json(res, 413, { error: "file too large" }); return; } @@ -377,14 +599,19 @@ function apply(ctx) { bmp: "image/bmp", ico: "image/x-icon" }; - res.writeHead(200, { "content-type": FILE_TYPES[ext] ?? "application/octet-stream", "cache-control": "no-store" }); + res.writeHead(200, { + "content-type": FILE_TYPES[ext] ?? "application/octet-stream", + "cache-control": "no-store", + "x-content-type-options": "nosniff", + .../^(html?|svg)$/.test(ext) ? { "content-security-policy": PREVIEW_CSP, "referrer-policy": "no-referrer" } : {} + }); res.end(data); } catch (err) { json(res, 404, { error: String(err) }); } } }); - webServer.register({ + register({ kind: "prefix", path: TEMPLATE_PREFIX, handler: (req, res) => { @@ -409,7 +636,7 @@ function apply(ctx) { } } }); - webServer.register({ + register({ kind: "prefix", path: SITE_PREFIX, handler: async (req, res) => { @@ -435,7 +662,7 @@ function apply(ctx) { } const root = pathResolve(rootToken); let abs = pathResolve(root, rel); - if (abs !== root && !abs.startsWith(root + sep)) { + if (abs !== root && !abs.startsWith(root + sep2)) { json(res, 403, { error: "outside root" }); return; } @@ -455,14 +682,20 @@ function apply(ctx) { } const data = await readFile(abs); const ext = (abs.split(".").pop() || "").toLowerCase(); - res.writeHead(200, { "content-type": FILE_TYPES[ext] ?? "application/octet-stream", "cache-control": "no-store" }); + res.writeHead(200, { + "content-type": FILE_TYPES[ext] ?? "application/octet-stream", + "cache-control": "no-store", + "content-security-policy": PREVIEW_CSP, + "referrer-policy": "no-referrer", + "x-content-type-options": "nosniff" + }); res.end(data); } catch (err) { json(res, 404, { error: String(err) }); } } }); - webServer.register({ + register({ kind: "exact", path: "/api/worktable/fs", handler: async (req, res) => { @@ -471,11 +704,11 @@ function apply(ctx) { const path = typeof body.path === "string" && body.path ? body.path : serverCwd(ctx, body.sessionId, body.cwd); json(res, 200, await listDirectory(path)); } catch (err) { - json(res, 500, { path: "", entries: [], truncated: false, error: String(err) }); + json(res, err.status ?? 500, { path: "", entries: [], truncated: false, error: String(err) }); } } }); - webServer.register({ + register({ kind: "exact", path: "/api/worktable/workspaces", handler: async (_req, res) => { @@ -524,7 +757,7 @@ function apply(ctx) { } } }); - webServer.register({ + register({ kind: "exact", path: "/api/worktable/write", handler: async (req, res) => { @@ -549,11 +782,11 @@ function apply(ctx) { await import("node:fs/promises").then((m) => m.writeFile(abs, content, "utf8")); json(res, 200, { ok: true }); } catch (err) { - json(res, 500, { error: String(err) }); + json(res, err.status ?? 500, { error: String(err) }); } } }); - webServer.register({ + register({ kind: "exact", path: "/api/worktable/mkdir", handler: async (req, res) => { @@ -581,17 +814,21 @@ function apply(ctx) { await fsx.mkdir(abs); json(res, 200, { ok: true, path: abs }); } catch (err) { - json(res, err?.code === "EEXIST" ? 200 : 500, err?.code === "EEXIST" ? { ok: true, exists: true } : { error: String(err) }); + json(res, err?.code === "EEXIST" ? 200 : err.status ?? 500, err?.code === "EEXIST" ? { ok: true, exists: true } : { error: String(err) }); } } }); - webServer.register({ + register({ kind: "exact", path: "/api/worktable/git", handler: async (req, res) => { - const body = await readJsonBody(req); - const cwd = serverCwd(ctx, body.sessionId, body.cwd); - json(res, 200, await gitStatus(cwd)); + try { + const body = await readJsonBody(req); + const cwd = serverCwd(ctx, body.sessionId, body.cwd); + json(res, 200, await gitStatus(cwd)); + } catch (err) { + json(res, err.status ?? 500, { error: String(err) }); + } } }); setupTerminal(webServer, ctx); diff --git a/01_content/package-lock.json b/01_content/package-lock.json index cdef5e2..53b1f5e 100644 --- a/01_content/package-lock.json +++ b/01_content/package-lock.json @@ -1,15 +1,16 @@ { "name": "dsh-worktable", - "version": "0.4.0", + "version": "0.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dsh-worktable", - "version": "0.4.0", + "version": "0.5.0", "license": "MIT", "engines": { - "node": ">=22" + "node": ">=22", + "dsh": "0.2.0-rc.2 || 0.2.1-rc.3" }, "devDependencies": { "esbuild": "^0.25.0", diff --git a/01_content/package.json b/01_content/package.json index 8ca5f95..0645b8f 100644 --- a/01_content/package.json +++ b/01_content/package.json @@ -1,14 +1,15 @@ { "name": "dsh-worktable", "description": "工作台 Worktable:DeepSeek Harness 侧边栏的 agent 级项目容器(应用抽屉)", - "version": "0.4.0", + "version": "0.5.0", "repository": { "type": "git", "url": "https://github.com/Aisland-SJL/dsh-worktable.git", "directory": "01_content" }, "engines": { - "node": ">=22" + "node": ">=22", + "dsh": "0.2.0-rc.2 || 0.2.1-rc.3" }, "license": "MIT", "type": "module", @@ -39,11 +40,10 @@ ] }, "compatibility": { - "dsh": "0.1.1-rc.2 || 0.1.2-rc.1 || 0.2.0-rc.2", + "dsh": "0.2.0-rc.2 || 0.2.1-rc.3", "dshReleases": { - "0.1.1-rc.2": "compatible", - "0.1.2-rc.1": "compatible", - "0.2.0-rc.2": "compatible" + "0.2.0-rc.2": "compatible", + "0.2.1-rc.3": "compatible" } } }, @@ -52,11 +52,13 @@ "check": "node --check lib/index.js && node --check lib/client.js", "pack": "node release-prep.mjs", "test:gate": "node client-factory-gate.test.mjs", + "test:sidebar": "node ../04_test/sidebar-popup.test.mjs", "test:input": "node ../04_test/host-input.test.mjs", "test:session": "node ../04_test/session-compat.test.mjs", "test:details": "node ../04_test/session-details.test.mjs", "test:transport": "node ../04_test/host-transport.test.mjs", "test:widget": "node ../04_test/widget-mount.test.mjs", + "test:security": "node --test ../04_test/request-auth.test.mjs ../04_test/preview-files.test.mjs ../04_test/sandboxed-frame.test.mjs", "verify:remote": "node verify-remote.mjs" }, "peerDependencies": { diff --git a/01_content/release-prep.mjs b/01_content/release-prep.mjs index c234845..f482bda 100644 --- a/01_content/release-prep.mjs +++ b/01_content/release-prep.mjs @@ -83,7 +83,7 @@ if (!patchOk) process.exit(1) console.log('[release-prep] identity check passed (' + IDENTITY + ', cordis.patch.yml strict)') // ---------- 2. 会话/输入兼容回归 + 构建 + 语法检查 ---------- -const compatTests = ['host-input.test.mjs', 'session-compat.test.mjs', 'session-details.test.mjs', 'host-transport.test.mjs', 'widget-mount.test.mjs'] +const compatTests = ['host-input.test.mjs', 'session-compat.test.mjs', 'session-details.test.mjs', 'host-transport.test.mjs', 'widget-mount.test.mjs', 'request-auth.test.mjs', 'preview-files.test.mjs', 'sandboxed-frame.test.mjs'] .map((file) => join(HERE, '..', '04_test', file)) const compatRun = spawnSync(process.execPath, ['--test', ...compatTests], { cwd: HERE, stdio: 'inherit' }) if (compatRun.status !== 0) { console.error('[release-prep] FAIL: session/input compatibility tests'); process.exit(1) } @@ -191,7 +191,28 @@ if (anchorRun.status !== 0) { console.error(anchorRun.stderr || anchorRun.stdout) process.exit(1) } -console.log('[release-prep] split anchor dom tests passed (8 scenarios, on installed artifact)') +console.log('[release-prep] split anchor dom tests passed (14 scenarios, on installed artifact)') + +// Sidebar popup regression evaluates the same installed client, including rail/wide branches. +const sidebarTest = join(HERE, '..', '04_test', 'sidebar-popup.test.mjs') +if (!existsSync(sidebarTest)) { console.error('[release-prep] FAIL: sidebar-popup.test.mjs missing'); process.exit(1) } +const sidebarRun = spawnSync(process.execPath, [sidebarTest, join(inst, 'node_modules', 'dsh-worktable', 'lib', 'client.js')], { cwd: HERE, stdio: 'pipe', encoding: 'utf8' }) +if (sidebarRun.status !== 0) { + console.error('[release-prep] FAIL: sidebar-popup.test.mjs') + console.error(sidebarRun.stderr || sidebarRun.stdout) + process.exit(1) +} +console.log(sidebarRun.stdout.trim()) +console.log('[release-prep] sidebar popup tests passed (installed artifact)') + +// CSSOM serializes fractional margins; a string-only fake DOM cannot cover this. +const marginRun = spawnSync(process.execPath, [join(HERE, '..', '04_test', 'split-margin.test.mjs'), join(inst, 'node_modules', 'dsh-worktable', 'lib', 'client.js')], { cwd: HERE, stdio: 'pipe', encoding: 'utf8' }) +if (marginRun.status !== 0) { + console.error('[release-prep] FAIL: real Chromium split margin regression') + console.error(marginRun.stderr || marginRun.stdout) + process.exit(1) +} +console.log('[release-prep] real Chromium split margin tests passed (5 scenarios, on installed artifact)') // ---------- 5d. 服务端数据目录解析回归(loadPkg ↔ resolveDshHomeSafe 循环调用修复) ---------- // 评估「安装目录中的最终产物」lib/index.js;官方路径 / 包不可解析兜底 / ~ 与相对路径展开 diff --git a/01_content/src/client/SandboxedFrame.tsx b/01_content/src/client/SandboxedFrame.tsx new file mode 100644 index 0000000..f8a7e2d --- /dev/null +++ b/01_content/src/client/SandboxedFrame.tsx @@ -0,0 +1,57 @@ +import { useEffect, useState } from 'react' +import { hostPreviewUrl } from './hostTransport' + +const SANDBOX = 'allow-scripts allow-forms allow-downloads' +const SITE = '/api/worktable/site/' + +/** Accept navigations, not executable URL schemes. Keep saved URLs free of capabilities. */ +export function previewTarget(input: string, base: string) { + const url = new URL(input, base) + const own = new URL(base) + const sameHost = url.protocol === own.protocol && url.host === own.host + if (!['http:', 'https:'].includes(url.protocol) && !(sameHost && url.protocol === 'dsh-app:')) throw new Error('Unsupported preview URL') + if (!sameHost || !url.pathname.startsWith(SITE)) return { url: url.href } + const segments = url.pathname.slice(SITE.length).split('/') + const root = decodeURIComponent(segments.shift() ?? '') + if (!root) throw new Error('Missing preview directory') + const relative = segments.map(decodeURIComponent) + if (relative.some(s => s === '..' || /[\\/\0]/.test(s))) throw new Error('Invalid preview path') + return { root, relative: relative.map(encodeURIComponent).join('/'), suffix: url.search + url.hash } +} + +/** Untrusted project pages never share the host's document origin or storage. */ +export function SandboxedFrame(props: { url: string; title?: string; reloadKey: number }) { + const [src, setSrc] = useState('about:blank') + const [error, setError] = useState('') + useEffect(() => { + let alive = true + let token: string | undefined + const revoke = (value: string) => { void fetch('/api/worktable/preview', { + method: 'POST', headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ revoke: value }), keepalive: true, + }).catch(() => {}) } + setSrc('about:blank') + setError('') + void (async () => { + if (!props.url || props.url === 'about:blank') return + const target = previewTarget(props.url, location.href) + if ('url' in target) { if (alive) setSrc(target.url!); return } + const response = await fetch('/api/worktable/preview', { + method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ root: target.root }), + }) + if (!response.ok) throw new Error('Preview HTTP ' + response.status) + const grant = await response.json() + if (typeof grant.token !== 'string' || !/^[\w-]{32}$/.test(grant.token) || grant.base !== '/api/worktable/preview-static/' + grant.token + '/') throw new Error('Invalid preview grant') + token = grant.token + if (!alive) { revoke(token!); token = undefined; return } + setSrc(hostPreviewUrl(grant.base + target.relative + target.suffix)) + })().catch(err => { + if (token) { revoke(token); token = undefined } + if (alive) setError(String(err)) + }) + return () => { alive = false; if (token) revoke(token) } + }, [props.url, props.reloadKey]) + if (error) return
{error}
+ return