Skip to content

chenkunqing/label-kit

Repository files navigation

@chenkunqing/label-kit

English | 简体中文

Table of Contents

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.

Feature Overview

This package currently provides 6 reusable capability areas:

1. Hashtag parsing

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

2. Hashtag highlighting

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

3. Suggestion item generation

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

4. Label resolve-or-create flow

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

5. Label binding diff calculation

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

6. Label color and chip styling

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:

  • getTagChipStyle
  • getTagColorValue
  • TAG_COLOR_KEYS
  • TAG_COLOR_PRESETS
  • DEFAULT_TAG_TEXT_COLOR

Status

  • 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

Why not just copy the code?

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.

Install

npm install @chenkunqing/label-kit

Quick Start

import {
  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');

API

parseHashtags(html: string): string[]

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 #FFF and #FF0000

Example:

parseHashtags('<p>#设计 #2026-04-24 #ND17027/V1</p>');

highlightHashtags(html: string, className = 'hashtag-tag'): string

Wraps hashtags in a <span> with the given class name while preserving existing HTML tags.

Example:

highlightHashtags('<p>#design</p>');

buildHashtagSuggestionItems(labels, query, limit = 10): TagSuggestionItem[]

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',
);

resolveTagIds({ names, existing, create, matches? }): Promise<string[]>

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 #hashtags to 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[];
};

getTagChipStyle(colorKey: string): TagChipStyle | undefined

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)'
// }

getTagColorValue(colorKey: string, isDark: boolean): string

Returns the solid color value for a tag or note surface.

Exported constants

  • HASHTAG_NAME_SOURCE
  • HASHTAG_QUERY_SOURCE
  • DEFAULT_TAG_TEXT_COLOR
  • TAG_COLOR_KEYS
  • TAG_COLOR_PRESETS

Exported types

  • TagColorKey
  • TagEntity
  • TagSuggestionItem
  • TagChipStyle
  • ResolveTagIdsOptions
  • ComputeHashtagTagChangesOptions
  • HashtagTagChanges

Example Integration

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;
}

Demo

There is a minimal runnable demo in demo/minimal-demo.mjs.

Run it with:

npm run demo

Local Development

npm install
npm run build
npm run check
npm run pack:preview

Publish Checklist

  1. Update the package name if needed.
  2. Update version in package.json.
  3. Update repository metadata if this folder has been moved to its own repo.
  4. Run npm run prepublishOnly.
  5. Publish with npm publish.

Not Included

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.

Best Fit

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

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors