@fonlan/dsh-quick-commands 0.1.5 → 0.1.7
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 +182 -52
- package/lib/client.js.map +1 -1
- package/lib/index.js +48 -23
- package/lib/types/index.d.ts +4 -2
- package/lib/types/server/settings.d.ts +14 -4
- package/lib/types/shared/contract.d.ts +7 -0
- package/package.json +17 -16
- package/src/client/header.tsx +15 -12
- package/src/client/icons.tsx +106 -0
- package/src/client/index.tsx +15 -14
- package/src/client/locales.ts +7 -7
- package/src/client/quick-commands.css +1 -191
- package/src/client/settings-section.css +163 -0
- package/src/client/settings-section.tsx +192 -0
- package/src/index.ts +11 -4
- package/src/server/settings.ts +74 -29
- package/src/shared/contract.ts +10 -0
- package/src/client/settings-card.tsx +0 -209
package/lib/client.js
CHANGED
|
@@ -4,10 +4,124 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
//#region \0rolldown/runtime.js
|
|
8
|
+
var __create = Object.create;
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
16
|
+
key = keys[i];
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
18
|
+
get: ((k) => from[k]).bind(null, key),
|
|
19
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
25
|
+
value: mod,
|
|
26
|
+
enumerable: true
|
|
27
|
+
}) : target, mod));
|
|
28
|
+
//#endregion
|
|
7
29
|
let react = require("react");
|
|
8
30
|
let react_dom = require("react-dom");
|
|
9
31
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
32
|
+
_deepseek_ai_dsh_client_ui_primitives = __toESM(_deepseek_ai_dsh_client_ui_primitives, 1);
|
|
10
33
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
34
|
+
//#region src/client/icons.tsx
|
|
35
|
+
const ICON_EXPORTS = _deepseek_ai_dsh_client_ui_primitives;
|
|
36
|
+
/** First export name that exists on the running host, else the bundled artwork. */
|
|
37
|
+
function pickIcon(exportNames, fallback) {
|
|
38
|
+
for (const name of exportNames) {
|
|
39
|
+
const candidate = ICON_EXPORTS[name];
|
|
40
|
+
if (typeof candidate === "function") return candidate;
|
|
41
|
+
}
|
|
42
|
+
return fallback;
|
|
43
|
+
}
|
|
44
|
+
/** Wrap one locally drawn 16×16 artwork as an icon component. */
|
|
45
|
+
function localIcon(children) {
|
|
46
|
+
return function LocalIcon({ size = 16, className }) {
|
|
47
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
48
|
+
width: size,
|
|
49
|
+
height: size,
|
|
50
|
+
viewBox: "0 0 16 16",
|
|
51
|
+
fill: "none",
|
|
52
|
+
className,
|
|
53
|
+
"aria-hidden": "true",
|
|
54
|
+
children
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Run/play glyph for the header quick-run button. */
|
|
59
|
+
const PlayIcon = pickIcon([
|
|
60
|
+
"IconPlayOutlineRegular",
|
|
61
|
+
"IconPlayOutlineMedium",
|
|
62
|
+
"IconPlayOutline16"
|
|
63
|
+
], localIcon(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
64
|
+
cx: "8",
|
|
65
|
+
cy: "8",
|
|
66
|
+
r: "6.5",
|
|
67
|
+
stroke: "currentColor"
|
|
68
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
69
|
+
d: "M6.6 5.9 10.4 8l-3.8 2.1Z",
|
|
70
|
+
fill: "currentColor"
|
|
71
|
+
})] })));
|
|
72
|
+
/** Dismiss glyph (menu header and popup close). */
|
|
73
|
+
const CloseIcon = pickIcon([
|
|
74
|
+
"IconCloseOutlineRegular",
|
|
75
|
+
"IconCloseOutlineMedium",
|
|
76
|
+
"IconCloseOutline16"
|
|
77
|
+
], localIcon(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
78
|
+
d: "M4.2 4.2 11.8 11.8M11.8 4.2 4.2 11.8",
|
|
79
|
+
stroke: "currentColor",
|
|
80
|
+
strokeWidth: "1.2",
|
|
81
|
+
strokeLinecap: "round"
|
|
82
|
+
})));
|
|
83
|
+
/** Stop/kill glyph for a running command. */
|
|
84
|
+
const StopIcon = pickIcon([
|
|
85
|
+
"IconStopFillRegular",
|
|
86
|
+
"IconStopFillMedium",
|
|
87
|
+
"IconStopFill16"
|
|
88
|
+
], localIcon(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
89
|
+
x: "4.6",
|
|
90
|
+
y: "4.6",
|
|
91
|
+
width: "6.8",
|
|
92
|
+
height: "6.8",
|
|
93
|
+
rx: "1.6",
|
|
94
|
+
fill: "currentColor"
|
|
95
|
+
})));
|
|
96
|
+
/** Add-row glyph in the settings page. */
|
|
97
|
+
const PlusIcon = pickIcon([
|
|
98
|
+
"IconPlusOutlineRegular",
|
|
99
|
+
"IconPlusOutlineMedium",
|
|
100
|
+
"IconPlusOutline16"
|
|
101
|
+
], localIcon(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
102
|
+
d: "M8 3.4v9.2M3.4 8h9.2",
|
|
103
|
+
stroke: "currentColor",
|
|
104
|
+
strokeWidth: "1.2",
|
|
105
|
+
strokeLinecap: "round"
|
|
106
|
+
})));
|
|
107
|
+
/** Remove-command glyph in the settings page. */
|
|
108
|
+
const TrashIcon = pickIcon([
|
|
109
|
+
"IconTrashOutlineRegular",
|
|
110
|
+
"IconTrashOutlineMedium",
|
|
111
|
+
"IconTrashOutline16"
|
|
112
|
+
], localIcon(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
113
|
+
d: "M3.4 4.4h9.2",
|
|
114
|
+
stroke: "currentColor",
|
|
115
|
+
strokeWidth: "1.2",
|
|
116
|
+
strokeLinecap: "round"
|
|
117
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
118
|
+
d: "M6.4 4.4V3.1h3.2v1.3M4.9 4.4l.6 8.5h5l.6-8.5",
|
|
119
|
+
stroke: "currentColor",
|
|
120
|
+
strokeWidth: "1.2",
|
|
121
|
+
strokeLinecap: "round",
|
|
122
|
+
strokeLinejoin: "round"
|
|
123
|
+
})] })));
|
|
124
|
+
//#endregion
|
|
11
125
|
//#region src/client/api.ts
|
|
12
126
|
var QuickCommandsApiError = class extends Error {
|
|
13
127
|
code;
|
|
@@ -161,6 +275,17 @@ window.__ModuleLoader__.load({
|
|
|
161
275
|
});
|
|
162
276
|
}
|
|
163
277
|
//#endregion
|
|
278
|
+
//#region src/shared/contract.ts
|
|
279
|
+
/**
|
|
280
|
+
* The document's anchor as a usable literal. Wire data is defensive by
|
|
281
|
+
* contract: an older host can still answer with schemastery's empty-object
|
|
282
|
+
* resolution (`{}`) for this field, which must never reach a CSS class name or
|
|
283
|
+
* a radio `checked` comparison.
|
|
284
|
+
*/
|
|
285
|
+
function anchorOf(value) {
|
|
286
|
+
return value === "button" ? "button" : "corner";
|
|
287
|
+
}
|
|
288
|
+
//#endregion
|
|
164
289
|
//#region src/client/header.tsx
|
|
165
290
|
/**
|
|
166
291
|
* Session-header quick-command surface: the ▶ play icon button (left of the
|
|
@@ -206,9 +331,9 @@ window.__ModuleLoader__.load({
|
|
|
206
331
|
/** Resolve the current session's workspace from the standard kits. */
|
|
207
332
|
function useCurrentWorkspace(sessionId, useSessions, useWorkspaces) {
|
|
208
333
|
const row = useSessions?.((s) => s.byId[sessionId]);
|
|
209
|
-
const
|
|
334
|
+
const items = useWorkspaces?.((s) => s.items) ?? [];
|
|
210
335
|
return {
|
|
211
|
-
workspace: matchWorkspace(row?.cwd,
|
|
336
|
+
workspace: items.find((ws) => ws.sessionIds?.includes(sessionId) === true) ?? matchWorkspace(row?.cwd, items),
|
|
212
337
|
cwd: row?.cwd
|
|
213
338
|
};
|
|
214
339
|
}
|
|
@@ -270,7 +395,7 @@ window.__ModuleLoader__.load({
|
|
|
270
395
|
const own = entries.find((e) => e.workspaceId === workspace?.workspaceId);
|
|
271
396
|
setCommands(own === void 0 ? [] : own.commands);
|
|
272
397
|
setMenuRemoteHost(own?.remoteHost ?? null);
|
|
273
|
-
setAnchor(settings.popupAnchor);
|
|
398
|
+
setAnchor(anchorOf(settings.popupAnchor));
|
|
274
399
|
setPopupSize(settings.popupSize);
|
|
275
400
|
setMenuLoadError(null);
|
|
276
401
|
} catch (e) {
|
|
@@ -312,7 +437,7 @@ window.__ModuleLoader__.load({
|
|
|
312
437
|
onClick: () => {
|
|
313
438
|
popover.menuOpen ? closeMenu() : openMenu();
|
|
314
439
|
},
|
|
315
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
440
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlayIcon, { size: 15 })
|
|
316
441
|
}),
|
|
317
442
|
popover.menuOpen && workspace !== void 0 && menuPosition !== null && (0, react_dom.createPortal)(/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
318
443
|
ref: menuRef,
|
|
@@ -345,7 +470,7 @@ window.__ModuleLoader__.load({
|
|
|
345
470
|
className: "qc-menu-close",
|
|
346
471
|
"aria-label": t("runClose"),
|
|
347
472
|
onClick: closeMenu,
|
|
348
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
473
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 12 })
|
|
349
474
|
})
|
|
350
475
|
]
|
|
351
476
|
}),
|
|
@@ -582,7 +707,7 @@ window.__ModuleLoader__.load({
|
|
|
582
707
|
title: t("runKill"),
|
|
583
708
|
"aria-label": t("runKill"),
|
|
584
709
|
onClick: () => void kill(),
|
|
585
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
710
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StopIcon, { size: 14 })
|
|
586
711
|
}),
|
|
587
712
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
588
713
|
type: "button",
|
|
@@ -591,7 +716,7 @@ window.__ModuleLoader__.load({
|
|
|
591
716
|
"aria-label": t("runClose"),
|
|
592
717
|
onPointerUp: onClosePointerUp,
|
|
593
718
|
onClick: closeOutput,
|
|
594
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
719
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CloseIcon, { size: 14 })
|
|
595
720
|
})
|
|
596
721
|
]
|
|
597
722
|
}),
|
|
@@ -700,19 +825,29 @@ window.__ModuleLoader__.load({
|
|
|
700
825
|
});
|
|
701
826
|
}
|
|
702
827
|
//#endregion
|
|
703
|
-
//#region src/client/settings-
|
|
828
|
+
//#region \0dsh-css:/home/runner/work/dsh-quick-commands/dsh-quick-commands/src/client/settings-section.css.mjs
|
|
829
|
+
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";
|
|
830
|
+
const tagId$1 = "@fonlan/dsh-quick-commands/settings-section.css#d7004149";
|
|
831
|
+
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
832
|
+
const tag = document.createElement("style");
|
|
833
|
+
tag.dataset.plugin = "@fonlan/dsh-quick-commands";
|
|
834
|
+
tag.dataset.pluginCss = tagId$1;
|
|
835
|
+
tag.textContent = css$1;
|
|
836
|
+
document.head.appendChild(tag);
|
|
837
|
+
}
|
|
838
|
+
//#endregion
|
|
839
|
+
//#region src/client/settings-section.tsx
|
|
704
840
|
/**
|
|
705
|
-
* Quick-commands settings
|
|
706
|
-
* header (title + description + chevron) collapsed by default, body reveals
|
|
707
|
-
* the workspace × command-set editor and the popup anchor preference. Body
|
|
708
|
-
* edits instant-save per change; partial rows (empty name/command) persist
|
|
709
|
-
* while the user types.
|
|
841
|
+
* Quick-commands settings page (设置 → 侧栏「快捷命令」).
|
|
710
842
|
*
|
|
711
|
-
*
|
|
712
|
-
*
|
|
843
|
+
* Registers into the "settings.section" list slot, so the page owns one entry
|
|
844
|
+
* in the settings sidebar and renders in the settings panel's content column.
|
|
845
|
+
* The header is static (title + one-line summary) over the always-visible
|
|
846
|
+
* body: the workspace / command-set editor and the popup anchor preference.
|
|
847
|
+
* Body edits instant-save per change; partial rows (empty name/command)
|
|
848
|
+
* persist while the user types.
|
|
713
849
|
*/
|
|
714
|
-
function
|
|
715
|
-
const [open, setOpen] = (0, react.useState)(false);
|
|
850
|
+
function QuickCommandsSettingsSection({ t }) {
|
|
716
851
|
const [state, setState] = (0, react.useState)({
|
|
717
852
|
loaded: false,
|
|
718
853
|
entries: [],
|
|
@@ -726,7 +861,7 @@ window.__ModuleLoader__.load({
|
|
|
726
861
|
setState({
|
|
727
862
|
loaded: true,
|
|
728
863
|
entries,
|
|
729
|
-
anchor: settings.popupAnchor
|
|
864
|
+
anchor: anchorOf(settings.popupAnchor)
|
|
730
865
|
});
|
|
731
866
|
setError(null);
|
|
732
867
|
} catch (e) {
|
|
@@ -798,7 +933,7 @@ window.__ModuleLoader__.load({
|
|
|
798
933
|
const next = await quickApi.settingsSetAnchor(anchor);
|
|
799
934
|
setState((s) => ({
|
|
800
935
|
...s,
|
|
801
|
-
anchor: next.popupAnchor
|
|
936
|
+
anchor: anchorOf(next.popupAnchor)
|
|
802
937
|
}));
|
|
803
938
|
} catch (e) {
|
|
804
939
|
setError(e instanceof Error ? e.message : String(e));
|
|
@@ -806,26 +941,19 @@ window.__ModuleLoader__.load({
|
|
|
806
941
|
}
|
|
807
942
|
};
|
|
808
943
|
const title = t("cardTitle");
|
|
809
|
-
const description = t("
|
|
810
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
811
|
-
className: "qc-
|
|
812
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
children: title
|
|
823
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
824
|
-
className: "qc-settings-description",
|
|
825
|
-
children: description
|
|
826
|
-
})]
|
|
827
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: "qc-settings-chevron" + (open ? " qc-settings-chevron-open" : "") })]
|
|
828
|
-
}), open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
944
|
+
const description = t("sectionSub");
|
|
945
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
946
|
+
className: "qc-page",
|
|
947
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
948
|
+
className: "qc-page-head",
|
|
949
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
950
|
+
className: "qc-page-title",
|
|
951
|
+
children: title
|
|
952
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
953
|
+
className: "qc-page-sub",
|
|
954
|
+
children: description
|
|
955
|
+
})]
|
|
956
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
829
957
|
className: "qc-settings-body",
|
|
830
958
|
children: [
|
|
831
959
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -911,7 +1039,7 @@ window.__ModuleLoader__.load({
|
|
|
911
1039
|
"aria-label": t("cardRemove"),
|
|
912
1040
|
title: t("cardRemove"),
|
|
913
1041
|
onClick: () => removeCommand(entry.workspaceId, index),
|
|
914
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1042
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 14 })
|
|
915
1043
|
})
|
|
916
1044
|
]
|
|
917
1045
|
}, `${entry.workspaceId}-${index}`)),
|
|
@@ -919,7 +1047,7 @@ window.__ModuleLoader__.load({
|
|
|
919
1047
|
type: "button",
|
|
920
1048
|
className: "qc-settings-add-btn",
|
|
921
1049
|
onClick: () => addCommand(entry.workspaceId),
|
|
922
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1050
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, { size: 14 }), t("cardAddCommand")]
|
|
923
1051
|
})
|
|
924
1052
|
]
|
|
925
1053
|
}, entry.workspaceId))
|
|
@@ -961,9 +1089,9 @@ window.__ModuleLoader__.load({
|
|
|
961
1089
|
runPollFailed: "输出读取失败",
|
|
962
1090
|
runLost: "运行记录已失效",
|
|
963
1091
|
runNotFound: "工作区或命令未找到",
|
|
1092
|
+
settingsTitle: "快捷命令",
|
|
964
1093
|
cardTitle: "快速命令",
|
|
965
|
-
|
|
966
|
-
collapseOrExpand: "展开 / 收起",
|
|
1094
|
+
sectionSub: "为每个工作区配置常用命令,会话头部一键运行",
|
|
967
1095
|
cardIntro: "为每个工作区配置常用命令,运行于工作区目录。支持占位符:{workspace} 工作区路径,{cwd} 会话目录,{title} 工作区标题。",
|
|
968
1096
|
cardWorkspaces: "工作区",
|
|
969
1097
|
cardNoWorkspaces: "没有工作区。请在侧边栏先添加工作区。",
|
|
@@ -1006,9 +1134,9 @@ window.__ModuleLoader__.load({
|
|
|
1006
1134
|
runPollFailed: "Failed to read output",
|
|
1007
1135
|
runLost: "Run record expired",
|
|
1008
1136
|
runNotFound: "Workspace or command not found",
|
|
1137
|
+
settingsTitle: "Quick Commands",
|
|
1009
1138
|
cardTitle: "Quick commands",
|
|
1010
|
-
|
|
1011
|
-
collapseOrExpand: "Expand / collapse",
|
|
1139
|
+
sectionSub: "Configure common commands per workspace, run them from the session header",
|
|
1012
1140
|
cardIntro: "Configure common commands per workspace; they run in the workspace directory. Placeholders: {workspace} workspace path, {cwd} session cwd, {title} workspace title.",
|
|
1013
1141
|
cardWorkspaces: "Workspaces",
|
|
1014
1142
|
cardNoWorkspaces: "No workspaces. Add one in the sidebar first.",
|
|
@@ -1025,8 +1153,8 @@ window.__ModuleLoader__.load({
|
|
|
1025
1153
|
};
|
|
1026
1154
|
//#endregion
|
|
1027
1155
|
//#region \0dsh-css:/home/runner/work/dsh-quick-commands/dsh-quick-commands/src/client/quick-commands.css.mjs
|
|
1028
|
-
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\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}\n";
|
|
1029
|
-
const tagId = "@fonlan/dsh-quick-commands/quick-commands.css#
|
|
1156
|
+
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}";
|
|
1157
|
+
const tagId = "@fonlan/dsh-quick-commands/quick-commands.css#b65a4ce4";
|
|
1030
1158
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1031
1159
|
const tag = document.createElement("style");
|
|
1032
1160
|
tag.dataset.plugin = "@fonlan/dsh-quick-commands";
|
|
@@ -1044,13 +1172,15 @@ window.__ModuleLoader__.load({
|
|
|
1044
1172
|
zh,
|
|
1045
1173
|
en
|
|
1046
1174
|
}), "quick-commands: dictionaries");
|
|
1047
|
-
ctx.locale.bind(LOCALE_NS);
|
|
1175
|
+
const t = ctx.locale.bind(LOCALE_NS);
|
|
1048
1176
|
const slots = ctx.slots;
|
|
1049
|
-
slots.inject("settings.
|
|
1050
|
-
name: "settings.
|
|
1051
|
-
|
|
1177
|
+
slots.inject("settings.section", () => slots.register({
|
|
1178
|
+
name: "settings.section",
|
|
1179
|
+
id: "quick-commands",
|
|
1180
|
+
order: 330,
|
|
1181
|
+
label: () => t("settingsTitle"),
|
|
1052
1182
|
locale: LOCALE_NS
|
|
1053
|
-
},
|
|
1183
|
+
}, QuickCommandsSettingsSection));
|
|
1054
1184
|
slots.inject("conversation.session.header.utilities", () => slots.register({
|
|
1055
1185
|
name: "conversation.session.header.utilities",
|
|
1056
1186
|
id: "quick-commands",
|