Compare commits

...

27 commits

Author SHA1 Message Date
@gapmiss
e6e1f76e8f chore: release 0.1.11 2026-06-25 16:38:40 -05:00
@gapmiss
3755d19417 fix: render inline callout labels containing square brackets in Live Preview
The Live Preview decorator used `[^\]]*` for the label, which stops at the
first `]`. A multi-part label like `[!!mail|test [with brackets]|var(...)]`
broke as soon as it hit the `]` after "brackets", so the closing ]` no longer
matched and the callout stayed as raw code. Reading mode was unaffected
because its post-processor checks startsWith/endsWith. Switch the class to
`[^`]+` so it matches up to the bounding backtick, consistent with the
post-processor and cursor-detection regexes.

Co-authored-by: Thierry VUILLEMIN <6804067+vuillemint@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 16:37:38 -05:00
@gapmiss
858a209cbe fix: sync versions.json with manifest.json minAppVersion and bump esbuild
versions.json was out of sync — releases 0.1.5–0.1.10 incorrectly listed
minAppVersion as 0.15.0 instead of 1.4.10. Also updates release.mjs to
read minAppVersion from manifest.json rather than hardcoding it, and bumps
esbuild from 0.25.4 to 0.28.1.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-14 12:07:54 -05:00
@gapmiss
ef1fca869e docs: remove duplicate Installation section from README.md 2026-06-06 15:46:56 -05:00
@gapmiss
5da6a34253 fix: update package-lock.json for removed packages 2026-06-06 15:31:31 -05:00
@gapmiss
ae2caf86b6 chore: release 0.1.10 2026-06-06 14:42:33 -05:00
@gapmiss
3cb8a6d45e fix: remove unused variables in editor view
Remove dead code (_currentLine, startOfLine, endOfLine) flagged by
community plugin scanner. Per-match selection checking already handles
cursor overlap.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-06 14:42:24 -05:00
@gapmiss
d20986d90a chore: release 0.1.9 2026-06-06 14:32:07 -05:00
@gapmiss
a03e81ed06 fix: enable strict mode and fix TypeScript errors
Update tsconfig.json for TypeScript 5.9+ compatibility:
- Remove deprecated baseUrl, use moduleResolution "bundler"
- Enable strict mode to match IDE settings
- Scope include to src/**/*.ts

Fix strict mode violations across codebase.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-06-06 14:31:46 -05:00
@gapmiss
dcde16861f docs: update README.md 2026-06-02 17:14:27 -05:00
@gapmiss
d208ad379a chore: release 0.1.8 2026-05-31 14:12:43 -05:00
@gapmiss
2501bea1bc docs: add CONTRIBUTING guide 2026-05-31 14:12:33 -05:00
@gapmiss
e063d0145a chore: release 0.1.7 2026-05-30 17:55:01 -05:00
@gapmiss
b0835b9889 feat: add custom font CSS variable
Added --inline-callout-font-family variable (default: inherit).
Configurable via Style Settings plugin.

Closes #6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-30 17:46:14 -05:00
@gapmiss
750716057c feat: add no-icon button to modals
Both new and modify modals now show "None" text when no-icon is selected
and include a circle-off button to quickly set icon to none.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-30 17:40:19 -05:00
@gapmiss
224a035795 feat: add no-icon support with none/blank
Use `none` or `blank` as the icon name to render a callout without an icon.
Icons ending with `-none` also work. The icon space is not reserved.

Closes #4

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-30 17:30:34 -05:00
@gapmiss
cf52f94f29 feat: add hex color support
Support hex colors (#RGB, #RRGGBB) in addition to RGB values and CSS variables.
Color picker in modals now preserves hex format instead of converting to RGB.

Closes #2

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-30 17:20:54 -05:00
@gapmiss
663e6b83f0 chore: release 0.1.6 2026-05-21 19:28:43 -05:00
@gapmiss
0738435a23 fix: replace builtin-modules with Node.js built-in
Use module.builtinModules instead of deprecated builtin-modules package.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 19:28:21 -05:00
@gapmiss
fa00a41857 chore: release 0.1.5 2026-05-21 19:22:52 -05:00
@gapmiss
2e2db6aad4 fix: remove plugin reload, fix all ESLint errors, add release workflow
- Remove disablePlugin/enablePlugin reload method (Scorecard security risk)
- Add eslint-plugin-obsidianmd with typescript-eslint type-checked rules
- Fix 113 ESLint errors across all source files:
  - Replace any types with proper typing
  - Use createSpan/createEl instead of document.createElement
  - Fix floating promises, unused vars, case declarations
  - Shorten command IDs to avoid plugin name duplication
  - Use window.setTimeout instead of bare setTimeout
- Update minAppVersion to 1.4.10 (required by AbstractInputSuggest)
- Update TypeScript to 5.x, ESLint to 9.x
- Add GitHub Actions workflow for releases with artifact attestation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-05-21 19:22:33 -05:00
gapmiss
38b094b4d2
Update README.md 2025-06-29 10:34:23 -05:00
gapmiss
bd2ff6ee10 chore: release 0.1.4 2025-05-13 14:45:57 -05:00
gapmiss
2d6980c908 menu.addItem icon 2025-05-13 14:45:13 -05:00
gapmiss
9a7e655eb4 chore: release 0.1.3 2025-05-12 16:06:26 -05:00
gapmiss
1a7238ca4b addCommand icons 2025-05-12 16:05:51 -05:00
gapmiss
74e5d9dfcc npm audit; README 2025-05-10 07:18:09 -05:00
25 changed files with 5403 additions and 343 deletions

View file

@ -1,3 +0,0 @@
node_modules/
main.js

View file

@ -1,23 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/ban-ts-comment": "off",
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}

49
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,49 @@
name: Release
on:
push:
tags:
- '*.*.*'
permissions:
contents: write
id-token: write
attestations: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Attest main.js
uses: actions/attest-build-provenance@v2
with:
subject-path: main.js
- name: Attest styles.css
uses: actions/attest-build-provenance@v2
with:
subject-path: styles.css
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
main.js
manifest.json
styles.css
generate_release_notes: true

6
.gitignore vendored
View file

@ -21,8 +21,4 @@ data.json
# Exclude macOS Finder (System Explorer) View States
.DS_Store
.hotreload
package-lock.json
pnpm-lock.yaml
build-*.sh
releases/*
.claude

24
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,24 @@
# Contributing
Thanks for your interest in contributing!
## Bug reports
Open an issue with steps to reproduce and expected vs actual behavior.
## Pull requests
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Run `npm run build` and `npx eslint .` to verify
5. Test in Obsidian
6. Submit a PR with a clear description
## Development
```bash
npm install
npm run dev # Watch mode
npm run build # Production build
```

View file

@ -4,16 +4,30 @@
A plugin for displaying inline "callouts" in [Obsidian.md](https://github.com/obsidianmd); a successor to the [Badges](https://github.com/gapmiss/badges) plugin by the same author. Key differences: simpler syntax, access to all Lucide icons in Obsidian, modal interfaces for icon suggestions, new inline callout and modify inline callout.
- [Demo](#demo)
- [Features](#features)
- [Usage](#usage)
- [Caveats](#caveats)
- [Installation](#installation)
- [CSS styles](#css)
- [Style Settings plugin](#style-settings-plugin)
- [Development](#development)
- [Credits](#credits)
- [Notes](#notes)
## Installation
[Install from community.obsidian.md](https://community.obsidian.md/plugins/inline-callouts)
From Obsidian's settings or preferences:
1. Community Plugins > Browse
2. Search for "Inline Callouts"
Manually:
1. download the latest [release](https://github.com/gapmiss/inline-callouts/releases/latest) archive
2. uncompress the downloaded archive
3. move the `inline-callouts` folder to `/path/to/vault/.obsidian/plugins/`
4. Settings > Community plugins > reload **Installed plugins**
5. enable plugin
or:
1. download `main.js`, `manifest.json` & `styles.css` from the latest [release](https://github.com/gapmiss/inline-callouts/releases/latest)
2. create a new folder `/path/to/vault/.obsidian/plugins/inline-callouts`
3. move all 3 files to `/path/to/vault/.obsidian/plugins/inline-callouts`
4. Settings > Community plugins > reload **Installed plugins**
5. enable plugin
## Demo
@ -94,11 +108,11 @@ A plugin for displaying inline "callouts" in [Obsidian.md](https://github.com/ob
`[!!ICON|LABEL|COLOR]`
```
| Syntax | Details |
| ----------------- | ------------------------------ |
| `ICON` | Name of the Lucide icon |
| `LABEL`(optional) | Callout label/title text |
| `COLOR`(optional) | RGB values or Obsidian CSS var |
| Syntax | Details |
| ----------------- | -------------------------------------------------- |
| `ICON` | Lucide icon name, or `none`/`blank` for no icon |
| `LABEL`(optional) | Callout label/title text |
| `COLOR`(optional) | Hex, RGB values, or Obsidian CSS var |
> [!IMPORTANT]
@ -116,31 +130,6 @@ A plugin for displaying inline "callouts" in [Obsidian.md](https://github.com/ob
| `[!!info\|Lorem\|var(--color-blue-rgb)]` |
```
## Installation
[Find at Obsidian.md/plugins](https://obsidian.md/plugins?search=inline-callouts)
From Obsidian's settings:
1. Community Plugins > Browse
2. Search for "Inline Callouts"
Via BRAT (Beta Reviewer's Auto-update Tool):
1. Ensure [BRAT](https://github.com/TfTHacker/obsidian42-brat) is installed
2. Trigger the command Obsidian42 - BRAT: Add a beta plugin for testing
3. Enter this repository, gapmiss/inline-callouts
4. Enable Vault Name plugin in the community plugin list
Manually:
1. download `main.js`, `manifest.json` & `styles.css` from the latest [release](https://github.com/gapmiss/inline-callouts/releases/)
2. create a new folder `/path/to/vault/.obsidian/plugins/inline-callouts`
3. move all 3 files to `/path/to/vault/.obsidian/plugins/inline-callouts`
4. Settings > Community plugins > reload **Installed plugins**
5. enable plugin
## CSS
Custom `CSS` styles can be applied via CSS snippets. All colors and styles can be over-written just the same.
@ -151,6 +140,7 @@ See [CSS snippets - Obsidian Help](https://help.obsidian.md/Extending+Obsidian/C
```css
body {
--inline-callout-font-family: inherit;
--inline-callout-font-size: .85em;
--inline-callout-font-weight: 400;
--inline-callout-border-radius: 4px;
@ -204,8 +194,8 @@ cd inline-callouts
### Install packages and run
```bash
pnpm i
pnpm run dev
npm i
npm run dev
```
### Enable plugin

View file

@ -1,6 +1,6 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { builtinModules } from "module";
const banner =
`/*
@ -31,7 +31,7 @@ const context = await esbuild.context({
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
...builtinModules],
format: "cjs",
target: "es2018",
logLevel: "info",

28
eslint.config.mjs Normal file
View file

@ -0,0 +1,28 @@
import tsParser from "@typescript-eslint/parser";
import tseslint from "typescript-eslint";
import obsidianmd from "eslint-plugin-obsidianmd";
export default [
{ ignores: ["node_modules/**", "main.js", "*.mjs", "package.json", "package-lock.json", "versions.json", "tsconfig.json"] },
...tseslint.configs.recommendedTypeChecked.map(config => ({
...config,
files: ["src/**/*.ts"],
})),
...obsidianmd.configs.recommended,
{
files: ["src/**/*.ts"],
languageOptions: {
parser: tsParser,
parserOptions: {
project: "./tsconfig.json",
sourceType: "module",
},
},
rules: {
"@typescript-eslint/no-unused-vars": ["error", {
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
}],
},
},
];

