dsh-gufa-code 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +96 -2
- package/client.js +11548 -0
- package/cordis.patch.yml +8 -0
- package/docs/icon-180.png +0 -0
- package/docs/icon.svg +18 -0
- package/docs/market-submission.md +32 -0
- package/docs/screenshot-editor.jpg +0 -0
- package/docs/screenshot-ide.jpg +0 -0
- package/host/assets.js +31 -0
- package/host/chat.js +523 -0
- package/host/context.js +438 -0
- package/host/dev.js +35 -0
- package/host/fs.js +306 -0
- package/host/git.js +532 -0
- package/host/project.js +305 -0
- package/host/ssh.js +54 -0
- package/host/terminal-pty.js +237 -0
- package/host/terminal.js +947 -0
- package/icon.svg +18 -0
- package/index.js +63 -0
- package/locale/en.json +121 -0
- package/locale/zh.json +121 -0
- package/package.json +66 -4
- package/scripts/build-client.mjs +38 -0
- package/scripts/selftest-client.mjs +457 -0
- package/scripts/selftest-host.mjs +197 -0
- package/scripts/selftest-http.mjs +284 -0
- package/scripts/selftest-router.mjs +101 -0
- package/scripts/selftest-terminal-client.mjs +314 -0
- package/scripts/selftest-terminal-pty.mjs +190 -0
- package/scripts/selftest-terminal.mjs +351 -0
- package/scripts/test-chat-render.mjs +702 -0
- package/scripts/test-chat.mjs +761 -0
- package/scripts/test-editor.mjs +516 -0
- package/scripts/verify-workspace-sessions.mjs +135 -0
- package/src/client/00-core.js +1686 -0
- package/src/client/10-icons.js +205 -0
- package/src/client/20-editor.js +1318 -0
- package/src/client/30-terminal.js +1510 -0
- package/src/client/40-chat.js +1184 -0
- package/src/client/46-gitgraph.js +278 -0
- package/src/client/50-panels.js +916 -0
- package/src/client/55-backdrop.js +508 -0
- package/src/client/60-dock.js +662 -0
- package/src/client/85-shortcuts.js +516 -0
- package/src/client/90-app.js +2272 -0
- package/src/client/99-main.js +449 -0
- package/vendor/monaco/LICENSE +21 -0
- package/vendor/monaco/ThirdPartyNotices.txt +448 -0
- package/vendor/monaco/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- package/vendor/monaco/vs/base/worker/workerMain.js +31 -0
- package/vendor/monaco/vs/basic-languages/abap/abap.js +10 -0
- package/vendor/monaco/vs/basic-languages/apex/apex.js +10 -0
- package/vendor/monaco/vs/basic-languages/azcli/azcli.js +10 -0
- package/vendor/monaco/vs/basic-languages/bat/bat.js +10 -0
- package/vendor/monaco/vs/basic-languages/bicep/bicep.js +11 -0
- package/vendor/monaco/vs/basic-languages/cameligo/cameligo.js +10 -0
- package/vendor/monaco/vs/basic-languages/clojure/clojure.js +10 -0
- package/vendor/monaco/vs/basic-languages/coffee/coffee.js +10 -0
- package/vendor/monaco/vs/basic-languages/cpp/cpp.js +10 -0
- package/vendor/monaco/vs/basic-languages/csharp/csharp.js +10 -0
- package/vendor/monaco/vs/basic-languages/csp/csp.js +10 -0
- package/vendor/monaco/vs/basic-languages/css/css.js +12 -0
- package/vendor/monaco/vs/basic-languages/cypher/cypher.js +10 -0
- package/vendor/monaco/vs/basic-languages/dart/dart.js +10 -0
- package/vendor/monaco/vs/basic-languages/dockerfile/dockerfile.js +10 -0
- package/vendor/monaco/vs/basic-languages/ecl/ecl.js +10 -0
- package/vendor/monaco/vs/basic-languages/elixir/elixir.js +10 -0
- package/vendor/monaco/vs/basic-languages/flow9/flow9.js +10 -0
- package/vendor/monaco/vs/basic-languages/freemarker2/freemarker2.js +12 -0
- package/vendor/monaco/vs/basic-languages/fsharp/fsharp.js +10 -0
- package/vendor/monaco/vs/basic-languages/go/go.js +10 -0
- package/vendor/monaco/vs/basic-languages/graphql/graphql.js +10 -0
- package/vendor/monaco/vs/basic-languages/handlebars/handlebars.js +10 -0
- package/vendor/monaco/vs/basic-languages/hcl/hcl.js +10 -0
- package/vendor/monaco/vs/basic-languages/html/html.js +10 -0
- package/vendor/monaco/vs/basic-languages/ini/ini.js +10 -0
- package/vendor/monaco/vs/basic-languages/java/java.js +10 -0
- package/vendor/monaco/vs/basic-languages/javascript/javascript.js +10 -0
- package/vendor/monaco/vs/basic-languages/julia/julia.js +10 -0
- package/vendor/monaco/vs/basic-languages/kotlin/kotlin.js +10 -0
- package/vendor/monaco/vs/basic-languages/less/less.js +11 -0
- package/vendor/monaco/vs/basic-languages/lexon/lexon.js +10 -0
- package/vendor/monaco/vs/basic-languages/liquid/liquid.js +10 -0
- package/vendor/monaco/vs/basic-languages/lua/lua.js +10 -0
- package/vendor/monaco/vs/basic-languages/m3/m3.js +10 -0
- package/vendor/monaco/vs/basic-languages/markdown/markdown.js +10 -0
- package/vendor/monaco/vs/basic-languages/mdx/mdx.js +10 -0
- package/vendor/monaco/vs/basic-languages/mips/mips.js +10 -0
- package/vendor/monaco/vs/basic-languages/msdax/msdax.js +10 -0
- package/vendor/monaco/vs/basic-languages/mysql/mysql.js +10 -0
- package/vendor/monaco/vs/basic-languages/objective-c/objective-c.js +10 -0
- package/vendor/monaco/vs/basic-languages/pascal/pascal.js +10 -0
- package/vendor/monaco/vs/basic-languages/pascaligo/pascaligo.js +10 -0
- package/vendor/monaco/vs/basic-languages/perl/perl.js +10 -0
- package/vendor/monaco/vs/basic-languages/pgsql/pgsql.js +10 -0
- package/vendor/monaco/vs/basic-languages/php/php.js +10 -0
- package/vendor/monaco/vs/basic-languages/pla/pla.js +10 -0
- package/vendor/monaco/vs/basic-languages/postiats/postiats.js +10 -0
- package/vendor/monaco/vs/basic-languages/powerquery/powerquery.js +10 -0
- package/vendor/monaco/vs/basic-languages/powershell/powershell.js +10 -0
- package/vendor/monaco/vs/basic-languages/protobuf/protobuf.js +11 -0
- package/vendor/monaco/vs/basic-languages/pug/pug.js +10 -0
- package/vendor/monaco/vs/basic-languages/python/python.js +10 -0
- package/vendor/monaco/vs/basic-languages/qsharp/qsharp.js +10 -0
- package/vendor/monaco/vs/basic-languages/r/r.js +10 -0
- package/vendor/monaco/vs/basic-languages/razor/razor.js +10 -0
- package/vendor/monaco/vs/basic-languages/redis/redis.js +10 -0
- package/vendor/monaco/vs/basic-languages/redshift/redshift.js +10 -0
- package/vendor/monaco/vs/basic-languages/restructuredtext/restructuredtext.js +10 -0
- package/vendor/monaco/vs/basic-languages/ruby/ruby.js +10 -0
- package/vendor/monaco/vs/basic-languages/rust/rust.js +10 -0
- package/vendor/monaco/vs/basic-languages/sb/sb.js +10 -0
- package/vendor/monaco/vs/basic-languages/scala/scala.js +10 -0
- package/vendor/monaco/vs/basic-languages/scheme/scheme.js +10 -0
- package/vendor/monaco/vs/basic-languages/scss/scss.js +12 -0
- package/vendor/monaco/vs/basic-languages/shell/shell.js +10 -0
- package/vendor/monaco/vs/basic-languages/solidity/solidity.js +10 -0
- package/vendor/monaco/vs/basic-languages/sophia/sophia.js +10 -0
- package/vendor/monaco/vs/basic-languages/sparql/sparql.js +10 -0
- package/vendor/monaco/vs/basic-languages/sql/sql.js +10 -0
- package/vendor/monaco/vs/basic-languages/st/st.js +10 -0
- package/vendor/monaco/vs/basic-languages/swift/swift.js +13 -0
- package/vendor/monaco/vs/basic-languages/systemverilog/systemverilog.js +10 -0
- package/vendor/monaco/vs/basic-languages/tcl/tcl.js +10 -0
- package/vendor/monaco/vs/basic-languages/twig/twig.js +10 -0
- package/vendor/monaco/vs/basic-languages/typescript/typescript.js +10 -0
- package/vendor/monaco/vs/basic-languages/typespec/typespec.js +10 -0
- package/vendor/monaco/vs/basic-languages/vb/vb.js +10 -0
- package/vendor/monaco/vs/basic-languages/wgsl/wgsl.js +307 -0
- package/vendor/monaco/vs/basic-languages/xml/xml.js +10 -0
- package/vendor/monaco/vs/basic-languages/yaml/yaml.js +10 -0
- package/vendor/monaco/vs/editor/editor.main.css +8 -0
- package/vendor/monaco/vs/editor/editor.main.js +798 -0
- package/vendor/monaco/vs/language/css/cssMode.js +13 -0
- package/vendor/monaco/vs/language/css/cssWorker.js +77 -0
- package/vendor/monaco/vs/language/html/htmlMode.js +13 -0
- package/vendor/monaco/vs/language/html/htmlWorker.js +454 -0
- package/vendor/monaco/vs/language/json/jsonMode.js +19 -0
- package/vendor/monaco/vs/language/json/jsonWorker.js +42 -0
- package/vendor/monaco/vs/language/typescript/tsMode.js +20 -0
- package/vendor/monaco/vs/language/typescript/tsWorker.js +51328 -0
- package/vendor/monaco/vs/loader.js +11 -0
- package/vendor/monaco/vs/nls.messages.de.js +21 -0
- package/vendor/monaco/vs/nls.messages.es.js +21 -0
- package/vendor/monaco/vs/nls.messages.fr.js +19 -0
- package/vendor/monaco/vs/nls.messages.it.js +19 -0
- package/vendor/monaco/vs/nls.messages.ja.js +21 -0
- package/vendor/monaco/vs/nls.messages.ko.js +19 -0
- package/vendor/monaco/vs/nls.messages.ru.js +21 -0
- package/vendor/monaco/vs/nls.messages.zh-cn.js +21 -0
- package/vendor/monaco/vs/nls.messages.zh-tw.js +19 -0
- package/vendor/xterm/LICENSE +21 -0
- package/vendor/xterm/LICENSE.addon-fit +19 -0
- package/vendor/xterm/addon-fit.js +2 -0
- package/vendor/xterm/xterm.css +285 -0
- package/vendor/xterm/xterm.js +2 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 99-main:client 模块装配 —— 注册「古法编程」侧边栏按钮与 main 面板。
|
|
3
|
+
* 这是 build-client.mjs 拼接的最后一个文件,factory 必须在这里返回插件对象。
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* 把「古法编程」作为一行注入到工作区行的「...」菜单里(第二个位置)。
|
|
7
|
+
* DSH 的 workspace 菜单没有可用的 slot,只能在菜单弹出后克隆原生菜单项注入。
|
|
8
|
+
* 菜单是复用的 portal 容器,打开时不产生 childList 变化,因此用点击/指针事件 + 轮询兜底触发扫描。
|
|
9
|
+
*/
|
|
10
|
+
G.installWorkspaceMenuEntry = function installWorkspaceMenuEntry(ctx) {
|
|
11
|
+
if (typeof document === 'undefined') return () => {}
|
|
12
|
+
let lastWorkspaceKey = ''
|
|
13
|
+
let observer = null
|
|
14
|
+
let scanTimer = 0
|
|
15
|
+
let pollTimer = 0
|
|
16
|
+
let diagLogged = false
|
|
17
|
+
|
|
18
|
+
const log = (message) => {
|
|
19
|
+
try {
|
|
20
|
+
void G.api('dev.log', { message: `menu-entry ${message}` }).catch(() => {})
|
|
21
|
+
} catch {
|
|
22
|
+
/* ignore */
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const removeInjected = () => {
|
|
27
|
+
for (const node of Array.from(document.querySelectorAll('[data-gufa-workspace-menu]'))) node.remove()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const labelOf = () => {
|
|
31
|
+
try {
|
|
32
|
+
return G.t('panel.label')
|
|
33
|
+
} catch {
|
|
34
|
+
return '古法编程'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const isRenameText = (text) => text === '重命名' || /^rename$/i.test(text)
|
|
39
|
+
const isDeleteText = (text) => text.includes('删除工作区') || /delete workspace/i.test(text)
|
|
40
|
+
|
|
41
|
+
/** 找文本最深匹配的元素(避免选中包含图标的父容器)。 */
|
|
42
|
+
const deepestByText = (scope, predicate) => {
|
|
43
|
+
let best = null
|
|
44
|
+
for (const element of Array.from(scope.querySelectorAll('*'))) {
|
|
45
|
+
const text = (element.textContent ?? '').trim()
|
|
46
|
+
if (text !== '' && predicate(text)) best = element
|
|
47
|
+
}
|
|
48
|
+
return best
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** 同时包含「重命名 / 删除工作区」的最小容器。 */
|
|
52
|
+
const findWorkspaceMenu = () => {
|
|
53
|
+
for (const menu of Array.from(document.querySelectorAll('[role="menu"]'))) {
|
|
54
|
+
const text = menu.textContent ?? ''
|
|
55
|
+
if (isDeleteText(text) && /重命名|rename/i.test(text)) return menu
|
|
56
|
+
}
|
|
57
|
+
for (const element of Array.from(document.body.querySelectorAll('div,ul,section'))) {
|
|
58
|
+
const text = element.textContent ?? ''
|
|
59
|
+
if (!isDeleteText(text) || !/重命名|rename/i.test(text)) continue
|
|
60
|
+
let deeper = false
|
|
61
|
+
for (const child of Array.from(element.children)) {
|
|
62
|
+
const childText = child.textContent ?? ''
|
|
63
|
+
if (isDeleteText(childText) && /重命名|rename/i.test(childText)) {
|
|
64
|
+
deeper = true
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!deeper) return element
|
|
69
|
+
}
|
|
70
|
+
return null
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const injectInto = (menu) => {
|
|
74
|
+
if (menu.querySelector('[data-gufa-workspace-menu]') !== null) return true
|
|
75
|
+
const renameEl = deepestByText(menu, isRenameText)
|
|
76
|
+
const deleteEl = deepestByText(menu, isDeleteText)
|
|
77
|
+
if (renameEl === null || deleteEl === null) return false
|
|
78
|
+
/* 两个菜单项的最近公共父节点 = 菜单列表容器 */
|
|
79
|
+
let list = renameEl.parentElement
|
|
80
|
+
while (list !== null && !list.contains(deleteEl)) list = list.parentElement
|
|
81
|
+
if (list === null) return false
|
|
82
|
+
/* 从列表容器向上找一层得到完整的菜单项(含原生图标与内边距),而不是只克隆文字节点 */
|
|
83
|
+
const liftToItem = (target) => {
|
|
84
|
+
let item = target
|
|
85
|
+
while (item.parentElement !== null && item.parentElement !== list) item = item.parentElement
|
|
86
|
+
return item
|
|
87
|
+
}
|
|
88
|
+
const renameItem = liftToItem(renameEl)
|
|
89
|
+
const deleteItem = liftToItem(deleteEl)
|
|
90
|
+
const row = renameItem.cloneNode(true)
|
|
91
|
+
row.setAttribute('data-gufa-workspace-menu', '')
|
|
92
|
+
/* 换标签:只替换最深的纯文本元素,保留原生图标 */
|
|
93
|
+
const label = deepestByText(row, isRenameText)
|
|
94
|
+
if (label !== null && label.children.length === 0) label.textContent = labelOf()
|
|
95
|
+
else row.textContent = labelOf()
|
|
96
|
+
row.addEventListener('click', (event) => {
|
|
97
|
+
event.preventDefault()
|
|
98
|
+
event.stopPropagation()
|
|
99
|
+
void enterFromWorkspace(lastWorkspaceKey)
|
|
100
|
+
}, true)
|
|
101
|
+
try {
|
|
102
|
+
list.insertBefore(row, deleteItem)
|
|
103
|
+
} catch {
|
|
104
|
+
return false
|
|
105
|
+
}
|
|
106
|
+
return true
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const enterFromWorkspace = async (workspaceKey) => {
|
|
110
|
+
try {
|
|
111
|
+
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }))
|
|
112
|
+
} catch {
|
|
113
|
+
/* ignore */
|
|
114
|
+
}
|
|
115
|
+
G.pendingEntryWorkspace = typeof workspaceKey === 'string' && workspaceKey !== '' ? { key: workspaceKey } : null
|
|
116
|
+
window.setTimeout(() => {
|
|
117
|
+
try {
|
|
118
|
+
G.enterGufa()
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error('[gufa-code] 从工作区菜单进入失败', error)
|
|
121
|
+
}
|
|
122
|
+
}, 120)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const scan = () => {
|
|
126
|
+
try {
|
|
127
|
+
const menu = findWorkspaceMenu()
|
|
128
|
+
if (menu === null) return
|
|
129
|
+
const injected = injectInto(menu)
|
|
130
|
+
if (!diagLogged) {
|
|
131
|
+
diagLogged = true
|
|
132
|
+
log(`diag roleMenus=${document.querySelectorAll('[role="menu"]').length} injected=${injected}`)
|
|
133
|
+
}
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (!diagLogged) {
|
|
136
|
+
diagLogged = true
|
|
137
|
+
log(`diag error=${String(error && error.message ? error.message : error)}`)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const scheduleScan = (delayMs) => {
|
|
143
|
+
if (scanTimer !== 0) return
|
|
144
|
+
scanTimer = window.setTimeout(() => {
|
|
145
|
+
scanTimer = 0
|
|
146
|
+
scan()
|
|
147
|
+
}, typeof delayMs === 'number' ? delayMs : 50)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const onPointer = (event) => {
|
|
151
|
+
scheduleScan(60)
|
|
152
|
+
scheduleScan(220)
|
|
153
|
+
try {
|
|
154
|
+
const target = event.target
|
|
155
|
+
const row = target !== null && target !== undefined && typeof target.closest === 'function'
|
|
156
|
+
? target.closest('[data-row-key^="workspace:"]')
|
|
157
|
+
: null
|
|
158
|
+
if (row !== null) lastWorkspaceKey = String(row.getAttribute('data-row-key') ?? '').slice('workspace:'.length)
|
|
159
|
+
} catch {
|
|
160
|
+
/* ignore */
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
removeInjected()
|
|
165
|
+
document.addEventListener('click', onPointer, true)
|
|
166
|
+
document.addEventListener('pointerdown', onPointer, true)
|
|
167
|
+
try {
|
|
168
|
+
observer = new MutationObserver(() => scheduleScan(60))
|
|
169
|
+
observer.observe(document.body, { childList: true, subtree: true })
|
|
170
|
+
} catch {
|
|
171
|
+
observer = null
|
|
172
|
+
}
|
|
173
|
+
pollTimer = window.setInterval(() => {
|
|
174
|
+
try {
|
|
175
|
+
if (typeof G.mode?.get === 'function' && G.mode.get() === true) return
|
|
176
|
+
if (document.querySelector('[role="menu"]') !== null) scan()
|
|
177
|
+
} catch {
|
|
178
|
+
/* ignore */
|
|
179
|
+
}
|
|
180
|
+
}, 500)
|
|
181
|
+
return () => {
|
|
182
|
+
if (pollTimer !== 0) window.clearInterval(pollTimer)
|
|
183
|
+
pollTimer = 0
|
|
184
|
+
if (observer !== null) observer.disconnect()
|
|
185
|
+
observer = null
|
|
186
|
+
if (scanTimer !== 0) window.clearTimeout(scanTimer)
|
|
187
|
+
scanTimer = 0
|
|
188
|
+
document.removeEventListener('click', onPointer, true)
|
|
189
|
+
document.removeEventListener('pointerdown', onPointer, true)
|
|
190
|
+
removeInjected()
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** 「更多」菜单里的入口行:点击进入古法编程(从当前工作区进入)。 */
|
|
195
|
+
G.MoreMenuEntry = function MoreMenuEntry(props) {
|
|
196
|
+
return h(
|
|
197
|
+
'button',
|
|
198
|
+
{
|
|
199
|
+
type: 'button',
|
|
200
|
+
className: 'gufa-more-entry',
|
|
201
|
+
role: 'menuitem',
|
|
202
|
+
onClick: () => {
|
|
203
|
+
try {
|
|
204
|
+
props.onDismiss?.()
|
|
205
|
+
} catch {
|
|
206
|
+
/* ignore */
|
|
207
|
+
}
|
|
208
|
+
G.enterGufa()
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
h(G.Icon, { name: 'seal', size: 16 }),
|
|
212
|
+
h('span', { className: 'gufa-more-entry-label' }, G.t('panel.label')),
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const GUFA_PLUGIN = {
|
|
217
|
+
inject: [],
|
|
218
|
+
apply(ctx) {
|
|
219
|
+
G.ctx = ctx
|
|
220
|
+
|
|
221
|
+
/* HMR 重载时如果仍处于 IDE 模式,重新打上标记(卸载清理会移除它) */
|
|
222
|
+
try {
|
|
223
|
+
if (typeof G.mode?.get === 'function' && G.mode.get() === true) document.documentElement.setAttribute('data-gufa-ide', '')
|
|
224
|
+
} catch {
|
|
225
|
+
/* ignore */
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 样式(随插件 context 卸载自动移除)
|
|
229
|
+
ctx.effect(() => G.injectStyles(), 'gufa-code: styles')
|
|
230
|
+
|
|
231
|
+
// 文案:注册进 Client locale 服务;缺服务时保持内置中文
|
|
232
|
+
const locale = ctx.get('locale')
|
|
233
|
+
if (locale !== undefined && locale !== null) {
|
|
234
|
+
try {
|
|
235
|
+
ctx.effect(() => locale.register(GUFA_NS, { zh: DICT_ZH, en: DICT_EN }), 'gufa-code: locale')
|
|
236
|
+
const bound = locale.bind(GUFA_NS)
|
|
237
|
+
G.t = (key, params) => bound(key, params)
|
|
238
|
+
G.translate = G.t
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error('[gufa-code] 注册文案失败,使用内置中文', error)
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// 启动诊断:记录页面地址、真实 Host 基址与传输信息(排查桌面壳 dsh-app:// 连接问题)。
|
|
245
|
+
try {
|
|
246
|
+
const transport = typeof globalThis.__DSH_TRANSPORT__ === 'object' && globalThis.__DSH_TRANSPORT__ !== null ? globalThis.__DSH_TRANSPORT__ : null
|
|
247
|
+
void G.api('dev.log', {
|
|
248
|
+
message: `boot href=${location.href} base=${G.serverBase()} transport=${transport === null ? 'none' : JSON.stringify(transport)}`,
|
|
249
|
+
}).catch(() => {})
|
|
250
|
+
} catch {
|
|
251
|
+
/* 诊断失败不影响启动 */
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// 全局错误捕获:渲染/事件/异步异常都写入诊断日志(排查「切不进去」)。
|
|
255
|
+
try {
|
|
256
|
+
if (window.__gufaErrorHooks !== true) {
|
|
257
|
+
window.__gufaErrorHooks = true
|
|
258
|
+
let errorLogs = 30
|
|
259
|
+
const logError = (kind, detail) => {
|
|
260
|
+
if (errorLogs <= 0) return
|
|
261
|
+
errorLogs -= 1
|
|
262
|
+
try {
|
|
263
|
+
void G.api('dev.log', { message: `${kind} ${String(detail).slice(0, 2000)}` }).catch(() => {})
|
|
264
|
+
} catch {
|
|
265
|
+
/* ignore */
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
window.addEventListener('error', (event) => {
|
|
269
|
+
const error = event && event.error
|
|
270
|
+
logError('window-error', `${event && event.message ? event.message : ''} | ${error && error.stack ? error.stack : ''}`)
|
|
271
|
+
})
|
|
272
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
273
|
+
const reason = event && event.reason
|
|
274
|
+
logError('unhandled-rejection', reason && reason.stack ? reason.stack : String(reason))
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
} catch {
|
|
278
|
+
/* ignore */
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// IDE 快捷键:接入 DSH 原生快捷键派发(只做命令改派,不修改其定义/配置),
|
|
282
|
+
// 另外安装 DOM 兜底桥(给未被原生拦截的组合用)。
|
|
283
|
+
try {
|
|
284
|
+
if (typeof G.installShortcutIntegration === 'function' && typeof ctx.inject === 'function') {
|
|
285
|
+
ctx.inject(['shortcuts'], (scope) => {
|
|
286
|
+
try {
|
|
287
|
+
let service
|
|
288
|
+
try {
|
|
289
|
+
service = typeof scope.get === 'function' ? scope.get('shortcuts') : scope.shortcuts
|
|
290
|
+
} catch {
|
|
291
|
+
service = undefined
|
|
292
|
+
}
|
|
293
|
+
if (service !== undefined && service !== null) G.installShortcutIntegration(service)
|
|
294
|
+
} catch (error) {
|
|
295
|
+
console.error('[gufa-code] 安装原生快捷键集成失败', error)
|
|
296
|
+
}
|
|
297
|
+
try {
|
|
298
|
+
if (typeof G.installShortcutBridge === 'function') G.installShortcutBridge()
|
|
299
|
+
} catch (error) {
|
|
300
|
+
console.error('[gufa-code] 安装快捷键桥失败', error)
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error('[gufa-code] 快捷键服务不可用', error)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 编辑器焦点/命令通道自检(每个页面会话一次,只读安全:只做聚焦 + 全选)
|
|
309
|
+
try {
|
|
310
|
+
if (window.sessionStorage.getItem('gufa-editor-focus-selftest-v2') !== '1') {
|
|
311
|
+
window.sessionStorage.setItem('gufa-editor-focus-selftest-v2', '1')
|
|
312
|
+
let attempts = 0
|
|
313
|
+
const runSelfTest = () => {
|
|
314
|
+
attempts += 1
|
|
315
|
+
try {
|
|
316
|
+
const debug = G.__gufaEditorDebug
|
|
317
|
+
if (debug === undefined || debug === null) {
|
|
318
|
+
if (attempts < 40) {
|
|
319
|
+
window.setTimeout(runSelfTest, 500)
|
|
320
|
+
return
|
|
321
|
+
}
|
|
322
|
+
void G.api('dev.log', { message: 'editor-focus-selftest no-editor-open' }).catch(() => {})
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
debug.focus()
|
|
326
|
+
window.setTimeout(() => {
|
|
327
|
+
try {
|
|
328
|
+
const active = document.activeElement
|
|
329
|
+
const focused = typeof G.isEditorFocused === 'function' ? G.isEditorFocused() : '?'
|
|
330
|
+
const hasText = typeof debug.hasTextFocus === 'function' ? debug.hasTextFocus() : '?'
|
|
331
|
+
G.bus.emit('editor:command', 'editor.action.selectAll')
|
|
332
|
+
void G.api('dev.log', {
|
|
333
|
+
message: `editor-focus-selftest isEditorFocused=${focused} hasTextFocus=${hasText} controller=${G.__gufaEditorController !== null && G.__gufaEditorController !== undefined} active=${active === null || active === undefined ? 'null' : `${String(active.tagName ?? '?')}.${String(active.className ?? '').slice(0, 60)}`}`,
|
|
334
|
+
}).catch(() => {})
|
|
335
|
+
} catch (error) {
|
|
336
|
+
void G.api('dev.log', { message: `editor-focus-selftest-error ${String(error)}` }).catch(() => {})
|
|
337
|
+
}
|
|
338
|
+
}, 200)
|
|
339
|
+
} catch (error) {
|
|
340
|
+
void G.api('dev.log', { message: `editor-focus-selftest-error ${String(error)}` }).catch(() => {})
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
window.setTimeout(runSelfTest, 2000)
|
|
344
|
+
}
|
|
345
|
+
} catch {
|
|
346
|
+
/* 自检失败不影响启动 */
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// 终端 PTY 自检(每个页面会话一次):确认 Live Host 的 spawnTerminal 能出数据。
|
|
350
|
+
try {
|
|
351
|
+
if (window.sessionStorage.getItem('gufa-code-probe') !== '1') {
|
|
352
|
+
window.sessionStorage.setItem('gufa-code-probe', '1')
|
|
353
|
+
window.setTimeout(() => {
|
|
354
|
+
void G.api('terminal.probe', {})
|
|
355
|
+
.then((value) => G.api('dev.log', { message: `terminal-probe ${JSON.stringify(value).slice(0, 3000)}` }))
|
|
356
|
+
.catch((error) => G.api('dev.log', { message: `terminal-probe-failed ${G.errorText(error)}` }))
|
|
357
|
+
.catch(() => {})
|
|
358
|
+
}, 800)
|
|
359
|
+
}
|
|
360
|
+
} catch {
|
|
361
|
+
/* 自检失败不影响启动 */
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Host 半版本探针:新 Host 有 dev.version;旧 Host 返回 not-found。
|
|
365
|
+
// 注册一个空的 overlay 条目(id 带版本状态),便于通过 Client Inspect 验证当前 Host 代际。
|
|
366
|
+
void G.api('dev.version', {})
|
|
367
|
+
.then(() => 'ok')
|
|
368
|
+
.catch(() => 'stale')
|
|
369
|
+
.then((hostState) => {
|
|
370
|
+
try {
|
|
371
|
+
ctx.inject(['slots'], (scope) => {
|
|
372
|
+
scope.slots.inject('shell.overlay', () => scope.slots.register({
|
|
373
|
+
name: 'shell.overlay',
|
|
374
|
+
id: `gufa-code-host-${hostState}`,
|
|
375
|
+
order: 999,
|
|
376
|
+
}, () => null))
|
|
377
|
+
})
|
|
378
|
+
} catch (error) {
|
|
379
|
+
console.error('[gufa-code] 注册 Host 探针失败', error)
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
// 开启滑动变祖的「31 档细分」:让外观滑块/背景停在松手位置,只有思考等级提交最近档位。
|
|
384
|
+
try {
|
|
385
|
+
const fineResult = typeof G.ensureSkinFineStops === 'function' ? G.ensureSkinFineStops() : { applied: false }
|
|
386
|
+
if (fineResult.applied === true) console.info('[gufa-code] 已开启滑动变祖「31 档细分」(思考等级仍按最近真实档位提交)')
|
|
387
|
+
} catch (error) {
|
|
388
|
+
console.error('[gufa-code] 开启「31 档细分」失败', error)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// 梁圣皮肤背景诊断探针:延迟读取一次皮肤背景状态,注册空 overlay 条目便于 Inspect 验证。
|
|
392
|
+
window.setTimeout(() => {
|
|
393
|
+
try {
|
|
394
|
+
const meta = typeof G.readSkinBackdropMeta === 'function' ? G.readSkinBackdropMeta() : { available: false }
|
|
395
|
+
const probeId = meta.available === true
|
|
396
|
+
? (meta.portraitSrc ? 'gufa-code-backdrop-portrait' : 'gufa-code-backdrop-color')
|
|
397
|
+
: 'gufa-code-backdrop-missing'
|
|
398
|
+
ctx.inject(['slots'], (scope) => {
|
|
399
|
+
scope.slots.inject('shell.overlay', () => scope.slots.register({
|
|
400
|
+
name: 'shell.overlay',
|
|
401
|
+
id: probeId,
|
|
402
|
+
order: 998,
|
|
403
|
+
}, () => null))
|
|
404
|
+
const fine = typeof G.readSkinFineStops === 'function' && G.readSkinFineStops() === true
|
|
405
|
+
scope.slots.inject('shell.overlay', () => scope.slots.register({
|
|
406
|
+
name: 'shell.overlay',
|
|
407
|
+
id: `gufa-code-skin-fine-${fine ? 'on' : 'off'}`,
|
|
408
|
+
order: 997,
|
|
409
|
+
}, () => null))
|
|
410
|
+
})
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error('[gufa-code] 注册背景探针失败', error)
|
|
413
|
+
}
|
|
414
|
+
}, 2500)
|
|
415
|
+
|
|
416
|
+
// 入口:工作区行的「...」菜单里注入「古法编程」一行。
|
|
417
|
+
try {
|
|
418
|
+
const disposeMenuEntry = G.installWorkspaceMenuEntry(ctx)
|
|
419
|
+
if (typeof disposeMenuEntry === 'function') ctx.effect(() => disposeMenuEntry, 'gufa-code: workspace menu entry')
|
|
420
|
+
} catch (error) {
|
|
421
|
+
console.error('[gufa-code] 安装工作区菜单入口失败', error)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// 面板注册:入口注入到工作区行的「...」菜单里(DSH 未提供该菜单的 slot)。
|
|
425
|
+
ctx.inject(['slots'], (scope) => {
|
|
426
|
+
// main key 仅作安全网:若侧边栏把主面板切到 gufa-code(点击拦截失效时),
|
|
427
|
+
// MainRedirect 会立刻进入覆盖层并把主视图还原成官方会话。
|
|
428
|
+
scope.slots.inject('main', () => scope.slots.register({
|
|
429
|
+
name: 'main',
|
|
430
|
+
key: G.PANEL_ID,
|
|
431
|
+
}, G.MainRedirect))
|
|
432
|
+
// 全屏 IDE 覆盖层:官方会话面板在底下保持挂载,右侧 AI 栏由 60-dock 停靠复用。
|
|
433
|
+
scope.slots.inject('shell.overlay', () => scope.slots.register({
|
|
434
|
+
name: 'shell.overlay',
|
|
435
|
+
id: 'gufa-code-overlay',
|
|
436
|
+
order: 100,
|
|
437
|
+
}, G.OverlayHost))
|
|
438
|
+
})
|
|
439
|
+
},
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// 浏览器控制台调试句柄(不影响注册;页面刷新后重建)
|
|
443
|
+
try {
|
|
444
|
+
if (typeof window !== 'undefined') window.__GUFA_CODE__ = { version: '1.0.0', G, plugin: GUFA_PLUGIN }
|
|
445
|
+
} catch {
|
|
446
|
+
/* 忽略只读 window */
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return GUFA_PLUGIN
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2016 - present Microsoft Corporation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|