@fonlan/dsh-quick-commands 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +110 -74
- package/lib/client.js.map +1 -1
- package/package.json +1 -1
- package/src/client/header.tsx +89 -34
- package/src/client/index.tsx +15 -14
- package/src/client/locales.ts +7 -7
- package/src/client/quick-commands.css +25 -190
- package/src/client/settings-section.css +163 -0
- package/src/client/settings-section.tsx +195 -0
- package/src/client/settings-card.tsx +0 -209
package/lib/client.js
CHANGED
|
@@ -5,6 +5,7 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
+
let react_dom = require("react-dom");
|
|
8
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
11
|
//#region src/client/api.ts
|
|
@@ -169,6 +170,26 @@ window.__ModuleLoader__.load({
|
|
|
169
170
|
* Props are the standard session/global kit of `conversation.session.header.utilities`
|
|
170
171
|
* plus this plugin's injected hooks (anchor preference + resolver).
|
|
171
172
|
*/
|
|
173
|
+
const MENU_VIEWPORT_GAP = 12;
|
|
174
|
+
const MENU_PREFERRED_WIDTH = 280;
|
|
175
|
+
/**
|
|
176
|
+
* Position the portal menu beside its button without ever entering the phone
|
|
177
|
+
* nav rail. Calculating actual pixels (rather than only CSS max-width) also
|
|
178
|
+
* means it remains correct when the header itself sits in a clipped stacking
|
|
179
|
+
* context created by a shell/mobile plugin.
|
|
180
|
+
*/
|
|
181
|
+
function positionMenu(button) {
|
|
182
|
+
const rect = button.getBoundingClientRect();
|
|
183
|
+
const minLeft = window.matchMedia("(max-width: 767px)").matches ? 68 : MENU_VIEWPORT_GAP;
|
|
184
|
+
const availableWidth = Math.max(0, window.innerWidth - minLeft - MENU_VIEWPORT_GAP);
|
|
185
|
+
const width = Math.min(MENU_PREFERRED_WIDTH, availableWidth);
|
|
186
|
+
const maxLeft = Math.max(minLeft, window.innerWidth - MENU_VIEWPORT_GAP - width);
|
|
187
|
+
return {
|
|
188
|
+
left: Math.max(minLeft, Math.min(rect.right - MENU_VIEWPORT_GAP - width, maxLeft)),
|
|
189
|
+
top: Math.min(rect.bottom + 6, Math.max(MENU_VIEWPORT_GAP, window.innerHeight - 72)),
|
|
190
|
+
width
|
|
191
|
+
};
|
|
192
|
+
}
|
|
172
193
|
/** Longest-path workspace match for a cwd (subdirectory allowed). */
|
|
173
194
|
function matchWorkspace(cwd, workspaces) {
|
|
174
195
|
if (cwd === void 0) return void 0;
|
|
@@ -203,12 +224,16 @@ window.__ModuleLoader__.load({
|
|
|
203
224
|
const [anchor, setAnchor] = (0, react.useState)("corner");
|
|
204
225
|
const [popupSize, setPopupSize] = (0, react.useState)(void 0);
|
|
205
226
|
const wrapRef = (0, react.useRef)(null);
|
|
227
|
+
const buttonRef = (0, react.useRef)(null);
|
|
228
|
+
const menuRef = (0, react.useRef)(null);
|
|
229
|
+
const [menuPosition, setMenuPosition] = (0, react.useState)(null);
|
|
206
230
|
(0, react.useEffect)(() => {
|
|
207
231
|
if (!popover.menuOpen) return;
|
|
208
232
|
const onPointerDown = (e) => {
|
|
209
233
|
const wrap = wrapRef.current;
|
|
210
|
-
|
|
211
|
-
if (e.target instanceof Node
|
|
234
|
+
const menu = menuRef.current;
|
|
235
|
+
if (!(e.target instanceof Node)) return;
|
|
236
|
+
if (wrap?.contains(e.target) || menu?.contains(e.target)) return;
|
|
212
237
|
closeMenu();
|
|
213
238
|
};
|
|
214
239
|
document.addEventListener("pointerdown", onPointerDown);
|
|
@@ -216,6 +241,25 @@ window.__ModuleLoader__.load({
|
|
|
216
241
|
document.removeEventListener("pointerdown", onPointerDown);
|
|
217
242
|
};
|
|
218
243
|
}, [popover.menuOpen]);
|
|
244
|
+
(0, react.useLayoutEffect)(() => {
|
|
245
|
+
if (!popover.menuOpen) {
|
|
246
|
+
setMenuPosition(null);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const update = () => {
|
|
250
|
+
const button = buttonRef.current;
|
|
251
|
+
if (button !== null) setMenuPosition(positionMenu(button));
|
|
252
|
+
};
|
|
253
|
+
update();
|
|
254
|
+
window.addEventListener("resize", update);
|
|
255
|
+
window.addEventListener("scroll", update, true);
|
|
256
|
+
window.visualViewport?.addEventListener("resize", update);
|
|
257
|
+
return () => {
|
|
258
|
+
window.removeEventListener("resize", update);
|
|
259
|
+
window.removeEventListener("scroll", update, true);
|
|
260
|
+
window.visualViewport?.removeEventListener("resize", update);
|
|
261
|
+
};
|
|
262
|
+
}, [popover.menuOpen]);
|
|
219
263
|
(0, react.useEffect)(() => {
|
|
220
264
|
if (!popover.menuOpen) return;
|
|
221
265
|
let cancelled = false;
|
|
@@ -259,6 +303,7 @@ window.__ModuleLoader__.load({
|
|
|
259
303
|
className: "qc-head-wrap",
|
|
260
304
|
children: [
|
|
261
305
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
306
|
+
ref: buttonRef,
|
|
262
307
|
type: "button",
|
|
263
308
|
className: "qc-head-btn",
|
|
264
309
|
title: workspace === void 0 ? t("noWorkspace") : t("buttonTooltip"),
|
|
@@ -269,9 +314,15 @@ window.__ModuleLoader__.load({
|
|
|
269
314
|
},
|
|
270
315
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPlayOutline16, { size: 15 })
|
|
271
316
|
}),
|
|
272
|
-
popover.menuOpen && workspace !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
273
|
-
|
|
317
|
+
popover.menuOpen && workspace !== void 0 && menuPosition !== null && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
318
|
+
ref: menuRef,
|
|
319
|
+
className: "qc-menu qc-menu-portal",
|
|
274
320
|
role: "menu",
|
|
321
|
+
style: {
|
|
322
|
+
left: menuPosition.left,
|
|
323
|
+
top: menuPosition.top,
|
|
324
|
+
width: menuPosition.width
|
|
325
|
+
},
|
|
275
326
|
children: [
|
|
276
327
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
277
328
|
className: "qc-menu-head",
|
|
@@ -317,7 +368,7 @@ window.__ModuleLoader__.load({
|
|
|
317
368
|
})]
|
|
318
369
|
}, command.name))
|
|
319
370
|
]
|
|
320
|
-
}),
|
|
371
|
+
}), document.body),
|
|
321
372
|
popover.runId !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RunPopup, {
|
|
322
373
|
runId: popover.runId,
|
|
323
374
|
workspaceId: popover.workspaceId ?? "",
|
|
@@ -384,7 +435,6 @@ window.__ModuleLoader__.load({
|
|
|
384
435
|
}));
|
|
385
436
|
const [view, setView] = (0, react.useState)("stdout");
|
|
386
437
|
const [autoFollow, setAutoFollow] = (0, react.useState)(true);
|
|
387
|
-
const [killConfirm, setKillConfirm] = (0, react.useState)(false);
|
|
388
438
|
const [pollError, setPollError] = (0, react.useState)(null);
|
|
389
439
|
const [closing, setClosing] = (0, react.useState)(false);
|
|
390
440
|
const outputRef = (0, react.useRef)(null);
|
|
@@ -447,15 +497,13 @@ window.__ModuleLoader__.load({
|
|
|
447
497
|
onClose();
|
|
448
498
|
}
|
|
449
499
|
};
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
onClose();
|
|
453
|
-
return;
|
|
454
|
-
}
|
|
455
|
-
setKillConfirm(true);
|
|
500
|
+
const closeOutput = () => {
|
|
501
|
+
onClose();
|
|
456
502
|
};
|
|
457
|
-
const
|
|
458
|
-
|
|
503
|
+
const onClosePointerUp = (e) => {
|
|
504
|
+
if (e.pointerType !== "touch") return;
|
|
505
|
+
e.preventDefault();
|
|
506
|
+
closeOutput();
|
|
459
507
|
};
|
|
460
508
|
const heightFree = anchor === "corner" ? "top" : "bottom";
|
|
461
509
|
const onResizePointerDown = (e) => {
|
|
@@ -541,29 +589,12 @@ window.__ModuleLoader__.load({
|
|
|
541
589
|
className: "qc-popup-icon qc-popup-close",
|
|
542
590
|
title: t("runClose"),
|
|
543
591
|
"aria-label": t("runClose"),
|
|
544
|
-
|
|
592
|
+
onPointerUp: onClosePointerUp,
|
|
593
|
+
onClick: closeOutput,
|
|
545
594
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 })
|
|
546
595
|
})
|
|
547
596
|
]
|
|
548
597
|
}),
|
|
549
|
-
killConfirm && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
550
|
-
className: "qc-popup-confirm",
|
|
551
|
-
children: [
|
|
552
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("runKillConfirm") }),
|
|
553
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
554
|
-
type: "button",
|
|
555
|
-
className: "qc-popup-btn",
|
|
556
|
-
onClick: confirmClose,
|
|
557
|
-
children: t("runKill")
|
|
558
|
-
}),
|
|
559
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
560
|
-
type: "button",
|
|
561
|
-
className: "qc-popup-btn qc-popup-btn-plain",
|
|
562
|
-
onClick: () => setKillConfirm(false),
|
|
563
|
-
children: t("runClose")
|
|
564
|
-
})
|
|
565
|
-
]
|
|
566
|
-
}),
|
|
567
598
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
568
599
|
className: "qc-popup-tabs",
|
|
569
600
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -669,19 +700,29 @@ window.__ModuleLoader__.load({
|
|
|
669
700
|
});
|
|
670
701
|
}
|
|
671
702
|
//#endregion
|
|
672
|
-
//#region src/client/settings-
|
|
703
|
+
//#region \0dsh-css:/home/runner/work/dsh-quick-commands/dsh-quick-commands/src/client/settings-section.css.mjs
|
|
704
|
+
const css$1 = "/**\n * Styles for the quick-commands settings page.\n *\n * The page occupies the settings panel content column: a static header\n * (title plus a one-line summary) over the always-visible command editor.\n * Inner controls follow the built-in settings language.\n */\n\n/* page layout */\n\n.qc-page {\n display: flex;\n flex-direction: column;\n gap: 16px;\n max-width: 720px;\n}\n.qc-page-head {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n.qc-page-title {\n margin: 0;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n.qc-page-sub {\n margin: 0;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n font-size: 13px;\n line-height: 1.5;\n}\n\n/* body: workspace / command-set editor */\n\n.qc-settings-body {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.qc-settings-intro {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n margin: 0;\n line-height: 1.5;\n}\n.qc-settings-anchor {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 12px;\n}\n.qc-settings-anchor-label {\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-weight: 500;\n}\n.qc-settings-radio {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n cursor: pointer;\n}\n.qc-settings-error {\n font-size: 12px;\n color: var(--dsw-alias-state-error-primary, #e5484d);\n}\n.qc-settings-empty {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n margin: 0;\n}\n.qc-settings-workspace {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 10px;\n padding: 10px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.qc-settings-workspace-head {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n.qc-settings-workspace-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-settings-workspace-path {\n font-size: 11px;\n font-family: var(--dsw-font-family-mono, monospace);\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.qc-settings-no-commands {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n margin: 0;\n}\n.qc-settings-command {\n display: flex;\n gap: 6px;\n align-items: center;\n}\n.qc-settings-field {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1, #2a2a2d);\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 12px;\n padding: 6px 8px;\n outline: none;\n}\n.qc-settings-field::placeholder {\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n}\n.qc-settings-field:focus {\n border-color: var(--dsw-alias-brand-primary, #4a9eff);\n}\n.qc-settings-field-name {\n width: 160px;\n flex: none;\n}\n.qc-settings-field-command {\n flex: 1;\n font-family: var(--dsw-font-family-mono, monospace);\n}\n.qc-settings-icon-btn {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n cursor: pointer;\n padding: 4px;\n display: inline-flex;\n align-items: center;\n border-radius: 6px;\n}\n.qc-settings-icon-btn:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n color: var(--dsw-alias-state-error-primary, #e5484d);\n}\n.qc-settings-add-btn {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 12px;\n padding: 4px 10px;\n cursor: pointer;\n}\n.qc-settings-add-btn:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n";
|
|
705
|
+
const tagId$1 = "@fonlan/dsh-quick-commands/settings-section.css#d7004149";
|
|
706
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
707
|
+
const tag = document.createElement("style");
|
|
708
|
+
tag.dataset.plugin = "@fonlan/dsh-quick-commands";
|
|
709
|
+
tag.dataset.pluginCss = tagId$1;
|
|
710
|
+
tag.textContent = css$1;
|
|
711
|
+
document.head.appendChild(tag);
|
|
712
|
+
}
|
|
713
|
+
//#endregion
|
|
714
|
+
//#region src/client/settings-section.tsx
|
|
673
715
|
/**
|
|
674
|
-
* Quick-commands settings
|
|
675
|
-
* header (title + description + chevron) collapsed by default, body reveals
|
|
676
|
-
* the workspace × command-set editor and the popup anchor preference. Body
|
|
677
|
-
* edits instant-save per change; partial rows (empty name/command) persist
|
|
678
|
-
* while the user types.
|
|
716
|
+
* Quick-commands settings page (设置 → 侧栏「快捷命令」).
|
|
679
717
|
*
|
|
680
|
-
*
|
|
681
|
-
*
|
|
718
|
+
* Registers into the "settings.section" list slot, so the page owns one entry
|
|
719
|
+
* in the settings sidebar and renders in the settings panel's content column.
|
|
720
|
+
* The header is static (title + one-line summary) over the always-visible
|
|
721
|
+
* body: the workspace / command-set editor and the popup anchor preference.
|
|
722
|
+
* Body edits instant-save per change; partial rows (empty name/command)
|
|
723
|
+
* persist while the user types.
|
|
682
724
|
*/
|
|
683
|
-
function
|
|
684
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
725
|
+
function QuickCommandsSettingsSection({ t }) {
|
|
685
726
|
const [state, setState] = (0, react.useState)({
|
|
686
727
|
loaded: false,
|
|
687
728
|
entries: [],
|
|
@@ -775,26 +816,19 @@ window.__ModuleLoader__.load({
|
|
|
775
816
|
}
|
|
776
817
|
};
|
|
777
818
|
const title = t("cardTitle");
|
|
778
|
-
const description = t("
|
|
779
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
780
|
-
className: "qc-
|
|
781
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
children: title
|
|
792
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
793
|
-
className: "qc-settings-description",
|
|
794
|
-
children: description
|
|
795
|
-
})]
|
|
796
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "qc-settings-chevron" + (open ? " qc-settings-chevron-open" : "") })]
|
|
797
|
-
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
819
|
+
const description = t("sectionSub");
|
|
820
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
821
|
+
className: "qc-page",
|
|
822
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
823
|
+
className: "qc-page-head",
|
|
824
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
825
|
+
className: "qc-page-title",
|
|
826
|
+
children: title
|
|
827
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
828
|
+
className: "qc-page-sub",
|
|
829
|
+
children: description
|
|
830
|
+
})]
|
|
831
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
798
832
|
className: "qc-settings-body",
|
|
799
833
|
children: [
|
|
800
834
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -930,9 +964,9 @@ window.__ModuleLoader__.load({
|
|
|
930
964
|
runPollFailed: "输出读取失败",
|
|
931
965
|
runLost: "运行记录已失效",
|
|
932
966
|
runNotFound: "工作区或命令未找到",
|
|
967
|
+
settingsTitle: "快捷命令",
|
|
933
968
|
cardTitle: "快速命令",
|
|
934
|
-
|
|
935
|
-
collapseOrExpand: "展开 / 收起",
|
|
969
|
+
sectionSub: "为每个工作区配置常用命令,会话头部一键运行",
|
|
936
970
|
cardIntro: "为每个工作区配置常用命令,运行于工作区目录。支持占位符:{workspace} 工作区路径,{cwd} 会话目录,{title} 工作区标题。",
|
|
937
971
|
cardWorkspaces: "工作区",
|
|
938
972
|
cardNoWorkspaces: "没有工作区。请在侧边栏先添加工作区。",
|
|
@@ -975,9 +1009,9 @@ window.__ModuleLoader__.load({
|
|
|
975
1009
|
runPollFailed: "Failed to read output",
|
|
976
1010
|
runLost: "Run record expired",
|
|
977
1011
|
runNotFound: "Workspace or command not found",
|
|
1012
|
+
settingsTitle: "Quick Commands",
|
|
978
1013
|
cardTitle: "Quick commands",
|
|
979
|
-
|
|
980
|
-
collapseOrExpand: "Expand / collapse",
|
|
1014
|
+
sectionSub: "Configure common commands per workspace, run them from the session header",
|
|
981
1015
|
cardIntro: "Configure common commands per workspace; they run in the workspace directory. Placeholders: {workspace} workspace path, {cwd} session cwd, {title} workspace title.",
|
|
982
1016
|
cardWorkspaces: "Workspaces",
|
|
983
1017
|
cardNoWorkspaces: "No workspaces. Add one in the sidebar first.",
|
|
@@ -994,8 +1028,8 @@ window.__ModuleLoader__.load({
|
|
|
994
1028
|
};
|
|
995
1029
|
//#endregion
|
|
996
1030
|
//#region \0dsh-css:/home/runner/work/dsh-quick-commands/dsh-quick-commands/src/client/quick-commands.css.mjs
|
|
997
|
-
const css = "/* @fonlan/dsh-quick-commands client styles.\n Uses theme CSS variables (--dsw-alias-*); falls back to neutral literals.\n Constraint: this deployment's theme defines only these text tokens —\n label-primary, label-secondary, label-tertiary, label-dimmed. READABLE\n description copy uses label-secondary / label-tertiary (never label-dimmed,\n which is intentionally near-invisible and reserved for disabled states). */\n\n/* ── header button (icon-only, left of Session log pill) ─────────────────── */\n.qc-head-wrap {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n/* Sized to DSH built-in header buttons (e.g. the open-in-app split button on\n the left): 26px tall pill, 13px radius, hairline border-l4, hover uses the\n shared --dsw-alias-interactive-bg-hover token. */\n.qc-head-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: 0.5px solid var(--dsw-alias-border-l4, rgba(128, 128, 128, 0.35));\n border-radius: 13px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n cursor: pointer;\n padding: 0;\n}\n.qc-head-btn:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent));\n}\n.qc-head-btn:disabled {\n color: var(--dsw-alias-label-dimmed, #666);\n cursor: not-allowed;\n}\n.qc-head-btn svg {\n flex: none;\n}\n\n/* ── command menu popover (anchored under the button) ────────────────────── */\n.qc-menu {\n position: absolute;\n top: calc(100% + 6px);\n right: 12px;\n min-width: 280px;\n max-height: 420px;\n overflow: auto;\n background: var(--dsw-alias-bg-overlay, #1b1b1c);\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);\n z-index: 50;\n padding: 6px;\n display: flex;\n flex-direction: column;\n}\n.qc-menu-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 8px 6px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-menu-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-menu-ws {\n flex: 1;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: right;\n margin-right: 2px;\n}\n.qc-menu-remote {\n flex: 0 0 auto;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n padding: 2px 5px;\n border-radius: 5px;\n color: var(--dsw-alias-fg-on-accent, #fff);\n background: var(--dsw-alias-accent, #4a8dff);\n letter-spacing: 0.04em;\n}\n.qc-menu-close {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n cursor: pointer;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n}\n.qc-menu-empty {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n padding: 10px 8px;\n margin: 0;\n}\n.qc-menu-item {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n text-align: left;\n border: 0;\n background: transparent;\n border-radius: 8px;\n padding: 6px 8px;\n cursor: pointer;\n width: 100%;\n}\n.qc-menu-item:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n.qc-menu-item:disabled {\n opacity: 0.6;\n cursor: wait;\n}\n.qc-menu-item-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-menu-item-cmd {\n font-family: var(--dsw-font-family-mono, monospace);\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n}\n\n/* ── live output popup ───────────────────────────────────────────────────── */\n.qc-popup {\n position: relative; /* anchors the absolutely-positioned resize grip */\n display: flex;\n flex-direction: column;\n background: var(--dsw-alias-bg-overlay, #1b1b1c);\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 12px;\n box-shadow: 0 12px 36px rgba(0, 0, 0, 0.45);\n z-index: 60;\n overflow: hidden;\n}\n.qc-popup-corner {\n position: fixed;\n right: 20px;\n bottom: 20px;\n width: 520px;\n max-width: calc(100vw - 40px);\n height: 340px;\n max-height: calc(100vh - 120px);\n}\n.qc-popup-button {\n position: absolute;\n top: calc(100% + 6px);\n right: 12px;\n width: 440px;\n max-width: calc(100vw - 40px);\n height: 300px;\n max-height: calc(100vh - 120px);\n}\n.qc-popup-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-popup-title {\n flex: 1;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.qc-popup-cmd {\n font-family: var(--dsw-font-family-mono, monospace);\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 40%;\n}\n.qc-popup-remote {\n flex: 0 0 auto;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n padding: 3px 6px;\n border-radius: 6px;\n color: var(--dsw-alias-fg-on-accent, #fff);\n background: var(--dsw-alias-accent, #4a8dff);\n letter-spacing: 0.04em;\n}\n.qc-popup-icon {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n cursor: pointer;\n padding: 4px;\n display: inline-flex;\n align-items: center;\n border-radius: 6px;\n}\n.qc-popup-icon:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n.qc-popup-icon:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.qc-popup-kill {\n color: var(--dsw-alias-state-error-primary, #e5484d);\n}\n.qc-popup-confirm {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n padding: 8px 10px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #f5a623) 12%, transparent);\n color: var(--dsw-alias-label-primary, #f2f2f2);\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-popup-btn {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 12px;\n padding: 4px 10px;\n cursor: pointer;\n}\n.qc-popup-btn:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n.qc-popup-btn-plain {\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n}\n.qc-popup-tabs {\n display: flex;\n gap: 2px;\n padding: 6px 10px 0;\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-popup-tab {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n font-size: 12px;\n padding: 5px 10px;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n}\n.qc-popup-tab-active {\n color: var(--dsw-alias-label-primary, #f2f2f2);\n border-bottom-color: var(--dsw-alias-brand-primary, #4a9eff);\n}\n.qc-popup-output {\n flex: 1;\n overflow: auto;\n padding: 8px 10px;\n font-family: var(--dsw-font-family-mono, monospace);\n font-size: 12px;\n line-height: 1.5;\n}\n.qc-popup-pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-popup-empty {\n margin: 0;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n font-size: 12px;\n}\n.qc-popup-error {\n margin: 0 0 6px;\n color: var(--dsw-alias-state-error-primary, #e5484d);\n font-size: 12px;\n}\n.qc-popup-foot {\n display: flex;\n align-items: center;\n padding: 6px 10px;\n border-top: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n font-size: 11px;\n}\n.qc-popup-state {\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n}\n\n/* Resize handles. The popup pins its RIGHT edge (both anchors); width is\n always dragged from the LEFT border. Height is dragged from the TOP border\n (corner anchor, bottom pinned) or the BOTTOM border (button anchor, top\n pinned below the button). The free corner (top-left / bottom-left) carries\n the diagonal grip. Pointer capture lives in header.tsx; touch-action:none\n lets touch drags resize instead of scrolling. */\n.qc-popup-resize {\n position: absolute;\n z-index: 3;\n touch-action: none;\n user-select: none;\n}\n.qc-popup-resize-left {\n left: 0;\n top: 10px;\n bottom: 10px;\n width: 7px;\n cursor: ew-resize;\n}\n.qc-popup-resize-top {\n top: 0;\n left: 10px;\n right: 10px;\n height: 7px;\n cursor: ns-resize;\n}\n.qc-popup-resize-bottom {\n bottom: 0;\n left: 10px;\n right: 10px;\n height: 7px;\n cursor: ns-resize;\n}\n.qc-popup-resize-left:hover,\n.qc-popup-resize-top:hover,\n.qc-popup-resize-bottom:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 14%, transparent);\n}\n.qc-popup-resize-corner {\n width: 22px;\n height: 22px;\n z-index: 4;\n display: flex;\n padding: 3px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n opacity: 0.6;\n transition: opacity .15s ease;\n}\n.qc-popup-resize-corner-tl {\n top: 0;\n left: 0;\n cursor: nwse-resize;\n align-items: flex-start;\n justify-content: flex-start;\n}\n.qc-popup-resize-corner-bl {\n bottom: 0;\n left: 0;\n cursor: nesw-resize;\n align-items: flex-end;\n justify-content: flex-start;\n}\n.qc-popup-resize-corner:hover,\n.qc-popup-resize-corner:active {\n opacity: 1;\n}\n\n/* ── settings card (设置 → 插件配置) — mirrors official PluginCard disclosure ── */\n.qc-settings-card {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n background: var(--dsw-alias-bg-layer-2, #1f1f21);\n border-radius: 12px;\n list-style: none;\n transition: border-color .16s, background .16s;\n}\n.qc-settings-card:hover {\n border-color: var(--dsw-alias-label-secondary, #cfd3d6);\n}\n.qc-settings-card-open {\n background: var(--dsw-alias-bg-layer-1, #27272a);\n border-color: var(--dsw-alias-label-secondary, #cfd3d6);\n}\n.qc-settings-header {\n appearance: none;\n width: 100%;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n background: transparent;\n border: 0;\n border-radius: 12px;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n display: flex;\n}\n.qc-settings-header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary, #4a9eff);\n outline-offset: -2px;\n}\n.qc-settings-head-text {\n flex-direction: column;\n flex: 1;\n gap: 4px;\n min-width: 0;\n display: flex;\n}\n.qc-settings-name {\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n}\n.qc-settings-description {\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n font-size: 13px;\n line-height: 1.5;\n}\n.qc-settings-chevron {\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n flex: none;\n transition: transform .16s;\n}\n.qc-settings-chevron-open {\n transform: rotate(180deg);\n}\n.qc-settings-body {\n border-top: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n margin: 0 16px;\n padding-bottom: 12px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n.qc-settings-intro {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n margin: 8px 0 0;\n line-height: 1.5;\n}\n.qc-settings-anchor {\n display: flex;\n align-items: center;\n gap: 12px;\n font-size: 12px;\n}\n.qc-settings-anchor-label {\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-weight: 500;\n}\n.qc-settings-radio {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n cursor: pointer;\n}\n.qc-settings-error {\n font-size: 12px;\n color: var(--dsw-alias-state-error-primary, #e5484d);\n}\n.qc-settings-empty {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n margin: 0;\n}\n.qc-settings-workspace {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 10px;\n padding: 10px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n.qc-settings-workspace-head {\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n.qc-settings-workspace-title {\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-settings-workspace-path {\n font-size: 11px;\n font-family: var(--dsw-font-family-mono, monospace);\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.qc-settings-no-commands {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n margin: 0;\n}\n.qc-settings-command {\n display: flex;\n gap: 6px;\n align-items: center;\n}\n.qc-settings-field {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1, #2a2a2d);\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 12px;\n padding: 6px 8px;\n outline: none;\n}\n.qc-settings-field::placeholder {\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n}\n.qc-settings-field:focus {\n border-color: var(--dsw-alias-brand-primary, #4a9eff);\n}\n.qc-settings-field-name {\n width: 160px;\n flex: none;\n}\n.qc-settings-field-command {\n flex: 1;\n font-family: var(--dsw-font-family-mono, monospace);\n}\n.qc-settings-icon-btn {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n cursor: pointer;\n padding: 4px;\n display: inline-flex;\n align-items: center;\n border-radius: 6px;\n}\n.qc-settings-icon-btn:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n color: var(--dsw-alias-state-error-primary, #e5484d);\n}\n.qc-settings-add-btn {\n align-self: flex-start;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 12px;\n padding: 4px 10px;\n cursor: pointer;\n}\n.qc-settings-add-btn:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n\n/* ── mobile fallback (dsh-mobile-nav) ──────────────────────────────────────\n The dsh-mobile-nav plugin (≤1023px) hides the ENTIRE header utilities seat\n with `[data-mobile-nav=\"frame\"] [data-phase] header > :first-child >\n :last-child { display: none !important; }`, because the seat used to hold\n only the session-log-export capsule (which it relocates to the drawer\n footer). Our quick-run ▶ button rides that same seat, so it vanished on\n phones.\n\n Re-show ONLY our button: restore the seat's flex layout (out-ranking the\n host rule via `:has(.qc-head-wrap)` specificity (0,5,1) > (0,4,1)) and keep\n every other seat occupant hidden (the session-log capsule stays relocated\n to the drawer footer). Desktop (no [data-mobile-nav]) is untouched. */\n[data-mobile-nav=\"frame\"] [data-phase] header > :first-child > :last-child:has(.qc-head-wrap) {\n display: flex !important;\n margin-left: 4px !important;\n gap: 0 !important;\n}\n[data-mobile-nav=\"frame\"] [data-phase] header > :first-child > :last-child:has(.qc-head-wrap)\n [data-slot=\"conversation.session.header.utilities\"] > :not(.qc-head-wrap) {\n display: none !important;\n}\n";
|
|
998
|
-
const tagId = "@fonlan/dsh-quick-commands/quick-commands.css#
|
|
1031
|
+
const css = "/* @fonlan/dsh-quick-commands client styles.\n Uses theme CSS variables (--dsw-alias-*); falls back to neutral literals.\n Constraint: this deployment's theme defines only these text tokens —\n label-primary, label-secondary, label-tertiary, label-dimmed. READABLE\n description copy uses label-secondary / label-tertiary (never label-dimmed,\n which is intentionally near-invisible and reserved for disabled states). */\n\n/* ── header button (icon-only, left of Session log pill) ─────────────────── */\n.qc-head-wrap {\n position: relative;\n display: inline-flex;\n align-items: center;\n}\n/* Sized to DSH built-in header buttons (e.g. the open-in-app split button on\n the left): 26px tall pill, 13px radius, hairline border-l4, hover uses the\n shared --dsw-alias-interactive-bg-hover token. */\n.qc-head-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 26px;\n height: 26px;\n border: 0.5px solid var(--dsw-alias-border-l4, rgba(128, 128, 128, 0.35));\n border-radius: 13px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n cursor: pointer;\n padding: 0;\n}\n.qc-head-btn:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover, color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent));\n}\n.qc-head-btn:disabled {\n color: var(--dsw-alias-label-dimmed, #666);\n cursor: not-allowed;\n}\n.qc-head-btn svg {\n flex: none;\n}\n\n/* ── command menu popover (anchored under the button) ────────────────────── */\n.qc-menu {\n position: absolute;\n top: calc(100% + 6px);\n right: 12px;\n min-width: 280px;\n max-height: 420px;\n overflow: auto;\n background: var(--dsw-alias-bg-overlay, #1b1b1c);\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 10px;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);\n z-index: 50;\n padding: 6px;\n display: flex;\n flex-direction: column;\n}\n.qc-menu-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 8px 6px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-menu-title {\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-menu-ws {\n flex: 1;\n font-size: 11px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n text-align: right;\n margin-right: 2px;\n}\n.qc-menu-remote {\n flex: 0 0 auto;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n padding: 2px 5px;\n border-radius: 5px;\n color: var(--dsw-alias-fg-on-accent, #fff);\n background: var(--dsw-alias-accent, #4a8dff);\n letter-spacing: 0.04em;\n}\n.qc-menu-close {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n cursor: pointer;\n padding: 2px;\n display: inline-flex;\n align-items: center;\n}\n.qc-menu-empty {\n font-size: 12px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n padding: 10px 8px;\n margin: 0;\n}\n.qc-menu-item {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 2px;\n text-align: left;\n border: 0;\n background: transparent;\n border-radius: 8px;\n padding: 6px 8px;\n cursor: pointer;\n width: 100%;\n}\n.qc-menu-item:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n.qc-menu-item:disabled {\n opacity: 0.6;\n cursor: wait;\n}\n.qc-menu-item-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-menu-item-cmd {\n font-family: var(--dsw-font-family-mono, monospace);\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 100%;\n}\n\n/* ── live output popup ───────────────────────────────────────────────────── */\n.qc-popup {\n position: relative; /* anchors the absolutely-positioned resize grip */\n display: flex;\n flex-direction: column;\n background: var(--dsw-alias-bg-overlay, #1b1b1c);\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 12px;\n box-shadow: 0 12px 36px rgba(0, 0, 0, 0.45);\n z-index: 60;\n overflow: hidden;\n}\n.qc-popup-corner {\n position: fixed;\n right: 20px;\n bottom: 20px;\n width: 520px;\n max-width: calc(100vw - 40px);\n height: 340px;\n max-height: calc(100vh - 120px);\n}\n.qc-popup-button {\n position: absolute;\n top: calc(100% + 6px);\n right: 12px;\n width: 440px;\n max-width: calc(100vw - 40px);\n height: 300px;\n max-height: calc(100vh - 120px);\n}\n.qc-popup-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-popup-title {\n flex: 1;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.qc-popup-cmd {\n font-family: var(--dsw-font-family-mono, monospace);\n font-size: 11px;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n max-width: 40%;\n}\n.qc-popup-remote {\n flex: 0 0 auto;\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n padding: 3px 6px;\n border-radius: 6px;\n color: var(--dsw-alias-fg-on-accent, #fff);\n background: var(--dsw-alias-accent, #4a8dff);\n letter-spacing: 0.04em;\n}\n.qc-popup-icon {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n cursor: pointer;\n padding: 4px;\n display: inline-flex;\n align-items: center;\n border-radius: 6px;\n}\n.qc-popup-icon:hover:not(:disabled) {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n.qc-popup-icon:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n.qc-popup-kill {\n color: var(--dsw-alias-state-error-primary, #e5484d);\n}\n.qc-popup-confirm {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n padding: 8px 10px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary, #f5a623) 12%, transparent);\n color: var(--dsw-alias-label-primary, #f2f2f2);\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-popup-btn {\n border: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.35));\n border-radius: 8px;\n background: transparent;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n font-size: 12px;\n padding: 4px 10px;\n cursor: pointer;\n}\n.qc-popup-btn:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 10%, transparent);\n}\n.qc-popup-btn-plain {\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n}\n.qc-popup-tabs {\n display: flex;\n gap: 2px;\n padding: 6px 10px 0;\n border-bottom: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n}\n.qc-popup-tab {\n border: 0;\n background: transparent;\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n font-size: 12px;\n padding: 5px 10px;\n cursor: pointer;\n border-bottom: 2px solid transparent;\n}\n.qc-popup-tab-active {\n color: var(--dsw-alias-label-primary, #f2f2f2);\n border-bottom-color: var(--dsw-alias-brand-primary, #4a9eff);\n}\n.qc-popup-output {\n flex: 1;\n overflow: auto;\n padding: 8px 10px;\n font-family: var(--dsw-font-family-mono, monospace);\n font-size: 12px;\n line-height: 1.5;\n}\n.qc-popup-pre {\n margin: 0;\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--dsw-alias-label-primary, #f2f2f2);\n}\n.qc-popup-empty {\n margin: 0;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n font-size: 12px;\n}\n.qc-popup-error {\n margin: 0 0 6px;\n color: var(--dsw-alias-state-error-primary, #e5484d);\n font-size: 12px;\n}\n.qc-popup-foot {\n display: flex;\n align-items: center;\n padding: 6px 10px;\n border-top: 1px solid var(--dsw-alias-border-l2, rgba(128, 128, 128, 0.25));\n font-size: 11px;\n}\n.qc-popup-state {\n color: var(--dsw-alias-label-secondary, #cfd3d6);\n}\n\n/* Resize handles. The popup pins its RIGHT edge (both anchors); width is\n always dragged from the LEFT border. Height is dragged from the TOP border\n (corner anchor, bottom pinned) or the BOTTOM border (button anchor, top\n pinned below the button). The free corner (top-left / bottom-left) carries\n the diagonal grip. Pointer capture lives in header.tsx; touch-action:none\n lets touch drags resize instead of scrolling. */\n.qc-popup-resize {\n position: absolute;\n z-index: 3;\n touch-action: none;\n user-select: none;\n}\n.qc-popup-resize-left {\n left: 0;\n top: 10px;\n bottom: 10px;\n width: 7px;\n cursor: ew-resize;\n}\n.qc-popup-resize-top {\n top: 0;\n left: 10px;\n right: 10px;\n height: 7px;\n cursor: ns-resize;\n}\n.qc-popup-resize-bottom {\n bottom: 0;\n left: 10px;\n right: 10px;\n height: 7px;\n cursor: ns-resize;\n}\n.qc-popup-resize-left:hover,\n.qc-popup-resize-top:hover,\n.qc-popup-resize-bottom:hover {\n background: color-mix(in srgb, var(--dsw-alias-label-primary, #f2f2f2) 14%, transparent);\n}\n.qc-popup-resize-corner {\n width: 22px;\n height: 22px;\n z-index: 4;\n display: flex;\n padding: 3px;\n color: var(--dsw-alias-label-tertiary, #adb2b8);\n opacity: 0.6;\n transition: opacity .15s ease;\n}\n.qc-popup-resize-corner-tl {\n top: 0;\n left: 0;\n cursor: nwse-resize;\n align-items: flex-start;\n justify-content: flex-start;\n}\n.qc-popup-resize-corner-bl {\n bottom: 0;\n left: 0;\n cursor: nesw-resize;\n align-items: flex-end;\n justify-content: flex-start;\n}\n.qc-popup-resize-corner:hover,\n.qc-popup-resize-corner:active {\n opacity: 1;\n}\n\n/* ── mobile fallback (dsh-mobile-nav) ──────────────────────────────────────\n The dsh-mobile-nav plugin (≤1023px) hides the ENTIRE header utilities seat\n with `[data-mobile-nav=\"frame\"] [data-phase] header > :first-child >\n :last-child { display: none !important; }`, because the seat used to hold\n only the session-log-export capsule (which it relocates to the drawer\n footer). Our quick-run ▶ button rides that same seat, so it vanished on\n phones.\n\n Re-show ONLY our button: restore the seat's flex layout (out-ranking the\n host rule via `:has(.qc-head-wrap)` specificity (0,5,1) > (0,4,1)) and keep\n every other seat occupant hidden (the session-log capsule stays relocated\n to the drawer footer). Desktop (no [data-mobile-nav]) is untouched. */\n[data-mobile-nav=\"frame\"] [data-phase] header > :first-child > :last-child:has(.qc-head-wrap) {\n display: flex !important;\n margin-left: 4px !important;\n gap: 0 !important;\n}\n[data-mobile-nav=\"frame\"] [data-phase] header > :first-child > :last-child:has(.qc-head-wrap)\n [data-slot=\"conversation.session.header.utilities\"] > :not(.qc-head-wrap) {\n display: none !important;\n}\n\n/* ── viewport-level command menu ─────────────────────────────────────────\n The phone shell's nav rail creates a sibling stacking context and its\n center column can clip header descendants. The React menu therefore mounts\n in document.body; these styles deliberately detach it from that header\n context. Pixel coordinates (including the 56px rail exclusion) are set by\n positionMenu() in header.tsx. */\n.qc-menu-portal {\n position: fixed;\n right: auto;\n z-index: 1100;\n box-sizing: border-box;\n max-height: min(420px, calc(100dvh - 72px));\n}\n\n@media (max-width: 767px) {\n /* A 44px target lets a fingertip reliably activate both the stop and close\n buttons without changing the desktop header density. */\n body[data-dsh-mobile] .qc-popup-icon,\n [data-mobile-nav=\"frame\"] .qc-popup-icon {\n min-width: 44px;\n min-height: 44px;\n justify-content: center;\n }\n}";
|
|
1032
|
+
const tagId = "@fonlan/dsh-quick-commands/quick-commands.css#b65a4ce4";
|
|
999
1033
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1000
1034
|
const tag = document.createElement("style");
|
|
1001
1035
|
tag.dataset.plugin = "@fonlan/dsh-quick-commands";
|
|
@@ -1013,13 +1047,15 @@ window.__ModuleLoader__.load({
|
|
|
1013
1047
|
zh,
|
|
1014
1048
|
en
|
|
1015
1049
|
}), "quick-commands: dictionaries");
|
|
1016
|
-
ctx.locale.bind(LOCALE_NS);
|
|
1050
|
+
const t = ctx.locale.bind(LOCALE_NS);
|
|
1017
1051
|
const slots = ctx.slots;
|
|
1018
|
-
slots.inject("settings.
|
|
1019
|
-
name: "settings.
|
|
1020
|
-
|
|
1052
|
+
slots.inject("settings.section", () => slots.register({
|
|
1053
|
+
name: "settings.section",
|
|
1054
|
+
id: "quick-commands",
|
|
1055
|
+
order: 330,
|
|
1056
|
+
label: () => t("settingsTitle"),
|
|
1021
1057
|
locale: LOCALE_NS
|
|
1022
|
-
},
|
|
1058
|
+
}, QuickCommandsSettingsSection));
|
|
1023
1059
|
slots.inject("conversation.session.header.utilities", () => slots.register({
|
|
1024
1060
|
name: "conversation.session.header.utilities",
|
|
1025
1061
|
id: "quick-commands",
|