sa2kit 1.6.85 → 1.6.89

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/dist/index.mjs CHANGED
@@ -13,7 +13,7 @@ import { useSortable, sortableKeyboardCoordinates, SortableContext, rectSortingS
13
13
  import { CSS } from '@dnd-kit/utilities';
14
14
  import { pgEnum, pgTable, boolean, timestamp, jsonb, text, uniqueIndex, foreignKey } from 'drizzle-orm/pg-core';
15
15
  import { sql, relations } from 'drizzle-orm';
16
- import 'crypto';
16
+ import { randomUUID } from 'crypto';
17
17
  import 'bcryptjs';
18
18
  import 'jsonwebtoken';
19
19
  import * as THREE2 from 'three';
@@ -8013,6 +8013,362 @@ var createInMemoryFestivalCardDb = () => ({
8013
8013
  }
8014
8014
  });
8015
8015
 
8016
+ // src/vocaloidBooth/core/code.ts
8017
+ var AMBIGUOUS = /* @__PURE__ */ new Set(["0", "1", "I", "O", "L"]);
8018
+ var ALPHABET = "ABCDEFGHJKMNPQRSTUVWXYZ23456789".split("").filter((c) => !AMBIGUOUS.has(c));
8019
+ var normalizeMatchCode = (value) => value.trim().toUpperCase();
8020
+ var generateMatchCode = async ({
8021
+ length = 6,
8022
+ maxAttempts = 20,
8023
+ exists
8024
+ }) => {
8025
+ if (length < 4) {
8026
+ throw new Error("Match code length must be at least 4");
8027
+ }
8028
+ for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
8029
+ const code = Array.from({ length }).map(() => ALPHABET[Math.floor(Math.random() * ALPHABET.length)]).join("");
8030
+ if (!await exists(code)) {
8031
+ return code;
8032
+ }
8033
+ }
8034
+ throw new Error("Unable to generate unique match code");
8035
+ };
8036
+ var BoothVaultService = class {
8037
+ emitAudit(event) {
8038
+ this.onAuditEvent?.({
8039
+ ...event,
8040
+ at: (/* @__PURE__ */ new Date()).toISOString()
8041
+ });
8042
+ }
8043
+ constructor(options) {
8044
+ this.store = options.store;
8045
+ this.codeLength = options.codeLength ?? 6;
8046
+ this.defaultTtlHours = options.defaultTtlHours ?? 24 * 14;
8047
+ this.baseDownloadPath = options.baseDownloadPath ?? "/redeem";
8048
+ this.redeemGuard = options.redeemGuard;
8049
+ this.onAuditEvent = options.onAuditEvent;
8050
+ }
8051
+ async createUpload(input) {
8052
+ if (!input.files?.length) {
8053
+ throw new Error("At least one file is required");
8054
+ }
8055
+ const now = /* @__PURE__ */ new Date();
8056
+ const ttlHours = Math.max(1, input.ttlHours ?? this.defaultTtlHours);
8057
+ const expiresAt = new Date(now.getTime() + ttlHours * 60 * 60 * 1e3);
8058
+ const matchCode = await generateMatchCode({
8059
+ length: this.codeLength,
8060
+ exists: (code) => this.store.existsByMatchCode(code)
8061
+ });
8062
+ const record = {
8063
+ id: randomUUID(),
8064
+ boothId: input.boothId,
8065
+ matchCode,
8066
+ createdAt: now.toISOString(),
8067
+ expiresAt: expiresAt.toISOString(),
8068
+ files: input.files.map((file) => ({
8069
+ ...file,
8070
+ id: randomUUID()
8071
+ })),
8072
+ metadata: input.metadata,
8073
+ status: "active",
8074
+ downloadCount: 0
8075
+ };
8076
+ await this.store.saveRecord(record);
8077
+ this.emitAudit({
8078
+ type: "upload.created",
8079
+ boothId: record.boothId,
8080
+ recordId: record.id,
8081
+ matchCode: record.matchCode,
8082
+ detail: { fileCount: record.files.length }
8083
+ });
8084
+ return {
8085
+ record,
8086
+ downloadUrlPath: `${this.baseDownloadPath}?code=${record.matchCode}`
8087
+ };
8088
+ }
8089
+ async getByMatchCode(matchCode) {
8090
+ const normalized = normalizeMatchCode(matchCode);
8091
+ const record = await this.store.findByMatchCode(normalized);
8092
+ if (!record) {
8093
+ return null;
8094
+ }
8095
+ if (new Date(record.expiresAt).getTime() <= Date.now() && record.status === "active") {
8096
+ return {
8097
+ ...record,
8098
+ status: "expired"
8099
+ };
8100
+ }
8101
+ return record;
8102
+ }
8103
+ async markDownloaded(recordId) {
8104
+ await this.store.incrementDownloadCount(recordId);
8105
+ }
8106
+ async resolveDownloadFilesByCode(matchCode, options) {
8107
+ const requesterKey = options?.requesterKey;
8108
+ if (requesterKey && this.redeemGuard) {
8109
+ try {
8110
+ this.redeemGuard.assertAllowed(requesterKey);
8111
+ } catch (error) {
8112
+ this.emitAudit({
8113
+ type: "redeem.blocked",
8114
+ requesterKey,
8115
+ matchCode,
8116
+ detail: { message: error instanceof Error ? error.message : "blocked" }
8117
+ });
8118
+ throw error;
8119
+ }
8120
+ }
8121
+ const record = await this.getByMatchCode(matchCode);
8122
+ const success = !!record && record.status === "active";
8123
+ if (requesterKey && this.redeemGuard) {
8124
+ this.redeemGuard.registerAttempt(requesterKey, success);
8125
+ }
8126
+ if (!success) {
8127
+ this.emitAudit({
8128
+ type: "redeem.failed",
8129
+ requesterKey,
8130
+ matchCode,
8131
+ boothId: record?.boothId,
8132
+ recordId: record?.id
8133
+ });
8134
+ return record;
8135
+ }
8136
+ await this.markDownloaded(record.id);
8137
+ const reloaded = this.store.findByRecordId ? await this.store.findByRecordId(record.id) : await this.getByMatchCode(record.matchCode);
8138
+ this.emitAudit({
8139
+ type: "redeem.success",
8140
+ requesterKey,
8141
+ matchCode: record.matchCode,
8142
+ boothId: record.boothId,
8143
+ recordId: record.id
8144
+ });
8145
+ return reloaded ?? record;
8146
+ }
8147
+ };
8148
+
8149
+ // src/vocaloidBooth/core/config.ts
8150
+ var defaultVocaloidBoothConfig = {
8151
+ boothId: "default-booth",
8152
+ title: "MMD / Vocaloid \u521B\u4F5C\u6587\u4EF6\u5BC4\u5B58\u7AD9",
8153
+ description: "\u4E0A\u4F20\u521B\u4F5C\u6587\u4EF6\u5E76\u751F\u6210\u5339\u914D\u7801\uFF0C\u540E\u7EED\u53EF\u51ED\u7801\u4E0B\u8F7D",
8154
+ defaultTtlHours: 24 * 14,
8155
+ maxFiles: 20,
8156
+ maxSingleFileSizeMb: 2048,
8157
+ maxTotalFileSizeMb: 5120,
8158
+ allowedExtensions: ["zip", "7z", "rar", "vsqx", "vpr", "vmd", "pmx", "wav", "mp3", "mp4"]
8159
+ };
8160
+ var normalizeVocaloidBoothConfig = (input) => {
8161
+ const merged = {
8162
+ ...defaultVocaloidBoothConfig,
8163
+ ...input ?? {}
8164
+ };
8165
+ return {
8166
+ ...merged,
8167
+ boothId: merged.boothId || defaultVocaloidBoothConfig.boothId,
8168
+ title: merged.title || defaultVocaloidBoothConfig.title,
8169
+ defaultTtlHours: Math.max(1, merged.defaultTtlHours),
8170
+ maxFiles: Math.max(1, merged.maxFiles),
8171
+ maxSingleFileSizeMb: Math.max(1, merged.maxSingleFileSizeMb),
8172
+ maxTotalFileSizeMb: Math.max(1, merged.maxTotalFileSizeMb),
8173
+ allowedExtensions: (merged.allowedExtensions?.length ? merged.allowedExtensions : defaultVocaloidBoothConfig.allowedExtensions).map((ext) => ext.toLowerCase())
8174
+ };
8175
+ };
8176
+ var BoothUploadPanel = ({
8177
+ boothId,
8178
+ maxFiles = 10,
8179
+ maxFileSizeMb = 2048,
8180
+ accept,
8181
+ uploading = false,
8182
+ onSubmit
8183
+ }) => {
8184
+ const [files, setFiles] = useState([]);
8185
+ const [nickname, setNickname] = useState("");
8186
+ const [contactTail, setContactTail] = useState("");
8187
+ const [ttlHours, setTtlHours] = useState(24 * 14);
8188
+ const [error, setError] = useState(null);
8189
+ const totalSizeMb = useMemo(
8190
+ () => files.reduce((acc, file) => acc + file.size, 0) / 1024 / 1024,
8191
+ [files]
8192
+ );
8193
+ const addFiles = (newFiles) => {
8194
+ if (!newFiles) return;
8195
+ const incoming = Array.from(newFiles);
8196
+ const next = [...files, ...incoming];
8197
+ if (next.length > maxFiles) {
8198
+ setError(`\u6700\u591A\u4E0A\u4F20 ${maxFiles} \u4E2A\u6587\u4EF6`);
8199
+ return;
8200
+ }
8201
+ const oversized = incoming.find((f) => f.size > maxFileSizeMb * 1024 * 1024);
8202
+ if (oversized) {
8203
+ setError(`\u6587\u4EF6 ${oversized.name} \u8D85\u8FC7 ${maxFileSizeMb}MB \u9650\u5236`);
8204
+ return;
8205
+ }
8206
+ setError(null);
8207
+ setFiles(next);
8208
+ };
8209
+ const removeFile = (name) => setFiles((prev) => prev.filter((f) => f.name !== name));
8210
+ const handleSubmit = async () => {
8211
+ if (files.length === 0) {
8212
+ setError("\u8BF7\u5148\u9009\u62E9\u81F3\u5C11\u4E00\u4E2A\u6587\u4EF6");
8213
+ return;
8214
+ }
8215
+ setError(null);
8216
+ await onSubmit({
8217
+ boothId,
8218
+ files,
8219
+ nickname: nickname || void 0,
8220
+ contactTail: contactTail || void 0,
8221
+ ttlHours
8222
+ });
8223
+ };
8224
+ return /* @__PURE__ */ React69__default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm" }, /* @__PURE__ */ React69__default.createElement("h3", { className: "mb-3 text-lg font-semibold" }, "\u4E0A\u4F20\u521B\u4F5C\u6587\u4EF6"), /* @__PURE__ */ React69__default.createElement(
8225
+ "input",
8226
+ {
8227
+ type: "file",
8228
+ multiple: true,
8229
+ accept,
8230
+ onChange: (e) => addFiles(e.target.files),
8231
+ className: "mb-3 block w-full text-sm"
8232
+ }
8233
+ ), /* @__PURE__ */ React69__default.createElement("div", { className: "mb-3 grid grid-cols-1 gap-2 md:grid-cols-3" }, /* @__PURE__ */ React69__default.createElement(
8234
+ "input",
8235
+ {
8236
+ value: nickname,
8237
+ onChange: (e) => setNickname(e.target.value),
8238
+ placeholder: "\u6635\u79F0\uFF08\u53EF\u9009\uFF09",
8239
+ className: "rounded-md border px-3 py-2 text-sm"
8240
+ }
8241
+ ), /* @__PURE__ */ React69__default.createElement(
8242
+ "input",
8243
+ {
8244
+ value: contactTail,
8245
+ onChange: (e) => setContactTail(e.target.value),
8246
+ placeholder: "\u8054\u7CFB\u65B9\u5F0F\u540E4\u4F4D\uFF08\u53EF\u9009\uFF09",
8247
+ className: "rounded-md border px-3 py-2 text-sm"
8248
+ }
8249
+ ), /* @__PURE__ */ React69__default.createElement(
8250
+ "input",
8251
+ {
8252
+ value: ttlHours,
8253
+ type: "number",
8254
+ min: 1,
8255
+ onChange: (e) => setTtlHours(Number(e.target.value) || 24),
8256
+ placeholder: "\u4FDD\u5B58\u65F6\u957F\uFF08\u5C0F\u65F6\uFF09",
8257
+ className: "rounded-md border px-3 py-2 text-sm"
8258
+ }
8259
+ )), /* @__PURE__ */ React69__default.createElement("div", { className: "mb-3 text-xs text-slate-500" }, "\u5DF2\u9009 ", files.length, " \u4E2A\u6587\u4EF6\uFF0C\u603B\u8BA1 ", totalSizeMb.toFixed(2), " MB"), /* @__PURE__ */ React69__default.createElement("ul", { className: "mb-3 max-h-40 overflow-auto rounded-md border border-slate-100 p-2 text-sm" }, files.length === 0 && /* @__PURE__ */ React69__default.createElement("li", { className: "text-slate-400" }, "\u5C1A\u672A\u9009\u62E9\u6587\u4EF6"), files.map((file) => /* @__PURE__ */ React69__default.createElement("li", { key: `${file.name}-${file.size}`, className: "mb-1 flex items-center justify-between gap-2" }, /* @__PURE__ */ React69__default.createElement("span", { className: "truncate" }, file.name), /* @__PURE__ */ React69__default.createElement("button", { type: "button", className: "text-rose-500", onClick: () => removeFile(file.name) }, "\u79FB\u9664")))), error && /* @__PURE__ */ React69__default.createElement("div", { className: "mb-3 rounded-md bg-rose-50 p-2 text-sm text-rose-700" }, error), /* @__PURE__ */ React69__default.createElement(
8260
+ "button",
8261
+ {
8262
+ type: "button",
8263
+ disabled: uploading,
8264
+ onClick: handleSubmit,
8265
+ className: "rounded-md bg-indigo-600 px-3 py-2 text-white disabled:cursor-not-allowed disabled:opacity-50"
8266
+ },
8267
+ uploading ? "\u4E0A\u4F20\u4E2D..." : "\u5F00\u59CB\u4E0A\u4F20"
8268
+ ));
8269
+ };
8270
+ var BoothRedeemPanel = ({ onRedeem, loading }) => {
8271
+ const [matchCode, setMatchCode] = useState("");
8272
+ const [record, setRecord] = useState(null);
8273
+ const [error, setError] = useState(null);
8274
+ const handleRedeem = async () => {
8275
+ setError(null);
8276
+ const result = await onRedeem(matchCode.trim());
8277
+ if (!result) {
8278
+ setRecord(null);
8279
+ setError("\u5339\u914D\u7801\u4E0D\u5B58\u5728\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5");
8280
+ return;
8281
+ }
8282
+ if (result.status !== "active") {
8283
+ setRecord(result);
8284
+ setError("\u5339\u914D\u7801\u5DF2\u8FC7\u671F\u6216\u5931\u6548");
8285
+ return;
8286
+ }
8287
+ setRecord(result);
8288
+ };
8289
+ return /* @__PURE__ */ React69__default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm" }, /* @__PURE__ */ React69__default.createElement("h3", { className: "mb-3 text-lg font-semibold" }, "\u51ED\u5339\u914D\u7801\u4E0B\u8F7D"), /* @__PURE__ */ React69__default.createElement("div", { className: "mb-3 flex gap-2" }, /* @__PURE__ */ React69__default.createElement(
8290
+ "input",
8291
+ {
8292
+ value: matchCode,
8293
+ onChange: (e) => setMatchCode(e.target.value.toUpperCase()),
8294
+ placeholder: "\u8F93\u5165\u5339\u914D\u7801\uFF08\u5982 A7K9Q2\uFF09",
8295
+ className: "w-full rounded-md border px-3 py-2 text-sm uppercase"
8296
+ }
8297
+ ), /* @__PURE__ */ React69__default.createElement(
8298
+ "button",
8299
+ {
8300
+ type: "button",
8301
+ onClick: handleRedeem,
8302
+ disabled: loading,
8303
+ className: "rounded-md bg-slate-900 px-3 py-2 text-white disabled:opacity-50"
8304
+ },
8305
+ "\u67E5\u8BE2"
8306
+ )), error && /* @__PURE__ */ React69__default.createElement("div", { className: "mb-3 rounded-md bg-amber-50 p-2 text-sm text-amber-700" }, error), record && /* @__PURE__ */ React69__default.createElement("div", { className: "rounded-md border border-slate-100 p-3" }, /* @__PURE__ */ React69__default.createElement("div", { className: "mb-2 text-xs text-slate-500" }, "\u5171 ", record.files.length, " \u4E2A\u6587\u4EF6"), /* @__PURE__ */ React69__default.createElement("ul", { className: "space-y-1 text-sm" }, record.files.map((file) => /* @__PURE__ */ React69__default.createElement("li", { key: file.id, className: "flex items-center justify-between gap-2" }, /* @__PURE__ */ React69__default.createElement("span", { className: "truncate" }, file.fileName), /* @__PURE__ */ React69__default.createElement("a", { href: file.objectKey, className: "text-indigo-600 hover:underline", download: true }, "\u4E0B\u8F7D"))))));
8307
+ };
8308
+ var BoothSuccessCard = ({
8309
+ matchCode,
8310
+ expiresAt,
8311
+ downloadUrlPath,
8312
+ onCopyCode,
8313
+ className
8314
+ }) => {
8315
+ const handleCopy = async () => {
8316
+ try {
8317
+ await navigator.clipboard.writeText(matchCode);
8318
+ } catch {
8319
+ }
8320
+ onCopyCode?.(matchCode);
8321
+ };
8322
+ return /* @__PURE__ */ React69__default.createElement("div", { className: `rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm ${className ?? ""}` }, /* @__PURE__ */ React69__default.createElement("div", { className: "mb-2 text-xs text-emerald-800" }, "\u4E0A\u4F20\u5B8C\u6210\uFF0C\u5DF2\u751F\u6210\u5339\u914D\u7801"), /* @__PURE__ */ React69__default.createElement("div", { className: "mb-3 text-2xl font-bold tracking-widest text-emerald-900" }, matchCode), /* @__PURE__ */ React69__default.createElement("div", { className: "mb-3 text-xs text-emerald-800" }, "\u8FC7\u671F\u65F6\u95F4\uFF1A", new Date(expiresAt).toLocaleString()), /* @__PURE__ */ React69__default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React69__default.createElement(
8323
+ "button",
8324
+ {
8325
+ type: "button",
8326
+ onClick: handleCopy,
8327
+ className: "rounded-md bg-emerald-600 px-3 py-2 text-white hover:bg-emerald-700"
8328
+ },
8329
+ "\u590D\u5236\u5339\u914D\u7801"
8330
+ ), /* @__PURE__ */ React69__default.createElement(
8331
+ "a",
8332
+ {
8333
+ href: downloadUrlPath,
8334
+ className: "rounded-md border border-emerald-400 bg-white px-3 py-2 text-emerald-700 hover:bg-emerald-100"
8335
+ },
8336
+ "\u6253\u5F00\u4E0B\u8F7D\u9875"
8337
+ )));
8338
+ };
8339
+ var BoothConfigPage = ({ initialConfig, onSave }) => {
8340
+ const [config, setConfig] = useState(
8341
+ normalizeVocaloidBoothConfig(initialConfig)
8342
+ );
8343
+ const [saving, setSaving] = useState(false);
8344
+ const extText = useMemo(() => config.allowedExtensions.join(","), [config.allowedExtensions]);
8345
+ const update = (key, value) => setConfig((prev) => ({ ...prev, [key]: value }));
8346
+ const save = async () => {
8347
+ setSaving(true);
8348
+ try {
8349
+ const normalized = normalizeVocaloidBoothConfig(config);
8350
+ setConfig(normalized);
8351
+ await onSave?.(normalized);
8352
+ } finally {
8353
+ setSaving(false);
8354
+ }
8355
+ };
8356
+ const reset = () => setConfig(defaultVocaloidBoothConfig);
8357
+ return /* @__PURE__ */ React69__default.createElement("div", { className: "rounded-xl border border-slate-200 bg-white p-4 shadow-sm space-y-3" }, /* @__PURE__ */ React69__default.createElement("h3", { className: "text-lg font-semibold" }, "Vocaloid Booth \u914D\u7F6E\u9875"), /* @__PURE__ */ React69__default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-3 text-sm" }, /* @__PURE__ */ React69__default.createElement("input", { className: "rounded border px-3 py-2", value: config.boothId, onChange: (e) => update("boothId", e.target.value), placeholder: "boothId" }), /* @__PURE__ */ React69__default.createElement("input", { className: "rounded border px-3 py-2", value: config.title, onChange: (e) => update("title", e.target.value), placeholder: "\u6807\u9898" }), /* @__PURE__ */ React69__default.createElement("input", { className: "rounded border px-3 py-2 md:col-span-2", value: config.description ?? "", onChange: (e) => update("description", e.target.value), placeholder: "\u63CF\u8FF0" }), /* @__PURE__ */ React69__default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.defaultTtlHours, onChange: (e) => update("defaultTtlHours", Number(e.target.value) || 1), placeholder: "\u9ED8\u8BA4\u4FDD\u5B58\u65F6\u957F\uFF08\u5C0F\u65F6\uFF09" }), /* @__PURE__ */ React69__default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxFiles, onChange: (e) => update("maxFiles", Number(e.target.value) || 1), placeholder: "\u6700\u5927\u6587\u4EF6\u6570" }), /* @__PURE__ */ React69__default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxSingleFileSizeMb, onChange: (e) => update("maxSingleFileSizeMb", Number(e.target.value) || 1), placeholder: "\u5355\u6587\u4EF6\u4E0A\u9650 MB" }), /* @__PURE__ */ React69__default.createElement("input", { className: "rounded border px-3 py-2", type: "number", value: config.maxTotalFileSizeMb, onChange: (e) => update("maxTotalFileSizeMb", Number(e.target.value) || 1), placeholder: "\u603B\u5927\u5C0F\u4E0A\u9650 MB" }), /* @__PURE__ */ React69__default.createElement(
8358
+ "textarea",
8359
+ {
8360
+ className: "rounded border px-3 py-2 md:col-span-2",
8361
+ rows: 3,
8362
+ value: extText,
8363
+ onChange: (e) => update(
8364
+ "allowedExtensions",
8365
+ e.target.value.split(",").map((v) => v.trim()).filter(Boolean)
8366
+ ),
8367
+ placeholder: "\u5141\u8BB8\u540E\u7F00\uFF0C\u9017\u53F7\u5206\u9694"
8368
+ }
8369
+ )), /* @__PURE__ */ React69__default.createElement("div", { className: "flex gap-2" }, /* @__PURE__ */ React69__default.createElement("button", { className: "rounded bg-indigo-600 px-3 py-2 text-white", disabled: saving, onClick: save }, saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58\u914D\u7F6E"), /* @__PURE__ */ React69__default.createElement("button", { className: "rounded border px-3 py-2", onClick: reset }, "\u6062\u590D\u9ED8\u8BA4")));
8370
+ };
8371
+
8016
8372
  // src/storage/adapters/react-native-adapter.ts
8017
8373
  var AsyncStorage = null;
8018
8374
  try {
@@ -8366,6 +8722,6 @@ function useElectronStorage(key, defaultValue) {
8366
8722
  return useStorage(electronStorage, key, defaultValue);
8367
8723
  }
8368
8724
 
8369
- export { About_default as About, Dialog as AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, DialogDescription as AlertDialogDescription, DialogFooter as AlertDialogFooter, DialogHeader as AlertDialogHeader, DialogOverlay as AlertDialogOverlay, DialogPortal as AlertDialogPortal, DialogTitle as AlertDialogTitle, DialogTrigger as AlertDialogTrigger, AutoOpenModal, Avatar, AvatarFallback, AvatarImage, BackButton, BackgroundRemover, Badge, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryFilter, CollisionBalls, CompletionFilterComponent, ConfirmModal, ConsoleLoggerAdapter, Contact_default as Contact, DANMAKU_MAX_LENGTH, DANMAKU_TRACK_COUNT, DEFAULT_FESTIVAL_CARD_CONFIG, DEFAULT_MAX_ACTIVE_FIREWORKS, DEFAULT_MAX_PARTICLES, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DanmakuPanel, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DraggableExperimentGrid, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EnhancedAvatar, ExperimentCard, ExperimentGrid, ExperimentItemGrid, FIREWORK_KIND_LABELS, FestivalCardBook3D, FestivalCardConfigEditor, FestivalCardConfigPage, FestivalCardManagedPage, FestivalCardPageRenderer, FestivalCardService, FestivalCardStudio, FilterButtonGroup, FireworksCanvas, FireworksControlPanel, FloatingMenu_default as FloatingMenu, FloatingMenuExample_default as FloatingMenuExample, GenericOrderManager, Grid, Home_default as Home, ImageMappingPanel, InMemorySkillRegistry, Input, Label, LocalImageMappingPanel, LogLevel, Logger, MIKU_PALETTE, MikuFireworks3D, Modal, NORMAL_PALETTE, Navigation_default as Navigation, NavigationItem_default as NavigationItem, NavigationToggle_default as NavigationToggle, OCRScanner, PageHeader, PermissionGuard, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ProfileButton, ProfileModal, Progress, ProjectCarousel, ScreenReceiverPanel, ScrollArea, ScrollBar, SearchBox, SearchResultHint, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SentimentAnalyzer, Separator, Dialog as Sheet, DialogClose as SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, DialogOverlay as SheetOverlay, DialogPortal as SheetPortal, SheetTitle, DialogTrigger as SheetTrigger, SmartAssistant, SortControl, SortModeToggle, SortableExperimentItem, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Timeline, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserInfoBar, WebSocketTransport, applyPromptTemplate, arrayUtils, badgeVariants, buttonVariants, cn, createAiClient, createChatSession, createInMemoryFestivalCardDb, createLogger, createOpenAICompatibleProvider, createSkillRegistry, debugUtils, errorUtils, fileUtils, filterExperiments, formatTime, getAllTags, getCategoryColor, getCategoryDisplayName, getCompletionFilterDisplayName, getCompletionStatusColor, getCompletionStatusText, getExperimentCounts, japaneseUtils, logger, normalizeFestivalCardConfig, normalizePromptVariables, resizeFestivalCardPages, resolveScreenReceiverSignalUrl, skillToToolDefinition, sortExperiments, stringUtils, useAiChat, useAsyncStorage, useBackgroundRemoval, useDanmakuController, useElectronStorage, useFestivalCardConfig, useFireworksEngine, useFireworksRealtime, useLocalStorage, useOCR, useScreenReceiver, useSentimentAnalysis, useStorage, useTaroStorage, useTextGeneration, validateExperiment, validators };
8725
+ export { About_default as About, Dialog as AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, DialogDescription as AlertDialogDescription, DialogFooter as AlertDialogFooter, DialogHeader as AlertDialogHeader, DialogOverlay as AlertDialogOverlay, DialogPortal as AlertDialogPortal, DialogTitle as AlertDialogTitle, DialogTrigger as AlertDialogTrigger, AutoOpenModal, Avatar, AvatarFallback, AvatarImage, BackButton, BackgroundRemover, Badge, BoothConfigPage, BoothRedeemPanel, BoothSuccessCard, BoothUploadPanel, BoothVaultService, Button, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CategoryFilter, CollisionBalls, CompletionFilterComponent, ConfirmModal, ConsoleLoggerAdapter, Contact_default as Contact, DANMAKU_MAX_LENGTH, DANMAKU_TRACK_COUNT, DEFAULT_FESTIVAL_CARD_CONFIG, DEFAULT_MAX_ACTIVE_FIREWORKS, DEFAULT_MAX_PARTICLES, DEFAULT_OPENAI_BASE_URL, DEFAULT_OPENAI_MODEL, DanmakuPanel, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DraggableExperimentGrid, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, EmptyState, EnhancedAvatar, ExperimentCard, ExperimentGrid, ExperimentItemGrid, FIREWORK_KIND_LABELS, FestivalCardBook3D, FestivalCardConfigEditor, FestivalCardConfigPage, FestivalCardManagedPage, FestivalCardPageRenderer, FestivalCardService, FestivalCardStudio, FilterButtonGroup, FireworksCanvas, FireworksControlPanel, FloatingMenu_default as FloatingMenu, FloatingMenuExample_default as FloatingMenuExample, GenericOrderManager, Grid, Home_default as Home, ImageMappingPanel, InMemorySkillRegistry, Input, Label, LocalImageMappingPanel, LogLevel, Logger, MIKU_PALETTE, MikuFireworks3D, Modal, NORMAL_PALETTE, Navigation_default as Navigation, NavigationItem_default as NavigationItem, NavigationToggle_default as NavigationToggle, OCRScanner, PageHeader, PermissionGuard, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, ProfileButton, ProfileModal, Progress, ProjectCarousel, ScreenReceiverPanel, ScrollArea, ScrollBar, SearchBox, SearchResultHint, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, SentimentAnalyzer, Separator, Dialog as Sheet, DialogClose as SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, DialogOverlay as SheetOverlay, DialogPortal as SheetPortal, SheetTitle, DialogTrigger as SheetTrigger, SmartAssistant, SortControl, SortModeToggle, SortableExperimentItem, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, Timeline, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserInfoBar, WebSocketTransport, applyPromptTemplate, arrayUtils, badgeVariants, buttonVariants, cn, createAiClient, createChatSession, createInMemoryFestivalCardDb, createLogger, createOpenAICompatibleProvider, createSkillRegistry, debugUtils, defaultVocaloidBoothConfig, errorUtils, fileUtils, filterExperiments, formatTime, generateMatchCode, getAllTags, getCategoryColor, getCategoryDisplayName, getCompletionFilterDisplayName, getCompletionStatusColor, getCompletionStatusText, getExperimentCounts, japaneseUtils, logger, normalizeFestivalCardConfig, normalizeMatchCode, normalizePromptVariables, normalizeVocaloidBoothConfig, resizeFestivalCardPages, resolveScreenReceiverSignalUrl, skillToToolDefinition, sortExperiments, stringUtils, useAiChat, useAsyncStorage, useBackgroundRemoval, useDanmakuController, useElectronStorage, useFestivalCardConfig, useFireworksEngine, useFireworksRealtime, useLocalStorage, useOCR, useScreenReceiver, useSentimentAnalysis, useStorage, useTaroStorage, useTextGeneration, validateExperiment, validators };
8370
8726
  //# sourceMappingURL=index.mjs.map
8371
8727
  //# sourceMappingURL=index.mjs.map