View file

@ -1,8 +1,8 @@
{
"id": "inline-callouts",
"name": "Inline Callouts",
"version": "0.1.2",
"minAppVersion": "0.15.0",
"version": "0.1.11",
"minAppVersion": "1.4.10",
"description": "Add inline callouts/badges to notes.",
"author": "@gapmiss",
"authorUrl": "https://github.com/gapmiss",

4966
package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "inline-callouts",
"version": "0.1.2",
"version": "0.1.11",
"description": "Inline Callouts: Add inline callouts/badges to notes",
"main": "main.js",
"scripts": {
@ -16,16 +16,20 @@
"license": "MIT",
"devDependencies": {
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"@typescript-eslint/parser": "^8.0.0",
"esbuild": "^0.28.1",
"eslint": "^9.0.0",
"eslint-plugin-obsidianmd": "^0.3.0",
"obsidian": "latest",
"tslib": "2.4.0",
"typescript": "4.7.4"
"typescript": "^5.4.0",
"typescript-eslint": "^8.0.0"
},
"dependencies": {
"@codemirror/state": "^6.0.0",
"@codemirror/view": "^6.0.0"
},
"allowScripts": {
"esbuild@0.28.1": true
}
}

View file

@ -23,7 +23,7 @@ const VERSION_TYPES = {
// Default settings
const DEFAULT_VERSION_TYPE = VERSION_TYPES.PATCH;
const RELEASE_FILES = ['main.js', 'manifest.json', 'styles.css'];
const MIN_APP_VERSION = "0.15.0"
const MIN_APP_VERSION = JSON.parse(readFileSync(path.resolve(process.cwd(), 'manifest.json'), 'utf8')).minAppVersion;
// Store original versions for rollback if needed
let originalPackageVersion = '';
@ -219,59 +219,7 @@ const createGitCommitAndTag = (version) => {
execSync('git push', { stdio: 'inherit' });
execSync('git push --tags', { stdio: 'inherit' });
console.log('🚀 Changes pushed to GitHub');
// Create GitHub Release
try {
console.log('📦 Creating GitHub Release...');
// First check if GitHub CLI is installed
try {
execSync('gh --version', { stdio: 'pipe' });
} catch (error) {
console.log('⚠️ GitHub CLI not found. Skipping GitHub Release creation.');
console.log(' To create GitHub Releases, install GitHub CLI: https://cli.github.com/');
return;
}
// Get previous tag for changelog link
let previousTag = '';
try {
// Get all tags sorted by version (newest first)
const tagsOutput = execSync('git tag --sort=-v:refname', { encoding: 'utf-8' });
// Split by line and remove empty lines
const tags = tagsOutput.split('\n').filter(tag => tag.trim() !== '');
// If current tag is in the list, get the next one (which is the previous release)
const currentTagIndex = tags.indexOf(tagName);
if (currentTagIndex >= 0 && currentTagIndex < tags.length - 1) {
previousTag = tags[currentTagIndex + 1];
} else if (tags.length > 1 && currentTagIndex < 0) {
// If new tag is not yet in the list, take the first one
previousTag = tags[0];
}
} catch (error) {
console.warn('⚠️ Could not determine previous tag:', error.message);
}
// Create release notes with changelog link if previous tag exists
let releaseNotes = `${tagName}`;
if (previousTag) {
const repoUrl = execSync('git config --get remote.origin.url', { encoding: 'utf-8' })
.trim()
.replace(/\.git$/, '')
.replace(/^git@github\.com:/, 'https://github.com/');
releaseNotes += `\n\n**Full Changelog**: ${repoUrl}/compare/${previousTag}...${tagName}`;
}
// Create GitHub Release using GitHub CLI
const releaseCommand = `gh release create ${tagName} ${RELEASE_FILES.join(' ')} --title "Release ${tagName}" --notes "${releaseNotes}"`;
execSync(releaseCommand, { stdio: 'inherit' });
console.log(`✅ GitHub Release created: ${tagName}`);
} catch (releaseError) {
console.error('⚠️ Failed to create GitHub Release:', releaseError.message);
console.log('Release files were still committed and pushed to GitHub.');
}
console.log('📦 GitHub Actions will create the release with artifact attestations.');
return true; // Successfully created commit, tag, and pushed
} catch (error) {
console.error('❌ Error during Git operations:', error.message);

View file

@ -3,6 +3,43 @@ import {
setIcon
} from 'obsidian';
function parseColorToRgb(color: string): string | null {
const trimmed = color.trim();
// Hex color (3, 4, 6, or 8 digit)
if (trimmed.startsWith('#')) {
let hex = trimmed.slice(1);
// Expand 3-digit hex to 6-digit
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
// Expand 4-digit hex (with alpha) to 8-digit
if (hex.length === 4) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
}
// Parse 6-digit or 8-digit hex
if (hex.length === 6 || hex.length === 8) {
const r = parseInt(hex.slice(0, 2), 16);
const g = parseInt(hex.slice(2, 4), 16);
const b = parseInt(hex.slice(4, 6), 16);
if (!isNaN(r) && !isNaN(g) && !isNaN(b)) {
return `${r}, ${g}, ${b}`;
}
}
return null;
}
// Already RGB format (e.g., "255, 0, 0")
return trimmed;
}
function isNoIcon(icon: string): boolean {
return icon === 'none' || icon === 'blank' || icon.endsWith('-none');
}
export class InlineCallout {
newEl: HTMLSpanElement;
@ -11,37 +48,38 @@ export class InlineCallout {
textEl: HTMLSpanElement;
constructor() {
this.newEl = document.createElement("span");
this.iconEl = document.createElement("span");
this.labelEl = document.createElement("span");
this.textEl = document.createElement("span");
this.newEl = createSpan();
this.iconEl = createSpan();
this.labelEl = createSpan();
this.textEl = createSpan();
}
public build(text: string) {
public build(text: string): HTMLElement {
// text content from <code>
let part: string = text.substring(3);
let content: string = part.slice(0, -1);
const part: string = text.substring(3);
const content: string = part.slice(0, -1);
// parts array split on pipe
let parts: any[] = content.split('|');
const parts: string[] = content.split('|');
// icon
let calloutIcon: string | null = parts[0] ? parts[0].trim().replace(/\\+$/, '').toLowerCase() : null;
const calloutIcon: string | null = parts[0] ? parts[0].trim().replace(/\\+$/, '').toLowerCase() : null;
// label
let calloutLabel: string | null = parts[1] ? parts[1].trim().replace(/\\+$/, '') : null;
const calloutLabel: string | null = parts[1] ? parts[1].trim().replace(/\\+$/, '') : null;
// color
let calloutColor: string | null = parts[2] ? parts[2].trim() : null;
const calloutColor: string | null = parts[2] ? parts[2].trim() : null;
let calloutColorStyle: string;
// no content, return <code>
const noIcon = calloutIcon ? isNoIcon(calloutIcon) : false;
if (
!content.length
|| parts.length === 0
!content.length
|| parts.length === 0
|| !calloutIcon
|| (getIconIds().indexOf('lucide-' + calloutIcon) == -1 && getIconIds().indexOf(calloutIcon) == -1) // 404, no icon found
|| (!noIcon && getIconIds().indexOf('lucide-' + calloutIcon) == -1 && getIconIds().indexOf(calloutIcon) == -1) // 404, no icon found
) {
this.newEl = document.createElement("code");
this.newEl.setText(text)
return this.newEl;
const codeEl = createEl("code");
codeEl.setText(text);
return codeEl;
}
this.newEl.addClass("inline-callout");
@ -52,35 +90,47 @@ export class InlineCallout {
// icon only
if (parts.length === 1) {
this.iconEl.setAttr("aria-label", calloutIcon);
setIcon(this.iconEl, calloutIcon);
this.newEl.appendChild(this.iconEl);
if (!noIcon) {
this.iconEl.setAttr("aria-label", calloutIcon);
setIcon(this.iconEl, calloutIcon);
this.newEl.appendChild(this.iconEl);
}
return this.newEl;
}
// icon and color only
if (!calloutLabel && calloutColor) {
calloutColorStyle = "color: rgba(" + calloutColor + ", 1);"
this.iconEl.setAttr("style", calloutColorStyle);
this.iconEl.setAttr("aria-label", calloutIcon);
setIcon(this.iconEl, calloutIcon);
this.newEl.appendChild(this.iconEl);
if (!noIcon) {
const rgb = parseColorToRgb(calloutColor);
if (rgb) {
calloutColorStyle = "color: rgba(" + rgb + ", 1);"
this.iconEl.setAttr("style", calloutColorStyle);
}
this.iconEl.setAttr("aria-label", calloutIcon);
setIcon(this.iconEl, calloutIcon);
this.newEl.appendChild(this.iconEl);
}
return this.newEl;
}
// icon
this.iconEl.setAttr("aria-label", calloutIcon);
setIcon(this.iconEl, calloutIcon);
this.newEl.appendChild(this.iconEl);
if (!noIcon) {
this.iconEl.setAttr("aria-label", calloutIcon);
setIcon(this.iconEl, calloutIcon);
this.newEl.appendChild(this.iconEl);
}
// label?
if (calloutLabel) {
this.labelEl.setText(calloutLabel!);
this.labelEl.setText(calloutLabel);
this.newEl.appendChild(this.labelEl);
}
// color?
if (calloutLabel && calloutColor) {
calloutColorStyle = "color: rgba(" + calloutColor + ", 1); background-color: rgba(" + calloutColor + ", var(--inline-callout-bg-transparency));"
this.newEl.setAttr("style", calloutColorStyle)
const rgb = parseColorToRgb(calloutColor);
if (rgb) {
calloutColorStyle = "color: rgba(" + rgb + ", 1); background-color: rgba(" + rgb + ", var(--inline-callout-bg-transparency));"
this.newEl.setAttr("style", calloutColorStyle)
}
}
return this.newEl;
}

View file

@ -4,8 +4,7 @@ import {
MarkdownPostProcessorContext,
Editor,
MarkdownView,
Notice,
Menu
Notice
} from 'obsidian';
import {
InlineCalloutsSettings,
@ -33,9 +32,9 @@ interface ContextData {
export default class InlineCalloutsPlugin extends Plugin {
settings: InlineCalloutsSettings;
settings!: InlineCalloutsSettings;
public postprocessor: MarkdownPostProcessor = (el: HTMLElement, ctx: MarkdownPostProcessorContext) => {
public postprocessor: MarkdownPostProcessor = (el: HTMLElement, _ctx: MarkdownPostProcessorContext) => {
const blockToReplace = el.querySelectorAll('code')
if (blockToReplace.length === 0) return
@ -66,61 +65,67 @@ export default class InlineCalloutsPlugin extends Plugin {
this.registerEditorExtension(viewPlugin)
if (this.settings.enableSuggester) {
this.registerEditorSuggest(new EditorIconSuggest(this));
}
this.registerEditorSuggest(new EditorIconSuggest(this));
this.addCommand({
id: "new-inline-callout",
id: "new",
name: "New inline callout",
icon: "form-input",
editorCallback: (editor) => {
let modal = new NewInlineCalloutModal(this, editor);
const modal = new NewInlineCalloutModal(this, editor);
modal.open();
}
});
if (this.settings.enableEditing) {
this.addCommand({
id: "modify-inline-callout",
name: "Modify inline callout",
editorCheckCallback: (checking, editor, view: MarkdownView) => {
let res = this.checkContextType(editor, view);
if (res) {
if (!checking) {
this.modifyInlineCallout(editor, view);
}
return true;
}
this.addCommand({
id: "modify",
name: "Modify inline callout",
icon: "form-input",
editorCheckCallback: (checking, editor, ctx) => {
if (!this.settings.enableEditing) {
return false;
}
});
}
if (!(ctx instanceof MarkdownView)) {
return false;
}
const res = this.checkContextType(editor, ctx);
if (res) {
if (!checking) {
this.modifyInlineCallout(editor, ctx);
}
return true;
}
return false;
}
});
this.addCommand({
id: "search-inline-callouts",
name: "Search for inline callouts",
id: "search",
name: "Search",
icon: "text-search",
callback: () => {
let modal = new SearchInlineCalloutsModal(this);
const modal = new SearchInlineCalloutsModal(this);
modal.open();
}
});
this.registerEvent(
this.app.workspace.on("editor-menu", (menu: Menu, editor: Editor, view: MarkdownView) => {
this.app.workspace.on("editor-menu", (menu, editor, info) => {
if (this.settings.enableEditing) {
let res = this.checkContextType(editor, view);
if (!(info instanceof MarkdownView)) {
return;
}
const res = this.checkContextType(editor, info);
if (res) {
menu.addItem(item => {
item
.setTitle('Modify inline callout')
.setIcon('quote')
.onClick(async () => {
this.modifyInlineCallout(editor, view);
.setIcon('form-input')
.onClick(() => {
this.modifyInlineCallout(editor, info);
});
})
return true;
});
}
return false;
}
})
);
@ -144,7 +149,7 @@ export default class InlineCalloutsPlugin extends Plugin {
const beforeCursor = curLine.slice(0, curCh);
const afterCursor = curLine.slice(curCh);
let matcher = { type: ContextType.INLINECODE, regex: /`(\[\!\![^`]+\])`/g, enable: true }
const matcher = { type: ContextType.INLINECODE, regex: /`(\[!![^`]+\])`/g, enable: true }
const matchInfo = this.getMatchInfo(beforeCursor, afterCursor, matcher.regex);
if (matchInfo) {
@ -182,7 +187,7 @@ export default class InlineCalloutsPlugin extends Plugin {
const beforeCursor = curLine.slice(0, curCh);
const afterCursor = curLine.slice(curCh);
let matcher = { type: ContextType.INLINECODE, regex: /`(\[\!\![^`]+\])`/g, enable: true }
const matcher = { type: ContextType.INLINECODE, regex: /`(\[!![^`]+\])`/g, enable: true }
const matchInfo = this.getMatchInfo(beforeCursor, afterCursor, matcher.regex);
if (matchInfo) {
@ -204,19 +209,16 @@ export default class InlineCalloutsPlugin extends Plugin {
return;
}
const filename = file.basename;
const contextType = this.determineContextType(editor, view);
if (contextType.type == ContextType.NULL) {
new Notice("No inline callout found at current cursor position");
// eslint-disable-next-line obsidianmd/ui/sentence-case -- false positive
new Notice("No inline callout found at current cursor position.");
return;
}
const cursor = editor.getCursor();
const curLine = cursor.line;
if (contextType.type == ContextType.INLINECODE) {
let modal = new ModifyInlineCalloutModal(this, editor, contextType);
const modal = new ModifyInlineCalloutModal(this, editor, contextType);
modal.open();
return;
}
@ -241,22 +243,11 @@ export default class InlineCalloutsPlugin extends Plugin {
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial<InlineCalloutsSettings>);
}
async saveSettings() {
await this.saveData(this.settings);
this.reload();
}
/** Reloads the plugin */
async reload() {
// @ts-ignore
await this.app.plugins.disablePlugin("inline-callouts");
// @ts-ignore
await this.app.plugins.enablePlugin("inline-callouts");
// @ts-ignore
this.app.setting.openTabById("inline-callouts").display();
}
}

View file

@ -8,18 +8,25 @@ import type InlineCalloutsPlugin from "../main";
import { IconSuggest } from '../suggest/icon';
import { InlineCallout } from '../callout/builder';
interface ContextData {
type: string;
curLine: string;
match: string | null;
range: [number, number];
}
export class ModifyInlineCalloutModal extends Modal {
public calloutIcon: string;
public calloutIcon: string = 'info';
public calloutColor: string | undefined;
public calloutLabel: string | undefined;
previewEl: HTMLDivElement;
document: Document = window.activeDocument ?? window.document;
previewEl!: HTMLDivElement;
activeDoc: Document = window.activeDocument ?? window.document;
constructor(
private plugin: InlineCalloutsPlugin,
private editor: Editor,
private contextType: any
private contextType: ContextData
) {
super(plugin.app);
@ -28,14 +35,14 @@ export class ModifyInlineCalloutModal extends Modal {
this.onOpen = () => this.display(true);
}
private async display(focus?: boolean, clearColor?: boolean, preserveIcon?: boolean) {
private display(_focus?: boolean, clearColor?: boolean, preserveIcon?: boolean) {
const { contentEl } = this;
contentEl.empty();
this.titleEl.setText("Modify inline callout");
let content = this.contextType.match.replace("[!!", "").replace("]", "");
let parts: any[] = content.split('|');
const content = (this.contextType.match ?? '').replace("[!!", "").replace("]", "");
const parts: string[] = content.split('|');
// icon
if (!clearColor) {
@ -46,28 +53,33 @@ export class ModifyInlineCalloutModal extends Modal {
// label
if (!clearColor) {
this.calloutLabel = this.calloutLabel! ? this.calloutLabel : (parts[1] ? parts[1].trim().replace(/\\+$/, '') : undefined);
this.calloutLabel = this.calloutLabel ? this.calloutLabel : (parts[1] ? parts[1].trim().replace(/\\+$/, '') : undefined);
}
// color
if (clearColor) {
this.calloutColor = '';
} else {
this.calloutColor = this.calloutColor! ? this.calloutColor : (parts[2] ? parts[2].trim() : undefined);
this.calloutColor = this.calloutColor ? this.calloutColor : (parts[2] ? parts[2].trim() : undefined);
}
const isNoIcon = this.calloutIcon === 'none' || this.calloutIcon === 'blank' || this.calloutIcon?.endsWith('-none');
new Setting(contentEl)
.setName('Icon')
.setDesc('To select an icon, click the button. Default: info')
.addButton((cb) => {
cb
.setIcon(this.calloutIcon ? this.calloutIcon : "lucide-info")
.setTooltip("Select icon")
.onClick(async (e) => {
if (isNoIcon) {
cb.setButtonText('None');
} else {
cb.setIcon(this.calloutIcon ? this.calloutIcon : "lucide-info");
}
cb.setTooltip(isNoIcon ? "No icon" : "Select icon")
.onClick((e) => {
e.preventDefault();
const modal = new IconSuggest(this.plugin, async (icon) => {
const modal = new IconSuggest(this.plugin, (icon) => {
this.calloutIcon = icon;
this.buildPreview();
this.display(false, false, true);
});
modal.open();
});
@ -76,15 +88,27 @@ export class ModifyInlineCalloutModal extends Modal {
cb.buttonEl, 'keydown', (e) => {
switch (e.key) {
case "Enter":
case " ":
case " ": {
e.preventDefault();
const modal = new IconSuggest(this.plugin, async (icon) => {
const modal = new IconSuggest(this.plugin, (icon) => {
this.calloutIcon = icon;
this.buildPreview();
this.display(false, false, true);
});
modal.open();
break;
}
}
});
})
.addExtraButton((cb) => {
cb.setIcon('circle-off')
.setTooltip('No icon')
.onClick(() => {
this.calloutIcon = 'none';
this.buildPreview();
this.display(false, false, true);
});
});
new Setting(contentEl)
@ -102,18 +126,18 @@ export class ModifyInlineCalloutModal extends Modal {
});
});
const hexToRgb = (hex: string) => {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `${r}, ${g}, ${b}`;
};
function rgbToHex(r: any, g: any, b: any) {
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b)
.toString(16)
.slice(1)
.toUpperCase();
function colorToHex(color: string | undefined): string {
if (!color) return '#000000';
if (color.startsWith('#')) return color;
// Convert RGB string to hex
const parts = color.split(',').map(s => parseInt(s.trim(), 10));
if (parts.length >= 3 && parts.every(n => !isNaN(n))) {
return '#' + ((1 << 24) + (parts[0] << 16) + (parts[1] << 8) + parts[2])
.toString(16)
.slice(1)
.toUpperCase();
}
return '#000000';
}
new Setting(contentEl)
@ -138,34 +162,24 @@ export class ModifyInlineCalloutModal extends Modal {
this.calloutColor = value;
this.buildPreview();
this.display(false, false, true);
setTimeout(() => {
let dropdown: HTMLSelectElement | null = this.document.querySelector(".modify-inline-callout-modal")!.querySelector(".inline-callouts-color-dropdown .dropdown");
dropdown!.focus();
window.setTimeout(() => {
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".modify-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
dropdown?.focus();
}, 10);
}
})
})
.addColorPicker((cb) => {
let r: number = 0;
let g: number = 0;
let b: number = 0;
if (this.calloutColor) {
let colorArr = this.calloutColor?.split(",");
r = Number(colorArr![0]);
g = Number(colorArr![1]);
b = Number(colorArr![2]);
}
cb.setValue(rgbToHex(r, g, b))
cb.setValue(colorToHex(this.calloutColor))
.onChange((value) => {
this.calloutColor = hexToRgb(value);
let dropdown: HTMLSelectElement | null = this.document.querySelector(".modify-inline-callout-modal")!.querySelector(".inline-callouts-color-dropdown .dropdown");
dropdown!.value = '';
this.calloutColor = value;
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".modify-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
if (dropdown) dropdown.value = '';
this.buildPreview();
setTimeout(() => {
let picker: HTMLSelectElement | null = this.document.querySelector(".modify-inline-callout-modal")!.querySelector('input[type="color"]');
picker!.focus();
window.setTimeout(() => {
const picker = this.activeDoc.querySelector<HTMLInputElement>(".modify-inline-callout-modal input[type='color']");
picker?.focus();
}, 10);
});
})
.addExtraButton((ex) => {
@ -173,7 +187,6 @@ export class ModifyInlineCalloutModal extends Modal {
.setTooltip('Reset to no color')
.onClick(() => {
this.calloutColor = '';
this.calloutIcon = this.calloutIcon;
this.display(false, true);
})
});
@ -193,8 +206,8 @@ export class ModifyInlineCalloutModal extends Modal {
.setCta()
.onClick(() => {
try {
let firstPipe: string = '';
let secondPipe: string = '';
let firstPipe = '';
let secondPipe = '';
if (this.calloutLabel !== undefined || this.calloutColor !== undefined) {
firstPipe = "|";
@ -207,14 +220,14 @@ export class ModifyInlineCalloutModal extends Modal {
secondPipe = "|";
}
let cursor = this.editor.getCursor();
const cursor = this.editor.getCursor();
this.editor.replaceRange(
`\`[!!${this.calloutIcon.replace("lucide-", "")}${firstPipe + (this.calloutLabel !== undefined ? this.calloutLabel : "")}${this.calloutColor ? secondPipe + this.calloutColor : ""}]\``,
{ line: cursor.line, ch: this.contextType.range?.[0] }, { line: cursor.line, ch: this.contextType.range?.[1] }
{ line: cursor.line, ch: this.contextType.range[0] }, { line: cursor.line, ch: this.contextType.range[1] }
);
} catch (e) {
console.log(e)
console.error(e);
new Notice(
"There was an issue saving the inline callout. Check the developer console for details."
);
@ -229,7 +242,7 @@ export class ModifyInlineCalloutModal extends Modal {
this.previewEl.empty();
this.previewEl.setAttr("style", "margin-bottom: 1em;");
const inlineCallout = new InlineCallout();
let newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
const newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
this.previewEl.appendChild(newEl);
}

View file

@ -13,8 +13,8 @@ export class NewInlineCalloutModal extends Modal {
public calloutIcon: string = 'lucide-info';
public calloutColor: string | undefined;
public calloutLabel: string | undefined;
previewEl: HTMLDivElement;
document: Document = window.activeDocument ?? window.document;
previewEl!: HTMLDivElement;
activeDoc: Document = window.activeDocument ?? window.document;
constructor(
private plugin: InlineCalloutsPlugin,
@ -32,24 +32,29 @@ export class NewInlineCalloutModal extends Modal {
}
}
private async display(focus?: boolean, clearColor?: boolean) {
private display(_focus?: boolean, _clearColor?: boolean) {
const { contentEl } = this;
contentEl.empty();
this.titleEl.setText("New inline callout");
const isNoIcon = this.calloutIcon === 'none' || this.calloutIcon === 'blank' || this.calloutIcon?.endsWith('-none');
new Setting(contentEl)
.setName('Icon')
.setDesc('To select an icon, click the button. Default: info')
.addButton((cb) => {
cb
.setIcon(this.calloutIcon)
.setTooltip("Select icon")
.onClick(async (e) => {
if (isNoIcon) {
cb.setButtonText('None');
} else {
cb.setIcon(this.calloutIcon);
}
cb.setTooltip(isNoIcon ? "No icon" : "Select icon")
.onClick((e) => {
e.preventDefault();
const modal = new IconSuggest(this.plugin, async (icon) => {
const modal = new IconSuggest(this.plugin, (icon) => {
this.calloutIcon = icon;
this.buildPreview();
this.display(false);
});
modal.open();
});
@ -58,35 +63,40 @@ export class NewInlineCalloutModal extends Modal {
cb.buttonEl, 'keydown', (e) => {
switch (e.key) {
case "Enter":
case " ":
case " ": {
e.preventDefault();
const modal = new IconSuggest(this.plugin, async (icon) => {
const modal = new IconSuggest(this.plugin, (icon) => {
this.calloutIcon = icon;
this.buildPreview();
this.display(false);
});
modal.open();
break;
}
}
});
})
.addExtraButton((cb) => {
cb.setIcon('circle-off')
.setTooltip('No icon')
.onClick(() => {
this.calloutIcon = 'none';
this.buildPreview();
this.display(false);
});
});
new Setting(contentEl)
.setName("Label")
.setDesc("Default: blank")
.addText((t) => {
t.setValue(this.calloutLabel!);
t.setValue(this.calloutLabel ?? '');
t.onChange((v) => {
this.calloutLabel = v;
this.buildPreview();
});
});
const hexToRgb = (hex: string) => {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return `${r}, ${g}, ${b}`;
};
new Setting(contentEl)
.setName('Color')
.setDesc('Pick a color using the dropdown or color picker. Default: Obsidian\'s base color')
@ -109,23 +119,23 @@ export class NewInlineCalloutModal extends Modal {
this.calloutColor = value;
this.buildPreview();
this.display(false, false);
setTimeout(() => {
let dropdown: HTMLSelectElement | null = this.document.querySelector(".new-inline-callout-modal")!.querySelector(".inline-callouts-color-dropdown .dropdown");
dropdown!.focus();
window.setTimeout(() => {
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".new-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
dropdown?.focus();
}, 10);
}
})
})
.addColorPicker((cb) => {
cb.setValue(this.calloutColor ?? '#000000')
cb.setValue(this.calloutColor?.startsWith('#') ? this.calloutColor : '#000000')
.onChange((value) => {
this.calloutColor = hexToRgb(value);
let dropdown: HTMLSelectElement | null = this.document.querySelector(".new-inline-callout-modal")!.querySelector(".inline-callouts-color-dropdown .dropdown");
dropdown!.value = '';
this.calloutColor = value;
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".new-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
if (dropdown) dropdown.value = '';
this.buildPreview();
setTimeout(() => {
let picker: HTMLSelectElement | null = this.document.querySelector(".new-inline-callout-modal")!.querySelector('input[type="color"]');
picker!.focus();
window.setTimeout(() => {
const picker = this.activeDoc.querySelector<HTMLInputElement>(".new-inline-callout-modal input[type='color']");
picker?.focus();
}, 10);
});
})
@ -134,7 +144,6 @@ export class NewInlineCalloutModal extends Modal {
.setTooltip('Reset to no color')
.onClick(() => {
this.calloutColor = '';
this.calloutIcon = this.calloutIcon;
this.display(false, true);
})
});
@ -154,8 +163,8 @@ export class NewInlineCalloutModal extends Modal {
.setCta()
.onClick(() => {
try {
let firstPipe: string = '';
let secondPipe: string = '';
let firstPipe = '';
let secondPipe = '';
if (this.calloutLabel !== undefined || this.calloutColor !== undefined) {
firstPipe = "|";
}
@ -166,13 +175,13 @@ export class NewInlineCalloutModal extends Modal {
) {
secondPipe = "|";
}
let trailingSpace: string = this.plugin.settings.enableTraiingSpace ? " " : "";
const trailingSpace = this.plugin.settings.enableTraiingSpace ? " " : "";
this.editor.getDoc().replaceSelection(
`\`[!!${this.calloutIcon.replace("lucide-", "")}${firstPipe + (this.calloutLabel !== undefined ? this.calloutLabel : "")}${this.calloutColor ? secondPipe + this.calloutColor : ""}]\`${trailingSpace}`,
);
// const cursor = this.editor.getCursor();
} catch (e) {
console.log(e)
console.error(e)
new Notice(
"There was an issue inserting the inline callout. Please check the developer console for details."
);
@ -188,7 +197,7 @@ export class NewInlineCalloutModal extends Modal {
this.previewEl.empty();
this.previewEl.setAttr("style", "margin-bottom: 1em;");
const inlineCallout = new InlineCallout();
let newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
const newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
this.previewEl.appendChild(newEl);
}

View file

@ -2,7 +2,6 @@ import {
Modal,
Notice,
Setting,
TextComponent,
getIconIds
} from 'obsidian';
import { InputIconSuggest } from '../suggest/inputIcon';
@ -10,7 +9,7 @@ import type InlineCalloutsPlugin from "../main";
export class SearchInlineCalloutsModal extends Modal {
public calloutIcon: string;
public calloutIcon: string = '';
constructor(
private plugin: InlineCalloutsPlugin,
@ -22,10 +21,9 @@ export class SearchInlineCalloutsModal extends Modal {
this.onOpen = () => this.display(true);
}
private async display(focus?: boolean) {
private display(_focus?: boolean) {
const { contentEl } = this;
contentEl.empty();
let input: TextComponent;
this.titleEl.setText("Search inline callouts");
new Setting(contentEl)
@ -33,7 +31,6 @@ export class SearchInlineCalloutsModal extends Modal {
.setDesc('Enter text to find an icon to search for.')
.setClass('search-icon-input')
.addSearch((t) => {
input = t;
t.setValue('')
.onChange((v) => {
this.calloutIcon = v;
@ -59,7 +56,7 @@ export class SearchInlineCalloutsModal extends Modal {
);
}
} catch (e) {
console.log(e)
console.error(e)
new Notice(
"There was an issue with your search query. Please check the developer console for details.",
5000

View file

@ -3,7 +3,7 @@ import {
PluginSettingTab,
Setting
} from 'obsidian';
import InlineCalloutsPlugin from 'src/main';
import InlineCalloutsPlugin from '../main';
export interface InlineCalloutsSettings {
enableSuggester: boolean;
@ -45,7 +45,7 @@ export class InlineCalloutsSettingTab extends PluginSettingTab {
new Setting(containerEl)
.setName('Enable editing command/menu')
.setDesc('In editing view (source & live preview modes), enable "Modify inline callout" command and context menu option.')
.setDesc('In editing view (source & live preview modes), enable "modify inline callout" command and context menu option.')
.addToggle((toggle) => {
toggle
.setValue(this.plugin.settings.enableEditing)

View file

@ -24,6 +24,9 @@ export class EditorIconSuggest extends EditorSuggest<string> {
}
onTrigger(cursor: EditorPosition, editor: Editor, _: TFile): EditorSuggestTriggerInfo | null {
if (!this.plugin.settings.enableSuggester) {
return null;
}
const sub = editor.getLine(cursor.line).substring(0, cursor.ch);
const match = sub.match(/!!\S+$/)?.first();
if (match) {
@ -55,7 +58,7 @@ export class EditorIconSuggest extends EditorSuggest<string> {
selectSuggestion(suggestion: string): void {
if (this.context) {
(this.context.editor as Editor).replaceRange('!!' + suggestion.replace("lucide-", ""), this.context.start, this.context.end);
this.context.editor.replaceRange('!!' + suggestion.replace("lucide-", ""), this.context.start, this.context.end);
}
}
}

View file

@ -54,7 +54,7 @@ export class IconSuggest extends SuggestModal<IconName> {
iconWrapper.appendChild(iconName);
}
onChooseSuggestion(item: string, evt: MouseEvent | KeyboardEvent) {
onChooseSuggestion(item: string, _evt: MouseEvent | KeyboardEvent) {
setIcon(activeDocument.querySelector('[data-note-toolbar-no-icon]')!, item);
IconSuggest.icon = item;
this.callback(item);

View file

@ -4,7 +4,7 @@ import {
setIcon
} from "obsidian";
export class InputIconSuggest extends AbstractInputSuggest<String> {
export class InputIconSuggest extends AbstractInputSuggest<string> {
textInputEl: HTMLInputElement;
constructor(
@ -14,6 +14,7 @@ export class InputIconSuggest extends AbstractInputSuggest<String> {
) {
super(app, inputEl);
this.textInputEl = inputEl;
}
getSuggestions(inputStr: string): string[] {

View file

@ -5,7 +5,7 @@ import { ViewPlugin, WidgetType, EditorView, ViewUpdate, Decoration, DecorationS
import { RangeSetBuilder } from "@codemirror/state";
import { InlineCallout } from '../callout/builder';
const REGEXP = /(`\[!!([^\]]*)\]`)/gm;
const REGEXP = /(`\[!!([^`]+)\]`)/gm;
export const viewPlugin = ViewPlugin.fromClass(class {
decorations: DecorationSet;
@ -37,18 +37,7 @@ export const viewPlugin = ViewPlugin.fromClass(class {
for (let n of lines) {
const line = view.state.doc.line(n);
const startOfLine = line.from;
const endOfLine = line.to;
let currentLine = false;
currentSelections.forEach((r) => {
if (r.to >= startOfLine && r.from <= endOfLine) {
currentLine = true;
return;
}
});
let matches = Array.from(line.text.matchAll(REGEXP))
const matches = Array.from(line.text.matchAll(REGEXP))
for (const match of matches) {
let add = true;
const from = match.index != undefined ? match.index + line.from : -1
@ -69,7 +58,7 @@ export const viewPlugin = ViewPlugin.fromClass(class {
return builder.finish();
}
}, {
decorations: (v: any) => v.decorations,
decorations: (v: { decorations: DecorationSet }) => v.decorations,
})
class InlineCalloutWidget extends WidgetType {
@ -77,7 +66,7 @@ class InlineCalloutWidget extends WidgetType {
super()
}
toDOM(view: EditorView): HTMLElement {
toDOM(_view: EditorView): HTMLElement {
let text: string = this.callout[0].substring(1).substring(this.callout[0].length - 2, 0);
const inlineCallout = new InlineCallout();
let newEl = inlineCallout.build(text);

View file

@ -1,4 +1,5 @@
body {
--inline-callout-font-family: inherit;
--inline-callout-font-size: .85em;
--inline-callout-font-weight: 400;
--inline-callout-border-radius: 4px;
@ -45,6 +46,7 @@ body {
}
.inline-callout-label {
font-family: var(--inline-callout-font-family);
font-size: var(--inline-callout-font-size);
margin-top: var(--inline-callout-label-margin-top);
margin-right: var(--inline-callout-label-margin-right);
@ -58,6 +60,19 @@ body {
width: calc(var(--dialog-width) / 1.5);
}
/* No-icon callouts: hide icon and adjust label spacing */
[data-inline-callout="none"] .inline-callout-icon,
[data-inline-callout="blank"] .inline-callout-icon,
[data-inline-callout$="-none"] .inline-callout-icon {
display: none;
}
[data-inline-callout="none"] .inline-callout-label,
[data-inline-callout="blank"] .inline-callout-label,
[data-inline-callout$="-none"] .inline-callout-label {
margin-left: var(--inline-callout-label-margin-right);
}
/* @settings
name: Inline Callouts
id: inline-callouts
@ -144,6 +159,11 @@ settings:
type: heading
level: 4
collapsed: true
-
id: inline-callout-font-family
title: Font family
type: variable-text
default: inherit
-
id: inline-callout-font-size
title: Font size

View file

@ -1,17 +1,16 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"strict": true,
"moduleResolution": "bundler",
"importHelpers": true,
"isolatedModules": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks": true,
"strictNullChecks": true,
"lib": [
"DOM",
"ES5",
@ -20,6 +19,6 @@
]
},
"include": [
"**/*.ts"
"src/**/*.ts"
]
}

View file

@ -1,5 +1,14 @@
{
"0.1.0": "0.15.0",
"0.1.1": "0.15.0",
"0.1.2": "0.15.0"
"0.1.2": "0.15.0",
"0.1.3": "0.15.0",
"0.1.4": "0.15.0",
"0.1.5": "1.4.10",
"0.1.6": "1.4.10",
"0.1.7": "1.4.10",
"0.1.8": "1.4.10",
"0.1.9": "1.4.10",
"0.1.10": "1.4.10",
"0.1.11": "1.4.10"
}