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/boothVaultService-Cn4WPhjg.d.mts +83 -0
- package/dist/boothVaultService-Cn4WPhjg.d.ts +83 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +366 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +358 -2
- package/dist/index.mjs.map +1 -1
- package/dist/vocaloidBooth/index.d.mts +64 -0
- package/dist/vocaloidBooth/index.d.ts +64 -0
- package/dist/vocaloidBooth/index.js +376 -0
- package/dist/vocaloidBooth/index.js.map +1 -0
- package/dist/vocaloidBooth/index.mjs +362 -0
- package/dist/vocaloidBooth/index.mjs.map +1 -0
- package/dist/vocaloidBooth/server/index.d.mts +110 -0
- package/dist/vocaloidBooth/server/index.d.ts +110 -0
- package/dist/vocaloidBooth/server/index.js +247 -0
- package/dist/vocaloidBooth/server/index.js.map +1 -0
- package/dist/vocaloidBooth/server/index.mjs +237 -0
- package/dist/vocaloidBooth/server/index.mjs.map +1 -0
- package/dist/vocaloidBooth/web/index.d.mts +3 -0
- package/dist/vocaloidBooth/web/index.d.ts +3 -0
- package/dist/vocaloidBooth/web/index.js +376 -0
- package/dist/vocaloidBooth/web/index.js.map +1 -0
- package/dist/vocaloidBooth/web/index.mjs +362 -0
- package/dist/vocaloidBooth/web/index.mjs.map +1 -0
- package/package.json +16 -1
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
|