@linxin666/dsh-remote-web-ui 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -28
- package/README.i18n.yaml +2 -2
- package/README.md +51 -15
- package/README.zh.md +9 -4
- package/lib/client.js +65 -34
- package/lib/client.js.map +1 -1
- package/lib/index.js +667 -77
- package/lib/mobile.js +874 -64
- package/lib/mobile.js.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +35 -5
- package/lib/types/client/remote-channel.d.ts +4 -5
- package/lib/types/client/remote-channel.d.ts.map +1 -1
- package/lib/types/client/remote-channel.js +13 -28
- package/lib/types/http.d.ts +37 -8
- package/lib/types/http.d.ts.map +1 -1
- package/lib/types/http.js +76 -11
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +40 -5
- package/lib/types/mobile/api.d.ts +57 -0
- package/lib/types/mobile/api.d.ts.map +1 -1
- package/lib/types/mobile/api.js +29 -0
- package/lib/types/mobile/markdown.d.ts +7 -1
- package/lib/types/mobile/markdown.d.ts.map +1 -1
- package/lib/types/mobile/markdown.js +16 -7
- package/lib/types/mobile/markdown.test.js +8 -0
- package/lib/types/mobile/mobile-styles.d.ts +1 -1
- package/lib/types/mobile/mobile-styles.d.ts.map +1 -1
- package/lib/types/mobile/mobile-styles.js +325 -6
- package/lib/types/mobile/views/ChatView.d.ts +10 -0
- package/lib/types/mobile/views/ChatView.d.ts.map +1 -1
- package/lib/types/mobile/views/ChatView.js +205 -8
- package/lib/types/mobile/views/ChatView.stream.test.d.ts +2 -0
- package/lib/types/mobile/views/ChatView.stream.test.d.ts.map +1 -0
- package/lib/types/mobile/views/ChatView.stream.test.js +205 -0
- package/lib/types/mobile/views/WorkspaceView.d.ts.map +1 -1
- package/lib/types/mobile/views/WorkspaceView.js +46 -5
- package/lib/types/mobile/views/WorkspaceView.test.js +129 -2
- package/lib/types/mobile-api.d.ts +9 -3
- package/lib/types/mobile-api.d.ts.map +1 -1
- package/lib/types/mobile-api.js +61 -16
- package/lib/types/mobile-pending.d.ts +38 -0
- package/lib/types/mobile-pending.d.ts.map +1 -0
- package/lib/types/mobile-pending.js +59 -0
- package/lib/types/paired-model-catalog.d.ts +17 -0
- package/lib/types/paired-model-catalog.d.ts.map +1 -0
- package/lib/types/paired-model-catalog.js +252 -0
- package/lib/types/pairing.d.ts +13 -4
- package/lib/types/pairing.d.ts.map +1 -1
- package/lib/types/pairing.js +13 -5
- package/lib/types/remote-api.d.ts +13 -4
- package/lib/types/remote-api.d.ts.map +1 -1
- package/lib/types/remote-api.js +27 -17
- package/lib/types/remote-channel-boot.d.ts +26 -0
- package/lib/types/remote-channel-boot.d.ts.map +1 -0
- package/lib/types/remote-channel-boot.js +128 -0
- package/lib/types/remote-channel-rules.d.ts +40 -0
- package/lib/types/remote-channel-rules.d.ts.map +1 -0
- package/lib/types/remote-channel-rules.js +32 -0
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +12 -23
- package/lib/types/uuid-polyfill.d.ts +10 -0
- package/lib/types/uuid-polyfill.d.ts.map +1 -0
- package/lib/types/uuid-polyfill.js +27 -0
- package/package.json +1 -1
- package/src/client/index.ts +41 -5
- package/src/client/remote-channel.ts +18 -28
- package/src/http.ts +87 -12
- package/src/index.ts +42 -6
- package/src/mobile/api.ts +91 -0
- package/src/mobile/markdown.test.ts +9 -0
- package/src/mobile/markdown.ts +17 -7
- package/src/mobile/mobile-styles.ts +326 -6
- package/src/mobile/views/ChatView.stream.test.tsx +231 -0
- package/src/mobile/views/ChatView.tsx +328 -10
- package/src/mobile/views/WorkspaceView.test.tsx +163 -2
- package/src/mobile/views/WorkspaceView.tsx +152 -26
- package/src/mobile-api.ts +59 -17
- package/src/mobile-pending.ts +88 -0
- package/src/paired-model-catalog.ts +224 -0
- package/src/pairing.ts +13 -5
- package/src/remote-api.ts +34 -18
- package/src/remote-channel-boot.ts +131 -0
- package/src/remote-channel-rules.ts +62 -0
- package/src/routes.ts +13 -24
- package/src/uuid-polyfill.ts +28 -0
package/lib/mobile.js
CHANGED
|
@@ -7632,6 +7632,35 @@ async function selectModel(sessionId, selection) {
|
|
|
7632
7632
|
...selection.reasoningEffort !== void 0 ? { reasoningEffort: selection.reasoningEffort } : {}
|
|
7633
7633
|
});
|
|
7634
7634
|
}
|
|
7635
|
+
/** Fetch pending approvals and questions for one session (polling fallback data source). */
|
|
7636
|
+
async function fetchPending(sessionId) {
|
|
7637
|
+
return await callUnary("mobile.pending", { sessionId });
|
|
7638
|
+
}
|
|
7639
|
+
/** Submit an approval decision (allowed-once or rejected). */
|
|
7640
|
+
async function respondApproval(sessionId, approvalId, outcome) {
|
|
7641
|
+
await callUnary("mobile.respond", {
|
|
7642
|
+
sessionId,
|
|
7643
|
+
type: "approval",
|
|
7644
|
+
approvalId,
|
|
7645
|
+
outcome
|
|
7646
|
+
});
|
|
7647
|
+
}
|
|
7648
|
+
/** Submit answers to a question group. */
|
|
7649
|
+
async function respondQuestion(sessionId, answers) {
|
|
7650
|
+
await callUnary("mobile.respond", {
|
|
7651
|
+
sessionId,
|
|
7652
|
+
type: "question",
|
|
7653
|
+
answers
|
|
7654
|
+
});
|
|
7655
|
+
}
|
|
7656
|
+
/** Browse one directory level on the host (defaults to home directory). */
|
|
7657
|
+
async function listDirectory(path) {
|
|
7658
|
+
return await callUnary("host.listDirectory", path === void 0 ? {} : { path });
|
|
7659
|
+
}
|
|
7660
|
+
/** Create a workspace from an existing host directory (does not mkdir). */
|
|
7661
|
+
async function createWorkspace(path) {
|
|
7662
|
+
return await callUnary("workspace.create", { path });
|
|
7663
|
+
}
|
|
7635
7664
|
//#endregion
|
|
7636
7665
|
//#region ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/core/core.js
|
|
7637
7666
|
var _a$1;
|
|
@@ -13513,9 +13542,23 @@ function snapshotOf(state) {
|
|
|
13513
13542
|
* Pure and exported for tests.
|
|
13514
13543
|
* @module dsh-remote-web-ui/mobile/markdown
|
|
13515
13544
|
*/
|
|
13516
|
-
/**
|
|
13545
|
+
/** HTML special-character map for {@link escapeHtml}. */
|
|
13546
|
+
const HTML_ESCAPE_MAP = {
|
|
13547
|
+
"&": "&",
|
|
13548
|
+
"<": "<",
|
|
13549
|
+
">": ">",
|
|
13550
|
+
"\"": """,
|
|
13551
|
+
"'": "'"
|
|
13552
|
+
};
|
|
13553
|
+
/**
|
|
13554
|
+
* Escape HTML special characters. One regex pass replaces all five
|
|
13555
|
+
* characters through the map; the output is identical to five sequential
|
|
13556
|
+
* passes (each replacement string contains none of the escaped characters),
|
|
13557
|
+
* and the common no-special-character case scans the string only once
|
|
13558
|
+
* instead of five times.
|
|
13559
|
+
*/
|
|
13517
13560
|
function escapeHtml(text) {
|
|
13518
|
-
return text.replace(
|
|
13561
|
+
return text.replace(/[&<>"']/g, (char) => HTML_ESCAPE_MAP[char] ?? char);
|
|
13519
13562
|
}
|
|
13520
13563
|
/**
|
|
13521
13564
|
* Guard a raw link/image target against dangerous protocols. Returns the
|
|
@@ -13994,6 +14037,12 @@ function ChatView({ session, mux, onBack }) {
|
|
|
13994
14037
|
* the legacy Enter-to-send behavior until the preference loads).
|
|
13995
14038
|
*/
|
|
13996
14039
|
const [mobileEnterToSend, setMobileEnterToSend] = (0, import_react.useState)(true);
|
|
14040
|
+
/** Whether the assistant is currently generating (turn/start..turn/end). */
|
|
14041
|
+
const [running, setRunning] = (0, import_react.useState)(false);
|
|
14042
|
+
/** Pending tool approvals awaiting user decision (#1025). */
|
|
14043
|
+
const [pendingApprovals, setPendingApprovals] = (0, import_react.useState)([]);
|
|
14044
|
+
/** Pending questions awaiting user answer (#1025). */
|
|
14045
|
+
const [pendingQuestions, setPendingQuestions] = (0, import_react.useState)([]);
|
|
13997
14046
|
(0, import_react.useEffect)(() => {
|
|
13998
14047
|
let cancelled = false;
|
|
13999
14048
|
fetchMobilePreferences().then((preferences) => {
|
|
@@ -14065,6 +14114,10 @@ function ChatView({ session, mux, onBack }) {
|
|
|
14065
14114
|
if (frame.type === "session/event") {
|
|
14066
14115
|
if (frame.sessionId !== session.sessionId) return;
|
|
14067
14116
|
const event = frame.event;
|
|
14117
|
+
if (typeof event.type === "string") {
|
|
14118
|
+
if (event.type === "turn/start") setRunning(true);
|
|
14119
|
+
if (event.type === "turn/end") setRunning(false);
|
|
14120
|
+
}
|
|
14068
14121
|
if (tailLoadingRef.current) {
|
|
14069
14122
|
if (liveBufferRef.current.length >= 500) {
|
|
14070
14123
|
liveBufferRef.current.shift();
|
|
@@ -14082,9 +14135,55 @@ function ChatView({ session, mux, onBack }) {
|
|
|
14082
14135
|
});
|
|
14083
14136
|
return;
|
|
14084
14137
|
}
|
|
14085
|
-
if (frame.type === "session/projection" && frame.sessionId === session.sessionId && frame.key === "permissions")
|
|
14138
|
+
if (frame.type === "session/projection" && frame.sessionId === session.sessionId && frame.key === "permissions") {
|
|
14139
|
+
setPermissions(parsePermissionSelect(frame.value));
|
|
14140
|
+
return;
|
|
14141
|
+
}
|
|
14142
|
+
if (!("sessionId" in frame) || frame.sessionId !== session.sessionId) return;
|
|
14143
|
+
if (frame.type === "approval/requested") {
|
|
14144
|
+
setPendingApprovals((previous) => {
|
|
14145
|
+
if (previous.some((a) => a.approvalId === frame.approvalId)) return previous;
|
|
14146
|
+
return [...previous, {
|
|
14147
|
+
approvalId: frame.approvalId,
|
|
14148
|
+
toolName: frame.toolName,
|
|
14149
|
+
callId: frame.callId,
|
|
14150
|
+
reason: frame.reason
|
|
14151
|
+
}];
|
|
14152
|
+
});
|
|
14153
|
+
return;
|
|
14154
|
+
}
|
|
14155
|
+
if (frame.type === "approval/resolved") {
|
|
14156
|
+
setPendingApprovals((previous) => previous.filter((a) => a.approvalId !== frame.approvalId));
|
|
14157
|
+
return;
|
|
14158
|
+
}
|
|
14159
|
+
if (frame.type === "question/requested") {
|
|
14160
|
+
const items = frame.questions;
|
|
14161
|
+
setPendingQuestions(items);
|
|
14162
|
+
return;
|
|
14163
|
+
}
|
|
14164
|
+
if (frame.type === "question/resolved") {
|
|
14165
|
+
setPendingQuestions([]);
|
|
14166
|
+
return;
|
|
14167
|
+
}
|
|
14086
14168
|
});
|
|
14087
14169
|
}, [mux, session.sessionId]);
|
|
14170
|
+
(0, import_react.useEffect)(() => {
|
|
14171
|
+
if (!running) return;
|
|
14172
|
+
let cancelled = false;
|
|
14173
|
+
const tick = () => {
|
|
14174
|
+
fetchPending(session.sessionId).then((state) => {
|
|
14175
|
+
if (cancelled) return;
|
|
14176
|
+
setPendingApprovals(state.approvals);
|
|
14177
|
+
setPendingQuestions(state.questions);
|
|
14178
|
+
}, () => {});
|
|
14179
|
+
};
|
|
14180
|
+
tick();
|
|
14181
|
+
const timer = setInterval(tick, 1500);
|
|
14182
|
+
return () => {
|
|
14183
|
+
cancelled = true;
|
|
14184
|
+
clearInterval(timer);
|
|
14185
|
+
};
|
|
14186
|
+
}, [running, session.sessionId]);
|
|
14088
14187
|
const scrollToBottom = (0, import_react.useCallback)(() => {
|
|
14089
14188
|
const el = scrollRef.current;
|
|
14090
14189
|
if (el === void 0) return;
|
|
@@ -14210,6 +14309,34 @@ function ChatView({ session, mux, onBack }) {
|
|
|
14210
14309
|
!loading && messages.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
14211
14310
|
className: "chat-typing",
|
|
14212
14311
|
children: "还没有消息,发一句话开始吧"
|
|
14312
|
+
}),
|
|
14313
|
+
running && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
14314
|
+
className: "chat-turn-status",
|
|
14315
|
+
role: "status",
|
|
14316
|
+
"aria-label": "输出中",
|
|
14317
|
+
children: ["输出中", /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
14318
|
+
className: "chat-turn-dots",
|
|
14319
|
+
"aria-hidden": true,
|
|
14320
|
+
children: [
|
|
14321
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
14322
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {}),
|
|
14323
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {})
|
|
14324
|
+
]
|
|
14325
|
+
})]
|
|
14326
|
+
}),
|
|
14327
|
+
pendingApprovals.map((approval) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApprovalPanel, {
|
|
14328
|
+
approval,
|
|
14329
|
+
sessionId: session.sessionId,
|
|
14330
|
+
onResolved: (id) => {
|
|
14331
|
+
setPendingApprovals((prev) => prev.filter((a) => a.approvalId !== id));
|
|
14332
|
+
}
|
|
14333
|
+
}, approval.approvalId)),
|
|
14334
|
+
pendingQuestions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(QuestionPanel, {
|
|
14335
|
+
questions: pendingQuestions,
|
|
14336
|
+
sessionId: session.sessionId,
|
|
14337
|
+
onResolved: () => {
|
|
14338
|
+
setPendingQuestions([]);
|
|
14339
|
+
}
|
|
14213
14340
|
})
|
|
14214
14341
|
]
|
|
14215
14342
|
}),
|
|
@@ -14371,7 +14498,10 @@ const MessageRow = (0, import_react.memo)(function MessageRow({ message, showToo
|
|
|
14371
14498
|
pending: message.pending === true
|
|
14372
14499
|
}),
|
|
14373
14500
|
showToolCalls && message.kind === "assistant" && message.tools !== void 0 && message.tools.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ToolDisclosure, { tools: message.tools }),
|
|
14374
|
-
message.kind === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownText, {
|
|
14501
|
+
message.kind === "assistant" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MarkdownText, {
|
|
14502
|
+
text: message.text,
|
|
14503
|
+
pending: message.pending === true
|
|
14504
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CollapsibleText, { text: message.text }),
|
|
14375
14505
|
message.failed === true && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
14376
14506
|
className: "chat-msg-failtag",
|
|
14377
14507
|
children: "本次回复失败"
|
|
@@ -14418,10 +14548,10 @@ function ReasoningDisclosure({ text, pending }) {
|
|
|
14418
14548
|
})]
|
|
14419
14549
|
});
|
|
14420
14550
|
}
|
|
14421
|
-
/** Collapsed-by-default tool-call disclosure: summary
|
|
14551
|
+
/** Collapsed-by-default tool-call disclosure: pill tag summary + card details (#529). */
|
|
14422
14552
|
function ToolDisclosure({ tools }) {
|
|
14423
14553
|
const [open, setOpen] = (0, import_react.useState)(false);
|
|
14424
|
-
const
|
|
14554
|
+
const uniqueNames = [...new Set(tools.map((tool) => tool.name))];
|
|
14425
14555
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
14426
14556
|
className: `chat-disclosure chat-tools${open ? " chat-disclosure-open" : ""}`,
|
|
14427
14557
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
@@ -14442,8 +14572,11 @@ function ToolDisclosure({ tools }) {
|
|
|
14442
14572
|
children: "工具"
|
|
14443
14573
|
}),
|
|
14444
14574
|
!open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
14445
|
-
className: "chat-disclosure-summary",
|
|
14446
|
-
children:
|
|
14575
|
+
className: "chat-disclosure-summary chat-tool-pills",
|
|
14576
|
+
children: uniqueNames.map((name) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
14577
|
+
className: "chat-tool-pill",
|
|
14578
|
+
children: name
|
|
14579
|
+
}, name))
|
|
14447
14580
|
}),
|
|
14448
14581
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
14449
14582
|
className: "chat-disclosure-count",
|
|
@@ -14453,9 +14586,9 @@ function ToolDisclosure({ tools }) {
|
|
|
14453
14586
|
}), open && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
14454
14587
|
className: "chat-disclosure-body chat-tools-body",
|
|
14455
14588
|
children: tools.map((tool, index) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
14456
|
-
className: "chat-tool-
|
|
14589
|
+
className: "chat-tool-card",
|
|
14457
14590
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
14458
|
-
className: "chat-tool-
|
|
14591
|
+
className: "chat-tool-pill",
|
|
14459
14592
|
children: tool.name
|
|
14460
14593
|
}), tool.arguments !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("pre", {
|
|
14461
14594
|
className: "chat-tool-args",
|
|
@@ -14472,9 +14605,49 @@ function ToolDisclosure({ tools }) {
|
|
|
14472
14605
|
* fences or tables never leak malformed markup into the DOM. User
|
|
14473
14606
|
* messages stay plain text (CollapsibleText).
|
|
14474
14607
|
*/
|
|
14475
|
-
function MarkdownText({ text }) {
|
|
14608
|
+
function MarkdownText({ text, pending }) {
|
|
14476
14609
|
const [open, setOpen] = (0, import_react.useState)(false);
|
|
14477
|
-
const html = (0, import_react.
|
|
14610
|
+
const [html, setHtml] = (0, import_react.useState)(() => renderMarkdown(text));
|
|
14611
|
+
/** Text of the last render actually applied to `html`. */
|
|
14612
|
+
const renderedTextRef = (0, import_react.useRef)(text);
|
|
14613
|
+
/** Newest streamed text, read by the trailing render at fire time. */
|
|
14614
|
+
const latestTextRef = (0, import_react.useRef)(text);
|
|
14615
|
+
/** Timestamp of the last applied parse (throttle window start). */
|
|
14616
|
+
const lastRenderAtRef = (0, import_react.useRef)(performance.now());
|
|
14617
|
+
const timerRef = (0, import_react.useRef)(void 0);
|
|
14618
|
+
(0, import_react.useEffect)(() => {
|
|
14619
|
+
latestTextRef.current = text;
|
|
14620
|
+
if (!pending) {
|
|
14621
|
+
if (timerRef.current !== void 0) {
|
|
14622
|
+
clearTimeout(timerRef.current);
|
|
14623
|
+
timerRef.current = void 0;
|
|
14624
|
+
}
|
|
14625
|
+
if (text === renderedTextRef.current) return;
|
|
14626
|
+
lastRenderAtRef.current = performance.now();
|
|
14627
|
+
renderedTextRef.current = text;
|
|
14628
|
+
setHtml(renderMarkdown(text));
|
|
14629
|
+
return;
|
|
14630
|
+
}
|
|
14631
|
+
if (text === renderedTextRef.current) return;
|
|
14632
|
+
const elapsed = performance.now() - lastRenderAtRef.current;
|
|
14633
|
+
if (elapsed >= 120) {
|
|
14634
|
+
lastRenderAtRef.current = performance.now();
|
|
14635
|
+
renderedTextRef.current = text;
|
|
14636
|
+
setHtml(renderMarkdown(text));
|
|
14637
|
+
return;
|
|
14638
|
+
}
|
|
14639
|
+
if (timerRef.current === void 0) timerRef.current = setTimeout(() => {
|
|
14640
|
+
timerRef.current = void 0;
|
|
14641
|
+
lastRenderAtRef.current = performance.now();
|
|
14642
|
+
renderedTextRef.current = latestTextRef.current;
|
|
14643
|
+
setHtml(renderMarkdown(latestTextRef.current));
|
|
14644
|
+
}, 120 - elapsed);
|
|
14645
|
+
}, [text, pending]);
|
|
14646
|
+
(0, import_react.useEffect)(() => {
|
|
14647
|
+
return () => {
|
|
14648
|
+
if (timerRef.current !== void 0) clearTimeout(timerRef.current);
|
|
14649
|
+
};
|
|
14650
|
+
}, []);
|
|
14478
14651
|
const long = text.length > LONG_TEXT_LIMIT;
|
|
14479
14652
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
14480
14653
|
className: "chat-msg-text chat-md" + (long && !open ? " chat-md-collapsed" : ""),
|
|
@@ -14903,6 +15076,207 @@ function DisplaySheet({ showToolCalls, showSystemMessages, onToolCalls, onSystem
|
|
|
14903
15076
|
})
|
|
14904
15077
|
});
|
|
14905
15078
|
}
|
|
15079
|
+
/** One pending tool approval card with allow/reject actions. */
|
|
15080
|
+
function ApprovalPanel({ approval, sessionId, onResolved }) {
|
|
15081
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
15082
|
+
const [panelError, setPanelError] = (0, import_react.useState)(void 0);
|
|
15083
|
+
const act = (outcome) => {
|
|
15084
|
+
if (busy) return;
|
|
15085
|
+
setBusy(true);
|
|
15086
|
+
setPanelError(void 0);
|
|
15087
|
+
respondApproval(sessionId, approval.approvalId, outcome).then(() => {
|
|
15088
|
+
onResolved(approval.approvalId);
|
|
15089
|
+
}, (reason) => {
|
|
15090
|
+
setBusy(false);
|
|
15091
|
+
setPanelError(reason instanceof Error ? reason.message : String(reason));
|
|
15092
|
+
});
|
|
15093
|
+
};
|
|
15094
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15095
|
+
className: "chat-approval-panel",
|
|
15096
|
+
role: "alert",
|
|
15097
|
+
children: [
|
|
15098
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15099
|
+
className: "chat-approval-header",
|
|
15100
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15101
|
+
className: "chat-tool-pill",
|
|
15102
|
+
children: approval.toolName
|
|
15103
|
+
}), approval.reason !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15104
|
+
className: "chat-approval-reason",
|
|
15105
|
+
children: approval.reason
|
|
15106
|
+
})]
|
|
15107
|
+
}),
|
|
15108
|
+
panelError !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15109
|
+
className: "chat-approval-error",
|
|
15110
|
+
children: panelError
|
|
15111
|
+
}),
|
|
15112
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15113
|
+
className: "chat-approval-actions",
|
|
15114
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15115
|
+
type: "button",
|
|
15116
|
+
className: "chat-approval-allow",
|
|
15117
|
+
disabled: busy,
|
|
15118
|
+
onClick: () => {
|
|
15119
|
+
act("allowed-once");
|
|
15120
|
+
},
|
|
15121
|
+
children: busy ? "提交中…" : "允许一次"
|
|
15122
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15123
|
+
type: "button",
|
|
15124
|
+
className: "chat-approval-reject",
|
|
15125
|
+
disabled: busy,
|
|
15126
|
+
onClick: () => {
|
|
15127
|
+
act("rejected");
|
|
15128
|
+
},
|
|
15129
|
+
children: "拒绝"
|
|
15130
|
+
})]
|
|
15131
|
+
})
|
|
15132
|
+
]
|
|
15133
|
+
});
|
|
15134
|
+
}
|
|
15135
|
+
/** Question panel: renders one or more questions with option pickers and a submit button. */
|
|
15136
|
+
function QuestionPanel({ questions, sessionId, onResolved }) {
|
|
15137
|
+
const [selections, setSelections] = (0, import_react.useState)(() => new Map(questions.map((q) => [q.id, {
|
|
15138
|
+
selected: [],
|
|
15139
|
+
custom: ""
|
|
15140
|
+
}])));
|
|
15141
|
+
const [busy, setBusy] = (0, import_react.useState)(false);
|
|
15142
|
+
const [panelError, setPanelError] = (0, import_react.useState)(void 0);
|
|
15143
|
+
const toggle = (questionId, label, multi) => {
|
|
15144
|
+
setSelections((previous) => {
|
|
15145
|
+
const next = new Map(previous);
|
|
15146
|
+
const entry = next.get(questionId) ?? {
|
|
15147
|
+
selected: [],
|
|
15148
|
+
custom: ""
|
|
15149
|
+
};
|
|
15150
|
+
if (multi) {
|
|
15151
|
+
const set = new Set(entry.selected);
|
|
15152
|
+
if (set.has(label)) set.delete(label);
|
|
15153
|
+
else set.add(label);
|
|
15154
|
+
next.set(questionId, {
|
|
15155
|
+
...entry,
|
|
15156
|
+
selected: [...set]
|
|
15157
|
+
});
|
|
15158
|
+
} else next.set(questionId, {
|
|
15159
|
+
...entry,
|
|
15160
|
+
selected: [label]
|
|
15161
|
+
});
|
|
15162
|
+
return next;
|
|
15163
|
+
});
|
|
15164
|
+
};
|
|
15165
|
+
const setCustom = (questionId, value) => {
|
|
15166
|
+
setSelections((previous) => {
|
|
15167
|
+
const next = new Map(previous);
|
|
15168
|
+
const entry = next.get(questionId) ?? {
|
|
15169
|
+
selected: [],
|
|
15170
|
+
custom: ""
|
|
15171
|
+
};
|
|
15172
|
+
next.set(questionId, {
|
|
15173
|
+
...entry,
|
|
15174
|
+
custom: value
|
|
15175
|
+
});
|
|
15176
|
+
return next;
|
|
15177
|
+
});
|
|
15178
|
+
};
|
|
15179
|
+
const submit = () => {
|
|
15180
|
+
if (busy) return;
|
|
15181
|
+
setBusy(true);
|
|
15182
|
+
setPanelError(void 0);
|
|
15183
|
+
respondQuestion(sessionId, questions.map((q) => {
|
|
15184
|
+
const entry = selections.get(q.id) ?? {
|
|
15185
|
+
selected: [],
|
|
15186
|
+
custom: ""
|
|
15187
|
+
};
|
|
15188
|
+
return {
|
|
15189
|
+
id: q.id,
|
|
15190
|
+
selected: entry.selected,
|
|
15191
|
+
...entry.custom.trim() !== "" ? { custom: entry.custom.trim() } : {}
|
|
15192
|
+
};
|
|
15193
|
+
})).then(() => {
|
|
15194
|
+
onResolved();
|
|
15195
|
+
}, (reason) => {
|
|
15196
|
+
setBusy(false);
|
|
15197
|
+
setPanelError(reason instanceof Error ? reason.message : String(reason));
|
|
15198
|
+
});
|
|
15199
|
+
};
|
|
15200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15201
|
+
className: "chat-question-panel",
|
|
15202
|
+
role: "form",
|
|
15203
|
+
"aria-label": "问题",
|
|
15204
|
+
children: [
|
|
15205
|
+
questions.map((q) => {
|
|
15206
|
+
const entry = selections.get(q.id) ?? {
|
|
15207
|
+
selected: [],
|
|
15208
|
+
custom: ""
|
|
15209
|
+
};
|
|
15210
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15211
|
+
className: "chat-question-group",
|
|
15212
|
+
children: [
|
|
15213
|
+
q.header !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15214
|
+
className: "chat-question-header",
|
|
15215
|
+
children: q.header
|
|
15216
|
+
}),
|
|
15217
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15218
|
+
className: "chat-question-text",
|
|
15219
|
+
children: q.question
|
|
15220
|
+
}),
|
|
15221
|
+
q.detail !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15222
|
+
className: "chat-question-detail",
|
|
15223
|
+
children: q.detail
|
|
15224
|
+
}),
|
|
15225
|
+
q.options !== void 0 && q.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15226
|
+
className: "chat-question-options",
|
|
15227
|
+
role: "group",
|
|
15228
|
+
"aria-label": q.question,
|
|
15229
|
+
children: q.options.map((option) => {
|
|
15230
|
+
const checked = entry.selected.includes(option.label);
|
|
15231
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", {
|
|
15232
|
+
className: `chat-question-option${checked ? " chat-question-option-selected" : ""}`,
|
|
15233
|
+
children: [
|
|
15234
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
15235
|
+
type: q.multiSelect ? "checkbox" : "radio",
|
|
15236
|
+
name: `q-${q.id}`,
|
|
15237
|
+
checked,
|
|
15238
|
+
onChange: () => {
|
|
15239
|
+
toggle(q.id, option.label, q.multiSelect === true);
|
|
15240
|
+
}
|
|
15241
|
+
}),
|
|
15242
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15243
|
+
className: "chat-question-option-label",
|
|
15244
|
+
children: option.label
|
|
15245
|
+
}),
|
|
15246
|
+
option.description !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15247
|
+
className: "chat-question-option-desc",
|
|
15248
|
+
children: option.description
|
|
15249
|
+
})
|
|
15250
|
+
]
|
|
15251
|
+
}, option.label);
|
|
15252
|
+
})
|
|
15253
|
+
}),
|
|
15254
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", {
|
|
15255
|
+
className: "chat-question-custom",
|
|
15256
|
+
placeholder: "自定义回答(可选)",
|
|
15257
|
+
rows: 2,
|
|
15258
|
+
value: entry.custom,
|
|
15259
|
+
onChange: (e) => {
|
|
15260
|
+
setCustom(q.id, e.target.value);
|
|
15261
|
+
}
|
|
15262
|
+
})
|
|
15263
|
+
]
|
|
15264
|
+
}, q.id);
|
|
15265
|
+
}),
|
|
15266
|
+
panelError !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15267
|
+
className: "chat-approval-error",
|
|
15268
|
+
children: panelError
|
|
15269
|
+
}),
|
|
15270
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15271
|
+
type: "button",
|
|
15272
|
+
className: "chat-question-submit",
|
|
15273
|
+
disabled: busy,
|
|
15274
|
+
onClick: submit,
|
|
15275
|
+
children: busy ? "提交中…" : "提交回答"
|
|
15276
|
+
})
|
|
15277
|
+
]
|
|
15278
|
+
});
|
|
15279
|
+
}
|
|
14906
15280
|
//#endregion
|
|
14907
15281
|
//#region src/mobile/views/SessionListView.tsx
|
|
14908
15282
|
/**
|
|
@@ -15163,6 +15537,116 @@ function SessionListView({ workspace, onBack, onPick }) {
|
|
|
15163
15537
|
* until a workspace is opened).
|
|
15164
15538
|
*/
|
|
15165
15539
|
/**
|
|
15540
|
+
* Render the directory browser for creating a new workspace.
|
|
15541
|
+
*/
|
|
15542
|
+
function DirectoryBrowser({ onCancel, onPick }) {
|
|
15543
|
+
const [listing, setListing] = (0, import_react.useState)(void 0);
|
|
15544
|
+
const [error, setError] = (0, import_react.useState)(void 0);
|
|
15545
|
+
const [creating, setCreating] = (0, import_react.useState)(false);
|
|
15546
|
+
const [path, setPath] = (0, import_react.useState)(void 0);
|
|
15547
|
+
const [reload, setReload] = (0, import_react.useState)(0);
|
|
15548
|
+
(0, import_react.useEffect)(() => {
|
|
15549
|
+
let cancelled = false;
|
|
15550
|
+
setError(void 0);
|
|
15551
|
+
setListing(void 0);
|
|
15552
|
+
listDirectory(path).then((res) => {
|
|
15553
|
+
if (cancelled) return;
|
|
15554
|
+
setListing(res);
|
|
15555
|
+
}, (reason) => {
|
|
15556
|
+
if (cancelled) return;
|
|
15557
|
+
setError(errorText(reason));
|
|
15558
|
+
});
|
|
15559
|
+
return () => {
|
|
15560
|
+
cancelled = true;
|
|
15561
|
+
};
|
|
15562
|
+
}, [path, reload]);
|
|
15563
|
+
const handleCreate = async () => {
|
|
15564
|
+
if (listing === void 0 || creating) return;
|
|
15565
|
+
setCreating(true);
|
|
15566
|
+
setError(void 0);
|
|
15567
|
+
try {
|
|
15568
|
+
onPick((await createWorkspace(listing.path)).workspace);
|
|
15569
|
+
} catch (reason) {
|
|
15570
|
+
setCreating(false);
|
|
15571
|
+
setError(errorText(reason));
|
|
15572
|
+
}
|
|
15573
|
+
};
|
|
15574
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15575
|
+
className: "mobile dir-browser",
|
|
15576
|
+
children: [
|
|
15577
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
|
|
15578
|
+
className: "mobile-header",
|
|
15579
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15580
|
+
type: "button",
|
|
15581
|
+
className: "mobile-headerAction",
|
|
15582
|
+
onClick: onCancel,
|
|
15583
|
+
children: "返回"
|
|
15584
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
15585
|
+
className: "mobile-title",
|
|
15586
|
+
children: "选择目录"
|
|
15587
|
+
})]
|
|
15588
|
+
}),
|
|
15589
|
+
listing !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15590
|
+
className: "dir-crumbs",
|
|
15591
|
+
children: listing.crumbs.map((crumb, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15592
|
+
type: "button",
|
|
15593
|
+
className: "dir-crumb",
|
|
15594
|
+
onClick: () => setPath(crumb.path),
|
|
15595
|
+
children: crumb.name || "/"
|
|
15596
|
+
}), idx < listing.crumbs.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15597
|
+
className: "dir-crumb-separator",
|
|
15598
|
+
children: "/"
|
|
15599
|
+
})] }, crumb.path))
|
|
15600
|
+
}),
|
|
15601
|
+
error !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15602
|
+
className: "mobile-empty",
|
|
15603
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15604
|
+
className: "mobile-error",
|
|
15605
|
+
children: error
|
|
15606
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15607
|
+
type: "button",
|
|
15608
|
+
className: "mobile-button",
|
|
15609
|
+
onClick: () => setReload((n) => n + 1),
|
|
15610
|
+
children: "重试"
|
|
15611
|
+
})]
|
|
15612
|
+
}) : listing === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15613
|
+
className: "mobile-empty",
|
|
15614
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15615
|
+
className: "mobile-muted",
|
|
15616
|
+
children: "加载中…"
|
|
15617
|
+
})
|
|
15618
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
|
|
15619
|
+
className: "mobile-list",
|
|
15620
|
+
children: listing.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15621
|
+
className: "mobile-empty dir-empty",
|
|
15622
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15623
|
+
className: "mobile-muted",
|
|
15624
|
+
children: "空目录"
|
|
15625
|
+
})
|
|
15626
|
+
}) : listing.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15627
|
+
type: "button",
|
|
15628
|
+
className: `mobile-row dir-entry ${entry.hidden ? "dir-entry-hidden" : ""}`,
|
|
15629
|
+
onClick: () => setPath(entry.path),
|
|
15630
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
15631
|
+
className: "mobile-rowTitle",
|
|
15632
|
+
children: ["📁 ", entry.name]
|
|
15633
|
+
})
|
|
15634
|
+
}) }, entry.path))
|
|
15635
|
+
}),
|
|
15636
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15637
|
+
className: "dir-select",
|
|
15638
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15639
|
+
type: "button",
|
|
15640
|
+
className: "mobile-button",
|
|
15641
|
+
disabled: listing === void 0 || creating,
|
|
15642
|
+
onClick: () => void handleCreate(),
|
|
15643
|
+
children: creating ? "创建中…" : "选择此目录"
|
|
15644
|
+
})
|
|
15645
|
+
})
|
|
15646
|
+
]
|
|
15647
|
+
});
|
|
15648
|
+
}
|
|
15649
|
+
/**
|
|
15166
15650
|
* Render the workspace roster.
|
|
15167
15651
|
* @param props - the pick action.
|
|
15168
15652
|
* @returns the roster.
|
|
@@ -15171,6 +15655,7 @@ function WorkspaceView({ initialWorkspaceId, onPick }) {
|
|
|
15171
15655
|
const [items, setItems] = (0, import_react.useState)(void 0);
|
|
15172
15656
|
const [error, setError] = (0, import_react.useState)(void 0);
|
|
15173
15657
|
const [reload, setReload] = (0, import_react.useState)(0);
|
|
15658
|
+
const [isCreating, setIsCreating] = (0, import_react.useState)(false);
|
|
15174
15659
|
(0, import_react.useEffect)(() => {
|
|
15175
15660
|
let cancelled = false;
|
|
15176
15661
|
listWorkspaces().then((rows) => {
|
|
@@ -15193,6 +15678,10 @@ function WorkspaceView({ initialWorkspaceId, onPick }) {
|
|
|
15193
15678
|
onPick,
|
|
15194
15679
|
reload
|
|
15195
15680
|
]);
|
|
15681
|
+
if (isCreating) return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DirectoryBrowser, {
|
|
15682
|
+
onCancel: () => setIsCreating(false),
|
|
15683
|
+
onPick
|
|
15684
|
+
});
|
|
15196
15685
|
if (error !== void 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15197
15686
|
className: "mobile",
|
|
15198
15687
|
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
|
|
@@ -15234,54 +15723,56 @@ function WorkspaceView({ initialWorkspaceId, onPick }) {
|
|
|
15234
15723
|
})
|
|
15235
15724
|
})]
|
|
15236
15725
|
});
|
|
15237
|
-
if (items.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15238
|
-
className: "mobile",
|
|
15239
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
|
|
15240
|
-
className: "mobile-header",
|
|
15241
|
-
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
15242
|
-
className: "mobile-title",
|
|
15243
|
-
children: "工作区"
|
|
15244
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeToggle, {})]
|
|
15245
|
-
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15246
|
-
className: "mobile-empty",
|
|
15247
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15248
|
-
className: "mobile-muted",
|
|
15249
|
-
children: "暂无工作区"
|
|
15250
|
-
})
|
|
15251
|
-
})]
|
|
15252
|
-
});
|
|
15253
15726
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
15254
15727
|
className: "mobile",
|
|
15255
|
-
children: [
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
|
|
15262
|
-
|
|
15263
|
-
|
|
15264
|
-
|
|
15265
|
-
|
|
15266
|
-
|
|
15267
|
-
|
|
15268
|
-
}
|
|
15269
|
-
|
|
15270
|
-
|
|
15271
|
-
|
|
15272
|
-
|
|
15273
|
-
|
|
15274
|
-
|
|
15275
|
-
|
|
15276
|
-
|
|
15277
|
-
|
|
15278
|
-
|
|
15279
|
-
|
|
15280
|
-
|
|
15281
|
-
|
|
15282
|
-
|
|
15283
|
-
|
|
15284
|
-
|
|
15728
|
+
children: [
|
|
15729
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("header", {
|
|
15730
|
+
className: "mobile-header",
|
|
15731
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
15732
|
+
className: "mobile-title",
|
|
15733
|
+
children: "工作区"
|
|
15734
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThemeToggle, {})]
|
|
15735
|
+
}),
|
|
15736
|
+
items.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15737
|
+
className: "mobile-empty",
|
|
15738
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15739
|
+
className: "mobile-muted",
|
|
15740
|
+
children: "暂无工作区"
|
|
15741
|
+
})
|
|
15742
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", {
|
|
15743
|
+
className: "mobile-list",
|
|
15744
|
+
children: items.map((workspace) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("button", {
|
|
15745
|
+
type: "button",
|
|
15746
|
+
className: "mobile-row",
|
|
15747
|
+
onClick: () => {
|
|
15748
|
+
onPick(workspace);
|
|
15749
|
+
},
|
|
15750
|
+
children: [
|
|
15751
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15752
|
+
className: "mobile-rowTitle",
|
|
15753
|
+
children: workspace.title
|
|
15754
|
+
}),
|
|
15755
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15756
|
+
className: "mobile-rowMeta",
|
|
15757
|
+
children: workspace.path
|
|
15758
|
+
}),
|
|
15759
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
15760
|
+
className: "mobile-chevron",
|
|
15761
|
+
children: "›"
|
|
15762
|
+
})
|
|
15763
|
+
]
|
|
15764
|
+
}) }, workspace.workspaceId))
|
|
15765
|
+
}),
|
|
15766
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
15767
|
+
style: { padding: "16px" },
|
|
15768
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15769
|
+
type: "button",
|
|
15770
|
+
className: "mobile-button",
|
|
15771
|
+
onClick: () => setIsCreating(true),
|
|
15772
|
+
children: "+ 新建工作区"
|
|
15773
|
+
})
|
|
15774
|
+
})
|
|
15775
|
+
]
|
|
15285
15776
|
});
|
|
15286
15777
|
}
|
|
15287
15778
|
//#endregion
|
|
@@ -16197,16 +16688,33 @@ body {
|
|
|
16197
16688
|
gap: 8px;
|
|
16198
16689
|
}
|
|
16199
16690
|
|
|
16200
|
-
.chat-tool-
|
|
16691
|
+
.chat-tool-card {
|
|
16201
16692
|
display: flex;
|
|
16202
16693
|
flex-direction: column;
|
|
16203
|
-
gap:
|
|
16694
|
+
gap: 4px;
|
|
16695
|
+
padding: 6px 8px;
|
|
16696
|
+
border: 1px solid var(--m-border);
|
|
16697
|
+
border-radius: 8px;
|
|
16698
|
+
background: var(--m-bg-raised);
|
|
16204
16699
|
}
|
|
16205
16700
|
|
|
16206
|
-
.chat-tool-
|
|
16207
|
-
|
|
16208
|
-
|
|
16701
|
+
.chat-tool-pills {
|
|
16702
|
+
display: flex;
|
|
16703
|
+
gap: 4px;
|
|
16704
|
+
flex-wrap: wrap;
|
|
16705
|
+
align-items: center;
|
|
16706
|
+
}
|
|
16707
|
+
|
|
16708
|
+
.chat-tool-pill {
|
|
16709
|
+
display: inline-block;
|
|
16710
|
+
padding: 1px 7px;
|
|
16711
|
+
border-radius: 999px;
|
|
16712
|
+
background: var(--m-accent-soft);
|
|
16713
|
+
color: var(--m-accent);
|
|
16714
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
16715
|
+
font-size: 11px;
|
|
16209
16716
|
font-weight: 600;
|
|
16717
|
+
white-space: nowrap;
|
|
16210
16718
|
}
|
|
16211
16719
|
|
|
16212
16720
|
.chat-tool-args {
|
|
@@ -16220,8 +16728,245 @@ body {
|
|
|
16220
16728
|
line-height: 1.5;
|
|
16221
16729
|
overflow-wrap: break-word;
|
|
16222
16730
|
white-space: pre-wrap;
|
|
16731
|
+
max-height: 200px;
|
|
16732
|
+
overflow-y: auto;
|
|
16733
|
+
}
|
|
16734
|
+
|
|
16735
|
+
/* ── turn status indicator (#1017) ───────────────────────────────────── */
|
|
16736
|
+
|
|
16737
|
+
.chat-turn-status {
|
|
16738
|
+
display: flex;
|
|
16739
|
+
align-items: center;
|
|
16740
|
+
gap: 6px;
|
|
16741
|
+
padding: 10px 16px;
|
|
16742
|
+
color: var(--m-accent);
|
|
16743
|
+
font-size: 13px;
|
|
16744
|
+
font-weight: 600;
|
|
16745
|
+
background: linear-gradient(
|
|
16746
|
+
90deg,
|
|
16747
|
+
var(--m-accent) 0%,
|
|
16748
|
+
color-mix(in srgb, var(--m-accent) 60%, #ffffff) 50%,
|
|
16749
|
+
var(--m-accent) 100%
|
|
16750
|
+
);
|
|
16751
|
+
background-size: 200% 100%;
|
|
16752
|
+
background-clip: text;
|
|
16753
|
+
-webkit-background-clip: text;
|
|
16754
|
+
-webkit-text-fill-color: transparent;
|
|
16755
|
+
animation: dsh-shimmer 1.8s linear infinite;
|
|
16756
|
+
}
|
|
16757
|
+
|
|
16758
|
+
.chat-turn-dots {
|
|
16759
|
+
display: inline-flex;
|
|
16760
|
+
gap: 3px;
|
|
16761
|
+
}
|
|
16762
|
+
|
|
16763
|
+
.chat-turn-dots span {
|
|
16764
|
+
display: inline-block;
|
|
16765
|
+
width: 5px;
|
|
16766
|
+
height: 5px;
|
|
16767
|
+
border-radius: 50%;
|
|
16768
|
+
background: var(--m-accent);
|
|
16769
|
+
animation: dsh-dot-bounce 1.2s ease-in-out infinite;
|
|
16770
|
+
}
|
|
16771
|
+
|
|
16772
|
+
.chat-turn-dots span:nth-child(2) {
|
|
16773
|
+
animation-delay: 0.15s;
|
|
16774
|
+
}
|
|
16775
|
+
|
|
16776
|
+
.chat-turn-dots span:nth-child(3) {
|
|
16777
|
+
animation-delay: 0.3s;
|
|
16778
|
+
}
|
|
16779
|
+
|
|
16780
|
+
@keyframes dsh-shimmer {
|
|
16781
|
+
from { background-position: 200% 0; }
|
|
16782
|
+
to { background-position: -200% 0; }
|
|
16783
|
+
}
|
|
16784
|
+
|
|
16785
|
+
@keyframes dsh-dot-bounce {
|
|
16786
|
+
0%, 80%, 100% { transform: translateY(0); opacity: 0.4; }
|
|
16787
|
+
40% { transform: translateY(-4px); opacity: 1; }
|
|
16788
|
+
}
|
|
16789
|
+
|
|
16790
|
+
/* ── approval panel (#1025) ──────────────────────────────────────────── */
|
|
16791
|
+
|
|
16792
|
+
.chat-approval-panel {
|
|
16793
|
+
margin: 8px 12px;
|
|
16794
|
+
padding: 10px 12px;
|
|
16795
|
+
border: 1px solid var(--m-accent);
|
|
16796
|
+
border-radius: var(--m-radius);
|
|
16797
|
+
background: var(--m-bg-raised);
|
|
16798
|
+
box-shadow: var(--m-shadow);
|
|
16799
|
+
}
|
|
16800
|
+
|
|
16801
|
+
.chat-approval-header {
|
|
16802
|
+
display: flex;
|
|
16803
|
+
align-items: center;
|
|
16804
|
+
gap: 8px;
|
|
16805
|
+
margin-bottom: 8px;
|
|
16806
|
+
}
|
|
16807
|
+
|
|
16808
|
+
.chat-approval-reason {
|
|
16809
|
+
color: var(--m-text-secondary);
|
|
16810
|
+
font-size: 12.5px;
|
|
16811
|
+
}
|
|
16812
|
+
|
|
16813
|
+
.chat-approval-error {
|
|
16814
|
+
margin: 4px 0;
|
|
16815
|
+
color: var(--m-danger);
|
|
16816
|
+
font-size: 12px;
|
|
16817
|
+
}
|
|
16818
|
+
|
|
16819
|
+
.chat-approval-actions {
|
|
16820
|
+
display: flex;
|
|
16821
|
+
gap: 8px;
|
|
16822
|
+
}
|
|
16823
|
+
|
|
16824
|
+
.chat-approval-allow {
|
|
16825
|
+
flex: 1;
|
|
16826
|
+
height: 34px;
|
|
16827
|
+
border: none;
|
|
16828
|
+
border-radius: 8px;
|
|
16829
|
+
background: var(--m-accent);
|
|
16830
|
+
color: #fff;
|
|
16831
|
+
font: inherit;
|
|
16832
|
+
font-size: 13px;
|
|
16833
|
+
font-weight: 600;
|
|
16834
|
+
cursor: pointer;
|
|
16835
|
+
}
|
|
16836
|
+
|
|
16837
|
+
.chat-approval-allow:disabled {
|
|
16838
|
+
opacity: 0.6;
|
|
16839
|
+
}
|
|
16840
|
+
|
|
16841
|
+
.chat-approval-reject {
|
|
16842
|
+
flex: 1;
|
|
16843
|
+
height: 34px;
|
|
16844
|
+
border: 1px solid var(--m-border);
|
|
16845
|
+
border-radius: 8px;
|
|
16846
|
+
background: var(--m-bg-raised);
|
|
16847
|
+
color: var(--m-text);
|
|
16848
|
+
font: inherit;
|
|
16849
|
+
font-size: 13px;
|
|
16850
|
+
cursor: pointer;
|
|
16851
|
+
}
|
|
16852
|
+
|
|
16853
|
+
.chat-approval-reject:disabled {
|
|
16854
|
+
opacity: 0.6;
|
|
16855
|
+
}
|
|
16856
|
+
|
|
16857
|
+
/* ── question panel (#1025) ──────────────────────────────────────────── */
|
|
16858
|
+
|
|
16859
|
+
.chat-question-panel {
|
|
16860
|
+
margin: 8px 12px;
|
|
16861
|
+
padding: 10px 12px;
|
|
16862
|
+
border: 1px solid var(--m-accent);
|
|
16863
|
+
border-radius: var(--m-radius);
|
|
16864
|
+
background: var(--m-bg-raised);
|
|
16865
|
+
box-shadow: var(--m-shadow);
|
|
16866
|
+
display: flex;
|
|
16867
|
+
flex-direction: column;
|
|
16868
|
+
gap: 10px;
|
|
16869
|
+
}
|
|
16870
|
+
|
|
16871
|
+
.chat-question-group {
|
|
16872
|
+
display: flex;
|
|
16873
|
+
flex-direction: column;
|
|
16874
|
+
gap: 6px;
|
|
16223
16875
|
}
|
|
16224
16876
|
|
|
16877
|
+
.chat-question-header {
|
|
16878
|
+
font-size: 14px;
|
|
16879
|
+
font-weight: 600;
|
|
16880
|
+
color: var(--m-text);
|
|
16881
|
+
}
|
|
16882
|
+
|
|
16883
|
+
.chat-question-text {
|
|
16884
|
+
font-size: 13px;
|
|
16885
|
+
color: var(--m-text);
|
|
16886
|
+
line-height: 1.5;
|
|
16887
|
+
}
|
|
16888
|
+
|
|
16889
|
+
.chat-question-detail {
|
|
16890
|
+
font-size: 12px;
|
|
16891
|
+
color: var(--m-text-secondary);
|
|
16892
|
+
line-height: 1.5;
|
|
16893
|
+
}
|
|
16894
|
+
|
|
16895
|
+
.chat-question-options {
|
|
16896
|
+
display: flex;
|
|
16897
|
+
flex-direction: column;
|
|
16898
|
+
gap: 4px;
|
|
16899
|
+
}
|
|
16900
|
+
|
|
16901
|
+
.chat-question-option {
|
|
16902
|
+
display: flex;
|
|
16903
|
+
align-items: flex-start;
|
|
16904
|
+
gap: 8px;
|
|
16905
|
+
padding: 8px 10px;
|
|
16906
|
+
border: 1px solid var(--m-border);
|
|
16907
|
+
border-radius: 8px;
|
|
16908
|
+
cursor: pointer;
|
|
16909
|
+
font-size: 13px;
|
|
16910
|
+
transition: border-color 0.12s, background 0.12s;
|
|
16911
|
+
}
|
|
16912
|
+
|
|
16913
|
+
.chat-question-option input {
|
|
16914
|
+
margin: 2px 0 0;
|
|
16915
|
+
flex: none;
|
|
16916
|
+
}
|
|
16917
|
+
|
|
16918
|
+
.chat-question-option-selected {
|
|
16919
|
+
border-color: var(--m-accent);
|
|
16920
|
+
background: var(--m-accent-soft);
|
|
16921
|
+
}
|
|
16922
|
+
|
|
16923
|
+
.chat-question-option-label {
|
|
16924
|
+
color: var(--m-text);
|
|
16925
|
+
font-weight: 500;
|
|
16926
|
+
}
|
|
16927
|
+
|
|
16928
|
+
.chat-question-option-desc {
|
|
16929
|
+
display: block;
|
|
16930
|
+
color: var(--m-text-tertiary);
|
|
16931
|
+
font-size: 12px;
|
|
16932
|
+
}
|
|
16933
|
+
|
|
16934
|
+
.chat-question-custom {
|
|
16935
|
+
width: 100%;
|
|
16936
|
+
min-height: 40px;
|
|
16937
|
+
box-sizing: border-box;
|
|
16938
|
+
padding: 8px 10px;
|
|
16939
|
+
border: 1px solid var(--m-border);
|
|
16940
|
+
border-radius: 8px;
|
|
16941
|
+
background: var(--m-bg-input);
|
|
16942
|
+
color: var(--m-text);
|
|
16943
|
+
font: inherit;
|
|
16944
|
+
font-size: 13px;
|
|
16945
|
+
resize: vertical;
|
|
16946
|
+
outline: none;
|
|
16947
|
+
}
|
|
16948
|
+
|
|
16949
|
+
.chat-question-custom:focus-visible {
|
|
16950
|
+
border-color: var(--m-accent);
|
|
16951
|
+
}
|
|
16952
|
+
|
|
16953
|
+
.chat-question-submit {
|
|
16954
|
+
height: 36px;
|
|
16955
|
+
border: none;
|
|
16956
|
+
border-radius: 8px;
|
|
16957
|
+
background: var(--m-accent);
|
|
16958
|
+
color: #fff;
|
|
16959
|
+
font: inherit;
|
|
16960
|
+
font-size: 13px;
|
|
16961
|
+
font-weight: 600;
|
|
16962
|
+
cursor: pointer;
|
|
16963
|
+
}
|
|
16964
|
+
|
|
16965
|
+
.chat-question-submit:disabled {
|
|
16966
|
+
opacity: 0.6;
|
|
16967
|
+
}
|
|
16968
|
+
|
|
16969
|
+
|
|
16225
16970
|
/* ── composer toolbar (model / permission chips) ─────────────────────── */
|
|
16226
16971
|
|
|
16227
16972
|
.chat-tools {
|
|
@@ -16662,10 +17407,16 @@ body {
|
|
|
16662
17407
|
.sheet-confirm-danger,
|
|
16663
17408
|
.sheet-toggle-switch,
|
|
16664
17409
|
.sheet-toggle-switch-knob,
|
|
16665
|
-
.chat-input
|
|
17410
|
+
.chat-input,
|
|
17411
|
+
.chat-turn-status,
|
|
17412
|
+
.chat-turn-dots span {
|
|
16666
17413
|
animation: none;
|
|
16667
17414
|
transition: none;
|
|
16668
17415
|
}
|
|
17416
|
+
.chat-turn-status {
|
|
17417
|
+
background: none;
|
|
17418
|
+
-webkit-text-fill-color: var(--m-accent);
|
|
17419
|
+
}
|
|
16669
17420
|
}
|
|
16670
17421
|
|
|
16671
17422
|
/* ── installed-app pairing ───────────────────────────────────────────── */
|
|
@@ -16715,6 +17466,65 @@ body {
|
|
|
16715
17466
|
.mobile-pairSubmit {
|
|
16716
17467
|
width: 100%;
|
|
16717
17468
|
}
|
|
17469
|
+
|
|
17470
|
+
/* ── directory browser ─────────────────────────────────────────────── */
|
|
17471
|
+
|
|
17472
|
+
.dir-browser {
|
|
17473
|
+
display: flex;
|
|
17474
|
+
flex-direction: column;
|
|
17475
|
+
}
|
|
17476
|
+
|
|
17477
|
+
.dir-crumbs {
|
|
17478
|
+
display: flex;
|
|
17479
|
+
align-items: center;
|
|
17480
|
+
overflow-x: auto;
|
|
17481
|
+
padding: 12px 16px;
|
|
17482
|
+
background: var(--m-bg-raised);
|
|
17483
|
+
border-bottom: 1px solid var(--m-border);
|
|
17484
|
+
white-space: nowrap;
|
|
17485
|
+
}
|
|
17486
|
+
|
|
17487
|
+
.dir-crumbs::-webkit-scrollbar {
|
|
17488
|
+
display: none;
|
|
17489
|
+
}
|
|
17490
|
+
|
|
17491
|
+
.dir-crumb {
|
|
17492
|
+
border: none;
|
|
17493
|
+
background: transparent;
|
|
17494
|
+
padding: 4px 6px;
|
|
17495
|
+
color: var(--m-text);
|
|
17496
|
+
font-size: 14px;
|
|
17497
|
+
cursor: pointer;
|
|
17498
|
+
}
|
|
17499
|
+
|
|
17500
|
+
.dir-crumb:active {
|
|
17501
|
+
background: var(--m-bg-input);
|
|
17502
|
+
border-radius: 4px;
|
|
17503
|
+
}
|
|
17504
|
+
|
|
17505
|
+
.dir-crumb-separator {
|
|
17506
|
+
color: var(--m-text-tertiary);
|
|
17507
|
+
margin: 0 4px;
|
|
17508
|
+
font-size: 14px;
|
|
17509
|
+
}
|
|
17510
|
+
|
|
17511
|
+
.dir-entry {
|
|
17512
|
+
padding-left: 16px;
|
|
17513
|
+
}
|
|
17514
|
+
|
|
17515
|
+
.dir-entry-hidden {
|
|
17516
|
+
opacity: 0.5;
|
|
17517
|
+
}
|
|
17518
|
+
|
|
17519
|
+
.dir-select {
|
|
17520
|
+
padding: 16px;
|
|
17521
|
+
background: var(--m-bg);
|
|
17522
|
+
border-top: 1px solid var(--m-border);
|
|
17523
|
+
}
|
|
17524
|
+
|
|
17525
|
+
.dir-empty {
|
|
17526
|
+
padding: 40px 0;
|
|
17527
|
+
}
|
|
16718
17528
|
`;
|
|
16719
17529
|
const MOBILE_SERVICE_WORKER_URL = "/m/service-worker.js";
|
|
16720
17530
|
/** Register the mobile shell worker when the current browser supports it. */
|