refactor(component): cmdk ordering (#5722)

Replace internal CMDK command filtering/sorting logic.
The new implementation includes the following for command scoring:
- categories weights
- highlighted fragments
- original command score value

The new logic should be much cleaner and remove some hacks in the original implementation.

Not sure if this is optimal yet. Could be changed later.

fix https://github.com/toeverything/AFFiNE/issues/5699
This commit is contained in:
Peng Xiao
2024-02-16 13:20:24 +00:00
parent 9e7eb5629c
commit d1c4e6141a
13 changed files with 304 additions and 189 deletions
@@ -1,33 +0,0 @@
import { describe, expect, test } from 'vitest';
import { highlightTextFragments } from '../affine/use-highlight';
describe('highlightTextFragments', () => {
test('should correctly highlight full matches', () => {
const highlights = highlightTextFragments('This is a test', 'is');
expect(highlights).toStrictEqual([
{ text: 'Th', highlight: false },
{ text: 'is', highlight: true },
{ text: ' is a test', highlight: false },
]);
});
test('highlight with space', () => {
const result = highlightTextFragments('Hello World', 'lo w');
expect(result).toEqual([
{ text: 'Hel', highlight: false },
{ text: 'lo W', highlight: true },
{ text: 'orld', highlight: false },
]);
});
test('should correctly perform partial matching', () => {
const highlights = highlightTextFragments('Hello World', 'hw');
expect(highlights).toStrictEqual([
{ text: 'H', highlight: true },
{ text: 'ello ', highlight: false },
{ text: 'W', highlight: true },
{ text: 'orld', highlight: false },
]);
});
});
@@ -1,50 +0,0 @@
import { useMemo } from 'react';
function* highlightTextFragmentsGenerator(text: string, query: string) {
const lowerCaseText = text.toLowerCase();
let startIndex = lowerCaseText.indexOf(query);
if (startIndex !== -1) {
if (startIndex > 0) {
yield { text: text.substring(0, startIndex), highlight: false };
}
yield {
text: text.substring(startIndex, startIndex + query.length),
highlight: true,
};
if (startIndex + query.length < text.length) {
yield {
text: text.substring(startIndex + query.length),
highlight: false,
};
}
} else {
startIndex = 0;
for (const char of query) {
const pos = text.toLowerCase().indexOf(char, startIndex);
if (pos !== -1) {
if (pos > startIndex) {
yield {
text: text.substring(startIndex, pos),
highlight: false,
};
}
yield { text: text.substring(pos, pos + 1), highlight: true };
startIndex = pos + 1;
}
}
if (startIndex < text.length) {
yield { text: text.substring(startIndex), highlight: false };
}
}
}
export function highlightTextFragments(text: string, query: string) {
return Array.from(highlightTextFragmentsGenerator(text, query));
}
export function useHighlight(text: string, query: string) {
return useMemo(() => highlightTextFragments(text, query), [text, query]);
}
@@ -126,6 +126,7 @@ export function useRegisterBlocksuiteEditorCommands(
})
);
// todo: should not show duplicate for journal
unsubs.push(
registerAffineCommand({
id: `editor:${mode}-duplicate`,