Skip to content

Commit 2ea7af7

Browse files
committed
Add setting for customizable emoji support in ratings
Added InteractiveRatingsSettings interface, settings tab, and dynamic symbol pattern updates. Users can now customize supported emojis through plugin settings. Default: πŸŽ₯πŸ†β­πŸ’ŽπŸ”₯βš‘πŸŽ―πŸš€πŸ’°πŸŽ–οΈ Fix regex detection bug for low ratings (1-2 stars) Modified regex to detect 1+ symbols instead of 3+, and updated editor logic to intelligently handle short patterns with rating text or full-only symbols. Fixes issue where reducing ratings to 1-2 stars made them uneditable. Simplify pattern detection logic - remove complex special cases Replaced complex logic with simple rule: if no rating text, require 3+ symbols to avoid false positives. If rating text exists, any symbol count is valid. This generic approach handles all cases without special full-only symbol logic. Remove hardcoded configurable emojis from constants.ts since they're now managed through settings
1 parent 3c0cf40 commit 2ea7af7

File tree

7 files changed

+133
-24
lines changed

7 files changed

+133
-24
lines changed

β€Žsrc/InteractiveRatingsPlugin.tsβ€Ž

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
import { Plugin } from 'obsidian';
22
import { LOGGING_ENABLED } from './constants';
33
import { ratingEditorExtension } from './editor-extension';
4+
import { InteractiveRatingsSettings } from './types';
5+
import { DEFAULT_SETTINGS, InteractiveRatingsSettingTab } from './settings';
6+
import { updateSymbolPatternsFromSettings } from './utils';
47

58
export class InteractiveRatingsPlugin extends Plugin {
9+
settings: InteractiveRatingsSettings;
10+
611
async onload(): Promise<void> {
712
if (LOGGING_ENABLED) {
813
console.info('[InteractiveRatings] Plugin loading - edit mode only');
914
}
1015

16+
// Load settings
17+
await this.loadSettings();
18+
19+
// Update symbol patterns based on settings
20+
this.updateSymbolPatterns();
21+
1122
// Register editor extension for interactive ratings in editing mode only
1223
this.registerEditorExtension(ratingEditorExtension);
1324

25+
// Add settings tab
26+
this.addSettingTab(new InteractiveRatingsSettingTab(this.app, this));
27+
1428
if (LOGGING_ENABLED) {
1529
console.info('[InteractiveRatings] Plugin loaded successfully');
1630
}
@@ -21,4 +35,16 @@ export class InteractiveRatingsPlugin extends Plugin {
2135
console.info('[InteractiveRatings] Plugin unloaded');
2236
}
2337
}
38+
39+
async loadSettings(): Promise<void> {
40+
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
41+
}
42+
43+
async saveSettings(): Promise<void> {
44+
await this.saveData(this.settings);
45+
}
46+
47+
updateSymbolPatterns(): void {
48+
updateSymbolPatternsFromSettings(this.settings);
49+
}
2450
}

β€Žsrc/constants.tsβ€Ž

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { SymbolSet } from './types';
33
// Global logging control - set to false for production, true for debugging
44
export const LOGGING_ENABLED = false;
55

