mirror of
https://github.com/gapmiss/inline-callouts.git
synced 2026-07-22 12:30:25 +00:00
Compare commits
No commits in common. "master" and "0.1.3" have entirely different histories.
25 changed files with 1184 additions and 3799 deletions
3
.eslintignore
Normal file
3
.eslintignore
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
|
||||
main.js
|
||||
23
.eslintrc
Normal file
23
.eslintrc
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"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
49
.github/workflows/release.yml
vendored
|
|
@ -1,49 +0,0 @@
|
|||
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
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -21,4 +21,4 @@ data.json
|
|||
# Exclude macOS Finder (System Explorer) View States
|
||||
.DS_Store
|
||||
|
||||
.claude
|
||||
.hotreload
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
# 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
|
||||
```
|
||||
70
README.md
70
README.md
|
|
@ -4,30 +4,16 @@
|
|||
|
||||
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.
|
||||
|
||||
## 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](#demo)
|
||||
- [Features](#features)
|
||||
- [Usage](#usage)
|
||||
- [Caveats](#caveats)
|
||||
- [Installation](#installation)
|
||||
- [CSS styles](#css)
|
||||
- [Style Settings plugin](#style-settings-plugin)
|
||||
- [Development](#development)
|
||||
- [Credits](#credits)
|
||||
- [Notes](#notes)
|
||||
|
||||
## Demo
|
||||
|
||||
|
|
@ -108,11 +94,11 @@ or:
|
|||
`[!!ICON|LABEL|COLOR]`
|
||||
```
|
||||
|
||||
| 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 |
|
||||
| Syntax | Details |
|
||||
| ----------------- | ------------------------------ |
|
||||
| `ICON` | Name of the Lucide icon |
|
||||
| `LABEL`(optional) | Callout label/title text |
|
||||
| `COLOR`(optional) | RGB values or Obsidian CSS var |
|
||||
|
||||
|
||||
> [!IMPORTANT]
|
||||
|
|
@ -130,6 +116,31 @@ or:
|
|||
| `[!!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.
|
||||
|
|
@ -140,7 +151,6 @@ 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;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import esbuild from "esbuild";
|
||||
import process from "process";
|
||||
import { builtinModules } from "module";
|
||||
import builtins from "builtin-modules";
|
||||
|
||||
const banner =
|
||||
`/*
|
||||
|
|
@ -31,7 +31,7 @@ const context = await esbuild.context({
|
|||
"@lezer/common",
|
||||
"@lezer/highlight",
|
||||
"@lezer/lr",
|
||||
...builtinModules],
|
||||
...builtins],
|
||||
format: "cjs",
|
||||
target: "es2018",
|
||||
logLevel: "info",
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
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: "^_",
|
||||
}],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"id": "inline-callouts",
|
||||
"name": "Inline Callouts",
|
||||
"version": "0.1.11",
|
||||
"minAppVersion": "1.4.10",
|
||||
"version": "0.1.3",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Add inline callouts/badges to notes.",
|
||||
"author": "@gapmiss",
|
||||
"authorUrl": "https://github.com/gapmiss",
|
||||
|
|
|
|||
4213
package-lock.json
generated
4213
package-lock.json
generated
File diff suppressed because it is too large
Load diff
16
package.json
16
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "inline-callouts",
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.3",
|
||||
"description": "Inline Callouts: Add inline callouts/badges to notes",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
|
|
@ -16,20 +16,16 @@
|
|||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^16.11.6",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"esbuild": "^0.28.1",
|
||||
"eslint": "^9.0.0",
|
||||
"eslint-plugin-obsidianmd": "^0.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.29.0",
|
||||
"@typescript-eslint/parser": "5.29.0",
|
||||
"builtin-modules": "3.3.0",
|
||||
"esbuild": "^0.25.4",
|
||||
"obsidian": "latest",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "^5.4.0",
|
||||
"typescript-eslint": "^8.0.0"
|
||||
"typescript": "4.7.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0"
|
||||
},
|
||||
"allowScripts": {
|
||||
"esbuild@0.28.1": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
56
release.mjs
56
release.mjs
|
|
@ -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 = JSON.parse(readFileSync(path.resolve(process.cwd(), 'manifest.json'), 'utf8')).minAppVersion;
|
||||
const MIN_APP_VERSION = "0.15.0"
|
||||
|
||||
// Store original versions for rollback if needed
|
||||
let originalPackageVersion = '';
|
||||
|
|
@ -219,7 +219,59 @@ const createGitCommitAndTag = (version) => {
|
|||
execSync('git push', { stdio: 'inherit' });
|
||||
execSync('git push --tags', { stdio: 'inherit' });
|
||||
console.log('🚀 Changes pushed to GitHub');
|
||||
console.log('📦 GitHub Actions will create the release with artifact attestations.');
|
||||
|
||||
// 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.');
|
||||
}
|
||||
return true; // Successfully created commit, tag, and pushed
|
||||
} catch (error) {
|
||||
console.error('❌ Error during Git operations:', error.message);
|
||||
|
|
|
|||
|
|
@ -3,43 +3,6 @@ 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;
|
||||
|
|
@ -48,38 +11,37 @@ export class InlineCallout {
|
|||
textEl: HTMLSpanElement;
|
||||
|
||||
constructor() {
|
||||
this.newEl = createSpan();
|
||||
this.iconEl = createSpan();
|
||||
this.labelEl = createSpan();
|
||||
this.textEl = createSpan();
|
||||
this.newEl = document.createElement("span");
|
||||
this.iconEl = document.createElement("span");
|
||||
this.labelEl = document.createElement("span");
|
||||
this.textEl = document.createElement("span");
|
||||
}
|
||||
|
||||
public build(text: string): HTMLElement {
|
||||
public build(text: string) {
|
||||
|
||||
// text content from <code>
|
||||
const part: string = text.substring(3);
|
||||
const content: string = part.slice(0, -1);
|
||||
let part: string = text.substring(3);
|
||||
let content: string = part.slice(0, -1);
|
||||
// parts array split on pipe
|
||||
const parts: string[] = content.split('|');
|
||||
let parts: any[] = content.split('|');
|
||||
// icon
|
||||
const calloutIcon: string | null = parts[0] ? parts[0].trim().replace(/\\+$/, '').toLowerCase() : null;
|
||||
let calloutIcon: string | null = parts[0] ? parts[0].trim().replace(/\\+$/, '').toLowerCase() : null;
|
||||
// label
|
||||
const calloutLabel: string | null = parts[1] ? parts[1].trim().replace(/\\+$/, '') : null;
|
||||
let calloutLabel: string | null = parts[1] ? parts[1].trim().replace(/\\+$/, '') : null;
|
||||
// color
|
||||
const calloutColor: string | null = parts[2] ? parts[2].trim() : null;
|
||||
let 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
|
||||
|| (!noIcon && getIconIds().indexOf('lucide-' + calloutIcon) == -1 && getIconIds().indexOf(calloutIcon) == -1) // 404, no icon found
|
||||
|| (getIconIds().indexOf('lucide-' + calloutIcon) == -1 && getIconIds().indexOf(calloutIcon) == -1) // 404, no icon found
|
||||
) {
|
||||
const codeEl = createEl("code");
|
||||
codeEl.setText(text);
|
||||
return codeEl;
|
||||
this.newEl = document.createElement("code");
|
||||
this.newEl.setText(text)
|
||||
return this.newEl;
|
||||
}
|
||||
|
||||
this.newEl.addClass("inline-callout");
|
||||
|
|
@ -90,47 +52,35 @@ export class InlineCallout {
|
|||
|
||||
// icon only
|
||||
if (parts.length === 1) {
|
||||
if (!noIcon) {
|
||||
this.iconEl.setAttr("aria-label", calloutIcon);
|
||||
setIcon(this.iconEl, calloutIcon);
|
||||
this.newEl.appendChild(this.iconEl);
|
||||
}
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
return this.newEl;
|
||||
}
|
||||
|
||||
// icon
|
||||
if (!noIcon) {
|
||||
this.iconEl.setAttr("aria-label", calloutIcon);
|
||||
setIcon(this.iconEl, calloutIcon);
|
||||
this.newEl.appendChild(this.iconEl);
|
||||
}
|
||||
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) {
|
||||
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)
|
||||
}
|
||||
calloutColorStyle = "color: rgba(" + calloutColor + ", 1); background-color: rgba(" + calloutColor + ", var(--inline-callout-bg-transparency));"
|
||||
this.newEl.setAttr("style", calloutColorStyle)
|
||||
}
|
||||
return this.newEl;
|
||||
}
|
||||
|
|
|
|||
98
src/main.ts
98
src/main.ts
|
|
@ -4,7 +4,8 @@ import {
|
|||
MarkdownPostProcessorContext,
|
||||
Editor,
|
||||
MarkdownView,
|
||||
Notice
|
||||
Notice,
|
||||
Menu
|
||||
} from 'obsidian';
|
||||
import {
|
||||
InlineCalloutsSettings,
|
||||
|
|
@ -32,9 +33,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
|
||||
|
||||
|
|
@ -65,67 +66,64 @@ export default class InlineCalloutsPlugin extends Plugin {
|
|||
|
||||
this.registerEditorExtension(viewPlugin)
|
||||
|
||||
this.registerEditorSuggest(new EditorIconSuggest(this));
|
||||
if (this.settings.enableSuggester) {
|
||||
this.registerEditorSuggest(new EditorIconSuggest(this));
|
||||
}
|
||||
|
||||
this.addCommand({
|
||||
id: "new",
|
||||
id: "new-inline-callout",
|
||||
name: "New inline callout",
|
||||
icon: "form-input",
|
||||
editorCallback: (editor) => {
|
||||
const modal = new NewInlineCalloutModal(this, editor);
|
||||
let modal = new NewInlineCalloutModal(this, editor);
|
||||
modal.open();
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
if (this.settings.enableEditing) {
|
||||
this.addCommand({
|
||||
id: "modify-inline-callout",
|
||||
name: "Modify inline callout",
|
||||
icon: "form-input",
|
||||
editorCheckCallback: (checking, editor, view: MarkdownView) => {
|
||||
let res = this.checkContextType(editor, view);
|
||||
if (res) {
|
||||
if (!checking) {
|
||||
this.modifyInlineCallout(editor, view);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
this.addCommand({
|
||||
id: "search",
|
||||
name: "Search",
|
||||
id: "search-inline-callouts",
|
||||
name: "Search for inline callouts",
|
||||
icon: "text-search",
|
||||
callback: () => {
|
||||
const modal = new SearchInlineCalloutsModal(this);
|
||||
let modal = new SearchInlineCalloutsModal(this);
|
||||
modal.open();
|
||||
}
|
||||
});
|
||||
|
||||
this.registerEvent(
|
||||
this.app.workspace.on("editor-menu", (menu, editor, info) => {
|
||||
this.app.workspace.on("editor-menu", (menu: Menu, editor: Editor, view: MarkdownView) => {
|
||||
if (this.settings.enableEditing) {
|
||||
if (!(info instanceof MarkdownView)) {
|
||||
return;
|
||||
}
|
||||
const res = this.checkContextType(editor, info);
|
||||
let res = this.checkContextType(editor, view);
|
||||
if (res) {
|
||||
menu.addItem(item => {
|
||||
item
|
||||
.setTitle('Modify inline callout')
|
||||
.setIcon('form-input')
|
||||
.onClick(() => {
|
||||
this.modifyInlineCallout(editor, info);
|
||||
.setIcon('quote')
|
||||
.onClick(async () => {
|
||||
this.modifyInlineCallout(editor, view);
|
||||
});
|
||||
});
|
||||
})
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
|
@ -149,7 +147,7 @@ export default class InlineCalloutsPlugin extends Plugin {
|
|||
const beforeCursor = curLine.slice(0, curCh);
|
||||
const afterCursor = curLine.slice(curCh);
|
||||
|
||||
const matcher = { type: ContextType.INLINECODE, regex: /`(\[!![^`]+\])`/g, enable: true }
|
||||
let matcher = { type: ContextType.INLINECODE, regex: /`(\[\!\![^`]+\])`/g, enable: true }
|
||||
const matchInfo = this.getMatchInfo(beforeCursor, afterCursor, matcher.regex);
|
||||
|
||||
if (matchInfo) {
|
||||
|
|
@ -187,7 +185,7 @@ export default class InlineCalloutsPlugin extends Plugin {
|
|||
const beforeCursor = curLine.slice(0, curCh);
|
||||
const afterCursor = curLine.slice(curCh);
|
||||
|
||||
const matcher = { type: ContextType.INLINECODE, regex: /`(\[!![^`]+\])`/g, enable: true }
|
||||
let matcher = { type: ContextType.INLINECODE, regex: /`(\[\!\![^`]+\])`/g, enable: true }
|
||||
const matchInfo = this.getMatchInfo(beforeCursor, afterCursor, matcher.regex);
|
||||
|
||||
if (matchInfo) {
|
||||
|
|
@ -209,16 +207,19 @@ export default class InlineCalloutsPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
const filename = file.basename;
|
||||
const contextType = this.determineContextType(editor, view);
|
||||
|
||||
if (contextType.type == ContextType.NULL) {
|
||||
// eslint-disable-next-line obsidianmd/ui/sentence-case -- false positive
|
||||
new Notice("No inline callout found at current cursor position.");
|
||||
new Notice("No inline callout found at current cursor position");
|
||||
return;
|
||||
}
|
||||
|
||||
const cursor = editor.getCursor();
|
||||
const curLine = cursor.line;
|
||||
|
||||
if (contextType.type == ContextType.INLINECODE) {
|
||||
const modal = new ModifyInlineCalloutModal(this, editor, contextType);
|
||||
let modal = new ModifyInlineCalloutModal(this, editor, contextType);
|
||||
modal.open();
|
||||
return;
|
||||
}
|
||||
|
|
@ -243,11 +244,22 @@ export default class InlineCalloutsPlugin extends Plugin {
|
|||
}
|
||||
|
||||
async loadSettings() {
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData() as Partial<InlineCalloutsSettings>);
|
||||
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,25 +8,18 @@ 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 = 'info';
|
||||
public calloutIcon: string;
|
||||
public calloutColor: string | undefined;
|
||||
public calloutLabel: string | undefined;
|
||||
previewEl!: HTMLDivElement;
|
||||
activeDoc: Document = window.activeDocument ?? window.document;
|
||||
previewEl: HTMLDivElement;
|
||||
document: Document = window.activeDocument ?? window.document;
|
||||
|
||||
constructor(
|
||||
private plugin: InlineCalloutsPlugin,
|
||||
private editor: Editor,
|
||||
private contextType: ContextData
|
||||
private contextType: any
|
||||
) {
|
||||
super(plugin.app);
|
||||
|
||||
|
|
@ -35,14 +28,14 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
this.onOpen = () => this.display(true);
|
||||
}
|
||||
|
||||
private display(_focus?: boolean, clearColor?: boolean, preserveIcon?: boolean) {
|
||||
private async display(focus?: boolean, clearColor?: boolean, preserveIcon?: boolean) {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
|
||||
this.titleEl.setText("Modify inline callout");
|
||||
|
||||
const content = (this.contextType.match ?? '').replace("[!!", "").replace("]", "");
|
||||
const parts: string[] = content.split('|');
|
||||
let content = this.contextType.match.replace("[!!", "").replace("]", "");
|
||||
let parts: any[] = content.split('|');
|
||||
|
||||
// icon
|
||||
if (!clearColor) {
|
||||
|
|
@ -53,33 +46,28 @@ 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) => {
|
||||
if (isNoIcon) {
|
||||
cb.setButtonText('None');
|
||||
} else {
|
||||
cb.setIcon(this.calloutIcon ? this.calloutIcon : "lucide-info");
|
||||
}
|
||||
cb.setTooltip(isNoIcon ? "No icon" : "Select icon")
|
||||
.onClick((e) => {
|
||||
cb
|
||||
.setIcon(this.calloutIcon ? this.calloutIcon : "lucide-info")
|
||||
.setTooltip("Select icon")
|
||||
.onClick(async (e) => {
|
||||
e.preventDefault();
|
||||
const modal = new IconSuggest(this.plugin, (icon) => {
|
||||
const modal = new IconSuggest(this.plugin, async (icon) => {
|
||||
this.calloutIcon = icon;
|
||||
this.buildPreview();
|
||||
this.display(false, false, true);
|
||||
});
|
||||
modal.open();
|
||||
});
|
||||
|
|
@ -88,27 +76,15 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
cb.buttonEl, 'keydown', (e) => {
|
||||
switch (e.key) {
|
||||
case "Enter":
|
||||
case " ": {
|
||||
case " ":
|
||||
e.preventDefault();
|
||||
const modal = new IconSuggest(this.plugin, (icon) => {
|
||||
const modal = new IconSuggest(this.plugin, async (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)
|
||||
|
|
@ -126,18 +102,18 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
});
|
||||
});
|
||||
|
||||
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';
|
||||
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();
|
||||
}
|
||||
|
||||
new Setting(contentEl)
|
||||
|
|
@ -162,24 +138,34 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
this.calloutColor = value;
|
||||
this.buildPreview();
|
||||
this.display(false, false, true);
|
||||
window.setTimeout(() => {
|
||||
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".modify-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
|
||||
dropdown?.focus();
|
||||
setTimeout(() => {
|
||||
let dropdown: HTMLSelectElement | null = this.document.querySelector(".modify-inline-callout-modal")!.querySelector(".inline-callouts-color-dropdown .dropdown");
|
||||
dropdown!.focus();
|
||||
}, 10);
|
||||
}
|
||||
})
|
||||
})
|
||||
.addColorPicker((cb) => {
|
||||
cb.setValue(colorToHex(this.calloutColor))
|
||||
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))
|
||||
.onChange((value) => {
|
||||
this.calloutColor = value;
|
||||
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".modify-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
|
||||
if (dropdown) dropdown.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.buildPreview();
|
||||
window.setTimeout(() => {
|
||||
const picker = this.activeDoc.querySelector<HTMLInputElement>(".modify-inline-callout-modal input[type='color']");
|
||||
picker?.focus();
|
||||
setTimeout(() => {
|
||||
let picker: HTMLSelectElement | null = this.document.querySelector(".modify-inline-callout-modal")!.querySelector('input[type="color"]');
|
||||
picker!.focus();
|
||||
}, 10);
|
||||
|
||||
});
|
||||
})
|
||||
.addExtraButton((ex) => {
|
||||
|
|
@ -187,6 +173,7 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
.setTooltip('Reset to no color')
|
||||
.onClick(() => {
|
||||
this.calloutColor = '';
|
||||
this.calloutIcon = this.calloutIcon;
|
||||
this.display(false, true);
|
||||
})
|
||||
});
|
||||
|
|
@ -206,8 +193,8 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
.setCta()
|
||||
.onClick(() => {
|
||||
try {
|
||||
let firstPipe = '';
|
||||
let secondPipe = '';
|
||||
let firstPipe: string = '';
|
||||
let secondPipe: string = '';
|
||||
|
||||
if (this.calloutLabel !== undefined || this.calloutColor !== undefined) {
|
||||
firstPipe = "|";
|
||||
|
|
@ -220,14 +207,14 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
secondPipe = "|";
|
||||
}
|
||||
|
||||
const cursor = this.editor.getCursor();
|
||||
let 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.error(e);
|
||||
console.log(e)
|
||||
new Notice(
|
||||
"There was an issue saving the inline callout. Check the developer console for details."
|
||||
);
|
||||
|
|
@ -242,7 +229,7 @@ export class ModifyInlineCalloutModal extends Modal {
|
|||
this.previewEl.empty();
|
||||
this.previewEl.setAttr("style", "margin-bottom: 1em;");
|
||||
const inlineCallout = new InlineCallout();
|
||||
const newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
|
||||
let newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
|
||||
this.previewEl.appendChild(newEl);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ export class NewInlineCalloutModal extends Modal {
|
|||
public calloutIcon: string = 'lucide-info';
|
||||
public calloutColor: string | undefined;
|
||||
public calloutLabel: string | undefined;
|
||||
previewEl!: HTMLDivElement;
|
||||
activeDoc: Document = window.activeDocument ?? window.document;
|
||||
previewEl: HTMLDivElement;
|
||||
document: Document = window.activeDocument ?? window.document;
|
||||
|
||||
constructor(
|
||||
private plugin: InlineCalloutsPlugin,
|
||||
|
|
@ -32,29 +32,24 @@ export class NewInlineCalloutModal extends Modal {
|
|||
}
|
||||
}
|
||||
|
||||
private display(_focus?: boolean, _clearColor?: boolean) {
|
||||
private async 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) => {
|
||||
if (isNoIcon) {
|
||||
cb.setButtonText('None');
|
||||
} else {
|
||||
cb.setIcon(this.calloutIcon);
|
||||
}
|
||||
cb.setTooltip(isNoIcon ? "No icon" : "Select icon")
|
||||
.onClick((e) => {
|
||||
cb
|
||||
.setIcon(this.calloutIcon)
|
||||
.setTooltip("Select icon")
|
||||
.onClick(async (e) => {
|
||||
e.preventDefault();
|
||||
const modal = new IconSuggest(this.plugin, (icon) => {
|
||||
const modal = new IconSuggest(this.plugin, async (icon) => {
|
||||
this.calloutIcon = icon;
|
||||
this.buildPreview();
|
||||
this.display(false);
|
||||
});
|
||||
modal.open();
|
||||
});
|
||||
|
|
@ -63,40 +58,35 @@ export class NewInlineCalloutModal extends Modal {
|
|||
cb.buttonEl, 'keydown', (e) => {
|
||||
switch (e.key) {
|
||||
case "Enter":
|
||||
case " ": {
|
||||
case " ":
|
||||
e.preventDefault();
|
||||
const modal = new IconSuggest(this.plugin, (icon) => {
|
||||
const modal = new IconSuggest(this.plugin, async (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')
|
||||
|
|
@ -119,23 +109,23 @@ export class NewInlineCalloutModal extends Modal {
|
|||
this.calloutColor = value;
|
||||
this.buildPreview();
|
||||
this.display(false, false);
|
||||
window.setTimeout(() => {
|
||||
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".new-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
|
||||
dropdown?.focus();
|
||||
setTimeout(() => {
|
||||
let dropdown: HTMLSelectElement | null = this.document.querySelector(".new-inline-callout-modal")!.querySelector(".inline-callouts-color-dropdown .dropdown");
|
||||
dropdown!.focus();
|
||||
}, 10);
|
||||
}
|
||||
})
|
||||
})
|
||||
.addColorPicker((cb) => {
|
||||
cb.setValue(this.calloutColor?.startsWith('#') ? this.calloutColor : '#000000')
|
||||
cb.setValue(this.calloutColor ?? '#000000')
|
||||
.onChange((value) => {
|
||||
this.calloutColor = value;
|
||||
const dropdown = this.activeDoc.querySelector<HTMLSelectElement>(".new-inline-callout-modal .inline-callouts-color-dropdown .dropdown");
|
||||
if (dropdown) dropdown.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.buildPreview();
|
||||
window.setTimeout(() => {
|
||||
const picker = this.activeDoc.querySelector<HTMLInputElement>(".new-inline-callout-modal input[type='color']");
|
||||
picker?.focus();
|
||||
setTimeout(() => {
|
||||
let picker: HTMLSelectElement | null = this.document.querySelector(".new-inline-callout-modal")!.querySelector('input[type="color"]');
|
||||
picker!.focus();
|
||||
}, 10);
|
||||
});
|
||||
})
|
||||
|
|
@ -144,6 +134,7 @@ export class NewInlineCalloutModal extends Modal {
|
|||
.setTooltip('Reset to no color')
|
||||
.onClick(() => {
|
||||
this.calloutColor = '';
|
||||
this.calloutIcon = this.calloutIcon;
|
||||
this.display(false, true);
|
||||
})
|
||||
});
|
||||
|
|
@ -163,8 +154,8 @@ export class NewInlineCalloutModal extends Modal {
|
|||
.setCta()
|
||||
.onClick(() => {
|
||||
try {
|
||||
let firstPipe = '';
|
||||
let secondPipe = '';
|
||||
let firstPipe: string = '';
|
||||
let secondPipe: string = '';
|
||||
if (this.calloutLabel !== undefined || this.calloutColor !== undefined) {
|
||||
firstPipe = "|";
|
||||
}
|
||||
|
|
@ -175,13 +166,13 @@ export class NewInlineCalloutModal extends Modal {
|
|||
) {
|
||||
secondPipe = "|";
|
||||
}
|
||||
const trailingSpace = this.plugin.settings.enableTraiingSpace ? " " : "";
|
||||
let trailingSpace: string = 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.error(e)
|
||||
console.log(e)
|
||||
new Notice(
|
||||
"There was an issue inserting the inline callout. Please check the developer console for details."
|
||||
);
|
||||
|
|
@ -197,7 +188,7 @@ export class NewInlineCalloutModal extends Modal {
|
|||
this.previewEl.empty();
|
||||
this.previewEl.setAttr("style", "margin-bottom: 1em;");
|
||||
const inlineCallout = new InlineCallout();
|
||||
const newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
|
||||
let newEl = inlineCallout.build('[!!' + this.calloutIcon + '|' + (this.calloutLabel ? this.calloutLabel : '') + '|' + this.calloutColor + ']');
|
||||
this.previewEl.appendChild(newEl);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import {
|
|||
Modal,
|
||||
Notice,
|
||||
Setting,
|
||||
TextComponent,
|
||||
getIconIds
|
||||
} from 'obsidian';
|
||||
import { InputIconSuggest } from '../suggest/inputIcon';
|
||||
|
|
@ -9,7 +10,7 @@ import type InlineCalloutsPlugin from "../main";
|
|||
|
||||
export class SearchInlineCalloutsModal extends Modal {
|
||||
|
||||
public calloutIcon: string = '';
|
||||
public calloutIcon: string;
|
||||
|
||||
constructor(
|
||||
private plugin: InlineCalloutsPlugin,
|
||||
|
|
@ -21,9 +22,10 @@ export class SearchInlineCalloutsModal extends Modal {
|
|||
this.onOpen = () => this.display(true);
|
||||
}
|
||||
|
||||
private display(_focus?: boolean) {
|
||||
private async display(focus?: boolean) {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
let input: TextComponent;
|
||||
this.titleEl.setText("Search inline callouts");
|
||||
|
||||
new Setting(contentEl)
|
||||
|
|
@ -31,6 +33,7 @@ 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;
|
||||
|
|
@ -56,7 +59,7 @@ export class SearchInlineCalloutsModal extends Modal {
|
|||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
console.log(e)
|
||||
new Notice(
|
||||
"There was an issue with your search query. Please check the developer console for details.",
|
||||
5000
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import {
|
|||
PluginSettingTab,
|
||||
Setting
|
||||
} from 'obsidian';
|
||||
import InlineCalloutsPlugin from '../main';
|
||||
import InlineCalloutsPlugin from 'src/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)
|
||||
|
|
|
|||
|
|
@ -24,9 +24,6 @@ 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) {
|
||||
|
|
@ -58,7 +55,7 @@ export class EditorIconSuggest extends EditorSuggest<string> {
|
|||
|
||||
selectSuggestion(suggestion: string): void {
|
||||
if (this.context) {
|
||||
this.context.editor.replaceRange('!!' + suggestion.replace("lucide-", ""), this.context.start, this.context.end);
|
||||
(this.context.editor as Editor).replaceRange('!!' + suggestion.replace("lucide-", ""), this.context.start, this.context.end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import {
|
|||
setIcon
|
||||
} from "obsidian";
|
||||
|
||||
export class InputIconSuggest extends AbstractInputSuggest<string> {
|
||||
export class InputIconSuggest extends AbstractInputSuggest<String> {
|
||||
textInputEl: HTMLInputElement;
|
||||
|
||||
constructor(
|
||||
|
|
@ -14,7 +14,6 @@ export class InputIconSuggest extends AbstractInputSuggest<string> {
|
|||
|
||||
) {
|
||||
super(app, inputEl);
|
||||
this.textInputEl = inputEl;
|
||||
}
|
||||
|
||||
getSuggestions(inputStr: string): string[] {
|
||||
|
|
|
|||
|
|
@ -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,7 +37,18 @@ export const viewPlugin = ViewPlugin.fromClass(class {
|
|||
|
||||
for (let n of lines) {
|
||||
const line = view.state.doc.line(n);
|
||||
const matches = Array.from(line.text.matchAll(REGEXP))
|
||||
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))
|
||||
for (const match of matches) {
|
||||
let add = true;
|
||||
const from = match.index != undefined ? match.index + line.from : -1
|
||||
|
|
@ -58,7 +69,7 @@ export const viewPlugin = ViewPlugin.fromClass(class {
|
|||
return builder.finish();
|
||||
}
|
||||
}, {
|
||||
decorations: (v: { decorations: DecorationSet }) => v.decorations,
|
||||
decorations: (v: any) => v.decorations,
|
||||
})
|
||||
|
||||
class InlineCalloutWidget extends WidgetType {
|
||||
|
|
@ -66,7 +77,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);
|
||||
|
|
|
|||
20
styles.css
20
styles.css
|
|
@ -1,5 +1,4 @@
|
|||
body {
|
||||
--inline-callout-font-family: inherit;
|
||||
--inline-callout-font-size: .85em;
|
||||
--inline-callout-font-weight: 400;
|
||||
--inline-callout-border-radius: 4px;
|
||||
|
|
@ -46,7 +45,6 @@ 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);
|
||||
|
|
@ -60,19 +58,6 @@ 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
|
||||
|
|
@ -159,11 +144,6 @@ 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
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"moduleResolution": "bundler",
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictNullChecks": true,
|
||||
"strictNullChecks": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
|
|
@ -19,6 +20,6 @@
|
|||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
"**/*.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,5 @@
|
|||
"0.1.0": "0.15.0",
|
||||
"0.1.1": "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"
|
||||
"0.1.3": "0.15.0"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue