Skip to content

Commit 386bf83

Browse files
committed
feat: unwatch config change by defauilt, reduce js file overwrite
1 parent 9a3e5f8 commit 386bf83

File tree

9 files changed

+109
-64
lines changed

9 files changed

+109
-64
lines changed

README.md

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,30 @@ VSCode extension that custom ui css style in both editor and webview
1515

1616
<!-- configs -->
1717

18-
| Key | Description | Type | Default |
19-
| ------------------------------------------ | ------------------------------------------------------------------- | -------- | ---------- |
20-
| `custom-ui-style.monospace` | Global monospace font family that apply in both editor and webview | `string` | `` |
21-
| `custom-ui-style.sansSerif` | Global sans-serif font family that apply in both editor and webview | `string` | `` |
22-
| `custom-ui-style.backgroundUrl` | | `string` | `` |
23-
| `custom-ui-style.backgroundOpacity` | Background image opacity | `number` | `0.9` |
24-
| `custom-ui-style.backgroundSize` | Background image size | `string` | `"cover"` |
25-
| `custom-ui-style.backgroundPosition` | Background image size | `string` | `"center"` |
26-
| `custom-ui-style.stylesheet` | Custom css for editor, support nest selectors | `object` | `{}` |
27-
| `custom-ui-style.webviewMonospaceSelector` | Custom monospace selector in webview | `array` | `` |
28-
| `custom-ui-style.webviewSansSerifSelector` | Custom sans-serif selector in webview | `array` | `` |
29-
| `custom-ui-style.webviewStylesheet` | Custom css for webview, support nest selectors | `object` | `{}` |
18+
| Key | Description | Type | Default |
19+
| --------------------------------------------- | ---------------------------------------------------------------------------------- | --------- | ---------- |
20+
| `custom-ui-style.monospace` | Global monospace font family that apply in both editor and webview | `string` | `` |
21+
| `custom-ui-style.sansSerif` | Global sans-serif font family that apply in both editor and webview | `string` | `` |
22+
| `custom-ui-style.backgroundUrl` | Full-screen background image url, support protocol: 'https://', 'file://', 'data:' | `string` | `` |
23+
| `custom-ui-style.backgroundOpacity` | Background image opacity | `number` | `0.9` |
24+
| `custom-ui-style.backgroundSize` | Background image size | `string` | `"cover"` |
25+
| `custom-ui-style.backgroundPosition` | Background image size | `string` | `"center"` |
26+
| `custom-ui-style.stylesheet` | Custom css for editor, support nest selectors | `object` | `{}` |
27+
| `custom-ui-style.webviewMonospaceSelector` | Custom monospace selector in webview | `array` | `` |
28+
| `custom-ui-style.webviewSansSerifSelector` | Custom sans-serif selector in webview | `array` | `` |
29+
| `custom-ui-style.webviewStylesheet` | Custom css for webview, support nest selectors | `object` | `{}` |
30+
| `custom-ui-style.applyOnConfigurationChanged` | Whether to apply styles when configuration changed | `boolean` | `false` |
3031

3132
<!-- configs -->
3233

3334
## Commands
3435

3536
<!-- commands -->
3637

37-
| Command | Title |
38-
| -------------------------- | ---------------------------------- |
39-
| `custom-ui-style.reload` | Custom UI Style: Load custom style |
40-
| `custom-ui-style.rollback` | Custom UI Style: Rollback |
38+
| Command | Title |
39+
| -------------------------- | ------------------------- |
40+
| `custom-ui-style.reload` | Custom UI Style: Reload |
41+
| `custom-ui-style.rollback` | Custom UI Style: Rollback |
4142

4243
<!-- commands -->
4344