6-
// Define symbol patterns as a global constant
7-
export const SYMBOL_PATTERNS: SymbolSet[] = [
6+
// Base symbol patterns (excluding user-configurable emojis)
7+
export const BASE_SYMBOL_PATTERNS: SymbolSet[] = [
88
{ full: 'β˜…', empty: 'β˜†', half: null }, // Symbols
99
{ full: '✦', empty: '✧', half: null }, // Star symbols
1010
{ full: 'πŸŒ•', empty: 'πŸŒ‘', half: 'πŸŒ—' }, // Moon phases
@@ -33,20 +33,22 @@ export const SYMBOL_PATTERNS: SymbolSet[] = [
3333
{ full: '⬀', empty: 'β—―', half: null }, // Bold circles
3434
{ full: '⚫', empty: 'βšͺ', half: null }, // Black/white circles
3535
{ full: 'β–ˆ', empty: 'β–‘', half: null }, // Block/light shade
36+
];
3637

37-
// Full-only symbols (same symbol for full and empty, no half)
38-
{ full: 'πŸŽ₯', empty: 'πŸŽ₯', half: null }, // Movie cameras
39-
{ full: 'πŸ†', empty: 'πŸ†', half: null }, // Trophies
40-
{ full: '⭐', empty: '⭐', half: null }, // Gold stars
41-
{ full: 'πŸ’Ž', empty: 'πŸ’Ž', half: null }, // Diamonds
42-
{ full: 'πŸ”₯', empty: 'πŸ”₯', half: null }, // Fire
43-
{ full: '⚑', empty: '⚑', half: null }, // Lightning
44-
{ full: '🎯', empty: '🎯', half: null }, // Target/bullseye
45-
{ full: 'πŸš€', empty: 'πŸš€', half: null }, // Rockets
46-
{ full: 'πŸ’°', empty: 'πŸ’°', half: null }, // Money bags
47-
{ full: 'πŸŽ–οΈ', empty: 'πŸŽ–οΈ', half: null }, // Military medals
38+
// Mutable symbol patterns - starts with base patterns and user-configurable emojis are added dynamically
39+
export let SYMBOL_PATTERNS: SymbolSet[] = [
40+
...BASE_SYMBOL_PATTERNS,
41+
// User-configurable emojis are added dynamically from settings
4842
];
4943

44+
/**
45+
* Update the global symbol patterns array
46+
*/
47+
export function updateSymbolPatterns(newPatterns: SymbolSet[]): void {
48+
SYMBOL_PATTERNS.length = 0; // Clear the array
49+
SYMBOL_PATTERNS.push(...newPatterns); // Add new patterns
50+
}
51+
5052
// Interaction constants
5153
export const INTERACTION_BUFFER = 5; // Buffer for interaction detection
5254
export const OVERLAY_VERTICAL_ADJUSTMENT = 2.1; // Vertical alignment for overlay

β€Žsrc/editor-extension.tsβ€Ž

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -416,10 +416,6 @@ const ratingViewPlugin = ViewPlugin.fromClass(
416416
const start = match.index;
417417
const end = start + pattern.length;
418418

419-
// Skip if too short (minimum 3 symbols) - use Unicode length
420-
const unicodeLength = getUnicodeCharLength(pattern);
421-
if (unicodeLength < 3) continue;
422-
423419
const symbolSet = getSymbolSetForPattern(pattern);
424420
if (!symbolSet) continue;
425421

@@ -432,8 +428,11 @@ const ratingViewPlugin = ViewPlugin.fromClass(
432428
// Check for rating text after the symbols - pass UTF-16 positions
433429
const ratingText = parseRatingText(text, start, end);
434430

435-
// For full-only symbols without rating text, they can still be interactive
436-
// (rating text will be auto-added on first interaction)
431+
// Simple rule: if no rating text, require minimum 3 symbols to avoid false positives
432+
const unicodeLength = getUnicodeCharLength(pattern);
433+
if (!ratingText && unicodeLength < 3) {
434+
continue;
435+
}
437436

438437
const actualEnd = ratingText ? ratingText.endPosition : end;
439438

@@ -515,14 +514,16 @@ const ratingViewPlugin = ViewPlugin.fromClass(
515514
const fullOnlyCount = filteredMatches.filter(m => isFullOnlySymbol(m.symbolSet)).length;
516515
const commentCount = filteredMatches.filter(m => m.ratingText && m.ratingText.format === 'comment-fraction').length;
517516
const autoAddCandidates = filteredMatches.filter(m => isFullOnlySymbol(m.symbolSet) && !m.ratingText).length;
517+
const shortPatternCount = filteredMatches.filter(m => getUnicodeCharLength(m.pattern) < 3).length;
518518
console.debug(`[InteractiveRatings] Built ${filteredMatches.length - skippedCount}/${filteredMatches.length} rating decorations (${skippedCount} skipped due to cursor proximity)`, {
519519
cursorPos,
520520
withRatingText: filteredMatches.filter(m => m.ratingText).length,
521521
symbolsOnly: filteredMatches.filter(m => !m.ratingText).length,
522522
withHalfSymbols: filteredMatches.filter(m => m.symbolSet.half).length,
523523
fullOnlySymbols: fullOnlyCount,
524524
commentFormatSymbols: commentCount,
525-
autoAddCandidates
525+
autoAddCandidates,
526+
shortPatterns: shortPatternCount
526527
});
527528
}
528529

β€Žsrc/ratings-parser.tsβ€Ž

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ function escapeRegexChar(char: string): string {
167167
/**
168168
* Generate regex patterns for detecting rating symbols in text
169169
* Fixed to handle multibyte Unicode characters (emojis) properly
170+
* Now supports 1+ symbols to detect low ratings
170171
*/
171172
export function generateSymbolRegexPatterns(): RegExp[] {
172173
return SYMBOL_PATTERNS.map(symbols => {
@@ -178,13 +179,15 @@ export function generateSymbolRegexPatterns(): RegExp[] {
178179

179180
// Escape each symbol for regex and create alternation
180181
const escapedSymbols = symbolChars.map(escapeRegexChar);
181-
const pattern = `(?:${escapedSymbols.join('|')}){3,}`;
182+
// Changed from {3,} to {1,} to detect single symbols (fixes low rating bug)
183+
const pattern = `(?:${escapedSymbols.join('|')})+`;
182184

183185
if (LOGGING_ENABLED) {
184186
console.debug(`[InteractiveRatings] Generated regex pattern for symbol set`, {
185187
symbolSet: symbols,
186188
pattern,
187-
escapedSymbols
189+
escapedSymbols,
190+
allowsSingleSymbol: true
188191
});
189192
}
190193

β€Žsrc/settings.tsβ€Ž

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { App, PluginSettingTab, Setting } from 'obsidian';
2+
import { InteractiveRatingsPlugin } from './InteractiveRatingsPlugin';
3+
import { InteractiveRatingsSettings } from './types';
4+
5+
const DEFAULT_SUPPORTED_EMOJIS = 'πŸŽ₯πŸ†β­πŸ’ŽπŸ”₯βš‘πŸŽ―πŸš€πŸ’°πŸŽ–οΈ';
6+
7+
export const DEFAULT_SETTINGS: InteractiveRatingsSettings = {
8+
supportedEmojis: DEFAULT_SUPPORTED_EMOJIS
9+
};
10+
11+
export class InteractiveRatingsSettingTab extends PluginSettingTab {
12+
plugin: InteractiveRatingsPlugin;
13+
14+
constructor(app: App, plugin: InteractiveRatingsPlugin) {
15+
super(app, plugin);
16+
this.plugin = plugin;
17+
}
18+
19+
display(): void {
20+
const { containerEl } = this;
21+
22+
containerEl.empty();
23+
24+
containerEl.createEl('h2', { text: 'Interactive Ratings Settings' });
25+
26+
new Setting(containerEl)
27+
.setName('Emojis to support in ratings')
28+
.setDesc('Emojis to use for ratings.')
29+
.addTextArea(text => text
30+
.setPlaceholder(DEFAULT_SUPPORTED_EMOJIS)
31+
.setValue(this.plugin.settings.supportedEmojis)
32+
.onChange(async (value) => {
33+
this.plugin.settings.supportedEmojis = value;
34+
await this.plugin.saveSettings();
35+
// Update the symbol patterns with new emojis
36+
this.plugin.updateSymbolPatterns();
37+
}));
38+
}
39+
}

β€Žsrc/types.tsβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ export interface RatingText {
1111
text: string;
1212
endPosition: number;
1313
}
14+
15+
export interface InteractiveRatingsSettings {
16+
supportedEmojis: string;
17+
}

β€Žsrc/utils.tsβ€Ž

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { LOGGING_ENABLED } from './constants';
2-
import { SymbolSet } from './types';
1+
import { LOGGING_ENABLED, BASE_SYMBOL_PATTERNS, updateSymbolPatterns } from './constants';
2+
import { SymbolSet, InteractiveRatingsSettings } from './types';
33

44
/**
55
* Get the length of a string in Unicode characters
@@ -32,6 +32,40 @@ export function isFullOnlySymbol(symbolSet: SymbolSet): boolean {
3232
return symbolSet.full === symbolSet.empty && !symbolSet.half;
3333
}
3434

35+
/**
36+
* Update symbol patterns based on user settings
37+
*/
38+
export function updateSymbolPatternsFromSettings(settings: InteractiveRatingsSettings): void {
39+
// Start with base patterns (all the existing ones except the customizable emojis)
40+
const newPatterns = [...BASE_SYMBOL_PATTERNS];
41+
42+
// Add emoji patterns from settings
43+
if (settings.supportedEmojis) {
44+
// Split the emoji string into individual characters (handling Unicode properly)
45+
const emojis = [...settings.supportedEmojis];
46+
47+
for (const emoji of emojis) {
48+
if (emoji.trim()) { // Skip empty characters
49+
newPatterns.push({
50+
full: emoji,
51+
empty: emoji,
52+
half: null
53+
});
54+
}
55+
}
56+
}
57+
58+
// Update the global symbol patterns
59+
updateSymbolPatterns(newPatterns);
60+
61+
if (LOGGING_ENABLED) {
62+
console.debug('[InteractiveRatings] Updated symbol patterns from settings', {
63+
supportedEmojis: settings.supportedEmojis,
64+
totalPatterns: newPatterns.length
65+
});
66+
}
67+
}
68+
3569
/**
3670
* Calculate the new rating based on cursor position
3771
*/

0 commit comments

Comments
Β (0)