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.
@@ -0,0 +1,1317 @@
1
+ // src/core/baseline.ts
2
+ var BASELINE = {
3
+ "manifest": {
4
+ "schema": 1,
5
+ "version": "0.0.2-drill",
6
+ "revision": 2,
7
+ "updatedAt": "2026-10-31T00:00:00+08:00",
8
+ "fallbackUrl": "https://github.com/FeatherHunter/featherhunter-plugin-manage",
9
+ "groups": [
10
+ {
11
+ "id": "feishu",
12
+ "platform": "feishu",
13
+ "order": 10,
14
+ "readmeTitle": {
15
+ "zh": "\u98DE\u4E66\u7FA4\uFF08\u4E3B\uFF09",
16
+ "en": "Feishu Group (Main)"
17
+ },
18
+ "alt": {
19
+ "zh": "DSH \u63D2\u4EF6\u4EA4\u6D41\u7FA4 \u98DE\u4E66\u7FA4\u4E8C\u7EF4\u7801",
20
+ "en": "DSH plugin community group Feishu QR code"
21
+ },
22
+ "card": "card-feishu.png",
23
+ "qr": {
24
+ "kind": "bundled",
25
+ "key": "qr-feishu"
26
+ },
27
+ "title": {
28
+ "zh": "DSH \u63D2\u4EF6\u4EA4\u6D41\u7FA4",
29
+ "en": "DSH Plugin Community"
30
+ },
31
+ "subtitle": {
32
+ "zh": "\u7528\u6237418832\u7684\u7EC4\u7EC7",
33
+ "en": "User 418832's organization"
34
+ },
35
+ "note": {
36
+ "zh": "\u8BE5\u4E8C\u7EF4\u7801\u6C38\u4E45\u6709\u6548",
37
+ "en": "This QR code never expires"
38
+ }
39
+ },
40
+ {
41
+ "id": "qq",
42
+ "platform": "qq",
43
+ "order": 20,
44
+ "readmeTitle": {
45
+ "zh": "QQ\u7FA4\uFF08\u65B0\uFF09",
46
+ "en": "QQ Group (New)"
47
+ },
48
+ "alt": {
49
+ "zh": "DSH \u63D2\u4EF6\u4EA4\u6D41\u7FA4 QQ \u7FA4\u4E8C\u7EF4\u7801",
50
+ "en": "DSH plugin community group QQ QR code"
51
+ },
52
+ "card": "card-qq.png",
53
+ "qr": {
54
+ "kind": "bundled",
55
+ "key": "qr-qq"
56
+ },
57
+ "title": {
58
+ "zh": "DSH \u63D2\u4EF6\u4EA4\u6D41\u7FA4",
59
+ "en": "DSH Plugin Community"
60
+ },
61
+ "subtitle": {
62
+ "zh": "\u7FA4\u53F7\uFF1A1020866453",
63
+ "en": "Group ID: 1020866453"
64
+ },
65
+ "note": {
66
+ "zh": "\u626B\u4E00\u626B\u4E8C\u7EF4\u7801\uFF0C\u52A0\u5165\u7FA4\u804A",
67
+ "en": "Scan the QR code to join the group chat"
68
+ }
69
+ },
70
+ {
71
+ "id": "wechat",
72
+ "platform": "wechat",
73
+ "order": 30,
74
+ "readmeTitle": {
75
+ "zh": "\u5FAE\u4FE1\u7FA4\uFF08\u65B0\uFF09",
76
+ "en": "WeChat Group (New)"
77
+ },
78
+ "alt": {
79
+ "zh": "DSH \u63D2\u4EF6\u4EA4\u6D41\u7FA4\u5FAE\u4FE1\u7FA4\u4E8C\u7EF4\u7801",
80
+ "en": "DSH plugin community group WeChat QR code"
81
+ },
82
+ "card": "card-wechat-2026-11-06.png",
83
+ "qr": {
84
+ "kind": "remote",
85
+ "path": "qr-wechat-2026-11-06.png"
86
+ },
87
+ "title": {
88
+ "zh": "DSH \u63D2\u4EF6\u4EA4\u6D41\u7FA4",
89
+ "en": "DSH Plugin Community"
90
+ },
91
+ "subtitle": {
92
+ "zh": "\u7FA4\u804A\uFF1ADSH \u63D2\u4EF6\u4EA4\u6D41\u7FA4",
93
+ "en": "Group chat: DSH Plugin Community"
94
+ },
95
+ "validUntil": "2026-11-06"
96
+ }
97
+ ],
98
+ "plugins": [
99
+ {
100
+ "id": "deck",
101
+ "order": 10,
102
+ "verifiedAt": "2026-10-30",
103
+ "name": "dsh-mattpocock-skills-deck",
104
+ "repo": "FeatherHunter/dsh-mattpocock-skills-deck",
105
+ "desc": {
106
+ "zh": "\u8FD1 30 \u4E07 star \u7684 mattpocock/skills \u6574\u5957\u9884\u7F6E\u597D\uFF0827 \u4E2A\u6280\u80FD\uFF09\uFF0C\u88C5\u597D\u5373\u7528\u3002",
107
+ "en": "The whole mattpocock/skills set (nearly 300k stars) preinstalled \u2014 27 skills, ready as soon as it is installed."
108
+ },
109
+ "url": "https://github.com/FeatherHunter/dsh-mattpocock-skills-deck"
110
+ },
111
+ {
112
+ "id": "prompt",
113
+ "order": 20,
114
+ "verifiedAt": "2026-10-30",
115
+ "name": "dsh-prompt",
116
+ "repo": "FeatherHunter/dsh-prompt",
117
+ "desc": {
118
+ "zh": "\u7ECF\u5E38\u53CD\u590D\u91CD\u590D\u7684\u6A21\u677F\u5316\u63D0\u793A\u8BCD\uFF0C\u5B58\u6210\u81EA\u5B9A\u4E49\u6A21\u677F\uFF0C\u9009\u4E2D\u540E\u76F4\u63A5\u6CE8\u5165\uFF0C\u4E0D\u518D\u9700\u8981\u8017\u8D39\u7CBE\u529B\u3002",
119
+ "en": "The boilerplate prompts you keep retyping, saved as custom templates and injected on selection \u2014 no more wasted effort."
120
+ },
121
+ "url": "https://github.com/FeatherHunter/dsh-prompt"
122
+ },
123
+ {
124
+ "id": "palette",
125
+ "order": 30,
126
+ "verifiedAt": "2026-10-30",
127
+ "name": "dsh-opencode-palette",
128
+ "repo": "FeatherHunter/dsh-opencode-palette",
129
+ "desc": {
130
+ "zh": "38 \u6B3E opencode \u7ECF\u5178\u914D\u8272 + 20+\u81EA\u5B9A\u4E49\u4E3B\u9898 \u4E00\u952E\u6362\u88C5 DSH\u3002",
131
+ "en": "38 classic opencode themes + 20+ custom themes \u2014 reskin DSH in one click."
132
+ },
133
+ "url": "https://github.com/FeatherHunter/dsh-opencode-palette"
134
+ },
135
+ {
136
+ "id": "im",
137
+ "order": 40,
138
+ "verifiedAt": "2026-10-30",
139
+ "name": "dsh-im-companion",
140
+ "repo": "FeatherHunter/dsh-im-companion",
141
+ "desc": {
142
+ "zh": "\u7ED9 dsh-im \u52A0\u4E86\u300C\u52A9\u7406\u300D\u548C\u300C\u5BB6\u300D\u7684\u6982\u5FF5\u3002",
143
+ "en": "Adds the ideas of an \u201Cassistant\u201D and a \u201Chome\u201D to dsh-im."
144
+ },
145
+ "url": "https://github.com/FeatherHunter/dsh-im-companion"
146
+ },
147
+ {
148
+ "id": "ilife",
149
+ "order": 50,
150
+ "verifiedAt": "2026-10-30",
151
+ "name": "ilife",
152
+ "repo": "FeatherHunter/ilife",
153
+ "desc": {
154
+ "zh": "\u26A0\uFE0F\u5EFA\u8BBE\u4E2D\u26A0\uFE0F\u7231\u751F\u6D3B\uFF1A\u5C45\u5BB6\u751F\u6D3B\u5957\u4EF6\uFF0C\u8BB0\u8D26\u3001\u8FD0\u52A8\u3001\u996E\u98DF\u3001\u4F53\u91CD\u3001\u98DF\u8C31\u3001\u7269\u54C1\u7BA1\u7406\u3001\u4F5C\u606F\u89C4\u5212\u3001\u5907\u5FD8\u5F55\u7B49\u3002",
155
+ "en": "\u26A0\uFE0FWork in progress\u26A0\uFE0F iLife, the home-life suite: bookkeeping, exercise, diet, weight, recipes, item management, schedule planning, memos and more."
156
+ },
157
+ "url": "https://github.com/FeatherHunter/ilife"
158
+ }
159
+ ]
160
+ },
161
+ "qr": {
162
+ "qr-feishu": "data:image/png;base64,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",
163
+ "qr-qq": "data:image/png;base64,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"
164
+ }
165
+ };
166
+
167
+ // src/core/index.ts
168
+ var DAY_MS = 24 * 60 * 60 * 1e3;
169
+ var DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
170
+ function pickText(text, lang) {
171
+ if (!text) return "";
172
+ return text[lang] ?? text.zh ?? text.en ?? "";
173
+ }
174
+ function normalizeLang(value) {
175
+ if (!value) return null;
176
+ const lower = value.toLowerCase();
177
+ if (lower.startsWith("zh")) return "zh";
178
+ if (lower.startsWith("en")) return "en";
179
+ return null;
180
+ }
181
+ function resolveLang(hints = {}) {
182
+ const explicit = normalizeLang(hints.explicit);
183
+ if (explicit) return explicit;
184
+ const html = normalizeLang(hints.htmlLang);
185
+ if (html) return html;
186
+ for (const candidate of hints.navigatorLanguages ?? []) {
187
+ const normalized = normalizeLang(candidate);
188
+ if (normalized) return normalized;
189
+ }
190
+ return "zh";
191
+ }
192
+ function sortByOrder(items) {
193
+ return [...items].sort((a, b) => a.order - b.order);
194
+ }
195
+ function parseDateOnly(value) {
196
+ const match = DATE_ONLY_RE.exec(value);
197
+ if (!match) return null;
198
+ const year = Number(match[1]);
199
+ const month = Number(match[2]);
200
+ const day = Number(match[3]);
201
+ const date = new Date(Date.UTC(year, month - 1, day));
202
+ if (date.getUTCFullYear() !== year || date.getUTCMonth() !== month - 1 || date.getUTCDate() !== day) {
203
+ return null;
204
+ }
205
+ return date;
206
+ }
207
+ function todayUtc(now) {
208
+ return Date.UTC(now.getFullYear(), now.getMonth(), now.getDate());
209
+ }
210
+ function isExpired(validUntil, now = /* @__PURE__ */ new Date()) {
211
+ if (!validUntil) return false;
212
+ const until = parseDateOnly(validUntil);
213
+ if (!until) return false;
214
+ return until.getTime() < todayUtc(now);
215
+ }
216
+ function formatValidity(validUntil, options) {
217
+ if (!validUntil) return null;
218
+ const until = parseDateOnly(validUntil);
219
+ if (!until) return null;
220
+ const now = options.now ?? /* @__PURE__ */ new Date();
221
+ if (isExpired(validUntil, now)) return null;
222
+ const days = Math.round((until.getTime() - todayUtc(now)) / DAY_MS);
223
+ const month = until.getUTCMonth() + 1;
224
+ const day = until.getUTCDate();
225
+ if (options.lang === "en") {
226
+ return `Valid for ${days} day${days === 1 ? "" : "s"} (until ${month}/${day}), refreshed on re-entry`;
227
+ }
228
+ return `\u8BE5\u4E8C\u7EF4\u7801 ${days} \u5929\u5185\uFF08${month} \u6708 ${day} \u65E5\u524D\uFF09\u6709\u6548\uFF0C\u91CD\u65B0\u8FDB\u5165\u5C06\u66F4\u65B0`;
229
+ }
230
+ function isRecord(value) {
231
+ return typeof value === "object" && value !== null && !Array.isArray(value);
232
+ }
233
+ function requireString(record, key, where) {
234
+ const value = record[key];
235
+ if (typeof value !== "string" || value.length === 0) {
236
+ throw new Error(`manifest: ${where}.${key} must be a non-empty string`);
237
+ }
238
+ return value;
239
+ }
240
+ function requireI18n(record, key, where) {
241
+ const value = record[key];
242
+ if (!isRecord(value)) throw new Error(`manifest: ${where}.${key} must be an object`);
243
+ const zh = value.zh;
244
+ const en = value.en;
245
+ if (typeof zh !== "string" || zh.length === 0) {
246
+ throw new Error(`manifest: ${where}.${key}.zh must be a non-empty string`);
247
+ }
248
+ if (typeof en !== "string" || en.length === 0) {
249
+ throw new Error(`manifest: ${where}.${key}.en must be a non-empty string`);
250
+ }
251
+ return { zh, en };
252
+ }
253
+ function requireOrder(record, where) {
254
+ const value = record["order"];
255
+ if (typeof value !== "number" || !Number.isFinite(value)) {
256
+ throw new Error(`manifest: ${where}.order must be a number`);
257
+ }
258
+ return value;
259
+ }
260
+ function validateGroup(value, index) {
261
+ const where = `groups[${index}]`;
262
+ if (!isRecord(value)) throw new Error(`manifest: ${where} must be an object`);
263
+ const id = requireString(value, "id", where);
264
+ const platform = requireString(value, "platform", where);
265
+ const order = requireOrder(value, where);
266
+ const readmeTitle = requireI18n(value, "readmeTitle", where);
267
+ const alt = requireI18n(value, "alt", where);
268
+ const card = requireString(value, "card", where);
269
+ const title = requireI18n(value, "title", where);
270
+ const qrRaw = value["qr"];
271
+ if (!isRecord(qrRaw)) throw new Error(`manifest: ${where}.qr must be an object`);
272
+ const kind = qrRaw["kind"];
273
+ let qr;
274
+ if (kind === "bundled") {
275
+ qr = { kind: "bundled", key: requireString(qrRaw, "key", `${where}.qr`) };
276
+ } else if (kind === "remote") {
277
+ qr = { kind: "remote", path: requireString(qrRaw, "path", `${where}.qr`) };
278
+ } else {
279
+ throw new Error(`manifest: ${where}.qr.kind must be "bundled" or "remote"`);
280
+ }
281
+ const group = { id, platform, order, readmeTitle, alt, card, qr, title };
282
+ const subtitle = value["subtitle"];
283
+ if (subtitle !== void 0) {
284
+ if (!isRecord(subtitle)) throw new Error(`manifest: ${where}.subtitle must be an object`);
285
+ group.subtitle = requireI18n(value, "subtitle", where);
286
+ }
287
+ const note = value["note"];
288
+ if (note !== void 0) {
289
+ if (!isRecord(note)) throw new Error(`manifest: ${where}.note must be an object`);
290
+ group.note = requireI18n(value, "note", where);
291
+ }
292
+ const validUntil = value["validUntil"];
293
+ if (validUntil !== void 0) {
294
+ if (typeof validUntil !== "string" || parseDateOnly(validUntil) === null) {
295
+ throw new Error(`manifest: ${where}.validUntil must be a valid YYYY-MM-DD date`);
296
+ }
297
+ group.validUntil = validUntil;
298
+ }
299
+ return group;
300
+ }
301
+ function validatePlugin(value, index) {
302
+ const where = `plugins[${index}]`;
303
+ if (!isRecord(value)) throw new Error(`manifest: ${where} must be an object`);
304
+ const id = requireString(value, "id", where);
305
+ const order = requireOrder(value, where);
306
+ const verifiedAt = requireString(value, "verifiedAt", where);
307
+ if (parseDateOnly(verifiedAt) === null) {
308
+ throw new Error(`manifest: ${where}.verifiedAt must be a valid YYYY-MM-DD date`);
309
+ }
310
+ const name = requireString(value, "name", where);
311
+ const repo = requireString(value, "repo", where);
312
+ const desc = requireI18n(value, "desc", where);
313
+ const entry = { id, order, verifiedAt, name, repo, desc };
314
+ const url = value["url"];
315
+ if (url !== void 0) entry.url = requireString(value, "url", where);
316
+ return entry;
317
+ }
318
+ function parseManifest(raw) {
319
+ if (!isRecord(raw)) throw new Error("manifest: root must be an object");
320
+ if (raw["schema"] !== 1) throw new Error("manifest: schema must be 1");
321
+ const version = requireString(raw, "version", "manifest");
322
+ const revision = raw["revision"];
323
+ if (typeof revision !== "number" || !Number.isInteger(revision) || revision < 0) {
324
+ throw new Error("manifest: revision must be a non-negative integer");
325
+ }
326
+ const updatedAt = requireString(raw, "updatedAt", "manifest");
327
+ if (Number.isNaN(Date.parse(updatedAt))) {
328
+ throw new Error("manifest: updatedAt must be a parseable timestamp");
329
+ }
330
+ const fallbackUrl = requireString(raw, "fallbackUrl", "manifest");
331
+ if (!/^https?:\/\//.test(fallbackUrl)) {
332
+ throw new Error("manifest: fallbackUrl must be an absolute http(s) URL");
333
+ }
334
+ const groupsRaw = raw["groups"];
335
+ if (!Array.isArray(groupsRaw)) throw new Error("manifest: groups must be an array");
336
+ if (groupsRaw.length === 0) throw new Error("manifest: groups must not be empty");
337
+ const pluginsRaw = raw["plugins"];
338
+ if (!Array.isArray(pluginsRaw)) throw new Error("manifest: plugins must be an array");
339
+ if (pluginsRaw.length === 0) throw new Error("manifest: plugins must not be empty");
340
+ const groups = groupsRaw.map(validateGroup);
341
+ const plugins = pluginsRaw.map(validatePlugin);
342
+ const ids = /* @__PURE__ */ new Set();
343
+ for (const group of groups) {
344
+ if (ids.has(group.id)) throw new Error(`manifest: duplicate group id "${group.id}"`);
345
+ ids.add(group.id);
346
+ }
347
+ return { schema: 1, version, revision, updatedAt, fallbackUrl, groups, plugins };
348
+ }
349
+
350
+ // src/client/content.ts
351
+ var PACKAGE_NAME = "dsh-featherhunter-plugin-base";
352
+ var MANIFEST_TIMEOUT_MS = 1500;
353
+ var MANIFEST_CACHE_TTL_MS = 30 * 60 * 1e3;
354
+ var MAX_IMAGE_BYTES = 512 * 1024;
355
+ var ALLOWED_CDN_HOSTS = [
356
+ "unpkg.com",
357
+ "registry.npmmirror.com",
358
+ "cdn.jsdelivr.net"
359
+ ];
360
+ var SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
361
+ var PACKAGE_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
362
+ var IMAGE_PATH_PATTERN = /^[a-z0-9][a-z0-9._-]*\.(?:png|jpe?g)$/i;
363
+ var MAX_ID_LENGTH = 64;
364
+ var MAX_TEXT_LENGTH = 200;
365
+ var MAX_NOTE_LENGTH = 300;
366
+ var MAX_PATH_LENGTH = 128;
367
+ function fail(message) {
368
+ throw new Error("manifest: " + message);
369
+ }
370
+ function isRecord2(value) {
371
+ return typeof value === "object" && value !== null && !Array.isArray(value);
372
+ }
373
+ function requireString2(value, field, maxLength) {
374
+ if (typeof value !== "string" || value.length === 0) fail(field + " must be a non-empty string");
375
+ if (value.length > maxLength) fail(field + " is longer than " + maxLength + " characters");
376
+ return value;
377
+ }
378
+ function requireI18nText(value, field) {
379
+ if (!isRecord2(value)) fail(field + " must be an object with zh/en");
380
+ requireString2(value["zh"], field + ".zh", MAX_TEXT_LENGTH);
381
+ requireString2(value["en"], field + ".en", MAX_TEXT_LENGTH);
382
+ }
383
+ function parseUrl(value) {
384
+ try {
385
+ return new URL(value);
386
+ } catch {
387
+ return null;
388
+ }
389
+ }
390
+ function isHttpsUrl(value) {
391
+ const url = parseUrl(value);
392
+ return url !== null && url.protocol === "https:";
393
+ }
394
+ function isAllowedCdnUrl(value) {
395
+ const url = parseUrl(value);
396
+ if (!url || url.protocol !== "https:") return false;
397
+ return ALLOWED_CDN_HOSTS.includes(url.hostname);
398
+ }
399
+ function imageUrls(version, path, packageName = PACKAGE_NAME) {
400
+ if (!SEMVER_PATTERN.test(version)) return [];
401
+ if (!IMAGE_PATH_PATTERN.test(path) || path.length > MAX_PATH_LENGTH) return [];
402
+ return [
403
+ "https://cdn.jsdelivr.net/npm/" + packageName + "@" + version + "/data/img/" + path,
404
+ "https://registry.npmmirror.com/" + packageName + "/" + version + "/files/data/img/" + path
405
+ ];
406
+ }
407
+ function defaultChannels(packageName = PACKAGE_NAME) {
408
+ const unpkgUrl = "https://unpkg.com/" + packageName + "@latest/data/manifest.json";
409
+ const npmmirrorLatest = "https://registry.npmmirror.com/" + packageName + "/latest";
410
+ const jsdelivrUrl = "https://cdn.jsdelivr.net/npm/" + packageName + "@latest/data/manifest.json";
411
+ return [
412
+ {
413
+ name: "unpkg",
414
+ load: async (context) => ({ raw: JSON.parse(await context.fetchText(unpkgUrl)), url: unpkgUrl })
415
+ },
416
+ {
417
+ // Two steps on purpose: the dist-tag document names the exact version,
418
+ // and only the versioned file listing is immutable enough to trust.
419
+ name: "npmmirror",
420
+ load: async (context) => {
421
+ const latest = await context.fetchJson(npmmirrorLatest);
422
+ const version = isRecord2(latest) ? latest["version"] : void 0;
423
+ if (typeof version !== "string" || !SEMVER_PATTERN.test(version)) {
424
+ fail("npmmirror latest is missing a usable version");
425
+ }
426
+ const url = "https://registry.npmmirror.com/" + packageName + "/" + version + "/files/data/manifest.json";
427
+ return { raw: JSON.parse(await context.fetchText(url)), url };
428
+ }
429
+ },
430
+ {
431
+ name: "jsdelivr",
432
+ load: async (context) => ({
433
+ raw: JSON.parse(await context.fetchText(jsdelivrUrl)),
434
+ url: jsdelivrUrl
435
+ })
436
+ }
437
+ ];
438
+ }
439
+ function validateRemoteManifest(raw) {
440
+ const manifest = parseManifest(raw);
441
+ if (!SEMVER_PATTERN.test(manifest.version)) fail("version must be a semantic version");
442
+ if (!isHttpsUrl(manifest.fallbackUrl)) fail("fallbackUrl must be https");
443
+ const seen = /* @__PURE__ */ new Set();
444
+ for (const group of manifest.groups) {
445
+ if (group.id.length > MAX_ID_LENGTH) fail("group id is too long");
446
+ if (seen.has(group.id)) fail("duplicate group id " + group.id);
447
+ seen.add(group.id);
448
+ requireString2(group.platform, "group.platform", MAX_ID_LENGTH);
449
+ requireI18nText(group.readmeTitle, "group.readmeTitle");
450
+ requireI18nText(group.alt, "group.alt");
451
+ requireI18nText(group.title, "group.title");
452
+ if (group.subtitle) requireI18nText(group.subtitle, "group.subtitle");
453
+ if (group.note) {
454
+ if (!isRecord2(group.note)) fail("group.note must be an object with zh/en");
455
+ requireString2(group.note.zh, "group.note.zh", MAX_NOTE_LENGTH);
456
+ requireString2(group.note.en, "group.note.en", MAX_NOTE_LENGTH);
457
+ }
458
+ requireString2(group.card, "group.card", MAX_PATH_LENGTH);
459
+ if (!IMAGE_PATH_PATTERN.test(group.card)) fail("group.card must be a png/jpg file name");
460
+ if (group.qr.kind === "remote") {
461
+ if (!IMAGE_PATH_PATTERN.test(group.qr.path)) fail("group.qr.path must be a png/jpg file name");
462
+ if (group.qr.path.length > MAX_PATH_LENGTH) fail("group.qr.path is too long");
463
+ } else {
464
+ requireString2(group.qr.key, "group.qr.key", MAX_ID_LENGTH);
465
+ }
466
+ }
467
+ return manifest;
468
+ }
469
+ function toDataUrl(bytes, contentType) {
470
+ let binary = "";
471
+ const chunk = 32768;
472
+ for (let index = 0; index < bytes.length; index += chunk) {
473
+ binary += String.fromCharCode(...bytes.subarray(index, index + chunk));
474
+ }
475
+ const type = contentType && contentType.startsWith("image/") ? contentType : "image/png";
476
+ return "data:" + type + ";base64," + btoa(binary);
477
+ }
478
+ var sessionCache = null;
479
+ var inFlight = null;
480
+ function resetContentCache() {
481
+ sessionCache = null;
482
+ inFlight = null;
483
+ }
484
+ var ContentLoader = class {
485
+ packageName;
486
+ timeoutMs;
487
+ maxImageBytes;
488
+ fetchImpl;
489
+ now;
490
+ channels;
491
+ constructor(options = {}) {
492
+ this.packageName = options.packageName ?? PACKAGE_NAME;
493
+ if (!PACKAGE_NAME_PATTERN.test(this.packageName)) {
494
+ throw new Error("invalid package name: " + this.packageName);
495
+ }
496
+ this.timeoutMs = options.timeoutMs ?? MANIFEST_TIMEOUT_MS;
497
+ this.maxImageBytes = options.maxImageBytes ?? MAX_IMAGE_BYTES;
498
+ this.now = options.now ?? (() => Date.now());
499
+ const injected = options.fetchImpl ?? globalThis.fetch;
500
+ if (typeof injected !== "function") throw new Error("fetch is not available in this environment");
501
+ this.fetchImpl = injected;
502
+ if (options.manifestUrl !== void 0) {
503
+ if (!isHttpsUrl(options.manifestUrl)) throw new Error("manifestUrl must be https");
504
+ const url = options.manifestUrl;
505
+ this.channels = [
506
+ { name: "custom", load: async (context) => ({ raw: JSON.parse(await context.fetchText(url)), url }) }
507
+ ];
508
+ } else if (options.channels) {
509
+ this.channels = options.channels.slice();
510
+ } else {
511
+ this.channels = defaultChannels(this.packageName);
512
+ }
513
+ }
514
+ /** Cached content when it is still inside the 30 minute window. */
515
+ getCached() {
516
+ if (!sessionCache) return null;
517
+ if (this.now() - sessionCache.at >= MANIFEST_CACHE_TTL_MS) return null;
518
+ return sessionCache.content;
519
+ }
520
+ /**
521
+ * Fetch and validate the manifest once per 30 minutes; concurrent callers
522
+ * share one request. Resolves to null when every channel failed.
523
+ */
524
+ load() {
525
+ const cached = this.getCached();
526
+ if (cached !== null || sessionCache !== null && this.now() - sessionCache.at < MANIFEST_CACHE_TTL_MS) {
527
+ return Promise.resolve(cached);
528
+ }
529
+ if (inFlight) return inFlight;
530
+ const startedAt = this.now();
531
+ const promise = this.fetchContent(startedAt).then((content) => {
532
+ sessionCache = { at: this.now(), content };
533
+ return content;
534
+ }).finally(() => {
535
+ inFlight = null;
536
+ });
537
+ inFlight = promise;
538
+ return promise;
539
+ }
540
+ /** Prefetch every remote QR image of a manifest (best effort). */
541
+ async ensureImages(content) {
542
+ const pending = [];
543
+ for (const group of content.manifest.groups) {
544
+ if (group.qr.kind !== "remote") continue;
545
+ const key = content.manifest.version + "/" + group.qr.path;
546
+ if (content.images.has(key)) continue;
547
+ pending.push(this.loadImage(content, group.qr.path, key));
548
+ }
549
+ await Promise.all(pending);
550
+ }
551
+ /** Data URI for a remote QR path, or null when it is not prefetched. */
552
+ imageFor(content, path) {
553
+ if (!content) return null;
554
+ return content.images.get(content.manifest.version + "/" + path) ?? null;
555
+ }
556
+ /** Warm the cache: manifest plus images. Never rejects. */
557
+ async preload() {
558
+ try {
559
+ const content = await this.load();
560
+ if (content) await this.ensureImages(content);
561
+ } catch {
562
+ }
563
+ }
564
+ async loadImage(content, path, key) {
565
+ for (const url of imageUrls(content.manifest.version, path, this.packageName)) {
566
+ if (!isAllowedCdnUrl(url)) continue;
567
+ try {
568
+ const data = await this.fetchImage(url);
569
+ if (data) {
570
+ content.images.set(key, data);
571
+ return;
572
+ }
573
+ } catch {
574
+ }
575
+ }
576
+ }
577
+ async fetchImage(url) {
578
+ const response = await this.request(url);
579
+ if (!response.ok) return null;
580
+ const declared = Number(response.headers.get("content-length"));
581
+ if (Number.isFinite(declared) && declared > this.maxImageBytes) return null;
582
+ const buffer = await response.arrayBuffer();
583
+ if (buffer.byteLength > this.maxImageBytes) return null;
584
+ return toDataUrl(new Uint8Array(buffer), response.headers.get("content-type"));
585
+ }
586
+ async fetchContent(fetchedAt) {
587
+ const context = {
588
+ packageName: this.packageName,
589
+ fetchText: (url) => this.fetchText(url),
590
+ fetchJson: async (url) => JSON.parse(await this.fetchText(url))
591
+ };
592
+ for (const channel of this.channels) {
593
+ try {
594
+ const result = await channel.load(context);
595
+ const manifest = validateRemoteManifest(result.raw);
596
+ return { manifest, source: channel.name, fetchedAt, images: /* @__PURE__ */ new Map() };
597
+ } catch {
598
+ }
599
+ }
600
+ return null;
601
+ }
602
+ async fetchText(url) {
603
+ const response = await this.request(url);
604
+ if (!response.ok) throw new Error("HTTP " + response.status);
605
+ return response.text();
606
+ }
607
+ /** One request with the 1.5s abort window; the timer is always cleared. */
608
+ async request(url) {
609
+ if (!isAllowedCdnUrl(url)) throw new Error("blocked url: " + url);
610
+ const controller = typeof AbortController === "function" ? new AbortController() : null;
611
+ let timer;
612
+ const timeout = new Promise((_resolve, reject) => {
613
+ timer = setTimeout(() => {
614
+ if (controller) controller.abort();
615
+ reject(new Error("timeout after " + this.timeoutMs + "ms"));
616
+ }, this.timeoutMs);
617
+ });
618
+ try {
619
+ const request = this.fetchImpl(url, {
620
+ signal: controller ? controller.signal : void 0,
621
+ cache: "no-store",
622
+ credentials: "omit"
623
+ });
624
+ request.catch(() => {
625
+ });
626
+ return await Promise.race([request, timeout]);
627
+ } finally {
628
+ if (timer !== void 0) clearTimeout(timer);
629
+ }
630
+ }
631
+ };
632
+ function createContentLoader(options = {}) {
633
+ return new ContentLoader(options);
634
+ }
635
+
636
+ // src/client/styles.ts
637
+ var BASE_STYLE_ID = "dsh-base-style";
638
+ var BASE_CSS = [
639
+ // --- settings panel widgets -------------------------------------------------
640
+ ".dsh-base-contact-button{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;border:1px solid rgba(127,127,127,.28);border-radius:999px;background:transparent;color:inherit;font:inherit;font-size:13px;line-height:1.4;cursor:pointer;transition:background .15s ease,border-color .15s ease}",
641
+ ".dsh-base-contact-button:hover{background:rgba(127,127,127,.12);border-color:rgba(127,127,127,.45)}",
642
+ ".dsh-base-contact-button:focus-visible{outline:2px solid #4d6bfe;outline-offset:2px}",
643
+ ".dsh-base-contact-button__icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto}",
644
+ ".dsh-base-contact-button__icon svg{display:block}",
645
+ ".dsh-base-contact-button__label{white-space:nowrap}",
646
+ ".dsh-base-contact-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid rgba(127,127,127,.16)}",
647
+ ".dsh-base-contact-row:last-child{border-bottom:0}",
648
+ ".dsh-base-contact-row__label{font-size:13px;opacity:.85}",
649
+ ".dsh-base-contact-row__hint{font-size:12px;opacity:.55}",
650
+ // --- dialog shell -----------------------------------------------------------
651
+ ".dsh-base-modal{position:fixed;inset:0;margin:auto;width:min(800px,92vw);max-width:min(800px,92vw);max-height:88vh;padding:0;border:0;border-radius:18px;background:#fff;color:#1f2329;box-shadow:0 24px 64px rgba(0,0,0,.24);overflow:hidden}",
652
+ ".dsh-base-modal[open]{display:block}",
653
+ ".dsh-base-modal::backdrop{background:rgba(0,0,0,.45)}",
654
+ ".dsh-base-modal__panel{display:flex;flex-direction:column;max-height:88vh}",
655
+ ".dsh-base-modal__header{position:relative;padding:22px 56px 8px;text-align:center}",
656
+ ".dsh-base-modal__heading{display:flex;flex-direction:column;align-items:center;gap:2px}",
657
+ ".dsh-base-modal__kicker{display:block;font-size:10px;font-weight:600;letter-spacing:.18em;text-transform:uppercase;color:#86909c}",
658
+ ".dsh-base-modal__title{margin:0;font-size:18px;font-weight:600;line-height:1.4;letter-spacing:.01em}",
659
+ ".dsh-base-modal__close{position:absolute;top:14px;right:14px;width:32px;height:32px;padding:0;border:0;border-radius:10px;background:transparent;color:#86909c;font:inherit;font-size:20px;line-height:1;cursor:pointer;transition:background .15s ease,color .15s ease}",
660
+ ".dsh-base-modal__close:hover{background:rgba(0,0,0,.06);color:#1f2329}",
661
+ ".dsh-base-modal__close:focus-visible{outline:2px solid #4d6bfe;outline-offset:2px}",
662
+ ".dsh-base-modal__welcome{margin:4px auto 16px;padding:0 24px;font-size:13px;line-height:1.75;color:#4e5969;text-align:center;max-width:640px}",
663
+ // --- scrollable body + card grid -------------------------------------------
664
+ ".dsh-base-modal__body{flex:1 1 auto;min-height:0;padding:0 24px 18px;overflow:auto;overscroll-behavior:contain}",
665
+ ".dsh-base-modal__grid{display:grid;grid-template-columns:repeat(var(--dsh-base-columns,4),minmax(0,1fr));gap:14px;align-items:start}",
666
+ ".dsh-base-modal__card{display:flex;flex-direction:column;align-items:stretch;gap:6px;min-width:0;padding:14px 12px;border:1px solid rgba(127,127,127,.2);border-radius:14px;background:#fff;text-align:center}",
667
+ ".dsh-base-modal__card--expired{background:#fffaf3;border-color:rgba(180,83,9,.28)}",
668
+ ".dsh-base-modal__card-title{margin:0;font-size:13px;font-weight:600;line-height:1.4}",
669
+ ".dsh-base-modal__card-subtitle{display:inline-block;align-self:center;margin:0;padding:2px 6px;border:0;border-radius:6px;background:transparent;color:#86909c;font:inherit;font-size:11px;line-height:1.5;cursor:pointer;transition:background .15s ease,color .15s ease}",
670
+ ".dsh-base-modal__card-subtitle:hover{background:rgba(127,127,127,.1);color:#4e5969}",
671
+ ".dsh-base-modal__card-subtitle[data-copied='true']{color:#4d6bfe}",
672
+ ".dsh-base-modal__card-subtitle:focus-visible{outline:2px solid #4d6bfe;outline-offset:2px}",
673
+ ".dsh-base-modal__qr{width:100%;height:auto;min-height:150px;padding:6px;border-radius:8px;background:#fff;object-fit:contain;image-rendering:pixelated}",
674
+ ".dsh-base-modal__card-note{margin-top:2px;font-size:10.5px;line-height:1.5;color:#86909c}",
675
+ ".dsh-base-modal__card-expired{margin-top:2px;font-size:10.5px;line-height:1.5;color:#b45309}",
676
+ ".dsh-base-modal__card-link{align-self:center;margin-top:4px;padding:6px 10px;border:1px solid rgba(77,107,254,.35);border-radius:8px;color:#4d6bfe;font-size:11.5px;line-height:1.4;text-decoration:none;word-break:break-all}",
677
+ ".dsh-base-modal__card-link:hover{background:rgba(77,107,254,.08)}",
678
+ ".dsh-base-modal__card-link:focus-visible{outline:2px solid #4d6bfe;outline-offset:2px}",
679
+ // --- footer hint ------------------------------------------------------------
680
+ ".dsh-base-modal__footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:4px 10px;padding:0 24px 18px;font-size:11.5px;line-height:1.6;color:#86909c;text-align:center}",
681
+ ".dsh-base-modal__mobile-hint{display:none}",
682
+ // --- author plugin card (settings panel) ------------------------------------
683
+ // Card: 1px border, 10px radius, layer-2 background, rows are whole anchors.
684
+ ".dsh-base-plugins{display:flex;flex-direction:column;gap:10px;padding:12px 14px;border:1px solid var(--dsw-alias-border-l1,rgba(127,127,127,.2));border-radius:10px;background:var(--dsw-alias-bg-layer-2,rgba(127,127,127,.06))}",
685
+ ".dsh-base-plugins__title{display:flex;align-items:center;gap:6px;margin:0;font-size:13px;font-weight:600;line-height:1.4;color:inherit}",
686
+ ".dsh-base-plugins__title-icon{display:inline-flex;flex:0 0 auto;align-items:center;color:var(--dsw-alias-label-secondary,#86909c)}",
687
+ ".dsh-base-plugins__title-icon svg{display:block}",
688
+ ".dsh-base-plugins__list{display:flex;flex-direction:column;gap:2px;margin:0;padding:0;list-style:none}",
689
+ ".dsh-base-plugins__item{min-width:0}",
690
+ ".dsh-base-plugins__row{display:flex;align-items:center;gap:8px;min-width:0;padding:7px 4px;border-radius:6px;color:inherit;text-decoration:none;transition:background .15s ease}",
691
+ ".dsh-base-plugins__row:hover{background:rgba(127,127,127,.1)}",
692
+ ".dsh-base-plugins__row:focus-visible{outline:2px solid #4d6bfe;outline-offset:2px}",
693
+ ".dsh-base-plugins__icon{display:inline-flex;flex:0 0 auto;align-items:center;color:var(--dsw-alias-label-secondary,#86909c)}",
694
+ ".dsh-base-plugins__icon svg{display:block}",
695
+ ".dsh-base-plugins__name{flex:0 0 auto;font-family:var(--ds-font-family-code,Consolas,Menlo,monospace);font-size:12px;font-weight:650;line-height:1.5;white-space:nowrap;color:inherit}",
696
+ ".dsh-base-plugins__desc{flex:1 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:1.5;color:var(--dsw-alias-label-secondary,#86909c)}",
697
+ ".dsh-base-plugins__ext{display:inline-flex;flex:0 0 auto;align-items:center;color:var(--dsw-alias-label-tertiary,#a9aeb8)}",
698
+ ".dsh-base-plugins__ext svg{display:block}",
699
+ // --- responsive grid: 4 columns -> 2 under 720px -> 1 under 480px -----------
700
+ "@media (max-width:719px){.dsh-base-modal__grid{grid-template-columns:repeat(2,minmax(0,1fr))}}",
701
+ "@media (max-width:479px){.dsh-base-modal__grid{grid-template-columns:minmax(0,1fr)}}",
702
+ "@media (max-width:719px){.dsh-base-modal__mobile-hint{display:inline}}",
703
+ "@media (prefers-reduced-motion:reduce){.dsh-base-modal__close,.dsh-base-modal__card-subtitle,.dsh-base-contact-button,.dsh-base-plugins__row{transition:none}}"
704
+ ].join("\n");
705
+ function ensureBaseStyles(doc) {
706
+ if (doc.getElementById(BASE_STYLE_ID)) return;
707
+ const style = doc.createElement("style");
708
+ style.id = BASE_STYLE_ID;
709
+ style.textContent = BASE_CSS;
710
+ (doc.head ?? doc.documentElement).appendChild(style);
711
+ }
712
+
713
+ // src/client/texts.ts
714
+ var MESSAGES = {
715
+ zh: {
716
+ contact: "\u8054\u7CFB\u6211\u4EEC",
717
+ contactHint: "\u52A0\u5165\u4EA4\u6D41\u7FA4",
718
+ kicker: "CONNECT",
719
+ modalTitle: "\u53CD\u9988\u4E0E\u8054\u7CFB",
720
+ welcome: "\u{1F44F}\u6B22\u8FCE\u63D0\u4EA4PR\u3001ISSUE\u3002\u661F\u661F\u4E4B\u706B\u53EF\u4EE5\u71CE\u539F\u{1F525}\u6709\u4EFB\u4F55\u5EFA\u8BAE\u3001\u610F\u89C1\u3001\u60F3\u6CD5\u3001\u5410\u69FD\u3001idea\u90FD\u53EF\u4EE5\u8FDB\u7FA4\u4EA4\u6D41\u3002",
721
+ scanHint: "\u626B\u63CF\u4E8C\u7EF4\u7801\u52A0\u5165\u6211\u4EEC",
722
+ mobileHint: "\u8BF7\u5728\u624B\u673A\u4E0A\u626B\u7801\uFF0C\u6216\u590D\u5236\u7FA4\u53F7\u641C\u7D22",
723
+ close: "\u5173\u95ED",
724
+ copyHint: "\u70B9\u51FB\u590D\u5236",
725
+ copied: "\u5DF2\u590D\u5236",
726
+ expired: "\u8BE5\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u70B9\u51FB\u4E0B\u65B9\u94FE\u63A5\u83B7\u53D6\u6700\u65B0\u7FA4\u7801",
727
+ fallback: "\u70B9\u6B64\u83B7\u53D6\u6700\u65B0\u7FA4\u4E8C\u7EF4\u7801",
728
+ plugins: "\u4F5C\u8005\u7684\u5176\u4ED6\u63D2\u4EF6"
729
+ },
730
+ en: {
731
+ contact: "Contact us",
732
+ contactHint: "Join the community",
733
+ kicker: "CONNECT",
734
+ modalTitle: "Feedback and Contact",
735
+ welcome: "\u{1F44F}Welcome PRs and issues. A single spark can start a prairie fire \u{1F525}. Any suggestion, opinion, idea or rant \u2014 come join the group and talk.",
736
+ scanHint: "Scan the QR code to join us",
737
+ mobileHint: "Scan with your phone, or copy the group ID to search",
738
+ close: "Close",
739
+ copyHint: "Click to copy",
740
+ copied: "Copied",
741
+ expired: "This QR code has expired. Use the link below for the latest one.",
742
+ fallback: "Get the latest QR code",
743
+ plugins: "More plugins by the author"
744
+ }
745
+ };
746
+ function resolveText(key, lang, overrides) {
747
+ const custom = overrides?.[key];
748
+ if (typeof custom === "string" && custom.length > 0) return custom;
749
+ return MESSAGES[lang][key];
750
+ }
751
+
752
+ // src/client/modal.ts
753
+ var TITLE_ID = "dsh-base-modal-title";
754
+ var DESC_ID = "dsh-base-modal-desc";
755
+ var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
756
+ function mountContactModal(config) {
757
+ const { doc, lang } = config;
758
+ const win = doc.defaultView;
759
+ let current = config;
760
+ const t = (key) => resolveText(key, lang, config.texts);
761
+ ensureBaseStyles(doc);
762
+ const dialog = doc.createElement("dialog");
763
+ dialog.className = "dsh-base-modal";
764
+ dialog.setAttribute("role", "dialog");
765
+ dialog.setAttribute("aria-modal", "true");
766
+ dialog.setAttribute("aria-labelledby", TITLE_ID);
767
+ dialog.setAttribute("aria-describedby", DESC_ID);
768
+ const panel = doc.createElement("div");
769
+ panel.className = "dsh-base-modal__panel";
770
+ const header = doc.createElement("div");
771
+ header.className = "dsh-base-modal__header";
772
+ const heading = doc.createElement("div");
773
+ heading.className = "dsh-base-modal__heading";
774
+ const kicker = doc.createElement("span");
775
+ kicker.className = "dsh-base-modal__kicker";
776
+ kicker.textContent = t("kicker");
777
+ const title = doc.createElement("h2");
778
+ title.id = TITLE_ID;
779
+ title.className = "dsh-base-modal__title";
780
+ title.textContent = t("modalTitle");
781
+ heading.append(kicker, title);
782
+ const closeButton = doc.createElement("button");
783
+ closeButton.type = "button";
784
+ closeButton.className = "dsh-base-modal__close";
785
+ closeButton.setAttribute("aria-label", t("close"));
786
+ closeButton.title = t("close");
787
+ closeButton.textContent = "\xD7";
788
+ header.append(heading, closeButton);
789
+ const welcome = doc.createElement("p");
790
+ welcome.id = DESC_ID;
791
+ welcome.className = "dsh-base-modal__welcome";
792
+ welcome.textContent = t("welcome");
793
+ const body = doc.createElement("div");
794
+ body.className = "dsh-base-modal__body";
795
+ const grid = doc.createElement("div");
796
+ grid.className = "dsh-base-modal__grid";
797
+ function renderGroups(groups, columns) {
798
+ grid.style.setProperty("--dsh-base-columns", String(columns));
799
+ grid.replaceChildren(...sortByOrder(groups).map((group) => buildCard(group)));
800
+ }
801
+ renderGroups(current.groups, current.columns);
802
+ body.append(grid);
803
+ const footer = doc.createElement("div");
804
+ footer.className = "dsh-base-modal__footer";
805
+ const scanHint = doc.createElement("span");
806
+ scanHint.className = "dsh-base-modal__hint";
807
+ scanHint.textContent = t("scanHint");
808
+ const mobileHint = doc.createElement("span");
809
+ mobileHint.className = "dsh-base-modal__mobile-hint";
810
+ mobileHint.textContent = t("mobileHint");
811
+ footer.append(scanHint, mobileHint);
812
+ panel.append(header, welcome, body, footer);
813
+ dialog.append(panel);
814
+ const previousOverflow = doc.body.style.overflow;
815
+ doc.body.style.overflow = "hidden";
816
+ let closed = false;
817
+ function destroy(restoreFocus = true) {
818
+ if (closed) return;
819
+ closed = true;
820
+ if (typeof dialog.close === "function" && dialog.hasAttribute("open")) {
821
+ try {
822
+ dialog.close();
823
+ } catch {
824
+ dialog.removeAttribute("open");
825
+ }
826
+ }
827
+ if (dialog.isConnected) {
828
+ dialog.remove();
829
+ doc.body.style.overflow = previousOverflow;
830
+ }
831
+ if (restoreFocus && config.trigger && config.trigger.isConnected && typeof config.trigger.focus === "function") {
832
+ config.trigger.focus();
833
+ }
834
+ }
835
+ function trapFocus(event) {
836
+ const focusable = Array.from(
837
+ dialog.querySelectorAll(FOCUSABLE_SELECTOR)
838
+ ).filter((element) => !element.hasAttribute("disabled"));
839
+ if (focusable.length === 0) return;
840
+ const first = focusable[0];
841
+ const last = focusable[focusable.length - 1];
842
+ const active = doc.activeElement;
843
+ if (event.shiftKey) {
844
+ if (active === first || !dialog.contains(active)) {
845
+ event.preventDefault();
846
+ last.focus();
847
+ }
848
+ } else if (active === last || !dialog.contains(active)) {
849
+ event.preventDefault();
850
+ first.focus();
851
+ }
852
+ }
853
+ dialog.addEventListener("keydown", (event) => {
854
+ if (event.key === "Escape") {
855
+ event.preventDefault();
856
+ destroy(true);
857
+ return;
858
+ }
859
+ if (event.key === "Tab") trapFocus(event);
860
+ });
861
+ dialog.addEventListener("cancel", (event) => {
862
+ event.preventDefault();
863
+ destroy(true);
864
+ });
865
+ dialog.addEventListener("click", (event) => {
866
+ if (event.target === dialog) destroy(true);
867
+ });
868
+ dialog.addEventListener("close", () => destroy(true));
869
+ closeButton.addEventListener("click", () => destroy(true));
870
+ doc.body.append(dialog);
871
+ let inTopLayer = false;
872
+ if (typeof dialog.showModal === "function") {
873
+ try {
874
+ dialog.showModal();
875
+ inTopLayer = true;
876
+ } catch {
877
+ inTopLayer = false;
878
+ }
879
+ }
880
+ if (!inTopLayer) dialog.setAttribute("open", "");
881
+ closeButton.focus();
882
+ function buildCard(group) {
883
+ const card = doc.createElement("div");
884
+ card.className = "dsh-base-modal__card";
885
+ card.dataset.groupId = group.id;
886
+ const expired = isExpired(group.validUntil, current.now);
887
+ if (expired) card.classList.add("dsh-base-modal__card--expired");
888
+ const qrSrc = expired ? null : current.resolveQr(group);
889
+ const cardTitle = doc.createElement("h3");
890
+ cardTitle.className = "dsh-base-modal__card-title";
891
+ cardTitle.textContent = pickText(group.title, lang);
892
+ card.append(cardTitle);
893
+ if (group.subtitle) {
894
+ const subtitle = doc.createElement("button");
895
+ subtitle.type = "button";
896
+ subtitle.className = "dsh-base-modal__card-subtitle";
897
+ subtitle.textContent = pickText(group.subtitle, lang);
898
+ subtitle.title = t("copyHint");
899
+ subtitle.addEventListener("click", () => copyText(subtitle, subtitle.textContent ?? ""));
900
+ card.append(subtitle);
901
+ }
902
+ if (qrSrc) {
903
+ const image = doc.createElement("img");
904
+ image.className = "dsh-base-modal__qr";
905
+ image.src = qrSrc;
906
+ image.alt = pickText(group.alt, lang);
907
+ image.loading = "lazy";
908
+ image.decoding = "async";
909
+ card.append(image);
910
+ }
911
+ if (expired) {
912
+ const note = doc.createElement("span");
913
+ note.className = "dsh-base-modal__card-expired";
914
+ note.textContent = t("expired");
915
+ card.append(note);
916
+ } else {
917
+ const validity = formatValidity(group.validUntil, { lang, now: current.now });
918
+ const noteText = validity ?? (group.note ? pickText(group.note, lang) : null);
919
+ if (noteText) {
920
+ const note = doc.createElement("span");
921
+ note.className = "dsh-base-modal__card-note";
922
+ note.textContent = noteText;
923
+ card.append(note);
924
+ }
925
+ }
926
+ if (!qrSrc) {
927
+ const link = doc.createElement("a");
928
+ link.className = "dsh-base-modal__card-link";
929
+ link.href = current.fallbackUrl;
930
+ link.target = "_blank";
931
+ link.rel = "noopener noreferrer";
932
+ link.textContent = t("fallback");
933
+ card.append(link);
934
+ }
935
+ return card;
936
+ }
937
+ function copyText(button, text) {
938
+ const markCopied = () => {
939
+ button.dataset.copied = "true";
940
+ button.setAttribute("aria-label", t("copied"));
941
+ };
942
+ const selectText = () => {
943
+ const selection = win?.getSelection?.();
944
+ if (!selection) return;
945
+ const range = doc.createRange();
946
+ range.selectNodeContents(button);
947
+ selection.removeAllRanges();
948
+ selection.addRange(range);
949
+ };
950
+ const clipboard = win?.navigator?.clipboard;
951
+ if (clipboard && typeof clipboard.writeText === "function") {
952
+ void clipboard.writeText(text).then(markCopied, selectText);
953
+ } else {
954
+ selectText();
955
+ }
956
+ }
957
+ function update(next) {
958
+ if (closed) return;
959
+ current = { ...current, ...next };
960
+ renderGroups(current.groups, current.columns);
961
+ }
962
+ return {
963
+ dialog,
964
+ close: (restoreFocus = true) => destroy(restoreFocus),
965
+ update
966
+ };
967
+ }
968
+
969
+ // src/client/plugins.ts
970
+ var DEFAULT_MAX_ROWS = 5;
971
+ function selectAuthorPlugins(options) {
972
+ const cap = typeof options.maxRows === "number" && Number.isFinite(options.maxRows) ? Math.max(0, Math.floor(options.maxRows)) : DEFAULT_MAX_ROWS;
973
+ const sorted = sortByOrder(options.plugins);
974
+ const visible = options.showSelf || !options.selfId ? sorted : sorted.filter((plugin) => plugin.id !== options.selfId);
975
+ return visible.slice(0, cap);
976
+ }
977
+ function pluginHref(plugin) {
978
+ return plugin.url && plugin.url.length > 0 ? plugin.url : "https://github.com/" + plugin.repo;
979
+ }
980
+ function renderAuthorPlugins(options) {
981
+ const rows = selectAuthorPlugins(options);
982
+ if (rows.length === 0) return null;
983
+ if (typeof document !== "undefined") ensureBaseStyles(document);
984
+ const h = options.React.createElement;
985
+ const className = options.className ? "dsh-base-plugins " + options.className : "dsh-base-plugins";
986
+ return h(
987
+ "div",
988
+ { className },
989
+ h(
990
+ "h3",
991
+ { className: "dsh-base-plugins__title", key: "title" },
992
+ h(
993
+ "span",
994
+ { className: "dsh-base-plugins__title-icon", key: "icon", "aria-hidden": "true" },
995
+ gridIcon(h, 14)
996
+ ),
997
+ h("span", { className: "dsh-base-plugins__title-text", key: "text" }, options.title)
998
+ ),
999
+ h(
1000
+ "ul",
1001
+ { className: "dsh-base-plugins__list", key: "list" },
1002
+ rows.map(
1003
+ (plugin) => h(
1004
+ "li",
1005
+ { className: "dsh-base-plugins__item", key: plugin.id },
1006
+ h(
1007
+ "a",
1008
+ {
1009
+ className: "dsh-base-plugins__row",
1010
+ href: pluginHref(plugin),
1011
+ target: "_blank",
1012
+ rel: "noopener noreferrer"
1013
+ },
1014
+ h(
1015
+ "span",
1016
+ { className: "dsh-base-plugins__icon", key: "icon", "aria-hidden": "true" },
1017
+ gridIcon(h, 12)
1018
+ ),
1019
+ h("span", { className: "dsh-base-plugins__name", key: "name" }, plugin.name),
1020
+ h(
1021
+ "span",
1022
+ { className: "dsh-base-plugins__desc", key: "desc" },
1023
+ pickText(plugin.desc, options.lang)
1024
+ ),
1025
+ h(
1026
+ "span",
1027
+ { className: "dsh-base-plugins__ext", key: "ext", "aria-hidden": "true" },
1028
+ externalLinkIcon(h)
1029
+ )
1030
+ )
1031
+ )
1032
+ )
1033
+ )
1034
+ );
1035
+ }
1036
+ function gridIcon(h, size) {
1037
+ return h(
1038
+ "svg",
1039
+ {
1040
+ width: size,
1041
+ height: size,
1042
+ viewBox: "0 0 24 24",
1043
+ fill: "none",
1044
+ stroke: "currentColor",
1045
+ strokeWidth: 2,
1046
+ strokeLinecap: "round",
1047
+ strokeLinejoin: "round",
1048
+ "aria-hidden": "true",
1049
+ focusable: "false"
1050
+ },
1051
+ h("rect", { x: 3, y: 3, width: 7, height: 7, rx: 1, key: "tl" }),
1052
+ h("rect", { x: 14, y: 3, width: 7, height: 7, rx: 1, key: "tr" }),
1053
+ h("rect", { x: 3, y: 14, width: 7, height: 7, rx: 1, key: "bl" }),
1054
+ h("rect", { x: 14, y: 14, width: 7, height: 7, rx: 1, key: "br" })
1055
+ );
1056
+ }
1057
+ function externalLinkIcon(h) {
1058
+ return h(
1059
+ "svg",
1060
+ {
1061
+ width: 14,
1062
+ height: 14,
1063
+ viewBox: "0 0 24 24",
1064
+ fill: "none",
1065
+ stroke: "currentColor",
1066
+ strokeWidth: 2,
1067
+ strokeLinecap: "round",
1068
+ strokeLinejoin: "round",
1069
+ "aria-hidden": "true",
1070
+ focusable: "false"
1071
+ },
1072
+ h("path", {
1073
+ d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6",
1074
+ key: "box"
1075
+ }),
1076
+ h("polyline", { points: "15 3 21 3 21 9", key: "arrow" }),
1077
+ h("line", { x1: 10, y1: 14, x2: 21, y2: 3, key: "line" })
1078
+ );
1079
+ }
1080
+
1081
+ // src/client/index.ts
1082
+ var MAX_COLUMNS = 4;
1083
+ var sharedModal = null;
1084
+ function detectLang(explicit) {
1085
+ return resolveLang({
1086
+ explicit: explicit ?? null,
1087
+ htmlLang: typeof document === "undefined" ? null : document.documentElement.lang,
1088
+ navigatorLanguages: typeof navigator === "undefined" ? null : navigator.languages
1089
+ });
1090
+ }
1091
+ function clampColumns(requested, groupCount) {
1092
+ if (typeof requested === "number" && Number.isFinite(requested)) {
1093
+ return Math.min(MAX_COLUMNS, Math.max(1, Math.floor(requested)));
1094
+ }
1095
+ return Math.max(1, Math.min(MAX_COLUMNS, groupCount));
1096
+ }
1097
+ function resolveBaselineQr(group) {
1098
+ if (group.qr.kind !== "bundled") return null;
1099
+ return BASELINE.qr[group.qr.key] ?? null;
1100
+ }
1101
+ function resolveGroupQr(group, content, loader) {
1102
+ if (group.qr.kind === "bundled") return resolveBaselineQr(group);
1103
+ if (!content || !loader) return null;
1104
+ return loader.imageFor(content, group.qr.path);
1105
+ }
1106
+ function mergeTexts(overrides, extra) {
1107
+ if (!overrides && !extra) return void 0;
1108
+ return { ...overrides, ...extra };
1109
+ }
1110
+ function classes(base, extra) {
1111
+ return extra ? base + " " + extra : base;
1112
+ }
1113
+ function createBaseUI(React, options = {}) {
1114
+ let active = null;
1115
+ let loader = options.contentLoader ?? null;
1116
+ function getLoader() {
1117
+ if (loader) return loader;
1118
+ try {
1119
+ loader = createContentLoader({
1120
+ manifestUrl: options.manifestUrl,
1121
+ fetchImpl: options.fetchImpl
1122
+ });
1123
+ } catch {
1124
+ loader = null;
1125
+ }
1126
+ return loader;
1127
+ }
1128
+ function widgetText(key) {
1129
+ return resolveText(key, detectLang(options.lang), options.texts);
1130
+ }
1131
+ function closeActive(restoreFocus) {
1132
+ const handle = active;
1133
+ active = null;
1134
+ if (!handle) return;
1135
+ if (sharedModal === handle) sharedModal = null;
1136
+ handle.close(restoreFocus);
1137
+ }
1138
+ function closeContactModal() {
1139
+ closeActive(true);
1140
+ }
1141
+ function buildResolver(callOptions, content) {
1142
+ const custom = callOptions.resolveQr ?? options.resolveQr;
1143
+ if (custom) return custom;
1144
+ return (group) => resolveGroupQr(group, content, loader);
1145
+ }
1146
+ function applyContent(handle, content) {
1147
+ if (active !== handle) return;
1148
+ const groups = content.manifest.groups.length > 0 ? content.manifest.groups : BASELINE.manifest.groups;
1149
+ handle.update({
1150
+ groups,
1151
+ columns: clampColumns(void 0, groups.length),
1152
+ fallbackUrl: content.manifest.fallbackUrl,
1153
+ resolveQr: (group) => resolveGroupQr(group, content, loader) ?? null
1154
+ });
1155
+ }
1156
+ async function refreshRemote(handle, callOptions) {
1157
+ if (callOptions.groups && callOptions.groups.length > 0) return;
1158
+ const currentLoader = getLoader();
1159
+ if (!currentLoader) return;
1160
+ const content = await currentLoader.load();
1161
+ if (!content || active !== handle) return;
1162
+ await currentLoader.ensureImages(content);
1163
+ if (active !== handle) return;
1164
+ const custom = callOptions.resolveQr ?? options.resolveQr;
1165
+ if (custom) {
1166
+ handle.update({
1167
+ groups: content.manifest.groups,
1168
+ columns: clampColumns(callOptions.columns ?? options.columns, content.manifest.groups.length),
1169
+ fallbackUrl: content.manifest.fallbackUrl,
1170
+ resolveQr: (group) => custom(group) ?? null
1171
+ });
1172
+ return;
1173
+ }
1174
+ applyContent(handle, content);
1175
+ }
1176
+ function openContactModal(callOptions = {}) {
1177
+ if (typeof document === "undefined") return () => {
1178
+ };
1179
+ const previous = sharedModal;
1180
+ sharedModal = null;
1181
+ active = null;
1182
+ if (previous) previous.close(false);
1183
+ const groups = callOptions.groups && callOptions.groups.length > 0 ? callOptions.groups : BASELINE.manifest.groups;
1184
+ const lang = detectLang(callOptions.lang ?? options.lang);
1185
+ const candidate = callOptions.trigger ?? document.activeElement;
1186
+ const trigger = candidate && typeof candidate.focus === "function" ? candidate : null;
1187
+ const cached = getLoader()?.getCached() ?? null;
1188
+ const resolver = buildResolver(callOptions, cached);
1189
+ const handle = mountContactModal({
1190
+ doc: document,
1191
+ lang,
1192
+ groups,
1193
+ columns: clampColumns(callOptions.columns ?? options.columns, groups.length),
1194
+ trigger,
1195
+ fallbackUrl: callOptions.fallbackUrl ?? options.fallbackUrl ?? cached?.manifest.fallbackUrl ?? BASELINE.manifest.fallbackUrl,
1196
+ texts: mergeTexts(options.texts, callOptions.texts),
1197
+ resolveQr: (group) => resolver(group) ?? null,
1198
+ now: callOptions.now
1199
+ });
1200
+ active = handle;
1201
+ sharedModal = handle;
1202
+ handle.dialog.addEventListener("close", () => {
1203
+ if (active === handle) active = null;
1204
+ if (sharedModal === handle) sharedModal = null;
1205
+ });
1206
+ void refreshRemote(handle, callOptions);
1207
+ return () => {
1208
+ if (active === handle) active = null;
1209
+ if (sharedModal === handle) sharedModal = null;
1210
+ handle.close();
1211
+ };
1212
+ }
1213
+ function ContactButton(props) {
1214
+ const h = React.createElement;
1215
+ const label = props.title ?? widgetText("contact");
1216
+ const size = props.size ?? 16;
1217
+ const icon = h(
1218
+ "svg",
1219
+ {
1220
+ width: size,
1221
+ height: size,
1222
+ viewBox: "0 0 24 24",
1223
+ fill: "none",
1224
+ stroke: "currentColor",
1225
+ strokeWidth: 2,
1226
+ strokeLinecap: "round",
1227
+ strokeLinejoin: "round",
1228
+ "aria-hidden": "true",
1229
+ focusable: "false"
1230
+ },
1231
+ h("path", { d: "M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2", key: "body" }),
1232
+ h("circle", { cx: 9, cy: 7, r: 4, key: "head" }),
1233
+ h("path", { d: "M23 21v-2a4 4 0 0 0-3-3.87", key: "arm" }),
1234
+ h("path", { d: "M16 3.13a4 4 0 0 1 0 7.75", key: "arm2" })
1235
+ );
1236
+ return h(
1237
+ "button",
1238
+ {
1239
+ type: "button",
1240
+ className: classes("dsh-base-contact-button", props.className),
1241
+ title: label,
1242
+ "aria-label": label,
1243
+ onClick: (event) => {
1244
+ openContactModal({ trigger: event.currentTarget });
1245
+ }
1246
+ },
1247
+ h("span", { className: "dsh-base-contact-button__icon", key: "icon" }, icon),
1248
+ props.iconOnly ? null : h("span", { className: "dsh-base-contact-button__label", key: "label" }, label)
1249
+ );
1250
+ }
1251
+ function ContactRow(props) {
1252
+ const h = React.createElement;
1253
+ return h(
1254
+ "div",
1255
+ { className: classes("dsh-base-contact-row", props.className) },
1256
+ h(
1257
+ "span",
1258
+ { className: "dsh-base-contact-row__label", key: "label" },
1259
+ props.label ?? widgetText("contact"),
1260
+ props.hint ? h("span", { className: "dsh-base-contact-row__hint", key: "hint" }, " \xB7 " + props.hint) : null
1261
+ ),
1262
+ h(ContactButton, { title: props.label ?? widgetText("contact"), key: "button" })
1263
+ );
1264
+ }
1265
+ function AuthorPlugins(props) {
1266
+ const lang = props.lang ?? detectLang(options.lang);
1267
+ return renderAuthorPlugins({
1268
+ React,
1269
+ lang,
1270
+ plugins: props.plugins ?? BASELINE.manifest.plugins,
1271
+ selfId: props.selfId ?? options.selfId,
1272
+ showSelf: props.showSelf ?? options.showSelf,
1273
+ maxRows: props.maxRows ?? options.maxRows,
1274
+ title: props.title ?? resolveText("plugins", lang, options.texts),
1275
+ className: props.className
1276
+ });
1277
+ }
1278
+ async function preloadContent() {
1279
+ const currentLoader = getLoader();
1280
+ if (!currentLoader) return;
1281
+ await currentLoader.preload();
1282
+ if (options.resolveQr) return;
1283
+ const content = currentLoader.getCached();
1284
+ const handle = active;
1285
+ if (!content || !handle) return;
1286
+ if (active !== handle) return;
1287
+ applyContent(handle, content);
1288
+ }
1289
+ return {
1290
+ ContactButton,
1291
+ ContactRow,
1292
+ AuthorPlugins,
1293
+ openContactModal,
1294
+ closeContactModal,
1295
+ preloadContent
1296
+ };
1297
+ }
1298
+ export {
1299
+ ALLOWED_CDN_HOSTS,
1300
+ BASE_CSS,
1301
+ BASE_STYLE_ID,
1302
+ ContentLoader,
1303
+ MANIFEST_CACHE_TTL_MS,
1304
+ MANIFEST_TIMEOUT_MS,
1305
+ MAX_IMAGE_BYTES,
1306
+ MESSAGES,
1307
+ createBaseUI,
1308
+ createContentLoader,
1309
+ defaultChannels,
1310
+ ensureBaseStyles,
1311
+ imageUrls,
1312
+ isAllowedCdnUrl,
1313
+ isHttpsUrl,
1314
+ resetContentCache,
1315
+ resolveText,
1316
+ validateRemoteManifest
1317
+ };