Skip to content

dsh-birthday

Verified

dsh-birthday · v0.2.1 · MIT · Web UI

DSH plugin: multi-calendar birthday library plus a 万年历 (perpetual calendar) — Gregorian / Lunar / Islamic / Hebrew with pluggable adapters

Install

dsh plugin add dsh-birthday

Confirm the layer applied with dsh --profile default --dump-config — see the install guide.

Source

Tags

Creators

Readme

dsh-birthday

A multi-calendar birthday library for the DSH plugin ecosystem. Records birthdays in any of four built-in calendars — 公历 (Gregorian), 中国农历 (Chinese lunar), 伊斯兰历 (Islamic Hijri), 希伯来历 (Hebrew) — and shows them in a sidebar panel with cross-timezone-aware reminders.

All data stays on your machine; the plugin makes no network calls.

Features

  • Four calendars, with a pluggable adapter API for more (Tibetan, Mayan, …).
  • 万年历 (perpetual calendar) window: a Gregorian month grid carrying each day's 农历 date, 节气 and festivals, plus a detail panel for the selected day with 干支, 生肖, 宜 and 忌. Jump by month, year, or any date.
  • Gregorian hub: every record is anchored to a concrete Gregorian date, so reminders and sorting work uniformly across calendars.
  • Leap-month aware: the Chinese form offers the 闰月 tick only when the selected month really is doubled that year, and tells you which one it is.
  • 时辰 (Chinese double-hour) select for lunar birthdays, defaulting to 辰.
  • Age (completed years, correct across the date line) on each card.
  • 生肖 from the lunar year, so a Gregorian birthday gets one too.
  • Smart cross-timezone reminders: when the birth timezone and yours differ, the reminder is pinned to a wall-clock time you can act on.
  • Degradation, not failure: a record whose date has no equivalent in the current year (a leap-month birthday in a year without that month) is flagged and still resolvable, instead of throwing.
  • Export / import as a versioned JSON envelope; derived values are recomputed on import rather than trusted.

Install

Requires DSH 0.2.0-rc.2 or a compatible release.

Install

dsh plugin --profile web add dsh-birthday

That is the whole install: DSH forwards its arguments to pnpm in the profile directory and then registers the bundle itself, appending dsh-birthday to dsh.profile.bundles. Verified end to end in a empty DSH_HOME: the command exits 0, the package lands in profiles/web/node_modules/, and dsh --dump-config --profile web composes the entry with the configuration from cordis.patch.yml.

Then start (or restart) DSH and hard-refresh the browser:

dsh web

Installing through the in-app plugin manager (Settings → Plugins) is equivalent.

Other install sources

# a local tarball
npm pack
dsh plugin --profile web add ./dsh-birthday-0.2.1.tgz

# a git checkout
dsh plugin --profile web add github:stevending2015/dsh-birthday

The build output is not committed, so the git route builds on install (prepare) and needs a working Node/TypeScript toolchain. Prefer the registry if you only want to use the plugin.

Uninstall

dsh plugin --profile web remove dsh-birthday

Your records live outside the package and are left alone; delete <DSH_HOME>/dsh-birthday/ to remove them too.

What it adds to DSH

  • Sidebar panel 「生日」 (slot sidebar.panellist + main) with a 4-column card grid. Cards show the age, the calendar date, 时辰, 星座 and 生肖, tinted light blue for male and pink for female. Each card has 查看 / 编辑 / 删除 buttons. A 📅 万年历 button next to 刷新 opens the perpetual-calendar window.
  • 9 agent tools: birthday_add, birthday_query, birthday_update, birthday_delete, birthday_upcoming, birthday_almanac, birthday_db_info, birthday_export, birthday_import.
  • HTTP API under /api/birthday/*: list, calendars, year-info, almanac, db-info, export, upcoming, add, update, delete, import. See docs/birthday-api.md.

Configuration

Defaults ship in cordis.patch.yml. Override them in your profile's own cordis.patch.yml (the user layer), targeting the entry by id:

- id: dsh-birthday
  config:
    enabledCalendars: [gregorian, lunar]
    defaultTimezone: Asia/Shanghai
    reminderDaysBefore: [3, 1]
key default meaning
timezoneEngine system system uses the platform's Intl zone data
enabledCalendars all four which adapters to register
allowThirdPartyAdapters true permit runtime adapter registration
dbPath '' → derived explicit database path; empty means derive one (see below)
defaultTimezone Asia/Shanghai fallback birth timezone
reminderDaysBefore [3, 1] how many days ahead to remind
leapMonthStrategy normal handling for leap-month birthdays

Where your data lives

With dbPath empty the path is derived, and every source is independent of the directory dsh was launched from — otherwise the same library would split across files and look like data loss after a restart. Resolution order:

  1. config.dbPath (explicit)
  2. $DSH_HOME
  3. the host's dshHomePath service
  4. this package's own install location (<home>/profiles/<profile>/node_modules/... → walk up to profiles)
  5. ~/.dsh, when it contains profiles
  6. <cwd>/data/dsh-birthday.db (only outside DSH)

The engine is chosen by what the file actually is, not by which modules happen to load: a JSON store stays JSON even if the native SQLite driver becomes available later. That matters because handing a JSON file to SQLite would start an empty database over your records.

  • SQLite via better-sqlite3 when it is genuinely usable. That driver is an optional peer, not a dependency, so it is never installed for you: its native build script is blocked by pnpm's default policy, and installing it anyway would make dsh plugin … add exit non-zero on a fresh machine. Add it yourself if you want the SQLite engine:

    dsh plugin --profile web add better-sqlite3
    pnpm approve-builds      # in the profile directory, to allow the native build
    
  • JSON otherwise, which is the default. Writes are atomic (temp file → fsync → rename), so an interrupted write cannot truncate the store, and an unparseable file is moved aside as <name>.corrupt-<timestamp> rather than silently reset.

Check the live state at any time with the birthday_db_info tool or GET /api/birthday/db-info; it reports the path, engine, byte-level format and whether the store is encrypted.

Development

npm install
npm run build:all   # tsc → lib/, esbuild → client/client.js
npm run lint        # type-check sources and tests, no emit
npm test            # vitest
npm run verify      # integration harness (real HTTP + real defineTool)
npm run verify:client
npm run check       # all of the above

npm pack runs prepack, so the tarball always contains a fresh build.

Documentation

License

MIT — see LICENSE.