- Feature Overview
- Status
- Why not just copy the code?
- Install
- Quick Start
- API
- Example Integration
- Demo
- Local Development
- Publish Checklist
- Not Included
- Best Fit
Framework-agnostic label and hashtag utilities for apps that need:
- hashtag parsing from rich text or HTML content
- label suggestion list generation
- label create-or-resolve flows
- content diff helpers for binding and unbinding labels
- consistent label chip color styling
This package is the extracted core from a real app. It stays focused on reusable logic and does not depend on a specific state library, router, or API client.
This package currently provides 6 reusable capability areas:
Extract #tags from rich text or HTML content.
Typical uses:
- scan editor content before save
- extract hashtags from notes, documents, or comments
- sync in-content hashtags into structured label relations
Main API:
parseHashtags
Wrap hashtags in styled HTML spans so cards, previews, and read-only views can render them consistently.
Typical uses:
- tag highlighting in content cards
- read-only content previews
- post-processing rendered HTML
Main API:
highlightHashtags
Build autocomplete items from an existing label list plus the current query, including a create option when needed.
Typical uses:
- autocomplete dropdown after typing
# - label search panels
- instant filtering in label pickers
Main API:
buildHashtagSuggestionItems
Resolve label ids from label names, and create missing labels through your own callback.
Typical uses:
- auto-create labels during save
- batch map names to stored ids
- implement a find-or-create label workflow
Main API:
resolveTagIds
Compare old and new content and compute which label bindings should be added or removed.
Typical uses:
- sync labels when an editor closes
- incremental updates instead of rebuilding all bindings
- map text hashtags into structured label relations
Main API:
computeHashtagTagChanges
Provide color presets, color keys, and ready-to-use chip style objects.
Typical uses:
- consistent label chip styling
- centralized label color themes
- unified tag visuals across cards, details, and pickers
Main API and constants:
getTagChipStylegetTagColorValueTAG_COLOR_KEYSTAG_COLOR_PRESETSDEFAULT_TAG_TEXT_COLOR
- Ready to publish as a standalone npm package
- Current package name is
@chenkunqing/label-kit - If you move this folder into a new GitHub repository, you will usually only need to change the package name and repository metadata before publishing
Copying the code is fine for a one-off internal project, but it usually stops scaling once multiple apps start evolving independently.
Using a package instead of copy-paste helps because:
- fixes are made once and reused everywhere
- API boundaries become explicit instead of leaking app-specific code
- upgrades are easier to review because changes are versioned
- tests and docs stay attached to the reusable unit
- future apps can install the package directly instead of repeating extraction work
If your use case is truly single-project and short-lived, copying may still be acceptable. This package exists for the case where label behavior is expected to survive across products.
npm install @chenkunqing/label-kitimport {
parseHashtags,
highlightHashtags,
buildHashtagSuggestionItems,
resolveTagIds,
computeHashtagTagChanges,
getTagChipStyle,
} from '@chenkunqing/label-kit';
const names = parseHashtags('<p>#design #roadmap</p>');
// ['design', 'roadmap']
const highlighted = highlightHashtags('<p>#design #roadmap</p>');
// <p><span class="hashtag-tag">#design</span> <span class="hashtag-tag">#roadmap</span></p>
const suggestions = buildHashtagSuggestionItems(
[
{ id: '1', name: 'design' },
{ id: '2', name: 'roadmap' },
],
'des',
);
const chipStyle = getTagChipStyle('blue');Extracts unique hashtag names from rich text or HTML strings.
Behavior:
- strips HTML tags before parsing
- supports Unicode letters and numbers
- supports
_,-, and/ - ignores pure 3-digit and 6-digit hex colors such as
#FFFand#FF0000
Example:
parseHashtags('<p>#设计 #2026-04-24 #ND17027/V1</p>');Wraps hashtags in a <span> with the given class name while preserving existing HTML tags.
Example:
highlightHashtags('<p>#design</p>');Filters an existing label list and optionally appends a create option when the query does not already exist.
Useful for:
- autocomplete dropdowns
- inline editor suggestions
- label search popovers
Example:
buildHashtagSuggestionItems(
[
{ id: '1', name: 'design' },
{ id: '2', name: 'research' },
],
'new-label',
);Resolves label ids from label names. If a label does not exist, the provided create function is called.
Typical integration:
const ids = await resolveTagIds({
names: ['design', 'roadmap'],
existing: labels,
create: async (name) => api.createLabel(name),
});computeHashtagTagChanges({ previousContent, nextContent, existingTagIds, resolveTagIds }): Promise<HashtagTagChanges>
Compares old and new content and returns which resolved label ids should be added or removed.
Useful for:
- syncing
#hashtagsto explicit label relations - editor close/save hooks
- background content sync flows
Returned shape:
type HashtagTagChanges = {
nextTagNames: string[];
previousTagNames: string[];
nextTagIds: string[];
previousTagIds: string[];
toAdd: string[];
toRemove: string[];
};Returns a tinted chip style object for known color keys.
Example:
getTagChipStyle('blue');
// {
// backgroundColor: 'rgba(44, 147, 253, 0.14)',
// color: '#145f96',
// borderColor: 'rgba(44, 147, 253, 0.22)'
// }Returns the solid color value for a tag or note surface.
HASHTAG_NAME_SOURCEHASHTAG_QUERY_SOURCEDEFAULT_TAG_TEXT_COLORTAG_COLOR_KEYSTAG_COLOR_PRESETS
TagColorKeyTagEntityTagSuggestionItemTagChipStyleResolveTagIdsOptionsComputeHashtagTagChangesOptionsHashtagTagChanges
import {
computeHashtagTagChanges,
resolveTagIds,
} from '@chenkunqing/label-kit';
async function syncLabels({
previousContent,
nextContent,
labels,
createLabel,
existingLabelIds,
}: {
previousContent: string;
nextContent: string;
labels: Array<{ id: string; name: string }>;
createLabel: (name: string) => Promise<{ id: string; name: string } | null>;
existingLabelIds: string[];
}) {
const changes = await computeHashtagTagChanges({
previousContent,
nextContent,
existingTagIds: existingLabelIds,
resolveTagIds: (names) =>
resolveTagIds({
names,
existing: labels,
create: createLabel,
}),
});
return changes;
}There is a minimal runnable demo in demo/minimal-demo.mjs.
Run it with:
npm run demonpm install
npm run build
npm run check
npm run pack:preview- Update the package name if needed.
- Update
versioninpackage.json. - Update repository metadata if this folder has been moved to its own repo.
- Run
npm run prepublishOnly. - Publish with
npm publish.
This package currently does not include:
- React UI components
- Tiptap extensions
- Zustand stores
- API clients
- app-specific routing or persistence
Those pieces should stay in adapters in the consuming app, or be added later as separate exports if you want a broader package.
This package is a good fit when:
- your app has a real label system and also supports inline
#hashtags - you need to keep text hashtags and structured label relations in sync
- you want a reusable label capability layer independent from app framework details
- you expect to reuse the same label rules across multiple products