96 lines
3.5 KiB
Markdown
96 lines
3.5 KiB
Markdown
# UI Theming and CM6 Highlight
|
||
|
||
## Theme Variables
|
||
|
||
- **Global tokens** (in `src/styles.css`):
|
||
- `--color-accent` main accent color (per-theme override via `[data-theme]`).
|
||
- `--cm-hl-bg` computed background for inline highlights (theme dependent).
|
||
- `--cm-hl-br` border radius for highlights (default 3px).
|
||
- **Button tokens**:
|
||
- `--btn-radius`, `--btn-padding-y`, `--btn-padding-x`, `--btn-font`, `--btn-shadow`, `--btn-ring`, `--btn-speed`.
|
||
- `--btn-bg`, `--btn-fg`, `--btn-bg-hover`, `--btn-bg-active`, `--btn-outline`.
|
||
|
||
### Defaults
|
||
|
||
```css
|
||
:root {
|
||
--color-accent: #3b82f6;
|
||
--cm-hl-bg: color-mix(in srgb, var(--color-accent) 28%, transparent);
|
||
--cm-hl-br: 3px;
|
||
}
|
||
[data-theme="dark"], .dark {
|
||
--cm-hl-bg: color-mix(in srgb, var(--color-accent) 22%, transparent);
|
||
}
|
||
[data-theme="nimbus"] { --color-accent: #7c3aed; }
|
||
[data-theme="emerald"] { --color-accent: #10b981; }
|
||
```
|
||
|
||
## CodeMirror 6 Highlight Extensions
|
||
|
||
Files in `src/app/shared/editor/extensions/`:
|
||
- `highlight-occurrences.extension.ts` – inline dynamic occurrences via regex.
|
||
- `ranged-highlights.extension.ts` – ranged highlights via `StateEffect`/`StateField`.
|
||
- `markdown-theme-highlight.extension.ts` – composes ranged + Markdown `HighlightStyle` and facet-driven CSS variable.
|
||
|
||
### CSS class
|
||
|
||
- `.cm-md-highlight { background: var(--cm-hl-bg); border-radius: var(--cm-hl-br, 3px); transition: background var(--btn-speed) ease; }`
|
||
|
||
## Angular Integration
|
||
|
||
Service: `EditorHighlightService` (`src/app/shared/editor/editor-highlight.service.ts`)
|
||
|
||
- `extensions: Extension[]` – base markdown theme highlight extensions to add at editor creation.
|
||
- `occurrencesExtension(pattern)` – build occurrences extension.
|
||
- `applyOccurrences(view, compartment, pattern)` – reconfigure a `Compartment` with occurrences.
|
||
- `setRanges(view, ranges)` / `clearRanges(view)` – ranged highlights via effects.
|
||
- `facet()` – access the facet to optionally override `--cm-hl-bg` per-view.
|
||
|
||
Example inside editor component:
|
||
|
||
```ts
|
||
// compartments
|
||
occurrencesCompartment = new Compartment();
|
||
highlightFacetCompartment = new Compartment();
|
||
|
||
// in EditorState.create extensions
|
||
...highlightService.extensions,
|
||
occurrencesCompartment.of([]),
|
||
highlightFacetCompartment.of([]),
|
||
|
||
// API usage
|
||
highlightOccurrences("TODO");
|
||
setHighlights([{ from: 10, to: 24 }]);
|
||
clearHighlights();
|
||
// optional color override
|
||
view.dispatch({ effects: highlightFacetCompartment.reconfigure([ highlightService.facet().of('color-mix(in srgb, var(--color-accent) 40%, transparent)') ])});
|
||
```
|
||
|
||
Live theme changes are applied by reconfiguring the theme compartment and calling `EditorView.requestMeasure(...)` to refresh decorations that rely on CSS variables.
|
||
|
||
## Button Utilities
|
||
|
||
Classes in `src/styles.css`:
|
||
- Base: `btn`
|
||
- Variants: `btn-solid`, `btn-outline`, `btn-ghost`
|
||
- Sizes: `btn-sm`, `btn-md`, `btn-lg`
|
||
|
||
Example:
|
||
|
||
```html
|
||
<button class="btn btn-solid btn-sm">Save</button>
|
||
<button class="btn btn-outline btn-sm">Cancel</button>
|
||
<button class="btn btn-ghost btn-sm" aria-label="Close">✕</button>
|
||
```
|
||
|
||
## Accessibility
|
||
|
||
- Buttons use focus ring via `--btn-outline`.
|
||
- Ensure icon-only buttons have `aria-label`.
|
||
|
||
## Performance Notes
|
||
|
||
- Occurrence highlights compute decorations only over visible ranges; cost is O(visibleText) per viewport update.
|
||
- Ranged highlights use a `StateField` and `tr.changes.mapPos` to keep ranges aligned after edits; updates are incremental.
|
||
- For large docs (≥5k lines), prefer ranged highlights for bulk operations; occurrences are fine for quick regex patterns like `TODO`.
|