dsh-featherhunter-plugin-base 0.0.0-stage → 0.0.2-drill
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +46 -2
- package/data/img/card-feishu.png +0 -0
- package/data/img/card-qq.png +0 -0
- package/data/img/card-wechat-2026-10-23.png +0 -0
- package/data/img/card-wechat-2026-11-06.png +0 -0
- package/data/img/qr-feishu.png +0 -0
- package/data/img/qr-qq.png +0 -0
- package/data/img/qr-wechat-2026-10-23.png +0 -0
- package/data/img/qr-wechat-2026-11-06.png +0 -0
- package/data/manifest.json +158 -0
- package/dist/client/content.d.ts +116 -0
- package/dist/client/index.d.ts +15 -0
- package/dist/client/index.js +1317 -0
- package/dist/client/modal.d.ts +42 -0
- package/dist/client/plugins.d.ts +44 -0
- package/dist/client/styles.d.ts +13 -0
- package/dist/client/texts.d.ts +11 -0
- package/dist/client/types.d.ts +90 -0
- package/dist/core/baseline.d.ts +2 -0
- package/dist/core/index.d.ts +40 -0
- package/dist/core/index.js +358 -0
- package/dist/core/types.d.ts +84 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +797 -0
- package/dist/node/index.d.ts +17 -0
- package/dist/node/index.js +1220 -0
- package/dist/node/readme-compare.d.ts +199 -0
- package/dist/node/readme-sync.d.ts +143 -0
- package/dist/node/renderers.d.ts +48 -0
- package/dist/node/types.d.ts +56 -0
- package/package.json +75 -4
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Group, Lang } from "../core/types";
|
|
2
|
+
import { type TextOverrides } from "./texts";
|
|
3
|
+
/** Everything the modal needs; built by createBaseUI's openContactModal. */
|
|
4
|
+
export interface ContactModalConfig {
|
|
5
|
+
doc: Document;
|
|
6
|
+
lang: Lang;
|
|
7
|
+
groups: readonly Group[];
|
|
8
|
+
/** Already clamped to 1..4 columns (and to the group count). */
|
|
9
|
+
columns: number;
|
|
10
|
+
trigger: HTMLElement | null;
|
|
11
|
+
fallbackUrl: string;
|
|
12
|
+
texts?: TextOverrides;
|
|
13
|
+
/** Returns null when no image is available (card degrades to a link). */
|
|
14
|
+
resolveQr: (group: Group) => string | null;
|
|
15
|
+
/** Clock override for date-dependent validity text. */
|
|
16
|
+
now?: Date;
|
|
17
|
+
}
|
|
18
|
+
export interface ContactModalUpdate {
|
|
19
|
+
groups?: readonly Group[];
|
|
20
|
+
/** Already clamped to 1..4 columns. */
|
|
21
|
+
columns?: number;
|
|
22
|
+
/** Returns null when no image is available (card degrades to a link). */
|
|
23
|
+
resolveQr?: (group: Group) => string | null;
|
|
24
|
+
fallbackUrl?: string;
|
|
25
|
+
now?: Date;
|
|
26
|
+
}
|
|
27
|
+
export interface ContactModalHandle {
|
|
28
|
+
dialog: HTMLDialogElement;
|
|
29
|
+
/** Destroy the dialog; restore focus by default. */
|
|
30
|
+
close: (restoreFocus?: boolean) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Re-render the group cards in place. Used when the remote manifest arrives
|
|
33
|
+
* after the baseline was painted, so the dialog is never rebuilt.
|
|
34
|
+
*/
|
|
35
|
+
update: (next: ContactModalUpdate) => void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build the contact dialog, enter it into the top layer with showModal() and
|
|
39
|
+
* wire the close paths (Esc, backdrop, close button). The dialog element is
|
|
40
|
+
* the public seam: tests and hosts can query it in the document.
|
|
41
|
+
*/
|
|
42
|
+
export declare function mountContactModal(config: ContactModalConfig): ContactModalHandle;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type * as ReactNamespace from "react";
|
|
2
|
+
import type { Lang, PluginEntry } from "../core/types";
|
|
3
|
+
/**
|
|
4
|
+
* Default row cap of the author plugin card. The list is the five finalised
|
|
5
|
+
* plugins (contact/plugins.json); after dropping the host plugin four remain,
|
|
6
|
+
* so five is a natural ceiling that never hides a sibling by accident.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_MAX_ROWS = 5;
|
|
9
|
+
/** Options that decide which plugin rows are visible. */
|
|
10
|
+
export interface AuthorPluginRowsOptions {
|
|
11
|
+
/** Full plugin list, usually `manifest.plugins`. */
|
|
12
|
+
plugins: readonly PluginEntry[];
|
|
13
|
+
/** Id of the host plugin; excluded unless `showSelf` is on. */
|
|
14
|
+
selfId?: string;
|
|
15
|
+
/** Show the host plugin itself. */
|
|
16
|
+
showSelf?: boolean;
|
|
17
|
+
/** Hard cap on rendered rows (default {@link DEFAULT_MAX_ROWS}). */
|
|
18
|
+
maxRows?: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Visible rows: data order (order asc), host plugin dropped unless asked for,
|
|
22
|
+
* then capped. An undecorated list keeps the exact order of the manifest.
|
|
23
|
+
*/
|
|
24
|
+
export declare function selectAuthorPlugins(options: AuthorPluginRowsOptions): PluginEntry[];
|
|
25
|
+
/**
|
|
26
|
+
* Link target of one row: the explicit `url` when the data provides one,
|
|
27
|
+
* otherwise the canonical GitHub repository page.
|
|
28
|
+
*/
|
|
29
|
+
export declare function pluginHref(plugin: PluginEntry): string;
|
|
30
|
+
/** Everything the pure renderer needs; built by createBaseUI's wire-up. */
|
|
31
|
+
export interface RenderAuthorPluginsOptions extends AuthorPluginRowsOptions {
|
|
32
|
+
/** Injected runtime: the package never imports React itself. */
|
|
33
|
+
React: typeof ReactNamespace;
|
|
34
|
+
lang: Lang;
|
|
35
|
+
/** Already localised, already overridden card title. */
|
|
36
|
+
title: string;
|
|
37
|
+
className?: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Render the settings-panel card, or null when nothing is left to show: an
|
|
41
|
+
* empty list, a failed manifest, or a list containing only the host plugin
|
|
42
|
+
* must not leave an empty container in the settings page.
|
|
43
|
+
*/
|
|
44
|
+
export declare function renderAuthorPlugins(options: RenderAuthorPluginsOptions): ReactNamespace.ReactElement | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Style element id shared by every widget of this package. */
|
|
2
|
+
export declare const BASE_STYLE_ID = "dsh-base-style";
|
|
3
|
+
/**
|
|
4
|
+
* One stylesheet for the whole package, injected once per document.
|
|
5
|
+
*
|
|
6
|
+
* The modal mirrors a README-in-a-dialog: 800px panel, centered heading,
|
|
7
|
+
* centered card grid (four columns at most) and 10-13px type. Narrow screens
|
|
8
|
+
* collapse the grid through media queries because the inline column count only
|
|
9
|
+
* expresses the data-driven part of the layout.
|
|
10
|
+
*/
|
|
11
|
+
export declare const BASE_CSS: string;
|
|
12
|
+
/** Inject the stylesheet once per document. */
|
|
13
|
+
export declare function ensureBaseStyles(doc: Document): void;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Lang } from "../core/types";
|
|
2
|
+
/** Message ids shared by the widgets and the contact modal. */
|
|
3
|
+
export type TextKey = "contact" | "contactHint" | "kicker" | "modalTitle" | "welcome" | "scanHint" | "mobileHint" | "close" | "copyHint" | "copied" | "expired" | "fallback" | "plugins";
|
|
4
|
+
export type TextOverrides = Partial<Record<string, string>>;
|
|
5
|
+
/**
|
|
6
|
+
* Built-in copy. The wording mirrors the README contact block so the settings
|
|
7
|
+
* panel and the README read the same; callers can override any message id.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MESSAGES: Record<Lang, Record<TextKey, string>>;
|
|
10
|
+
/** Resolve one message id: caller overrides win, then the detected language. */
|
|
11
|
+
export declare function resolveText(key: TextKey, lang: Lang, overrides?: TextOverrides): string;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
import type { Group, Lang, PluginEntry } from "../core/types";
|
|
3
|
+
import type { ContentLoader, FetchLike } from "./content";
|
|
4
|
+
import type { TextOverrides } from "./texts";
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the image source for a group's QR code.
|
|
7
|
+
*
|
|
8
|
+
* A null / undefined result means "no image available": the card degrades to
|
|
9
|
+
* the fallback link instead of rendering a broken or expired QR code. Ticket
|
|
10
|
+
* 05 injects a CDN resolver here; the default only reads bundled baseline
|
|
11
|
+
* codes.
|
|
12
|
+
*/
|
|
13
|
+
export type QrResolver = (group: Group) => string | null | undefined;
|
|
14
|
+
/** Options for createBaseUI. */
|
|
15
|
+
export interface BaseUIOptions {
|
|
16
|
+
/** Force a language instead of auto-detecting it. */
|
|
17
|
+
lang?: Lang | string;
|
|
18
|
+
/** Id of the host plugin; excluded from the author plugin list. */
|
|
19
|
+
selfId?: string;
|
|
20
|
+
/** Manifest URL override (defaults to the published CDN manifest). */
|
|
21
|
+
manifestUrl?: string;
|
|
22
|
+
/** Fetch implementation override (testing seam). */
|
|
23
|
+
fetchImpl?: FetchLike;
|
|
24
|
+
/** Pre-built content loader override (testing seam). */
|
|
25
|
+
contentLoader?: ContentLoader;
|
|
26
|
+
/** Text overrides keyed by message id. */
|
|
27
|
+
texts?: TextOverrides;
|
|
28
|
+
/** Max columns of the contact grid (hard ceiling: 4). */
|
|
29
|
+
columns?: number;
|
|
30
|
+
/** URL used when a group QR code expired or is unavailable. */
|
|
31
|
+
fallbackUrl?: string;
|
|
32
|
+
/** QR image resolver; defaults to the bundled baseline data URIs. */
|
|
33
|
+
resolveQr?: QrResolver;
|
|
34
|
+
/** Show the host plugin itself in the author plugin list. */
|
|
35
|
+
showSelf?: boolean;
|
|
36
|
+
/** Max rows of the author plugin list (default 5). */
|
|
37
|
+
maxRows?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface ContactButtonProps {
|
|
40
|
+
className?: string;
|
|
41
|
+
/** Overrides the default tooltip / aria label. */
|
|
42
|
+
title?: string;
|
|
43
|
+
/** Render only the icon. */
|
|
44
|
+
iconOnly?: boolean;
|
|
45
|
+
/** Icon size in px (14 / 16 / 18, default 16). */
|
|
46
|
+
size?: 14 | 16 | 18;
|
|
47
|
+
}
|
|
48
|
+
export interface ContactRowProps {
|
|
49
|
+
className?: string;
|
|
50
|
+
label?: string;
|
|
51
|
+
/** Small hint next to the button. */
|
|
52
|
+
hint?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface AuthorPluginsProps {
|
|
55
|
+
className?: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
lang?: Lang;
|
|
58
|
+
/** Id of the host plugin; defaults to the createBaseUI option. */
|
|
59
|
+
selfId?: string;
|
|
60
|
+
/** Show the host plugin itself instead of dropping it. */
|
|
61
|
+
showSelf?: boolean;
|
|
62
|
+
plugins?: PluginEntry[];
|
|
63
|
+
/** Hard cap on rendered rows (default 5). */
|
|
64
|
+
maxRows?: number;
|
|
65
|
+
}
|
|
66
|
+
export interface OpenContactModalOptions {
|
|
67
|
+
/** Element that opened the modal; focus returns here on close. */
|
|
68
|
+
trigger?: HTMLElement | null;
|
|
69
|
+
/** Groups override (defaults to the baseline manifest). Empty falls back. */
|
|
70
|
+
groups?: Group[];
|
|
71
|
+
lang?: Lang | string;
|
|
72
|
+
texts?: TextOverrides;
|
|
73
|
+
/** Max columns of the contact grid (hard ceiling: 4). */
|
|
74
|
+
columns?: number;
|
|
75
|
+
/** URL used when a group QR code expired or is unavailable. */
|
|
76
|
+
fallbackUrl?: string;
|
|
77
|
+
/** QR image resolver; defaults to the bundled baseline data URIs. */
|
|
78
|
+
resolveQr?: QrResolver;
|
|
79
|
+
/** Clock override for date-dependent validity text (testing seam). */
|
|
80
|
+
now?: Date;
|
|
81
|
+
}
|
|
82
|
+
/** Widget set returned by createBaseUI. */
|
|
83
|
+
export interface BaseUI {
|
|
84
|
+
ContactButton: ComponentType<ContactButtonProps>;
|
|
85
|
+
ContactRow: ComponentType<ContactRowProps>;
|
|
86
|
+
AuthorPlugins: ComponentType<AuthorPluginsProps>;
|
|
87
|
+
openContactModal: (options?: OpenContactModalOptions) => () => void;
|
|
88
|
+
closeContactModal: () => void;
|
|
89
|
+
preloadContent: () => Promise<void>;
|
|
90
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { I18nText, Lang, Manifest } from "./types";
|
|
2
|
+
export * from "./types";
|
|
3
|
+
export { BASELINE } from "./baseline";
|
|
4
|
+
/** Pick the localized variant, falling back to the other language. */
|
|
5
|
+
export declare function pickText(text: I18nText | undefined, lang: Lang): string;
|
|
6
|
+
export interface LangHints {
|
|
7
|
+
/** Explicit user setting. */
|
|
8
|
+
explicit?: string | null;
|
|
9
|
+
/** `<html lang>` of the host page. */
|
|
10
|
+
htmlLang?: string | null;
|
|
11
|
+
/** `navigator.languages` of the host page. */
|
|
12
|
+
navigatorLanguages?: readonly string[] | null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Language fallback chain: explicit setting -> page `<html lang>` ->
|
|
16
|
+
* `navigator.languages` -> `zh`.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveLang(hints?: LangHints): Lang;
|
|
19
|
+
/** Copy sorted by display order (stable, input untouched). */
|
|
20
|
+
export declare function sortByOrder<T extends {
|
|
21
|
+
order: number;
|
|
22
|
+
}>(items: readonly T[]): T[];
|
|
23
|
+
/** Strict `YYYY-MM-DD` parser (UTC midnight). Invalid dates return null. */
|
|
24
|
+
export declare function parseDateOnly(value: string): Date | null;
|
|
25
|
+
/** `validUntil` is inclusive; a group without `validUntil` never expires. */
|
|
26
|
+
export declare function isExpired(validUntil: string | undefined, now?: Date): boolean;
|
|
27
|
+
export interface ValidityOptions {
|
|
28
|
+
lang: Lang;
|
|
29
|
+
now?: Date;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Human validity line for the modal footnote. Returns null for permanent codes
|
|
33
|
+
* and for expired ones (the caller falls back to the link).
|
|
34
|
+
*/
|
|
35
|
+
export declare function formatValidity(validUntil: string | undefined, options: ValidityOptions): string | null;
|
|
36
|
+
/**
|
|
37
|
+
* Validate a published `data/manifest.json` payload. Throws on the first
|
|
38
|
+
* structural problem so a bad deploy fails loudly instead of rendering blanks.
|
|
39
|
+
*/
|
|
40
|
+
export declare function parseManifest(raw: unknown): Manifest;
|