vscode-neon-glow
English · 한국어
Neon glow for VS Code syntax highlighting — for any colour theme.
It ships no theme of its own. It reads whatever colours your current theme produces
and makes the vivid ones glow, so you can keep changing themes and the glow follows
along — no reload needed.

The same file at the same moment, without the glow on the left and with it on the
right — the token colours, the matching bracket box, every other place the symbol
under the caret appears, and error, warning and info squiggles:

caretArc and caretArcOnDrag are both on in that clip, and both ship off.
Which tokens glow · What else can glow · Install · Settings · Things that will bite you · Status
Which tokens glow
Strength is driven by chroma (max(r,g,b) - min(r,g,b)), not HSL saturation —
saturation divides by 1 - |2L-1|, which explodes for near-white colours and would
let body text glow. Measured on Monokai:
| Colour |
Role |
Chroma |
Strength |
#F92672 |
keyword |
0.827 |
1.00 |
#FD971F |
parameter |
0.871 |
1.00 |
#A6E22E |
function name |
0.706 |
0.89 |
#66D9EF |
type / class |
0.537 |
0.68 |
#AE81FF |
number |
0.494 |
0.63 |
#E6DB74 |
string |
0.447 |
0.58 |
#F8F8F2 |
plain text |
0.024 |
— (skipped) |
#88846F |
comment |
0.098 |
— (skipped) |
Because it infers role from colour, themes that reuse one colour across different
roles can't be told apart. Themes with distinct per-role colours (Monokai, Tokyo
Night, Dracula, …) work well.
Flatter themes need different numbers. The defaults are calibrated on Monokai,
which is unusually saturated. In Abyss the class-name colour #ffeebb never reaches
the 0.30 threshold while the comment colour #384887 clears it, so the roles that
glow come out close to inverted. Dropping minChroma to about 0.25 and raising
brightness past 1 gets it back.
Bracket pair colouring is left to VS Code: brackets get a rule of their own in
currentColor, so each nesting level glows in the colour it is painted.
What else can glow
The token glow is not the only thing on. Nine more surfaces light with it, and four
effects wait to be asked for. The line between them is one sentence — a setting
that only decides what colour lands where is on, and a setting that changes what the
editor does while you work is off.
Everything in the first group is the theme's own colour carried somewhere it was not
carried before, so none of it puts a palette on your screen the theme had not already
chosen.
| On by default |
|
Find results (findGlow) |
matches stand out without hunting the scrollbar for them |
The selection (selectionGlow) |
the text you selected |
The symbol under the caret (occurrenceGlow) |
the word under the caret and every other place it appears |
The gutter's change bars (gutterGlow) |
the added, modified and deleted marks from source control |
The matching bracket box (bracketMatchGlow) |
the box around a bracket and its partner |
Error, warning and info squiggles (squiggleGlow) |
the wavy line lights, the word above it does not |
What changed in a diff (diffGlow) |
the changed words, not the whole line |
The line the editor points at (lineHighlightGlow) |
the debugger's stopped line, a jump, a peek |
Breakpoints (breakpointGlow) |
in whatever colour they are painted |
A snippet's tabstops (snippetGlow) |
the places a snippet is waiting for you to fill in |
The rename box (renameGlow) |
the box F2 opens |
Coloured terminal text (terminalGlow) |
the panel's own ANSI colours, while terminal.integrated.gpuAcceleration is off |
Find, selection, occurrence and diff colours are semi-transparent, because they sit behind
text and must not hide it, so those rules take a spread — the weak colour is carried
outwards at full width before the blur starts. The gutter bars take one for a
different reason: the box their glow hangs on is width: 0, and a shadow of a box
with no area paints nothing whatever the blur. The caret and the bracket box, whose
colours are opaque, need none. The squiggles take no box-shadow at all: the wave is an SVG
image, not a box, so they use a drop-shadow filter, which follows the drawn shape. This
is why the rules do not match each other, and the reasoning is kept beside each one in
neon-glow.js.
| Off until you set it |
|
The caret trail (cursorTrail) |
the caret slides instead of jumping and leaves a streak; 45 keeps up with typing |
The save jolt (saveShake) |
the window knocks sideways when you save; leave it off with auto save |
A breath on what is waiting for you (breathe) |
a slow pulse on the find match, the stopped line, the bracket box, snippet tabstops and the rename box |
The caret arc (caretArc) |
one of twelve shapes drawn along a caret jump, in the theme's caret colour or one you pick |
These four move things, and nobody asked for movement, so they ship at 0 and stay
there until you say otherwise. All four stand still when the system or VS Code's
workbench.reduceMotion asks for reduced motion.
Turning it on and off
The toggle is a real VS Code command, so it lives inside the normal keybinding system.
From the command palette (F1):
| Command |
|
Neon Glow: Toggle |
flip the glow on/off, instantly |
Neon Glow: Enable / Neon Glow: Disable |
set it explicitly — only whichever one would actually change something is listed |
Neon Glow: Show status |
current state, and whether the bundle is patched |
Neon Glow: Choose a preset |
subtle, default, or everything on — twenty-three knobs as three answers. Applies at once, with no restart |
No default keybinding ships with this, deliberately — that is what makes it
impossible to collide with another extension. Bind whatever you like in Keyboard
Shortcuts (Ctrl+K Ctrl+S) and search Neon Glow.
Toggling never touches a file in the install directory, so it needs no admin rights
and no restart. A status bar item on the right shows NEON:ON / NEON:OFF and
toggles on click; a warning background on it means the switch is real but nothing can
glow — the bundle is unpatched, or carries an older payload, or was patched after this
window started. The tooltip says which, and clicking does the thing that fixes it.
Commands run in the extension host and the glow lives in the renderer, with no API
between them, so the state crosses on two channels at once: a MutationObserver on
the status bar item's label, which lands a toggle in about 16ms, and a state.json
poll for whatever that misses. neon-glow.js carries the details.
You can also drive it from the DevTools console:
__neonGlow.toggle(); // .enable() / .disable() / .isEnabled()
__neonGlow.bridgeOk(); // is either half live? .statusBarOk() for the fast one
Install
Requires write access to the VS Code install directory — run the terminal as
administrator on Windows, or with sudo on macOS/Linux.
From the Marketplace
Search Neon Glow in the Extensions view, or:
code --install-extension Ruminem.vscode-neon-glow
Installing the extension does not by itself make anything glow — the payload lives in
workbench.js, and only a patch puts it there. The extension notices an unpatched
bundle when it activates and offers to fix it; you can also run Neon Glow: Show status from the palette. Either route needs write access and a full restart.
Everyday on/off needs neither.
From a release
On Windows, download neon-glow-<version>-windows.zip from
Releases, unzip it, and
double-click install.cmd. It finds VS Code, installs the extension and patches the
bundle in one pass. Nothing else has to be on the machine: VS Code is Electron, so
Code.exe doubles as the Node that runs the patcher.
On macOS and Linux, download neon-glow-<version>-macos-linux.tar.gz, unpack it, and
run ./install.sh. It does the same three things, and says what to do if the install
directory belongs to root. A VS Code installed as a snap or a flatpak cannot be
patched at all — those are mounted read-only; use the .deb, the .rpm or the
tarball.
The same release carries the .vsix if you would rather install that directly.
From the command line
No extension at all — patch the bundle directly:
git clone https://github.com/Ruminem/vscode-neon-glow
cd vscode-neon-glow
node install.js # node uninstall.js to revert
Point at a specific install with --target "<path to resources/app>". Without the
extension there are no commands, so the toggle falls back to Ctrl+Alt+N.
After any of these routes: quit VS Code completely and start it again.
Building it yourself
npm run package # -> neon-glow-<version>.vsix
node tools/make-icon.js # -> icon.png (also: bars, n)
node tools/smoke.js # run the payload against a stub workbench
Tagging a commit v<version> builds the VSIX in CI and attaches it to a GitHub
release; the tag must match the version in package.json or the job fails. There
are no dependencies anywhere in this repo, the icon included: its shapes are signed
distance fields and the PNG is assembled on top of zlib.
Settings
Open Settings (Ctrl+,) and search Neon Glow. Changes apply to the open editor as
you make them — no re-patch, no restart, and nothing to open first: the file you were
reading is the preview.
| Setting |
Default |
|
neonGlow.glowLayers |
3 |
halo passes per token, over an edge pass that always runs — the expensive one |
neonGlow.maxBlur |
36 |
ceiling on any one blur radius; the default is already the widest emitted |
neonGlow.brightness |
1.0 |
overall strength; 0 leaves the colours alone and drops the glow |
neonGlow.minChroma |
0.30 |
colours flatter than this never glow — this is what keeps body text out |
neonGlow.chromaSpan |
0.50 |
how much chroma above the threshold reaches full strength |
neonGlow.floor |
0.40 |
strength of a colour that only just passes minChroma |
neonGlow.minLightness |
0.25 |
skip colours darker than this, however saturated |
neonGlow.cursorTrail |
0 |
ms for the caret to slide to a new position, so its glow streaks; 0 keeps the jump |
neonGlow.saveShake |
0 |
px the workbench jolts on save; 0 holds it still, and so should files.autoSave |
neonGlow.findGlow |
18 |
px of bloom on find results, in the theme's own find colours; 0 turns it off |
neonGlow.selectionGlow |
12 |
px of bloom on selected text, in the theme's own selection colour; 0 turns it off |
neonGlow.occurrenceGlow |
10 |
px of bloom on the symbol under the caret, wherever else it appears; 0 turns it off |
neonGlow.gutterGlow |
8 |
px of bloom on the gutter's change bars, in the theme's own editorGutter colours; 0 turns it off |
neonGlow.bracketMatchGlow |
10 |
px of bloom on the box around a bracket and its partner; 0 turns it off |
neonGlow.squiggleGlow |
5 |
px of bloom on error, warning and info squiggles, in the theme's own colours; 0 turns it off |
neonGlow.diffGlow |
10 |
px of bloom on what changed inside a diff, on the word-level highlight only; 0 turns it off |
neonGlow.lineHighlightGlow |
8 |
px of bloom on the line the editor points at — the debug stop, a jump, a peek; 0 turns it off |
neonGlow.breakpointGlow |
8 |
px of bloom on breakpoint glyphs, in the colour they were painted; 0 turns it off |
neonGlow.snippetGlow |
8 |
px of bloom on a snippet's tabstops while you fill them in; 0 turns it off |
neonGlow.renameGlow |
12 |
px of bloom around the rename box, in the theme's focusBorder colour; 0 turns it off |
neonGlow.terminalGlow |
8 |
px of bloom on coloured terminal text, in the colour the shell asked for; needs terminal.integrated.gpuAcceleration set to off, because every other value draws the terminal into a canvas and CSS cannot reach one; 0 turns it off |
neonGlow.caretArc |
off |
what to draw along a caret jump — twelve shapes, from a straight beam to a ring that needs no distance |
neonGlow.caretArcColor |
cursor |
the arc's colour — cursor follows the theme's caret, or give a hex like #ff2f92; settings.json offers a colour picker |
neonGlow.caretArcMinJump |
5 |
px of travel before an arc is drawn — under a character, so an arrow key counts |
neonGlow.caretArcDuration |
300 |
ms the arc takes to cross its path — eighteen frames at 60Hz, and it wants raising on a slower panel |
neonGlow.caretArcOnDrag |
false |
keep drawing while a selection is dragged out; the click that starts the drag draws either way |
neonGlow.breathe |
0 |
ms for one breath on whatever waits for you — the find match, the stopped line, the bracket box, snippet tabstops, the rename box; 0 holds them still, and under 600 is held at 600 |
glowLayers is the one that costs. The editor virtualises, so a 10,000 line file
is not 10,000 glowing spans and the cost scales with the viewport rather than the
file. But the widest pass is very nearly the whole bill, not the two thirds the area
arithmetic predicts — blur time climbs much faster than radius, and neighbouring
tokens' widest blurs overlap. Measured over a driven scroll on a dense C++ file, the
defaults took raster from 26ms with no glow to 101ms, and 2 brought it to 58ms.
Raising brightness makes it worse, because it widens every blur: at 2 the
same scroll took 270ms, and glowLayers 2 brought that to 61ms. Drop to 2 on a
slow machine, or cap maxBlur to keep the wide halo for less (26 took a third off
at brightness 2). The measurements and their caveats are kept in neon-glow.js,
and tools/bench.js re-runs them in pairs.
Settings work without a restart because they do not live in the patch: the extension
writes them into the same state.json the toggle uses, and the renderer clamps and
applies them on its next poll. The same values are also the defaults at the top of
neon-glow.js, which is what a CLI-only install uses — editing those means re-running
node install.js and restarting.
Things that will bite you
"Reload Window" does not apply changes. It is a soft reload, and Chromium serves
the vscode-file bundle from cache. A window launched before you patched keeps
running the old bundle no matter how many times you reload it. Quit every VS Code
process and relaunch.
VS Code will warn that the installation is corrupt. Expected: product.json
carries a SHA-256 of workbench.js, and patching it breaks the match. Dismiss with
Don't Show Again. Rewriting the checksum would silence it permanently but disables
tamper detection for all future modifications, not just this one.
Updating the extension no longer asks you to patch again. What goes into
workbench.js is a small loader; the code it loads lives beside the settings file and
is rewritten whenever the extension starts with a newer copy of it. So an extension
update reaches the glow on the next full restart, with nothing to click. Only a change
to the loader itself — rare, and nothing a release usually touches — makes the
extension ask, and it says so when it does.
VS Code updates wipe the patch. The updater replaces workbench.js, which leaves
the same state as a fresh install, so the extension offers to re-patch on next launch.
Re-patching is safe: it always rebuilds from the pristine .pre-neon.bak, never from
an already-patched file.
Uninstalling the extension restores the bundle through a vscode:uninstall hook.
It is best effort — if the install directory is not writable the hook fails and the
bundle stays patched; run node uninstall.js with the rights it needs. Disabling
the extension is not uninstalling it: the patch stays and the glow keeps working off
the last state it saw.
files.autoSave and saveShake do not mix. Every autosave is a save, and the
screen never stops moving.
SynthWave '84 can sit alongside this, but only one of them should paint. Different
files and different backups, so neither patch can corrupt the other — but both build a
<style> from .vscode-tokens-styles at equal specificity, so whichever is appended
last wins. Neon Glow: Show status reports whether it is competing, and
Neon Glow: Disable steps aside instantly without touching a file.
Status
Developed and verified against VS Code 1.136.1 on Windows 11, with the applied
text-shadow values read back out of the live renderer over the Chrome DevTools
Protocol.
|
Works |
Verified |
|
| Windows |
yes |
yes |
user and system installs, including the commit-hash resources directory |
| macOS |
should |
no |
patching edits a file inside the signed .app, which invalidates its code signature — it keeps running in practice, but that is the trade |
Linux, .deb / .rpm / tarball |
should |
no |
/usr/share/code belongs to root, so the palette command cannot do it; use sudo node install.js or sudo ./install.sh |
| Linux, snap or flatpak |
no |
— |
mounted read-only, so nothing can patch them. The extension says so rather than failing obscurely |
| Insiders, portable |
should |
no |
patch from the palette rather than install.js, which assumes the stable build's folders. After an extension update the new version shows on the second restart, not the first |
macOS and Linux are written to work and have not been run by anyone — please open an
issue if they misfire.
Credit
The idea — that syntax highlighting can glow, and that patching the workbench is a way
to get there — comes from
SynthWave '84 by Robb Owen.
License
MIT
Korean
English · 한국어
VS Code 구문 강조에 네온 글로우를 입힘.
어느 테마에서나 그 테마의 색으로.
테마를 함께 배포하지 않음. 지금 쓰는 테마가 만들어내는 색을 그대로 읽어서 그중 선명한
것만 빛나게 하므로, 테마를 계속 바꿔도 글로우가 따라옴. 리로드도 필요 없음.
같은 파일의 같은 순간임. 왼쪽은 글로우 없음, 오른쪽은 글로우 있음 — 토큰 색, 일치하는
괄호 상자, 커서가 놓인 심볼의 다른 출현, 에러·경고·정보 물결선까지:

