@ddtcorex/dsh-maestro-config 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +240 -57
- package/lib/types/client/MaestroSettings.d.ts.map +1 -1
- package/lib/types/client/components/BrandMark.d.ts +9 -0
- package/lib/types/client/components/BrandMark.d.ts.map +1 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/client/MaestroSettings.tsx +239 -53
- package/src/client/components/BrandMark.tsx +22 -0
- package/src/client/index.tsx +7 -5
package/lib/client.js
CHANGED
|
@@ -2176,6 +2176,18 @@ var t = {
|
|
|
2176
2176
|
shadowLv3: "var(--dsw-shadow-lv3)",
|
|
2177
2177
|
scrollbarL2: "var(--dsw-alias-scrollbar-bg-l2)"
|
|
2178
2178
|
};
|
|
2179
|
+
var TAB_ICON_PATHS = {
|
|
2180
|
+
globe: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20M2 12h20M12 2a15 15 0 0 1 4 10 15 15 0 0 1-4 10 15 15 0 0 1-4-10A15 15 0 0 1 12 2z",
|
|
2181
|
+
git: "M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5 0-1.2-.4-2.3-1-3 0-1 0-2 0-3s-1 0-2 1a11 11 0 0 0-6 0c-1-1-2-1-2-1 0 1 0 2 0 3-.6.7-1 1.8-1 3 0 3.5 3 5.5 6 5.5-.4.3-.7.7-.9 1.2-.2.5-.3 1-.3 1.5V22",
|
|
2182
|
+
search: "M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14zM20 20l-3.5-3.5",
|
|
2183
|
+
shield: "M12 2l7 4v5c0 5-3.5 7.5-7 9-3.5-1.5-7-4-7-9V6l7-4z",
|
|
2184
|
+
ban: "M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM4 12h16",
|
|
2185
|
+
cpu: "M5 12H2a2 2 0 0 1 2-2h2M12 5V2a2 2 0 0 1 2 2v2M19 12h2a2 2 0 0 1-2 2h-2M12 19v2a2 2 0 0 1-2-2v-2M8 8h8v8H8z",
|
|
2186
|
+
bell: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0"
|
|
2187
|
+
};
|
|
2188
|
+
function TabIcon({ name, size = 14 }) {
|
|
2189
|
+
return (0, import_react.createElement)("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", style: { flex: "none" } }, (0, import_react.createElement)("path", { d: TAB_ICON_PATHS[name] }));
|
|
2190
|
+
}
|
|
2179
2191
|
function Button({
|
|
2180
2192
|
variant = "ghost",
|
|
2181
2193
|
size = "md",
|
|
@@ -2520,14 +2532,15 @@ function ReviewModelSelector({
|
|
|
2520
2532
|
position: "absolute",
|
|
2521
2533
|
top: "calc(100% + 8px)",
|
|
2522
2534
|
left: "0",
|
|
2523
|
-
minWidth: "300px",
|
|
2524
|
-
maxWidth: "360px",
|
|
2535
|
+
minWidth: "min(300px, calc(100vw - 32px))",
|
|
2536
|
+
maxWidth: "min(360px, calc(100vw - 24px))",
|
|
2525
2537
|
background: t.bgLayer2,
|
|
2526
2538
|
border: `1px solid ${t.borderL2}`,
|
|
2527
2539
|
borderRadius: "12px",
|
|
2528
2540
|
boxShadow: t.shadowLv3,
|
|
2529
2541
|
zIndex: "20",
|
|
2530
|
-
padding: "6px"
|
|
2542
|
+
padding: "6px",
|
|
2543
|
+
boxSizing: "border-box"
|
|
2531
2544
|
};
|
|
2532
2545
|
const rowStyle2 = {
|
|
2533
2546
|
width: "100%",
|
|
@@ -2551,7 +2564,7 @@ function ReviewModelSelector({
|
|
|
2551
2564
|
const modelLabel = selectedProvider === "" ? "Select model" : value?.model ?? "Select model";
|
|
2552
2565
|
return (0, import_react.createElement)(
|
|
2553
2566
|
"div",
|
|
2554
|
-
{ ref: rootRef, "data-maestro-trigger-wrap": "", style: { position: "relative", display: "inline-block", maxWidth: "100%" } },
|
|
2567
|
+
{ ref: rootRef, "data-maestro-trigger-wrap": "", style: { position: "relative", display: "inline-block", maxWidth: "100%", minWidth: 0, boxSizing: "border-box" } },
|
|
2555
2568
|
label ? (0, import_react.createElement)("span", { style: fieldLabelStyle }, label) : null,
|
|
2556
2569
|
(0, import_react.createElement)(
|
|
2557
2570
|
"button",
|
|
@@ -2570,8 +2583,10 @@ function ReviewModelSelector({
|
|
|
2570
2583
|
alignItems: "center",
|
|
2571
2584
|
gap: 8,
|
|
2572
2585
|
cursor: "pointer",
|
|
2573
|
-
maxWidth:
|
|
2574
|
-
|
|
2586
|
+
maxWidth: "min(320px, 100%)",
|
|
2587
|
+
minWidth: 0,
|
|
2588
|
+
whiteSpace: "nowrap",
|
|
2589
|
+
boxSizing: "border-box"
|
|
2575
2590
|
},
|
|
2576
2591
|
onClick: () => {
|
|
2577
2592
|
setOpen((v) => !v);
|
|
@@ -2977,6 +2992,143 @@ function NamedTunnelSetupNote() {
|
|
|
2977
2992
|
(0, import_react.createElement)("ol", { style: { margin: "4px 0", paddingLeft: 20, fontSize: 12, color: t.labelSecondary, lineHeight: "18px" } }, (0, import_react.createElement)("li", null, "cloudflared tunnel login"), (0, import_react.createElement)("li", null, "cloudflared tunnel create dsh-maestro-webhook"), (0, import_react.createElement)("li", null, "cloudflared tunnel route dns dsh-maestro-webhook <your-hostname>"), (0, import_react.createElement)("li", null, "Paste the printed Tunnel ID, credentials file path and hostname below."))
|
|
2978
2993
|
);
|
|
2979
2994
|
}
|
|
2995
|
+
function PlaceholderMappingsEditor({ rows, onChange }) {
|
|
2996
|
+
const updateRow = (index, field, value) => {
|
|
2997
|
+
onChange(rows.map((row, i) => i === index ? { ...row, [field]: value } : row));
|
|
2998
|
+
};
|
|
2999
|
+
const removeRow = (index) => onChange(rows.filter((_, i) => i !== index));
|
|
3000
|
+
const addRow = () => onChange([...rows, { pattern: "", suggestion: "" }]);
|
|
3001
|
+
return (0, import_react.createElement)(
|
|
3002
|
+
"div",
|
|
3003
|
+
{ "data-maestro-placeholders": "", style: { display: "flex", flexDirection: "column", gap: 12, paddingTop: 8 } },
|
|
3004
|
+
// Header — count + primary Add (stacks on mobile via CSS)
|
|
3005
|
+
(0, import_react.createElement)(
|
|
3006
|
+
"div",
|
|
3007
|
+
{ "data-maestro-placeholders-header": "", style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12, padding: "4px 0" } },
|
|
3008
|
+
(0, import_react.createElement)(
|
|
3009
|
+
"div",
|
|
3010
|
+
{ style: { flex: 1, minWidth: 0 } },
|
|
3011
|
+
(0, import_react.createElement)("div", { style: { fontSize: 14, fontWeight: 600, color: t.labelPrimary, lineHeight: "20px" } }, `Placeholder mappings \u2014 ${rows.length} mapped`),
|
|
3012
|
+
(0, import_react.createElement)("div", { style: { fontSize: 12, color: t.labelSecondary, lineHeight: "16px", marginTop: 2 } }, "Blocked pattern \u2192 placeholder suggestion, one mapping per row")
|
|
3013
|
+
),
|
|
3014
|
+
(0, import_react.createElement)(Button, { variant: "primary", size: "md", onClick: addRow, "data-maestro-placeholders-add": "" }, "+ Add mapping")
|
|
3015
|
+
),
|
|
3016
|
+
rows.length === 0 ? (0, import_react.createElement)(
|
|
3017
|
+
"div",
|
|
3018
|
+
{
|
|
3019
|
+
"data-maestro-placeholders-empty": "",
|
|
3020
|
+
style: {
|
|
3021
|
+
display: "flex",
|
|
3022
|
+
flexDirection: "column",
|
|
3023
|
+
alignItems: "center",
|
|
3024
|
+
gap: 10,
|
|
3025
|
+
padding: "20px 16px",
|
|
3026
|
+
borderRadius: 12,
|
|
3027
|
+
border: `1px dashed ${t.borderL2}`,
|
|
3028
|
+
background: "transparent",
|
|
3029
|
+
textAlign: "center"
|
|
3030
|
+
}
|
|
3031
|
+
},
|
|
3032
|
+
(0, import_react.createElement)("div", { style: { fontSize: 13, color: t.labelSecondary, lineHeight: "18px" } }, "No placeholder mappings yet \u2014 add your first mapping"),
|
|
3033
|
+
(0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: addRow }, "+ Add mapping")
|
|
3034
|
+
) : (0, import_react.createElement)(
|
|
3035
|
+
"div",
|
|
3036
|
+
{ "data-maestro-placeholders-list": "", style: { display: "flex", flexDirection: "column", gap: 12 } },
|
|
3037
|
+
...rows.map(
|
|
3038
|
+
(row, i) => (0, import_react.createElement)(
|
|
3039
|
+
"div",
|
|
3040
|
+
{
|
|
3041
|
+
key: i,
|
|
3042
|
+
"data-maestro-mapping-card": "",
|
|
3043
|
+
style: {
|
|
3044
|
+
display: "flex",
|
|
3045
|
+
flexDirection: "column",
|
|
3046
|
+
gap: 12,
|
|
3047
|
+
padding: 12,
|
|
3048
|
+
borderRadius: 12,
|
|
3049
|
+
border: `1px solid ${t.borderL2}`,
|
|
3050
|
+
background: t.bgLayer1,
|
|
3051
|
+
boxSizing: "border-box"
|
|
3052
|
+
}
|
|
3053
|
+
},
|
|
3054
|
+
// Card header: index badge + preview + X (same as Review's project card header)
|
|
3055
|
+
(0, import_react.createElement)(
|
|
3056
|
+
"div",
|
|
3057
|
+
{ style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 } },
|
|
3058
|
+
(0, import_react.createElement)(
|
|
3059
|
+
"div",
|
|
3060
|
+
{ style: { display: "flex", alignItems: "center", gap: 8, minWidth: 0, flex: 1 } },
|
|
3061
|
+
(0, import_react.createElement)(
|
|
3062
|
+
"span",
|
|
3063
|
+
{
|
|
3064
|
+
style: {
|
|
3065
|
+
flex: "none",
|
|
3066
|
+
width: 24,
|
|
3067
|
+
height: 24,
|
|
3068
|
+
borderRadius: 12,
|
|
3069
|
+
background: "var(--dsw-alias-bg-module-platform, #F5F6F7)",
|
|
3070
|
+
display: "inline-flex",
|
|
3071
|
+
alignItems: "center",
|
|
3072
|
+
justifyContent: "center",
|
|
3073
|
+
fontSize: 11,
|
|
3074
|
+
fontWeight: 600,
|
|
3075
|
+
color: t.labelSecondary
|
|
3076
|
+
}
|
|
3077
|
+
},
|
|
3078
|
+
String(i + 1)
|
|
3079
|
+
),
|
|
3080
|
+
(0, import_react.createElement)(
|
|
3081
|
+
"span",
|
|
3082
|
+
{
|
|
3083
|
+
style: {
|
|
3084
|
+
fontSize: 12,
|
|
3085
|
+
fontFamily: "ui-monospace, monospace",
|
|
3086
|
+
color: row.pattern ? t.labelPrimary : t.labelTertiary,
|
|
3087
|
+
overflow: "hidden",
|
|
3088
|
+
textOverflow: "ellipsis",
|
|
3089
|
+
whiteSpace: "nowrap",
|
|
3090
|
+
minWidth: 0
|
|
3091
|
+
}
|
|
3092
|
+
},
|
|
3093
|
+
row.pattern ? row.suggestion ? `${row.pattern} \u2192 ${row.suggestion}` : `${row.pattern} \u2192 \u2026` : "New mapping"
|
|
3094
|
+
)
|
|
3095
|
+
),
|
|
3096
|
+
(0, import_react.createElement)(Button, { variant: "outline", size: "sm", onClick: () => removeRow(i), "aria-label": `Remove mapping ${i + 1}`, title: "Remove mapping" }, "\u2715")
|
|
3097
|
+
),
|
|
3098
|
+
// Fields grid — 2-col desktop, 1-col mobile (same as Review's project grid)
|
|
3099
|
+
(0, import_react.createElement)(
|
|
3100
|
+
"div",
|
|
3101
|
+
{ "data-maestro-mapping-grid": "", style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 } },
|
|
3102
|
+
(0, import_react.createElement)(
|
|
3103
|
+
"label",
|
|
3104
|
+
{ style: fieldLabelStyle, "data-maestro-field": "" },
|
|
3105
|
+
"Blocked pattern",
|
|
3106
|
+
(0, import_react.createElement)(FieldInput, {
|
|
3107
|
+
value: row.pattern,
|
|
3108
|
+
placeholder: "example-project",
|
|
3109
|
+
onChange: (e) => updateRow(i, "pattern", e.target.value),
|
|
3110
|
+
"aria-label": `Blocked pattern ${i + 1}`,
|
|
3111
|
+
style: { width: "100%" }
|
|
3112
|
+
})
|
|
3113
|
+
),
|
|
3114
|
+
(0, import_react.createElement)(
|
|
3115
|
+
"label",
|
|
3116
|
+
{ style: fieldLabelStyle, "data-maestro-field": "" },
|
|
3117
|
+
"Placeholder",
|
|
3118
|
+
(0, import_react.createElement)(FieldInput, {
|
|
3119
|
+
value: row.suggestion,
|
|
3120
|
+
placeholder: "my-project",
|
|
3121
|
+
onChange: (e) => updateRow(i, "suggestion", e.target.value),
|
|
3122
|
+
"aria-label": `Placeholder suggestion ${i + 1}`,
|
|
3123
|
+
style: { width: "100%" }
|
|
3124
|
+
})
|
|
3125
|
+
)
|
|
3126
|
+
)
|
|
3127
|
+
)
|
|
3128
|
+
)
|
|
3129
|
+
)
|
|
3130
|
+
);
|
|
3131
|
+
}
|
|
2980
3132
|
function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
2981
3133
|
const [status, setStatus] = (0, import_react.useState)(null);
|
|
2982
3134
|
const [proxyStatus, setProxyStatus] = (0, import_react.useState)(null);
|
|
@@ -2991,34 +3143,49 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
2991
3143
|
const [showLanPin, setShowLanPin] = (0, import_react.useState)(false);
|
|
2992
3144
|
const [guard, setGuard] = (0, import_react.useState)({});
|
|
2993
3145
|
const [patternsText, setPatternsText] = (0, import_react.useState)("");
|
|
2994
|
-
const [
|
|
3146
|
+
const [placeholderRows, setPlaceholderRows] = (0, import_react.useState)([]);
|
|
2995
3147
|
const [supervisorCfg, setSupervisorCfg] = (0, import_react.useState)({});
|
|
2996
3148
|
const [notifierCfg, setNotifierCfg] = (0, import_react.useState)({});
|
|
2997
3149
|
const [activeTab, setActiveTab] = (0, import_react.useState)("tunnel");
|
|
2998
3150
|
(0, import_react.useEffect)(() => {
|
|
2999
3151
|
const css = `
|
|
3000
|
-
/*
|
|
3001
|
-
[data-maestro-
|
|
3152
|
+
/* Model override dropdown \u2014 prevent right-edge overlap */
|
|
3153
|
+
[data-maestro-control] [data-maestro-menu] { left:auto !important; right:0 !important; }
|
|
3154
|
+
[data-maestro-project-card] [data-maestro-menu] { left:auto !important; right:0 !important; }
|
|
3155
|
+
[data-maestro-trigger-wrap] { min-width:0; }
|
|
3156
|
+
/* Maestro nested tabs \u2014 unified with dsh-maestro-jobs (maestro-design pill bar) */
|
|
3157
|
+
[data-maestro-tabs] { display:flex; gap:6px; overflow-x:auto; overflow-y:hidden; scrollbar-width:none; -webkit-overflow-scrolling:touch; overscroll-behavior-x:contain; touch-action:pan-x; padding:2px 2px 8px; margin:0 -2px 4px; border-bottom:1px solid var(--dsw-alias-border-l1, rgba(0,0,0,.08)); }
|
|
3002
3158
|
[data-maestro-tabs]::-webkit-scrollbar { display:none; width:0; height:0; }
|
|
3003
|
-
[data-maestro-tab] { flex:none;
|
|
3004
|
-
[data-maestro-tab][data-active="true"] { background:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); border-color:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); color:var(--dsw-alias-label-primary); }
|
|
3159
|
+
[data-maestro-tab] { flex:none; min-width:fit-content; min-height:40px; padding:0 14px; border-radius:999px; border:1px solid var(--dsw-alias-border-l1, rgba(0,0,0,.12)); background:transparent; color:var(--dsw-alias-label-secondary); font-size:13px; line-height:20px; font-weight:500; font-family:inherit; white-space:nowrap; display:inline-flex; align-items:center; justify-content:center; gap:6px; cursor:pointer; -webkit-tap-highlight-color:transparent; transition: background 150ms ease, color 150ms ease, border-color 150ms ease; touch-action:manipulation; }
|
|
3160
|
+
[data-maestro-tab][data-active="true"] { background:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); border-color:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); color:var(--dsw-alias-label-primary); font-weight:600; }
|
|
3005
3161
|
[data-maestro-tab]:hover { background:var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.06)); }
|
|
3006
3162
|
[data-maestro-tab][data-active="true"]:hover { background:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); }
|
|
3007
3163
|
[data-maestro-tab]:focus-visible { outline:2px solid var(--dsw-alias-state-business-primary, #4f6ef7); outline-offset:1px; }
|
|
3164
|
+
[data-maestro-tab]:active { transform: scale(.97); }
|
|
3008
3165
|
[data-maestro-panel] { width:100%; min-width:0; box-sizing:border-box; }
|
|
3009
|
-
/* label
|
|
3166
|
+
/* label/input overlap fix: flex column gap + full width */
|
|
3010
3167
|
[data-maestro-panel] label { gap:6px !important; }
|
|
3011
3168
|
[data-maestro-row]:last-child { border-bottom:none !important; }
|
|
3012
3169
|
[data-maestro-panel] label > span { width:100% !important; box-sizing:border-box !important; }
|
|
3170
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3171
|
+
[data-maestro-tab] { transition: none !important; }
|
|
3172
|
+
[data-maestro-tab]:active { transform: none !important; }
|
|
3173
|
+
}
|
|
3013
3174
|
@media (max-width: 640px) {
|
|
3014
3175
|
[data-maestro-settings-card] { max-width:100% !important; gap:6px !important; padding:0 2px !important; }
|
|
3015
|
-
[data-maestro-tabs] { gap:6px !important; padding:
|
|
3016
|
-
[data-maestro-tab] { height:
|
|
3017
|
-
|
|
3018
|
-
[data-maestro-
|
|
3176
|
+
[data-maestro-tabs] { gap:6px !important; padding:2px 2px 8px !important; margin:0 -2px 8px !important; }
|
|
3177
|
+
[data-maestro-tab] { min-height:40px !important; height:auto !important; padding:0 12px !important; font-size:13px !important; }
|
|
3178
|
+
/* Placeholder mappings \u2014 card design unified with Review project cards (header X, grid fields) */
|
|
3179
|
+
[data-maestro-placeholders-header] { flex-direction: column !important; align-items: stretch !important; }
|
|
3180
|
+
[data-maestro-placeholders-header] [data-maestro-placeholders-add] { width: 100% !important; justify-content: center !important; }
|
|
3181
|
+
[data-maestro-placeholders-list] { gap: 12px !important; }
|
|
3182
|
+
[data-maestro-mapping-card] { padding:10px !important; }
|
|
3183
|
+
[data-maestro-mapping-grid] { grid-template-columns:1fr !important; gap:12px !important; }
|
|
3019
3184
|
[data-maestro-qr-row] { flex-direction:column !important; align-items:flex-start !important; }
|
|
3020
3185
|
[data-maestro-trigger-wrap] { max-width:100% !important; }
|
|
3021
3186
|
[data-maestro-menu] { min-width:0 !important; max-width:calc(100vw - 32px) !important; left:0 !important; right:auto !important; }
|
|
3187
|
+
[data-maestro-control] [data-maestro-menu] { left:0 !important; right:auto !important; }
|
|
3188
|
+
[data-maestro-project-card] [data-maestro-menu] { left:0 !important; right:auto !important; }
|
|
3022
3189
|
[data-maestro-panel] label { gap:8px !important; }
|
|
3023
3190
|
div[data-maestro-row] { flex-direction:column !important; align-items:stretch !important; padding:12px 0 !important; }
|
|
3024
3191
|
[data-maestro-row-text] { padding-right:0 !important; }
|
|
@@ -3031,7 +3198,7 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3031
3198
|
[data-maestro-project-profile-row] > label { flex:1 1 100% !important; width:100% !important; }
|
|
3032
3199
|
}
|
|
3033
3200
|
@media (max-width: 390px) {
|
|
3034
|
-
[data-maestro-tab] { height:
|
|
3201
|
+
[data-maestro-tab] { min-height:38px !important; height:auto !important; padding:0 10px !important; font-size:12px !important; }
|
|
3035
3202
|
}
|
|
3036
3203
|
`;
|
|
3037
3204
|
const tag = document.createElement("style");
|
|
@@ -3083,15 +3250,13 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3083
3250
|
setError(e.message ?? String(e));
|
|
3084
3251
|
}
|
|
3085
3252
|
};
|
|
3086
|
-
const commitPlaceholders = async () => {
|
|
3253
|
+
const commitPlaceholders = async (rows) => {
|
|
3087
3254
|
setError(null);
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
if (
|
|
3092
|
-
|
|
3093
|
-
setError(`placeholders JSON invalid: ${e.message ?? String(e)}`);
|
|
3094
|
-
return;
|
|
3255
|
+
const obj = {};
|
|
3256
|
+
for (const row of rows) {
|
|
3257
|
+
const pattern = (row.pattern ?? "").trim();
|
|
3258
|
+
if (!pattern) continue;
|
|
3259
|
+
obj[pattern] = (row.suggestion ?? "").trim();
|
|
3095
3260
|
}
|
|
3096
3261
|
try {
|
|
3097
3262
|
await cfgSet("guardBlacklist", { placeholders: obj });
|
|
@@ -3114,6 +3279,7 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3114
3279
|
const next = { ...prev };
|
|
3115
3280
|
for (const [k, v] of Object.entries(patch)) {
|
|
3116
3281
|
if (k === "telegram" && typeof v === "object" && v !== null) next.telegram = { ...prev.telegram ?? {}, ...v };
|
|
3282
|
+
else if (k === "policy" && typeof v === "object" && v !== null) next.policy = { ...prev.policy ?? {}, ...v };
|
|
3117
3283
|
else next[k] = v;
|
|
3118
3284
|
}
|
|
3119
3285
|
return next;
|
|
@@ -3147,7 +3313,7 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3147
3313
|
const pats = Array.isArray(bl?.patterns) ? bl.patterns : [];
|
|
3148
3314
|
const ph = bl?.placeholders && typeof bl.placeholders === "object" ? bl.placeholders : {};
|
|
3149
3315
|
setPatternsText(pats.join("\n"));
|
|
3150
|
-
|
|
3316
|
+
setPlaceholderRows(Object.entries(ph).map(([pattern, suggestion]) => ({ pattern, suggestion: String(suggestion) })));
|
|
3151
3317
|
setSupervisorCfg(sup ?? {});
|
|
3152
3318
|
setNotifierCfg(not ?? {});
|
|
3153
3319
|
}).catch(() => {
|
|
@@ -3264,14 +3430,13 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3264
3430
|
}
|
|
3265
3431
|
};
|
|
3266
3432
|
const TABS = [
|
|
3267
|
-
{ id: "tunnel", label: "Tunnel" },
|
|
3268
|
-
{ id: "
|
|
3269
|
-
{ id: "
|
|
3270
|
-
{ id: "
|
|
3271
|
-
{ id: "
|
|
3272
|
-
{ id: "
|
|
3273
|
-
{ id: "
|
|
3274
|
-
{ id: "notifier", label: "Notifier" }
|
|
3433
|
+
{ id: "tunnel", label: "Tunnel", icon: "globe" },
|
|
3434
|
+
{ id: "gitlab", label: "GitLab", icon: "git" },
|
|
3435
|
+
{ id: "review", label: "Review", icon: "search" },
|
|
3436
|
+
{ id: "guard", label: "Guard", icon: "shield" },
|
|
3437
|
+
{ id: "blacklist", label: "Blacklist", icon: "ban" },
|
|
3438
|
+
{ id: "supervisor", label: "Supervisor", icon: "cpu" },
|
|
3439
|
+
{ id: "notifier", label: "Notifier", icon: "bell" }
|
|
3275
3440
|
];
|
|
3276
3441
|
const tabContents = {
|
|
3277
3442
|
tunnel: (0, import_react.createElement)(
|
|
@@ -3290,14 +3455,6 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3290
3455
|
(0, import_react.createElement)("div", { style: { ...cardInsetStyle, marginTop: "12px" } }, (0, import_react.createElement)("div", { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary } }, "Remote access \u2014 LAN"), (0, import_react.createElement)(LanAccess, { proxyStatus, lanPin: lanPinEnabled === null ? null : { enabled: lanPinEnabled, pin: lanPin, show: showLanPin, onShow: revealLanPin, onHide: () => setShowLanPin(false), onRotate: rotateLanPin, onToggle: toggleLanPin } })),
|
|
3291
3456
|
(0, import_react.createElement)("div", { style: { ...cardInsetStyle, marginTop: "12px" } }, (0, import_react.createElement)("div", { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary } }, "Public access"), (0, import_react.createElement)(PublicAccess, { status, pin, showPin, onRevealPin: revealPin, onHidePin: () => setShowPin(false), onRotatePin: rotatePin }))
|
|
3292
3457
|
),
|
|
3293
|
-
notify: (0, import_react.createElement)(
|
|
3294
|
-
"div",
|
|
3295
|
-
{ style: { display: "flex", flexDirection: "column" } },
|
|
3296
|
-
(0, import_react.createElement)("div", { style: { padding: "12px 0", borderBottom: `1px solid ${t.borderL2}` } }, (0, import_react.createElement)("p", { style: captionStyle }, "Sends one protected startup update with the current public-access PIN to a single chat. Leave either credential blank to disable.")),
|
|
3297
|
-
(0, import_react.createElement)(SettingRow, { title: "Bot token", description: "Telegram bot token from @BotFather.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(FieldInput, { placeholder: "123456:ABC-DEF...", type: "password", autoComplete: "off", value: config.hasTelegramBotToken ? "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022" : "", onChange: (e) => saveField("telegramBotToken", e.target.value), style: { width: 220 } }), config.hasTelegramBotToken ? (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveField("telegramBotToken", "") }, "Clear") : null) }),
|
|
3298
|
-
(0, import_react.createElement)(SettingRow, { title: "Chat ID", description: "Target chat, e.g. -1001234567890.", control: (0, import_react.createElement)(FieldInput, { placeholder: "-1001234567890", value: config.telegramChatId ?? "", onChange: (e) => saveField("telegramChatId", e.target.value), style: { width: 220 } }) }),
|
|
3299
|
-
(0, import_react.createElement)(ToggleRow, { title: "Also notify about reviews", description: "One message per review run with its outcome.", checked: config.telegramReviewNotifications === true, onChange: (v) => saveField("telegramReviewNotifications", v) })
|
|
3300
|
-
),
|
|
3301
3458
|
gitlab: (0, import_react.createElement)(
|
|
3302
3459
|
"div",
|
|
3303
3460
|
{ style: { display: "flex", flexDirection: "column" } },
|
|
@@ -3343,11 +3500,17 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3343
3500
|
"div",
|
|
3344
3501
|
{ style: { ...rowStyle, flexDirection: "column", alignItems: "stretch", gap: 8, borderBottom: "none" } },
|
|
3345
3502
|
(0, import_react.createElement)("div", { style: rowTitleStyle }, "Placeholder mappings"),
|
|
3346
|
-
(0, import_react.createElement)("div", { style: rowDescStyle }, "
|
|
3347
|
-
(0, import_react.createElement)(
|
|
3503
|
+
(0, import_react.createElement)("div", { style: rowDescStyle }, "Blocked pattern \u2192 placeholder suggestion. One mapping per row \u2014 no JSON needed."),
|
|
3504
|
+
(0, import_react.createElement)(PlaceholderMappingsEditor, {
|
|
3505
|
+
rows: placeholderRows,
|
|
3506
|
+
onChange: (rows) => {
|
|
3507
|
+
setPlaceholderRows(rows);
|
|
3508
|
+
commitPlaceholders(rows);
|
|
3509
|
+
}
|
|
3510
|
+
}),
|
|
3348
3511
|
(0, import_react.createElement)("div", { style: { marginTop: 8 } }, (0, import_react.createElement)(Button, { variant: "outline", size: "sm", onClick: () => {
|
|
3349
3512
|
commitBlacklistPatterns(patternsText);
|
|
3350
|
-
commitPlaceholders();
|
|
3513
|
+
commitPlaceholders(placeholderRows);
|
|
3351
3514
|
} }, "Save Blacklist"))
|
|
3352
3515
|
)
|
|
3353
3516
|
),
|
|
@@ -3367,9 +3530,10 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3367
3530
|
notifier: (0, import_react.createElement)(
|
|
3368
3531
|
"div",
|
|
3369
3532
|
{ style: { display: "flex", flexDirection: "column" } },
|
|
3370
|
-
(0, import_react.createElement)(
|
|
3371
|
-
(0, import_react.createElement)(SettingRow, { title: "
|
|
3372
|
-
(0, import_react.createElement)(
|
|
3533
|
+
(0, import_react.createElement)("div", { style: { padding: "12px 0", borderBottom: `1px solid ${t.borderL2}` } }, (0, import_react.createElement)("p", { style: captionStyle }, "Telegram bot settings for notifications: startup PIN, review digests, PIN rotation. Leave blank to disable.")),
|
|
3534
|
+
(0, import_react.createElement)(SettingRow, { title: "Bot token", description: "Telegram bot token from @BotFather.", control: (0, import_react.createElement)("div", { style: { display: "flex", gap: 8, alignItems: "center" } }, (0, import_react.createElement)(FieldInput, { type: "password", autoComplete: "off", value: notifierCfg.telegram?.botToken ?? "", placeholder: "123456:ABC-DEF...", onChange: (e) => saveNotifierCfg({ telegram: { botToken: e.target.value } }), style: { width: 220 } }), notifierCfg.telegram?.botToken ? (0, import_react.createElement)(Button, { variant: "outline", size: "md", onClick: () => saveNotifierCfg({ telegram: { botToken: "" } }) }, "Clear") : null) }),
|
|
3535
|
+
(0, import_react.createElement)(SettingRow, { title: "Chat ID", description: "Target chat, e.g. -1001234567890.", control: (0, import_react.createElement)(FieldInput, { value: notifierCfg.telegram?.chatId ?? "", placeholder: "-1001234567890", onChange: (e) => saveNotifierCfg({ telegram: { chatId: e.target.value } }), style: { width: 220 } }) }),
|
|
3536
|
+
(0, import_react.createElement)(ToggleRow, { title: "Review notifications", description: "Also notify about finished reviews.", checked: notifierCfg.policy?.reviewNotifications === true, onChange: (v) => saveNotifierCfg({ policy: { reviewNotifications: v } }) })
|
|
3373
3537
|
)
|
|
3374
3538
|
};
|
|
3375
3539
|
return (0, import_react.createElement)(
|
|
@@ -3377,14 +3541,31 @@ function MaestroSettingsTab({ rpcCall, configRpcCall }) {
|
|
|
3377
3541
|
{ "data-maestro-settings-card": "", style: { display: "flex", flexDirection: "column", gap: 8, width: "100%", maxWidth: 640, minWidth: 0, boxSizing: "border-box" } },
|
|
3378
3542
|
(0, import_react.createElement)(
|
|
3379
3543
|
"div",
|
|
3380
|
-
{ style: { padding: "2px 2px 8px" } },
|
|
3381
|
-
|
|
3382
|
-
(0, import_react.createElement)(
|
|
3544
|
+
{ style: { padding: "2px 2px 8px", display: "flex", gap: 10, alignItems: "flex-start" } },
|
|
3545
|
+
// Shared BrandBadge — same as dashboard sidebar/popup (BrandMark #0A84FF)
|
|
3546
|
+
(0, import_react.createElement)(
|
|
3547
|
+
"span",
|
|
3548
|
+
{ "data-maestro-logo": "", style: { width: 28, height: 28, borderRadius: 8, display: "inline-flex", alignItems: "center", justifyContent: "center", background: "var(--dsw-alias-brand-primary, #0A84FF)", backgroundColor: "#0A84FF", color: "#fff", flex: "none", border: "1px solid rgba(0,0,0,0.08)", boxShadow: "0 0 0 1px var(--dsw-alias-border-l1)", boxSizing: "border-box", alignSelf: "flex-start", marginTop: 2 } },
|
|
3549
|
+
(0, import_react.createElement)("svg", { width: 16, height: 16, viewBox: "0 0 16 16", fill: "none", "aria-hidden": "true" }, (0, import_react.createElement)("path", { d: "M2 11 L5 4 L8 9 L11 4 L14 11", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round" }))
|
|
3550
|
+
),
|
|
3551
|
+
(0, import_react.createElement)(
|
|
3552
|
+
"div",
|
|
3553
|
+
{ style: { display: "flex", flexDirection: "column", minWidth: 0 } },
|
|
3554
|
+
(0, import_react.createElement)("div", { style: { fontSize: 15, fontWeight: 600, color: t.labelPrimary, lineHeight: "22px" } }, "Maestro"),
|
|
3555
|
+
(0, import_react.createElement)("div", { style: { fontSize: 12, color: t.labelSecondary, lineHeight: "16px", marginTop: 2 } }, "Tunnel, access, review & guard \u2014 all via the shared Maestro store. Uses the same tokens and primitives as DSH settings.")
|
|
3556
|
+
)
|
|
3383
3557
|
),
|
|
3558
|
+
(0, import_react.createElement)("style", {}, "[data-maestro-logo]{background:#0A84FF !important; background-color:#0A84FF !important; color:#fff !important;}"),
|
|
3384
3559
|
(0, import_react.createElement)(
|
|
3385
3560
|
"div",
|
|
3386
3561
|
{ "data-maestro-tabs": "", role: "tablist", "aria-label": "Maestro settings sections" },
|
|
3387
|
-
...TABS.map((tab) => (0, import_react.createElement)("button", { key: tab.id, "data-maestro-tab": "", "data-active": String(activeTab === tab.id), role: "tab", "aria-selected": activeTab === tab.id, onClick: () =>
|
|
3562
|
+
...TABS.map((tab) => (0, import_react.createElement)("button", { key: tab.id, "data-maestro-tab": "", "data-active": String(activeTab === tab.id), role: "tab", "aria-selected": activeTab === tab.id, onClick: (e) => {
|
|
3563
|
+
setActiveTab(tab.id);
|
|
3564
|
+
try {
|
|
3565
|
+
e.currentTarget.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" });
|
|
3566
|
+
} catch {
|
|
3567
|
+
}
|
|
3568
|
+
} }, (0, import_react.createElement)(TabIcon, { name: tab.icon }), tab.label))
|
|
3388
3569
|
),
|
|
3389
3570
|
(0, import_react.createElement)("div", { "data-maestro-panel": activeTab, style: { display: "flex", flexDirection: "column", gap: 10, minWidth: 0 } }, tabContents[activeTab]),
|
|
3390
3571
|
error ? (0, import_react.createElement)("p", { style: { color: t.stateError, fontSize: 12, margin: "8px 0 0", padding: "8px 10px", borderRadius: 8, background: "color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent)", border: `1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent)` } }, error) : null
|
|
@@ -3430,9 +3611,10 @@ function registerSettingsNavIcon(label, root) {
|
|
|
3430
3611
|
// src/client/index.tsx
|
|
3431
3612
|
var SETTINGS_NAV_CSS = `
|
|
3432
3613
|
|
|
3433
|
-
/* maestro: replace the settings-nav fallback gear with the Maestro M-logo glyph */
|
|
3434
|
-
[${SETTINGS_NAV_MARKER}] > svg:first-child
|
|
3435
|
-
|
|
3614
|
+
/* maestro: replace the settings-nav fallback gear with the Maestro M-logo glyph \u2014 same mark as sidebar/popup BrandMark */
|
|
3615
|
+
[${SETTINGS_NAV_MARKER}] > svg:first-child,
|
|
3616
|
+
[${SETTINGS_NAV_MARKER}] > svg.zWKi1a_navIcon {
|
|
3617
|
+
display: none !important;
|
|
3436
3618
|
}
|
|
3437
3619
|
|
|
3438
3620
|
[${SETTINGS_NAV_MARKER}]::before {
|
|
@@ -3440,9 +3622,10 @@ var SETTINGS_NAV_CSS = `
|
|
|
3440
3622
|
flex: none;
|
|
3441
3623
|
width: 16px;
|
|
3442
3624
|
height: 16px;
|
|
3625
|
+
display: inline-block;
|
|
3443
3626
|
background: currentColor;
|
|
3444
|
-
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.
|
|
3445
|
-
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.
|
|
3627
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 11 L5 4 L8 9 L11 4 L14 11'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
3628
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 11 L5 4 L8 9 L11 4 L14 11'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
3446
3629
|
}
|
|
3447
3630
|
`;
|
|
3448
3631
|
var inject = ["slots", "connection"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MaestroSettings.d.ts","sourceRoot":"","sources":["../../../src/client/MaestroSettings.tsx"],"names":[],"mappings":"AACA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"MaestroSettings.d.ts","sourceRoot":"","sources":["../../../src/client/MaestroSettings.tsx"],"names":[],"mappings":"AACA;;;;;;;;GAQG;AAisCH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE;IAAE,OAAO,EAAE,GAAG,CAAC;IAAC,aAAa,CAAC,EAAE,GAAG,CAAA;CAAE;;;;;;;;;mBAkZmE,GAAG;;gBAoBzK"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function MaestroMark(props: {
|
|
2
|
+
size?: number;
|
|
3
|
+
}): import("react").ReactSVGElement;
|
|
4
|
+
export declare function BrandBadge(props: {
|
|
5
|
+
size?: number;
|
|
6
|
+
outer?: number;
|
|
7
|
+
radius?: number;
|
|
8
|
+
}): import("react").DetailedReactHTMLElement<any, HTMLElement>;
|
|
9
|
+
//# sourceMappingURL=BrandMark.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BrandMark.d.ts","sourceRoot":"","sources":["../../../../src/client/components/BrandMark.tsx"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,mCAKnD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,8DAYnF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AA0CA,UAAU,SAAS;IACjB,GAAG,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAA;IAC3B,MAAM,CAAC,EAAE,EAAE,MAAM,MAAM,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACtD;AAED,eAAO,MAAM,MAAM,kCAAmC,CAAA;AAatD,wBAAgB,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CA+B1C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ddtcorex/dsh-maestro-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Maestro Config — shared settings service for the dsh-maestro-* suite over the single namespaced store (~/.dsh/maestro/settings.json)",
|
|
6
6
|
"type": "module",
|
|
@@ -34,14 +34,14 @@
|
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
37
|
-
"@deepseek-ai/dsh-client-connection": "0.1.
|
|
37
|
+
"@deepseek-ai/dsh-client-connection": "0.1.2-alpha.2"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@ddtcorex/dsh-maestro-config-lib": "^0.1.2"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
44
|
-
"@deepseek-ai/dsh-client-connection": "0.1.
|
|
44
|
+
"@deepseek-ai/dsh-client-connection": "0.1.2-alpha.2",
|
|
45
45
|
"@types/node": "^22.0.0",
|
|
46
46
|
"@types/qrcode": "^1.5.6",
|
|
47
47
|
"@types/react": "~18.3.1",
|
|
@@ -37,6 +37,26 @@ const t = {
|
|
|
37
37
|
scrollbarL2: 'var(--dsw-alias-scrollbar-bg-l2)',
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Nested pill tabs — unified with dsh-maestro-jobs (maestro-design: Minimalism & Swiss)
|
|
42
|
+
// Shared geometry: 40px min-height (44px jobs → 40px unified, touch-friendly ≥24px WCAG),
|
|
43
|
+
// 14px icon + 13px label gap 6, pill 999, border-l1, #EBEEF2 active, hover, focus ring.
|
|
44
|
+
// Icons: lucide-style 14px SVG, stroke 1.8, currentColor.
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
type TabIcon = 'globe' | 'git' | 'search' | 'shield' | 'ban' | 'cpu' | 'bell'
|
|
47
|
+
const TAB_ICON_PATHS: Record<TabIcon, string> = {
|
|
48
|
+
globe: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20M2 12h20M12 2a15 15 0 0 1 4 10 15 15 0 0 1-4 10 15 15 0 0 1-4-10A15 15 0 0 1 12 2z',
|
|
49
|
+
git: 'M15 22v-4a4.8 4.8 0 0 0-1-3.5c3 0 6-2 6-5.5 0-1.2-.4-2.3-1-3 0-1 0-2 0-3s-1 0-2 1a11 11 0 0 0-6 0c-1-1-2-1-2-1 0 1 0 2 0 3-.6.7-1 1.8-1 3 0 3.5 3 5.5 6 5.5-.4.3-.7.7-.9 1.2-.2.5-.3 1-.3 1.5V22',
|
|
50
|
+
search: 'M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14zM20 20l-3.5-3.5',
|
|
51
|
+
shield: 'M12 2l7 4v5c0 5-3.5 7.5-7 9-3.5-1.5-7-4-7-9V6l7-4z',
|
|
52
|
+
ban: 'M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM4 12h16',
|
|
53
|
+
cpu: 'M5 12H2a2 2 0 0 1 2-2h2M12 5V2a2 2 0 0 1 2 2v2M19 12h2a2 2 0 0 1-2 2h-2M12 19v2a2 2 0 0 1-2-2v-2M8 8h8v8H8z',
|
|
54
|
+
bell: 'M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9M13.7 21a2 2 0 0 1-3.4 0',
|
|
55
|
+
}
|
|
56
|
+
function TabIcon({ name, size = 14 }: { name: TabIcon; size?: number }) {
|
|
57
|
+
return h('svg', { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 1.8, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': 'true', style: { flex: 'none' } }, h('path', { d: TAB_ICON_PATHS[name] }))
|
|
58
|
+
}
|
|
59
|
+
|
|
40
60
|
// ---------------------------------------------------------------------------
|
|
41
61
|
// Lightweight DSH primitive mirrors (geometry + tokens identical to host)
|
|
42
62
|
// Usage is identical to @deepseek-ai/dsh-client-ui-primitives at runtime.
|
|
@@ -533,14 +553,15 @@ function ReviewModelSelector({
|
|
|
533
553
|
position: 'absolute',
|
|
534
554
|
top: 'calc(100% + 8px)',
|
|
535
555
|
left: '0',
|
|
536
|
-
minWidth: '300px',
|
|
537
|
-
maxWidth: '360px',
|
|
556
|
+
minWidth: 'min(300px, calc(100vw - 32px))',
|
|
557
|
+
maxWidth: 'min(360px, calc(100vw - 24px))',
|
|
538
558
|
background: t.bgLayer2 as string,
|
|
539
559
|
border: `1px solid ${t.borderL2}`,
|
|
540
560
|
borderRadius: '12px',
|
|
541
561
|
boxShadow: t.shadowLv3 as string,
|
|
542
562
|
zIndex: '20',
|
|
543
563
|
padding: '6px',
|
|
564
|
+
boxSizing: 'border-box' as string,
|
|
544
565
|
}
|
|
545
566
|
const rowStyle: Record<string, any> = {
|
|
546
567
|
width: '100%',
|
|
@@ -567,7 +588,7 @@ function ReviewModelSelector({
|
|
|
567
588
|
const modelLabel = selectedProvider === '' ? 'Select model' : (value?.model ?? 'Select model')
|
|
568
589
|
return h(
|
|
569
590
|
'div',
|
|
570
|
-
{ ref: rootRef as any, 'data-maestro-trigger-wrap': '', style: { position: 'relative', display: 'inline-block', maxWidth: '100%' } },
|
|
591
|
+
{ ref: rootRef as any, 'data-maestro-trigger-wrap': '', style: { position: 'relative', display: 'inline-block', maxWidth: '100%', minWidth: 0, boxSizing: 'border-box' as string } },
|
|
571
592
|
label ? h('span', { style: fieldLabelStyle }, label) : null,
|
|
572
593
|
h(
|
|
573
594
|
'button',
|
|
@@ -586,8 +607,10 @@ function ReviewModelSelector({
|
|
|
586
607
|
alignItems: 'center',
|
|
587
608
|
gap: 8,
|
|
588
609
|
cursor: 'pointer',
|
|
589
|
-
maxWidth:
|
|
610
|
+
maxWidth: 'min(320px, 100%)',
|
|
611
|
+
minWidth: 0,
|
|
590
612
|
whiteSpace: 'nowrap',
|
|
613
|
+
boxSizing: 'border-box' as string,
|
|
591
614
|
},
|
|
592
615
|
onClick: () => {
|
|
593
616
|
setOpen((v) => !v)
|
|
@@ -1054,6 +1077,150 @@ function NamedTunnelSetupNote() {
|
|
|
1054
1077
|
)
|
|
1055
1078
|
}
|
|
1056
1079
|
|
|
1080
|
+
// ---------------------------------------------------------------------------
|
|
1081
|
+
// PlaceholderMappingsEditor — row-based "blocked pattern → suggestion" mappings
|
|
1082
|
+
// (same interaction as ProjectMappingsEditor; no JSON editing for users)
|
|
1083
|
+
// ---------------------------------------------------------------------------
|
|
1084
|
+
function PlaceholderMappingsEditor({ rows, onChange }: { rows: { pattern: string; suggestion: string }[]; onChange: (rows: { pattern: string; suggestion: string }[]) => void }) {
|
|
1085
|
+
const updateRow = (index: number, field: 'pattern' | 'suggestion', value: string) => {
|
|
1086
|
+
onChange(rows.map((row, i) => (i === index ? { ...row, [field]: value } : row)))
|
|
1087
|
+
}
|
|
1088
|
+
const removeRow = (index: number) => onChange(rows.filter((_, i) => i !== index))
|
|
1089
|
+
const addRow = () => onChange([...rows, { pattern: '', suggestion: '' }])
|
|
1090
|
+
return h(
|
|
1091
|
+
'div',
|
|
1092
|
+
{ 'data-maestro-placeholders': '', style: { display: 'flex', flexDirection: 'column', gap: 12, paddingTop: 8 } },
|
|
1093
|
+
// Header — count + primary Add (stacks on mobile via CSS)
|
|
1094
|
+
h(
|
|
1095
|
+
'div',
|
|
1096
|
+
{ 'data-maestro-placeholders-header': '', style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, padding: '4px 0' } },
|
|
1097
|
+
h(
|
|
1098
|
+
'div',
|
|
1099
|
+
{ style: { flex: 1, minWidth: 0 } },
|
|
1100
|
+
h('div', { style: { fontSize: 14, fontWeight: 600, color: t.labelPrimary as string, lineHeight: '20px' } }, `Placeholder mappings — ${rows.length} mapped`),
|
|
1101
|
+
h('div', { style: { fontSize: 12, color: t.labelSecondary as string, lineHeight: '16px', marginTop: 2 } }, 'Blocked pattern → placeholder suggestion, one mapping per row'),
|
|
1102
|
+
),
|
|
1103
|
+
h(Button as any, { variant: 'primary', size: 'md', onClick: addRow, 'data-maestro-placeholders-add': '' }, '+ Add mapping'),
|
|
1104
|
+
),
|
|
1105
|
+
rows.length === 0
|
|
1106
|
+
? h(
|
|
1107
|
+
'div',
|
|
1108
|
+
{
|
|
1109
|
+
'data-maestro-placeholders-empty': '',
|
|
1110
|
+
style: {
|
|
1111
|
+
display: 'flex',
|
|
1112
|
+
flexDirection: 'column',
|
|
1113
|
+
alignItems: 'center',
|
|
1114
|
+
gap: 10,
|
|
1115
|
+
padding: '20px 16px',
|
|
1116
|
+
borderRadius: 12,
|
|
1117
|
+
border: `1px dashed ${t.borderL2}`,
|
|
1118
|
+
background: 'transparent',
|
|
1119
|
+
textAlign: 'center' as const,
|
|
1120
|
+
},
|
|
1121
|
+
},
|
|
1122
|
+
h('div', { style: { fontSize: 13, color: t.labelSecondary as string, lineHeight: '18px' } }, 'No placeholder mappings yet — add your first mapping'),
|
|
1123
|
+
h(Button as any, { variant: 'outline', size: 'md', onClick: addRow }, '+ Add mapping'),
|
|
1124
|
+
)
|
|
1125
|
+
: h(
|
|
1126
|
+
'div',
|
|
1127
|
+
{ 'data-maestro-placeholders-list': '', style: { display: 'flex', flexDirection: 'column', gap: 12 } },
|
|
1128
|
+
...rows.map((row, i) =>
|
|
1129
|
+
h(
|
|
1130
|
+
'div',
|
|
1131
|
+
{
|
|
1132
|
+
key: i,
|
|
1133
|
+
'data-maestro-mapping-card': '',
|
|
1134
|
+
style: {
|
|
1135
|
+
display: 'flex',
|
|
1136
|
+
flexDirection: 'column',
|
|
1137
|
+
gap: 12,
|
|
1138
|
+
padding: 12,
|
|
1139
|
+
borderRadius: 12,
|
|
1140
|
+
border: `1px solid ${t.borderL2}`,
|
|
1141
|
+
background: t.bgLayer1 as string,
|
|
1142
|
+
boxSizing: 'border-box' as const,
|
|
1143
|
+
},
|
|
1144
|
+
},
|
|
1145
|
+
// Card header: index badge + preview + X (same as Review's project card header)
|
|
1146
|
+
h(
|
|
1147
|
+
'div',
|
|
1148
|
+
{ style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12 } },
|
|
1149
|
+
h(
|
|
1150
|
+
'div',
|
|
1151
|
+
{ style: { display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flex: 1 } },
|
|
1152
|
+
h(
|
|
1153
|
+
'span',
|
|
1154
|
+
{
|
|
1155
|
+
style: {
|
|
1156
|
+
flex: 'none',
|
|
1157
|
+
width: 24,
|
|
1158
|
+
height: 24,
|
|
1159
|
+
borderRadius: 12,
|
|
1160
|
+
background: 'var(--dsw-alias-bg-module-platform, #F5F6F7)',
|
|
1161
|
+
display: 'inline-flex',
|
|
1162
|
+
alignItems: 'center',
|
|
1163
|
+
justifyContent: 'center',
|
|
1164
|
+
fontSize: 11,
|
|
1165
|
+
fontWeight: 600,
|
|
1166
|
+
color: t.labelSecondary as string,
|
|
1167
|
+
},
|
|
1168
|
+
},
|
|
1169
|
+
String(i + 1),
|
|
1170
|
+
),
|
|
1171
|
+
h(
|
|
1172
|
+
'span',
|
|
1173
|
+
{
|
|
1174
|
+
style: {
|
|
1175
|
+
fontSize: 12,
|
|
1176
|
+
fontFamily: 'ui-monospace, monospace',
|
|
1177
|
+
color: row.pattern ? (t.labelPrimary as string) : (t.labelTertiary as string),
|
|
1178
|
+
overflow: 'hidden',
|
|
1179
|
+
textOverflow: 'ellipsis',
|
|
1180
|
+
whiteSpace: 'nowrap',
|
|
1181
|
+
minWidth: 0,
|
|
1182
|
+
},
|
|
1183
|
+
},
|
|
1184
|
+
row.pattern ? (row.suggestion ? `${row.pattern} → ${row.suggestion}` : `${row.pattern} → …`) : 'New mapping',
|
|
1185
|
+
),
|
|
1186
|
+
),
|
|
1187
|
+
h(Button as any, { variant: 'outline', size: 'sm', onClick: () => removeRow(i), 'aria-label': `Remove mapping ${i + 1}`, title: 'Remove mapping' }, '✕'),
|
|
1188
|
+
),
|
|
1189
|
+
// Fields grid — 2-col desktop, 1-col mobile (same as Review's project grid)
|
|
1190
|
+
h(
|
|
1191
|
+
'div',
|
|
1192
|
+
{ 'data-maestro-mapping-grid': '', style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 } },
|
|
1193
|
+
h(
|
|
1194
|
+
'label',
|
|
1195
|
+
{ style: fieldLabelStyle, 'data-maestro-field': '' },
|
|
1196
|
+
'Blocked pattern',
|
|
1197
|
+
h(FieldInput as any, {
|
|
1198
|
+
value: row.pattern,
|
|
1199
|
+
placeholder: 'example-project',
|
|
1200
|
+
onChange: (e: any) => updateRow(i, 'pattern', e.target.value),
|
|
1201
|
+
'aria-label': `Blocked pattern ${i + 1}`,
|
|
1202
|
+
style: { width: '100%' } as any,
|
|
1203
|
+
}),
|
|
1204
|
+
),
|
|
1205
|
+
h(
|
|
1206
|
+
'label',
|
|
1207
|
+
{ style: fieldLabelStyle, 'data-maestro-field': '' },
|
|
1208
|
+
'Placeholder',
|
|
1209
|
+
h(FieldInput as any, {
|
|
1210
|
+
value: row.suggestion,
|
|
1211
|
+
placeholder: 'my-project',
|
|
1212
|
+
onChange: (e: any) => updateRow(i, 'suggestion', e.target.value),
|
|
1213
|
+
'aria-label': `Placeholder suggestion ${i + 1}`,
|
|
1214
|
+
style: { width: '100%' } as any,
|
|
1215
|
+
}),
|
|
1216
|
+
),
|
|
1217
|
+
),
|
|
1218
|
+
),
|
|
1219
|
+
),
|
|
1220
|
+
),
|
|
1221
|
+
)
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1057
1224
|
// ---------------------------------------------------------------------------
|
|
1058
1225
|
// Main — DSH-native grouped settings (DisclosureRow per domain)
|
|
1059
1226
|
// ---------------------------------------------------------------------------
|
|
@@ -1071,35 +1238,50 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1071
1238
|
const [showLanPin, setShowLanPin] = useState(false)
|
|
1072
1239
|
const [guard, setGuard] = useState<any>({})
|
|
1073
1240
|
const [patternsText, setPatternsText] = useState('')
|
|
1074
|
-
const [
|
|
1241
|
+
const [placeholderRows, setPlaceholderRows] = useState<{ pattern: string; suggestion: string }[]>([])
|
|
1075
1242
|
const [supervisorCfg, setSupervisorCfg] = useState<any>({})
|
|
1076
1243
|
const [notifierCfg, setNotifierCfg] = useState<any>({})
|
|
1077
1244
|
const [activeTab, setActiveTab] = useState('tunnel')
|
|
1078
1245
|
// Mobile: inject responsive overrides once (mirrors dsh-maestro-mobile settings-sheet pill pattern + market catsWrap)
|
|
1079
1246
|
useEffect(() => {
|
|
1080
1247
|
const css = `
|
|
1081
|
-
/*
|
|
1082
|
-
[data-maestro-
|
|
1248
|
+
/* Model override dropdown — prevent right-edge overlap */
|
|
1249
|
+
[data-maestro-control] [data-maestro-menu] { left:auto !important; right:0 !important; }
|
|
1250
|
+
[data-maestro-project-card] [data-maestro-menu] { left:auto !important; right:0 !important; }
|
|
1251
|
+
[data-maestro-trigger-wrap] { min-width:0; }
|
|
1252
|
+
/* Maestro nested tabs — unified with dsh-maestro-jobs (maestro-design pill bar) */
|
|
1253
|
+
[data-maestro-tabs] { display:flex; gap:6px; overflow-x:auto; overflow-y:hidden; scrollbar-width:none; -webkit-overflow-scrolling:touch; overscroll-behavior-x:contain; touch-action:pan-x; padding:2px 2px 8px; margin:0 -2px 4px; border-bottom:1px solid var(--dsw-alias-border-l1, rgba(0,0,0,.08)); }
|
|
1083
1254
|
[data-maestro-tabs]::-webkit-scrollbar { display:none; width:0; height:0; }
|
|
1084
|
-
[data-maestro-tab] { flex:none;
|
|
1085
|
-
[data-maestro-tab][data-active="true"] { background:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); border-color:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); color:var(--dsw-alias-label-primary); }
|
|
1255
|
+
[data-maestro-tab] { flex:none; min-width:fit-content; min-height:40px; padding:0 14px; border-radius:999px; border:1px solid var(--dsw-alias-border-l1, rgba(0,0,0,.12)); background:transparent; color:var(--dsw-alias-label-secondary); font-size:13px; line-height:20px; font-weight:500; font-family:inherit; white-space:nowrap; display:inline-flex; align-items:center; justify-content:center; gap:6px; cursor:pointer; -webkit-tap-highlight-color:transparent; transition: background 150ms ease, color 150ms ease, border-color 150ms ease; touch-action:manipulation; }
|
|
1256
|
+
[data-maestro-tab][data-active="true"] { background:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); border-color:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); color:var(--dsw-alias-label-primary); font-weight:600; }
|
|
1086
1257
|
[data-maestro-tab]:hover { background:var(--dsw-alias-interactive-bg-hover, rgba(0,0,0,.06)); }
|
|
1087
1258
|
[data-maestro-tab][data-active="true"]:hover { background:var(--dsw-specific-sidebar-nav-item-active, #EBEEF2); }
|
|
1088
1259
|
[data-maestro-tab]:focus-visible { outline:2px solid var(--dsw-alias-state-business-primary, #4f6ef7); outline-offset:1px; }
|
|
1260
|
+
[data-maestro-tab]:active { transform: scale(.97); }
|
|
1089
1261
|
[data-maestro-panel] { width:100%; min-width:0; box-sizing:border-box; }
|
|
1090
|
-
/* label
|
|
1262
|
+
/* label/input overlap fix: flex column gap + full width */
|
|
1091
1263
|
[data-maestro-panel] label { gap:6px !important; }
|
|
1092
1264
|
[data-maestro-row]:last-child { border-bottom:none !important; }
|
|
1093
1265
|
[data-maestro-panel] label > span { width:100% !important; box-sizing:border-box !important; }
|
|
1266
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1267
|
+
[data-maestro-tab] { transition: none !important; }
|
|
1268
|
+
[data-maestro-tab]:active { transform: none !important; }
|
|
1269
|
+
}
|
|
1094
1270
|
@media (max-width: 640px) {
|
|
1095
1271
|
[data-maestro-settings-card] { max-width:100% !important; gap:6px !important; padding:0 2px !important; }
|
|
1096
|
-
[data-maestro-tabs] { gap:6px !important; padding:
|
|
1097
|
-
[data-maestro-tab] { height:
|
|
1098
|
-
|
|
1099
|
-
[data-maestro-
|
|
1272
|
+
[data-maestro-tabs] { gap:6px !important; padding:2px 2px 8px !important; margin:0 -2px 8px !important; }
|
|
1273
|
+
[data-maestro-tab] { min-height:40px !important; height:auto !important; padding:0 12px !important; font-size:13px !important; }
|
|
1274
|
+
/* Placeholder mappings — card design unified with Review project cards (header X, grid fields) */
|
|
1275
|
+
[data-maestro-placeholders-header] { flex-direction: column !important; align-items: stretch !important; }
|
|
1276
|
+
[data-maestro-placeholders-header] [data-maestro-placeholders-add] { width: 100% !important; justify-content: center !important; }
|
|
1277
|
+
[data-maestro-placeholders-list] { gap: 12px !important; }
|
|
1278
|
+
[data-maestro-mapping-card] { padding:10px !important; }
|
|
1279
|
+
[data-maestro-mapping-grid] { grid-template-columns:1fr !important; gap:12px !important; }
|
|
1100
1280
|
[data-maestro-qr-row] { flex-direction:column !important; align-items:flex-start !important; }
|
|
1101
1281
|
[data-maestro-trigger-wrap] { max-width:100% !important; }
|
|
1102
1282
|
[data-maestro-menu] { min-width:0 !important; max-width:calc(100vw - 32px) !important; left:0 !important; right:auto !important; }
|
|
1283
|
+
[data-maestro-control] [data-maestro-menu] { left:0 !important; right:auto !important; }
|
|
1284
|
+
[data-maestro-project-card] [data-maestro-menu] { left:0 !important; right:auto !important; }
|
|
1103
1285
|
[data-maestro-panel] label { gap:8px !important; }
|
|
1104
1286
|
div[data-maestro-row] { flex-direction:column !important; align-items:stretch !important; padding:12px 0 !important; }
|
|
1105
1287
|
[data-maestro-row-text] { padding-right:0 !important; }
|
|
@@ -1112,7 +1294,7 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1112
1294
|
[data-maestro-project-profile-row] > label { flex:1 1 100% !important; width:100% !important; }
|
|
1113
1295
|
}
|
|
1114
1296
|
@media (max-width: 390px) {
|
|
1115
|
-
[data-maestro-tab] { height:
|
|
1297
|
+
[data-maestro-tab] { min-height:38px !important; height:auto !important; padding:0 10px !important; font-size:12px !important; }
|
|
1116
1298
|
}
|
|
1117
1299
|
`
|
|
1118
1300
|
const tag = document.createElement('style')
|
|
@@ -1165,15 +1347,13 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1165
1347
|
setError(e.message ?? String(e))
|
|
1166
1348
|
}
|
|
1167
1349
|
}
|
|
1168
|
-
const commitPlaceholders = async () => {
|
|
1350
|
+
const commitPlaceholders = async (rows: { pattern: string; suggestion: string }[]) => {
|
|
1169
1351
|
setError(null)
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
if (
|
|
1174
|
-
|
|
1175
|
-
setError(`placeholders JSON invalid: ${e.message ?? String(e)}`)
|
|
1176
|
-
return
|
|
1352
|
+
const obj: Record<string, string> = {}
|
|
1353
|
+
for (const row of rows) {
|
|
1354
|
+
const pattern = (row.pattern ?? '').trim()
|
|
1355
|
+
if (!pattern) continue
|
|
1356
|
+
obj[pattern] = (row.suggestion ?? '').trim()
|
|
1177
1357
|
}
|
|
1178
1358
|
try {
|
|
1179
1359
|
await cfgSet('guardBlacklist', { placeholders: obj })
|
|
@@ -1196,6 +1376,7 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1196
1376
|
const next = { ...prev }
|
|
1197
1377
|
for (const [k, v] of Object.entries(patch)) {
|
|
1198
1378
|
if (k === 'telegram' && typeof v === 'object' && v !== null) (next as any).telegram = { ...((prev as any).telegram ?? {}), ...(v as any) }
|
|
1379
|
+
else if (k === 'policy' && typeof v === 'object' && v !== null) (next as any).policy = { ...((prev as any).policy ?? {}), ...(v as any) }
|
|
1199
1380
|
else (next as any)[k] = v
|
|
1200
1381
|
}
|
|
1201
1382
|
return next
|
|
@@ -1230,7 +1411,7 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1230
1411
|
const pats = Array.isArray((bl as any)?.patterns) ? (bl as any).patterns : []
|
|
1231
1412
|
const ph = (bl as any)?.placeholders && typeof (bl as any).placeholders === 'object' ? (bl as any).placeholders : {}
|
|
1232
1413
|
setPatternsText(pats.join('\n'))
|
|
1233
|
-
|
|
1414
|
+
setPlaceholderRows(Object.entries(ph).map(([pattern, suggestion]) => ({ pattern, suggestion: String(suggestion) })))
|
|
1234
1415
|
setSupervisorCfg(sup ?? {})
|
|
1235
1416
|
setNotifierCfg(not ?? {})
|
|
1236
1417
|
})
|
|
@@ -1348,17 +1529,16 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1348
1529
|
}
|
|
1349
1530
|
}
|
|
1350
1531
|
|
|
1351
|
-
// Nested tabs
|
|
1352
|
-
const TABS = [
|
|
1353
|
-
{ id: 'tunnel', label: 'Tunnel' },
|
|
1354
|
-
{ id: '
|
|
1355
|
-
{ id: '
|
|
1356
|
-
{ id: '
|
|
1357
|
-
{ id: '
|
|
1358
|
-
{ id: '
|
|
1359
|
-
{ id: '
|
|
1360
|
-
|
|
1361
|
-
] as const
|
|
1532
|
+
// Nested tabs — unified pill bar with icons (maestro-design, matches dsh-maestro-jobs)
|
|
1533
|
+
const TABS: Array<{ id: string; label: string; icon: TabIcon }> = [
|
|
1534
|
+
{ id: 'tunnel', label: 'Tunnel', icon: 'globe' },
|
|
1535
|
+
{ id: 'gitlab', label: 'GitLab', icon: 'git' },
|
|
1536
|
+
{ id: 'review', label: 'Review', icon: 'search' },
|
|
1537
|
+
{ id: 'guard', label: 'Guard', icon: 'shield' },
|
|
1538
|
+
{ id: 'blacklist', label: 'Blacklist', icon: 'ban' },
|
|
1539
|
+
{ id: 'supervisor', label: 'Supervisor', icon: 'cpu' },
|
|
1540
|
+
{ id: 'notifier', label: 'Notifier', icon: 'bell' },
|
|
1541
|
+
]
|
|
1362
1542
|
|
|
1363
1543
|
const tabContents: Record<string, unknown> = {
|
|
1364
1544
|
tunnel: h(
|
|
@@ -1377,14 +1557,6 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1377
1557
|
h('div', { style: { ...cardInsetStyle, marginTop: '12px' } }, h('div', { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary as string } }, 'Remote access — LAN'), h(LanAccess as any, { proxyStatus, lanPin: lanPinEnabled === null ? null : { enabled: lanPinEnabled, pin: lanPin, show: showLanPin, onShow: revealLanPin, onHide: () => setShowLanPin(false), onRotate: rotateLanPin, onToggle: toggleLanPin } })),
|
|
1378
1558
|
h('div', { style: { ...cardInsetStyle, marginTop: '12px' } }, h('div', { style: { fontSize: 13, fontWeight: 600, color: t.labelPrimary as string } }, 'Public access'), h(PublicAccess as any, { status, pin, showPin, onRevealPin: revealPin, onHidePin: () => setShowPin(false), onRotatePin: rotatePin })),
|
|
1379
1559
|
),
|
|
1380
|
-
notify: h(
|
|
1381
|
-
'div',
|
|
1382
|
-
{ style: { display: 'flex', flexDirection: 'column' } },
|
|
1383
|
-
h('div', { style: { padding: '12px 0', borderBottom: `1px solid ${t.borderL2}` } }, h('p', { style: captionStyle }, 'Sends one protected startup update with the current public-access PIN to a single chat. Leave either credential blank to disable.')),
|
|
1384
|
-
h(SettingRow as any, { title: 'Bot token', description: 'Telegram bot token from @BotFather.', control: h('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } }, h(FieldInput as any, { placeholder: '123456:ABC-DEF...', type: 'password', autoComplete: 'off', value: config.hasTelegramBotToken ? '••••••••' : '', onChange: (e: any) => saveField('telegramBotToken', e.target.value), style: { width: 220 } as any }), config.hasTelegramBotToken ? h(Button as any, { variant: 'outline', size: 'md', onClick: () => saveField('telegramBotToken', '') }, 'Clear') : null) }),
|
|
1385
|
-
h(SettingRow as any, { title: 'Chat ID', description: 'Target chat, e.g. -1001234567890.', control: h(FieldInput as any, { placeholder: '-1001234567890', value: config.telegramChatId ?? '', onChange: (e: any) => saveField('telegramChatId', e.target.value), style: { width: 220 } as any }) }),
|
|
1386
|
-
h(ToggleRow as any, { title: 'Also notify about reviews', description: 'One message per review run with its outcome.', checked: config.telegramReviewNotifications === true, onChange: (v: boolean) => saveField('telegramReviewNotifications', v) }),
|
|
1387
|
-
),
|
|
1388
1560
|
gitlab: h(
|
|
1389
1561
|
'div',
|
|
1390
1562
|
{ style: { display: 'flex', flexDirection: 'column' } },
|
|
@@ -1424,9 +1596,15 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1424
1596
|
),
|
|
1425
1597
|
h('div', { style: { ...rowStyle, flexDirection:'column', alignItems:'stretch', gap: 8, borderBottom:'none' } as any },
|
|
1426
1598
|
h('div', { style: rowTitleStyle }, 'Placeholder mappings'),
|
|
1427
|
-
h('div', { style: rowDescStyle }, '
|
|
1428
|
-
h(
|
|
1429
|
-
|
|
1599
|
+
h('div', { style: rowDescStyle }, 'Blocked pattern → placeholder suggestion. One mapping per row — no JSON needed.'),
|
|
1600
|
+
h(PlaceholderMappingsEditor as any, {
|
|
1601
|
+
rows: placeholderRows,
|
|
1602
|
+
onChange: (rows: any) => {
|
|
1603
|
+
setPlaceholderRows(rows)
|
|
1604
|
+
commitPlaceholders(rows)
|
|
1605
|
+
},
|
|
1606
|
+
}),
|
|
1607
|
+
h('div', { style: { marginTop: 8 } }, h(Button as any, { variant: 'outline', size: 'sm', onClick: () => { commitBlacklistPatterns(patternsText); commitPlaceholders(placeholderRows) } }, 'Save Blacklist')),
|
|
1430
1608
|
),
|
|
1431
1609
|
),
|
|
1432
1610
|
supervisor: h(
|
|
@@ -1439,9 +1617,10 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1439
1617
|
notifier: h(
|
|
1440
1618
|
'div',
|
|
1441
1619
|
{ style: { display: 'flex', flexDirection: 'column' } },
|
|
1442
|
-
h(
|
|
1443
|
-
h(SettingRow as any, { title: '
|
|
1444
|
-
h(
|
|
1620
|
+
h('div', { style: { padding: '12px 0', borderBottom: `1px solid ${t.borderL2}` } }, h('p', { style: captionStyle }, 'Telegram bot settings for notifications: startup PIN, review digests, PIN rotation. Leave blank to disable.')),
|
|
1621
|
+
h(SettingRow as any, { title: 'Bot token', description: 'Telegram bot token from @BotFather.', control: h('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } }, h(FieldInput as any, { type: 'password', autoComplete: 'off', value: notifierCfg.telegram?.botToken ?? '', placeholder: '123456:ABC-DEF...', onChange: (e: any) => saveNotifierCfg({ telegram: { botToken: e.target.value } }), style: { width: 220 } as any }), notifierCfg.telegram?.botToken ? h(Button as any, { variant: 'outline', size: 'md', onClick: () => saveNotifierCfg({ telegram: { botToken: '' } }) }, 'Clear') : null) }),
|
|
1622
|
+
h(SettingRow as any, { title: 'Chat ID', description: 'Target chat, e.g. -1001234567890.', control: h(FieldInput as any, { value: notifierCfg.telegram?.chatId ?? '', placeholder: '-1001234567890', onChange: (e: any) => saveNotifierCfg({ telegram: { chatId: e.target.value } }), style: { width: 220 } as any }) }),
|
|
1623
|
+
h(ToggleRow as any, { title: 'Review notifications', description: 'Also notify about finished reviews.', checked: notifierCfg.policy?.reviewNotifications === true, onChange: (v: boolean) => saveNotifierCfg({ policy: { reviewNotifications: v } }) }),
|
|
1445
1624
|
),
|
|
1446
1625
|
}
|
|
1447
1626
|
|
|
@@ -1450,12 +1629,19 @@ export function MaestroSettingsTab({ rpcCall, configRpcCall }: { rpcCall: any; c
|
|
|
1450
1629
|
{ 'data-maestro-settings-card': '', style: { display: 'flex', flexDirection: 'column', gap: 8, width: '100%', maxWidth: 640, minWidth:0, boxSizing:'border-box' as any } },
|
|
1451
1630
|
h(
|
|
1452
1631
|
'div',
|
|
1453
|
-
{ style: { padding: '2px 2px 8px' } },
|
|
1454
|
-
|
|
1455
|
-
h('
|
|
1632
|
+
{ style: { padding: '2px 2px 8px', display: 'flex', gap: 10, alignItems: 'flex-start' } },
|
|
1633
|
+
// Shared BrandBadge — same as dashboard sidebar/popup (BrandMark #0A84FF)
|
|
1634
|
+
h('span', { 'data-maestro-logo': '', style: { width: 28, height: 28, borderRadius: 8, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: 'var(--dsw-alias-brand-primary, #0A84FF)', backgroundColor: '#0A84FF', color: '#fff', flex: 'none', border: '1px solid rgba(0,0,0,0.08)', boxShadow: '0 0 0 1px var(--dsw-alias-border-l1)', boxSizing: 'border-box' as any, alignSelf: 'flex-start' as any, marginTop: 2 } } as any,
|
|
1635
|
+
h('svg', { width: 16, height: 16, viewBox: '0 0 16 16', fill: 'none', 'aria-hidden': 'true' } as any, h('path', { d: 'M2 11 L5 4 L8 9 L11 4 L14 11', stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' } as any))
|
|
1636
|
+
),
|
|
1637
|
+
h('div', { style: { display: 'flex', flexDirection: 'column', minWidth: 0 } },
|
|
1638
|
+
h('div', { style: { fontSize: 15, fontWeight: 600, color: t.labelPrimary as string, lineHeight: '22px' } }, 'Maestro'),
|
|
1639
|
+
h('div', { style: { fontSize: 12, color: t.labelSecondary as string, lineHeight: '16px', marginTop: 2 } }, 'Tunnel, access, review & guard — all via the shared Maestro store. Uses the same tokens and primitives as DSH settings.'),
|
|
1640
|
+
)
|
|
1456
1641
|
),
|
|
1642
|
+
h('style', {}, '[data-maestro-logo]{background:#0A84FF !important; background-color:#0A84FF !important; color:#fff !important;}'),
|
|
1457
1643
|
h('div', { 'data-maestro-tabs': '', role:'tablist', 'aria-label':'Maestro settings sections' },
|
|
1458
|
-
...TABS.map(tab => h('button', { key: tab.id, 'data-maestro-tab':'', 'data-active': String(activeTab===tab.id), role:'tab', 'aria-selected': activeTab===tab.id, onClick: () => setActiveTab(tab.id) }, tab.label))
|
|
1644
|
+
...TABS.map(tab => h('button', { key: tab.id, 'data-maestro-tab':'', 'data-active': String(activeTab===tab.id), role:'tab', 'aria-selected': activeTab===tab.id, onClick: (e: any) => { setActiveTab(tab.id); try { e.currentTarget.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }) } catch {} } }, h(TabIcon as any, { name: tab.icon }), tab.label))
|
|
1459
1645
|
),
|
|
1460
1646
|
h('div', { 'data-maestro-panel': activeTab, style: { display:'flex', flexDirection:'column', gap:10, minWidth:0 } }, tabContents[activeTab] as any),
|
|
1461
1647
|
error ? h('p', { style: { color: t.stateError as string, fontSize: 12, margin: '8px 0 0', padding: '8px 10px', borderRadius: 8, background: 'color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent)', border: `1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 30%, transparent)` } }, error) : null,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createElement as h } from 'react'
|
|
2
|
+
|
|
3
|
+
export function MaestroMark(props: { size?: number }) {
|
|
4
|
+
const s = props.size ?? 16
|
|
5
|
+
return h('svg', { width: s, height: s, viewBox: '0 0 16 16', fill: 'none', 'aria-hidden': 'true' } as any,
|
|
6
|
+
h('path', { d: 'M2 11 L5 4 L8 9 L11 4 L14 11', stroke: 'currentColor', strokeWidth: 1.6, strokeLinecap: 'round', strokeLinejoin: 'round' } as any)
|
|
7
|
+
)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function BrandBadge(props: { size?: number; outer?: number; radius?: number }) {
|
|
11
|
+
const outer = props.outer ?? 28
|
|
12
|
+
const size = props.size ?? 16
|
|
13
|
+
const radius = props.radius ?? 8
|
|
14
|
+
return h('span', {
|
|
15
|
+
'data-maestro-logo': '',
|
|
16
|
+
style: {
|
|
17
|
+
width: outer, height: outer, borderRadius: radius, display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
|
18
|
+
background: 'var(--dsw-alias-brand-primary, #0A84FF)', backgroundColor: '#0A84FF', color: '#fff', flex: 'none',
|
|
19
|
+
border: '1px solid rgba(0,0,0,0.08)', boxShadow: '0 0 0 1px var(--dsw-alias-border-l1)', boxSizing: 'border-box' as any,
|
|
20
|
+
},
|
|
21
|
+
} as any, h(MaestroMark as any, { size }))
|
|
22
|
+
}
|
package/src/client/index.tsx
CHANGED
|
@@ -13,9 +13,10 @@ import { registerSettingsNavIcon, SETTINGS_NAV_MARKER } from './settings-nav-ico
|
|
|
13
13
|
*/
|
|
14
14
|
const SETTINGS_NAV_CSS = `
|
|
15
15
|
|
|
16
|
-
/* maestro: replace the settings-nav fallback gear with the Maestro M-logo glyph */
|
|
17
|
-
[${SETTINGS_NAV_MARKER}] > svg:first-child
|
|
18
|
-
|
|
16
|
+
/* maestro: replace the settings-nav fallback gear with the Maestro M-logo glyph — same mark as sidebar/popup BrandMark */
|
|
17
|
+
[${SETTINGS_NAV_MARKER}] > svg:first-child,
|
|
18
|
+
[${SETTINGS_NAV_MARKER}] > svg.zWKi1a_navIcon {
|
|
19
|
+
display: none !important;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
[${SETTINGS_NAV_MARKER}]::before {
|
|
@@ -23,9 +24,10 @@ const SETTINGS_NAV_CSS = `
|
|
|
23
24
|
flex: none;
|
|
24
25
|
width: 16px;
|
|
25
26
|
height: 16px;
|
|
27
|
+
display: inline-block;
|
|
26
28
|
background: currentColor;
|
|
27
|
-
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.
|
|
28
|
-
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.
|
|
29
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 11 L5 4 L8 9 L11 4 L14 11'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
30
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2 11 L5 4 L8 9 L11 4 L14 11'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
29
31
|
}
|
|
30
32
|
`
|
|
31
33
|
|