feat: Schema documentation and enhancement plans

This commit is contained in:
Rainer Kottenhoff 2026-04-22 18:06:06 +02:00
parent 2e187641c7
commit aa0ce545c5
4 changed files with 888 additions and 1 deletions

View File

@ -117,6 +117,8 @@ Notepad3 uses a portable INI file for all settings. Press **Ctrl+F7** to open it
📖 **Full configuration reference:** [readme/config/Configuration.md](readme/config/Configuration.md)
🎨 **Schemas, styles & themes:** [readme/schema/CustomSchema.md](readme/schema/CustomSchema.md) — the layered override model, the style mini-language, `View → Customize Schemes` (**F12**), and how to export / import / collect custom themes.
## Contributing
Contributions are welcome! Here's how to help:

View File

@ -0,0 +1,424 @@
# Schema / Style / Theme System — Enhancements & Fixes
> Companion plan to the user-facing reference at
> [`readme/schema/CustomSchema.md`](../readme/schema/CustomSchema.md).
> Use this document to scope a future Claude Code session focused on the
> schema/style subsystem.
## 1. Background
Notepad3's schema system (the `EDITLEXER` array, per-schema `EDITSTYLE` slots,
the *Common Base* / *2nd Common Base* super-schemas, and the `themes\*.ini`
collection) is stable and user-visible, but has accumulated small rough
edges over time. This plan consolidates:
- issues surfaced while writing the user documentation,
- follow-up ideas marked as `//~` in the code,
- UX gaps reported during the review,
- plus a few additional findings that emerged from cross-checking the
code paths.
Nothing here is a correctness bug blocking release. All items are
low-to-moderate effort; several are trivial single-call-site fixes.
## 2. Key source locations
| File | What lives there |
|------|------------------|
| `src/Styles.c` | `g_pLexArray[]`, load/save, mini-DSL parsers, Customize / Select / Themes dialogs, Import / Export |
| `src/StyleLexers/EditLexer.h` | `EDITLEXER`, `EDITSTYLE`, `KEYWORDLIST`, `BUFSIZE_STYLE_VALUE = 256` |
| `src/TypeDefs.h` | `STYLE_EXTENTIONS_BUFFER = 512`, `EXTENTIONS_FILTER_BUFFER = 1024` |
| `src/StyleLexers/*.c` | 60+ per-language lexer definitions |
| `src/Config/Config.cpp` | Settings → theme name persistence (`Settings.CurrentThemeName`) |
| `language/common_res.h` | Menu IDs: `IDM_VIEW_SCHEME=41001`, `IDM_VIEW_USE2NDDEFAULT=41002`, `IDM_VIEW_SCHEMECONFIG=41003`, `IDM_THEMES_FACTORY_RESET=37001`, `IDM_THEMES_STD_CFG=37002` |
| `Build/themes/Dark.ini`, `Obsidian.ini`, `Sombra.ini` | Shipped themes |
## 3. Inventory of issues
| # | Severity | Area | Issue |
|---|----------|------|-------|
| I-01 | Medium | Buffers | 255-char style-string cap; silent truncation on load |
| I-02 | Low | Buffers | 511-char `FileNameExtensions` cap; silent truncation |
| I-03 | Medium | Themes | 24-theme hard cap (fixed-size `Theme_Files[25]`); silently drops extras |
| I-04 | Medium | UX | No "Reset this whole schema" button — user must edit INI by hand or factory-reset everything |
| I-05 | Medium | UX | Import overwrites `FileNameExtensions` — users lose extension tweaks when switching themes |
| I-06 | Low | UX | No mid-session revert: `pStylesBackup` is only taken on dialog open |
| I-07 | Low | UX | No "import from URL" and no way to distribute themes via anything but a file copy |
| I-08 | Low | Round-trip | `smoothing:` token parsed on load but never written back by the dialog |
| I-09 | Low | Round-trip | `//~` dead code around `Style_StrGetAlpha` for Whitespace background layer (alpha2 never exposed) |
| I-10 | Low | Round-trip | `SelectDlgSizeX/Y` travels inside `[Styles]` and leaks into every exported theme |
| I-11 | Medium | Matching | First-in-array wins for extension conflicts — no per-user priority mechanism |
| I-12 | Low | Matching | `\regex` extension syntax is entirely undocumented in-UI |
| I-13 | Low | Validation | Silent load of malformed theme files — no minimum-viable-theme check |
| I-14 | Low | Validation | Stale `Settings.CurrentThemeName` silently falls back to Standard Config |
| I-15 | Low | Consistency | Dialog-cancel restores styles but *not* extension-list edits (they hit the array directly via `_ApplyDialogItemText`) — verify & fix if confirmed |
| I-16 | Low | Dead code | `//~bool ChooseFontDirectWrite(...)` (`Styles.c:~50`) — decide delete vs enable |
| I-17 | Low | Feature | Dark-mode 16-colour defaults (`s_colorLightDefault[]`, `s_colorDarkDefault[]`) are hard-coded with no load/save path |
| I-18 | Low | Feature | Style names are compile-time fixed — no user-defined styles, no extension lists on custom detection beyond extension/regex |
| I-19 | Low | Performance | Schema auto-detect is O(n) per file open across ~60 schemas × multi-token extension lists |
| I-20 | Low | Performance | All `themes\*.ini` are stat-scanned at startup even if user never switches themes |
## 4. Enhancement proposals
Each proposal below lists the motivation, concrete change, files to touch,
and likely risk level. They are grouped into tiers so a session can pick
up an easy batch without stepping on the harder ones.
---
### Tier 1 — Quick wins (half-day each)
#### E-01 — Warn on style-string truncation (closes I-01)
**Motivation.** When a user hand-edits a style in `Notepad3.ini` and the
result exceeds 255 chars (easy with long font names plus many
attributes), the tail is silently dropped on load. No signal is given.
**Change.**
- In `_ReadFromIniCache()` (`src/Styles.c`), after each
`IniSectionGetString(..., dst, BUFSIZE_STYLE_VALUE)`, compare the
returned length against `BUFSIZE_STYLE_VALUE - 1`. If equal, the value
was almost certainly clipped.
- Accumulate a list of offending `<section> / <key>` pairs in a static
buffer.
- After `_ReadFromIniCache()` completes, if any clipped values were
found, show a single `InfoBoxLng(MB_ICONWARNING, ...)` listing the
first few (e.g. up to 5) and hinting that the cap is 255 chars.
- Add string IDs `IDS_MUI_STYLE_TRUNCATED` to `language/common_res.h`
and all 26 `strings_*.rc` files with an English placeholder (per
memory note about locale files).
**Risk.** Low. Read-only addition.
#### E-02 — Reset-whole-schema button (closes I-04)
**Motivation.** Customize Schemes has a *Default* button that resets the
currently-selected style. Users who want to revert an entire schema
(say, C++) to defaults currently have no one-click path.
**Change.**
- Add a new button to `IDD_MUI_STYLECONFIG` next to the existing
*Default* button, e.g. `IDC_STYLERESETSCHEMA`, labelled
"Reset &Scheme".
- When the selected tree node is a schema (not a style), enable the
button; otherwise disable. Current wiring in
`Style_CustomizeSchemesDlgProc` already distinguishes the two cases.
- On click: for every `EDITSTYLE` in `pLexCurrent->Styles[]`, copy
`pszDefault` into `szValue`; also reset `szExtensions` to
`pszDefExt`. Refresh the preview.
- No backup/undo required — the existing `pStylesBackup` captured on
dialog open already covers Cancel.
**Risk.** Low. UI addition only.
#### E-03 — Drop `SelectDlgSize*` from theme exports (closes I-10)
**Motivation.** `SelectDlgSizeX` / `SelectDlgSizeY` are remembered
ListView dimensions for the *Select Scheme* dialog. They are unrelated
to styling and pollute every exported theme.
**Change.**
Two reasonable variants — pick one:
1. **Move them out of `[Styles]`** into `[Settings]` where other UI
remembered-size values already live. Keeps the INI cleaner.
2. **Skip writing them** inside `Style_ToIniSection()` when invoked by
the *Export* path. Easiest: add a boolean flag to
`Style_ToIniSection(bool bForceAll, bool bIncludeUIState)` and pass
`false` from `Style_Export()`.
Variant 2 is minimally invasive; Variant 1 is cleaner long-term. A
future session should pick one and stick with it.
**Risk.** Low. Either variant is easy to revert if feedback is
negative.
#### E-04 — Tooltip documenting `\regex` extension syntax (closes I-12)
**Motivation.** `\^CMakeLists$` style regex entries in `FileNameExtensions`
are a real feature, implemented in `Style_RegExMatchLexer()`
(`src/Styles.c:2543`), but the UI never mentions it.
**Change.**
- Add a tooltip (`TOOLTIPS_CLASS`) to the `IDC_STYLEEDIT_ROOT` control
in `Style_CustomizeSchemesDlgProc`.
- Text (new localised string `IDS_MUI_EXTLIST_TOOLTIP`): "Semicolon-
separated. Prefix with `\` for a regex matched against the full file
name, e.g. `\^CMakeLists$`."
- Add the string to all 26 locale files.
**Risk.** Trivial.
#### E-05 — Theme-file header comment (closes part of I-07)
**Motivation.** Shared theme files currently have no authorship or
version metadata. A single leading comment line is cheap.
**Change.**
- In `Style_ExportToFile()` (when `bForceAll=true` path), before calling
`SaveIniFileCache()`, prepend a single comment line to the cache via
the existing IniSection helpers — e.g. via a new helper
`IniFileSetHeaderComment()` if one doesn't exist, otherwise write the
line as a key in a throwaway `[; meta]` section ordering hack.
- Line format: `; Notepad3 theme — exported YYYY-MM-DD from Notepad3
<version>`.
- Check that existing `LoadIniFileCache()` tolerates a leading comment
before the first section header (it should — standard INI).
**Risk.** Low; verify that Windows INI APIs (if used anywhere — the code
uses a custom parser) don't choke.
#### E-06 — Remove documented dead code (closes I-16, part of I-09)
**Motivation.** `//~`-prefixed lines accumulate without a decision.
**Change.**
- Delete the commented `ChooseFontDirectWrite` declaration near
`Styles.c:50` if the MUI/ChooseFont path is the intended permanent
solution.
- Either implement the commented `Style_StrGetAlpha` alpha2 branch for
the Whitespace style (`src/Styles.c:~1626`, `~4489`) or delete it
along with the matching "expose alpha2 for Whitespace" TODO. A
comment in the source explaining *why* it's not supported (e.g.
"Scintilla does not honour alpha2 on SCE_STANDARDWHITESPACE") would
also close the loop.
**Risk.** Low. Cleanup only.
---
### Tier 2 — Medium features (12 days each)
#### E-07 — Decouple `FileNameExtensions` from theme Import (closes I-05)
**Motivation.** The current Import behaviour forces users to choose
between colours and their accumulated extension tweaks. Most real-world
"themes" shared online are colour-only.
**Change.**
- In `Style_Import()` (`src/Styles.c:714`), before the
`FileOpenDlg` call, show a small confirmation dialog or use
`FOS_` custom-place extension with a checkbox: *"Also import file-name
extensions"*, default **off**.
- Propagate the flag into `Style_ImportFromFile(HPATHL, bool
bImportExtensions)`.
- Inside `_ReadFromIniCache()`, gate the call to
`Style_FileExtFromIniSection()` on this flag.
- Keep the old signature as a `static` inline caller that passes the
legacy `true` for startup / theme-switch paths where the extension
set must follow.
- **Symmetry:** add the mirror "include extensions" checkbox to Export
too — see E-09.
**Risk.** Medium. The Import path also runs on theme switch; ensure
that Factory Reset and Theme switching still reset extensions. Default
`true` on those paths, `false` only on explicit *Import…* button.
#### E-08 — Dynamic `Theme_Files[]` (closes I-03)
**Motivation.** 24-theme cap is arbitrary and not even documented.
Some users have 40+ curated themes.
**Change.**
- Replace the fixed `THEMEFILES Theme_Files[25]` (`src/Styles.c:374-400`)
with a heap-allocated grow-on-demand array. Suggested: a local helper
`ThemeList` wrapping a `THEMEFILES*` pointer and a size_t count.
- Update every reference: `ThemeItems_CountOf()`, `ThemesItems_Init()`,
`ThemesItems_Release()`, `ThemesItems_MaxIndex()`, the loop in
`_FillThemesMenuTable()`, and `Style_InsertThemesMenu()`.
- Keep `extern "C" THEMEFILES Theme_Files[]` compatibility by exposing
a `Theme_Files_Get(unsigned i)` accessor, or by turning the external
reference (`src/Config/Config.cpp:77`) into a function call.
- Bound at something sane (e.g. 256) to avoid accidental DoS on a
poisoned `themes\` directory.
**Risk.** Medium. Touches many call sites; needs care with menu
command ID ranges (`IDM_THEMES_STD_CFG + i`) — decide whether to keep
consecutive IDs (cap at a known range) or switch to a single
reserved range checked in the WM_COMMAND handler.
#### E-09 — Export filter: styles vs styles+extensions (pairs with E-07)
**Motivation.** Symmetry with the Import change; authors can now
publish *colours only* themes cleanly.
**Change.**
- Add a checkbox "Include file-name extensions" to the Export dialog
(use `CustomizeSaveFileDialog` or bundle it into a small pre-dialog
that then launches `FileSaveDlg`).
- Propagate into `Style_ExportToFile(HPATHL, bool bForceAll, bool
bIncludeExtensions)`.
- Inside that function, gate the `Style_FileExtToIniSection()` call.
**Risk.** Low.
#### E-10 — Import validation (closes I-13)
**Motivation.** Loading a random INI as a theme silently succeeds,
overwriting in-memory styles with partial or unrelated data.
**Change.**
- Before calling `_ReadFromIniCache()`, peek at the cache to confirm
the imported file contains at least one of: `[Common Base]`,
`[Styles]`, or any per-schema section that matches a known
`g_pLexArray[]` name.
- If none found, show `InfoBoxLng(MB_ICONWARNING, ...,
IDS_MUI_IMPORT_NOT_A_THEME)` and abort without touching in-memory
state.
- Add the string to all 26 locale files.
**Risk.** Low.
#### E-11 — Handle missing theme file gracefully (closes I-14)
**Motivation.** If a theme file referenced by `Settings.CurrentThemeName`
was deleted between sessions, Notepad3 silently falls back to Standard
Config without informing the user — changes made "to the active theme"
after that point land in `Notepad3.ini`, not the expected file.
**Change.**
- In `_FillThemesMenuTable()` (`src/Styles.c:438`), remember whether
the stored `Settings.CurrentThemeName` was actually matched.
- If not matched and non-empty, show a one-shot info message at
startup: *"Theme '<name>' was not found in `themes\`. Reverting to
Standard Config."*
- Gate under a `Settings2.NoThemeMissingWarning` flag so power users
can suppress it.
**Risk.** Low. Add a new `Settings2` key — remember to document it per
the project's memory note on new parameters.
---
### Tier 3 — Larger features (multi-day)
#### E-12 — Per-schema extension priority (closes I-11)
**Motivation.** Today, if two schemas claim `.h`, whichever appears
first in `g_pLexArray[]` wins. Users who prefer C over C++ for `.h`
(or vice versa) can't express it.
**Change.**
- Add an optional integer key `Priority=<n>` inside each per-schema
INI section. Default 0 when absent.
- In `Style_MatchLexer()` (`src/Styles.c:2517`), when searching by
extension with `bCheckNames=false`, collect all matches and return
the one with highest priority; tie-break by array index as before.
- Surface in Customize Schemes: a numeric spinner on the schema node
page.
**Risk.** Medium. Touches the hot detection path; add a short-circuit
fast path for "exactly one match" (today's common case) to avoid a
double scan.
#### E-13 — Named custom-colour references (closes I-17 partially)
**Motivation.** The 16-slot custom-colour palette is currently isolated
from style strings; it's only used by the colour-picker dialog. Letting
users reference slots from style strings (`fore:@3`) would enable
trivial "palette-swap" themes: change 16 lines, re-theme everything.
**Change.**
- Extend `Style_StrGetColor()` (`src/Styles.c:~3476`) to accept `@N`
(1-16) as an alternative to `#RRGGBB`. Resolve via
`Globals.crCustom[N-1]`.
- When writing styles via the dialog, continue to emit `#RRGGBB` (so
pre-existing themes stay pre-existing). Only users who opt in by
hand-editing get the `@N` form.
- Add documentation (both in `CustomSchema.md` and an in-dialog
tooltip) that `@N` resolves at draw time and follows changes to the
custom palette.
**Risk.** Medium. Watch for cases where a style is evaluated before
`Globals.crCustom` is populated (early startup). Add a guard that
falls back to the compiled-in default colour in that window.
#### E-14 — Schema-detection cache (closes I-19)
**Motivation.** `Style_MatchLexer()` is O(n × tokens) per file open.
For a batch open of 1000 files, this adds up.
**Change.**
- Maintain a `WCHAR *ext → EDITLEXER*` hash map built once per session
from the schema extension lists, rebuilt when Customize Schemes
commits edits.
- `Style_MatchLexer()` first checks the hash, only falling through to
the existing regex / shebang / sniff pipeline when the hash misses.
- Use `uthash` (`src/uthash/`) to stay consistent with the project's
existing dependencies.
**Risk.** Medium. Must invalidate on every `_ApplyDialogItemText()`
for the extension field and after every theme switch.
---
### Tier 4 — Nice-to-have / discussion
- **E-15 — Custom-colour load/save symmetry for dark vs light defaults
(I-17).** Today `s_colorLightDefault[]` / `s_colorDarkDefault[]` are
compile-time constants; the palette is reset on factory reset. Allow
persisting user-tweaked defaults per dark/light mode to match
Windows' adaptive behaviour.
- **E-16 — User-defined highlight classes (I-18).** Huge undertaking —
would require extending `EDITSTYLE` to be dynamically allocated
per-lexer, teaching the INI parser about user-added keys, and
wiring Scintilla indicator slots. Out of scope for a normal
session; noted here only for completeness.
- **E-17 — Lazy theme scanning (I-20).** Defer the `FindFirstFile`
sweep of `themes\` to the first time the user opens the *View →
Themes* submenu. Negligible savings for most users; probably not
worth it.
- **E-18 — Cancel-revert for extension edits (I-15).** Unverified
claim; needs a quick read of `Style_CustomizeSchemesDlgProc`'s
Cancel path. If the Cancel handler only restores `szValue` and not
`szExtensions`, restore both. Two-liner fix if the issue is real.
## 5. Verification plan (shared across items)
For any change that touches style I/O:
1. **Round-trip test.** Save a reference file list with heavy theming
(e.g. `Dark.ini`), load it, save again — the second save must be
byte-identical except for the known `SelectDlgSize*` / timestamp
differences.
2. **Locale coverage.** For every new `IDS_MUI_*` string, run
`Build/rc_to_utf8.cmd` and confirm all 26 locale `strings_*.rc`
files still load (no BOM, CRLF preserved — see project memory
note).
3. **Theme-switch smoke test.** Switch Standard Config → Dark →
Obsidian → Standard Config; confirm `Settings.CurrentThemeName`
is persisted and the menu radio follows.
4. **Customize Schemes smoke test.** Open the dialog, edit several
styles, click Cancel — verify that the edits are reverted (nothing
leaks to INI on a subsequent Save).
5. **Missing-theme test.** Rename `themes\Dark.ini` to something else
between sessions; ensure E-11's warning fires exactly once.
6. **ARM64 smoke.** Theme switching invokes a lot of redraws — verify
no flicker regression on ARM64 (relevant because of
`RenderingTechnology=2` default there — see
`readme/config/Configuration.md`).
## 6. Documentation hooks
Whenever any of the enhancements above lands, update:
- `readme/schema/CustomSchema.md` — user-facing behaviour.
- `readme/config/Configuration.md` — new `Settings2` keys (E-11, and
anything else that introduces one).
- `CLAUDE.md` and `.github/copilot-instructions.md` — if the change
introduces a new pattern worth guiding future contributions toward.
- `Build/Notepad3.ini` — commented-out entries for any new INI keys,
per the project's "document new parameters" convention.
## 7. Out of scope (explicitly)
- Renaming schemas or reordering `g_pLexArray[]`. The array order is
load-bearing for current-session INI compatibility.
- Switching away from Scintilla / Lexilla lexers.
- Adding a full-blown theme editor GUI separate from Customize
Schemes.
- Making style keys user-extensible (E-16 above is documented but
deliberately parked).

View File

@ -78,6 +78,13 @@ Available languages:
#### `IMEInteraction=0`
Scintilla IME composition mode:
- `-1` — Auto-detect (one-shot at startup): Korean code pages (`949`, `1361`) → inline (`1`); all other languages → windowed (`0`).
- `0``SC_IME_WINDOWED`: IME composition shown in a separate floating window.
- `1``SC_IME_INLINE`: composition happens inline at the caret. Recommended when working with Far East languages that emit long composition strings.
Range: `-1``1`.
### Date & Time
#### `DateTimeFormat=`
@ -115,6 +122,8 @@ Default extension for saved files (omit the leading dot).
#### `DenyVirtualSpaceAccess=0`
Set to `1` to disable virtual-space access (the area beyond each line's end). With access denied, rectangular (column) selections cannot extend past the shortest line end. Default (`0`) allows rectangular selection to span virtual space — useful for aligning columns across lines of varying length.
#### `filebrowser.exe=minipath.exe`
External program launched by the Browse toolbar button. Defaults to MiniPath.
@ -269,6 +278,14 @@ Whether to prompt for save when closing an untitled document that contains only
#### `SciFontQuality=3`
Scintilla font rendering quality:
- `0` — Default (system-chosen)
- `1` — Non-antialiased
- `2` — Antialiased
- `3` — LCD-optimized (ClearType; default)
Range: `0``3`. A per-resolution override is supported in the `[Window]` section via a key named `<ResX>x<ResY> SciFontQuality=<n>` (e.g. `1920x1080 SciFontQuality=2`), so different monitor layouts can use different font quality settings.
#### `SimpleIndentGuides=0`
Set to `1` to prevent indentation guides from jumping across empty lines.
@ -293,10 +310,29 @@ Set to `true` for old selection behavior: single click selects the visible sub-l
#### `LaunchInstanceWndPosOffset=28`
Pixel offset cascade applied to each new Notepad3 window launched as an additional instance. Each successive window is shifted by this many pixels (both X and Y) relative to the previous one, so overlapping windows remain distinguishable. Range: `-10000``10000`. Set to `0` to spawn new windows at the exact same position.
#### `LaunchInstanceFullVisible=true`
When `true` (default), newly launched windows are clamped so they remain fully visible on the target monitor, even if the `LaunchInstanceWndPosOffset` cascade would push them off-screen. Set to `false` to allow partially off-screen positions (e.g. if you rely on the OS to handle multi-monitor placement).
### Appearance
#### `RenderingTechnology=1`
Scintilla rendering back-end:
| Value | Name | Description |
|-------|------|-------------|
| `0` | GDI | Classic Win32 rendering |
| `1` | DirectWrite | Direct2D (default on x86/x64) |
| `2` | DirectWriteRetain | Direct2D preserving the back buffer between frames (default on ARM64 to prevent flicker on Qualcomm Adreno GPUs and the Win11 25H2 DWM compositor) |
| `3` | DirectWriteDC | DC-based Direct2D |
Also configurable via **Menu → View → Rendering Technology**. A per-resolution override is supported in the `[Window]` section, e.g. `1920x1080 RenderingTechnology=0`, so a docked laptop and an external 4K monitor can use different back-ends.
**Tip:** On ARM64 systems showing rendering glitches, try `0` (GDI) or `3` (DirectWriteDC) as a workaround.
#### `UseOldStyleBraceMatching=0`
Set to `1` for legacy brace-matching style.
@ -307,10 +343,16 @@ Display scale percent threshold to switch to larger file-type icons.
#### `DarkModeBkgColor=0x1F1F1F`
Dark-mode window background color. The three `DarkMode*Color` entries use Win32 `COLORREF` byte order — `0xBBGGRR` (blue, green, red), **not** the usual HTML `0xRRGGBB`. So `0x1F1F1F` is a near-black neutral, and e.g. `0x0000FF` is pure red, not pure blue.
#### `DarkModeBtnFaceColor=0x333333`
Dark-mode button face color. Same `0xBBGGRR` byte order as above.
#### `DarkModeTxtColor=0xEFEFEF`
Dark-mode text color. Same `0xBBGGRR` byte order as above.
### Web Actions
#### `WebTemplate1=https://google.com/search?q=%s`
@ -385,10 +427,16 @@ Font priority list for the "Text Files" scheme.
#### `UpdateDelayMarkAllOccurrences=50`
Debounce interval (milliseconds) before the "mark all occurrences" highlight is recomputed after a document change. Lower values feel more responsive but cost more CPU on large documents; higher values reduce flicker while typing. Range: ~`20``10000` ms (the lower bound is clamped to `USER_TIMER_MINIMUM * 2`, typically around 20 ms).
#### `CurrentLineHorizontalSlop=40`
Horizontal caret slop in columns, passed to Scintilla's `SCI_SETXCARETPOLICY`. Defines the minimum horizontal gap (in character cells) the caret keeps from the left/right edge of the view before the view starts to scroll horizontally. Larger values keep more surrounding context visible; `0` disables the slop. Range: `0``240`.
#### `CurrentLineVerticalSlop=5`
Vertical caret slop in lines, passed to Scintilla's `SCI_SETYCARETPOLICY`. Defines the minimum vertical gap (in whole lines) the caret keeps from the top/bottom of the view before the view scrolls. `0` forces the "even caret" policy (caret always centered-ish); positive values set a fixed top/bottom margin. Range: `0``100`.
#### `UndoTransactionTimeout=0`
Timeout in milliseconds. Set to `1` (clamped to 10 ms minimum) to make nearly every keystroke a separate undo action. `0` disables the timer.
@ -424,7 +472,9 @@ Bias added to confidence when the system's ANSI code page matches the detected e
Enable `#` as a line-comment character in MySQL-dialect SQL. Set to `0` to disable.
#### `AtomicFileSave=true;`
#### `AtomicFileSave=true`
When `true` (default), `Save` writes to a temporary file alongside the target and then atomically replaces the original via `ReplaceFileW`. This preserves the original file's ACLs, alternate data streams, and attributes on failure (nothing is lost if the write is interrupted). Set to `false` to write in-place, which is faster on very large files but offers no rollback if the write fails mid-way. During Windows session end (`WM_ENDSESSION`) the atomic path is bypassed regardless of this setting.
#### `ExitOnESCSkipLevel=2`