클립에서는 caretArc와 caretArcOnDrag를 켜뒀음. 둘 다 기본은 꺼짐임.
빛나는 토큰 · 토큰 말고 빛나는 것 · 설치 · 설정 · 발목 잡는 것들 · 상태
어떤 토큰이 빛나는가
세기는 HSL 채도가 아니라 크로마(max(r,g,b) - min(r,g,b))가 정함. 채도는
1 - |2L-1|로 나누는 값이라 흰색에 가까운 색에서 폭발함. 그대로 쓰면 본문 텍스트가
빛남. Monokai에서 측정한 값:
| 색 |
역할 |
크로마 |
세기 |
#F92672 |
키워드 |
0.827 |
1.00 |
#FD971F |
매개변수 |
0.871 |
1.00 |
#A6E22E |
함수 이름 |
0.706 |
0.89 |
#66D9EF |
타입 / 클래스 |
0.537 |
0.68 |
#AE81FF |
숫자 |
0.494 |
0.63 |
#E6DB74 |
문자열 |
0.447 |
0.58 |
#F8F8F2 |
일반 텍스트 |
0.024 |
— (건너뜀) |
#88846F |
주석 |
0.098 |
— (건너뜀) |
색에서 역할을 유추하는 방식이라, 여러 역할에 같은 색을 쓰는 테마는 구분하지 못함.
역할마다 색이 다른 테마(Monokai, Tokyo Night, Dracula …)에서 잘 나옴.
밋밋한 테마는 다른 숫자가 필요함. 기본값은 유난히 채도가 높은 Monokai에 맞춰져 있음.
Abyss에서는 클래스 이름 색 #ffeebb가 0.30 문턱에 아예 못 닿고 주석 색 #384887은
넘어서, 빛나는 역할이 거의 뒤집혀 나옴. minChroma를 0.25 근처로 내리고 brightness를
1 위로 올리면 돌아옴.
괄호 쌍 색칠은 VS Code에 맡김. 괄호에는 currentColor로 된 규칙을 따로 줘서, 중첩 단계
하나하나가 실제로 칠해진 그 색으로 빛남.
토큰 말고 빛나는 것
켜져 있는 것이 토큰 글로우만은 아님. 표면 아홉 개가 같이 빛나고, 효과 넷은 부를 때까지
기다림. 그 경계는 한 문장임 — 어떤 색이 어디에 놓이는지만 정하는 설정은 켜져 나가고,
일하는 동안 에디터가 하는 일을 바꾸는 설정은 꺼져 나감.
앞쪽은 전부 테마 자신의 색을 여태 안 가던 자리로 옮기는 것뿐임. 그래서 켜져 있어도
테마가 고르지 않은 색이 화면에 새로 생기지 않음.
| 기본으로 켜짐 |
|
찾기 결과 (findGlow) |
스크롤바를 뒤지지 않아도 매치가 눈에 띔 |
선택 영역 (selectionGlow) |
선택한 텍스트 |
커서가 놓인 심볼 (occurrenceGlow) |
커서가 놓인 단어와, 화면에 나온 같은 단어 전부 |
거터의 변경 막대 (gutterGlow) |
소스 관리가 남기는 추가·수정·삭제 표시 |
일치하는 괄호 상자 (bracketMatchGlow) |
괄호와 그 짝을 감싼 상자 |
에러·경고·정보 물결선 (squiggleGlow) |
물결선만 빛나고 그 위 단어는 번지지 않음 |
diff에서 바뀐 것 (diffGlow) |
줄 전체가 아니라 바뀐 단어만 |
에디터가 가리키는 줄 (lineHighlightGlow) |
디버그 중 멈춘 줄, 정의로 이동하거나 미리본 자리 |
중단점 (breakpointGlow) |
칠해진 색 그대로 |
스니펫 탭 정지 자리 (snippetGlow) |
스니펫이 채워주길 기다리는 자리 |
이름 바꾸기 상자 (renameGlow) |
F2가 여는 입력 상자 |
색이 붙은 터미널 글자 (terminalGlow) |
터미널 자신의 ANSI 색. terminal.integrated.gpuAcceleration이 off일 때만 |
찾기·선택·심볼·diff 색은 글자 뒤에 깔리는 색이라 테마가 반투명으로 잡아둠. 그래서 그 넷에는
spread가 붙음 — 약한 색을 원래 폭만큼 먼저 밀어낸 다음에 번지게 함. 거터 막대에도
spread가 붙지만 이유가 다름. 글로우를 거는 상자가 width: 0이라, 넓이가 없는 상자의
그림자는 블러를 아무리 키워도 아무것도 안 그림. 커서와 괄호 상자는 색이 불투명이라
필요 없음. 물결선은 box-shadow부터 안 씀 — 물결은 상자가 아니라 SVG 그림이라, 그린
모양을 따라가는 drop-shadow 필터를 씀. 규칙이 서로 다르게 생긴 것은 그 때문이고,
이유는 규칙마다 neon-glow.js에 붙여뒀음.
| 켜야 도는 것 |
|
커서 잔상 (cursorTrail) |
커서가 튀는 대신 미끄러지며 꼬리를 남김. 45면 타이핑을 따라감 |
저장 흔들림 (saveShake) |
저장할 때 화면이 옆으로 흔들림. 자동 저장을 쓰면 끌 것 |
기다리는 것만 숨쉬기 (breathe) |
찾기 결과·멈춘 줄·괄호 상자·스니펫 자리·이름 바꾸기 상자가 천천히 숨쉼 |
커서 아크 (caretArc) |
커서가 뛴 길에 열두 모양 중 하나를 테마의 커서 색이나 직접 고른 색으로 그림 |
이 넷은 무언가를 움직임. 움직임을 달라고 한 사람은 없으므로 0으로 나가고 말하기
전까지 그대로 있음. 넷 다 시스템이나 VS Code의 workbench.reduceMotion이 모션 줄이기를
요청하면 멈춤.
켜고 끄기
토글은 진짜 VS Code 명령이라 평범한 키바인딩 체계 안에 있음. 명령 팔레트(F1)에서:
| 명령 |
|
Neon Glow: 설정/해제 |
글로우를 즉시 켜고 끔 |
Neon Glow: 사용 / Neon Glow: 사용 안 함 |
명시적으로 지정. 실제로 뭔가 바뀌는 쪽만 목록에 뜸 |
Neon Glow: 상태 표시 |
지금 상태와 번들이 패치돼 있는지 |
Neon Glow: 프리셋 선택 |
은은하게 / 기본 / 다 켜기 — 노브 스물셋을 세 답으로. 재시작 없이 즉시 적용됨 |
기본 키바인딩을 넣지 않았음. 일부러 그랬고, 다른 확장과 충돌할 수 없는 이유가
그것임. 바로 가기 키(Ctrl+K Ctrl+S)에서 Neon Glow로 찾아 원하는 대로 잡으면 됨.
토글은 설치 디렉터리의 파일을 건드리지 않으므로 관리자 권한도 재시작도 필요 없음.
오른쪽 상태 표시줄에 NEON:ON / NEON:OFF가 뜨고 눌러서 토글함. 거기에 경고 배경이
뜨면 스위치는 살아 있는데 빛날 수가 없는 상태임 — 번들이 패치가 안 됐거나, 옛 페이로드를
싣고 있거나, 이 창이 뜬 뒤에 패치됐거나. 툴팁이 어느 쪽인지 말하고, 누르면 그걸 고치는
일을 함.
명령은 확장 호스트에서 돌고 글로우는 렌더러 안에 있음. 둘 사이에 API가 없어서 상태가 두
경로로 동시에 건너감 — 상태 표시줄 항목의 라벨에 건 MutationObserver(토글이 16ms쯤에
도착함)와 state.json 폴링(앞엣것이 놓친 것을 메움)임. 자세한 것은 neon-glow.js에
있음.
DevTools 콘솔에서 직접 부를 수도 있음:
__neonGlow.toggle(); // .enable() / .disable() / .isEnabled()
__neonGlow.bridgeOk(); // 둘 중 하나라도 살아 있는가? 빠른 쪽만 보려면 .statusBarOk()
설치
VS Code 설치 디렉터리에 쓸 수 있어야 함 — Windows에서는 터미널을 관리자로, macOS/Linux
에서는 sudo로 띄울 것.
마켓플레이스에서
확장 뷰에서 Neon Glow를 검색하거나:
code --install-extension Ruminem.vscode-neon-glow
확장을 설치했다고 바로 빛나지는 않음. 페이로드는 workbench.js 안에 있고 거기 넣는 것은
패치뿐임. 확장이 활성화될 때 패치 안 된 번들을 알아보고 고칠지 물어봄. 팔레트에서
Neon Glow: 상태 표시를 불러도 됨. 어느 쪽이든 쓰기 권한과 완전 재시작이 필요함.
평소 켜고 끄는 데는 둘 다 필요 없음.
릴리스에서
Windows는 Releases에서
neon-glow-<version>-windows.zip을 받아 풀고 install.cmd를 더블클릭. VS Code를 찾아
확장 설치와 번들 패치를 한 번에 함. 기계에 다른 게 깔려 있을 필요가 없음 — VS Code가
Electron이라 Code.exe가 패처를 돌릴 Node 노릇도 함.
macOS와 Linux는 neon-glow-<version>-macos-linux.tar.gz를 받아 풀고 ./install.sh.
같은 세 가지를 하고, 설치 디렉터리가 root 소유일 때 뭘 해야 하는지 알려줌. snap이나
flatpak으로 깐 VS Code는 아예 패치할 수 없음 — 읽기 전용으로 마운트됨. .deb, .rpm,
tarball을 쓸 것.
같은 릴리스에 .vsix도 붙어 있으니 직접 설치해도 됨.
명령줄에서
확장 없이 번들만 직접 패치:
git clone https://github.com/Ruminem/vscode-neon-glow
cd vscode-neon-glow
node install.js # 되돌리려면 node uninstall.js
특정 설치를 지정하려면 --target "<resources/app 경로>". 확장이 없으면 명령도 없으므로
토글이 Ctrl+Alt+N으로 떨어짐.
어느 경로로 했든 마지막에: VS Code를 완전히 종료하고 다시 켤 것.
직접 빌드하기
npm run package # -> neon-glow-<version>.vsix
node tools/make-icon.js # -> icon.png (bars, n 도 있음)
node tools/smoke.js # 스텁 워크벤치에 페이로드를 올려 검사
커밋에 v<version> 태그를 달면 CI가 VSIX를 빌드해 GitHub 릴리스에 붙임. 태그와
package.json의 version이 어긋나면 잡이 거부함. 이 리포에는 어디에도 의존성이 없고
아이콘도 마찬가지임 — 모양은 signed distance field고 PNG는 zlib 위에 직접 조립함.
설정
설정(Ctrl+,)에서 Neon Glow로 검색. 값을 바꾸는 즉시 열려 있는 에디터에 반영됨.
재패치도 재시작도 없고, 먼저 열어둘 것도 없음 — 읽고 있던 그 파일이 미리보기임.
| 설정 |
기본값 |
|
neonGlow.glowLayers |
3 |
토큰당 헤일로 겹 수. 항상 도는 가장자리 겹 위에 얹힘 — 비싼 것이 이것임 |
neonGlow.maxBlur |
36 |
블러 반경 하나의 상한. 기본값이 이미 최대치라 그대로면 변화 없음 |
neonGlow.brightness |
1.0 |
전체 세기. 0이면 색은 그대로 두고 글로우만 없앰 |
neonGlow.minChroma |
0.30 |
이보다 밋밋한 색은 빛나지 않음 — 본문 텍스트를 걸러내는 것이 이 값임 |
neonGlow.chromaSpan |
0.50 |
임계값 위로 크로마가 얼마나 더 있어야 최대 세기에 닿는가 |
neonGlow.floor |
0.40 |
minChroma를 겨우 넘긴 색의 세기 |
neonGlow.minLightness |
0.25 |
아무리 채도가 높아도 이보다 어두우면 건너뜀 |
neonGlow.cursorTrail |
0 |
커서가 새 위치로 미끄러지는 시간(ms). 커서 글로우가 꼬리를 남김. 0이면 그대로 튐 |
neonGlow.saveShake |
0 |
저장할 때 워크벤치가 흔들리는 폭(px). 0이면 가만히 있음. files.autoSave를 쓰면 끄는 게 나음 |
neonGlow.findGlow |
18 |
찾기 결과가 번지는 폭(px). 테마 자신의 찾기 색을 씀. 0이면 꺼짐 |
neonGlow.selectionGlow |
12 |
선택 영역이 번지는 폭(px). 테마 자신의 선택 색을 씀. 0이면 꺼짐 |
neonGlow.occurrenceGlow |
10 |
커서가 놓인 심볼이 화면에 나온 자리마다 번지는 폭(px). 0이면 꺼짐 |
neonGlow.gutterGlow |
8 |
거터의 변경 막대가 번지는 폭(px). 테마 자신의 editorGutter 색을 씀. 0이면 꺼짐 |
neonGlow.bracketMatchGlow |
10 |
괄호와 그 짝을 감싸는 상자가 번지는 폭(px). 0이면 꺼짐 |
neonGlow.squiggleGlow |
5 |
에러·경고·정보 물결선이 번지는 폭(px). 테마 자신의 색을 씀. 0이면 꺼짐 |
neonGlow.diffGlow |
10 |
diff에서 바뀐 단어가 번지는 폭(px). 줄 전체 틴트에는 안 붙음. 0이면 꺼짐 |
neonGlow.lineHighlightGlow |
8 |
에디터가 가리키는 줄이 번지는 폭(px) — 디버그 정지, 점프, 미리보기. 0이면 꺼짐 |
neonGlow.breakpointGlow |
8 |
중단점 글리프가 번지는 폭(px). 칠해진 색 그대로 씀. 0이면 꺼짐 |
neonGlow.snippetGlow |
8 |
스니펫 탭 정지 자리가 번지는 폭(px). 채우는 동안에만 보임. 0이면 꺼짐 |
neonGlow.renameGlow |
12 |
이름 바꾸기 상자가 번지는 폭(px). 테마의 focusBorder 색을 씀. 0이면 꺼짐 |
neonGlow.terminalGlow |
8 |
색이 붙은 터미널 글자가 번지는 폭(px). 셸이 요청한 색 그대로 씀. terminal.integrated.gpuAcceleration이 off여야 함 — 나머지 값에서는 터미널을 캔버스에 그려서 CSS가 못 닿음. 0이면 꺼짐 |
neonGlow.caretArc |
off |
커서가 뛴 길에 무엇을 그릴지. 열두 모양 — 곧은 beam부터 거리가 필요 없는 ring까지 |
neonGlow.caretArcColor |
cursor |
아크 색. cursor는 테마의 커서 색을 따르고, #ff2f92 같은 hex도 받음. settings.json에서는 색 선택기가 뜸 |
neonGlow.caretArcMinJump |
5 |
아크를 그리기까지 필요한 이동 거리(px). 한 글자보다 작아 화살표도 걸림 |
neonGlow.caretArcDuration |
300 |
아크가 경로를 건너는 시간(ms). 60Hz에서 열여덟 프레임이고, 주사율이 낮으면 올려 잡아야 함 |
neonGlow.caretArcOnDrag |
false |
마우스로 선택을 끄는 동안에도 계속 그릴지. 드래그를 시작한 클릭은 어느 쪽이든 그림 |
neonGlow.breathe |
0 |
기다리는 것들(찾기 결과·멈춘 줄·괄호 상자·스니펫 자리·이름 바꾸기 상자)이 한 번 숨쉬는 시간(ms). 0이면 가만히 있고, 600 아래는 600으로 붙듦 |
돈이 드는 것은 glowLayers임. 에디터는 가상화하므로 10,000줄 파일이 곧 10,000개의
빛나는 span은 아니고, 비용은 파일 크기가 아니라 뷰포트 크기에 비례함. 그런데 가장 넓은
겹이 면적 산수가 예측하는 3분의 2가 아니라 사실상 전부임 — 블러 시간이 반경보다 훨씬
빠르게 오르고, 이웃 토큰끼리 가장 넓은 블러가 겹침. 토큰이 빽빽한 C++ 파일에서 스크롤을
구동하며 재보면 기본값은 래스터 시간을 글로우 없을 때의 26ms에서 101ms로 늘리고, 2면
58ms임. brightness를 올리면 더 무거워짐 — 모든 블러가 넓어지기 때문임. 2에서는 같은
스크롤이 270ms였고 glowLayers 2로 61ms가 됐음. 느린 기계에서는 2로 내리거나, 넓은
후광을 남기고 싶으면 maxBlur를 낮출 것(밝기 2에서 26이면 3분의 1이 줄었음).
측정값과 그 단서는 neon-glow.js에 남겨뒀고, tools/bench.js가 쌍대로 다시 잼.
설정이 재시작 없이 도는 것은 패치 안에 살지 않기 때문임. 확장이 토글과 같은
state.json에 써 넣고, 렌더러가 다음 폴링에서 값을 죄어 적용함. 같은 값이
neon-glow.js 맨 위에도 기본값으로 있는데 그게 CLI 전용 설치가 쓰는 것임 — 그걸 고치면
node install.js를 다시 돌리고 재시작해야 함.
발목 잡는 것들
"창 다시 로드"로는 반영되지 않음. 소프트 리로드라서 Chromium이 vscode-file 번들을
캐시에서 줌. 패치 전에 띄운 창은 몇 번을 리로드해도 옛 번들을 계속 씀. VS Code 프로세스를
전부 종료하고 다시 띄울 것.
VS Code가 설치가 손상됐다고 경고함. 예상된 일임. product.json이 workbench.js의
SHA-256을 들고 있는데 패치하면 그 값이 안 맞음. 다시 표시 안 함으로 닫으면 됨.
체크섬을 다시 써넣으면 영구히 조용해지지만, 이 변경만이 아니라 앞으로의 모든 변조에
대한 탐지가 꺼짐.
확장을 업데이트해도 다시 패치하라고 하지 않음. workbench.js에 들어가는 것은 작은
로더이고, 그 로더가 읽는 코드는 설정 파일 옆에 있음. 확장이 더 새 것을 들고 뜨면 그 파일을
알아서 갈아끼움. 그래서 확장 업데이트는 완전 재시작 한 번으로 글로우까지 닿고, 누를
것이 없음. 다시 패치가 필요한 경우는 로더 자체가 바뀔 때뿐이고 — 릴리스에서 흔한 일이
아님 — 그때는 확장이 말해줌.
VS Code 업데이트는 패치를 지움. 업데이터가 workbench.js를 갈아치움. 새로 설치한
것과 같은 상태라 확장이 다음 실행에서 다시 패치할지 물어봄. 다시 패치하는 것은 안전함 —
항상 손 안 댄 .pre-neon.bak에서 다시 만들지, 이미 패치된 파일에서 만들지 않음.
확장을 제거하면 번들이 복원됨. vscode:uninstall 훅이 함. 최선을 다할 뿐이라, 설치
디렉터리에 쓸 수 없으면 훅이 실패하고 번들은 패치된 채로 남음. 그때는 권한을 주고
node uninstall.js를 돌릴 것. 비활성화는 제거가 아님 — 패치는 남아 있고 글로우도
마지막으로 본 상태 그대로 계속 돎.
files.autoSave와 saveShake는 같이 쓰지 말 것. 자동 저장 하나하나가 전부 저장이라
화면이 멈추지 않음.
SynthWave '84과 같이 깔 수는 있지만 칠하는 것은 하나여야 함. 파일도 백업도 서로
달라서 패치끼리 망가뜨리지는 않음. 그런데 둘 다 .vscode-tokens-styles에서 <style>을
만들고 특이도가 같아서, 나중에 붙은 쪽이 이김. Neon Glow: 상태 표시가 지금 경합
중인지 알려주고, Neon Glow: 사용 안 함은 파일을 건드리지 않고 즉시 비켜줌.
상태
Windows 11의 VS Code 1.136.1에서 개발하고 검증했음. 적용된 text-shadow 값을 Chrome
DevTools Protocol로 살아 있는 렌더러에서 다시 읽어내 확인함.
|
동작 |
검증 |
|
| Windows |
함 |
함 |
사용자·시스템 설치 모두. 커밋 해시가 붙은 resources 디렉터리 포함 |
| macOS |
할 것임 |
안 함 |
서명된 .app 내부 파일을 고치므로 코드 서명이 깨짐. 실제로는 계속 실행되지만 그게 치르는 값임 |
Linux, .deb / .rpm / tarball |
할 것임 |
안 함 |
/usr/share/code가 root 소유라 팔레트 명령으로는 안 됨. sudo node install.js나 sudo ./install.sh를 쓸 것 |
| Linux, snap 또는 flatpak |
안 함 |
— |
읽기 전용으로 마운트돼서 무엇도 패치할 수 없음. 확장이 조용히 실패하는 대신 그렇다고 말함 |
| Insiders, 포터블 |
할 것임 |
안 함 |
install.js는 정식판 폴더를 가정하므로 팔레트에서 패치할 것. 확장을 업데이트하면 새 버전은 첫 재시작이 아니라 두 번째 재시작에 뜸 |
macOS와 Linux는 동작하도록 작성했을 뿐 아무도 돌려보지 않았음. 어긋나면 이슈를 열어주면
고맙겠음.
출처
구문 강조가 빛날 수 있다는 것, 그리고 워크벤치를 패치하는 것이 거기 닿는 길이라는 것 —
그 아이디어는 Robb Owen의
SynthWave '84에서 왔음.
라이선스
MIT