dsh-vscode-mode 0.14.8 → 0.14.9
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/README.md +5 -4
- package/lib/client.js +246 -4
- package/lib/client.js.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/editorModelState.ts +16 -0
- package/src/client/index.ts +9 -1
- package/src/client/monaco/editActions.ts +58 -0
- package/src/client/pageReload.ts +73 -0
- package/src/client/shortcutsOfficial.ts +14 -3
- package/src/client/state/uiStatePersist.ts +22 -0
- package/src/client/ui/EditorView.ts +25 -1
- package/src/client/ui/commandCatalog.ts +16 -0
- package/src/shared/keybindings.ts +16 -0
package/README.md
CHANGED
|
@@ -459,18 +459,18 @@ DSH 每次启动监听随机端口,`http://127.0.0.1:<port>` 就是浏览器 o
|
|
|
459
459
|
|
|
460
460
|
官方 `dsh plugin` 方式,三选一。下面以 `web` profile 为例;Desktop 或其他命名
|
|
461
461
|
profile 请替换为实际目标,不要把示例目录当成当前运行 profile。升级前可阅读
|
|
462
|
-
[0.14.
|
|
462
|
+
[0.14.9 发布说明](docs/release-0.14.9.md);从 0.14.2 或更旧版本升级还须阅读
|
|
463
463
|
[0.14.3 数据迁移与回退说明](docs/release-0.14.3.md)。
|
|
464
464
|
|
|
465
465
|
```bash
|
|
466
466
|
# ① Git 安装(clone + prepare 构建;推荐打固定 tag)
|
|
467
|
-
dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.14.
|
|
467
|
+
dsh plugin --profile web add github:Lenonss/DSH_VsCodeMode#v0.14.9
|
|
468
468
|
|
|
469
469
|
# ② npm 注册表(发布到 npm 后)
|
|
470
|
-
dsh plugin --profile web add dsh-vscode-mode@0.14.
|
|
470
|
+
dsh plugin --profile web add dsh-vscode-mode@0.14.9
|
|
471
471
|
|
|
472
472
|
# ③ GitHub Release tgz 直装(Release asset 可用后)
|
|
473
|
-
dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.14.
|
|
473
|
+
dsh plugin --profile web add https://github.com/Lenonss/DSH_VsCodeMode/releases/download/v0.14.9/dsh-vscode-mode-0.14.9.tgz
|
|
474
474
|
```
|
|
475
475
|
|
|
476
476
|
> **DSH 0.2 兼容范围**:`0.14.1` 起精确放行 `0.2.0-rc.1`,`0.14.4` 再加入
|
|
@@ -806,6 +806,7 @@ Keep All / Undo All 却是亮的。修复:单文件 Keep / Undo 覆盖冲突
|
|
|
806
806
|
完整变更见 [GitHub Releases](https://github.com/Lenonss/DSH_VsCodeMode/releases)。
|
|
807
807
|
近期关键版本:
|
|
808
808
|
|
|
809
|
+
- **v0.14.9**:**编辑快捷键与桌面安全刷新**——`Ctrl+/` 保留官方快捷键入口,行注释改为 `Ctrl+Alt+/`(Web Linux 默认不绑定);编辑器本地 `Ctrl+D` 调用 Monaco 原生向下复制行,块注释不变。桌面 `Ctrl+Shift+R` 刷新当前界面、不重启 Host;未保存代码/PDF、待保存、保存中或外部冲突时拒绝刷新,界面状态镜像失败/超时也不刷新。真实 GUI 按键和状态恢复尚未验收,详见 [0.14.9 发布说明](docs/release-0.14.9.md)。
|
|
809
810
|
- **v0.14.8**:**Unity 打开目标选择与桌面端打开修复**——Unity 外部脚本编辑器 `com.dsh.editor` 0.3.1 新增「打开目标」选择(自动/DSH Web/DSH 桌面端),桌面端按 profile bridge 定位、缺失明确报错;桌面 profile 开机自动准备独立 bridge,无需先注册 OS 菜单。修复桌面端 sessions 服务面缺 `.open`/`.create` 导致的「编辑器打开失败或取消」(改走 `materializeScope → manager.select` 多面切换与 `newDraftSession` 回落)。验证范围与真实 GUI 验收缺口见 [0.14.8 发布说明](docs/release-0.14.8.md)。
|
|
810
811
|
- **v0.14.7**:**SVN 资源管理器状态图标与编辑过程自抵消自动归档**——文件树叠加 SVN 状态图标(含目录子树聚合、远程状态、冲突/缺失等标记与矢量叠加),有界快照单飞投影、流式状态解析与写操作后定向刷新;新增后再还原的编辑过程自抵消记录自动归档跳过审查,先保存再独立归档、事务失败回滚内存标志。修复 DAP 帧源码路径按语言过滤候选、不回落错误语言文件;刷新事件携带 scope/paths 细化。验证范围与真实 GUI 验收缺口见 [0.14.7 发布说明](docs/release-0.14.7.md)。
|
|
811
812
|
- **v0.14.6**:**对话切换位置保持与共享查找**——同工作区切换对话时,在编辑器释放前保存实际模型的滚动、光标和选区等视图状态;只在模型重新绑定时恢复,显式行号导航仍优先。跨文件共享 Ctrl+F 查询、大小写/全词/正则与开关状态,不共享选区搜索范围或匹配序号,不抢焦点;工作区隔离并释放监听。验证范围与真实 GUI 验收缺口见 [0.14.6 发布说明](docs/release-0.14.6.md)。
|
package/lib/client.js
CHANGED
|
@@ -2736,6 +2736,144 @@ window.__ModuleLoader__.load({
|
|
|
2736
2736
|
return monacoPromise.finally(unsubscribe);
|
|
2737
2737
|
}
|
|
2738
2738
|
//#endregion
|
|
2739
|
+
//#region src/client/monaco/editActions.ts
|
|
2740
|
+
/** @public @author ddj 2026年10月12号 @param editor Target editor. @param monaco Monaco API. @param action Native action. @returns Whether a writable model accepted the action. @sideEffects Restores focus and runs an undoable native edit. */
|
|
2741
|
+
function runEditAction(editor, monaco, action) {
|
|
2742
|
+
if (!editor?.getModel() || !monaco || editor.getOption(monaco.editor.EditorOption.readOnly)) return false;
|
|
2743
|
+
editor.focus();
|
|
2744
|
+
editor.trigger("edrv-edit", action, null);
|
|
2745
|
+
return true;
|
|
2746
|
+
}
|
|
2747
|
+
/** @public @author ddj 2026年10月12号 @param editor Plugin editor. @param monaco Monaco API. @returns Idempotent disposer. @sideEffects Overrides only plugin-local bindings and menu entries until editor disposal. */
|
|
2748
|
+
function bindEditActions(editor, monaco) {
|
|
2749
|
+
const local = editor.createContextKey("edrvLocalEdit", true);
|
|
2750
|
+
const focus = "edrvLocalEdit && editorTextFocus";
|
|
2751
|
+
const writable = focus + " && !editorReadonly";
|
|
2752
|
+
const rules = monaco.editor.addKeybindingRules([{
|
|
2753
|
+
keybinding: monaco.KeyMod.CtrlCmd | monaco.KeyCode.Slash,
|
|
2754
|
+
command: "",
|
|
2755
|
+
when: focus
|
|
2756
|
+
}, {
|
|
2757
|
+
keybinding: monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyD,
|
|
2758
|
+
command: "editor.action.copyLinesDownAction",
|
|
2759
|
+
when: writable
|
|
2760
|
+
}]);
|
|
2761
|
+
/** @private @author ddj 2026年10月12号 Runs the native language-aware comment action. */
|
|
2762
|
+
const comment = () => {
|
|
2763
|
+
runEditAction(editor, monaco, "editor.action.commentLine");
|
|
2764
|
+
};
|
|
2765
|
+
/** @private @author ddj 2026年10月12号 Runs the native multi-selection line-copy action. */
|
|
2766
|
+
const duplicate = () => {
|
|
2767
|
+
runEditAction(editor, monaco, "editor.action.copyLinesDownAction");
|
|
2768
|
+
};
|
|
2769
|
+
const actions = [editor.addAction({
|
|
2770
|
+
id: "edrv.toggleComment",
|
|
2771
|
+
label: "切换行注释(Ctrl+Alt+/)",
|
|
2772
|
+
precondition: writable,
|
|
2773
|
+
contextMenuGroupId: "1_edrv",
|
|
2774
|
+
run: comment
|
|
2775
|
+
}), editor.addAction({
|
|
2776
|
+
id: "edrv.duplicateLine",
|
|
2777
|
+
label: "向下复制行(Ctrl+D,编辑器本地)",
|
|
2778
|
+
precondition: writable,
|
|
2779
|
+
contextMenuGroupId: "1_edrv",
|
|
2780
|
+
run: duplicate
|
|
2781
|
+
})];
|
|
2782
|
+
let disposed = false;
|
|
2783
|
+
/** @private @author ddj 2026年10月12号 Releases global rule contributions owned by this instance. */
|
|
2784
|
+
const dispose = () => {
|
|
2785
|
+
if (disposed) return;
|
|
2786
|
+
disposed = true;
|
|
2787
|
+
rules.dispose();
|
|
2788
|
+
for (const action of actions) action.dispose();
|
|
2789
|
+
local.reset();
|
|
2790
|
+
lifetime.dispose();
|
|
2791
|
+
};
|
|
2792
|
+
const lifetime = editor.onDidDispose(dispose);
|
|
2793
|
+
return dispose;
|
|
2794
|
+
}
|
|
2795
|
+
//#endregion
|
|
2796
|
+
//#region src/client/pageReload.ts
|
|
2797
|
+
const guards = /* @__PURE__ */ new Set();
|
|
2798
|
+
/** @public @author ddj 2026年10月12号 @param guard Live editor checks and view-state capture. @returns Unregister function. */
|
|
2799
|
+
function addReloadGuard(guard) {
|
|
2800
|
+
guards.add(guard);
|
|
2801
|
+
return () => {
|
|
2802
|
+
guards.delete(guard);
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
/** @private @author ddj 2026年10月12号 @param checks Current editor guards. @returns First unsafe state, or null. */
|
|
2806
|
+
function reloadBlock(checks) {
|
|
2807
|
+
for (const guard of checks) {
|
|
2808
|
+
const reason = guard.check();
|
|
2809
|
+
if (reason) return reason;
|
|
2810
|
+
}
|
|
2811
|
+
return null;
|
|
2812
|
+
}
|
|
2813
|
+
/** @public @author ddj 2026年10月12号 @param deps Page operations. @param checks Live editor guards. @returns Serialized request and cancellation. @sideEffects Flushes view state then reloads only when still safe. */
|
|
2814
|
+
function createPageReload(deps, checks = guards) {
|
|
2815
|
+
let busy = false;
|
|
2816
|
+
let disposed = false;
|
|
2817
|
+
/** @private @author ddj 2026年10月12号 @returns Completion of one guarded reload attempt. */
|
|
2818
|
+
async function request() {
|
|
2819
|
+
if (busy || disposed) return;
|
|
2820
|
+
busy = true;
|
|
2821
|
+
let timer;
|
|
2822
|
+
try {
|
|
2823
|
+
const reason = reloadBlock(checks);
|
|
2824
|
+
if (reason) {
|
|
2825
|
+
deps.notify("未刷新:" + reason);
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
for (const guard of checks) guard.prepare();
|
|
2829
|
+
const timeout = new Promise((_, reject) => {
|
|
2830
|
+
timer = setTimeout(() => reject(/* @__PURE__ */ new Error("界面状态保存超时")), deps.timeoutMs ?? 5e3);
|
|
2831
|
+
});
|
|
2832
|
+
await Promise.race([deps.flush(), timeout]);
|
|
2833
|
+
if (disposed) return;
|
|
2834
|
+
const changed = reloadBlock(checks);
|
|
2835
|
+
if (changed) {
|
|
2836
|
+
deps.notify("未刷新:" + changed);
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
deps.reload();
|
|
2840
|
+
} catch (error) {
|
|
2841
|
+
if (!disposed) deps.notify("未刷新:" + String(error));
|
|
2842
|
+
} finally {
|
|
2843
|
+
clearTimeout(timer);
|
|
2844
|
+
busy = false;
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
return {
|
|
2848
|
+
request,
|
|
2849
|
+
dispose: () => {
|
|
2850
|
+
disposed = true;
|
|
2851
|
+
}
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
/** @public @author ddj 2026年10月12号 @returns Whether no live modal blocks page commands. */
|
|
2855
|
+
function pageKeysAllowed() {
|
|
2856
|
+
if (typeof document === "undefined") return true;
|
|
2857
|
+
return document.querySelector("[role=\"dialog\"], [aria-modal=\"true\"]") === null;
|
|
2858
|
+
}
|
|
2859
|
+
/** @public @author ddj 2026年10月12号 @param flush Durable UI-state flush. @param notify User-visible failure sink. @returns Root-scope listener disposer. */
|
|
2860
|
+
function installPageReload(flush, notify) {
|
|
2861
|
+
const controller = createPageReload({
|
|
2862
|
+
flush,
|
|
2863
|
+
notify,
|
|
2864
|
+
reload: () => window.location.reload()
|
|
2865
|
+
});
|
|
2866
|
+
/** @private @author ddj 2026年10月12号 Ignores modal-blocked commands even from programmatic callers. */
|
|
2867
|
+
const onReload = () => {
|
|
2868
|
+
if (pageKeysAllowed()) controller.request();
|
|
2869
|
+
};
|
|
2870
|
+
window.addEventListener("edrv.command.reloadWindow", onReload);
|
|
2871
|
+
return () => {
|
|
2872
|
+
controller.dispose();
|
|
2873
|
+
window.removeEventListener("edrv.command.reloadWindow", onReload);
|
|
2874
|
+
};
|
|
2875
|
+
}
|
|
2876
|
+
//#endregion
|
|
2739
2877
|
//#region src/client/imagePreview.ts
|
|
2740
2878
|
/**
|
|
2741
2879
|
* dsh-vscode-mode client — 图片文件判定、data URL 组装与缩放纯逻辑(编辑区图片预览面板用)。
|
|
@@ -5356,6 +5494,16 @@ window.__ModuleLoader__.load({
|
|
|
5356
5494
|
//#region src/client/editorModelState.ts
|
|
5357
5495
|
/** Monaco 编辑器实例选择器(限定在插件编辑器行内,排除官方预览等其它 Monaco)。 */
|
|
5358
5496
|
const EDITOR_MODEL_SELECTOR = ".edrv-editor-row .monaco-editor";
|
|
5497
|
+
/** @public @author ddj 2026年10月12号 @param target Optional shortcut target. @returns Whether focus belongs to plugin code input, excluding widgets and read-only editors. */
|
|
5498
|
+
function hasCodeFocus(target) {
|
|
5499
|
+
if (typeof document === "undefined") return false;
|
|
5500
|
+
const node = target ?? document.activeElement;
|
|
5501
|
+
if (!(node instanceof Element)) return false;
|
|
5502
|
+
const editor = node.closest(EDITOR_MODEL_SELECTOR);
|
|
5503
|
+
if (!editor || editor.classList.contains("readonly")) return false;
|
|
5504
|
+
if (node.closest(".find-widget, .suggest-widget, .rename-box, .monaco-inputbox")) return false;
|
|
5505
|
+
return (typeof window === "undefined" ? null : window)?.monaco?.editor?.getEditors?.().some((item) => item.getDomNode() === editor && item.hasTextFocus() && Boolean(item.getModel())) ?? false;
|
|
5506
|
+
}
|
|
5359
5507
|
/**
|
|
5360
5508
|
* 已打开文件页签选择器(页签栏在编辑器行内;空态时无此节点)。
|
|
5361
5509
|
* 判据同样不依赖 Monaco:图片/PDF 页签没有 Monaco 实例,但仍是「有页签可关」。
|
|
@@ -7632,6 +7780,30 @@ window.__ModuleLoader__.load({
|
|
|
7632
7780
|
* @author ddj 2026年09月10号 / 2026年10月
|
|
7633
7781
|
*/
|
|
7634
7782
|
const EDITOR_COMMANDS = [
|
|
7783
|
+
{
|
|
7784
|
+
id: "edrv.toggleComment",
|
|
7785
|
+
label: "切换行注释",
|
|
7786
|
+
category: "编辑",
|
|
7787
|
+
order: 20,
|
|
7788
|
+
available: needsModel,
|
|
7789
|
+
run: () => emit$1("toggleComment")
|
|
7790
|
+
},
|
|
7791
|
+
{
|
|
7792
|
+
id: "edrv.duplicateLine",
|
|
7793
|
+
label: "向下复制行(Ctrl+D,编辑器本地)",
|
|
7794
|
+
category: "编辑",
|
|
7795
|
+
order: 30,
|
|
7796
|
+
available: needsModel,
|
|
7797
|
+
run: () => emit$1("duplicateLine")
|
|
7798
|
+
},
|
|
7799
|
+
{
|
|
7800
|
+
id: "edrv.reloadWindow",
|
|
7801
|
+
label: "刷新当前界面(不重启 Host)",
|
|
7802
|
+
category: "视图",
|
|
7803
|
+
order: 40,
|
|
7804
|
+
available: pageKeysAllowed,
|
|
7805
|
+
run: () => emit$1("reloadWindow")
|
|
7806
|
+
},
|
|
7635
7807
|
{
|
|
7636
7808
|
id: "edrv.save",
|
|
7637
7809
|
label: "保存文件",
|
|
@@ -10315,6 +10487,17 @@ window.__ModuleLoader__.load({
|
|
|
10315
10487
|
inflight = run.then(() => {}, () => {});
|
|
10316
10488
|
return run;
|
|
10317
10489
|
}
|
|
10490
|
+
/** @private @author ddj 2026年10月12号 @returns Durable refresh snapshot. @throws When state is unreadable, unsettled or rejected by Host. @sideEffects Resends current local state after waiting for inflight writes. */
|
|
10491
|
+
async function flushStrict() {
|
|
10492
|
+
if (!settleDone || !installed) throw new Error("界面状态镜像尚未就绪");
|
|
10493
|
+
if (inflight) await inflight;
|
|
10494
|
+
for (let index = 0; index < storage.length; index++) {
|
|
10495
|
+
const key = storage.key(index);
|
|
10496
|
+
if (key && isMirroredKey(key)) queue(key, storage.getItem(key));
|
|
10497
|
+
}
|
|
10498
|
+
const lost = await flush();
|
|
10499
|
+
if (lost > 0) throw new Error("界面状态保存失败(" + lost + " 个键):" + lastError);
|
|
10500
|
+
}
|
|
10318
10501
|
/** 回填本地缺失键(本地已有值一律保留)。 */
|
|
10319
10502
|
function applyHydration(snapshot) {
|
|
10320
10503
|
let filled = 0;
|
|
@@ -10436,6 +10619,7 @@ window.__ModuleLoader__.load({
|
|
|
10436
10619
|
},
|
|
10437
10620
|
hydrate,
|
|
10438
10621
|
flush,
|
|
10622
|
+
flushStrict,
|
|
10439
10623
|
hydrated() {
|
|
10440
10624
|
return done;
|
|
10441
10625
|
},
|
|
@@ -10596,6 +10780,11 @@ window.__ModuleLoader__.load({
|
|
|
10596
10780
|
async function flushUiState() {
|
|
10597
10781
|
if (singleton) await singleton.flush();
|
|
10598
10782
|
}
|
|
10783
|
+
/** @public @author ddj 2026年10月12号 @returns Durable page state. @throws If the mirror is unavailable or cannot persist. */
|
|
10784
|
+
async function flushPageState() {
|
|
10785
|
+
if (!singleton) throw new Error("界面状态镜像不可用");
|
|
10786
|
+
await singleton.flushStrict();
|
|
10787
|
+
}
|
|
10599
10788
|
//#endregion
|
|
10600
10789
|
//#region src/client/state/scopeStore.ts
|
|
10601
10790
|
/**
|
|
@@ -20963,6 +21152,24 @@ window.__ModuleLoader__.load({
|
|
|
20963
21152
|
/** @private @author ddj 2026年10月09号 @param path 可选预期文件路径。按已绑定模型归属保存,避免新 active 覆盖旧文件状态。 */
|
|
20964
21153
|
const saveViewState = (path) => viewKeeperRef.current?.save(path);
|
|
20965
21154
|
saveViewStateRef.current = saveViewState;
|
|
21155
|
+
react.default.useEffect(() => {
|
|
21156
|
+
/** @private @author ddj 2026年10月12号 @returns Unsafe editor state without saving or discarding file content. */
|
|
21157
|
+
function checkReload() {
|
|
21158
|
+
if (Object.values(dirtyRef.current).some(Boolean)) return "存在未保存文件";
|
|
21159
|
+
if (saveTimerRef.current.isPending() || savingRef.current.size) return "文件正在等待保存或保存中";
|
|
21160
|
+
for (const ctl of pdfCtlRef.current.values()) if (ctl.isDirty?.()) return "存在未保存 PDF";
|
|
21161
|
+
for (const path of tabPathsRef.current) if (readSync(scope, path)) return "存在外部文件冲突";
|
|
21162
|
+
return null;
|
|
21163
|
+
}
|
|
21164
|
+
/** @private @author ddj 2026年10月12号 Captures view state only; never flushes file auto-save. */
|
|
21165
|
+
function prepareReload() {
|
|
21166
|
+
saveViewStateRef.current?.();
|
|
21167
|
+
}
|
|
21168
|
+
return addReloadGuard({
|
|
21169
|
+
check: checkReload,
|
|
21170
|
+
prepare: prepareReload
|
|
21171
|
+
});
|
|
21172
|
+
}, [scope]);
|
|
20966
21173
|
/** @private @author ddj 2026年10月09号 ref 脱离与 effect 卸载共用:保存视图/查找后销毁,重复调用无副作用。 */
|
|
20967
21174
|
const disposeEditor = react.default.useCallback(() => {
|
|
20968
21175
|
viewKeeperRef.current?.dispose();
|
|
@@ -22150,6 +22357,9 @@ window.__ModuleLoader__.load({
|
|
|
22150
22357
|
["edrv.command.svnTortoiseDiff", () => runSvnTortoise("diff", activeRef.current)],
|
|
22151
22358
|
["edrv.command.svnTortoiseBlame", () => runSvnTortoise("blame", activeRef.current)],
|
|
22152
22359
|
["edrv.command.svnTortoiseRevert", () => runSvnTortoise("revert", activeRef.current)],
|
|
22360
|
+
["edrv:reload-status", (event) => setStatus(event.detail)],
|
|
22361
|
+
["edrv.command.toggleComment", () => runEditAction(editorRef.current, monacoRef.current, "editor.action.commentLine")],
|
|
22362
|
+
["edrv.command.duplicateLine", () => runEditAction(editorRef.current, monacoRef.current, "editor.action.copyLinesDownAction")],
|
|
22153
22363
|
["edrv.command.goToLine", () => {
|
|
22154
22364
|
const ed = editorRef.current;
|
|
22155
22365
|
if (ed?.getModel?.()) ed.trigger("edrv-goto", "editor.action.gotoLine", null);
|
|
@@ -22164,7 +22374,7 @@ window.__ModuleLoader__.load({
|
|
|
22164
22374
|
const action = byName.get(event.type);
|
|
22165
22375
|
if (!action) return;
|
|
22166
22376
|
try {
|
|
22167
|
-
action();
|
|
22377
|
+
action(event);
|
|
22168
22378
|
} catch (error) {
|
|
22169
22379
|
dbg(sessionId, "指令 " + event.type + " 失败:" + String(error));
|
|
22170
22380
|
}
|
|
@@ -22641,6 +22851,7 @@ window.__ModuleLoader__.load({
|
|
|
22641
22851
|
padding: { top: side ? 6 : 8 },
|
|
22642
22852
|
inlineSuggest: { enabled: true }
|
|
22643
22853
|
});
|
|
22854
|
+
bindEditActions(ed, m);
|
|
22644
22855
|
ed.onDidChangeModelContent(() => {
|
|
22645
22856
|
if (!ed.getModel() || programmaticRef.current) return;
|
|
22646
22857
|
syncTypeSuggest(ed, m);
|
|
@@ -25641,6 +25852,9 @@ window.__ModuleLoader__.load({
|
|
|
25641
25852
|
/** 命令 id → 默认键位(可为多候选)。命令目录以此为准,新增命令只需加一项。 */
|
|
25642
25853
|
const KEYBINDING_DEFAULTS = {
|
|
25643
25854
|
"edrv.save": "Ctrl+S",
|
|
25855
|
+
"edrv.toggleComment": "Ctrl+Alt+/",
|
|
25856
|
+
"edrv.duplicateLine": "",
|
|
25857
|
+
"edrv.reloadWindow": "",
|
|
25644
25858
|
"edrv.quickOpen": "Ctrl+P",
|
|
25645
25859
|
"edrv.toggleSidebar": "Ctrl+B",
|
|
25646
25860
|
"edrv.searchInFiles": "Ctrl+Shift+F",
|
|
@@ -25834,6 +26048,19 @@ window.__ModuleLoader__.load({
|
|
|
25834
26048
|
*/
|
|
25835
26049
|
const SHORTCUT_PROFILES = {
|
|
25836
26050
|
"edrv.toggleSidebar": {},
|
|
26051
|
+
"edrv.duplicateLine": {},
|
|
26052
|
+
"edrv.toggleComment": {
|
|
26053
|
+
"desktop:macos": bind("Slash", ["primary", "alt"]),
|
|
26054
|
+
"desktop:windows": bind("Slash", ["primary", "alt"]),
|
|
26055
|
+
"desktop:linux": bind("Slash", ["primary", "alt"]),
|
|
26056
|
+
"web:macos": bind("Slash", ["primary", "alt"]),
|
|
26057
|
+
"web:windows": bind("Slash", ["primary", "alt"])
|
|
26058
|
+
},
|
|
26059
|
+
"edrv.reloadWindow": {
|
|
26060
|
+
"desktop:macos": bind("KeyR", ["primary", "shift"]),
|
|
26061
|
+
"desktop:windows": bind("KeyR", ["primary", "shift"]),
|
|
26062
|
+
"desktop:linux": bind("KeyR", ["primary", "shift"])
|
|
26063
|
+
},
|
|
25837
26064
|
"edrv.save": {
|
|
25838
26065
|
"desktop:macos": bind("KeyS", ["primary"]),
|
|
25839
26066
|
"desktop:windows": bind("KeyS", ["primary"]),
|
|
@@ -25969,6 +26196,12 @@ window.__ModuleLoader__.load({
|
|
|
25969
26196
|
* 设置一次性迁移进官方存储。
|
|
25970
26197
|
* 作者 ddj 2026年10月
|
|
25971
26198
|
*/
|
|
26199
|
+
/** @private @author ddj 2026年10月12号 @param id Command id. @param target Shortcut target. @returns Whether new page commands can safely run from this context. */
|
|
26200
|
+
function shortcutAllowed(id, target) {
|
|
26201
|
+
if (id === "edrv.toggleComment" || id === "edrv.duplicateLine") return pageKeysAllowed() && hasCodeFocus(target);
|
|
26202
|
+
if (id === "edrv.reloadWindow") return pageKeysAllowed();
|
|
26203
|
+
return true;
|
|
26204
|
+
}
|
|
25972
26205
|
const bridgeLog$1 = log.child("shortcuts-official");
|
|
25973
26206
|
/** 迁移比对用的默认键位表副本(模块态,避免每次调用重建)。 */
|
|
25974
26207
|
const KEYBINDING_DEFAULTS_OF = defaultKeybindings();
|
|
@@ -26057,6 +26290,7 @@ window.__ModuleLoader__.load({
|
|
|
26057
26290
|
const onKey = (event) => {
|
|
26058
26291
|
if (disposed) return;
|
|
26059
26292
|
const key = event;
|
|
26293
|
+
if (key.isComposing || key.keyCode === 229 || key.defaultPrevented) return;
|
|
26060
26294
|
const current = chords();
|
|
26061
26295
|
for (const def of defs) {
|
|
26062
26296
|
const chord = current[def.id];
|
|
@@ -26066,7 +26300,8 @@ window.__ModuleLoader__.load({
|
|
|
26066
26300
|
bindings = parseChords(chord);
|
|
26067
26301
|
cache.set(chord, bindings);
|
|
26068
26302
|
}
|
|
26069
|
-
if (
|
|
26303
|
+
if (def.id === "edrv.duplicateLine") continue;
|
|
26304
|
+
if (!matchEvent(key, bindings) || !shortcutAllowed(def.id, key.target) || !runner.isAvailable(def.id)) continue;
|
|
26070
26305
|
key.preventDefault();
|
|
26071
26306
|
key.stopPropagation();
|
|
26072
26307
|
runner.run(def.id);
|
|
@@ -26097,8 +26332,8 @@ window.__ModuleLoader__.load({
|
|
|
26097
26332
|
defaults: void 0,
|
|
26098
26333
|
regions: ["page", "editable"],
|
|
26099
26334
|
modals: [],
|
|
26100
|
-
resolve: () => {
|
|
26101
|
-
if (!runner.isAvailable(def.id)) return { status: "pass" };
|
|
26335
|
+
resolve: (context) => {
|
|
26336
|
+
if (context.modal !== null || !shortcutAllowed(def.id, context.target) || !runner.isAvailable(def.id)) return { status: "pass" };
|
|
26102
26337
|
return {
|
|
26103
26338
|
status: "handled",
|
|
26104
26339
|
run: () => {
|
|
@@ -35811,6 +36046,13 @@ window.__ModuleLoader__.load({
|
|
|
35811
36046
|
};
|
|
35812
36047
|
}, "vscode-mode: settings document editor route");
|
|
35813
36048
|
const commands = setupCommands(ctx);
|
|
36049
|
+
/** @private @author ddj 2026年10月12号 @param message Refresh refusal; surfaced in mounted editor status and diagnostic log. */
|
|
36050
|
+
const notifyReload = (message) => {
|
|
36051
|
+
log.warn(message);
|
|
36052
|
+
window.dispatchEvent(new CustomEvent("edrv:reload-status", { detail: message }));
|
|
36053
|
+
window.alert(message);
|
|
36054
|
+
};
|
|
36055
|
+
ctx.effect(() => installPageReload(flushPageState, notifyReload), "vscode-mode: guarded page reload");
|
|
35814
36056
|
const registry = createFileOpenerRegistry();
|
|
35815
36057
|
const workspaces = ctx.get("workspaces");
|
|
35816
36058
|
const sessions = ctx.get("sessions");
|