View File

@ -0,0 +1,411 @@
# Notepad3 Schemas, Styles & Themes
This document describes how Notepad3's appearance customization works: what a *schema* (a.k.a. *lexer style set*) is, how styles are layered, how the style mini-language reads, how to export / import / switch themes, and where the user-editable files live.
Audience: advanced users who want to tweak colours and fonts beyond the built-in dialog, share a theme with others, or understand why a particular setting isn't sticking.
---
## Table of contents
1. [Core concepts](#1-core-concepts)
2. [Layered override model](#2-layered-override-model)
3. [The style mini-language](#3-the-style-mini-language)
4. [The default schemas (`Common Base` & `2nd Common Base`)](#4-the-default-schemas-common-base--2nd-common-base)
5. [File → schema matching](#5-file--schema-matching)
6. [The Customize Schemes dialog](#6-the-customize-schemes-dialog)
7. [Themes collection (`View → Themes`)](#7-themes-collection-view--themes)
8. [Export / Import](#8-export--import)
9. [Anatomy of a style INI file](#9-anatomy-of-a-style-ini-file)
10. [Toolbar themes (separate from colour schemes)](#10-toolbar-themes-separate-from-colour-schemes)
11. [References in the code](#references-in-the-code)
---
## 1. Core concepts
| Term | Meaning |
|------|---------|
| **Schema** / **Scheme** | A complete set of styles for one language (e.g. *C/C++ Source Code*, *Python Script*). Backed in code by an `EDITLEXER` record plus an array of `EDITSTYLE` entries. |
| **Style** | A single styleable element inside a schema (e.g. *Default Style*, *Keywords*, *Comments*, *Strings*). Each style has a plain-text *style string* describing its font, size, colours, etc. |
| **Theme** | A complete snapshot of all custom colours, schemas and per-schema extensions, stored as a standalone `.ini` file (e.g. `Dark.ini`). Switching themes replaces every schema's style strings at once. |
| **Lexilla lexer** | The underlying syntax-highlighting engine for a schema. Notepad3 ships with ~60 lexers (C++, Python, Markdown, Rust, YAML, …). |
| **Custom colours** | A 16-slot colour palette stored per-installation. Used by the *Customize Schemes* colour-picker and exported with every theme. |
Notepad3 ships with roughly 60 schemas; the exact list appears in the *Select Scheme* dialog (**Ctrl+F11**) and in the left-hand tree of *Customize Schemes* (**F12**).
---
## 2. Layered override model
Every style string you see in the editor is the result of a four-level resolution chain, applied each time a file is opened or a theme is switched:
```
┌─────────────────────────────────────────────────────────────┐
│ 4. Runtime edits (Customize Schemes dialog) │ ← highest priority, in-memory only
├─────────────────────────────────────────────────────────────┤
│ 3. User INI — either Notepad3.ini [SchemaSection] │
│ or the currently loaded theme file (themes/<Name>.ini) │
├─────────────────────────────────────────────────────────────┤
│ 2. 'Common Base' / '2nd Common Base' defaults │
│ (font, size, fg/bg for everything unstyled) │
├─────────────────────────────────────────────────────────────┤
│ 1. Hard-coded lexer defaults (compiled into Notepad3.exe) │ ← lowest priority
└─────────────────────────────────────────────────────────────┘
```
- **Lower levels fill gaps only.** If a user INI defines `fore:#ff0000` for a style but omits `font:`, the font comes from the Common Base, and ultimately from the hard-coded default.
- **Only deltas are saved.** When Notepad3 writes your styles back, it stores *only* values that differ from the merged defaults. An exported theme, by contrast, can force-save everything — see [§8 Export/Import](#8-export--import).
- **Empty lexer sections are removed.** After saving, any `[Python Script]`-style section that ended up empty (because the user reset everything to default) is stripped so the file stays readable.
- **Dark mode is its own level 2.** When Windows is in dark mode, the *Common Base* defaults come from a dark-mode palette instead of the light one. Your custom deltas stack on top of whichever palette is active.
### Where do my custom changes land on disk?
| Active theme | Changes written to |
|---|---|
| *Standard Config* (index 0) | `Notepad3.ini`, sections `[Styles]`, `[Custom Colors]`, and per-schema sections like `[C/C++ Source Code]`, `[Python Script]`, … |
| A named theme (e.g. *Dark*) | `<IniFileDir>\themes\Dark.ini` (full dump — all styles, not just deltas) |
Saving occurs only if `Settings.SaveSettings = 1` (Menu **Settings → Save Settings On Exit**) or you choose **Settings → Save Settings Now** (**F7**).
---
## 3. The style mini-language
Every `EDITSTYLE::szValue` is a **semicolon-delimited list of `attribute:value` tokens**. Order is irrelevant, keys are case-insensitive, and spacing around `:` and `;` is tolerated:
```
font:Consolas; size:11; fore:#D7BA7D; back:#1E1E1E; bold
```
Buffer limit: **255 characters** per style string. Longer strings are silently truncated — see [§11 Limitations](#11-known-limitations--quirks).
### 3.1 Attributes
| Token | Purpose | Examples |
|-------|---------|----------|
| `font:<name>` | Font face. Literal face names or the aliases `Default`, `$Code0…$Code9`, `$Text0…$Text9` which resolve to entries of `CodeFontPrefPrioList` / `TextFontPrefPrioList` (`Settings2`). | `font:Consolas`, `font:$Code0` |
| `size:<pt>` or `size:±<pt>` | Absolute (`11`) or relative (`+2`, `-1`) font size in points. Relative sizes cumulate across the inheritance chain. | `size:10`, `size:+1` |
| `fore:#RRGGBB` | Foreground (text) colour, hex. `#AARRGGBB` also accepted on styles that respect alpha. | `fore:#569CD6` |
| `back:#RRGGBB` | Background colour, hex. `#AARRGGBB` also accepted. | `back:#1E1E1E` |
| `bold` / `italic` / `underline` / `strikeout` | Plain flags, no value. | `italic` |
| `eolfilled` | Extends the background colour to the end of the line. | `eolfilled` |
| `case:u` \| `case:l` \| `case:m` | Force upper / lower / mixed case (visual only). | `case:u` |
| `charset:<n>` | LOGFONT charset id (0 = ANSI, 128 = Shift-JIS, 186 = Baltic, 204 = Russian, …). Clamped to ≥ 0. | `charset:204` |
| `smoothing:<q>` | Font quality. Tokens: `default`, `standard`, `aliased`, `antialiased`, `cleartype`. | `smoothing:cleartype` |
| `alpha:<0-255>` | Primary alpha (indicator / overlay opacity). | `alpha:40` |
| `alpha2:<0-255>` | Secondary alpha (outline for box-type indicators). | `alpha2:220` |
| `weight:<name>` | Font weight. Accepted words: `thin`, `extralight`, `light`, `book`, `regular`, `medium`, `semibold`, `bold`, `extrabold`, `heavy`, `extrablack`, plus a handful of synonyms. | `weight:semibold` |
| `stretch:<name>` | Font stretch: `ultracondensed`, `extracondensed`, `condensed`, `semicondensed`, `normal`, `semiexpanded`, `expanded`, `extraexpanded`, `ultraexpanded`. | `stretch:condensed` |
| `block` | Draw caret as a block. | `block` |
| `hotspot` | Mark the style as a hotspot (clickable region). | `hotspot` |
| `indic_<shape>` | Indicator drawing style (used by hotspot / brace / occurrence markers). Common values: `indic_plain`, `indic_squiggle`, `indic_tt`, `indic_diagonal`, `indic_strike`, `indic_box`, `indic_roundbox`, `indic_straightbox`, `indic_fullbox`, `indic_dash`, `indic_dots`, `indic_squigglelow`, `indic_squigglepixmap`, `indic_compositionthick`, `indic_compositionthin`, `indic_textfore`, `indic_point`, `indic_pointcharacter`, `indic_gradient`, `indic_gradientcentre`. | `indic_roundbox` |
### 3.2 Colour syntax
- Always `#` followed by six hex digits (`#RRGGBB`) — **not** `0xBBGGRR` as in the dark-mode INI keys.
- Named colours are **not** supported; use hex.
- Colours stored in the *Custom Colors* palette (the 16 slots at the top of any theme file) are written as `#RRGGBB` strings with keys `01`…`16` and are just a convenience shared with the Windows colour-picker dialog — they are not referenced from style strings.
### 3.3 Example — styling comments in C++
```
Comment=italic; fore:#608B4E; back:#1E1E1E
```
- Italic, no explicit font (inherits Common Base), explicit fg and bg.
- Key (`Comment`) matches a fixed slot inside `styleLexCPP.c`. You cannot invent new style names; you edit the ones the lexer defines.
---
## 4. The default schemas (`Common Base` & `2nd Common Base`)
Two "super-schemas" act as the global fallback for *every* language-specific schema:
| Schema | INI section | Role |
|--------|-------------|------|
| `Common Base` | `[Common Base]` | Primary defaults — applied to every style that does not explicitly set an attribute. |
| `2nd Common Base` | `[2nd Common Base]` | Alternate set, activated by **Menu → View → Use 2nd Default Style** (`IDM_VIEW_USE2NDDEFAULT = 41002`). |
Both schemas also contain the universal editor chrome styles (they are not language specific):
- `Default Style` — base font, size, fg, bg.
- `Margins and Line Numbers` — line-number gutter.
- `Matching Braces (Indicator)` / `Matching Braces Error (Indicator)` — brace highlight.
- `Control Characters` — the `[NUL]`, `[BS]` etc. glyphs.
- `Indent Guide` — vertical dotted lines.
- `Selection` — selection highlight (foreground/background, with alpha).
- `Whitespace` — dot/arrow/EOL glyphs.
- `Current Line` — background highlight of the line containing the caret.
- `Caret` — colour + width, `block` modifier.
- `Long Line Marker` — visual for the right-margin line.
- `Bookmark` / `Folding` colours.
- `Mark Occurrences` — the highlight colour of *Mark All Occurrences*.
- `Hotspot Style` / `Unicode Hotspot` / `Multi-Edit` — interactive overlays.
- `Change History` markers (modified/saved/unsaved).
- `IME Input` colour.
- `Invisible` / `Read-only` overlays.
### Switching with `Use 2nd Default Style`
The status bar shows **STD** or **2ND** in the INS/OVR field. Double-click there (or use the menu) to toggle. The active state is saved as:
```ini
[Styles]
Use2ndDefaultStyle=1
```
Both base schemas coexist in the INI: toggling only flips which one Notepad3 reads.
---
## 5. File → schema matching
When you open a file, Notepad3 runs a pipeline to pick the right schema. It stops at the first match.
1. **File variables** (`vim:` / `emacs:` modelines inside the file). If the file declares `mode: python;` or `-*- mode: cpp -*-`, the declared mode name is matched against schema names (case-insensitive prefix) and then against extension lists. Disabled by `Settings2.NoFileVariables=1`.
2. **Shebang detection** for `.cgi` / `.fcgi` files or files flagged as CGI by mode. Recognised interpreters: `python`, `ruby`, `bash`/`sh`, `perl`, `tcl`, `node`/`js`, `php`. Disabled by `Settings2.NoCGIGuess=1`.
3. **Regex match on the file name.** Any entry in a schema's extension list that starts with a backslash is treated as a regex — e.g. `\^CMakeLists$` matches the file name `CMakeLists.txt` once combined with the bare `txt` entry below.
4. **Plain extension match.** First lexer whose extension list contains the file's extension wins. Extension comparison is case-insensitive; separator is `;` (semicolon or space both work in practice).
5. **HTML/XML sniff** — if the first bytes start with `<`, classify as HTML or XML. Disabled by `Settings2.NoHTMLGuess=1`.
6. **Shebang fallback** for extension-less files (same recognisers as step 2).
7. **ANSI-art fallback** — DOS/OEM-encoded files fall back to the ANSI Art lexer.
8. **Default schema.** Whatever is set as *Default Scheme* in the *Select Scheme* dialog (default: *Pure Text Files*).
### The two toggles in the *Select Scheme* dialog
| Checkbox | INI key | Meaning |
|---|---|---|
| **Set as default** | `[Styles] DefaultScheme=<n>` | The schema used at startup and when nothing else matches. |
| **Automatic detection** | `[Styles] AutoSelect=0\|1` | When off, steps 37 above are skipped. File → schema association is based solely on extension. |
### Extension lists
Each schema has a hard-coded default extension list (compiled in), and a user-editable override stored in the schema's INI section as:
```ini
[Python Script]
FileNameExtensions=py;pyw;pyi;\^setup\.py$
```
The override replaces the default completely (not merged). Clearing the field in *Customize Schemes* restores the default. Buffer limit: **~512 characters per schema**.
> If two schemas both claim the same extension, the one that appears first in the internal schema array wins. There is currently no UI to reorder schemas.
---
## 6. The Customize Schemes dialog
- Launch: **Menu → View → Customize Schemes…** (`IDM_VIEW_SCHEMECONFIG = 41003`) or **F12**.
- Modeless — you can keep it open while editing files and see live preview.
### Tree
Every schema appears in the left-hand tree. Expand a schema to see its style slots. Selecting:
- **a schema node** lets you edit the *FileNameExtensions* field for that schema.
- **a style node** lets you edit the raw style string plus helpers for font, foreground, and background.
### Editing aids
| Control | Role |
|---|---|
| *Style* text field | Free-form style string — full mini-language from [§3](#3-the-style-mini-language). |
| *Font…* button | Opens the common ChooseFont dialog and writes `font:`, `size:`, `weight:`, `italic` back into the text field. |
| *Foreground* / *Background* | Colour pickers; write `fore:#…` / `back:#…` tokens. |
| *Default* | Resets the current style to its compiled-in default (discards the INI override for that style only). |
| *Preview* | Toggles live preview of your edits in the main editor. |
| *< Prev / Next >* | Step through the styles of the current schema. |
| *Import…* | See [§8](#8-export--import). |
| *Export…* | See [§8](#8-export--import). |
| *Dark Mode Highlight Contrast* (slider/field) | Adjusts how strongly foreground colours are re-mapped for legibility against the dark background. Stored as `Settings.DarkModeHiglightContrast`. |
### Cancel vs OK
Clicking **Cancel** reverts every style string that was modified during this dialog session (a backup is taken on open). Closing the window via the title-bar X behaves the same as Cancel.
### Drag-and-drop
Dragging a *style* node onto another *style* node copies the dragged style's string into the target. Dragging a *schema* node is blocked (the cursor changes to the *no-drop* glyph). Drag-and-drop is therefore a quick "use Comments' look for Documentation-Comments" shortcut — not a reordering tool.
---
## 7. Themes collection (`View → Themes`)
A theme is a standalone INI file holding a snapshot of every schema's styles. Themes live in:
```
<IniFileDir>\themes\*.ini
```
where `<IniFileDir>` is the directory that contains the active `Notepad3.ini`.
### Shipped themes
The release package includes three reference themes under `Build/themes/`:
- `Dark.ini`
- `Obsidian.ini`
- `Sombra.ini`
Copy any of them next to your `Notepad3.ini` (inside a `themes` folder) and they will appear in **View → Themes** on next launch.
### Menu layout (`View → Themes`)
```
Factory Reset ← reloads compiled-in defaults, discards INI/theme
--------------
● Standard Config ← Notepad3.ini itself (always present, always first)
Dark
Obsidian
Sombra
<your-theme>
```
- **Factory Reset** (`IDM_THEMES_FACTORY_RESET = 37001`) discards the current session's style overrides and reloads the compiled-in defaults. It does *not* delete your INI file, but if you save afterwards, the deltas will be rewritten.
- **Standard Config** (`IDM_THEMES_STD_CFG = 37002`) is the internal name for "styles in Notepad3.ini".
- Each theme file adds a menu item with consecutive IDs (`STD_CFG + 1`, `+2`, …).
### Capacity
The theme table is allocated with **25 slots** total — slot 0 for *Standard Config*, slots 1-24 for theme files. If more than 24 `.ini` files exist in `themes\`, the extras are silently ignored (the ones scanned first by `FindFirstFile` win).
### Switching themes
Selecting a theme from the menu triggers this sequence:
1. If **Save Settings On Exit** is on, save the *current* theme first (to `Notepad3.ini` for Standard Config, to the theme file otherwise — with `bForceAll = true`, i.e. every style written in full).
2. Reset the INI file cache.
3. Load the new theme's `.ini` (or the compiled-in defaults for **Factory Reset**).
4. Update `Settings.CurrentThemeName` and tick the chosen menu radio.
`Settings.CurrentThemeName` persists the choice across restarts. If the file disappears before the next launch, Notepad3 falls back to *Standard Config*.
### Creating a new theme
Easiest flow:
1. Get your styles looking the way you want (Customize Schemes, regular save).
2. **View → Customize Schemes… → Export…**, save as `<IniFileDir>\themes\MyTheme.ini` (see [§8](#8-export--import)).
3. Restart Notepad3. **View → Themes → MyTheme** now appears.
---
## 8. Export / Import
Both actions are in the *Customize Schemes* dialog (**F12**), lower left.
### Export
- File dialog filter: `*.ini` only.
- Written with `bForceAll = true`**every** style is written out in full, regardless of whether it equals the default. Exported files are therefore self-contained and can be shared with anyone.
- Sections written:
- `[Custom Colors]` — 16 palette slots.
- `[Styles]` — global settings: `Use2ndDefaultStyle`, `DefaultScheme`, `AutoSelect`, `SelectDlgSizeX`, `SelectDlgSizeY`.
- `[Common Base]`, `[2nd Common Base]` — super-schemas.
- One section per schema (~60 sections).
- `FileNameExtensions` keys inside each schema section.
### Import
- File dialog filter: `*.ini`.
- Loads the target file into the cache and re-reads style strings out of it. Existing in-memory styles are replaced slot-by-slot for keys present in the imported file; keys missing from the imported file keep their current value.
- Extensions are imported too — **your previous per-schema extension customisations will be overwritten** by whatever is in the imported file.
- Import does *not* persist by itself. Until you save settings (or a later auto-save fires), re-starting Notepad3 reverts to the previously persisted state. Hence the usual recipe: *Import → verify → Save Settings Now* (**F7**).
### Comparison table
| Operation | Scope | What it writes | Uses `bForceAll` |
|---|---|---|---|
| *Save Settings Now* (F7), theme = Standard Config | `Notepad3.ini` | Only deltas from defaults | ✘ |
| *Save Settings Now* (F7), theme = named theme | `themes/<Name>.ini` | Only deltas from defaults | ✘ |
| **View → Themes → <switch>** with SaveSettings on, leaving a *named* theme | `themes/<oldName>.ini` | Every style | ✔ |
| **View → Themes → <switch>** with SaveSettings on, leaving *Standard Config* | `Notepad3.ini` | Only deltas | ✘ |
| *Customize Schemes → Export…* | user-chosen `.ini` | Every style (self-contained) | ✔ |
---
## 9. Anatomy of a style INI file
Whether it is your `Notepad3.ini` or a shipped theme like `Dark.ini`, the schema-related sections follow the same layout:
```ini
[Custom Colors]
01=#252526
02=#1346CE
03=#A2C5D4
; … up to 16 slots
[Styles]
Use2ndDefaultStyle=0
DefaultScheme=2
AutoSelect=1
SelectDlgSizeX=0
SelectDlgSizeY=0
[Common Base]
Default Style=font:$Code0; size:11; fore:#DEDEDE; back:#1E1E1E
Margins and Line Numbers=font:Consolas; size:-2; fore:#A4FFFF; back:#2B2B2B
Matching Braces (Indicator)=fore:#00FF00; alpha:40; alpha2:220; indic_roundbox
Selection=fore:#000000; back:#3F84DB; alpha:50; alpha2:150; eolfilled
Current Line=back:#323232; alpha:60; alpha2:255
Caret=fore:#FFFFFF; block
; …
[2nd Common Base]
2nd Default Style=font:$Code1; size:10; fore:#DCDCDC; back:#000000
; …
[Text Files]
Default Style=font:$Text0; size:11; fore:#E0E0E0
FileNameExtensions=txt;text;log;asc;doc;diz;nfo
[C/C++ Source Code]
Default=fore:#D7D7D7
Comment=italic; fore:#608B4E
Keyword=bold; fore:#569CD6
Type Keyword=fore:#4EC9B0
String=fore:#CE9178
Number=fore:#B5CEA8
Operator=fore:#B4B4B4
Preprocessor=fore:#C586C0
FileNameExtensions=c;cc;cpp;cxx;h;hh;hpp;hxx;inl
; … one section per schema …
```
Observations:
- **Section names** (`[C/C++ Source Code]`, `[Python Script]`, …) are the schema display names as used internally; they are stable across versions but are localised copies of the names shown in the UI.
- **Style keys** inside a section (`Default`, `Comment`, `Keyword`, `Preprocessor`, …) are fixed per schema — you cannot add new ones.
- **Missing keys** fall through to the *Common Base* → hard-coded default chain.
- **`FileNameExtensions`** appears at most once per schema. The list is semicolon-separated; entries beginning with `\` are regexes matched against the full filename.
- **Custom Colors entries** are just hex colours; they populate the 16-slot picker, nothing more.
> **Encoding.** INI files are written in UTF-8. A UTF-8 BOM is tolerated on read. Do not hand-save as UTF-16 — Notepad3's INI parser is 8-bit.
---
## 10. Toolbar themes (separate from colour schemes)
The directories under the project's `themes/` folder (`Flat`, `b&w`, `professional`, `std_scaled`, `c6248`) are **toolbar bitmap themes**, not style themes. They contain `.bmp` files (`Toolbar.bmp`, `ToolbarDisabled.bmp`, `ToolbarHot.bmp`, plus 24- and 48-pixel variants).
The toolbar bitmap in use is chosen via the `[Toolbar Images]` section in `Notepad3.ini` — see [Configuration.md](../config/Configuration.md) for details. This mechanism is entirely independent of the schema/theme system described above.
---
## References in the code
For developers wanting to look up behaviour described above:
| Topic | Primary file(s) |
|---|---|
| Data structures (`EDITLEXER`, `EDITSTYLE`, `KEYWORDLIST`) | `src/StyleLexers/EditLexer.h` |
| Schema array, loading, saving, layering | `src/Styles.c``g_pLexArray[]`, `_ReadFromIniCache()`, `Style_ToIniSection()`, `Style_CanonicalSectionToIniCache()`, `Style_ExportToFile()` |
| Auto-detection pipeline | `src/Styles.c``Style_SetLexerFromFile()`, `Style_MatchLexer()`, `Style_RegExMatchLexer()`, `Style_SniffShebang()` |
| Customize Schemes dialog | `src/Styles.c``Style_CustomizeSchemesDlg()`, `Style_CustomizeSchemesDlgProc()` |
| Select Scheme dialog | `src/Styles.c``Style_SelectLexerDlg()`, `Style_SelectLexerDlgProc()` |
| Theme menu & switching | `src/Styles.c``_FillThemesMenuTable()`, `Style_InsertThemesMenu()`, `Style_DynamicThemesMenuCmd()`, `Style_ImportTheme()` |
| Menu command IDs | `language/common_res.h``IDM_VIEW_SCHEME`, `IDM_VIEW_USE2NDDEFAULT`, `IDM_VIEW_SCHEMECONFIG`, `IDM_THEMES_STD_CFG`, `IDM_THEMES_FACTORY_RESET` |
| Shipped themes | `Build/themes/Dark.ini`, `Obsidian.ini`, `Sombra.ini` |