@fonlan/dsh-quick-commands 0.1.6 → 0.1.8
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 +138 -13
- package/lib/client.js.map +1 -1
- package/lib/index.js +206 -52
- package/lib/types/index.d.ts +4 -2
- package/lib/types/server/settings.d.ts +14 -4
- package/lib/types/server/shell-path.d.ts +69 -0
- package/lib/types/shared/contract.d.ts +7 -0
- package/package.json +30 -27
- package/src/client/header.tsx +15 -12
- package/src/client/icons.tsx +106 -0
- package/src/client/settings-section.tsx +6 -9
- package/src/index.ts +51 -36
- package/src/server/rpc.ts +8 -3
- package/src/server/runner.ts +7 -3
- package/src/server/settings.ts +74 -29
- package/src/server/shell-path.ts +160 -0
- package/src/shared/contract.ts +10 -0
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,7 +825,7 @@ window.__ModuleLoader__.load({
|
|
|
700
825
|
});
|
|
701
826
|
}
|
|
702
827
|
//#endregion
|
|
703
|
-
//#region \0dsh-css:/
|
|
828
|
+
//#region \0dsh-css:/Users/fonlan/Repos/dsh-quick-commands/src/client/settings-section.css.mjs
|
|
704
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";
|
|
705
830
|
const tagId$1 = "@fonlan/dsh-quick-commands/settings-section.css#d7004149";
|
|
706
831
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
@@ -736,7 +861,7 @@ window.__ModuleLoader__.load({
|
|
|
736
861
|
setState({
|
|
737
862
|
loaded: true,
|
|
738
863
|
entries,
|
|
739
|
-
anchor: settings.popupAnchor
|
|
864
|
+
anchor: anchorOf(settings.popupAnchor)
|
|
740
865
|
});
|
|
741
866
|
setError(null);
|
|
742
867
|
} catch (e) {
|
|
@@ -808,7 +933,7 @@ window.__ModuleLoader__.load({
|
|
|
808
933
|
const next = await quickApi.settingsSetAnchor(anchor);
|
|
809
934
|
setState((s) => ({
|
|
810
935
|
...s,
|
|
811
|
-
anchor: next.popupAnchor
|
|
936
|
+
anchor: anchorOf(next.popupAnchor)
|
|
812
937
|
}));
|
|
813
938
|
} catch (e) {
|
|
814
939
|
setError(e instanceof Error ? e.message : String(e));
|
|
@@ -914,7 +1039,7 @@ window.__ModuleLoader__.load({
|
|
|
914
1039
|
"aria-label": t("cardRemove"),
|
|
915
1040
|
title: t("cardRemove"),
|
|
916
1041
|
onClick: () => removeCommand(entry.workspaceId, index),
|
|
917
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1042
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrashIcon, { size: 14 })
|
|
918
1043
|
})
|
|
919
1044
|
]
|
|
920
1045
|
}, `${entry.workspaceId}-${index}`)),
|
|
@@ -922,7 +1047,7 @@ window.__ModuleLoader__.load({
|
|
|
922
1047
|
type: "button",
|
|
923
1048
|
className: "qc-settings-add-btn",
|
|
924
1049
|
onClick: () => addCommand(entry.workspaceId),
|
|
925
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1050
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(PlusIcon, { size: 14 }), t("cardAddCommand")]
|
|
926
1051
|
})
|
|
927
1052
|
]
|
|
928
1053
|
}, entry.workspaceId))
|
|
@@ -1027,7 +1152,7 @@ window.__ModuleLoader__.load({
|
|
|
1027
1152
|
cardAnchorButton: "Anchored at button"
|
|
1028
1153
|
};
|
|
1029
1154
|
//#endregion
|
|
1030
|
-
//#region \0dsh-css:/
|
|
1155
|
+
//#region \0dsh-css:/Users/fonlan/Repos/dsh-quick-commands/src/client/quick-commands.css.mjs
|
|
1031
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}";
|
|
1032
1157
|
const tagId = "@fonlan/dsh-quick-commands/quick-commands.css#b65a4ce4";
|
|
1033
1158
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|