dsh-flyout-sidebar 0.1.9 → 0.1.10
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/dist/client.js +238 -129
- package/dist/index.js +145 -38
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -358,17 +358,9 @@ body[data-ds-dark-theme] .tok-function, body[data-ds-dark-theme] .tok-decorator
|
|
|
358
358
|
body[data-ds-dark-theme] .tok-class, body[data-ds-dark-theme] .tok-builtin, body[data-ds-dark-theme] .tok-tag, body[data-ds-dark-theme] .tok-key { color: #74c0fc; }
|
|
359
359
|
body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
360
360
|
|
|
361
|
-
/*
|
|
362
|
-
|
|
363
|
-
.fs-settings-
|
|
364
|
-
.fs-settings-open, .fs-settings-open:hover { background: var(--dsw-alias-bg-layer-2); border-color: var(--dsw-alias-label-dimmed); }
|
|
365
|
-
.fs-settings-head { width: 100%; appearance: none; border: 0; background: none; font: inherit; color: inherit; text-align: left; cursor: pointer; display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 12px; }
|
|
366
|
-
.fs-settings-headtext { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
|
|
367
|
-
.fs-settings-name { font-size: 15px; font-weight: 600; line-height: 1.4; color: var(--dsw-alias-label-primary); }
|
|
368
|
-
.fs-settings-desc { font-size: 13px; line-height: 1.5; color: var(--dsw-alias-label-tertiary); }
|
|
369
|
-
.fs-settings-chevron { flex: none; color: var(--dsw-alias-label-tertiary); transition: transform var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out); }
|
|
370
|
-
.fs-settings-open .fs-settings-chevron { transform: rotate(180deg); }
|
|
371
|
-
.fs-settings-body { border-top: 1px solid var(--dsw-alias-border-l2); margin: 0 16px; padding: 4px 0 12px; display: flex; flex-direction: column; gap: 0; }
|
|
361
|
+
/* 插件配置页(插件页 → 本插件的行 → 配置):行/标题/描述由插件管理页绘制,
|
|
362
|
+
这里只负责表单本体与保存控件,跟随 DSH 设计令牌。 */
|
|
363
|
+
.fs-settings-form { display: flex; flex-direction: column; gap: 0; }
|
|
372
364
|
.fs-settings-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; }
|
|
373
365
|
.fs-settings-label { flex: 1; min-width: 0; font-size: 14px; color: var(--dsw-alias-label-primary); }
|
|
374
366
|
.fs-settings-input {
|
|
@@ -380,6 +372,7 @@ body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
|
380
372
|
}
|
|
381
373
|
.fs-settings-input:hover:not(:disabled) { background: var(--dsw-alias-interactive-bg-hover); }
|
|
382
374
|
.fs-settings-input:disabled { opacity: .5; cursor: default; }
|
|
375
|
+
.fs-settings-input[data-invalid="true"] { border-color: var(--dsw-alias-accent-error, #e5484d); }
|
|
383
376
|
.fs-settings-toggle {
|
|
384
377
|
position: relative; width: 40px; height: 22px; border-radius: 11px;
|
|
385
378
|
border: none; cursor: pointer; padding: 0; flex: none;
|
|
@@ -393,6 +386,18 @@ body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
|
393
386
|
background: #fff; transition: transform var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out);
|
|
394
387
|
}
|
|
395
388
|
.fs-settings-toggle[data-on="true"]::after { transform: translateX(18px); }
|
|
389
|
+
.fs-settings-actions { display: flex; align-items: center; gap: 10px; padding: 12px 0 4px; }
|
|
390
|
+
.fs-settings-save {
|
|
391
|
+
appearance: none; border: 1px solid transparent; border-radius: 8px; cursor: pointer;
|
|
392
|
+
padding: 5px 14px; font: inherit; font-size: 13px; font-weight: 500;
|
|
393
|
+
background: var(--dsw-alias-accent-primary, #2a7fbf); color: #fff;
|
|
394
|
+
transition: opacity var(--ds-transition-duration, 0.2s) var(--ds-ease-in-out, ease-in-out);
|
|
395
|
+
}
|
|
396
|
+
.fs-settings-save:disabled { opacity: .45; cursor: default; }
|
|
397
|
+
.fs-settings-status, .fs-settings-note {
|
|
398
|
+
font-size: 12px; line-height: 1.5; color: var(--dsw-alias-label-tertiary);
|
|
399
|
+
}
|
|
400
|
+
.fs-settings-note { margin: 4px 0 8px; }
|
|
396
401
|
`;
|
|
397
402
|
/** 注入样式(幂等:内容不一致时替换,使热重载后新样式必然生效) */
|
|
398
403
|
function insertStyles() {
|
|
@@ -554,7 +559,13 @@ body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
|
554
559
|
settingsAutoRefresh: "打开面板时自动刷新",
|
|
555
560
|
settingsMinWidth: "面板最小宽度(%)",
|
|
556
561
|
settingsDefaultOpen: "页面加载后默认展开",
|
|
557
|
-
settingsFontSize: "内容区字号(px)"
|
|
562
|
+
settingsFontSize: "内容区字号(px)",
|
|
563
|
+
settingsSave: "保存",
|
|
564
|
+
settingsSaving: "保存中…",
|
|
565
|
+
settingsSaveFailed: "宿主未接受这些值,已保留供你修改。",
|
|
566
|
+
settingsReadonly: "本部署的设置为只读。",
|
|
567
|
+
settingsUnavailable: "该插件当前未加载,暂时无法配置。",
|
|
568
|
+
settingsInvalidNumber: "请填范围内的数字;留空表示恢复默认。"
|
|
558
569
|
};
|
|
559
570
|
/** @type {Record<string, string>} */
|
|
560
571
|
const EN = {
|
|
@@ -630,7 +641,13 @@ body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
|
630
641
|
settingsAutoRefresh: "Auto-refresh on panel open",
|
|
631
642
|
settingsMinWidth: "Min panel width (%)",
|
|
632
643
|
settingsDefaultOpen: "Open by default on page load",
|
|
633
|
-
settingsFontSize: "Content font size (px)"
|
|
644
|
+
settingsFontSize: "Content font size (px)",
|
|
645
|
+
settingsSave: "Save",
|
|
646
|
+
settingsSaving: "Saving…",
|
|
647
|
+
settingsSaveFailed: "The deployment did not accept these values; they were left for you to correct.",
|
|
648
|
+
settingsReadonly: "This deployment stores settings read-only.",
|
|
649
|
+
settingsUnavailable: "This plugin is not loaded, so it cannot be configured right now.",
|
|
650
|
+
settingsInvalidNumber: "Enter a number within range, or leave blank to use the default."
|
|
634
651
|
};
|
|
635
652
|
/** @type {Record<string, Record<string, string>>} */
|
|
636
653
|
const DICTS = {
|
|
@@ -878,11 +895,11 @@ body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
|
878
895
|
* 从宿主拉取插件配置(config 已由 cordis 校验填充默认值)并通知订阅者。
|
|
879
896
|
* 配置保存在宿主侧,页面内不持久化;面板打开即用最新值。
|
|
880
897
|
*
|
|
881
|
-
* syncDefaultOpen=true
|
|
882
|
-
*
|
|
883
|
-
*
|
|
884
|
-
*
|
|
885
|
-
*
|
|
898
|
+
* syncDefaultOpen=true(启动首次加载)时,若本次拉取确实改动了 defaultOpen
|
|
899
|
+
* 字段,则实时同步边栏开合 —— 用户改完「默认展开」设置后重新加载页面,边栏
|
|
900
|
+
* 立即按新偏好展开/收起。面板摊开时例行拉取(刷新 minPanelWidth 等设置)不传
|
|
901
|
+
* 此 flag:只刷新配置,绝不回写开合状态,否则刚点开的边栏会被 defaultOpen
|
|
902
|
+
* 当场压回(点击无反应)。
|
|
886
903
|
*/
|
|
887
904
|
load(opts) {
|
|
888
905
|
const syncDefaultOpen = !!(opts && opts.syncDefaultOpen);
|
|
@@ -2998,95 +3015,223 @@ body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
|
2998
3015
|
onClick: () => store.toggle()
|
|
2999
3016
|
}, /* @__PURE__ */ h(PanelIcon, { size: 18 }));
|
|
3000
3017
|
}
|
|
3018
|
+
//#endregion
|
|
3019
|
+
//#region src/client/config.tsx
|
|
3020
|
+
/**
|
|
3021
|
+
* Client 侧:插件配置页(插件页 → 本插件的详情页 → 配置表单)。
|
|
3022
|
+
*
|
|
3023
|
+
* 新版 DSH 客户端不再为插件的 volatile 配置自动生成设置卡片:配置页必须由插件
|
|
3024
|
+
* 自己注册。bundle 级配置注册进 `plugins.bundle.config`(key = 包名),宿主插件
|
|
3025
|
+
* 管理页会把它直接渲染在本插件的详情页上、位于描述与行列表之间(仅 `page`
|
|
3026
|
+
* 视图)。宿主侧不需要任何注册 —— settings 服务直接投影 Loader 条目里带
|
|
3027
|
+
* `.volatile()` 的字段,以条目 id(= 行 id)作为 namespace,配置值经
|
|
3028
|
+
* `ctx.configForms.get(条目 id)` 读写(插件管理页把它作为 owner props 的 `form`
|
|
3029
|
+
* 传下来,注册方通过注入的表单引用拿实时快照)。
|
|
3030
|
+
*
|
|
3031
|
+
* 表单是「一页一存」:控件只改本地草稿,保存时把所有改动合成一次带 revision
|
|
3032
|
+
* fence 的 mutate —— 与官方伴生包的 SettingsFormModel 语义一致,这样一次保存
|
|
3033
|
+
* 只产生一次宿主写入,用户也能在保存前看到自己要写什么。
|
|
3034
|
+
*/
|
|
3035
|
+
/** bundle config slot 的注册 key:包名(dsh-flyout-sidebar) */
|
|
3036
|
+
const BUNDLE_CONFIG_KEY = "dsh-flyout-sidebar";
|
|
3037
|
+
/** 宿主 settings 的 namespace:profile 条目 id,也就是插件行的 row id */
|
|
3038
|
+
const SETTINGS_NAMESPACE = "flyout-sidebar";
|
|
3039
|
+
const FIELDS = [
|
|
3040
|
+
{
|
|
3041
|
+
field: "autoRefresh",
|
|
3042
|
+
labelKey: "settingsAutoRefresh",
|
|
3043
|
+
kind: "toggle",
|
|
3044
|
+
min: 0,
|
|
3045
|
+
max: 0
|
|
3046
|
+
},
|
|
3047
|
+
{
|
|
3048
|
+
field: "defaultOpen",
|
|
3049
|
+
labelKey: "settingsDefaultOpen",
|
|
3050
|
+
kind: "toggle",
|
|
3051
|
+
min: 0,
|
|
3052
|
+
max: 0
|
|
3053
|
+
},
|
|
3054
|
+
{
|
|
3055
|
+
field: "minPanelWidth",
|
|
3056
|
+
labelKey: "settingsMinWidth",
|
|
3057
|
+
kind: "number",
|
|
3058
|
+
min: 15,
|
|
3059
|
+
max: 60
|
|
3060
|
+
},
|
|
3061
|
+
{
|
|
3062
|
+
field: "contentFontSize",
|
|
3063
|
+
labelKey: "settingsFontSize",
|
|
3064
|
+
kind: "number",
|
|
3065
|
+
min: 11,
|
|
3066
|
+
max: 20
|
|
3067
|
+
}
|
|
3068
|
+
];
|
|
3069
|
+
/** 宿主值 → 草稿文本:开关用 'true'/'false',数字无值时留空(= 未覆盖) */
|
|
3070
|
+
function seedText(spec, stored) {
|
|
3071
|
+
if (spec.kind === "toggle") return stored === true ? "true" : "false";
|
|
3072
|
+
return typeof stored === "number" && Number.isFinite(stored) ? String(stored) : "";
|
|
3073
|
+
}
|
|
3074
|
+
function seed(values) {
|
|
3075
|
+
const out = {};
|
|
3076
|
+
for (const spec of FIELDS) out[spec.field] = seedText(spec, values?.[spec.field]);
|
|
3077
|
+
return out;
|
|
3078
|
+
}
|
|
3079
|
+
/** 草稿文本 → 数字;空文本表示恢复默认,越界或非数字视为无效(阻止保存) */
|
|
3080
|
+
function parseNumber(spec, text) {
|
|
3081
|
+
const trimmed = text.trim();
|
|
3082
|
+
if (trimmed === "") return {
|
|
3083
|
+
ok: true,
|
|
3084
|
+
value: null
|
|
3085
|
+
};
|
|
3086
|
+
const n = Number(trimmed);
|
|
3087
|
+
if (!Number.isFinite(n) || n < spec.min || n > spec.max) return {
|
|
3088
|
+
ok: false,
|
|
3089
|
+
value: null
|
|
3090
|
+
};
|
|
3091
|
+
return {
|
|
3092
|
+
ok: true,
|
|
3093
|
+
value: n
|
|
3094
|
+
};
|
|
3095
|
+
}
|
|
3096
|
+
/** 把有改动的字段合成一次写入的操作序列;无效字段不产生操作(保存已被阻止) */
|
|
3097
|
+
function planOps(draft, remote) {
|
|
3098
|
+
const ops = [];
|
|
3099
|
+
for (const spec of FIELDS) {
|
|
3100
|
+
const text = draft[spec.field] ?? "";
|
|
3101
|
+
if (text === (remote[spec.field] ?? "")) continue;
|
|
3102
|
+
if (spec.kind === "toggle") {
|
|
3103
|
+
ops.push({
|
|
3104
|
+
op: "set",
|
|
3105
|
+
path: [spec.field],
|
|
3106
|
+
value: text === "true"
|
|
3107
|
+
});
|
|
3108
|
+
continue;
|
|
3109
|
+
}
|
|
3110
|
+
const parsed = parseNumber(spec, text);
|
|
3111
|
+
if (!parsed.ok) continue;
|
|
3112
|
+
ops.push(parsed.value === null ? {
|
|
3113
|
+
op: "unset",
|
|
3114
|
+
path: [spec.field]
|
|
3115
|
+
} : {
|
|
3116
|
+
op: "set",
|
|
3117
|
+
path: [spec.field],
|
|
3118
|
+
value: parsed.value
|
|
3119
|
+
});
|
|
3120
|
+
}
|
|
3121
|
+
return ops;
|
|
3122
|
+
}
|
|
3001
3123
|
function ToggleRow(props) {
|
|
3002
|
-
return /* @__PURE__ */ h(
|
|
3124
|
+
return /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h("span", { className: "fs-settings-label" }, props.label), /* @__PURE__ */ h("button", {
|
|
3003
3125
|
type: "button",
|
|
3004
3126
|
className: "fs-settings-toggle",
|
|
3005
3127
|
"data-on": props.value ? "true" : "false",
|
|
3006
3128
|
disabled: props.disabled,
|
|
3007
3129
|
"aria-pressed": props.value,
|
|
3130
|
+
"aria-label": props.label,
|
|
3008
3131
|
onClick: () => {
|
|
3009
3132
|
if (!props.disabled) props.onToggle(!props.value);
|
|
3010
3133
|
}
|
|
3011
3134
|
}));
|
|
3012
3135
|
}
|
|
3013
3136
|
function NumberRow(props) {
|
|
3014
|
-
return /* @__PURE__ */ h(
|
|
3137
|
+
return /* @__PURE__ */ h(Fragment, null, /* @__PURE__ */ h("span", { className: "fs-settings-label" }, props.label), /* @__PURE__ */ h("input", {
|
|
3015
3138
|
type: "number",
|
|
3016
3139
|
className: "fs-settings-input",
|
|
3017
|
-
value: props.
|
|
3140
|
+
value: props.text,
|
|
3018
3141
|
min: props.min,
|
|
3019
3142
|
max: props.max,
|
|
3020
3143
|
disabled: props.disabled,
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3144
|
+
"aria-label": props.label,
|
|
3145
|
+
"aria-invalid": props.invalid,
|
|
3146
|
+
"data-invalid": props.invalid ? "true" : "false",
|
|
3147
|
+
onChange: (e) => props.onEdit(e.currentTarget.value)
|
|
3025
3148
|
}));
|
|
3026
3149
|
}
|
|
3027
|
-
|
|
3028
|
-
|
|
3150
|
+
/**
|
|
3151
|
+
* 订阅宿主表单的实时快照:写入被接受后宿主镜像会折叠出新快照,本控件据此
|
|
3152
|
+
* 重新播种草稿(插件管理页是否跟着重渲染与本页无关)。
|
|
3153
|
+
*/
|
|
3154
|
+
function useConfigSnapshot(form, fallback) {
|
|
3155
|
+
const [snap, setSnap] = React.useState(() => form ? form.getSnapshot() : fallback);
|
|
3156
|
+
React.useEffect(() => {
|
|
3157
|
+
if (!form) return;
|
|
3158
|
+
setSnap(form.getSnapshot());
|
|
3159
|
+
return form.subscribe(() => setSnap(form.getSnapshot()));
|
|
3160
|
+
}, [form]);
|
|
3161
|
+
return form ? snap : fallback;
|
|
3162
|
+
}
|
|
3163
|
+
function FlyoutConfigPage(props) {
|
|
3029
3164
|
useLang();
|
|
3030
|
-
const
|
|
3031
|
-
|
|
3032
|
-
const
|
|
3033
|
-
const
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3165
|
+
const form = props.configForm;
|
|
3166
|
+
const snap = useConfigSnapshot(form, props.form?.state);
|
|
3167
|
+
const values = snap?.value;
|
|
3168
|
+
const remote = React.useMemo(() => seed(values), [snap]);
|
|
3169
|
+
const [staged, setStaged] = React.useState(() => ({
|
|
3170
|
+
revision: snap?.revision,
|
|
3171
|
+
text: {}
|
|
3172
|
+
}));
|
|
3173
|
+
const [saving, setSaving] = React.useState(false);
|
|
3174
|
+
const [failed, setFailed] = React.useState(false);
|
|
3175
|
+
const edits = staged.revision === snap?.revision ? staged.text : {};
|
|
3176
|
+
if (props.view === "summary") return /* @__PURE__ */ h(Fragment, null, t("settingsDesc"));
|
|
3177
|
+
if (snap === void 0 || snap.status === "unavailable") return /* @__PURE__ */ h("p", { className: "fs-settings-note" }, t("settingsUnavailable"));
|
|
3178
|
+
const draft = {
|
|
3179
|
+
...remote,
|
|
3180
|
+
...edits
|
|
3181
|
+
};
|
|
3182
|
+
const textOf = (field) => draft[field] ?? "";
|
|
3183
|
+
const dirty = FIELDS.some((spec) => textOf(spec.field) !== (remote[spec.field] ?? ""));
|
|
3184
|
+
const invalid = FIELDS.some((spec) => spec.kind === "number" && !parseNumber(spec, textOf(spec.field)).ok);
|
|
3185
|
+
const disabled = snap.status !== "ready" || snap.writable === false || saving;
|
|
3186
|
+
const mutate = form?.mutate ?? props.form?.mutate;
|
|
3187
|
+
const stage = (field, text) => {
|
|
3188
|
+
setFailed(false);
|
|
3189
|
+
setStaged({
|
|
3190
|
+
revision: snap.revision,
|
|
3191
|
+
text: {
|
|
3192
|
+
...edits,
|
|
3193
|
+
[field]: text
|
|
3194
|
+
}
|
|
3195
|
+
});
|
|
3196
|
+
};
|
|
3197
|
+
const save = () => {
|
|
3198
|
+
if (!mutate || !dirty || invalid || saving) return;
|
|
3199
|
+
setSaving(true);
|
|
3200
|
+
setFailed(false);
|
|
3201
|
+
mutate(planOps(draft, remote), snap.revision).then((ok) => {
|
|
3202
|
+
setSaving(false);
|
|
3203
|
+
if (ok) return;
|
|
3204
|
+
setFailed(true);
|
|
3205
|
+
}).catch(() => {
|
|
3206
|
+
setSaving(false);
|
|
3207
|
+
setFailed(true);
|
|
3208
|
+
});
|
|
3209
|
+
};
|
|
3210
|
+
return /* @__PURE__ */ h("div", { className: "fs-settings-form" }, snap.writable === false ? /* @__PURE__ */ h("p", { className: "fs-settings-note" }, t("settingsReadonly")) : null, FIELDS.map((spec) => /* @__PURE__ */ h("div", {
|
|
3211
|
+
className: "fs-settings-row",
|
|
3212
|
+
key: spec.field
|
|
3213
|
+
}, spec.kind === "toggle" ? /* @__PURE__ */ h(ToggleRow, {
|
|
3214
|
+
label: t(spec.labelKey),
|
|
3215
|
+
value: textOf(spec.field) === "true",
|
|
3072
3216
|
disabled,
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
max: 20,
|
|
3217
|
+
onToggle: (next) => stage(spec.field, next ? "true" : "false")
|
|
3218
|
+
}) : /* @__PURE__ */ h(NumberRow, {
|
|
3219
|
+
label: t(spec.labelKey),
|
|
3220
|
+
text: textOf(spec.field),
|
|
3221
|
+
min: spec.min,
|
|
3222
|
+
max: spec.max,
|
|
3223
|
+
invalid: !parseNumber(spec, textOf(spec.field)).ok,
|
|
3081
3224
|
disabled,
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3225
|
+
onEdit: (text) => stage(spec.field, text)
|
|
3226
|
+
}))), /* @__PURE__ */ h("div", { className: "fs-settings-actions" }, /* @__PURE__ */ h("button", {
|
|
3227
|
+
type: "button",
|
|
3228
|
+
className: "fs-settings-save",
|
|
3229
|
+
disabled: disabled || !dirty || invalid,
|
|
3230
|
+
onClick: save
|
|
3231
|
+
}, saving ? t("settingsSaving") : t("settingsSave")), invalid ? /* @__PURE__ */ h("span", { className: "fs-settings-status" }, t("settingsInvalidNumber")) : null, !invalid && failed ? /* @__PURE__ */ h("span", { className: "fs-settings-status" }, t("settingsSaveFailed")) : null));
|
|
3086
3232
|
}
|
|
3087
3233
|
//#endregion
|
|
3088
3234
|
//#region src/client/index.tsx
|
|
3089
|
-
const SETTINGS_NAMESPACE = "dsh-flyout-sidebar";
|
|
3090
3235
|
window.__ModuleLoader__.load({
|
|
3091
3236
|
id: "dsh-flyout-sidebar",
|
|
3092
3237
|
factory: (require) => {
|
|
@@ -3110,54 +3255,18 @@ body[data-ds-dark-theme] .tok-property { color: #ced4da; }
|
|
|
3110
3255
|
order: 50,
|
|
3111
3256
|
label: "Artifacts Panel"
|
|
3112
3257
|
}, () => /* @__PURE__ */ h(ArtifactsPanel, null)));
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
if (next === localSnap) return;
|
|
3124
|
-
localSnap = next;
|
|
3125
|
-
for (const fn of localListeners) try {
|
|
3126
|
-
fn();
|
|
3127
|
-
} catch {}
|
|
3128
|
-
};
|
|
3129
|
-
sctx.effect(() => scope.subscribe(() => {
|
|
3130
|
-
publishLocal(scope.getSnapshot());
|
|
3131
|
-
settingsStore.load({ syncDefaultOpen: true });
|
|
3132
|
-
}), "flyout: settings scope");
|
|
3133
|
-
const useSettingsSnapshot = (selector) => React.useSyncExternalStore((cb) => {
|
|
3134
|
-
localListeners.add(cb);
|
|
3135
|
-
return () => {
|
|
3136
|
-
localListeners.delete(cb);
|
|
3137
|
-
};
|
|
3138
|
-
}, () => selector(localSnap));
|
|
3139
|
-
const setField = (field, value) => {
|
|
3140
|
-
const cur = localSnap;
|
|
3141
|
-
const nextValue = cur.value && typeof cur.value === "object" ? {
|
|
3142
|
-
...cur.value,
|
|
3143
|
-
[field]: value
|
|
3144
|
-
} : { [field]: value };
|
|
3145
|
-
publishLocal({
|
|
3146
|
-
...cur,
|
|
3147
|
-
value: nextValue
|
|
3148
|
-
});
|
|
3149
|
-
scope.set(field, value);
|
|
3150
|
-
};
|
|
3151
|
-
scopedSlots.inject("settings.plugin.item", () => scopedSlots.register({
|
|
3152
|
-
name: "settings.plugin.item",
|
|
3153
|
-
key: SETTINGS_NAMESPACE,
|
|
3154
|
-
locale: SETTINGS_NAMESPACE,
|
|
3155
|
-
inject: () => ({
|
|
3156
|
-
useSettingsSnapshot,
|
|
3157
|
-
setField
|
|
3158
|
-
})
|
|
3159
|
-
}, (props) => h(SettingsCard, props)));
|
|
3258
|
+
ctx.inject(["configForms"], (sub) => {
|
|
3259
|
+
const configForms = sub.get("configForms");
|
|
3260
|
+
if (!configForms) return;
|
|
3261
|
+
ctx.effect(() => configForms.whileServed([SETTINGS_NAMESPACE], () => slots.inject("plugins.bundle.config", () => slots.register({
|
|
3262
|
+
name: "plugins.bundle.config",
|
|
3263
|
+
key: BUNDLE_CONFIG_KEY,
|
|
3264
|
+
order: 40,
|
|
3265
|
+
label: () => t("settingsTitle"),
|
|
3266
|
+
inject: () => ({ configForm: configForms.get(SETTINGS_NAMESPACE) })
|
|
3267
|
+
}, FlyoutConfigPage))), "dsh-flyout-sidebar: config page");
|
|
3160
3268
|
});
|
|
3269
|
+
settingsStore.load({ syncDefaultOpen: true });
|
|
3161
3270
|
}
|
|
3162
3271
|
};
|
|
3163
3272
|
}
|
package/dist/index.js
CHANGED
|
@@ -1022,7 +1022,7 @@ var highlight_default = "/**\n * 共享:零依赖语法高亮器(无模板
|
|
|
1022
1022
|
var markdown_default = "/**\n * 共享:极简 Markdown → HTML 渲染器。\n *\n * 可移植性约束见 ext.ts 顶部说明:本文件经 `?raw` 原样内联进独立弹出页的\n * 经典 <script>,只能使用 JSDoc 标注类型。围栏代码块通过 shared/highlight\n * 的 highlightCode 高亮。\n */\nimport { highlightCode } from './highlight.js'\n\n/** @param {string} s @returns {string} */\nfunction mdEscape(s) {\n return String(s).replace(/[\\u0000\\u0001]/g, '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"')\n}\n\n/**\n * URL 白名单:只放行 http(s)、站内相对路径、页内锚点与 data:image/*,\n * 其余(javascript:、vbscript: 及实体编码变体等)一律替换为 '#',防止\n * 预览渲染出来的链接/图片在点击时执行脚本。\n * @param {string} u @returns {string}\n */\nfunction mdSafeUrl(u) {\n var s = String(u || '').replace(/[\\s\\u0000-\\u001f]/g, '')\n if (/^https?:\\/\\//i.test(s)) return s\n if (/^\\/(?!\\/)/.test(s) || /^\\.{1,2}\\//.test(s) || s.charAt(0) === '#') return s\n if (/^data:image\\/(?:png|gif|jpeg|webp|bmp|avif);/i.test(s)) return s\n return '#'\n}\n\n/** @param {string} s @returns {string} */\nfunction mdInline(s) {\n // code span 先摘出为占位符再跑后续规则:反引号内的 `[x](url)`、`**b**`\n // 应原样输出,不被行内链接/强调规则二次渲染(占位符字符已在 mdEscape 剥除)。\n /** @type {string[]} */\n const codes = []\n s = s.replace(/`([^`]+)`/g, (m, c) => {\n codes.push('<code>' + c + '</code>')\n return '\\u0000' + (codes.length - 1) + '\\u0000'\n })\n s = s.replace(/!\\[([^\\]]*)\\]\\(([^)\\s]+)\\)/g, (m, alt, u) => '<img alt=\"' + alt + '\" src=\"' + mdSafeUrl(u) + '\">')\n s = s.replace(/\\[([^\\]]+)\\]\\(([^)\\s]+)\\)/g, (m, text, u) => '<a href=\"' + mdSafeUrl(u) + '\" target=\"_blank\" rel=\"noopener noreferrer\">' + text + '</a>')\n s = s.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')\n s = s.replace(/\\*([^*\\n]+)\\*/g, '<em>$1</em>')\n s = s.replace(/\\u0000(\\d+)\\u0000/g, (m, i) => codes[Number(i)] ?? '')\n return s\n}\n\n/**\n * GFM 管道表格辅助:按未转义的 | 切分单元格(行内 \\| 还原为字面量竖线),\n * 并去掉首尾管道产生的空单元格。\n * @param {string} line @returns {string[]}\n */\nfunction mdSplitCells(line) {\n const parts = String(line).replace(/\\\\\\|/g, '\\u0002').split('|')\n for (let i = 0; i < parts.length; i += 1) {\n parts[i] = (parts[i] || '').replace(/\\u0002/g, '|').trim()\n }\n return parts\n}\n\n/** @param {string[]} cells @returns {string[]} */\nfunction mdTrimEdgeCells(cells) {\n if (cells.length && cells[0] === '') cells.shift()\n if (cells.length && cells[cells.length - 1] === '') cells.pop()\n return cells\n}\n\n/** @param {string[]} cells @returns {boolean} 是否为 GFM 分隔行(:--- / :---: / ---: 形态) */\nfunction mdIsDelimiterRow(cells) {\n return cells.length > 0 && cells.every((c) => /^:?-+:?$/.test(c))\n}\n\n/**\n * @param {string} cell @returns {string} 'center' | 'right' | ''(left 与默认一致,不输出)\n */\nfunction mdCellAlign(cell) {\n if (/^:-+:$/.test(cell)) return 'center'\n if (/^:-+$/.test(cell)) return 'left'\n if (/^-+:$/.test(cell)) return 'right'\n return ''\n}\n\n/** @param {string} a @returns {string} 单元格对齐的内联样式属性 */\nfunction mdAlignAttr(a) {\n return a === 'center' || a === 'right' ? ' style=\"text-align:' + a + '\"' : ''\n}\n\n/** @param {string} src @returns {string} HTML */\nexport function mdToHtml(src) {\n const lines = String(src || '').replace(/\\r\\n/g, '\\n').split('\\n')\n /** @type {string[]} */\n const out = []\n let i = 0\n while (i < lines.length) {\n const line = /** @type {string} */ (lines[i])\n if (/^\\s*```/.test(line)) {\n const fence = /^\\s*```([\\w+-]*)/.exec(line)\n const langHint = fence ? fence[1] || '' : ''\n /** @type {string[]} */\n const buf = []\n i += 1\n while (i < lines.length && !/^\\s*```/.test(/** @type {string} */ (lines[i]))) {\n buf.push(/** @type {string} */ (lines[i]))\n i += 1\n }\n i += 1\n out.push('<pre><code>' + highlightCode(buf.join('\\n'), langHint) + '</code></pre>')\n continue\n }\n const h = /^(#{1,6})\\s+(.*)$/.exec(line)\n if (h) {\n const lv = (h[1] || '').length\n out.push('<h' + lv + '>' + mdInline(mdEscape(/** @type {string} */ (h[2]))) + '</h' + lv + '>')\n i += 1\n continue\n }\n if (/^\\s*(---+|\\*\\*\\*+|___+)\\s*$/.test(line)) {\n out.push('<hr>')\n i += 1\n continue\n }\n if (/^\\s*>\\s?/.test(line)) {\n /** @type {string[]} */\n const q = []\n while (i < lines.length && /^\\s*>\\s?/.test(/** @type {string} */ (lines[i]))) {\n q.push((/** @type {string} */ (lines[i])).replace(/^\\s*>\\s?/, ''))\n i += 1\n }\n out.push('<blockquote>' + mdInline(mdEscape(q.join(' '))) + '</blockquote>')\n continue\n }\n if (/^\\s*[-*+]\\s+/.test(line)) {\n /** @type {string[]} */\n const lis = []\n while (i < lines.length && /^\\s*[-*+]\\s+/.test(/** @type {string} */ (lines[i]))) {\n lis.push(mdInline(mdEscape((/** @type {string} */ (lines[i])).replace(/^\\s*[-*+]\\s+/, ''))))\n i += 1\n }\n out.push('<ul>' + lis.map((x) => '<li>' + x + '</li>').join('') + '</ul>')\n continue\n }\n if (/^\\s*\\d+\\.\\s+/.test(line)) {\n /** @type {string[]} */\n const lis2 = []\n while (i < lines.length && /^\\s*\\d+\\.\\s+/.test(/** @type {string} */ (lines[i]))) {\n lis2.push(mdInline(mdEscape((/** @type {string} */ (lines[i])).replace(/^\\s*\\d+\\.\\s+/, ''))))\n i += 1\n }\n out.push('<ol>' + lis2.map((x) => '<li>' + x + '</li>').join('') + '</ol>')\n continue\n }\n if (line.trim() === '') {\n i += 1\n continue\n }\n // GFM 管道表格:表头行(含 |)+ 分隔行(:---/:---:/---:)+ 数据行,\n // 空行或不含 | 的行结束;列数以表头为准,数据行多删少补,\n // 单元格内容走 mdEscape + mdInline,对齐声明转成内联 text-align。\n if (line.indexOf('|') >= 0 && i + 1 < lines.length) {\n const head = mdTrimEdgeCells(mdSplitCells(line))\n const delim = mdTrimEdgeCells(mdSplitCells(/** @type {string} */ (lines[i + 1])))\n if (head.length && mdIsDelimiterRow(delim)) {\n const aligns = delim.map(mdCellAlign)\n const colCount = head.length\n /** @type {string[][]} */\n const rows = []\n i += 2\n while (i < lines.length && (/** @type {string} */ (lines[i])).trim() !== '' && (/** @type {string} */ (lines[i])).indexOf('|') >= 0) {\n const cells = mdTrimEdgeCells(mdSplitCells(/** @type {string} */ (lines[i])))\n /** @type {string[]} */\n const padded = []\n for (let c = 0; c < colCount; c += 1) padded.push(cells[c] || '')\n rows.push(padded)\n i += 1\n }\n let table = '<table><thead><tr>'\n for (let c = 0; c < colCount; c += 1) {\n table += '<th' + mdAlignAttr(aligns[c] || '') + '>' + mdInline(mdEscape(head[c] || '')) + '</th>'\n }\n table += '</tr></thead><tbody>'\n for (const cells of rows) {\n table += '<tr>'\n for (let c = 0; c < colCount; c += 1) {\n table += '<td' + mdAlignAttr(aligns[c] || '') + '>' + mdInline(mdEscape(cells[c] || '')) + '</td>'\n }\n table += '</tr>'\n }\n out.push(table + '</tbody></table>')\n continue\n }\n }\n out.push('<p>' + mdInline(mdEscape(line)) + '</p>')\n i += 1\n }\n return out.join('\\n')\n}\n";
|
|
1023
1023
|
//#endregion
|
|
1024
1024
|
//#region src/shared/i18n.js?raw
|
|
1025
|
-
var i18n_default = "/**\n * 共享:中英双语 UI 文案。\n *\n * 与 ext/highlight/markdown 一样遵守「可移植源码」约束:只能使用 JSDoc 标注\n * 类型(不得出现 TS 语法注记)、不得引入本目录之外的依赖。本模块被同时用于:\n * 1. tsdown 打包进 client 侧(浏览器 React bundle);\n * 2. tsdown 构建期经 `?raw` 读入原始文本,内联进独立弹出页 /flyout-sidebar\n * 的经典 <script>(见 src/host/page.ts)。\n *\n * 语言选择:跟随 DSH 宿主界面语言。应用内侧边栏观察 `<html lang>`(宿主\n * 已设置语言时),把 'zh'/'en' 通过 localStorage `dsh-flyout-sidebar:lang`\n * 发布;独立弹出页 / 各入口读取该值,未发布时按浏览器语言自动判定。\n * `t(key)` 取当前语言文案,缺失时回退另一语言、再回退 key 本身。\n */\n\n/** @type {Record<string, string>} */\nconst ZH = {\n // 通用状态\n loading: '加载中…',\n loadingTree: '加载文件树…',\n loadingChanges: '加载变更列表…',\n loadingPdf: '加载 PDF…',\n searching: '搜索中…',\n emptyDir: '(空目录)',\n noChanges: '没有未提交的变更',\n noChangesHead: '没有未提交的变更(相对于 HEAD)',\n noResults: '没有匹配的文件',\n readFailed: '读取失败',\n retry: '重试',\n gitStatusFailed: 'git status 失败',\n imageLoadFailed: '图片加载失败',\n pdfLoadFailed: 'pdf.js 加载失败',\n searchFailed: '搜索失败',\n treeLoadFailed: '加载失败',\n truncated: '(已截断的预览)',\n // 复制 / 引用\n copied: '已复制',\n copiedPath: '已复制路径',\n copiedRef: '已复制 @引用(未能写入输入框)',\n insertedInput: '已插入输入框',\n refBtn: '@引用',\n refTitle: '引用到输入框(失败则复制 @path)',\n refFlyoutTitle: '复制 @path 引用',\n copyPath: '复制路径',\n refInput: '@引用到输入框',\n // 面板头部 / 控件\n collapsePanel: '收起侧边栏',\n panelAria: '文件面板',\n flyoutTitle: '弹出式侧边栏',\n flyoutOpen: '弹出式侧边栏 — 在新标签页打开(可拖到另一块显示器)',\n resizeHandle: '拖动调整宽度',\n resizePanel: '拖动调整面板宽度',\n refreshTree: '刷新文件树',\n refreshChanges: '刷新变更列表',\n refresh: '刷新',\n viewGit: '查看 Git 变更(未提交)',\n backToFiles: '返回文件列表',\n hidePreview: '隐藏预览(标签页保留)',\n closeTab: '关闭标签页',\n closeOthers: '关闭其他',\n closeRight: '关闭右侧标签',\n previewRegion: '文件预览',\n diffTabPrefix: '[diff] ',\n searchPlaceholder: '搜索文件…',\n searchToggle: '搜索文件',\n hintClickGit: '点击右侧变更文件查看 diff',\n hintClickTree: '点击右侧文件查看内容',\n movePanelLeft: '将文件面板移到左侧',\n movePanelRight: '将文件面板移到右侧',\n // git 状态\n statusU: '未跟踪',\n statusA: '新增',\n statusM: '修改',\n statusD: '删除',\n statusR: '重命名',\n statusC: '复制',\n statusT: '类型变更',\n staged: '(已暂存)',\n unstaged: '(未暂存)',\n // diff / 预览控件\n diffDeleted: '- 删除',\n diffAdded: '+ 新增',\n zoomOut: '缩小',\n zoomIn: '放大',\n prevPage: '上一页',\n nextPage: '下一页',\n openInEditor: '在系统编辑器打开',\n openedInEditor: '已在编辑器打开',\n openFailed: '打开失败',\n //
|
|
1025
|
+
var i18n_default = "/**\n * 共享:中英双语 UI 文案。\n *\n * 与 ext/highlight/markdown 一样遵守「可移植源码」约束:只能使用 JSDoc 标注\n * 类型(不得出现 TS 语法注记)、不得引入本目录之外的依赖。本模块被同时用于:\n * 1. tsdown 打包进 client 侧(浏览器 React bundle);\n * 2. tsdown 构建期经 `?raw` 读入原始文本,内联进独立弹出页 /flyout-sidebar\n * 的经典 <script>(见 src/host/page.ts)。\n *\n * 语言选择:跟随 DSH 宿主界面语言。应用内侧边栏观察 `<html lang>`(宿主\n * 已设置语言时),把 'zh'/'en' 通过 localStorage `dsh-flyout-sidebar:lang`\n * 发布;独立弹出页 / 各入口读取该值,未发布时按浏览器语言自动判定。\n * `t(key)` 取当前语言文案,缺失时回退另一语言、再回退 key 本身。\n */\n\n/** @type {Record<string, string>} */\nconst ZH = {\n // 通用状态\n loading: '加载中…',\n loadingTree: '加载文件树…',\n loadingChanges: '加载变更列表…',\n loadingPdf: '加载 PDF…',\n searching: '搜索中…',\n emptyDir: '(空目录)',\n noChanges: '没有未提交的变更',\n noChangesHead: '没有未提交的变更(相对于 HEAD)',\n noResults: '没有匹配的文件',\n readFailed: '读取失败',\n retry: '重试',\n gitStatusFailed: 'git status 失败',\n imageLoadFailed: '图片加载失败',\n pdfLoadFailed: 'pdf.js 加载失败',\n searchFailed: '搜索失败',\n treeLoadFailed: '加载失败',\n truncated: '(已截断的预览)',\n // 复制 / 引用\n copied: '已复制',\n copiedPath: '已复制路径',\n copiedRef: '已复制 @引用(未能写入输入框)',\n insertedInput: '已插入输入框',\n refBtn: '@引用',\n refTitle: '引用到输入框(失败则复制 @path)',\n refFlyoutTitle: '复制 @path 引用',\n copyPath: '复制路径',\n refInput: '@引用到输入框',\n // 面板头部 / 控件\n collapsePanel: '收起侧边栏',\n panelAria: '文件面板',\n flyoutTitle: '弹出式侧边栏',\n flyoutOpen: '弹出式侧边栏 — 在新标签页打开(可拖到另一块显示器)',\n resizeHandle: '拖动调整宽度',\n resizePanel: '拖动调整面板宽度',\n refreshTree: '刷新文件树',\n refreshChanges: '刷新变更列表',\n refresh: '刷新',\n viewGit: '查看 Git 变更(未提交)',\n backToFiles: '返回文件列表',\n hidePreview: '隐藏预览(标签页保留)',\n closeTab: '关闭标签页',\n closeOthers: '关闭其他',\n closeRight: '关闭右侧标签',\n previewRegion: '文件预览',\n diffTabPrefix: '[diff] ',\n searchPlaceholder: '搜索文件…',\n searchToggle: '搜索文件',\n hintClickGit: '点击右侧变更文件查看 diff',\n hintClickTree: '点击右侧文件查看内容',\n movePanelLeft: '将文件面板移到左侧',\n movePanelRight: '将文件面板移到右侧',\n // git 状态\n statusU: '未跟踪',\n statusA: '新增',\n statusM: '修改',\n statusD: '删除',\n statusR: '重命名',\n statusC: '复制',\n statusT: '类型变更',\n staged: '(已暂存)',\n unstaged: '(未暂存)',\n // diff / 预览控件\n diffDeleted: '- 删除',\n diffAdded: '+ 新增',\n zoomOut: '缩小',\n zoomIn: '放大',\n prevPage: '上一页',\n nextPage: '下一页',\n openInEditor: '在系统编辑器打开',\n openedInEditor: '已在编辑器打开',\n openFailed: '打开失败',\n // 插件配置页(插件页 → 本插件的行 → 配置)\n settingsTitle: '弹出式侧边栏',\n settingsDesc: '侧边栏面板偏好设置',\n settingsAutoRefresh: '打开面板时自动刷新',\n settingsMinWidth: '面板最小宽度(%)',\n settingsDefaultOpen: '页面加载后默认展开',\n settingsFontSize: '内容区字号(px)',\n settingsSave: '保存',\n settingsSaving: '保存中…',\n settingsSaveFailed: '宿主未接受这些值,已保留供你修改。',\n settingsReadonly: '本部署的设置为只读。',\n settingsUnavailable: '该插件当前未加载,暂时无法配置。',\n settingsInvalidNumber: '请填范围内的数字;留空表示恢复默认。',\n}\n\n/** @type {Record<string, string>} */\nconst EN = {\n loading: 'Loading…',\n loadingTree: 'Loading file tree…',\n loadingChanges: 'Loading change list…',\n loadingPdf: 'Loading PDF…',\n searching: 'Searching…',\n emptyDir: '(empty directory)',\n noChanges: 'No uncommitted changes',\n noChangesHead: 'No uncommitted changes (relative to HEAD)',\n noResults: 'No matching files',\n readFailed: 'Failed to read',\n retry: 'Retry',\n gitStatusFailed: 'git status failed',\n imageLoadFailed: 'Failed to load image',\n pdfLoadFailed: 'Failed to load pdf.js',\n searchFailed: 'Search failed',\n treeLoadFailed: 'Failed to load',\n truncated: '(truncated preview)',\n copied: 'Copied',\n copiedPath: 'Path copied',\n copiedRef: 'Copied @reference (could not write to input box)',\n insertedInput: 'Inserted into input box',\n refBtn: '@ref',\n refTitle: 'Quote into input box (falls back to copying @path)',\n refFlyoutTitle: 'Copy @path reference',\n copyPath: 'Copy path',\n refInput: '@reference into input box',\n collapsePanel: 'Collapse sidebar',\n panelAria: 'File panel',\n flyoutTitle: 'Flyout sidebar',\n flyoutOpen: 'Flyout sidebar — open in a new tab (drag to another display)',\n resizeHandle: 'Drag to resize',\n resizePanel: 'Drag to resize panel',\n refreshTree: 'Refresh file tree',\n refreshChanges: 'Refresh change list',\n refresh: 'Refresh',\n viewGit: 'View Git changes (uncommitted)',\n backToFiles: 'Back to file list',\n hidePreview: 'Hide preview (tabs are kept)',\n closeTab: 'Close tab',\n closeOthers: 'Close others',\n closeRight: 'Close tabs to the right',\n previewRegion: 'File preview',\n diffTabPrefix: '[diff] ',\n searchPlaceholder: 'Search files…',\n searchToggle: 'Search files',\n hintClickGit: 'Click a changed file to view its diff',\n hintClickTree: 'Click a file to view its content',\n movePanelLeft: 'Move file panel to the left',\n movePanelRight: 'Move file panel to the right',\n statusU: 'Untracked',\n statusA: 'Added',\n statusM: 'Modified',\n statusD: 'Deleted',\n statusR: 'Renamed',\n statusC: 'Copied',\n statusT: 'Type change',\n staged: '(staged)',\n unstaged: '(unstaged)',\n diffDeleted: '- Deleted',\n diffAdded: '+ Added',\n zoomOut: 'Zoom out',\n zoomIn: 'Zoom in',\n prevPage: 'Previous page',\n nextPage: 'Next page',\n openInEditor: 'Open in system editor',\n openedInEditor: 'Opened in editor',\n openFailed: 'Failed to open',\n // Plugins page → this bundle's row → configure\n settingsTitle: 'Flyout Sidebar',\n settingsDesc: 'Sidebar panel preferences',\n settingsAutoRefresh: 'Auto-refresh on panel open',\n settingsMinWidth: 'Min panel width (%)',\n settingsDefaultOpen: 'Open by default on page load',\n settingsFontSize: 'Content font size (px)',\n settingsSave: 'Save',\n settingsSaving: 'Saving…',\n settingsSaveFailed: 'The deployment did not accept these values; they were left for you to correct.',\n settingsReadonly: 'This deployment stores settings read-only.',\n settingsUnavailable: 'This plugin is not loaded, so it cannot be configured right now.',\n settingsInvalidNumber: 'Enter a number within range, or leave blank to use the default.',\n}\n\n/** @type {Record<string, Record<string, string>>} */\nconst DICTS = { zh: ZH, en: EN }\n\nconst LANG_KEY = 'dsh-flyout-sidebar:lang'\n\n/** @type {'zh' | 'en' | null} 已发布的主界面语言(null = 宿主未发布,按浏览器判定) */\nlet publishedLang = null\n/** @type {Array<(lang: string) => void>} */\nlet listeners = []\n\n/** @returns {'zh' | 'en' | null} 主面板发布到 localStorage 的语言,无效时 null */\nfunction readPublishedLang() {\n try {\n var v = localStorage.getItem(LANG_KEY)\n return v === 'zh' || v === 'en' ? v : null\n } catch (e) {\n return null\n }\n}\n\n/** 浏览器语言自动判定:非 zh 开头一律英文 */\nfunction autoLang() {\n try {\n var langs = navigator.languages || (navigator.language ? [navigator.language] : [])\n for (var i = 0; i < langs.length; i++) {\n var l = String(langs[i] || '').toLowerCase()\n if (l.indexOf('zh') === 0) return 'zh'\n if (l.indexOf('en') === 0) return 'en'\n }\n } catch (e) {\n // navigator 不可用时按英文处理\n }\n return 'en'\n}\n\nfunction currentLang() {\n if (publishedLang === null) publishedLang = readPublishedLang()\n return publishedLang || autoLang()\n}\n\n/**\n * @param {string} key 文案键\n * @returns {string} 当前语言文案;两级回退(另一语言 → key)\n */\nexport function t(key) {\n var lang = currentLang()\n var dict = DICTS[lang] || EN\n if (dict[key] != null) return dict[key]\n var other = DICTS[lang === 'zh' ? 'en' : 'zh']\n if (!other) return key\n return other[key] != null ? other[key] : key\n}\n\n/** @returns {'zh' | 'en'} 当前生效语言(含自动判定的结果) */\nexport function getLang() {\n return currentLang()\n}\n\n/**\n * 发布语言:主面板观察宿主界面语言后调用,写入 localStorage 供独立弹出页\n * 读取,并通知本页订阅者重渲染。\n * @param {'zh' | 'en'} lang\n */\nexport function setLang(lang) {\n publishedLang = lang\n try {\n localStorage.setItem(LANG_KEY, lang)\n } catch (e) {\n // localStorage 不可用时仅保存在内存\n }\n var next = listeners.slice()\n for (var i = 0; i < next.length; i++) {\n var fn = next[i]\n if (!fn) continue\n try {\n fn(lang)\n } catch (e) {\n // 单个订阅者异常不阻断其余\n }\n }\n}\n\n/** @param {(lang: string) => void} fn @returns {() => void} 取消订阅 */\nexport function subscribeLang(fn) {\n listeners.push(fn)\n return function () {\n listeners = listeners.filter(function (f) {\n return f !== fn\n })\n }\n}\n";
|
|
1026
1026
|
//#endregion
|
|
1027
1027
|
//#region src/shared/gitui.js?raw
|
|
1028
1028
|
var gitui_default = "/**\n * 共享:git 变更列表 / diff 的纯展示逻辑。\n *\n * client 侧 React 组件与独立弹出页(page.ts 内联脚本)共用,避免双端各自\n * 实现后细节漂移。与 ext/highlight/markdown 一样遵守「可移植源码」约束:\n * 只能使用 JSDoc 标注类型、依赖仅限本目录(i18n 的 t 在内联场景由同作用域\n * 函数声明提升提供)。\n */\nimport { t } from './i18n.js'\n\n/** @param {string} p @returns {string} */\nexport function basename(p) {\n var s = String(p).replace(/[\\\\/]+$/, '')\n var i = Math.max(s.lastIndexOf('/'), s.lastIndexOf('\\\\'))\n return i >= 0 ? s.slice(i + 1) : s\n}\n\n/**\n * 变更文件的状态徽章字母:未跟踪为 U,否则取工作树状态(y),再退到暂存\n * 状态(x),空缺按 M。\n * @param {{ x: string, y: string }} e @returns {string}\n */\nexport function gitLabel(e) {\n if (e.x === '?' || e.y === '?') return 'U'\n return (e.y !== ' ' ? e.y : e.x) || 'M'\n}\n\n/**\n * 变更文件行 hover 提示:状态名 + 暂存/未暂存。\n * @param {{ x: string, y: string }} e @returns {string}\n */\nexport function gitTitle(e) {\n var label = gitLabel(e)\n /** @type {Record<string, string>} */\n var map = {\n U: t('statusU'), A: t('statusA'), M: t('statusM'),\n D: t('statusD'), R: t('statusR'), C: t('statusC'), T: t('statusT'),\n }\n var staged = e.x !== ' ' && e.x !== '?'\n return (map[label] || label) + (staged ? t('staged') : t('unstaged'))\n}\n\n/**\n * unified diff 单行的样式类:hunk / 增 / 删 / 元信息(diff --git、index、\n * rename 等),其余为普通行。\n * @param {string} line @returns {string}\n */\nexport function gitDiffLineClass(line) {\n var cls = 'gd-line'\n if (line.indexOf('@@') === 0) cls += ' gd-hunk'\n else if (line.charAt(0) === '+' && line.indexOf('+++') !== 0) cls += ' gd-add'\n else if (line.charAt(0) === '-' && line.indexOf('---') !== 0) cls += ' gd-del'\n else if (\n line.indexOf('diff ') === 0 || line.indexOf('index ') === 0 || line.indexOf('--- ') === 0 ||\n line.indexOf('+++ ') === 0 || line.indexOf('new file') === 0 || line.indexOf('deleted file') === 0 ||\n line.indexOf('old mode') === 0 || line.indexOf('new mode') === 0 || line.indexOf('rename ') === 0 ||\n line.indexOf('similarity ') === 0 || line.indexOf('copy ') === 0 || line.indexOf('Binary files') === 0 ||\n line.charAt(0) === '\\\\'\n ) {\n cls += ' gd-meta'\n }\n return cls\n}\n";
|
|
@@ -1098,7 +1098,7 @@ function sendText(res, status, body) {
|
|
|
1098
1098
|
res.writeHead(status, { "Content-Type": "text/plain; charset=utf-8" });
|
|
1099
1099
|
res.end(body);
|
|
1100
1100
|
}
|
|
1101
|
-
function registerRoutes(ctx, webServer,
|
|
1101
|
+
function registerRoutes(ctx, webServer, getConfig) {
|
|
1102
1102
|
const register = (route, label) => {
|
|
1103
1103
|
ctx.effect(() => webServer.register(route), label);
|
|
1104
1104
|
};
|
|
@@ -1252,7 +1252,7 @@ function registerRoutes(ctx, webServer, _config, getConfig) {
|
|
|
1252
1252
|
}, "artifacts: pdf.js worker route");
|
|
1253
1253
|
}
|
|
1254
1254
|
//#endregion
|
|
1255
|
-
//#region node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.
|
|
1255
|
+
//#region node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.5/node_modules/@deepseek-ai/cosmokit/lib/index.js
|
|
1256
1256
|
/** Return true when a value is `null` or `undefined`. */
|
|
1257
1257
|
function isNullable(value) {
|
|
1258
1258
|
return value === null || value === void 0;
|
|
@@ -1276,6 +1276,43 @@ function pick(source, keys, forced) {
|
|
|
1276
1276
|
for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
|
|
1277
1277
|
return result;
|
|
1278
1278
|
}
|
|
1279
|
+
/** Shared config references used by schema validators and plugin runtimes. */
|
|
1280
|
+
const write = Symbol.for("cosmokit.volatile.write");
|
|
1281
|
+
function snapshot(value, ancestors = /* @__PURE__ */ new Set()) {
|
|
1282
|
+
if (typeof value === "function") throw new TypeError("volatile config cannot contain functions");
|
|
1283
|
+
if (value === null || typeof value !== "object") return value;
|
|
1284
|
+
if (ancestors.has(value)) throw new TypeError("volatile config cannot contain cycles");
|
|
1285
|
+
ancestors.add(value);
|
|
1286
|
+
try {
|
|
1287
|
+
if (Array.isArray(value)) return Object.freeze(value.map((item) => snapshot(item, ancestors)));
|
|
1288
|
+
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) throw new TypeError("volatile config objects must be plain objects or arrays");
|
|
1289
|
+
return Object.freeze(Object.fromEntries(Object.entries(value).map(([key, item]) => [key, snapshot(item, ancestors)])));
|
|
1290
|
+
} finally {
|
|
1291
|
+
ancestors.delete(value);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
/**
|
|
1295
|
+
* Create a detached reference containing an immutable copy of the supplied data.
|
|
1296
|
+
* @param value - validated config data; class instances and functions are unsupported.
|
|
1297
|
+
* @returns a reference whose value is updated only by its owning runtime.
|
|
1298
|
+
*/
|
|
1299
|
+
function createVolatile(value) {
|
|
1300
|
+
let current = snapshot(value);
|
|
1301
|
+
return Object.freeze({
|
|
1302
|
+
get: () => current,
|
|
1303
|
+
[write]: (value) => {
|
|
1304
|
+
current = value;
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Identify references across ESM/CJS copies of the shared library.
|
|
1310
|
+
* @param value - a parsed config value.
|
|
1311
|
+
* @returns whether the value implements the shared reference protocol.
|
|
1312
|
+
*/
|
|
1313
|
+
function isVolatile(value) {
|
|
1314
|
+
return typeof value === "object" && value !== null && write in value;
|
|
1315
|
+
}
|
|
1279
1316
|
/** Test values using `instanceof` with a `toStringTag` fallback. */
|
|
1280
1317
|
function is(type, value) {
|
|
1281
1318
|
if (arguments.length === 1) return (value) => is(type, value);
|
|
@@ -1356,26 +1393,47 @@ function clone(source, refs = /* @__PURE__ */ new Map()) {
|
|
|
1356
1393
|
}
|
|
1357
1394
|
return result;
|
|
1358
1395
|
}
|
|
1359
|
-
/**
|
|
1396
|
+
/**
|
|
1397
|
+
* Compare values recursively, treating two volatile references as equal regardless of value.
|
|
1398
|
+
* Strict comparison distinguishes null/undefined, treats opaque objects by identity,
|
|
1399
|
+
* compares URLs by normalized href, treats array holes as undefined, and considers distinct cyclic structures unequal.
|
|
1400
|
+
* @param a - first value.
|
|
1401
|
+
* @param b - second value.
|
|
1402
|
+
* @param strict - whether to require strict data equality outside volatile references.
|
|
1403
|
+
* @returns whether the values compare equal.
|
|
1404
|
+
*/
|
|
1360
1405
|
function deepEqual(a, b, strict) {
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1406
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
1407
|
+
function compare(a, b) {
|
|
1408
|
+
if (a === b) return true;
|
|
1409
|
+
if (isVolatile(a) || isVolatile(b)) return isVolatile(a) && isVolatile(b);
|
|
1410
|
+
if (!strict && isNullable(a) && isNullable(b)) return true;
|
|
1411
|
+
if (typeof a !== typeof b || typeof a !== "object" || !a || !b) return false;
|
|
1412
|
+
if (ancestors.has(a)) return false;
|
|
1413
|
+
function check(test, then) {
|
|
1414
|
+
return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
|
|
1415
|
+
}
|
|
1416
|
+
ancestors.add(a);
|
|
1417
|
+
try {
|
|
1418
|
+
return check(Array.isArray, (a, b) => {
|
|
1419
|
+
if (a.length !== b.length) return false;
|
|
1420
|
+
for (let index = 0; index < a.length; index++) if (!compare(a[index], b[index])) return false;
|
|
1421
|
+
return true;
|
|
1422
|
+
}) ?? check(is("Date"), (a, b) => a.valueOf() === b.valueOf()) ?? check(is("URL"), (a, b) => a.href === b.href) ?? check(is("RegExp"), (a, b) => a.source === b.source && a.flags === b.flags) ?? check(isArrayBufferLike, (a, b) => {
|
|
1423
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
1424
|
+
const viewA = new Uint8Array(a);
|
|
1425
|
+
const viewB = new Uint8Array(b);
|
|
1426
|
+
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
1427
|
+
return true;
|
|
1428
|
+
}) ?? ((!strict || [a, b].every((value) => Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null)) && Object.keys({
|
|
1429
|
+
...a,
|
|
1430
|
+
...b
|
|
1431
|
+
}).every((key) => compare(a[key], b[key])));
|
|
1432
|
+
} finally {
|
|
1433
|
+
ancestors.delete(a);
|
|
1434
|
+
}
|
|
1368
1435
|
}
|
|
1369
|
-
return
|
|
1370
|
-
if (a.byteLength !== b.byteLength) return false;
|
|
1371
|
-
const viewA = new Uint8Array(a);
|
|
1372
|
-
const viewB = new Uint8Array(b);
|
|
1373
|
-
for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
|
|
1374
|
-
return true;
|
|
1375
|
-
}) ?? Object.keys({
|
|
1376
|
-
...a,
|
|
1377
|
-
...b
|
|
1378
|
-
}).every((key) => deepEqual(a[key], b[key], strict));
|
|
1436
|
+
return compare(a, b);
|
|
1379
1437
|
}
|
|
1380
1438
|
/** Time constants plus parsing and formatting helpers. */
|
|
1381
1439
|
var Time;
|
|
@@ -1448,7 +1506,7 @@ var Time;
|
|
|
1448
1506
|
Time.template = template;
|
|
1449
1507
|
})(Time || (Time = {}));
|
|
1450
1508
|
//#endregion
|
|
1451
|
-
//#region node_modules/.pnpm/@deepseek-ai+schemastery@3.18.
|
|
1509
|
+
//#region node_modules/.pnpm/@deepseek-ai+schemastery@3.18.4/node_modules/@deepseek-ai/schemastery/lib/index.mjs
|
|
1452
1510
|
const kSchema = Symbol.for("schemastery");
|
|
1453
1511
|
const kValidationError = Symbol.for("ValidationError");
|
|
1454
1512
|
globalThis.__schemastery_index__ ??= 0;
|
|
@@ -1624,6 +1682,7 @@ Schema.prototype.pattern = function pattern(regexp) {
|
|
|
1624
1682
|
return schema;
|
|
1625
1683
|
};
|
|
1626
1684
|
Schema.prototype.simplify = function simplify(value) {
|
|
1685
|
+
if (isVolatile(value)) value = value.get();
|
|
1627
1686
|
if (deepEqual(value, this.meta.default, this.type === "dict")) return null;
|
|
1628
1687
|
if (isNullable(value)) return value;
|
|
1629
1688
|
if (this.type === "object" || this.type === "dict") {
|
|
@@ -1680,12 +1739,49 @@ for (const key of [
|
|
|
1680
1739
|
};
|
|
1681
1740
|
return schema;
|
|
1682
1741
|
} });
|
|
1742
|
+
Schema.prototype.volatile = function volatile() {
|
|
1743
|
+
if (this.meta.volatile) throw new TypeError("volatile schema is already wrapped");
|
|
1744
|
+
return this.extra("volatile", true);
|
|
1745
|
+
};
|
|
1683
1746
|
const resolvers = {};
|
|
1747
|
+
const checkedVolatile = Symbol("checked-volatile-schema");
|
|
1748
|
+
function validateVolatileSchema(schema, path = [], blocked = false, seen = /* @__PURE__ */ new Map()) {
|
|
1749
|
+
const states = seen.get(schema) ?? /* @__PURE__ */ new Set();
|
|
1750
|
+
if (states.has(blocked)) return;
|
|
1751
|
+
states.add(blocked);
|
|
1752
|
+
seen.set(schema, states);
|
|
1753
|
+
if (schema.meta?.volatile && blocked) throw new ValidationError("volatile fields require a fixed object path without an enclosing volatile field", { path });
|
|
1754
|
+
const nested = blocked || !!schema.meta?.volatile;
|
|
1755
|
+
if (schema.dict) for (const [key, child] of Object.entries(schema.dict)) validateVolatileSchema(child, [...path, key], nested, seen);
|
|
1756
|
+
if (schema.sKey) validateVolatileSchema(schema.sKey, [...path, "<key>"], true, seen);
|
|
1757
|
+
if (schema.inner && (schema.type !== "lazy" || schema.inner[kSchema])) validateVolatileSchema(schema.inner, [...path, "*"], true, seen);
|
|
1758
|
+
if (schema.list) for (let index = 0; index < schema.list.length; index++) validateVolatileSchema(schema.list[index], [...path, String(index)], true, seen);
|
|
1759
|
+
}
|
|
1684
1760
|
Schema.extend = function extend(type, resolve) {
|
|
1685
1761
|
resolvers[type] = resolve;
|
|
1686
1762
|
};
|
|
1687
1763
|
Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
|
|
1688
1764
|
if (!schema) return [data];
|
|
1765
|
+
if (!options[checkedVolatile]) {
|
|
1766
|
+
validateVolatileSchema(schema, options.path);
|
|
1767
|
+
options = {
|
|
1768
|
+
...options,
|
|
1769
|
+
[checkedVolatile]: true
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
if (schema.meta?.volatile) {
|
|
1773
|
+
const inner = Schema(schema);
|
|
1774
|
+
inner.meta = {
|
|
1775
|
+
...schema.meta,
|
|
1776
|
+
volatile: false
|
|
1777
|
+
};
|
|
1778
|
+
const [value, adapted] = Schema.resolve(data, inner, options, strict);
|
|
1779
|
+
try {
|
|
1780
|
+
return [createVolatile(value), adapted];
|
|
1781
|
+
} catch (error) {
|
|
1782
|
+
throw new ValidationError(error instanceof Error ? error.message : String(error), options);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1689
1785
|
if (options.ignore?.(data, schema)) return [data];
|
|
1690
1786
|
if (isNullable(data) && schema.type !== "lazy") {
|
|
1691
1787
|
if (schema.meta.required) throw new ValidationError(`missing required value`, options);
|
|
@@ -1788,6 +1884,7 @@ Schema.extend("lazy", (data, schema, options, strict) => {
|
|
|
1788
1884
|
...schema.meta,
|
|
1789
1885
|
...schema.inner.meta
|
|
1790
1886
|
};
|
|
1887
|
+
validateVolatileSchema(schema.inner, options.path, true);
|
|
1791
1888
|
}
|
|
1792
1889
|
return Schema.resolve(data, schema.inner, options, strict);
|
|
1793
1890
|
});
|
|
@@ -1887,7 +1984,7 @@ function property(data, key, schema, options) {
|
|
|
1887
1984
|
} catch (e) {
|
|
1888
1985
|
if (!options?.autofix) throw e;
|
|
1889
1986
|
delete data[key];
|
|
1890
|
-
return schema.meta.default;
|
|
1987
|
+
return schema.meta.volatile ? createVolatile(schema.meta.default) : schema.meta.default;
|
|
1891
1988
|
}
|
|
1892
1989
|
}
|
|
1893
1990
|
Schema.extend("array", (data, { inner, meta }, options) => {
|
|
@@ -2051,14 +2148,31 @@ defineMethod("transform", [
|
|
|
2051
2148
|
* 兼容(静态 bundle 下 harness 全局不存在,typeof 守卫直接跳过)。
|
|
2052
2149
|
*/
|
|
2053
2150
|
const name = "dsh-flyout-sidebar";
|
|
2151
|
+
/**
|
|
2152
|
+
* 插件配置 schema。字段以 `.volatile()` 声明:DSH Settings → Plugins 的表单只投影
|
|
2153
|
+
* volatile 字段(按 profile 条目 id `flyout-sidebar` 定位本插件),编辑结果写进当前
|
|
2154
|
+
* profile 的 Cordis patch。apply 收到的配置里 volatile 字段是稳定引用,读当前值必须
|
|
2155
|
+
* 走 `.get()`;引用由 loader 在配置变更时就地更新,插件无需重新加载。
|
|
2156
|
+
*/
|
|
2054
2157
|
const Config = Schema.object({
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2158
|
+
/** 面板打开时轮询刷新产物与 git 变更 */
|
|
2159
|
+
autoRefresh: Schema.boolean().default(true).description("打开面板时自动刷新产物与 git 变更").volatile(),
|
|
2160
|
+
/** 面板最小宽度(占窗口宽度的百分比,15–60) */
|
|
2161
|
+
minPanelWidth: Schema.number().min(15).max(60).default(20).description("最短面板宽度(占窗口宽度百分比)").volatile(),
|
|
2162
|
+
/** 页面加载后默认展开面板 */
|
|
2163
|
+
defaultOpen: Schema.boolean().default(true).description("页面加载后默认展开面板").volatile(),
|
|
2164
|
+
/** 内容区(代码/diff/markdown)基准字号(px) */
|
|
2165
|
+
contentFontSize: Schema.number().min(11).max(20).default(13).description("内容区字体大小(px),界面文字不受影响").volatile()
|
|
2059
2166
|
});
|
|
2060
|
-
/**
|
|
2061
|
-
|
|
2167
|
+
/** 读取 volatile 引用的当前快照;每次请求现读,用户在 Settings 改值后立即生效 */
|
|
2168
|
+
function readConfig(cfg) {
|
|
2169
|
+
return {
|
|
2170
|
+
autoRefresh: cfg.autoRefresh.get(),
|
|
2171
|
+
minPanelWidth: cfg.minPanelWidth.get(),
|
|
2172
|
+
defaultOpen: cfg.defaultOpen.get(),
|
|
2173
|
+
contentFontSize: cfg.contentFontSize.get()
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
2062
2176
|
const inject = [
|
|
2063
2177
|
"webServer",
|
|
2064
2178
|
"sessionQuery",
|
|
@@ -2068,11 +2182,6 @@ function apply(ctx, config) {
|
|
|
2068
2182
|
const cfg = config ?? Config({});
|
|
2069
2183
|
attachArtifactTracking(ctx);
|
|
2070
2184
|
attachGitTracking(ctx);
|
|
2071
|
-
let settingsScope;
|
|
2072
|
-
ctx.inject(["settings"], (sctx) => {
|
|
2073
|
-
const settings = sctx.get("settings");
|
|
2074
|
-
if (settings) settingsScope = settings.register(SETTINGS_NAMESPACE, Config);
|
|
2075
|
-
});
|
|
2076
2185
|
if (typeof harness !== "undefined" && harness) {
|
|
2077
2186
|
harness.handle("artifacts.list", () => ({ artifacts: snapshotArtifacts() }));
|
|
2078
2187
|
harness.handle("artifacts.remove", (args) => removeFile(args?.path));
|
|
@@ -2084,9 +2193,7 @@ function apply(ctx, config) {
|
|
|
2084
2193
|
harness.handle("git.diff", (args) => gitDiff(ctx, args?.path, args?.sessionId));
|
|
2085
2194
|
}
|
|
2086
2195
|
const webServer = ctx.get("webServer");
|
|
2087
|
-
if (webServer) registerRoutes(ctx, webServer,
|
|
2088
|
-
return settingsScope?.get() ?? cfg;
|
|
2089
|
-
});
|
|
2196
|
+
if (webServer) registerRoutes(ctx, webServer, () => readConfig(cfg));
|
|
2090
2197
|
}
|
|
2091
2198
|
//#endregion
|
|
2092
|
-
export { Config,
|
|
2199
|
+
export { Config, apply, inject, name };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-flyout-sidebar",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "可弹出侧边栏 · A DeepSeek Harness sidebar that shows artifacts and pops out into a larger web tab.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"node": ">=20"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
42
|
+
"@deepseek-ai/schemastery": "^3.18.4",
|
|
43
43
|
"@types/node": "^26.4.0",
|
|
44
44
|
"@types/react": "^19.2.18",
|
|
45
45
|
"happy-dom": "^20.11.10",
|