package.json

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,13 @@
4040
"commands": [
4141
{
4242
"command": "custom-ui-style.reload",
43-
"title": "Custom UI Style: Load custom style"
43+
"title": "Reload",
44+
"category": "Custom UI Style"
4445
},
4546
{
4647
"command": "custom-ui-style.rollback",
47-
"title": "Custom UI Style: Rollback"
48+
"title": "Rollback",
49+
"category": "Custom UI Style"
4850
}
4951
],
5052
"configuration": {
@@ -64,7 +66,7 @@
6466
"custom-ui-style.backgroundUrl": {
6567
"scope": "machine",
6668
"type": "string",
67-
"markdownDescription": "Full-screen background image url, support protocol: `https://`, `file://`, `data:`"
69+
"description": "Full-screen background image url, support protocol: 'https://', 'file://', 'data:'"
6870
},
6971
"custom-ui-style.backgroundOpacity": {
7072
"scope": "application",
@@ -113,6 +115,12 @@
113115
"scope": "application",
114116
"type": "object",
115117
"description": "Custom css for webview, support nest selectors"
118+
},
119+
"custom-ui-style.applyOnConfigurationChanged": {
120+
"scope": "resource",
121+
"type": "boolean",
122+
"default": false,
123+
"description": "Whether to apply styles when configuration changed"
116124
}
117125
}
118126
}

src/index.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,49 @@ import { defineExtension, useCommand, watch } from 'reactive-vscode'
22
import { config, editorConfig } from './config'
33
import * as Meta from './generated/meta'
44
import { createFileManagers } from './manager'
5+
import { debounce, showMessage } from './utils'
56

67
const { activate, deactivate } = defineExtension(() => {
78
const { reload, rollback } = createFileManagers()
9+
810
useCommand(Meta.commands.reload, () => {
911
reload('UI style changed')
1012
})
1113
useCommand(Meta.commands.rollback, () => {
1214
rollback('UI style rollback')
1315
})
1416

15-
watch(
16-
() => editorConfig.fontFamily,
17-
() => !config.monospace && reload('Configuration changed, reload'),
18-
)
19-
watch(
20-
config,
21-
() => reload('Configuration changed, reload'),
17+
const watchAndReload = debounce(
18+
(fontChanged: boolean) => showMessage('Configuration changed, apply?', 'Apply', 'Cancel')
19+
.then<any>(item => item === 'Apply' && reload('UI style changed', fontChanged)),
20+
1500,
2221
)
22+
const startWatch = () => {
23+
const cleanup1 = watch(
24+
() => editorConfig.fontFamily,
25+
() => !config.monospace && watchAndReload(true),
26+
)
27+
const cleanup2 = watch(
28+
config,
29+
(conf, oldConf) => watchAndReload(
30+
conf.monospace !== oldConf.monospace || conf.sansSerif !== oldConf.sansSerif,
31+
),
32+
)
33+
return () => {
34+
cleanup1()
35+
cleanup2()
36+
}
37+
}
38+
39+
let cleanup = () => {}
40+
41+
watch(() => config.applyOnConfigurationChanged, (is) => {
42+
if (is) {
43+
cleanup = startWatch()
44+
} else {
45+
cleanup()
46+
}
47+
})
2348
})
2449

2550
export { activate, deactivate }

src/manager/base.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { logger } from '../utils'
55

66
export interface FileManager {
77
hasBakFile: boolean
8-
reload: () => Promise<void>
8+
reload: (fontChanged: boolean) => Promise<void>
99
rollback: () => Promise<void>
1010
}
1111

@@ -24,12 +24,15 @@ export abstract class BaseFileManager implements FileManager {
2424
return existsSync(this.bakPath)
2525
}
2626

27-
async reload() {
27+
async reload(fontChanged: boolean) {
2828
if (!this.hasBakFile) {
2929
logger.warn(`Backup file [${this.bakPath}] does not exist, skip reload`)
3030
} else {
31-
writeFileSync(this.srcPath, await this.patch(readFileSync(this.bakPath, 'utf-8')))
32-
logger.info(`Config reload [${this.srcPath}]`)
31+
const newContent = await this.patch(fontChanged, () => readFileSync(this.bakPath, 'utf-8'))
32+
if (newContent) {
33+
writeFileSync(this.srcPath, newContent)
34+
logger.info(`Config reload [${this.srcPath}]`)
35+
}
3336
}
3437
}
3538

@@ -43,5 +46,5 @@ export abstract class BaseFileManager implements FileManager {
4346
}
4447
}
4548

46-
abstract patch(content: string): Promisable<string>
49+
abstract patch(fontChanged: boolean, content: () => string): Promisable<string | undefined>
4750
}

