dsh-git-changes 0.0.0-stage → 0.1.1
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.en.md +119 -0
- package/README.md +137 -2
- package/THIRD_PARTY_NOTICES.md +12 -0
- package/assets/vendor/monaco/LICENSE +21 -0
- package/assets/vendor/monaco/ThirdPartyNotices.txt +448 -0
- package/assets/vendor/monaco/vs/base/browser/ui/codicons/codicon/codicon.ttf +0 -0
- package/assets/vendor/monaco/vs/base/common/worker/simpleWorker.nls.js +7 -0
- package/assets/vendor/monaco/vs/base/common/worker/simpleWorker.nls.zh-cn.js +7 -0
- package/assets/vendor/monaco/vs/base/worker/workerMain.js +26 -0
- package/assets/vendor/monaco/vs/basic-languages/abap/abap.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/apex/apex.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/azcli/azcli.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/bat/bat.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/bicep/bicep.js +11 -0
- package/assets/vendor/monaco/vs/basic-languages/cameligo/cameligo.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/clojure/clojure.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/coffee/coffee.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/cpp/cpp.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/csharp/csharp.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/csp/csp.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/css/css.js +12 -0
- package/assets/vendor/monaco/vs/basic-languages/cypher/cypher.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/dart/dart.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/dockerfile/dockerfile.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/ecl/ecl.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/elixir/elixir.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/flow9/flow9.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/freemarker2/freemarker2.js +12 -0
- package/assets/vendor/monaco/vs/basic-languages/fsharp/fsharp.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/go/go.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/graphql/graphql.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/handlebars/handlebars.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/hcl/hcl.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/html/html.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/ini/ini.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/java/java.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/javascript/javascript.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/julia/julia.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/kotlin/kotlin.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/less/less.js +11 -0
- package/assets/vendor/monaco/vs/basic-languages/lexon/lexon.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/liquid/liquid.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/lua/lua.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/m3/m3.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/markdown/markdown.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/mdx/mdx.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/mips/mips.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/msdax/msdax.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/mysql/mysql.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/objective-c/objective-c.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/pascal/pascal.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/pascaligo/pascaligo.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/perl/perl.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/pgsql/pgsql.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/php/php.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/pla/pla.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/postiats/postiats.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/powerquery/powerquery.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/powershell/powershell.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/protobuf/protobuf.js +11 -0
- package/assets/vendor/monaco/vs/basic-languages/pug/pug.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/python/python.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/qsharp/qsharp.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/r/r.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/razor/razor.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/redis/redis.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/redshift/redshift.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/restructuredtext/restructuredtext.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/ruby/ruby.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/rust/rust.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/sb/sb.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/scala/scala.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/scheme/scheme.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/scss/scss.js +12 -0
- package/assets/vendor/monaco/vs/basic-languages/shell/shell.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/solidity/solidity.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/sophia/sophia.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/sparql/sparql.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/sql/sql.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/st/st.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/swift/swift.js +13 -0
- package/assets/vendor/monaco/vs/basic-languages/systemverilog/systemverilog.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/tcl/tcl.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/twig/twig.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/typescript/typescript.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/vb/vb.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/wgsl/wgsl.js +307 -0
- package/assets/vendor/monaco/vs/basic-languages/xml/xml.js +10 -0
- package/assets/vendor/monaco/vs/basic-languages/yaml/yaml.js +10 -0
- package/assets/vendor/monaco/vs/editor/editor.main.css +6 -0
- package/assets/vendor/monaco/vs/editor/editor.main.js +744 -0
- package/assets/vendor/monaco/vs/editor/editor.main.nls.js +28 -0
- package/assets/vendor/monaco/vs/editor/editor.main.nls.zh-cn.js +30 -0
- package/assets/vendor/monaco/vs/language/css/cssMode.js +13 -0
- package/assets/vendor/monaco/vs/language/css/cssWorker.js +81 -0
- package/assets/vendor/monaco/vs/language/html/htmlMode.js +13 -0
- package/assets/vendor/monaco/vs/language/html/htmlWorker.js +453 -0
- package/assets/vendor/monaco/vs/language/json/jsonMode.js +15 -0
- package/assets/vendor/monaco/vs/language/json/jsonWorker.js +36 -0
- package/assets/vendor/monaco/vs/language/typescript/tsMode.js +20 -0
- package/assets/vendor/monaco/vs/language/typescript/tsWorker.js +37016 -0
- package/assets/vendor/monaco/vs/loader.js +10 -0
- package/cordis.patch.yml +3 -0
- package/docs/images/git-changes-diff.png +0 -0
- package/docs/regression-checklist.md +47 -0
- package/icon.svg +10 -0
- package/lib/client.js +9418 -0
- package/lib/host/assets.js +161 -0
- package/lib/host/commit-message.js +158 -0
- package/lib/host/files-index.js +206 -0
- package/lib/host/git-write.js +194 -0
- package/lib/host/git.js +1147 -0
- package/lib/host/lsp.js +842 -0
- package/lib/host/routes.js +976 -0
- package/lib/host/session.js +84 -0
- package/lib/host/util.js +107 -0
- package/lib/index.js +33 -0
- package/licenses/dsh-vscode-mode-LICENSE +21 -0
- package/licenses/vscode-theme-seti-LICENSE +21 -0
- package/licenses/vscode-theme-seti-ThirdPartyNotices.txt +32 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +65 -4
- package/src/client/00-const.js +79 -0
- package/src/client/05-utils.js +277 -0
- package/src/client/10-i18n.js +548 -0
- package/src/client/12-vscode-icons.js +460 -0
- package/src/client/15-theme.js +509 -0
- package/src/client/20-monaco.js +1404 -0
- package/src/client/22-lsp.js +592 -0
- package/src/client/25-tree.js +563 -0
- package/src/client/28-changes.js +1163 -0
- package/src/client/30-page.js +2259 -0
- package/src/client/35-pill.js +185 -0
- package/src/client/40-commands.js +248 -0
- package/src/client/45-css.js +357 -0
- package/src/client/47-pickers.js +457 -0
- package/src/client/50-apply.js +207 -0
- package/src/client/99-footer.js +62 -0
|
@@ -0,0 +1,509 @@
|
|
|
1
|
+
/* dsh-git-changes client half — DSH theme detection and Monaco themes. */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Diff palette (adapted from the MIT `dsh-vscode-mode` reference): [addedBg, removedBg, addedText, removedText].
|
|
5
|
+
* Light/dark values keep Cursor-style whole-line tints readable on the DSH surfaces.
|
|
6
|
+
*/
|
|
7
|
+
const DIFF_PALETTE = {
|
|
8
|
+
// [added line bg, removed line bg, added word bg, removed word bg]
|
|
9
|
+
// Deliberately stronger than Monaco's defaults so changed lines read at a
|
|
10
|
+
// glance in both the inline renderer and the side-by-side diff editor.
|
|
11
|
+
light: ['#d7f0dc', '#ffd9d3', '#9fdcae99', '#f4a89a99'],
|
|
12
|
+
dark: ['#1f4d33', '#5a2626', '#2f8a5699', '#c2555599'],
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Syntax token rules (dark). */
|
|
16
|
+
const DARK_RULES = [
|
|
17
|
+
{ token: '', foreground: 'd4d4d4' },
|
|
18
|
+
{ token: 'comment', foreground: '6a9955', fontStyle: 'italic' },
|
|
19
|
+
{ token: 'keyword', foreground: 'c586c0' },
|
|
20
|
+
{ token: 'keyword.operator', foreground: 'd4d4d4' },
|
|
21
|
+
{ token: 'string', foreground: 'ce9178' },
|
|
22
|
+
{ token: 'string.escape', foreground: 'd7ba7d' },
|
|
23
|
+
{ token: 'number', foreground: 'b5cea8' },
|
|
24
|
+
{ token: 'regexp', foreground: 'd16969' },
|
|
25
|
+
{ token: 'type', foreground: '4ec9b0' },
|
|
26
|
+
{ token: 'type.identifier', foreground: '4ec9b0' },
|
|
27
|
+
{ token: 'identifier', foreground: 'd4d4d4' },
|
|
28
|
+
{ token: 'variable', foreground: '9cdcfe' },
|
|
29
|
+
{ token: 'variable.predefined', foreground: '4fc1ff' },
|
|
30
|
+
{ token: 'variable.parameter', foreground: '00bfae' },
|
|
31
|
+
{ token: 'parameter', foreground: '00bfae' },
|
|
32
|
+
{ token: 'function', foreground: 'dcdcaa' },
|
|
33
|
+
{ token: 'method', foreground: 'dcdcaa' },
|
|
34
|
+
{ token: 'property', foreground: '9cdcfe' },
|
|
35
|
+
{ token: 'member', foreground: 'dcdcaa' },
|
|
36
|
+
{ token: 'constant', foreground: '4fc1ff' },
|
|
37
|
+
{ token: 'constant.numeric', foreground: 'b5cea8' },
|
|
38
|
+
{ token: 'constant.language', foreground: '569cd6' },
|
|
39
|
+
{ token: 'tag', foreground: '569cd6' },
|
|
40
|
+
{ token: 'attribute.name', foreground: '9cdcfe' },
|
|
41
|
+
{ token: 'attribute.value', foreground: 'ce9178' },
|
|
42
|
+
{ token: 'delimiter', foreground: 'd4d4d4' },
|
|
43
|
+
{ token: 'delimiter.bracket', foreground: 'ffd700' },
|
|
44
|
+
{ token: 'operator', foreground: 'd4d4d4' },
|
|
45
|
+
{ token: 'namespace', foreground: '4ec9b0' },
|
|
46
|
+
{ token: 'class', foreground: '4ec9b0' },
|
|
47
|
+
{ token: 'interface', foreground: '4ec9b0' },
|
|
48
|
+
{ token: 'enum', foreground: '4ec9b0' },
|
|
49
|
+
{ token: 'struct', foreground: '4ec9b0' },
|
|
50
|
+
{ token: 'annotation', foreground: 'dcdcaa' },
|
|
51
|
+
{ token: 'punctuation', foreground: 'd4d4d4' },
|
|
52
|
+
{ token: 'invalid', foreground: 'f44747' },
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
/** Syntax token rules (light). */
|
|
56
|
+
const LIGHT_RULES = [
|
|
57
|
+
{ token: '', foreground: '1e1e1e' },
|
|
58
|
+
{ token: 'comment', foreground: '008000', fontStyle: 'italic' },
|
|
59
|
+
{ token: 'keyword', foreground: '0000ff' },
|
|
60
|
+
{ token: 'keyword.operator', foreground: '1e1e1e' },
|
|
61
|
+
{ token: 'string', foreground: 'a31515' },
|
|
62
|
+
{ token: 'string.escape', foreground: 'ee0000' },
|
|
63
|
+
{ token: 'number', foreground: '098658' },
|
|
64
|
+
{ token: 'regexp', foreground: '811f3f' },
|
|
65
|
+
{ token: 'type', foreground: '267f99' },
|
|
66
|
+
{ token: 'type.identifier', foreground: '267f99' },
|
|
67
|
+
{ token: 'identifier', foreground: '1e1e1e' },
|
|
68
|
+
{ token: 'variable', foreground: '001080' },
|
|
69
|
+
{ token: 'variable.predefined', foreground: '001080' },
|
|
70
|
+
{ token: 'variable.parameter', foreground: '008577' },
|
|
71
|
+
{ token: 'parameter', foreground: '008577' },
|
|
72
|
+
{ token: 'function', foreground: '795e26' },
|
|
73
|
+
{ token: 'method', foreground: '795e26' },
|
|
74
|
+
{ token: 'property', foreground: '001080' },
|
|
75
|
+
{ token: 'member', foreground: '795e26' },
|
|
76
|
+
{ token: 'constant', foreground: '0070c1' },
|
|
77
|
+
{ token: 'constant.numeric', foreground: '098658' },
|
|
78
|
+
{ token: 'constant.language', foreground: '0000ff' },
|
|
79
|
+
{ token: 'tag', foreground: '800000' },
|
|
80
|
+
{ token: 'attribute.name', foreground: 'e50000' },
|
|
81
|
+
{ token: 'attribute.value', foreground: '0451a5' },
|
|
82
|
+
{ token: 'delimiter', foreground: '1e1e1e' },
|
|
83
|
+
{ token: 'delimiter.bracket', foreground: 'b46400' },
|
|
84
|
+
{ token: 'operator', foreground: '1e1e1e' },
|
|
85
|
+
{ token: 'namespace', foreground: '267f99' },
|
|
86
|
+
{ token: 'class', foreground: '267f99' },
|
|
87
|
+
{ token: 'interface', foreground: '267f99' },
|
|
88
|
+
{ token: 'enum', foreground: '267f99' },
|
|
89
|
+
{ token: 'struct', foreground: '267f99' },
|
|
90
|
+
{ token: 'annotation', foreground: '795e26' },
|
|
91
|
+
{ token: 'punctuation', foreground: '1e1e1e' },
|
|
92
|
+
{ token: 'invalid', foreground: 'cd3131' },
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Monaco built-in themes: the guaranteed background/foreground base. Custom
|
|
97
|
+
* theme ids below may only add syntax rules; their failure can never leave the
|
|
98
|
+
* editor stuck on the other palette.
|
|
99
|
+
*/
|
|
100
|
+
const MONACO_BASE = { dark: 'vs-dark', light: 'vs' }
|
|
101
|
+
|
|
102
|
+
/** Monaco custom theme ids owned by this plugin (syntax rules on top of the base). */
|
|
103
|
+
const MONACO_THEME = { dark: 'drp-dark', light: 'drp-light' }
|
|
104
|
+
|
|
105
|
+
/** Runtime theme state: the optional theme service plus the last resolution. */
|
|
106
|
+
const themeRuntime = { service: undefined, scheme: 'dark', source: 'initial', base: 'vs-dark' }
|
|
107
|
+
|
|
108
|
+
// The editor always follows the DSH theme; a manual override from an earlier
|
|
109
|
+
// build is ignored and cleared so no stale localStorage value can force the
|
|
110
|
+
// wrong palette (a light page with dark ink, or the reverse).
|
|
111
|
+
try { localStorage.removeItem('dsh-git-changes.scheme') } catch (error) { /* storage is optional */ }
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Read a CSS custom property from the DSH theme surface.
|
|
115
|
+
* @param {string} name token name
|
|
116
|
+
* @returns {string} trimmed value or ''
|
|
117
|
+
*/
|
|
118
|
+
function readCssVar(name) {
|
|
119
|
+
try {
|
|
120
|
+
for (const node of [document.body, document.documentElement]) {
|
|
121
|
+
if (!node) continue
|
|
122
|
+
const value = getComputedStyle(node).getPropertyValue(name)
|
|
123
|
+
if (value && value.trim() && !value.trim().startsWith('var(')) return value.trim()
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
/* tokens are optional */
|
|
127
|
+
}
|
|
128
|
+
return ''
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Parse a CSS color (`#rgb`, `#rrggbb`, `rgb()`/`rgba()`) into RGB channels.
|
|
133
|
+
* @param {string} value raw color
|
|
134
|
+
* @returns {{r:number,g:number,b:number} | undefined}
|
|
135
|
+
*/
|
|
136
|
+
function parseColor(value) {
|
|
137
|
+
const text = String(value || '').trim()
|
|
138
|
+
if (!text) return undefined
|
|
139
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(text)
|
|
140
|
+
if (hex) {
|
|
141
|
+
const digits = hex[1]
|
|
142
|
+
if (digits.length === 3) {
|
|
143
|
+
return {
|
|
144
|
+
r: parseInt(digits[0] + digits[0], 16),
|
|
145
|
+
g: parseInt(digits[1] + digits[1], 16),
|
|
146
|
+
b: parseInt(digits[2] + digits[2], 16),
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const number = parseInt(digits, 16)
|
|
150
|
+
return { r: (number >> 16) & 255, g: (number >> 8) & 255, b: number & 255 }
|
|
151
|
+
}
|
|
152
|
+
const rgb = /^rgba?\(\s*([\d.]+)\s*[, ]\s*([\d.]+)\s*[, ]\s*([\d.]+)/i.exec(text)
|
|
153
|
+
if (rgb) return { r: Number(rgb[1]), g: Number(rgb[2]), b: Number(rgb[3]) }
|
|
154
|
+
return undefined
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Perceived luminance (0–255) of a CSS color.
|
|
159
|
+
* @param {string} value raw color
|
|
160
|
+
* @returns {number | undefined}
|
|
161
|
+
*/
|
|
162
|
+
function luminanceOf(value) {
|
|
163
|
+
const color = parseColor(value)
|
|
164
|
+
if (!color) return undefined
|
|
165
|
+
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The scheme implied by the rendered page background, when readable.
|
|
170
|
+
* The live computed background is the most reliable signal after a theme switch
|
|
171
|
+
* because it already reflects the active DSH theme.
|
|
172
|
+
* @returns {'light'|'dark' | undefined}
|
|
173
|
+
*/
|
|
174
|
+
function schemeFromRenderedBackground() {
|
|
175
|
+
try {
|
|
176
|
+
const candidates = []
|
|
177
|
+
for (const node of [document.body, document.documentElement]) {
|
|
178
|
+
if (!node) continue
|
|
179
|
+
const style = getComputedStyle(node)
|
|
180
|
+
candidates.push(style.backgroundColor)
|
|
181
|
+
const token = style.getPropertyValue('--dsw-alias-bg-base')
|
|
182
|
+
if (token) candidates.push(token.trim())
|
|
183
|
+
}
|
|
184
|
+
for (const value of candidates) {
|
|
185
|
+
const luminance = luminanceOf(value)
|
|
186
|
+
if (luminance === undefined) continue
|
|
187
|
+
if (luminance <= 4) continue // fully transparent body: not informative
|
|
188
|
+
return luminance < 128 ? 'dark' : 'light'
|
|
189
|
+
}
|
|
190
|
+
} catch (error) {
|
|
191
|
+
/* fall through */
|
|
192
|
+
}
|
|
193
|
+
return undefined
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Resolve the active color scheme.
|
|
198
|
+
*
|
|
199
|
+
* Priority is the **rendered DSH surface** first (the `--dsw-alias-bg-base`
|
|
200
|
+
* token / computed background): it already reflects the theme the user sees, so
|
|
201
|
+
* it survives a live switch in either direction even when the optional theme
|
|
202
|
+
* service snapshot or a DOM marker is stale. The official marker and the theme
|
|
203
|
+
* service are fallbacks for the short moment before CSS variables update.
|
|
204
|
+
* @returns {'light'|'dark'}
|
|
205
|
+
*/
|
|
206
|
+
function detectScheme() {
|
|
207
|
+
const rendered = schemeFromRenderedBackground()
|
|
208
|
+
if (rendered !== undefined) {
|
|
209
|
+
themeRuntime.source = 'rendered-background'
|
|
210
|
+
return rendered
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
const marked = document.body && document.body.getAttribute('data-ds-dark-theme')
|
|
214
|
+
if (marked !== null && marked !== undefined && marked !== 'false') {
|
|
215
|
+
themeRuntime.source = 'dom-marker'
|
|
216
|
+
return 'dark'
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
/* fall through */
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const snapshot = typeof themeRuntime.service?.getTheme === 'function' ? themeRuntime.service.getTheme() : undefined
|
|
223
|
+
const colorScheme = snapshot && snapshot.active && snapshot.active.colorScheme
|
|
224
|
+
if (colorScheme === 'light' || colorScheme === 'dark') {
|
|
225
|
+
themeRuntime.source = 'theme-service'
|
|
226
|
+
return colorScheme
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
/* fall through */
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
const attr = String(
|
|
233
|
+
(document.documentElement && document.documentElement.getAttribute('data-theme')) ||
|
|
234
|
+
(document.body && document.body.getAttribute('data-theme')) || '',
|
|
235
|
+
).toLowerCase()
|
|
236
|
+
if (attr.includes('light') || attr.includes('latte')) {
|
|
237
|
+
themeRuntime.source = 'data-theme'
|
|
238
|
+
return 'light'
|
|
239
|
+
}
|
|
240
|
+
if (attr.includes('dark') || attr.includes('mocha') || attr.includes('frappe') || attr.includes('macchiato')) {
|
|
241
|
+
themeRuntime.source = 'data-theme'
|
|
242
|
+
return 'dark'
|
|
243
|
+
}
|
|
244
|
+
} catch (error) {
|
|
245
|
+
/* fall through */
|
|
246
|
+
}
|
|
247
|
+
try {
|
|
248
|
+
if (typeof window.matchMedia === 'function') {
|
|
249
|
+
themeRuntime.source = 'prefers-color-scheme'
|
|
250
|
+
return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'
|
|
251
|
+
}
|
|
252
|
+
} catch (error) {
|
|
253
|
+
/* fall through */
|
|
254
|
+
}
|
|
255
|
+
themeRuntime.source = 'default'
|
|
256
|
+
return 'dark'
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Diff colors for one scheme, as Monaco color keys.
|
|
261
|
+
* @param {'light'|'dark'} scheme scheme
|
|
262
|
+
* @returns {Record<string,string>}
|
|
263
|
+
*/
|
|
264
|
+
function diffColorsOf(scheme) {
|
|
265
|
+
const [added, removed, addText, delText] = DIFF_PALETTE[scheme === 'light' ? 'light' : 'dark']
|
|
266
|
+
return {
|
|
267
|
+
'diffEditor.insertedLineBackground': added,
|
|
268
|
+
'diffEditor.removedLineBackground': removed,
|
|
269
|
+
'diffEditor.insertedTextBackground': addText,
|
|
270
|
+
'diffEditor.removedTextBackground': delText,
|
|
271
|
+
'diffEditorGutter.insertedLineBackground': added,
|
|
272
|
+
'diffEditorGutter.removedLineBackground': removed,
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Read one CSS token only when its luminance fits the target scheme (background
|
|
278
|
+
* tokens must be dark in the dark scheme and light in the light scheme);
|
|
279
|
+
* otherwise the literal fallback wins. This is what keeps the Monaco theme from
|
|
280
|
+
* inheriting stale light-theme token values after a live theme switch.
|
|
281
|
+
* @param {string} name token name
|
|
282
|
+
* @param {'light'|'dark'} scheme target scheme
|
|
283
|
+
* @param {number | undefined} expectedLight whether the token must be light
|
|
284
|
+
* @param {string} fallback fallback color
|
|
285
|
+
* @returns {string}
|
|
286
|
+
*/
|
|
287
|
+
function schemeToken(name, scheme, expectedLight, fallback) {
|
|
288
|
+
const raw = readCssVar(name)
|
|
289
|
+
if (!raw) return fallback
|
|
290
|
+
if (expectedLight === undefined) return raw
|
|
291
|
+
const luminance = luminanceOf(raw)
|
|
292
|
+
if (luminance === undefined) return raw
|
|
293
|
+
return (luminance >= 128) === expectedLight ? raw : fallback
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Editor chrome colors for one scheme, matching VS Code 1.131 Dark/Light Modern
|
|
298
|
+
* (the reference the file tree and tab bar are copied from) while keeping the
|
|
299
|
+
* diff tints from the plugin palette.
|
|
300
|
+
* @param {'light'|'dark'} scheme scheme
|
|
301
|
+
* @returns {Record<string,string>}
|
|
302
|
+
*/
|
|
303
|
+
function editorColorsOf(scheme) {
|
|
304
|
+
const light = scheme === 'light'
|
|
305
|
+
return {
|
|
306
|
+
'editor.background': light ? '#ffffff' : '#1f1f1f',
|
|
307
|
+
'editor.foreground': light ? '#3b3b3b' : '#cccccc',
|
|
308
|
+
'editorLineNumber.foreground': '#6e7681',
|
|
309
|
+
'editorLineNumber.activeForeground': light ? '#3b3b3b' : '#cccccc',
|
|
310
|
+
'editorCursor.foreground': light ? '#005fb8' : '#aeafad',
|
|
311
|
+
// Higher-contrast selection (Cursor-style blue) so a selection remains
|
|
312
|
+
// obvious on top of the green/red git-changes tints.
|
|
313
|
+
'editor.selectionBackground': light ? '#9fc9f5' : '#2f5c8f',
|
|
314
|
+
'editor.inactiveSelectionBackground': light ? '#d7e4f2' : '#3a4a5f',
|
|
315
|
+
'editor.selectionHighlightBackground': light ? '#add6ff66' : '#264f7866',
|
|
316
|
+
'editor.lineHighlightBackground': light ? '#00000000' : '#ffffff00',
|
|
317
|
+
'editorIndentGuide.background1': light ? '#d3d3d3' : '#404040',
|
|
318
|
+
'editorIndentGuide.activeBackground1': light ? '#939393' : '#707070',
|
|
319
|
+
'editorWidget.background': light ? '#f8f8f8' : '#202020',
|
|
320
|
+
'editorWidget.border': light ? '#e5e5e5' : '#454545',
|
|
321
|
+
'editorGutter.background': light ? '#ffffff' : '#1f1f1f',
|
|
322
|
+
'minimap.background': light ? '#ffffff' : '#1f1f1f',
|
|
323
|
+
'scrollbarSlider.background': light ? '#64646466' : '#79797966',
|
|
324
|
+
'scrollbarSlider.hoverBackground': light ? '#646464b3' : '#646464b3',
|
|
325
|
+
'scrollbarSlider.activeBackground': light ? '#a6a6a666' : '#bfbfbf66',
|
|
326
|
+
'editorBracketMatch.background': light ? '#0064001a' : '#0064001a',
|
|
327
|
+
'editorBracketMatch.border': light ? '#005fb8' : '#0078d4',
|
|
328
|
+
...diffColorsOf(scheme),
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Register the two plugin themes (idempotent).
|
|
334
|
+
*
|
|
335
|
+
* The custom theme carries the VS Code 1.131 Dark/Light Modern editor chrome
|
|
336
|
+
* colors plus the plugin syntax rules. The base theme is still applied first in
|
|
337
|
+
* {@link applyMonacoTheme}, so a failed defineTheme can never leave the editor on
|
|
338
|
+
* the previous palette.
|
|
339
|
+
* @param {any} monaco Monaco namespace
|
|
340
|
+
*/
|
|
341
|
+
function registerMonacoThemes(monaco) {
|
|
342
|
+
try {
|
|
343
|
+
if (!monaco || !monaco.editor || typeof monaco.editor.defineTheme !== 'function') return
|
|
344
|
+
monaco.editor.defineTheme(MONACO_THEME.dark, {
|
|
345
|
+
base: MONACO_BASE.dark,
|
|
346
|
+
inherit: true,
|
|
347
|
+
rules: DARK_RULES,
|
|
348
|
+
colors: editorColorsOf('dark'),
|
|
349
|
+
})
|
|
350
|
+
monaco.editor.defineTheme(MONACO_THEME.light, {
|
|
351
|
+
base: MONACO_BASE.light,
|
|
352
|
+
inherit: true,
|
|
353
|
+
rules: LIGHT_RULES,
|
|
354
|
+
colors: editorColorsOf('light'),
|
|
355
|
+
})
|
|
356
|
+
} catch (error) {
|
|
357
|
+
log('warn', 'monaco theme registration failed', error)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Apply the theme matching the current DSH scheme.
|
|
363
|
+
*
|
|
364
|
+
* The built-in base theme is applied FIRST and unconditionally, so the editor
|
|
365
|
+
* background/foreground always follow the light/dark switch even if the custom
|
|
366
|
+
* syntax theme cannot be defined in this Monaco build. The custom theme (syntax
|
|
367
|
+
* rules on top of that base) is then applied best-effort with a base fallback.
|
|
368
|
+
* @param {any} monaco Monaco namespace
|
|
369
|
+
* @returns {'light'|'dark'} the applied scheme
|
|
370
|
+
*/
|
|
371
|
+
function applyMonacoTheme(monaco) {
|
|
372
|
+
const scheme = detectScheme()
|
|
373
|
+
themeRuntime.scheme = scheme
|
|
374
|
+
const base = scheme === 'light' ? MONACO_BASE.light : MONACO_BASE.dark
|
|
375
|
+
const custom = scheme === 'light' ? MONACO_THEME.light : MONACO_THEME.dark
|
|
376
|
+
themeRuntime.base = base
|
|
377
|
+
try {
|
|
378
|
+
if (monaco && monaco.editor && typeof monaco.editor.setTheme === 'function') {
|
|
379
|
+
monaco.editor.setTheme(base)
|
|
380
|
+
try {
|
|
381
|
+
registerMonacoThemes(monaco)
|
|
382
|
+
monaco.editor.setTheme(custom)
|
|
383
|
+
} catch (error) {
|
|
384
|
+
// Any custom-theme problem keeps the base palette (correct background).
|
|
385
|
+
log('warn', 'monaco custom theme skipped', error)
|
|
386
|
+
try { monaco.editor.setTheme(base) } catch (fallbackError) { /* keep base */ }
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
/* no Monaco yet: the next render applies it */
|
|
391
|
+
}
|
|
392
|
+
return scheme
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Diff CSS variables for the current Monaco theme (used by view-zone DOM nodes).
|
|
397
|
+
* @param {'light'|'dark'} scheme scheme
|
|
398
|
+
* @returns {{add:string,del:string,fg:string,number:string}}
|
|
399
|
+
*/
|
|
400
|
+
function diffCssVarsOf(scheme) {
|
|
401
|
+
const palette = DIFF_PALETTE[scheme === 'light' ? 'light' : 'dark']
|
|
402
|
+
return {
|
|
403
|
+
add: palette[0],
|
|
404
|
+
del: palette[1],
|
|
405
|
+
addWord: palette[2],
|
|
406
|
+
delWord: palette[3],
|
|
407
|
+
fg: themeRuntime.scheme === 'light' ? '#1e1e1e' : '#d4d4d4',
|
|
408
|
+
number: themeRuntime.scheme === 'light' ? '#8c8c8c' : '#6e7681',
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Subscribe to DSH theme changes.
|
|
414
|
+
* @param {object} ctx client context
|
|
415
|
+
* @param {() => void} onChange called after the runtime scheme may have changed
|
|
416
|
+
* @returns {() => void} disposer
|
|
417
|
+
*/
|
|
418
|
+
function observeTheme(ctx, onChange) {
|
|
419
|
+
const disposers = []
|
|
420
|
+
const check = () => {
|
|
421
|
+
try {
|
|
422
|
+
onChange()
|
|
423
|
+
} catch (error) {
|
|
424
|
+
/* a theme repaint failure must never break the page */
|
|
425
|
+
}
|
|
426
|
+
// Theme CSS applies a tick after the preference event/mutation in some
|
|
427
|
+
// paths: re-check once shortly after so light↔dark converges in both
|
|
428
|
+
// directions even if the first read saw the previous surface.
|
|
429
|
+
try {
|
|
430
|
+
const retry = setTimeout(() => {
|
|
431
|
+
try { onChange() } catch (error) { /* ignore */ }
|
|
432
|
+
}, 120)
|
|
433
|
+
disposers.push(() => clearTimeout(retry))
|
|
434
|
+
} catch (error) {
|
|
435
|
+
/* ignore */
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
const dispose = ctx.on('theme/change', check)
|
|
440
|
+
if (typeof dispose === 'function') disposers.push(dispose)
|
|
441
|
+
} catch (error) {
|
|
442
|
+
/* optional event */
|
|
443
|
+
}
|
|
444
|
+
try {
|
|
445
|
+
if (typeof MutationObserver === 'function' && typeof document !== 'undefined') {
|
|
446
|
+
const observer = new MutationObserver(check)
|
|
447
|
+
for (const node of [document.body, document.documentElement]) {
|
|
448
|
+
if (node) observer.observe(node, { attributes: true, attributeFilter: ['data-ds-dark-theme', 'data-theme', 'class', 'style'] })
|
|
449
|
+
}
|
|
450
|
+
// Style-element swaps (theme stylesheets) mutate head, not the root attrs.
|
|
451
|
+
if (document.head) observer.observe(document.head, { childList: true })
|
|
452
|
+
disposers.push(() => observer.disconnect())
|
|
453
|
+
}
|
|
454
|
+
} catch (error) {
|
|
455
|
+
/* optional observer */
|
|
456
|
+
}
|
|
457
|
+
try {
|
|
458
|
+
if (typeof window.matchMedia === 'function') {
|
|
459
|
+
const media = window.matchMedia('(prefers-color-scheme: dark)')
|
|
460
|
+
if (media && typeof media.addEventListener === 'function') {
|
|
461
|
+
media.addEventListener('change', check)
|
|
462
|
+
disposers.push(() => media.removeEventListener('change', check))
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
} catch (error) {
|
|
466
|
+
/* optional media query */
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const onFocus = () => check()
|
|
470
|
+
window.addEventListener('focus', onFocus)
|
|
471
|
+
document.addEventListener('visibilitychange', onFocus)
|
|
472
|
+
disposers.push(() => {
|
|
473
|
+
window.removeEventListener('focus', onFocus)
|
|
474
|
+
document.removeEventListener('visibilitychange', onFocus)
|
|
475
|
+
})
|
|
476
|
+
} catch (error) {
|
|
477
|
+
/* optional listeners */
|
|
478
|
+
}
|
|
479
|
+
// Low-frequency safety net: the rendered surface is the source of truth, so a
|
|
480
|
+
// missed event cannot leave the editor stuck on the other theme.
|
|
481
|
+
try {
|
|
482
|
+
const interval = setInterval(() => {
|
|
483
|
+
try {
|
|
484
|
+
if (detectScheme() !== themeRuntime.scheme) onChange()
|
|
485
|
+
} catch (error) {
|
|
486
|
+
/* ignore */
|
|
487
|
+
}
|
|
488
|
+
}, 1200)
|
|
489
|
+
disposers.push(() => clearInterval(interval))
|
|
490
|
+
} catch (error) {
|
|
491
|
+
/* ignore */
|
|
492
|
+
}
|
|
493
|
+
// One delayed re-check catches themes applied right after this page mounted.
|
|
494
|
+
try {
|
|
495
|
+
const timer = setTimeout(check, 600)
|
|
496
|
+
disposers.push(() => clearTimeout(timer))
|
|
497
|
+
} catch (error) {
|
|
498
|
+
/* ignore */
|
|
499
|
+
}
|
|
500
|
+
return () => {
|
|
501
|
+
for (const dispose of disposers) {
|
|
502
|
+
try {
|
|
503
|
+
dispose()
|
|
504
|
+
} catch (error) {
|
|
505
|
+
/* ignore */
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|