Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4d5e1d1
feat(#141): allow custom markers, propose default colors
tuurep Sep 18, 2024
73bb056
feat(#141): handle unconfigured custom marker same as Obsidian
tuurep Nov 10, 2024
a697994
feat(#141): add alerts plugin source code as a file
tuurep Aug 22, 2025
04fdbe4
feat(#141): always use `markers: '*'`, aka. Obsidian Callout style
tuurep Aug 22, 2025
507ef4d
feat(#141): full handling of custom `octicons`
tuurep Aug 22, 2025
def11e6
feat(#141): allow customizable fallback icon for unknown markers
tuurep Aug 23, 2025
a613ab2
feat(#141): move 'fallback' icon to separate option
tuurep Aug 23, 2025
2cd9b07
feat(#141): shorten alert CSS classname
tuurep Aug 23, 2025
9bb1ffb
feat(#141): change naming to generic 'alerts'
tuurep Aug 23, 2025
f8377e7
feat(#141): allow multi-word markers
tuurep Aug 23, 2025
a1601ad
fix(#141): fix fallback alert styles
tuurep Aug 24, 2025
b82efe5
feat(#141): handle all sources of icons
tuurep Aug 25, 2025
e23cb8f
fix(#141): don't crash server on invalid icon option
tuurep Aug 25, 2025
6cea0e7
feat(#141): make alerts color customization much easier
tuurep Aug 25, 2025
0a2a81e
fix(#141): add default 16x16 size for alert icons
tuurep Aug 27, 2025
fcf53de
docs(#141): document `alertsOptions`
tuurep Aug 27, 2025
64bdaea
docs(#141): mention alerts extensibility in README
tuurep Aug 27, 2025
e44ede7
test(#141): add unit tests for invalid icon configs
tuurep Aug 28, 2025
f68e8b6
fix(#141): handle configBaseDir undefined case
tuurep Aug 28, 2025
a0829c7
refactor(#141): group functions below data
tuurep Aug 28, 2025
76ff7a8
refactor(#141): simplify icons table
tuurep Sep 1, 2025
3838302
fix(#141): change option name, add missing types
tuurep Sep 1, 2025
1a47519
refactor(#141): simplifications & improve readability
tuurep Sep 3, 2025
ce4e80f
feat(#141): match config keys case-insensitively
tuurep Sep 3, 2025
37f1814
test(#141): add positive cases for resolveIcon
tuurep Sep 3, 2025
388c93d
docs(#141): add a guide for alerts customization
tuurep Sep 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,11 @@ issue](https://github.com/jannis-baum/vivify/issues/new/choose) or
- [KaTeX math](https://katex.org)
- [graphviz/dot graphs](https://graphviz.org/doc/info/lang.html)
- [Mermaid diagrams & charts](https://mermaid.js.org)
- [GitHub alert
blocks](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts)
- alert blocks
- default styles like in [GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts)
- [syntax extensions](docs/alerts.md#custom-marker) similar to [Obsidian
callouts](https://help.obsidian.md/callouts)
- configurable [custom alerts](docs/alerts.md#configuring-alert-icons)
- links to other files: [relative links like in
GitHub](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#relative-links)
as well as absolute file links
Expand Down
213 changes: 213 additions & 0 deletions docs/alerts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# Alerts usage and customization

There are five default alerts, that are styled without configuration. These are the
[GitHub
alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts):

```md
> [!NOTE]
> Lorem ipsum
```

```md
> [!TIP]
> Lorem ipsum
```

```md
> [!IMPORTANT]
> Lorem ipsum
```

```md
> [!WARNING]
> Lorem ipsum
```

```md
> [!CAUTION]
> Lorem ipsum
```

## Custom marker

We have extended support similar to [how alerts work in
Obsidian](https://help.obsidian.md/callouts) (where they are called "callouts").
The marker can be a custom multi-word string, like this:

```md
> [!CUSTOM MARKER]
> Lorem ipsum
```

By default, the above alert (with an arbitrary marker) is styled like `[!NOTE]`.
This matches Obsidian's default behavior. If you want to style this separately,
see [customizing fallback alert](#customizing-fallback-alert).

## Custom title

Alert title can optionally be set with the following extended syntax:

```md
> [!NOTE] A custom title
> Lorem ipsum
```

Additionally, custom titles for markers can be set in the configuration:

```json
{
"alertOptions": {
"titles": {
"note": "A custom title"
}
}
}
```

With the above example, `[!NOTE]` would *always* be rendered with the title *A
custom title*.

## Case sensitivity

Marker names are matched case-insensitively, meaning `[!CUSTOM MARKER]`,
`[!custom marker]` and `[!Custom Marker]` refer to the same type of alert.

In the same way, all keys in `alertOptions.icons` and `alertOptions.titles` are
case-insensitive.

## Configuring alert icons

Set custom icons for alerts by marker type like this:

```json
{
"alertOptions": {
"icons": {
"custom": "bell"
}
}
}
```

When set as a string, the icon is interpreted to be a valid
[octicon](https://primer.style/octicons/) name.

This would set the octicon [`bell`](https://primer.style/octicons/icon/bell-16/)
for alert
`[!CUSTOM]`.

### Advanced icon configuration

To use icons other than octicons, the value can be set as a path to an svg
file, for example:

Absolute path:

- `"custom": "/home/user/.config/vivify/icons/flower.svg"`

Home as tilde:

- `"custom": "~/.config/vivify/icons/flower.svg"`

Path relative to Vivify config directory:

- `"custom": "./icons/flower.svg"`

Even a raw svg string can be set in the configuration:

- `"custom": "<svg> ... </svg>"`

## Customizing alert colors

Colors should be customized in your custom stylesheet (via the `"styles"` option
in the [config](customization.md)).

To set any color to a custom alert type:

```css
.alert-custom { --color: #00ff00; }
```

The left-side border, title icon, and title text will be colored as `--color`.

The CSS class is fully lowercased and in `kebab-case`. So for a marker with a
multi-word name, this is how you'd set the color:

```css
.alert-my-multi-word-name { --color: #00ff00; }
```

Instead of coming up with a new hex color, you may want to match to one of the
default alerts' colors. Use one of these CSS variables:

- `--alert-note`
- `--alert-tip`
- `--alert-important`
- `--alert-warning`
- `--alert-caution`

For example:

```css
.alert-custom { --color: var(--alert-important); }
```

## Examples

Here are a few practical examples to achieve some types of alerts that have
default styles in Obsidian, but not on GitHub:

- `[!TODO]`
- `[!QUESTION]`
- `[!EXAMPLE]`
- `[!SUCCESS]`
- `[!FAILURE]`

```json
{
"alertOptions": {
"icons": {
"todo": "check-circle",
"question": "question",
"example": "list-unordered",
"success": "check",
"failure": "x"
}
}
}
```

```css
.alert-todo { --color: var(--alert-note); }
.alert-question { --color: var(--alert-warning); }
.alert-example { --color: var(--alert-important); }
.alert-success { --color: var(--alert-tip); }
.alert-failure { --color: var(--alert-caution); }
```

## Customizing fallback alert

Any custom alert that hasn't been configured will have the same icon and color
as `[!NOTE]`. If you want a whole separate color and icon for unconfigured
alerts, use these options:

```json
{
"alertOptions": {
"fallbackIcon": "alert"
}
}
```

> [!NOTE]
> `"fallbackIcon"` is a separate option outside of the `"icons"` table, but
> accepts the same type of icon value as the [icon
> customization](#configuring-alert-icons).

For the color, any alert that doesn't have a "known" marker gets a CSS class as
`.fallback-alert`. So, to customize the color:

```css
.fallback-alert { --color: #ff0000; }
```
22 changes: 22 additions & 0 deletions docs/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,28 @@ keys:
}
```

- **`"alertOptions"`**\
Options to customize
[alerts](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax#alerts)
beyond the 5 default ones used by GitHub. You may also know this feature by
the name of [Obsidian callouts](https://help.obsidian.md/callouts).

```json
"alertOptions": {
"icons": {
"foo": "zap",
"bar": "./icons/flower.svg"
"titles": {
"bar": "Tip of the day"
},
"fallbackIcon": "alert"
}
```

For alerts customization, see [full explanation with examples](alerts.md).

### Reloading config

Vivify reads the config on startup, i.e. when you want it to reload the config,
you have to restart Vivify. You can do this for example by running

Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"viv": "VIV_PORT=3000 node --loader ts-node/esm src/app.ts",
"lint": "eslint src static",
"lint-markdown": "markdownlint-cli2 --config .github/.markdownlint-cli2.yaml",
"test": "node --loader ts-node/esm tests/unit/cli.ts",
"test": "node --loader ts-node/esm --test tests/unit/cli.ts tests/unit/alerts.ts",
"deduplicate": "yarn-deduplicate"
},
"type": "module",
Expand All @@ -28,7 +28,6 @@
"markdown-it-emoji": "^3.0.0",
"markdown-it-footnote": "^4.0.0",
"markdown-it-front-matter": "^0.2.4",
"markdown-it-github-alerts": "^1.0.0",
"markdown-it-inject-linenumbers": "^0.3.0",
"markdown-it-mark": "^4.0.0",
"markdown-it-sub": "^2.0.0",
Expand Down
9 changes: 8 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ type Config = {
katexOptions?: any;
tocOptions?: any;
/* eslint-enable @typescript-eslint/no-explicit-any */
alertOptions?: {
icons?: Record<string, string>;
titles?: Record<string, string>;
fallbackIcon?: string;
};
};

// fills in values from config file config that are not present
Expand Down Expand Up @@ -71,9 +76,10 @@ const getFileContents = (
return getFileContent(paths);
};

let configBaseDir: string | undefined = undefined;

const config = ((): Config => {
let config = undefined;
let configBaseDir = undefined;
// greedily find config
for (const cp of configPaths) {
if (!fs.existsSync(cp)) continue;
Expand Down Expand Up @@ -109,6 +115,7 @@ const config = ((): Config => {
return config;
})();

export { config, configBaseDir };
export default config;

export const address = `http://localhost:${config.port}`;
Loading
Loading