src/manager/css.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import type { Promisable } from '@subframe7536/type-utils'
2-
import type { ConfigShorthandMap } from '../generated/meta'
32
import { config, getFamilies } from '../config'
43
import { normalizeUrl } from '../path'
5-
import { captialize, generateStyleFromObject } from '../utils'
4+
import { generateStyleFromObject } from '../utils'
65
import { BaseFileManager } from './base'
76
import { VSC_DFAULT_SANS_FONT, VSC_NOTEBOOK_MONO_FONT } from './js'
87

@@ -14,7 +13,8 @@ function generateBackgroundCSS() {
1413
if (!url) {
1514
return ''
1615
}
17-
return `body {
16+
return `
17+
body {
1818
background-size: ${config.backgroundSize};
1919
background-repeat: no-repeat;
2020
background-attachment: fixed; // for code-server
@@ -28,32 +28,34 @@ function generateFontCSS() {
2828
let result = ''
2929
const { monospace, sansSerif } = getFamilies()
3030
if (monospace) {
31-
result += `span.monaco-keybinding-key, .setting-list-row {
31+
result += `
32+
span.monaco-keybinding-key, .setting-list-row {
3233
font-family: ${monospace}, ${VSC_NOTEBOOK_MONO_FONT} !important;
3334
}
3435
.windows,
3536
.mac,
3637
.linux {
37-
--monaco-monospace-font: ${monospace}, ${VSC_NOTEBOOK_MONO_FONT} !important;";
38+
--monaco-monospace-font: ${monospace}, ${VSC_NOTEBOOK_MONO_FONT} !important;
3839
}`
3940
}
4041
if (sansSerif) {
41-
result += `.windows {
42-
font-family: ${sansSerif}, ${VSC_DFAULT_SANS_FONT.win} !important;";
42+
result += `
43+
.windows {
44+
font-family: ${sansSerif}, ${VSC_DFAULT_SANS_FONT.win} !important;
4345
}
4446
.mac {
45-
font-family: ${sansSerif}, ${VSC_DFAULT_SANS_FONT.mac} !important;";
47+
font-family: ${sansSerif}, ${VSC_DFAULT_SANS_FONT.mac} !important;
4648
}
4749
.linux {
48-
font-family: ${sansSerif}, ${VSC_DFAULT_SANS_FONT.linux} !important;";
50+
font-family: ${sansSerif}, ${VSC_DFAULT_SANS_FONT.linux} !important;
4951
}`
5052
}
5153
return result
5254
}
5355

5456
export class CssFileManager extends BaseFileManager {
55-
patch(content: string): Promisable<string> {
56-
return `${content}
57+
patch(_fontChanged: boolean, content: () => string): Promisable<string | undefined> {
58+
return `${content()}
5759
${banner}
5860
${generateBackgroundCSS()}
5961
${generateFontCSS()}

src/manager/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export function createFileManagers() {
1212
new WebViewFileManager(webviewHTMLPath, webviewHTMLBakPath),
1313
]
1414
return {
15-
reload: (text: string) => runAndRestart(
15+
reload: (text: string, fontChanged = true) => runAndRestart(
1616
text,
17-
() => Promise.all(managers.map(m => m.reload())),
17+
() => Promise.all(managers.map(m => m.reload(fontChanged))),
1818
),
1919
rollback: (text: string) => runAndRestart(
2020
text,

src/manager/js.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,27 @@ export const VSC_DFAULT_SANS_FONT = {
1717
export const VSC_NOTEBOOK_MONO_FONT = `"SF Mono", Monaco, Menlo, Consolas, "Ubuntu Mono", "Liberation Mono", "DejaVu Sans Mono", "Courier New", monospace`
1818

1919
export class JsFileManager extends BaseFileManager {
20-
patch(content: string): Promisable<string> {
20+
patch(fontChanged: boolean, content: () => string): Promisable<string | undefined> {
21+
if (!fontChanged) {
22+
return undefined
23+
}
24+
let _content = content()
2125
let { monospace, sansSerif } = getFamilies()
2226
if (monospace) {
2327
monospace = escapeQuote(monospace)
24-
content = content
28+
_content = _content
2529
.replaceAll(VSC_DFAULT_MONO_FONT.win, monospace)
2630
.replaceAll(VSC_DFAULT_MONO_FONT.mac, monospace)
2731
.replaceAll(VSC_DFAULT_MONO_FONT.linux, monospace)
2832
.replaceAll(VSC_NOTEBOOK_MONO_FONT, monospace)
2933
}
3034
if (sansSerif) {
3135
sansSerif = escapeQuote(sansSerif)
32-
content = content
36+
_content = _content
3337
.replaceAll(VSC_DFAULT_SANS_FONT.win, sansSerif)
3438
.replaceAll(VSC_DFAULT_SANS_FONT.mac, sansSerif)
3539
.replaceAll(VSC_DFAULT_SANS_FONT.linux, sansSerif)
3640
}
37-
return content
41+
return _content
3842
}
3943
}

src/manager/webview.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ export function fixSha256(html: string) {
4242
}
4343

4444
export class WebViewFileManager extends BaseFileManager {
45-
patch(content: string): Promisable<string> {
45+
patch(_fontChanged: boolean, content: () => string): Promisable<string | undefined> {
4646
return fixSha256(
47-
content.replace(
47+
content().replace(
4848
entry,
4949
`${entry}.replace('</body>', '</body><style>${getCSS()}</style>')`,
5050
),

src/utils.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Promisable } from '@subframe7536/type-utils'
1+
import type { AnyFunction, Promisable } from '@subframe7536/type-utils'
22
import { existsSync, rmSync } from 'node:fs'
33
import { readFileSync, writeFileSync } from 'atomically'
44
import { useLogger } from 'reactive-vscode'
@@ -9,7 +9,7 @@ export const logger = useLogger(displayName)
99

1010
const lockFileName = `__${name}__.lock`
1111

12-
async function runWithLock(fn: () => Promisable<void>) {
12+
export async function runAndRestart(message: string, action: () => Promise<any>) {
1313
let count = 5
1414
const check = () => existsSync(lockFileName)
1515
while (check() && count--) {
@@ -26,23 +26,21 @@ async function runWithLock(fn: () => Promisable<void>) {
2626
}
2727
writeFileSync(lockFileName, String(Date.now()))
2828
try {
29-
await fn()
30-
} finally {
31-
rmSync(lockFileName)
32-
}
33-
}
34-
export async function runAndRestart(message: string, action: () => Promise<any>) {
35-
await runWithLock(async () => {
3629
await action()
3730
const item = await window.showInformationMessage(message, { title: 'Restart vscode' })
3831
if (item) {
3932
commands.executeCommand('workbench.action.reloadWindow')
4033
}
41-
})
34+
} finally {
35+
rmSync(lockFileName)
36+
}
4237
}
4338

44-
export async function showMessage(content: string) {
45-
await window.showInformationMessage(content)
39+
export async function showMessage<T extends string[]>(
40+
content: string,
41+
...buttons: T
42+
): Promise<T[number] | undefined> {
43+
return await window.showInformationMessage(content, ...buttons)
4644
}
4745

4846
export function escapeQuote(str: string) {
@@ -51,8 +49,12 @@ export function escapeQuote(str: string) {
5149
.replaceAll(`"`, `\\"`)
5250
}
5351

54-
export function captialize(text: string) {
55-
return text[0].toUpperCase() + text.substring(1)
52+
export function debounce<T extends AnyFunction<void>>(fn: T, delay: number): T {
53+
let timer: NodeJS.Timeout
54+
return ((...args: any[]) => {
55+
clearTimeout(timer)
56+
timer = setTimeout(() => fn(...args), delay)
57+
}) as T
5658
}
5759

5860
export function generateStyleFromObject(obj: Record<string, any>) {

0 commit comments

Comments
 (0)