@senguoyun/dsh-arkme 0.1.44 → 0.1.46
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/cordis.patch.yml +1 -0
- package/docs/direct-message-refusal.md +39 -0
- package/docs/plugin-release.md +28 -3
- package/lib/client.js +1719 -562
- package/lib/client.js.map +1 -1
- package/lib/harness-model-client.js +4922 -0
- package/lib/index.js +1605 -110
- package/lib/persistent-extension.js +1 -1
- package/lib/{persistent-runtime-CEGGFpR7.js → persistent-runtime-DpNP9UKr.js} +1152 -155
- package/lib/sdk.js +25 -2
- package/lib/types/arkme-service.d.ts +10 -1
- package/lib/types/arkme-service.d.ts.map +1 -1
- package/lib/types/chat-pin-projection.d.ts +4 -0
- package/lib/types/chat-pin-projection.d.ts.map +1 -0
- package/lib/types/chat-realtime.d.ts +13 -1
- package/lib/types/chat-realtime.d.ts.map +1 -1
- package/lib/types/client/ArkmeAttachmentStrip.d.ts +2 -1
- package/lib/types/client/ArkmeAttachmentStrip.d.ts.map +1 -1
- package/lib/types/client/ArkmeBillingSettings.d.ts +7 -1
- package/lib/types/client/ArkmeBillingSettings.d.ts.map +1 -1
- package/lib/types/client/ArkmeMarkdownComposerInput.d.ts.map +1 -1
- package/lib/types/client/ArkmeModelSelect.d.ts +20 -0
- package/lib/types/client/ArkmeModelSelect.d.ts.map +1 -0
- package/lib/types/client/ArkmeNoteDetails.d.ts +3 -1
- package/lib/types/client/ArkmeNoteDetails.d.ts.map +1 -1
- package/lib/types/client/ArkmeRichContent.d.ts +3 -1
- package/lib/types/client/ArkmeRichContent.d.ts.map +1 -1
- package/lib/types/client/ArkmeSidebar.d.ts.map +1 -1
- package/lib/types/client/ArkmeVirtualWorkspace.d.ts.map +1 -1
- package/lib/types/client/chat-directory-store.d.ts +7 -1
- package/lib/types/client/chat-directory-store.d.ts.map +1 -1
- package/lib/types/client/conversation-memory-cache.d.ts +3 -0
- package/lib/types/client/conversation-memory-cache.d.ts.map +1 -1
- package/lib/types/client/conversation-timeline-refresh.d.ts +5 -0
- package/lib/types/client/conversation-timeline-refresh.d.ts.map +1 -0
- package/lib/types/client/direct-message-admission.d.ts +15 -0
- package/lib/types/client/direct-message-admission.d.ts.map +1 -0
- package/lib/types/client/harness-model-client.d.ts +15 -0
- package/lib/types/client/harness-model-client.d.ts.map +1 -0
- package/lib/types/client/realtime-client-events.d.ts.map +1 -1
- package/lib/types/client/record-reedit-editors.d.ts +38 -0
- package/lib/types/client/record-reedit-editors.d.ts.map +1 -0
- package/lib/types/client/record-reedit-submissions.d.ts +12 -0
- package/lib/types/client/record-reedit-submissions.d.ts.map +1 -0
- package/lib/types/client/timeline-media.d.ts +4 -0
- package/lib/types/client/timeline-media.d.ts.map +1 -0
- package/lib/types/client/ui-controller.d.ts.map +1 -1
- package/lib/types/direct-message-admission.d.ts +17 -0
- package/lib/types/direct-message-admission.d.ts.map +1 -0
- package/lib/types/file-transfer-contract.d.ts +2 -0
- package/lib/types/file-transfer-contract.d.ts.map +1 -1
- package/lib/types/file-transfer-owner.d.ts.map +1 -1
- package/lib/types/harness-embed-contract.d.ts +1 -0
- package/lib/types/harness-embed-contract.d.ts.map +1 -1
- package/lib/types/harness-embed-route.d.ts +2 -1
- package/lib/types/harness-embed-route.d.ts.map +1 -1
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/local-database.d.ts +7 -2
- package/lib/types/local-database.d.ts.map +1 -1
- package/lib/types/managed-ai/adapter.d.ts +4 -1
- package/lib/types/managed-ai/adapter.d.ts.map +1 -1
- package/lib/types/managed-ai/image-preparer.d.ts +19 -0
- package/lib/types/managed-ai/image-preparer.d.ts.map +1 -0
- package/lib/types/managed-ai/multipart-upload.d.ts +8 -0
- package/lib/types/managed-ai/multipart-upload.d.ts.map +1 -0
- package/lib/types/managed-ai/transport.d.ts +59 -0
- package/lib/types/managed-ai/transport.d.ts.map +1 -0
- package/lib/types/record-reedit-contract.d.ts +84 -1
- package/lib/types/record-reedit-contract.d.ts.map +1 -1
- package/lib/types/rich-media-routes.d.ts.map +1 -1
- package/lib/types/sdk/index.d.ts +6 -1
- package/lib/types/sdk/index.d.ts.map +1 -1
- package/lib/types/services/chat-realtime-service.d.ts +4 -3
- package/lib/types/services/chat-realtime-service.d.ts.map +1 -1
- package/lib/types/services/chat-service.d.ts.map +1 -1
- package/lib/types/services/direct-message-admission-service.d.ts +21 -0
- package/lib/types/services/direct-message-admission-service.d.ts.map +1 -0
- package/lib/types/services/file-transfers.d.ts +8 -1
- package/lib/types/services/file-transfers.d.ts.map +1 -1
- package/lib/types/services/media-service.d.ts +2 -0
- package/lib/types/services/media-service.d.ts.map +1 -1
- package/lib/types/services/message-action-infrastructure.d.ts.map +1 -1
- package/lib/types/services/record-reedit-attachments.d.ts +30 -0
- package/lib/types/services/record-reedit-attachments.d.ts.map +1 -0
- package/lib/types/services/record-reedit-submissions.d.ts +37 -0
- package/lib/types/services/record-reedit-submissions.d.ts.map +1 -0
- package/lib/types/services/record-service.d.ts +30 -2
- package/lib/types/services/record-service.d.ts.map +1 -1
- package/lib/types/services/recording-forward-gateway.d.ts.map +1 -1
- package/lib/types/services/service.d.ts +12 -2
- package/lib/types/services/service.d.ts.map +1 -1
- package/lib/types/services/source-service.d.ts +0 -1
- package/lib/types/services/source-service.d.ts.map +1 -1
- package/lib/types/state-store.d.ts +8 -2
- package/lib/types/state-store.d.ts.map +1 -1
- package/lib/types/tools/business/conversation/direct-message-refusal.d.ts +3 -0
- package/lib/types/tools/business/conversation/direct-message-refusal.d.ts.map +1 -0
- package/lib/types/tools/business/index.d.ts.map +1 -1
- package/lib/types/tools/business/records/reedit.d.ts.map +1 -1
- package/lib/types/tools/ports/index.d.ts +2 -1
- package/lib/types/tools/ports/index.d.ts.map +1 -1
- package/lib/types/tools/registry/registrar.d.ts.map +1 -1
- package/lib/types/types.d.ts +24 -2
- package/lib/types/types.d.ts.map +1 -1
- package/package.json +6 -6
|
@@ -0,0 +1,4922 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@senguoyun/dsh-arkme/harness-model",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let react_dom = require("react-dom");
|
|
9
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
|
+
//#region src/client/arkme-billing.css?inline
|
|
11
|
+
var arkme_billing_default = ".arkme-billing-backdrop {\n z-index: 120;\n backdrop-filter: blur(3px);\n background: #181b223d;\n place-items: center;\n padding: 28px;\n display: grid;\n position: fixed;\n inset: 0;\n}\n\n.arkme-billing-recharge-dialog, .arkme-billing-payment-dialog {\n color: #20232c;\n background: #fff;\n border: 1px solid #dedfe3;\n border-radius: 20px;\n width: min(620px, 100%);\n max-height: min(720px, 100vh - 56px);\n overflow-y: auto;\n box-shadow: 0 24px 72px #191c232e;\n}\n\n.arkme-billing-payment-dialog {\n width: min(420px, 100%);\n}\n\n.arkme-billing-dialog-header {\n border-bottom: 1px solid #ececef;\n justify-content: space-between;\n align-items: flex-start;\n gap: 18px;\n min-height: 78px;\n padding: 20px 22px 16px;\n display: flex;\n}\n\n.arkme-billing-dialog-header h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n line-height: 25px;\n}\n\n.arkme-billing-dialog-header p {\n overflow-wrap: anywhere;\n color: #8a8e96;\n margin: 4px 0 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.arkme-billing-dialog-header > button {\n color: #777c85;\n width: 32px;\n height: 32px;\n font: inherit;\n cursor: pointer;\n background: none;\n border: 0;\n border-radius: 9px;\n flex: none;\n place-items: center;\n font-size: 22px;\n display: grid;\n}\n\n.arkme-billing-dialog-header > button:hover {\n color: #20232c;\n background: #f3f3f5;\n}\n\n.arkme-billing-dialog-body {\n padding: 18px 22px 22px;\n}\n\n.arkme-billing-dialog-balance {\n border-bottom: 1px solid #ececef;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n column-gap: 12px;\n min-height: 48px;\n padding: 0 2px 16px;\n display: grid;\n}\n\n.arkme-billing-dialog-balance > span {\n color: #8a8e96;\n grid-column: 1;\n font-size: 11px;\n line-height: 16px;\n}\n\n.arkme-billing-dialog-balance > strong {\n grid-column: 1;\n font-size: 22px;\n font-weight: 600;\n line-height: 28px;\n}\n\n.arkme-billing-dialog-balance > button, .arkme-billing-error > button {\n color: #626772;\n height: 30px;\n font: inherit;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 9px;\n grid-area: 1 / 2 / span 2;\n padding: 0 12px;\n font-size: 11px;\n}\n\n.arkme-billing-products-section, .arkme-billing-payment-section {\n padding-top: 17px;\n}\n\n.arkme-billing-products-section h3, .arkme-billing-payment-section h3 {\n color: #747984;\n margin: 0 0 11px;\n font-size: 12px;\n font-weight: 500;\n line-height: 18px;\n}\n\n.arkme-billing-product-grid {\n width: 100%;\n}\n\n.arkme-billing-product-grid > button {\n color: #20232c;\n min-width: 0;\n min-height: 62px;\n font: inherit;\n text-align: left;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 11px;\n align-content: center;\n gap: 4px;\n padding: 9px 11px;\n display: grid;\n}\n\n.arkme-billing-product-grid > button:hover {\n background: #f8f8f9;\n border-color: #cfd2d8;\n}\n\n.arkme-billing-product-grid > button.is-selected {\n border-color: #75839e;\n box-shadow: 0 0 0 1px #75839e;\n}\n\n.arkme-billing-product-grid > button > span {\n white-space: nowrap;\n text-overflow: ellipsis;\n font-size: 12px;\n font-weight: 500;\n line-height: 17px;\n overflow: hidden;\n}\n\n.arkme-billing-product-grid > button > strong {\n font-size: 14px;\n font-weight: 600;\n line-height: 19px;\n}\n\n.arkme-billing-payment-actions {\n flex-wrap: wrap;\n gap: 10px;\n display: flex;\n}\n\n.arkme-billing-payment-actions > button {\n color: #333842;\n min-width: 154px;\n height: 38px;\n font: inherit;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 10px;\n justify-content: center;\n align-items: center;\n gap: 8px;\n padding: 0 14px;\n font-size: 12px;\n display: inline-flex;\n}\n\n.arkme-billing-payment-actions > button:hover {\n background: #f7f7f8;\n border-color: #cfd3da;\n}\n\n.arkme-billing-payment-actions > button:disabled, .arkme-billing-product-grid > button:disabled {\n opacity: .48;\n cursor: default;\n}\n\n.arkme-billing-platform-icon {\n flex: none;\n width: 18px;\n height: 18px;\n}\n\n.arkme-billing-platform-icon.is-alipay {\n color: #1677ff;\n}\n\n.arkme-billing-platform-icon.is-wechat {\n color: #07c160;\n}\n\n.arkme-billing-state {\n color: #858992;\n margin: 12px 0 2px;\n font-size: 12px;\n line-height: 18px;\n}\n\n.arkme-billing-error {\n color: #b54841;\n justify-content: space-between;\n align-items: center;\n gap: 12px;\n margin-top: 10px;\n font-size: 12px;\n line-height: 18px;\n display: flex;\n}\n\n.arkme-billing-error.is-purchase {\n justify-content: flex-start;\n}\n\n.arkme-billing-payment-content {\n text-align: center;\n justify-items: center;\n gap: 10px;\n padding: 28px 24px 18px;\n display: grid;\n}\n\n.arkme-billing-payment-content > .arkme-billing-platform-icon {\n width: 38px;\n height: 38px;\n margin-bottom: 4px;\n}\n\n.arkme-billing-payment-content > img {\n border-radius: 10px;\n width: 210px;\n height: 210px;\n}\n\n.arkme-billing-payment-content > strong {\n font-size: 14px;\n font-weight: 600;\n line-height: 21px;\n}\n\n.arkme-billing-payment-content > p {\n color: #858992;\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n}\n\n.arkme-billing-payment-content.is-paid > strong {\n color: #16794f;\n font-size: 18px;\n line-height: 25px;\n}\n\n.arkme-billing-payment-content.is-paid > .arkme-billing-error {\n justify-content: center;\n}\n\n.arkme-billing-payment-content.is-crediting > strong {\n color: #335f9e;\n}\n\n.arkme-billing-dialog-actions {\n flex-wrap: wrap;\n justify-content: center;\n gap: 9px;\n padding: 0 22px 20px;\n display: flex;\n}\n\n.arkme-billing-dialog-actions > button, .arkme-billing-primary-action {\n color: #3c424d;\n height: 34px;\n font: inherit;\n cursor: pointer;\n background: #fff;\n border: 1px solid #dfe1e5;\n border-radius: 9px;\n padding: 0 14px;\n font-size: 12px;\n}\n\n.arkme-billing-dialog-actions > .arkme-billing-primary-action, .arkme-billing-payment-content > .arkme-billing-primary-action {\n color: #fff;\n background: #20232d;\n border-color: #20232d;\n}\n";
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/bot-avatar-ref.ts
|
|
14
|
+
const ARKME_BOT_AVATAR_REF_PATTERN = /^file_asset:\/\/[A-Za-z0-9_-]{8,128}$/;
|
|
15
|
+
/** Bot avatars cross process boundaries only as account-scoped file asset references. */
|
|
16
|
+
function isArkmeBotAvatarRef(value) {
|
|
17
|
+
return ARKME_BOT_AVATAR_REF_PATTERN.test(value.trim());
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
//#region src/extensions/source.ts
|
|
21
|
+
function normalizeGitHubRepositoryURL(raw) {
|
|
22
|
+
const value = raw?.trim() ?? "";
|
|
23
|
+
if (value === "") return void 0;
|
|
24
|
+
let parsed;
|
|
25
|
+
try {
|
|
26
|
+
parsed = new URL(value);
|
|
27
|
+
} catch {
|
|
28
|
+
throw new TypeError("GitHub repository URL is invalid");
|
|
29
|
+
}
|
|
30
|
+
const host = parsed.hostname.toLowerCase();
|
|
31
|
+
if (parsed.protocol !== "https:" || !["github.com", "www.github.com"].includes(host) || parsed.username !== "" || parsed.password !== "" || parsed.port !== "" || parsed.search !== "" || parsed.hash !== "") throw new TypeError("GitHub repository URL is invalid");
|
|
32
|
+
const parts = parsed.pathname.replace(/\/$/, "").replace(/^\//, "").split("/");
|
|
33
|
+
if (parts.length !== 2) throw new TypeError("GitHub repository URL must point to a repository root");
|
|
34
|
+
const owner = parts[0];
|
|
35
|
+
const repository = parts[1].replace(/\.git$/, "");
|
|
36
|
+
if (!/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/.test(owner) || !/^[A-Za-z0-9._-]{1,100}$/.test(repository) || repository.endsWith(".")) throw new TypeError("GitHub repository URL is invalid");
|
|
37
|
+
return `https://github.com/${owner.toLowerCase()}/${repository.toLowerCase()}`;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/sdk/index.ts
|
|
41
|
+
const DEFAULT_ROUTE = "/arkme-self/api";
|
|
42
|
+
function expectedUserIdHeaders(expectedUserId) {
|
|
43
|
+
if (expectedUserId === void 0) return {};
|
|
44
|
+
if (!Number.isSafeInteger(expectedUserId) || expectedUserId <= 0) throw new TypeError("Arkme expected user ID must be a positive safe integer");
|
|
45
|
+
return { "X-Arkme-Expected-User-Id": String(expectedUserId) };
|
|
46
|
+
}
|
|
47
|
+
function expectedUserIdParam(expectedUserId) {
|
|
48
|
+
if (expectedUserId === void 0) return {};
|
|
49
|
+
expectedUserIdHeaders(expectedUserId);
|
|
50
|
+
return { expectedUserId };
|
|
51
|
+
}
|
|
52
|
+
var ArkmeClientError = class extends Error {
|
|
53
|
+
body;
|
|
54
|
+
constructor(body) {
|
|
55
|
+
super(body.message);
|
|
56
|
+
this.body = body;
|
|
57
|
+
this.name = "ArkmeClientError";
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
function safeSdkCallDetail(detail) {
|
|
61
|
+
if (detail.videoRecord === void 0) return detail;
|
|
62
|
+
return {
|
|
63
|
+
...detail,
|
|
64
|
+
videoRecord: {
|
|
65
|
+
available: detail.videoRecord.available,
|
|
66
|
+
source: detail.videoRecord.source
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
var ArkmeSdk = class {
|
|
71
|
+
route;
|
|
72
|
+
fetchImpl;
|
|
73
|
+
constructor(options = {}) {
|
|
74
|
+
const route = options.route ?? DEFAULT_ROUTE;
|
|
75
|
+
if (!/^\/[A-Za-z0-9/_-]+$/.test(route) || route.startsWith("//")) throw new TypeError("Arkme SDK route must be a same-origin absolute path");
|
|
76
|
+
this.route = route;
|
|
77
|
+
this.fetchImpl = options.fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
78
|
+
}
|
|
79
|
+
async capabilities(signal) {
|
|
80
|
+
const capabilities = await this.call("provider.capabilities", void 0, signal);
|
|
81
|
+
if (capabilities.contractVersion !== 1) throw new Error(`Unsupported Arkme provider contract version ${String(capabilities.contractVersion)}`);
|
|
82
|
+
return capabilities;
|
|
83
|
+
}
|
|
84
|
+
async state(signal) {
|
|
85
|
+
return await this.call("provider.state", void 0, signal);
|
|
86
|
+
}
|
|
87
|
+
/** Read the credential-free managed OpenAPI MCP lifecycle projection. */
|
|
88
|
+
async openApiMcpStatus(signal) {
|
|
89
|
+
return await this.call("openapi.mcp.status", void 0, signal);
|
|
90
|
+
}
|
|
91
|
+
/** Retry a degraded managed MCP connection without exposing or accepting credentials. */
|
|
92
|
+
async retryOpenApiMcp(signal) {
|
|
93
|
+
return await this.call("openapi.mcp.retry", void 0, signal);
|
|
94
|
+
}
|
|
95
|
+
/** List every Team visible to the current account using opaque OpenAPI references. */
|
|
96
|
+
async listTeams(options = {}) {
|
|
97
|
+
return await this.call("team.list", {
|
|
98
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
99
|
+
...options.pageCursor === void 0 ? {} : { pageCursor: options.pageCursor }
|
|
100
|
+
}, options.signal);
|
|
101
|
+
}
|
|
102
|
+
/** Resolve exact Team Jotmo IDs or names only within the current account's Teams. */
|
|
103
|
+
async resolveTeams(items, signal) {
|
|
104
|
+
return await this.call("team.resolve", { items }, signal);
|
|
105
|
+
}
|
|
106
|
+
/** Read an authorized Team member page without exposing backend numeric identities. */
|
|
107
|
+
async listTeamMembers(teamRef, options = {}) {
|
|
108
|
+
return await this.call("team.members.list", {
|
|
109
|
+
teamRef,
|
|
110
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
111
|
+
...options.pageCursor === void 0 ? {} : { pageCursor: options.pageCursor }
|
|
112
|
+
}, options.signal);
|
|
113
|
+
}
|
|
114
|
+
/** Create Teams as an explicit current-human mutation; each item is independently idempotent. */
|
|
115
|
+
async createTeams(items, signal) {
|
|
116
|
+
return await this.call("team.create", { items }, signal);
|
|
117
|
+
}
|
|
118
|
+
/** Join Teams by exact public Jotmo ID as an explicit current-human mutation. */
|
|
119
|
+
async joinTeamsByJotmoID(items, signal) {
|
|
120
|
+
return await this.call("team.join-by-jotmo-id", { items }, signal);
|
|
121
|
+
}
|
|
122
|
+
/** Read Browser-safe installed extension projections without Host filesystem paths or runtime IDs. */
|
|
123
|
+
async installedExtensions(signal) {
|
|
124
|
+
return await this.call("extensions.installed-list", void 0, signal);
|
|
125
|
+
}
|
|
126
|
+
/** Search the market through the same Host owner used by Arkme UI and Tools. */
|
|
127
|
+
async searchExtensions(query = "", limit = 20, signal) {
|
|
128
|
+
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 50) throw new TypeError("Arkme extension search limit must be 1-50");
|
|
129
|
+
return await this.call("extensions.catalog.list", {
|
|
130
|
+
query: query.trim(),
|
|
131
|
+
limit
|
|
132
|
+
}, signal);
|
|
133
|
+
}
|
|
134
|
+
async extensionDetail(extensionId, signal) {
|
|
135
|
+
if (extensionId.trim() === "") throw new TypeError("Arkme extension ID must not be empty");
|
|
136
|
+
return await this.call("extensions.catalog.detail", { extensionId: extensionId.trim() }, signal);
|
|
137
|
+
}
|
|
138
|
+
/** Query V1/V2/V3 install authority before asking the user to approve installation. */
|
|
139
|
+
async extensionInstallPreview(extensionId, version, signal) {
|
|
140
|
+
if (extensionId.trim() === "") throw new TypeError("Arkme extension ID must not be empty");
|
|
141
|
+
return await this.call("extensions.install.preview", {
|
|
142
|
+
extensionId: extensionId.trim(),
|
|
143
|
+
...version === void 0 || version.trim() === "" ? {} : { version: version.trim() }
|
|
144
|
+
}, signal);
|
|
145
|
+
}
|
|
146
|
+
async extensionEnabledState(extensionId, signal) {
|
|
147
|
+
if (extensionId.trim() === "") throw new TypeError("Arkme extension ID must not be empty");
|
|
148
|
+
return await this.call("extensions.enabled-state", { extensionId }, signal);
|
|
149
|
+
}
|
|
150
|
+
/** Change desired activation without uninstalling the verified extension artifact. */
|
|
151
|
+
async setExtensionEnabled(extensionId, enabled, signal) {
|
|
152
|
+
if (extensionId.trim() === "") throw new TypeError("Arkme extension ID must not be empty");
|
|
153
|
+
return await this.call("extensions.enabled.set", {
|
|
154
|
+
extensionId,
|
|
155
|
+
enabled
|
|
156
|
+
}, signal);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Delete an owned marketplace extension and remove its current local runtime/Profile/source references.
|
|
160
|
+
* Consumers must call this only from an explicit current human delete action.
|
|
161
|
+
*/
|
|
162
|
+
async deleteExtension(extensionId, signal) {
|
|
163
|
+
const normalized = extensionId.trim();
|
|
164
|
+
if (normalized === "") throw new TypeError("Arkme extension ID must not be empty");
|
|
165
|
+
return await this.call("extensions.delete", { extensionId: normalized }, signal);
|
|
166
|
+
}
|
|
167
|
+
/** Build the same-origin URL used by every extension list/detail avatar surface. */
|
|
168
|
+
extensionIconUrl(extensionId, iconRef) {
|
|
169
|
+
if (extensionId.trim() === "" || !/^icon_v1_[a-f0-9]{64}$/.test(iconRef.trim())) throw new TypeError("Arkme extension icon identity is invalid");
|
|
170
|
+
return `${this.route}/extension-icon?extension_id=${encodeURIComponent(extensionId.trim())}&icon_ref=${encodeURIComponent(iconRef.trim())}`;
|
|
171
|
+
}
|
|
172
|
+
/** Upload or replace an owned extension icon without exposing registry signed transport. */
|
|
173
|
+
async setExtensionIcon(extensionId, file, options = {}) {
|
|
174
|
+
const mediaType = file.type.toLowerCase();
|
|
175
|
+
if (extensionId.trim() === "" || ![
|
|
176
|
+
"image/png",
|
|
177
|
+
"image/jpeg",
|
|
178
|
+
"image/webp"
|
|
179
|
+
].includes(mediaType)) throw new TypeError("Arkme extension icon must be PNG, JPEG, or WebP");
|
|
180
|
+
if (file.size <= 0 || file.size > 2 * 1024 * 1024) throw new TypeError("Arkme extension icon must be smaller than 2 MiB");
|
|
181
|
+
const mutationId = options.clientMutationId ?? crypto.randomUUID();
|
|
182
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(mutationId)) throw new TypeError("Arkme extension icon mutation id must be a UUID");
|
|
183
|
+
const response = await this.fetchImpl(`${this.route}/extension-icon/upload`, {
|
|
184
|
+
method: "POST",
|
|
185
|
+
credentials: "same-origin",
|
|
186
|
+
headers: {
|
|
187
|
+
"Content-Type": mediaType,
|
|
188
|
+
"X-Arkme-Extension-Id": extensionId.trim(),
|
|
189
|
+
"X-Arkme-Idempotency-Key": mutationId
|
|
190
|
+
},
|
|
191
|
+
body: file,
|
|
192
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
193
|
+
});
|
|
194
|
+
let body;
|
|
195
|
+
try {
|
|
196
|
+
body = await response.json();
|
|
197
|
+
} catch {
|
|
198
|
+
throw new ArkmeClientError({
|
|
199
|
+
code: "local-response-invalid",
|
|
200
|
+
message: "Arkme 插件返回了无效响应",
|
|
201
|
+
retryable: true
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (!body.ok) throw new ArkmeClientError(body.error);
|
|
205
|
+
return body.value;
|
|
206
|
+
}
|
|
207
|
+
extensionPreviewUrl(extensionId, previewRef) {
|
|
208
|
+
if (extensionId.trim() === "" || !/^preview_v1_[a-f0-9]{64}$/.test(previewRef.trim())) throw new TypeError("Arkme extension preview identity is invalid");
|
|
209
|
+
return `${this.route}/extension-preview?extension_id=${encodeURIComponent(extensionId.trim())}&preview_ref=${encodeURIComponent(previewRef.trim())}`;
|
|
210
|
+
}
|
|
211
|
+
async addExtensionPreview(extensionId, file, options = {}) {
|
|
212
|
+
const mediaType = file.type.toLowerCase();
|
|
213
|
+
if (extensionId.trim() === "" || ![
|
|
214
|
+
"image/png",
|
|
215
|
+
"image/jpeg",
|
|
216
|
+
"image/webp"
|
|
217
|
+
].includes(mediaType)) throw new TypeError("Arkme extension preview must be PNG, JPEG, or WebP");
|
|
218
|
+
if (file.size <= 0 || file.size > 5 * 1024 * 1024) throw new TypeError("Arkme extension preview must be smaller than 5 MiB");
|
|
219
|
+
const mutationId = options.clientMutationId ?? crypto.randomUUID();
|
|
220
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(mutationId)) throw new TypeError("Arkme extension preview mutation id must be a UUID");
|
|
221
|
+
const response = await this.fetchImpl(`${this.route}/extension-preview/upload`, {
|
|
222
|
+
method: "POST",
|
|
223
|
+
credentials: "same-origin",
|
|
224
|
+
headers: {
|
|
225
|
+
"Content-Type": mediaType,
|
|
226
|
+
"X-Arkme-Extension-Id": extensionId.trim(),
|
|
227
|
+
"X-Arkme-Idempotency-Key": mutationId
|
|
228
|
+
},
|
|
229
|
+
body: file,
|
|
230
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
231
|
+
});
|
|
232
|
+
let body;
|
|
233
|
+
try {
|
|
234
|
+
body = await response.json();
|
|
235
|
+
} catch {
|
|
236
|
+
throw new ArkmeClientError({
|
|
237
|
+
code: "local-response-invalid",
|
|
238
|
+
message: "Arkme 插件返回了无效响应",
|
|
239
|
+
retryable: true
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
if (!body.ok) throw new ArkmeClientError(body.error);
|
|
243
|
+
return body.value;
|
|
244
|
+
}
|
|
245
|
+
async deleteExtensionPreview(extensionId, previewRef, expectedRevision, signal) {
|
|
246
|
+
if (extensionId.trim() === "" || !/^preview_v1_[a-f0-9]{64}$/.test(previewRef.trim()) || !Number.isSafeInteger(expectedRevision) || expectedRevision < 0) throw new TypeError("Arkme extension preview delete parameters are invalid");
|
|
247
|
+
return await this.call("extensions.preview.delete", {
|
|
248
|
+
extensionId: extensionId.trim(),
|
|
249
|
+
previewRef: previewRef.trim(),
|
|
250
|
+
expectedRevision
|
|
251
|
+
}, signal);
|
|
252
|
+
}
|
|
253
|
+
async reorderExtensionPreviews(extensionId, orderedPreviewRefs, expectedRevision, signal) {
|
|
254
|
+
const refs = orderedPreviewRefs.map((value) => value.trim());
|
|
255
|
+
if (extensionId.trim() === "" || refs.length <= 0 || refs.length > 20 || refs.some((value) => !/^preview_v1_[a-f0-9]{64}$/.test(value)) || new Set(refs).size !== refs.length || !Number.isSafeInteger(expectedRevision) || expectedRevision < 0) throw new TypeError("Arkme extension preview reorder parameters are invalid");
|
|
256
|
+
return await this.call("extensions.preview.reorder", {
|
|
257
|
+
extensionId: extensionId.trim(),
|
|
258
|
+
orderedPreviewRefs: refs,
|
|
259
|
+
expectedRevision
|
|
260
|
+
}, signal);
|
|
261
|
+
}
|
|
262
|
+
/** Run a user-triggered AI audit for one marketplace extension without installing it. */
|
|
263
|
+
async auditExtension(extensionId, signal) {
|
|
264
|
+
if (extensionId.trim() === "") throw new TypeError("Arkme extension ID must not be empty");
|
|
265
|
+
return await this.call("extensions.audit.check", { extensionId: extensionId.trim() }, signal);
|
|
266
|
+
}
|
|
267
|
+
async authStatus(signal) {
|
|
268
|
+
return await this.call("auth.status", void 0, signal);
|
|
269
|
+
}
|
|
270
|
+
/** Read recent call history through Browser-safe opaque call references. */
|
|
271
|
+
async callHistory(options = {}, signal) {
|
|
272
|
+
const limit = options.limit === void 0 ? void 0 : Math.trunc(options.limit);
|
|
273
|
+
if (limit !== void 0 && (!Number.isSafeInteger(limit) || limit <= 0 || limit > 50)) throw new TypeError("Arkme call history limit must be 1-50");
|
|
274
|
+
const cursor = options.cursor?.trim() ?? "";
|
|
275
|
+
return await this.call("calls.history.list", {
|
|
276
|
+
...limit === void 0 ? {} : { limit },
|
|
277
|
+
...cursor === "" ? {} : { cursor },
|
|
278
|
+
...options.includeRecentContacts === void 0 ? {} : { includeRecentContacts: options.includeRecentContacts }
|
|
279
|
+
}, signal);
|
|
280
|
+
}
|
|
281
|
+
/** Read one call detail using an unchanged callRef returned by callHistory(). */
|
|
282
|
+
async callDetail(callRef, signal) {
|
|
283
|
+
const normalized = callRef.trim();
|
|
284
|
+
if (normalized === "") throw new TypeError("Arkme call reference must not be empty");
|
|
285
|
+
return safeSdkCallDetail(await this.call("calls.history.detail", { callRef: normalized }, signal));
|
|
286
|
+
}
|
|
287
|
+
/** Retry call summary generation only from an explicit current human action. */
|
|
288
|
+
async retryCallSummary(callRef, signal) {
|
|
289
|
+
const normalized = callRef.trim();
|
|
290
|
+
if (normalized === "") throw new TypeError("Arkme call reference must not be empty");
|
|
291
|
+
return await this.call("calls.history.summary.retry", { callRef: normalized }, signal);
|
|
292
|
+
}
|
|
293
|
+
async profile(options = {}) {
|
|
294
|
+
return await this.call(options.refresh === true ? "user.profile.refresh" : "user.profile", void 0, options.signal);
|
|
295
|
+
}
|
|
296
|
+
/** Employee-only current-state lookup for the user bound to a private-chat source. */
|
|
297
|
+
async userBanStatus(sourceRef, signal) {
|
|
298
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme private-chat source reference must not be empty");
|
|
299
|
+
return await this.call("user-ban.status", { sourceRef }, signal);
|
|
300
|
+
}
|
|
301
|
+
async directMessageAdmission(sourceRef, signal) {
|
|
302
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme private-chat source reference must not be empty");
|
|
303
|
+
if ((await this.capabilities(signal)).features.directMessageAdmission !== true) throw new ArkmeClientError({
|
|
304
|
+
code: "CAPABILITY_UNSUPPORTED",
|
|
305
|
+
message: "当前 Arkme Provider 不支持私聊拒收,请升级",
|
|
306
|
+
retryable: false
|
|
307
|
+
});
|
|
308
|
+
return await this.call("chat.direct-message-admission", { sourceRef }, signal);
|
|
309
|
+
}
|
|
310
|
+
async setDirectMessageRefusal(sourceRef, refused, expectedRevision, signal) {
|
|
311
|
+
if (sourceRef.trim() === "" || typeof refused !== "boolean" || !Number.isSafeInteger(expectedRevision) || expectedRevision < 0) throw new TypeError("Arkme private-chat refusal input is invalid");
|
|
312
|
+
if ((await this.capabilities(signal)).features.directMessageAdmission !== true) throw new ArkmeClientError({
|
|
313
|
+
code: "CAPABILITY_UNSUPPORTED",
|
|
314
|
+
message: "当前 Arkme Provider 不支持私聊拒收,请升级",
|
|
315
|
+
retryable: false
|
|
316
|
+
});
|
|
317
|
+
return await this.call("chat.direct-message-refusal.set", {
|
|
318
|
+
sourceRef,
|
|
319
|
+
refused,
|
|
320
|
+
expectedRevision
|
|
321
|
+
}, signal);
|
|
322
|
+
}
|
|
323
|
+
/** Employee-only idempotent ban. Callers must obtain explicit human approval first. */
|
|
324
|
+
async banPrivateChatUser(sourceRef, remark = "", signal) {
|
|
325
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme private-chat source reference must not be empty");
|
|
326
|
+
if (Array.from(remark.trim()).length > 255) throw new TypeError("Arkme user-ban remark must be at most 255 characters");
|
|
327
|
+
return await this.call("user-ban.ban", {
|
|
328
|
+
sourceRef,
|
|
329
|
+
remark: remark.trim()
|
|
330
|
+
}, signal);
|
|
331
|
+
}
|
|
332
|
+
/** Employee-only idempotent unban. Callers must obtain explicit human approval first. */
|
|
333
|
+
async unbanPrivateChatUser(sourceRef, remark = "", signal) {
|
|
334
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme private-chat source reference must not be empty");
|
|
335
|
+
if (Array.from(remark.trim()).length > 255) throw new TypeError("Arkme user-ban remark must be at most 255 characters");
|
|
336
|
+
return await this.call("user-ban.unban", {
|
|
337
|
+
sourceRef,
|
|
338
|
+
remark: remark.trim()
|
|
339
|
+
}, signal);
|
|
340
|
+
}
|
|
341
|
+
async backgroundSoundPreference(signal) {
|
|
342
|
+
return await this.call("settings.background-sound.get", {}, signal);
|
|
343
|
+
}
|
|
344
|
+
async updateBackgroundSoundPreference(enabled, signal, expectedUserId) {
|
|
345
|
+
if (typeof enabled !== "boolean") throw new TypeError("Arkme background-sound preference must be boolean");
|
|
346
|
+
return await this.call("settings.background-sound.update", {
|
|
347
|
+
enabled,
|
|
348
|
+
...expectedUserIdParam(expectedUserId)
|
|
349
|
+
}, signal);
|
|
350
|
+
}
|
|
351
|
+
/** Validate a candidate Arkme ID with the same one-time account rule used by the built-in UI. */
|
|
352
|
+
async checkArkmeIdAvailability(arkmeId, signal) {
|
|
353
|
+
const normalized = arkmeId.trim();
|
|
354
|
+
if (normalized === "") throw new TypeError("Arkme ID must not be empty");
|
|
355
|
+
return await this.call("user.arkme-id.check", { arkmeId: normalized }, signal);
|
|
356
|
+
}
|
|
357
|
+
/** Set the current account Arkme ID once after explicit user confirmation. */
|
|
358
|
+
async setArkmeIdOnce(arkmeId, signal) {
|
|
359
|
+
const normalized = arkmeId.trim();
|
|
360
|
+
if (normalized === "") throw new TypeError("Arkme ID must not be empty");
|
|
361
|
+
return await this.call("user.arkme-id.set", { arkmeId: normalized }, signal);
|
|
362
|
+
}
|
|
363
|
+
/** Send a phone verification code for login, first bind, or phone rebind depending on auth state. */
|
|
364
|
+
async sendPhoneCode(phone, captcha, signal) {
|
|
365
|
+
const normalized = phone.replace(/[\s-]/g, "");
|
|
366
|
+
if (!/^1[3-9][0-9]{9}$/.test(normalized)) throw new TypeError("Phone number is invalid");
|
|
367
|
+
return await this.call("auth.phone.send", {
|
|
368
|
+
phone: normalized,
|
|
369
|
+
captcha
|
|
370
|
+
}, signal);
|
|
371
|
+
}
|
|
372
|
+
/** Verify a phone code and refresh the account auth/profile state. */
|
|
373
|
+
async verifyPhoneCode(phone, code, signal) {
|
|
374
|
+
const normalized = phone.replace(/[\s-]/g, "");
|
|
375
|
+
const normalizedCode = code.trim();
|
|
376
|
+
if (!/^1[3-9][0-9]{9}$/.test(normalized)) throw new TypeError("Phone number is invalid");
|
|
377
|
+
if (!/^[0-9]{6}$/.test(normalizedCode)) throw new TypeError("Phone verification code is invalid");
|
|
378
|
+
return await this.call("auth.phone.verify", {
|
|
379
|
+
phone: normalized,
|
|
380
|
+
code: normalizedCode
|
|
381
|
+
}, signal);
|
|
382
|
+
}
|
|
383
|
+
/** Search by an exact phone number or Arkme ID without exposing internal account identifiers. */
|
|
384
|
+
async searchContact(identifier, signal) {
|
|
385
|
+
const value = identifier.trim();
|
|
386
|
+
if (value === "" || value.length > 64) throw new TypeError("Arkme contact identifier is invalid");
|
|
387
|
+
return await this.call("contacts.search", { identifier: value }, signal);
|
|
388
|
+
}
|
|
389
|
+
/** Add the exact candidate returned by searchContact and open its idempotent private/pending chat. */
|
|
390
|
+
async addContact(contactRef, options = {}) {
|
|
391
|
+
const ref = contactRef.trim();
|
|
392
|
+
if (!/^arkme-contact-v1\.[0-9a-f-]{36}$/i.test(ref)) throw new TypeError("Arkme contact reference is invalid");
|
|
393
|
+
const remark = options.remark?.trim() ?? "";
|
|
394
|
+
if (Array.from(remark).length > 100) throw new TypeError("Arkme contact remark is too long");
|
|
395
|
+
const requestUid = options.requestUid ?? crypto.randomUUID();
|
|
396
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(requestUid)) throw new TypeError("Arkme contact request id must be a UUID");
|
|
397
|
+
return await this.call("contacts.add", {
|
|
398
|
+
contactRef: ref,
|
|
399
|
+
...remark === "" ? {} : { remark },
|
|
400
|
+
requestUid
|
|
401
|
+
}, options.signal);
|
|
402
|
+
}
|
|
403
|
+
/** Open a private chat for a registered candidate returned by searchContact without adding it as a contact. */
|
|
404
|
+
async openPrivateChatFromContact(contactRef, signal) {
|
|
405
|
+
const ref = contactRef.trim();
|
|
406
|
+
if (!/^arkme-contact-v1\.[0-9a-f-]{36}$/i.test(ref)) throw new TypeError("Arkme contact reference is invalid");
|
|
407
|
+
return await this.call("chat.private.open-from-contact", { contactRef: ref }, signal);
|
|
408
|
+
}
|
|
409
|
+
/** Create an initially owner-only group chat with an idempotent client mutation id. */
|
|
410
|
+
async createGroup(title, options = {}) {
|
|
411
|
+
const normalizedTitle = title.trim();
|
|
412
|
+
if (normalizedTitle === "" || Array.from(normalizedTitle).length > 80) throw new TypeError("Arkme group title is invalid");
|
|
413
|
+
const clientMutationId = options.clientMutationId ?? crypto.randomUUID();
|
|
414
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(clientMutationId)) throw new TypeError("Arkme group mutation id must be a UUID");
|
|
415
|
+
return await this.call("group.create", {
|
|
416
|
+
title: normalizedTitle,
|
|
417
|
+
clientMutationId
|
|
418
|
+
}, options.signal);
|
|
419
|
+
}
|
|
420
|
+
async listBots(signal) {
|
|
421
|
+
return await this.call("bots.list", void 0, signal);
|
|
422
|
+
}
|
|
423
|
+
/** Create a Bot without exposing the Host-owned one-time credential to the Consumer. */
|
|
424
|
+
async createBot(input, signal) {
|
|
425
|
+
const name = input.name.trim();
|
|
426
|
+
if (name === "") throw new TypeError("Arkme Bot name must not be empty");
|
|
427
|
+
if (input.provider !== "openclaw" && input.provider !== "webhook") throw new TypeError("Arkme Bot provider is unsupported");
|
|
428
|
+
const description = input.description?.trim() ?? "";
|
|
429
|
+
const avatar = input.avatar?.trim() ?? "";
|
|
430
|
+
if (avatar !== "" && !isArkmeBotAvatarRef(avatar)) throw new TypeError("Arkme Bot avatar must be a file_asset reference");
|
|
431
|
+
return await this.call("bots.create", {
|
|
432
|
+
name,
|
|
433
|
+
provider: input.provider,
|
|
434
|
+
...description === "" ? {} : { description },
|
|
435
|
+
...avatar === "" ? {} : { avatar }
|
|
436
|
+
}, signal);
|
|
437
|
+
}
|
|
438
|
+
/** Browse public marketplace extensions with server-owned query, ordering and cursor semantics. */
|
|
439
|
+
async extensionCatalog(options = {}) {
|
|
440
|
+
if (options.ownerUserId !== void 0 && (!Number.isSafeInteger(options.ownerUserId) || options.ownerUserId <= 0)) throw new TypeError("Arkme extension owner user id must be a positive safe integer");
|
|
441
|
+
return await this.call("extensions.catalog.list", {
|
|
442
|
+
...options.query === void 0 || options.query.trim() === "" ? {} : { query: options.query.trim() },
|
|
443
|
+
...options.sort === void 0 ? {} : { sort: options.sort },
|
|
444
|
+
...options.cursor === void 0 || options.cursor.trim() === "" ? {} : { cursor: options.cursor.trim() },
|
|
445
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
446
|
+
...options.ownerUserId === void 0 ? {} : { ownerUserId: options.ownerUserId },
|
|
447
|
+
...options.excludeExtensionId === void 0 || options.excludeExtensionId.trim() === "" ? {} : { excludeExtensionId: options.excludeExtensionId.trim() }
|
|
448
|
+
}, options.signal);
|
|
449
|
+
}
|
|
450
|
+
/** Read the AI-generated marketplace category tree. */
|
|
451
|
+
async extensionCategories(options = {}) {
|
|
452
|
+
return await this.call("extensions.classification.tree", { ...options.limit === void 0 ? {} : { limit: options.limit } }, options.signal);
|
|
453
|
+
}
|
|
454
|
+
/** Browse one AI category while retaining the same server-side ordering contract. */
|
|
455
|
+
async extensionCategoryItems(categoryId, options = {}) {
|
|
456
|
+
if (categoryId.trim() === "") throw new TypeError("Arkme extension category id must not be empty");
|
|
457
|
+
return await this.call("extensions.classification.items", {
|
|
458
|
+
categoryId: categoryId.trim(),
|
|
459
|
+
...options.query === void 0 || options.query.trim() === "" ? {} : { query: options.query.trim() },
|
|
460
|
+
...options.sort === void 0 ? {} : { sort: options.sort },
|
|
461
|
+
...options.cursor === void 0 || options.cursor.trim() === "" ? {} : { cursor: options.cursor.trim() },
|
|
462
|
+
...options.limit === void 0 ? {} : { limit: options.limit }
|
|
463
|
+
}, options.signal);
|
|
464
|
+
}
|
|
465
|
+
/** List current-account sources with their Host-derived dynamic-cordis-v2 or profile-native-v3 publication route. */
|
|
466
|
+
async myExtensions(options = {}) {
|
|
467
|
+
return await this.call("extensions.mine.list", { ...options.currentSessionId === void 0 || options.currentSessionId.trim() === "" ? {} : { currentSessionId: options.currentSessionId.trim() } }, options.signal);
|
|
468
|
+
}
|
|
469
|
+
/** Publish one exact owned source through its Host-derived V2 or V3 route after explicit current-user intent. */
|
|
470
|
+
publishMyExtension(input, signal) {
|
|
471
|
+
if (input.ownedRef.trim() === "") throw new TypeError("Arkme extension reference must not be empty");
|
|
472
|
+
if (input.name.trim() === "" || input.name.trim().length > 120) throw new TypeError("Arkme extension name is invalid");
|
|
473
|
+
if (input.description.length > 2e3) throw new TypeError("Arkme extension description is too long");
|
|
474
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(input.version.trim())) throw new TypeError("Arkme extension version must be SemVer");
|
|
475
|
+
if (![
|
|
476
|
+
"private",
|
|
477
|
+
"unlisted",
|
|
478
|
+
"public"
|
|
479
|
+
].includes(input.visibility)) throw new TypeError("Arkme extension visibility is invalid");
|
|
480
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(input.clientMutationId)) throw new TypeError("Arkme extension client mutation id must be a UUID");
|
|
481
|
+
let githubRepositoryUrl;
|
|
482
|
+
try {
|
|
483
|
+
githubRepositoryUrl = normalizeGitHubRepositoryURL(input.githubRepositoryUrl);
|
|
484
|
+
} catch {
|
|
485
|
+
throw new TypeError("Arkme extension GitHub repository URL is invalid");
|
|
486
|
+
}
|
|
487
|
+
return this.call("extensions.mine.publish", {
|
|
488
|
+
ownedRef: input.ownedRef,
|
|
489
|
+
name: input.name,
|
|
490
|
+
description: input.description,
|
|
491
|
+
version: input.version,
|
|
492
|
+
visibility: input.visibility,
|
|
493
|
+
...input.changelog === void 0 || input.changelog.trim() === "" ? {} : { changelog: input.changelog },
|
|
494
|
+
...githubRepositoryUrl === void 0 ? {} : { githubRepositoryUrl },
|
|
495
|
+
clientMutationId: input.clientMutationId
|
|
496
|
+
}, signal);
|
|
497
|
+
}
|
|
498
|
+
async updateExtensionMetadata(extensionId, input, signal) {
|
|
499
|
+
const name = input.name.trim();
|
|
500
|
+
const description = input.description.trim();
|
|
501
|
+
if (extensionId.trim() === "" || name === "" || [...name].length > 120 || [...description].length > 2e3 || input.visibility !== "private" && input.visibility !== "public") throw new TypeError("Arkme extension metadata or visibility is invalid");
|
|
502
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(input.clientMutationId)) throw new TypeError("Arkme extension metadata mutation id must be a UUID");
|
|
503
|
+
return await this.call("extensions.metadata.update", {
|
|
504
|
+
extensionId: extensionId.trim(),
|
|
505
|
+
name,
|
|
506
|
+
description,
|
|
507
|
+
visibility: input.visibility,
|
|
508
|
+
clientMutationId: input.clientMutationId
|
|
509
|
+
}, signal);
|
|
510
|
+
}
|
|
511
|
+
async rotateExtensionShare(extensionId, clientMutationId, signal) {
|
|
512
|
+
if (extensionId.trim() === "" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(clientMutationId)) throw new TypeError("Arkme extension share rotation input is invalid");
|
|
513
|
+
return await this.call("extensions.share.rotate", {
|
|
514
|
+
extensionId: extensionId.trim(),
|
|
515
|
+
clientMutationId
|
|
516
|
+
}, signal);
|
|
517
|
+
}
|
|
518
|
+
async extensionShareDetail(shareRef, signal) {
|
|
519
|
+
const normalized = shareRef.trim();
|
|
520
|
+
if (!/^extshare_[0-9a-f]{32}$/.test(normalized)) throw new TypeError("Arkme extension share reference is invalid");
|
|
521
|
+
return await this.call("extensions.share.detail", { shareRef: normalized }, signal);
|
|
522
|
+
}
|
|
523
|
+
async extensionShareCatalogDetail(shareRef, signal) {
|
|
524
|
+
const normalized = shareRef.trim();
|
|
525
|
+
if (!/^extshare_[0-9a-f]{32}$/.test(normalized)) throw new TypeError("Arkme extension share reference is invalid");
|
|
526
|
+
return await this.call("extensions.share.resolve", { shareRef: normalized }, signal);
|
|
527
|
+
}
|
|
528
|
+
/** Read one current-user Arkme image through the authenticated Provider without exposing a signed OSS URL. */
|
|
529
|
+
async readImage(imageRef, signal) {
|
|
530
|
+
if (imageRef.trim() === "") throw new TypeError("Arkme image reference must not be empty");
|
|
531
|
+
return await this.call("image.read", { imageRef }, signal);
|
|
532
|
+
}
|
|
533
|
+
/** Convert a Provider image payload into a browser-renderable data URL. */
|
|
534
|
+
imageDataUrl(image) {
|
|
535
|
+
return `data:${image.mediaType};base64,${image.dataBase64}`;
|
|
536
|
+
}
|
|
537
|
+
/** Read the public World feed through the authenticated Provider boundary. */
|
|
538
|
+
async worldFeed(options = {}) {
|
|
539
|
+
return await this.call("world.feed", {
|
|
540
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
541
|
+
...options.offset === void 0 ? {} : { offset: options.offset }
|
|
542
|
+
}, options.signal);
|
|
543
|
+
}
|
|
544
|
+
/** Read the current account's public World posts through the authenticated Provider boundary. */
|
|
545
|
+
async myWorldFeed(options = {}) {
|
|
546
|
+
return await this.call("world.mine", {
|
|
547
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
548
|
+
...options.offset === void 0 ? {} : { offset: options.offset }
|
|
549
|
+
}, options.signal);
|
|
550
|
+
}
|
|
551
|
+
/** Read one user's public World homepage through the authenticated Provider boundary. */
|
|
552
|
+
async userWorldFeed(userId, options = {}) {
|
|
553
|
+
if (!Number.isSafeInteger(userId) || userId <= 0) throw new TypeError("Arkme World userId must be a positive integer");
|
|
554
|
+
return await this.call("world.user", {
|
|
555
|
+
userId,
|
|
556
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
557
|
+
...options.offset === void 0 ? {} : { offset: options.offset }
|
|
558
|
+
}, options.signal);
|
|
559
|
+
}
|
|
560
|
+
/** Open or reuse a private chat with a non-self World author from an unchanged authorRef. */
|
|
561
|
+
async openWorldAuthorPrivateChat(authorRef, signal) {
|
|
562
|
+
if (authorRef.trim() === "") throw new TypeError("Arkme World author reference must not be empty");
|
|
563
|
+
return await this.call("chat.world.private.open", { authorRef }, signal);
|
|
564
|
+
}
|
|
565
|
+
/** Open or reuse the same private chat used by the mobile "联系作者" entry. */
|
|
566
|
+
async openOfficialAuthorPrivateChat(signal) {
|
|
567
|
+
return await this.call("chat.official-author.private.open", {}, signal);
|
|
568
|
+
}
|
|
569
|
+
/** Read the browser-safe public profile for the mobile "联系作者" entry without creating a chat. */
|
|
570
|
+
async officialAuthorProfile(signal) {
|
|
571
|
+
return await this.call("chat.official-author.profile", {}, signal);
|
|
572
|
+
}
|
|
573
|
+
/** Resolve viewer-local labels for visible World authors without exposing their user IDs. */
|
|
574
|
+
async worldAuthorLabels(authorRefs, signal) {
|
|
575
|
+
const normalized = [...new Set(authorRefs.map((value) => value.trim()).filter((value) => value !== ""))];
|
|
576
|
+
if (normalized.length === 0) return [];
|
|
577
|
+
return await this.call("world.author-labels", { authorRefs: normalized.slice(0, 20) }, signal);
|
|
578
|
+
}
|
|
579
|
+
async worldVoiceprintPlaybackAvailability(recordRefs, signal) {
|
|
580
|
+
const normalized = [...new Set(recordRefs.map((value) => value.trim()).filter((value) => value !== ""))];
|
|
581
|
+
if (normalized.length === 0) return { items: [] };
|
|
582
|
+
return await this.call("world.voiceprint.availability", { recordRefs: normalized.slice(0, 20) }, signal);
|
|
583
|
+
}
|
|
584
|
+
async generateWorldVoiceprintPlayback(input, signal) {
|
|
585
|
+
if (input.recordRef.trim() === "") throw new TypeError("Arkme World record reference must not be empty");
|
|
586
|
+
return await this.call("world.voiceprint.playback.generate", {
|
|
587
|
+
recordRef: input.recordRef,
|
|
588
|
+
...input.chunkIndex === void 0 ? {} : { chunkIndex: input.chunkIndex }
|
|
589
|
+
}, signal);
|
|
590
|
+
}
|
|
591
|
+
async inviteWorldVoiceprint(recordRef, signal) {
|
|
592
|
+
if (recordRef.trim() === "") throw new TypeError("Arkme World record reference must not be empty");
|
|
593
|
+
return await this.call("world.voiceprint.invite", { recordRef }, signal);
|
|
594
|
+
}
|
|
595
|
+
async worldVoiceprintSocialContext(recordRef, options = {}) {
|
|
596
|
+
if (recordRef.trim() === "") throw new TypeError("Arkme World record reference must not be empty");
|
|
597
|
+
return await this.call("world.voiceprint.social-context", {
|
|
598
|
+
recordRef,
|
|
599
|
+
...options.forceRefresh === void 0 ? {} : { forceRefresh: options.forceRefresh }
|
|
600
|
+
}, options.signal);
|
|
601
|
+
}
|
|
602
|
+
/** Read comments and replies for one Provider-issued World record reference. */
|
|
603
|
+
async worldInteractions(recordRef, options = {}) {
|
|
604
|
+
if (recordRef.trim() === "") throw new TypeError("Arkme World record reference must not be empty");
|
|
605
|
+
return await this.call("world.interactions.list", {
|
|
606
|
+
recordRef,
|
|
607
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
608
|
+
...options.offset === void 0 ? {} : { offset: options.offset }
|
|
609
|
+
}, options.signal);
|
|
610
|
+
}
|
|
611
|
+
/** Publish a text comment or reply using a caller-stable mutation id. */
|
|
612
|
+
async createWorldTextInteraction(input, signal) {
|
|
613
|
+
if (input.targetRef.trim() === "" || input.textContent.trim() === "" || input.clientMutationId.trim() === "") throw new TypeError("Arkme World interaction target, text, and mutation id must not be empty");
|
|
614
|
+
return await this.call("world.interactions.create-text", {
|
|
615
|
+
targetRef: input.targetRef,
|
|
616
|
+
textContent: input.textContent,
|
|
617
|
+
clientMutationId: input.clientMutationId
|
|
618
|
+
}, signal);
|
|
619
|
+
}
|
|
620
|
+
async publishWorldText(input, signal) {
|
|
621
|
+
if (input.clientMutationId.trim() === "" || input.textContent.trim() === "") throw new TypeError("Arkme World publish text and mutation id must not be empty");
|
|
622
|
+
return await this.call("world.publish-text", {
|
|
623
|
+
clientMutationId: input.clientMutationId,
|
|
624
|
+
textContent: input.textContent
|
|
625
|
+
}, signal);
|
|
626
|
+
}
|
|
627
|
+
async publishWorldFileAssets(input, signal) {
|
|
628
|
+
if (input.clientMutationId.trim() === "" || input.textContent.trim() === "" || input.fileAssets.length === 0) throw new TypeError("Arkme World publish text, mutation id, and file assets must not be empty");
|
|
629
|
+
return await this.call("world.publish-file-assets", {
|
|
630
|
+
clientMutationId: input.clientMutationId,
|
|
631
|
+
textContent: input.textContent,
|
|
632
|
+
fileAssets: input.fileAssets
|
|
633
|
+
}, signal);
|
|
634
|
+
}
|
|
635
|
+
/** Resolve one Provider-issued World image ref without exposing its signed source URL. */
|
|
636
|
+
async readWorldImage(imageRef, signal) {
|
|
637
|
+
if (imageRef.trim() === "") throw new TypeError("Arkme World image reference must not be empty");
|
|
638
|
+
return await this.call("world.image.read", { imageRef }, signal);
|
|
639
|
+
}
|
|
640
|
+
/** Read the current account's Arrangement owner projection. */
|
|
641
|
+
async arrangements(options = {}) {
|
|
642
|
+
return await this.call("arrangements.list", {
|
|
643
|
+
...options.status === void 0 ? {} : { status: options.status },
|
|
644
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
645
|
+
...options.offset === void 0 ? {} : { offset: options.offset }
|
|
646
|
+
}, options.signal);
|
|
647
|
+
}
|
|
648
|
+
/** Read public reviews and rating summary for one extension. */
|
|
649
|
+
async extensionReviews(extensionId, options = {}) {
|
|
650
|
+
if (extensionId.trim() === "") throw new TypeError("Arkme extension id must not be empty");
|
|
651
|
+
return await this.call("extensions.reviews.list", {
|
|
652
|
+
extensionId,
|
|
653
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
654
|
+
...options.offset === void 0 ? {} : { offset: options.offset }
|
|
655
|
+
}, options.signal);
|
|
656
|
+
}
|
|
657
|
+
/** Read one Arrangement through a Provider-issued, account-bound reference. */
|
|
658
|
+
async arrangementDetail(arrangementRef, signal) {
|
|
659
|
+
if (arrangementRef.trim() === "") throw new TypeError("Arrangement reference must not be empty");
|
|
660
|
+
return await this.call("arrangements.detail", { arrangementRef }, signal);
|
|
661
|
+
}
|
|
662
|
+
async arrangementReminders(options = {}) {
|
|
663
|
+
return await this.call("arrangements.reminders.list", {
|
|
664
|
+
...options.unreadOnly === void 0 ? {} : { unreadOnly: options.unreadOnly },
|
|
665
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
666
|
+
...options.offset === void 0 ? {} : { offset: options.offset }
|
|
667
|
+
}, options.signal);
|
|
668
|
+
}
|
|
669
|
+
async arrangementReminderSummary(signal) {
|
|
670
|
+
return await this.call("arrangements.reminders.summary", void 0, signal);
|
|
671
|
+
}
|
|
672
|
+
async mutateArrangement(arrangementRef, intent, signal) {
|
|
673
|
+
const normalizedRef = arrangementRef.trim();
|
|
674
|
+
if (normalizedRef === "") throw new TypeError("Arrangement reference must not be empty");
|
|
675
|
+
if (![
|
|
676
|
+
"start-follow",
|
|
677
|
+
"cancel-follow",
|
|
678
|
+
"complete",
|
|
679
|
+
"cancel-complete",
|
|
680
|
+
"delete"
|
|
681
|
+
].includes(intent)) throw new TypeError("Arrangement mutation intent is invalid");
|
|
682
|
+
return await this.call("arrangements.mutate", {
|
|
683
|
+
arrangementRef: normalizedRef,
|
|
684
|
+
intent
|
|
685
|
+
}, signal);
|
|
686
|
+
}
|
|
687
|
+
async setArrangementReminderEnabled(arrangementRef, enabled, signal) {
|
|
688
|
+
const normalizedRef = arrangementRef.trim();
|
|
689
|
+
if (normalizedRef === "") throw new TypeError("Arrangement reference must not be empty");
|
|
690
|
+
return await this.call("arrangements.reminder-enabled", {
|
|
691
|
+
arrangementRef: normalizedRef,
|
|
692
|
+
enabled
|
|
693
|
+
}, signal);
|
|
694
|
+
}
|
|
695
|
+
async markArrangementRemindersRead(eventRefs, signal) {
|
|
696
|
+
const normalizedRefs = eventRefs.map((eventRef) => eventRef.trim());
|
|
697
|
+
if (normalizedRefs.length === 0 || normalizedRefs.some((eventRef) => eventRef === "")) throw new TypeError("Reminder references must not be empty");
|
|
698
|
+
if (new Set(normalizedRefs).size !== normalizedRefs.length) throw new TypeError("Reminder references must be unique");
|
|
699
|
+
if (normalizedRefs.length > 50) throw new TypeError("At most 50 reminder references are allowed");
|
|
700
|
+
return await this.call("arrangements.reminders.mark-read", { eventRefs: normalizedRefs }, signal);
|
|
701
|
+
}
|
|
702
|
+
async markAllArrangementRemindersRead(signal) {
|
|
703
|
+
return await this.call("arrangements.reminders.mark-all-read", void 0, signal);
|
|
704
|
+
}
|
|
705
|
+
async clearArrangementReminders(signal) {
|
|
706
|
+
return await this.call("arrangements.reminders.clear", void 0, signal);
|
|
707
|
+
}
|
|
708
|
+
/** Create a top-level review or reply through the account-bound Host owner. */
|
|
709
|
+
async createExtensionReview(input, signal) {
|
|
710
|
+
if (input.extensionId.trim() === "" || input.textContent.trim() === "" || input.clientMutationId.trim() === "") throw new TypeError("Arkme extension review id, text, and mutation id must not be empty");
|
|
711
|
+
return await this.call("extensions.reviews.create", {
|
|
712
|
+
extensionId: input.extensionId,
|
|
713
|
+
textContent: input.textContent,
|
|
714
|
+
...input.rating === void 0 ? {} : { rating: input.rating },
|
|
715
|
+
...input.parentReviewRef === void 0 ? {} : { parentReviewRef: input.parentReviewRef },
|
|
716
|
+
clientMutationId: input.clientMutationId
|
|
717
|
+
}, signal);
|
|
718
|
+
}
|
|
719
|
+
async listSources(directory, options = {}) {
|
|
720
|
+
return await this.call("sources.list", {
|
|
721
|
+
directory,
|
|
722
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
723
|
+
...options.cursor === void 0 ? {} : { cursor: options.cursor }
|
|
724
|
+
}, options.signal);
|
|
725
|
+
}
|
|
726
|
+
async listGroupMembers(sourceRef, signal) {
|
|
727
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme group source reference must not be empty");
|
|
728
|
+
return await this.call("group.members", {
|
|
729
|
+
sourceRef,
|
|
730
|
+
activeOnly: true
|
|
731
|
+
}, signal);
|
|
732
|
+
}
|
|
733
|
+
async groupAiPolishSettings(sourceRef, signal) {
|
|
734
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme group source reference must not be empty");
|
|
735
|
+
return await this.call("source.ai-polish.settings", { sourceRef }, signal);
|
|
736
|
+
}
|
|
737
|
+
async generateGroupAiPolishRule(sourceRef, requirement, options = {}) {
|
|
738
|
+
if (sourceRef.trim() === "" || requirement.trim() === "") throw new TypeError("Arkme group source reference and polish requirement must not be empty");
|
|
739
|
+
return await this.call("source.ai-polish.generate-rule", {
|
|
740
|
+
sourceRef,
|
|
741
|
+
requirement,
|
|
742
|
+
...options.threadMessages === void 0 ? {} : { threadMessages: options.threadMessages },
|
|
743
|
+
...options.targetRuleRef === void 0 || options.targetRuleRef.trim() === "" ? {} : { targetRuleRef: options.targetRuleRef.trim() }
|
|
744
|
+
}, options.signal);
|
|
745
|
+
}
|
|
746
|
+
async prepareEnableGroupAiPolishRule(sourceRef, ruleRef, signal) {
|
|
747
|
+
if (sourceRef.trim() === "" || ruleRef.trim() === "") throw new TypeError("Arkme group source and AI polish rule references must not be empty");
|
|
748
|
+
return await this.call("source.ai-polish.prepare-enable", {
|
|
749
|
+
sourceRef,
|
|
750
|
+
ruleRef
|
|
751
|
+
}, signal);
|
|
752
|
+
}
|
|
753
|
+
async confirmEnableGroupAiPolish(confirmationRef, signal) {
|
|
754
|
+
if (confirmationRef.trim() === "") throw new TypeError("Arkme AI polish confirmation reference must not be empty");
|
|
755
|
+
return await this.call("source.ai-polish.confirm-enable", { confirmationRef }, signal);
|
|
756
|
+
}
|
|
757
|
+
async prepareDisableGroupAiPolish(sourceRef, signal) {
|
|
758
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme group source reference must not be empty");
|
|
759
|
+
return await this.call("source.ai-polish.prepare-disable", { sourceRef }, signal);
|
|
760
|
+
}
|
|
761
|
+
async confirmDisableGroupAiPolish(confirmationRef, signal) {
|
|
762
|
+
if (confirmationRef.trim() === "") throw new TypeError("Arkme AI polish confirmation reference must not be empty");
|
|
763
|
+
return await this.call("source.ai-polish.confirm-disable", { confirmationRef }, signal);
|
|
764
|
+
}
|
|
765
|
+
async listSourceMembers(sourceRef, signal) {
|
|
766
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme chat source reference must not be empty");
|
|
767
|
+
return await this.call("source.members", {
|
|
768
|
+
sourceRef,
|
|
769
|
+
activeOnly: true
|
|
770
|
+
}, signal);
|
|
771
|
+
}
|
|
772
|
+
async sourceMemberRecords(sourceRef, memberRef, mode, options = {}) {
|
|
773
|
+
if (sourceRef.trim() === "" || memberRef.trim() === "" || !["owner", "mentioned"].includes(mode)) throw new TypeError("Arkme source, member reference, and member-record mode must be valid");
|
|
774
|
+
return await this.call("source.member-records", {
|
|
775
|
+
sourceRef,
|
|
776
|
+
memberRef,
|
|
777
|
+
mode,
|
|
778
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
779
|
+
...options.beforeSequence === void 0 ? {} : { beforeSequence: options.beforeSequence }
|
|
780
|
+
}, options.signal);
|
|
781
|
+
}
|
|
782
|
+
async listGroupMemberCandidates(sourceRef, options = {}) {
|
|
783
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme group source reference must not be empty");
|
|
784
|
+
return await this.call("group.member-candidates", {
|
|
785
|
+
sourceRef,
|
|
786
|
+
...options.query === void 0 ? {} : { query: options.query },
|
|
787
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
788
|
+
...options.groupSourceRefs === void 0 ? {} : { groupSourceRefs: options.groupSourceRefs.map((value) => value.trim()).filter((value) => value !== "") }
|
|
789
|
+
}, options.signal);
|
|
790
|
+
}
|
|
791
|
+
async groupInvitePreview(sourceRef, signal) {
|
|
792
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme group source reference must not be empty");
|
|
793
|
+
return await this.call("group.invite-preview", { sourceRef }, signal);
|
|
794
|
+
}
|
|
795
|
+
async addGroupMembers(sourceRef, candidateRefs, signal) {
|
|
796
|
+
const refs = candidateRefs.map((value) => value.trim());
|
|
797
|
+
if (sourceRef.trim() === "" || refs.length === 0 || refs.some((value) => value === "")) throw new TypeError("Arkme group source and candidate references must not be empty");
|
|
798
|
+
return await this.call("group.members.add", {
|
|
799
|
+
sourceRef,
|
|
800
|
+
candidateRefs: refs
|
|
801
|
+
}, signal);
|
|
802
|
+
}
|
|
803
|
+
async removeGroupMember(sourceRef, memberRef, options = {}) {
|
|
804
|
+
if (sourceRef.trim() === "" || memberRef.trim() === "") throw new TypeError("Arkme group source and member references must not be empty");
|
|
805
|
+
return await this.call("group.member-remove", {
|
|
806
|
+
sourceRef: sourceRef.trim(),
|
|
807
|
+
memberRef: memberRef.trim(),
|
|
808
|
+
preventRejoin: options.preventRejoin === true
|
|
809
|
+
}, options.signal);
|
|
810
|
+
}
|
|
811
|
+
async listGroupJoinRestrictions(sourceRef, options = {}) {
|
|
812
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme group source reference must not be empty");
|
|
813
|
+
return await this.call("group.join-restrictions", {
|
|
814
|
+
sourceRef: sourceRef.trim(),
|
|
815
|
+
...options.cursor === void 0 ? {} : { cursor: options.cursor.trim() },
|
|
816
|
+
...options.limit === void 0 ? {} : { limit: options.limit }
|
|
817
|
+
}, options.signal);
|
|
818
|
+
}
|
|
819
|
+
async setGroupJoinRestriction(sourceRef, memberRef, restricted, signal) {
|
|
820
|
+
if (sourceRef.trim() === "" || memberRef.trim() === "" || typeof restricted !== "boolean") throw new TypeError("Arkme group source, member reference, and restriction are required");
|
|
821
|
+
return await this.call("group.join-restriction.set", {
|
|
822
|
+
sourceRef: sourceRef.trim(),
|
|
823
|
+
memberRef: memberRef.trim(),
|
|
824
|
+
restricted
|
|
825
|
+
}, signal);
|
|
826
|
+
}
|
|
827
|
+
async listGroupBots(sourceRef, signal) {
|
|
828
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme group source reference must not be empty");
|
|
829
|
+
return await this.call("group.bots", { sourceRef }, signal);
|
|
830
|
+
}
|
|
831
|
+
async addGroupBot(sourceRef, botRef, signal) {
|
|
832
|
+
if (sourceRef.trim() === "" || botRef.trim() === "") throw new TypeError("Arkme group source and Bot references must not be empty");
|
|
833
|
+
return await this.call("group.bot.add", {
|
|
834
|
+
sourceRef,
|
|
835
|
+
botRef
|
|
836
|
+
}, signal);
|
|
837
|
+
}
|
|
838
|
+
async readSource(sourceRef, options = {}) {
|
|
839
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme source reference must not be empty");
|
|
840
|
+
return await this.call("source.timeline", {
|
|
841
|
+
sourceRef,
|
|
842
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
843
|
+
...options.cursor === void 0 ? {} : { cursor: options.cursor }
|
|
844
|
+
}, options.signal);
|
|
845
|
+
}
|
|
846
|
+
async readSourceAround(sourceRef, itemUid, recordOwnerUserId, options = {}) {
|
|
847
|
+
if (sourceRef.trim() === "" || itemUid.trim() === "" || !Number.isSafeInteger(recordOwnerUserId) || recordOwnerUserId <= 0) throw new TypeError("Arkme timeline around requires a source, record uid, and record owner");
|
|
848
|
+
return await this.call("source.timeline-around", {
|
|
849
|
+
sourceRef,
|
|
850
|
+
itemUid,
|
|
851
|
+
recordOwnerUserId,
|
|
852
|
+
...options.beforeLimit === void 0 ? {} : { beforeLimit: options.beforeLimit },
|
|
853
|
+
...options.afterLimit === void 0 ? {} : { afterLimit: options.afterLimit }
|
|
854
|
+
}, options.signal);
|
|
855
|
+
}
|
|
856
|
+
async messageReadReceiptSummaries(sourceRef, items, signal) {
|
|
857
|
+
const normalizedSourceRef = sourceRef.trim();
|
|
858
|
+
if (normalizedSourceRef === "" || items.length < 1 || items.length > 50) throw new TypeError(`Arkme message read receipts require one source and 1-${String(50)} messages`);
|
|
859
|
+
const seen = /* @__PURE__ */ new Set();
|
|
860
|
+
const normalizedItems = items.map((item) => {
|
|
861
|
+
const itemUid = item.itemUid.trim();
|
|
862
|
+
const sequence = Math.trunc(item.sequence);
|
|
863
|
+
const key = `${itemUid}\u0000${String(sequence)}`;
|
|
864
|
+
if (itemUid === "" || !Number.isSafeInteger(item.sequence) || sequence <= 0 || seen.has(key)) throw new TypeError("Arkme message read receipt item identity is invalid or duplicated");
|
|
865
|
+
seen.add(key);
|
|
866
|
+
return {
|
|
867
|
+
itemUid,
|
|
868
|
+
sequence
|
|
869
|
+
};
|
|
870
|
+
});
|
|
871
|
+
return await this.call("source.read-receipts.summary-list", {
|
|
872
|
+
sourceRef: normalizedSourceRef,
|
|
873
|
+
items: normalizedItems
|
|
874
|
+
}, signal);
|
|
875
|
+
}
|
|
876
|
+
async messageReadReceiptDetail(sourceRef, itemUid, sequence, signal) {
|
|
877
|
+
const normalizedSourceRef = sourceRef.trim();
|
|
878
|
+
const normalizedItemUid = itemUid.trim();
|
|
879
|
+
if (normalizedSourceRef === "" || normalizedItemUid === "" || !Number.isSafeInteger(sequence) || sequence <= 0) throw new TypeError("Arkme group message read receipt identity is invalid");
|
|
880
|
+
return await this.call("source.read-receipts.detail", {
|
|
881
|
+
sourceRef: normalizedSourceRef,
|
|
882
|
+
itemUid: normalizedItemUid,
|
|
883
|
+
sequence
|
|
884
|
+
}, signal);
|
|
885
|
+
}
|
|
886
|
+
async sendText(sourceRef, textContent, options = {}) {
|
|
887
|
+
if (sourceRef.trim() === "" || textContent.trim() === "") throw new TypeError("Arkme source reference and text must not be empty");
|
|
888
|
+
return await this.call("source.send-text", {
|
|
889
|
+
sourceRef,
|
|
890
|
+
textContent,
|
|
891
|
+
recordUid: options.recordUid ?? crypto.randomUUID(),
|
|
892
|
+
relationUid: options.relationUid ?? crypto.randomUUID(),
|
|
893
|
+
...options.agentAuthored === true ? { agentAuthored: true } : {},
|
|
894
|
+
...options.humanMentions === void 0 ? {} : { humanMentions: options.humanMentions },
|
|
895
|
+
...options.botMentions === void 0 ? {} : { botMentions: options.botMentions },
|
|
896
|
+
...options.botRefs === void 0 ? {} : { botRefs: options.botRefs },
|
|
897
|
+
...expectedUserIdParam(options.expectedUserId)
|
|
898
|
+
}, options.signal);
|
|
899
|
+
}
|
|
900
|
+
async copyMessageLink(sourceRef, actionRefs, signal) {
|
|
901
|
+
const refs = actionRefs.map((value) => value.trim()).filter((value) => value !== "");
|
|
902
|
+
if (sourceRef.trim() === "" || refs.length === 0 || refs.length > 100 || new Set(refs).size !== refs.length) throw new TypeError("Arkme message link action references must be 1-100 unique values");
|
|
903
|
+
return await this.call("source.message-copy-link", {
|
|
904
|
+
sourceRef,
|
|
905
|
+
actionRefs: refs
|
|
906
|
+
}, signal);
|
|
907
|
+
}
|
|
908
|
+
async reportMessage(messageRef, reportType, options = {}) {
|
|
909
|
+
const normalizedMessageRef = messageRef.trim();
|
|
910
|
+
const reason = options.reason?.trim() ?? "";
|
|
911
|
+
if (normalizedMessageRef === "" || normalizedMessageRef.length > 4096) throw new TypeError("Arkme message reference must not be empty or exceed 4096 characters");
|
|
912
|
+
if (![
|
|
913
|
+
1,
|
|
914
|
+
2,
|
|
915
|
+
3,
|
|
916
|
+
4
|
|
917
|
+
].includes(reportType) || reportType === 4 && reason === "" || [...reason].length > 500) throw new TypeError("Arkme message report type or reason is invalid");
|
|
918
|
+
const requestUid = (options.requestUid ?? crypto.randomUUID()).trim().toLowerCase();
|
|
919
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(requestUid)) throw new TypeError("Arkme message report request UID is invalid");
|
|
920
|
+
return await this.call("source.message-report", {
|
|
921
|
+
messageRef: normalizedMessageRef,
|
|
922
|
+
reportType,
|
|
923
|
+
...reason === "" ? {} : { reason },
|
|
924
|
+
requestUid
|
|
925
|
+
}, options.signal);
|
|
926
|
+
}
|
|
927
|
+
async withdrawGroupMessage(messageWithdrawalRef, signal) {
|
|
928
|
+
const normalized = messageWithdrawalRef.trim();
|
|
929
|
+
if (normalized === "" || normalized.length > 4096) throw new TypeError("Arkme message withdrawal reference must not be empty or exceed 4096 characters");
|
|
930
|
+
return await this.call("source.message-withdraw", { messageWithdrawalRef: normalized }, signal);
|
|
931
|
+
}
|
|
932
|
+
async resolveMessageCopyLink(sid, signal) {
|
|
933
|
+
const normalizedSid = sid.trim();
|
|
934
|
+
if (!/^[0-9A-Za-z]{16}$/.test(normalizedSid)) throw new TypeError("Arkme message copy-link sid must be 16 alphanumeric characters");
|
|
935
|
+
return await this.call("source.message-copy-link.resolve", { sid: normalizedSid }, signal);
|
|
936
|
+
}
|
|
937
|
+
async extendMessageCopyLink(sid, textContent, options = {}) {
|
|
938
|
+
const normalizedSid = sid.trim();
|
|
939
|
+
const text = textContent.trim();
|
|
940
|
+
const itemIndex = Math.trunc(options.itemIndex ?? 0);
|
|
941
|
+
if (!/^[0-9A-Za-z]{16}$/.test(normalizedSid)) throw new TypeError("Arkme message copy-link sid must be 16 alphanumeric characters");
|
|
942
|
+
if (text === "") throw new TypeError("Arkme message copy-link extension text must not be empty");
|
|
943
|
+
if (itemIndex < 0 || itemIndex >= 100) throw new TypeError("Arkme message copy-link item index must be 0-99");
|
|
944
|
+
return await this.call("source.message-copy-link.extend", {
|
|
945
|
+
sid: normalizedSid,
|
|
946
|
+
itemIndex,
|
|
947
|
+
textContent: text,
|
|
948
|
+
recordUid: options.recordUid ?? crypto.randomUUID()
|
|
949
|
+
}, options.signal);
|
|
950
|
+
}
|
|
951
|
+
async sourceMessageExtensionContext(sourceRef, messageActionRef, signal) {
|
|
952
|
+
if (sourceRef.trim() === "" || messageActionRef.trim() === "") throw new TypeError("Arkme source message extension requires a source and action reference");
|
|
953
|
+
return await this.call("source.message-extension.context", {
|
|
954
|
+
sourceRef,
|
|
955
|
+
messageActionRef
|
|
956
|
+
}, signal);
|
|
957
|
+
}
|
|
958
|
+
async extendSourceMessage(sourceRef, messageActionRef, textContent, options = {}) {
|
|
959
|
+
const fileRefs = [...new Set((options.fileRefs ?? []).map((value) => value.trim()).filter((value) => value !== ""))];
|
|
960
|
+
if (sourceRef.trim() === "" || messageActionRef.trim() === "" || textContent.trim() === "" && fileRefs.length === 0) throw new TypeError("Arkme source message extension requires a target and text or attachments");
|
|
961
|
+
if (fileRefs.length > 9) throw new TypeError("Arkme source message extension accepts at most 9 attachments");
|
|
962
|
+
return await this.call("source.message-extension.extend", {
|
|
963
|
+
sourceRef,
|
|
964
|
+
messageActionRef,
|
|
965
|
+
textContent,
|
|
966
|
+
recordUid: options.recordUid ?? crypto.randomUUID(),
|
|
967
|
+
relationUid: options.relationUid ?? crypto.randomUUID(),
|
|
968
|
+
...options.parentRecordUid?.trim() ? { parentRecordUid: options.parentRecordUid.trim() } : {},
|
|
969
|
+
fileRefs
|
|
970
|
+
}, options.signal);
|
|
971
|
+
}
|
|
972
|
+
async resolveLinkMetadata(url, signal) {
|
|
973
|
+
const normalizedUrl = url.trim();
|
|
974
|
+
if (normalizedUrl === "" || normalizedUrl.length > 2048) throw new TypeError("Arkme link metadata URL must not be empty");
|
|
975
|
+
return await this.call("source.link-metadata.resolve", { url: normalizedUrl }, signal);
|
|
976
|
+
}
|
|
977
|
+
async forwardMessages(sourceRef, actionRefs, options = {}) {
|
|
978
|
+
const refs = actionRefs.map((value) => value.trim()).filter((value) => value !== "");
|
|
979
|
+
if (sourceRef.trim() === "" || refs.length === 0 || refs.length > 100 || new Set(refs).size !== refs.length) throw new TypeError("Arkme forward action references must be 1-100 unique values");
|
|
980
|
+
return await this.call("source.forward-messages", {
|
|
981
|
+
sourceRef,
|
|
982
|
+
...options.targetSourceRef === void 0 || options.targetSourceRef.trim() === "" ? {} : { targetSourceRef: options.targetSourceRef.trim() },
|
|
983
|
+
actionRefs: refs,
|
|
984
|
+
recordUid: options.recordUid ?? crypto.randomUUID(),
|
|
985
|
+
relationUid: options.relationUid ?? crypto.randomUUID(),
|
|
986
|
+
...options.commentText === void 0 || options.commentText.trim() === "" ? {} : { commentText: options.commentText.trim() }
|
|
987
|
+
}, options.signal);
|
|
988
|
+
}
|
|
989
|
+
async relatedRecordingEligibility(sourceRef, signal) {
|
|
990
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme private-chat source reference must not be empty");
|
|
991
|
+
return await this.call("related-recordings.eligibility", { sourceRef }, signal);
|
|
992
|
+
}
|
|
993
|
+
async relatedRecordings(sourceRef, options = {}) {
|
|
994
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme private-chat source reference must not be empty");
|
|
995
|
+
return await this.call("related-recordings.page", {
|
|
996
|
+
sourceRef,
|
|
997
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
998
|
+
...options.cursor === void 0 ? {} : { cursor: options.cursor },
|
|
999
|
+
...options.monthKey === void 0 ? {} : { monthKey: options.monthKey },
|
|
1000
|
+
...options.timezoneOffsetMillis === void 0 ? {} : { timezoneOffsetMillis: options.timezoneOffsetMillis },
|
|
1001
|
+
...options.includeTimeIndex === void 0 ? {} : { includeTimeIndex: options.includeTimeIndex }
|
|
1002
|
+
}, options.signal);
|
|
1003
|
+
}
|
|
1004
|
+
async sendRich(sourceRef, input, options = {}) {
|
|
1005
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme source reference must not be empty");
|
|
1006
|
+
return await this.call("source.send-rich", {
|
|
1007
|
+
sourceRef,
|
|
1008
|
+
...input,
|
|
1009
|
+
recordUid: options.recordUid ?? crypto.randomUUID(),
|
|
1010
|
+
relationUid: options.relationUid ?? crypto.randomUUID(),
|
|
1011
|
+
...expectedUserIdParam(options.expectedUserId)
|
|
1012
|
+
}, options.signal);
|
|
1013
|
+
}
|
|
1014
|
+
async favoriteStickers(signal) {
|
|
1015
|
+
return await this.call("favorite-stickers.list", void 0, signal);
|
|
1016
|
+
}
|
|
1017
|
+
async addFavoriteSticker(item, signal) {
|
|
1018
|
+
if (item.fileAssetUid.trim() === "") throw new TypeError("Arkme favorite sticker asset must not be empty");
|
|
1019
|
+
return await this.call("favorite-stickers.add", { item }, signal);
|
|
1020
|
+
}
|
|
1021
|
+
async sendFavoriteSticker(sourceRef, fileAssetUid, options = {}) {
|
|
1022
|
+
if (sourceRef.trim() === "" || fileAssetUid.trim() === "") throw new TypeError("Arkme sticker destination and asset must not be empty");
|
|
1023
|
+
return await this.call("favorite-stickers.send", {
|
|
1024
|
+
sourceRef,
|
|
1025
|
+
fileAssetUid,
|
|
1026
|
+
recordUid: options.recordUid ?? crypto.randomUUID(),
|
|
1027
|
+
relationUid: options.relationUid ?? crypto.randomUUID()
|
|
1028
|
+
}, options.signal);
|
|
1029
|
+
}
|
|
1030
|
+
async manageFavoriteSticker(fileAssetUid, action, signal) {
|
|
1031
|
+
if (fileAssetUid.trim() === "") throw new TypeError("Arkme favorite sticker asset must not be empty");
|
|
1032
|
+
return await this.call("favorite-stickers.manage", {
|
|
1033
|
+
fileAssetUid,
|
|
1034
|
+
action
|
|
1035
|
+
}, signal);
|
|
1036
|
+
}
|
|
1037
|
+
async longArticleDetail(sourceRef, itemUid, signal) {
|
|
1038
|
+
if (sourceRef.trim() === "" || itemUid.trim() === "") throw new TypeError("Arkme source and article references must not be empty");
|
|
1039
|
+
return await this.call("source.long-article.detail", {
|
|
1040
|
+
sourceRef,
|
|
1041
|
+
itemUid
|
|
1042
|
+
}, signal);
|
|
1043
|
+
}
|
|
1044
|
+
async updateLongArticle(sourceRef, itemUid, input, signal) {
|
|
1045
|
+
if (sourceRef.trim() === "" || itemUid.trim() === "") throw new TypeError("Arkme source and article references must not be empty");
|
|
1046
|
+
return await this.call("source.long-article.update", {
|
|
1047
|
+
sourceRef,
|
|
1048
|
+
itemUid,
|
|
1049
|
+
...input
|
|
1050
|
+
}, signal);
|
|
1051
|
+
}
|
|
1052
|
+
async longArticleDraft(sourceRef, itemUid, signal) {
|
|
1053
|
+
return await this.call("source.long-article.draft.get", {
|
|
1054
|
+
sourceRef,
|
|
1055
|
+
...itemUid === void 0 ? {} : { itemUid }
|
|
1056
|
+
}, signal);
|
|
1057
|
+
}
|
|
1058
|
+
async saveLongArticleDraft(draft, signal) {
|
|
1059
|
+
if (draft.sourceRef.trim() === "") throw new TypeError("Arkme source reference must not be empty");
|
|
1060
|
+
await this.call("source.long-article.draft.put", draft, signal);
|
|
1061
|
+
}
|
|
1062
|
+
async deleteLongArticleDraft(sourceRef, itemUid, signal) {
|
|
1063
|
+
if (sourceRef.trim() === "") throw new TypeError("Arkme source reference must not be empty");
|
|
1064
|
+
await this.call("source.long-article.draft.delete", {
|
|
1065
|
+
sourceRef,
|
|
1066
|
+
...itemUid === void 0 ? {} : { itemUid }
|
|
1067
|
+
}, signal);
|
|
1068
|
+
}
|
|
1069
|
+
async fileCapabilities(signal) {
|
|
1070
|
+
const policy = await this.call("files.capabilities", void 0, signal);
|
|
1071
|
+
if (policy.version !== 1) throw new Error(`Unsupported Arkme file contract version ${String(policy.version)}`);
|
|
1072
|
+
return policy;
|
|
1073
|
+
}
|
|
1074
|
+
async searchFiles(options = {}) {
|
|
1075
|
+
const { signal, ...params } = options;
|
|
1076
|
+
return this.call("files.search", params, signal);
|
|
1077
|
+
}
|
|
1078
|
+
async localFiles(signal) {
|
|
1079
|
+
return this.call("files.local.list", void 0, signal);
|
|
1080
|
+
}
|
|
1081
|
+
async openLocalFile(fileRef, signal) {
|
|
1082
|
+
return this.call("files.local.open", { fileRef }, signal);
|
|
1083
|
+
}
|
|
1084
|
+
async removeLocalFile(fileRef) {
|
|
1085
|
+
return this.call("files.local.remove", { fileRef });
|
|
1086
|
+
}
|
|
1087
|
+
async sendFiles(input) {
|
|
1088
|
+
return this.call("files.send", {
|
|
1089
|
+
...input.content,
|
|
1090
|
+
...input
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
async fileSendTasks(sourceRef, signal) {
|
|
1094
|
+
return this.call("files.send.tasks", sourceRef === void 0 ? {} : { sourceRef }, signal);
|
|
1095
|
+
}
|
|
1096
|
+
async retryFileSend(taskRef) {
|
|
1097
|
+
return this.call("files.send.retry", { taskRef });
|
|
1098
|
+
}
|
|
1099
|
+
async discardFileSend(taskRef) {
|
|
1100
|
+
return this.call("files.send.discard", { taskRef });
|
|
1101
|
+
}
|
|
1102
|
+
async reconcileFileSend(taskRef) {
|
|
1103
|
+
return this.call("files.send.reconcile", { taskRef });
|
|
1104
|
+
}
|
|
1105
|
+
async stageFileBytes(contentBase64, metadata) {
|
|
1106
|
+
return this.call("files.stage-bytes", {
|
|
1107
|
+
contentBase64,
|
|
1108
|
+
...metadata
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
async receiveFile(mediaRef, start = false, signal) {
|
|
1112
|
+
return this.call("files.receive", {
|
|
1113
|
+
mediaRef,
|
|
1114
|
+
start
|
|
1115
|
+
}, signal);
|
|
1116
|
+
}
|
|
1117
|
+
subscribeFileSends(sourceRef, listener, options = {}) {
|
|
1118
|
+
const controller = new AbortController();
|
|
1119
|
+
let timer;
|
|
1120
|
+
const poll = async () => {
|
|
1121
|
+
try {
|
|
1122
|
+
const tasks = await this.fileSendTasks(sourceRef, controller.signal);
|
|
1123
|
+
if (!controller.signal.aborted) listener(tasks);
|
|
1124
|
+
} catch (error) {
|
|
1125
|
+
if (!controller.signal.aborted) options.onError?.(error);
|
|
1126
|
+
} finally {
|
|
1127
|
+
if (!controller.signal.aborted) timer = setTimeout(() => {
|
|
1128
|
+
poll();
|
|
1129
|
+
}, Math.max(250, options.intervalMs ?? 750));
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
if (options.immediate !== false) poll();
|
|
1133
|
+
else timer = setTimeout(() => {
|
|
1134
|
+
poll();
|
|
1135
|
+
}, Math.max(250, options.intervalMs ?? 750));
|
|
1136
|
+
return () => {
|
|
1137
|
+
controller.abort();
|
|
1138
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
localFileUrl(fileRef, download = false) {
|
|
1142
|
+
if (!/^arkme-file-v1\.[0-9a-f-]{36}$/.test(fileRef)) throw new TypeError("Invalid local file reference");
|
|
1143
|
+
return `${this.route}/files/local?ref=${encodeURIComponent(fileRef)}${download ? "&download=1" : ""}`;
|
|
1144
|
+
}
|
|
1145
|
+
/** Stage locally. Reference-managed files require a Host-persisted draft or task to retain them beyond seven days. */
|
|
1146
|
+
async stageFile(file, options = {}) {
|
|
1147
|
+
const payload = await (await this.fetchImpl(`${this.route}/files/stage`, {
|
|
1148
|
+
method: "POST",
|
|
1149
|
+
headers: {
|
|
1150
|
+
"Content-Type": file.type || "application/octet-stream",
|
|
1151
|
+
"X-Arkme-File-Name": encodeURIComponent(options.fileName ?? file.name ?? "attachment"),
|
|
1152
|
+
...expectedUserIdHeaders(options.expectedUserId),
|
|
1153
|
+
...options.retention ? { "X-Arkme-File-Retention": options.retention } : {}
|
|
1154
|
+
},
|
|
1155
|
+
body: file,
|
|
1156
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
1157
|
+
})).json();
|
|
1158
|
+
if (!payload.ok) throw new ArkmeClientError(payload.error);
|
|
1159
|
+
return payload.value;
|
|
1160
|
+
}
|
|
1161
|
+
async upload(file, options = {}) {
|
|
1162
|
+
const fileName = (options.fileName ?? file.name ?? "attachment").trim();
|
|
1163
|
+
const payload = await (await this.fetchImpl(`${this.route}/upload`, {
|
|
1164
|
+
method: "POST",
|
|
1165
|
+
headers: {
|
|
1166
|
+
"Content-Type": file.type || "application/octet-stream",
|
|
1167
|
+
"X-Arkme-File-Name": encodeURIComponent(fileName),
|
|
1168
|
+
...expectedUserIdHeaders(options.expectedUserId)
|
|
1169
|
+
},
|
|
1170
|
+
body: file,
|
|
1171
|
+
...options.signal === void 0 ? {} : { signal: options.signal }
|
|
1172
|
+
})).json();
|
|
1173
|
+
if (!payload.ok) throw new ArkmeClientError(payload.error);
|
|
1174
|
+
return payload.value;
|
|
1175
|
+
}
|
|
1176
|
+
mediaUrl(mediaRef) {
|
|
1177
|
+
if (mediaRef.trim() === "") throw new TypeError("Arkme media reference must not be empty");
|
|
1178
|
+
return `${this.route}/media?ref=${encodeURIComponent(mediaRef)}`;
|
|
1179
|
+
}
|
|
1180
|
+
async snapshot(options = {}) {
|
|
1181
|
+
return await this.call(options.refresh === true ? "records.refresh" : "records.cache", void 0, options.signal);
|
|
1182
|
+
}
|
|
1183
|
+
async calendarBuckets(options) {
|
|
1184
|
+
return await this.call("calendar.buckets", {
|
|
1185
|
+
startDate: options.startDate,
|
|
1186
|
+
endDate: options.endDate,
|
|
1187
|
+
...options.timezone === void 0 ? {} : { timezone: options.timezone }
|
|
1188
|
+
}, options.signal);
|
|
1189
|
+
}
|
|
1190
|
+
async calendarRecords(options) {
|
|
1191
|
+
return await this.call("calendar.records", {
|
|
1192
|
+
bucketDate: options.bucketDate,
|
|
1193
|
+
...options.timezone === void 0 ? {} : { timezone: options.timezone },
|
|
1194
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
1195
|
+
...options.cursor === void 0 ? {} : { cursor: options.cursor }
|
|
1196
|
+
}, options.signal);
|
|
1197
|
+
}
|
|
1198
|
+
async search(query, options = {}) {
|
|
1199
|
+
return await this.call("records.search", {
|
|
1200
|
+
query,
|
|
1201
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
1202
|
+
...options.beforeMillis === void 0 ? {} : { beforeMillis: options.beforeMillis },
|
|
1203
|
+
...options.syncAll === void 0 ? {} : { syncAll: options.syncAll }
|
|
1204
|
+
}, options.signal);
|
|
1205
|
+
}
|
|
1206
|
+
/** List the signed-in user's image library without exposing storage URLs. */
|
|
1207
|
+
async images(options = {}) {
|
|
1208
|
+
return await this.call("images.list", {
|
|
1209
|
+
...options.limit === void 0 ? {} : { limit: options.limit },
|
|
1210
|
+
...options.cursor?.trim() ? { cursor: options.cursor.trim() } : {}
|
|
1211
|
+
}, options.signal);
|
|
1212
|
+
}
|
|
1213
|
+
async createText(textContent, options = {}) {
|
|
1214
|
+
return await this.call("records.create", {
|
|
1215
|
+
recordUid: options.recordUid ?? crypto.randomUUID(),
|
|
1216
|
+
textContent
|
|
1217
|
+
}, options.signal);
|
|
1218
|
+
}
|
|
1219
|
+
async tags(options = {}) {
|
|
1220
|
+
return await this.call("records.tags.list", { limit: options.limit ?? 100 }, options.signal);
|
|
1221
|
+
}
|
|
1222
|
+
async tagRecords(normalizedTag, options = {}) {
|
|
1223
|
+
return await this.call("records.tags.query", {
|
|
1224
|
+
normalizedTag,
|
|
1225
|
+
limit: options.limit ?? 50,
|
|
1226
|
+
...options.cursorSendAt === void 0 ? {} : { cursorSendAt: options.cursorSendAt },
|
|
1227
|
+
...options.cursorRecordUid?.trim() ? { cursorRecordUid: options.cursorRecordUid.trim() } : {}
|
|
1228
|
+
}, options.signal);
|
|
1229
|
+
}
|
|
1230
|
+
async outbox(signal) {
|
|
1231
|
+
return await this.call("records.outbox", void 0, signal);
|
|
1232
|
+
}
|
|
1233
|
+
async retry(recordUid, signal) {
|
|
1234
|
+
return await this.call("records.retry", { recordUid }, signal);
|
|
1235
|
+
}
|
|
1236
|
+
subscribe(listener, options = {}) {
|
|
1237
|
+
const intervalMs = Math.min(6e4, Math.max(500, Math.trunc(options.intervalMs ?? 1e3)));
|
|
1238
|
+
let stopped = false;
|
|
1239
|
+
let timeout;
|
|
1240
|
+
let previousKey = "";
|
|
1241
|
+
const poll = async () => {
|
|
1242
|
+
if (stopped) return;
|
|
1243
|
+
try {
|
|
1244
|
+
const state = await this.state();
|
|
1245
|
+
if (stopped) return;
|
|
1246
|
+
const key = `${state.authStatus}:${String(state.userId ?? "")}:${String(state.revision)}`;
|
|
1247
|
+
if (key !== previousKey) {
|
|
1248
|
+
previousKey = key;
|
|
1249
|
+
listener(state);
|
|
1250
|
+
}
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
options.onError?.(error);
|
|
1253
|
+
} finally {
|
|
1254
|
+
if (!stopped) timeout = setTimeout(() => {
|
|
1255
|
+
poll();
|
|
1256
|
+
}, intervalMs);
|
|
1257
|
+
}
|
|
1258
|
+
};
|
|
1259
|
+
if (options.immediate === false) timeout = setTimeout(() => {
|
|
1260
|
+
poll();
|
|
1261
|
+
}, intervalMs);
|
|
1262
|
+
else poll();
|
|
1263
|
+
return () => {
|
|
1264
|
+
stopped = true;
|
|
1265
|
+
if (timeout !== void 0) clearTimeout(timeout);
|
|
1266
|
+
};
|
|
1267
|
+
}
|
|
1268
|
+
async remoteStatus(signal) {
|
|
1269
|
+
return await this.call("remote.getStatus", void 0, signal);
|
|
1270
|
+
}
|
|
1271
|
+
async renameRemoteDesktop(displayName, signal) {
|
|
1272
|
+
const normalized = displayName.trim();
|
|
1273
|
+
if (normalized === "" || [...normalized].length > 80) throw new TypeError("Remote desktop name must contain 1 to 80 characters");
|
|
1274
|
+
return await this.call("remote.renameDesktop", { displayName: normalized }, signal);
|
|
1275
|
+
}
|
|
1276
|
+
subscribeRemote(listener, options = {}) {
|
|
1277
|
+
const intervalMs = Math.min(6e4, Math.max(1e3, Math.trunc(options.intervalMs ?? 2e3)));
|
|
1278
|
+
let stopped = false;
|
|
1279
|
+
let timer;
|
|
1280
|
+
let revision = -1;
|
|
1281
|
+
const poll = async () => {
|
|
1282
|
+
if (stopped) return;
|
|
1283
|
+
try {
|
|
1284
|
+
const status = await this.remoteStatus();
|
|
1285
|
+
if (!stopped && status.revision !== revision) {
|
|
1286
|
+
revision = status.revision;
|
|
1287
|
+
listener(status);
|
|
1288
|
+
}
|
|
1289
|
+
} catch (error) {
|
|
1290
|
+
options.onError?.(error);
|
|
1291
|
+
} finally {
|
|
1292
|
+
if (!stopped) timer = setTimeout(() => {
|
|
1293
|
+
poll();
|
|
1294
|
+
}, intervalMs);
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
if (options.immediate === false) timer = setTimeout(() => {
|
|
1298
|
+
poll();
|
|
1299
|
+
}, intervalMs);
|
|
1300
|
+
else poll();
|
|
1301
|
+
return () => {
|
|
1302
|
+
stopped = true;
|
|
1303
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1304
|
+
};
|
|
1305
|
+
}
|
|
1306
|
+
async call(operation, params, signal) {
|
|
1307
|
+
const response = await this.fetchImpl(this.route, {
|
|
1308
|
+
method: "POST",
|
|
1309
|
+
credentials: "same-origin",
|
|
1310
|
+
headers: { "Content-Type": "application/json" },
|
|
1311
|
+
body: JSON.stringify({
|
|
1312
|
+
operation,
|
|
1313
|
+
...params === void 0 ? {} : { params }
|
|
1314
|
+
}),
|
|
1315
|
+
...signal === void 0 ? {} : { signal }
|
|
1316
|
+
});
|
|
1317
|
+
let body;
|
|
1318
|
+
try {
|
|
1319
|
+
body = await response.json();
|
|
1320
|
+
} catch {
|
|
1321
|
+
throw new ArkmeClientError({
|
|
1322
|
+
code: "local-response-invalid",
|
|
1323
|
+
message: "Arkme 插件返回了无效响应",
|
|
1324
|
+
retryable: true
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1327
|
+
if (!body.ok) throw new ArkmeClientError(body.error);
|
|
1328
|
+
return body.value;
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
function createArkmeSdk(options) {
|
|
1332
|
+
return new ArkmeSdk(options);
|
|
1333
|
+
}
|
|
1334
|
+
const defaultSdk = createArkmeSdk();
|
|
1335
|
+
async function callArkme$1(operation, params, signal) {
|
|
1336
|
+
return await defaultSdk.call(operation, params, signal);
|
|
1337
|
+
}
|
|
1338
|
+
//#endregion
|
|
1339
|
+
//#region src/client/api.ts
|
|
1340
|
+
/** Built-in UI bridge. UI-only operations intentionally stay out of the public Consumer SDK. */
|
|
1341
|
+
async function callArkme(operation, params, signal) {
|
|
1342
|
+
return await callArkme$1(operation, params, signal);
|
|
1343
|
+
}
|
|
1344
|
+
//#endregion
|
|
1345
|
+
//#region node_modules/qrcode-generator/dist/qrcode.mjs
|
|
1346
|
+
/**
|
|
1347
|
+
* qrcode
|
|
1348
|
+
* @param typeNumber 1 to 40
|
|
1349
|
+
* @param errorCorrectionLevel 'L','M','Q','H'
|
|
1350
|
+
*/
|
|
1351
|
+
const qrcode = function(typeNumber, errorCorrectionLevel) {
|
|
1352
|
+
const PAD0 = 236;
|
|
1353
|
+
const PAD1 = 17;
|
|
1354
|
+
let _typeNumber = typeNumber;
|
|
1355
|
+
const _errorCorrectionLevel = QRErrorCorrectionLevel[errorCorrectionLevel];
|
|
1356
|
+
let _modules = null;
|
|
1357
|
+
let _moduleCount = 0;
|
|
1358
|
+
let _dataCache = null;
|
|
1359
|
+
const _dataList = [];
|
|
1360
|
+
const _this = {};
|
|
1361
|
+
const makeImpl = function(test, maskPattern) {
|
|
1362
|
+
_moduleCount = _typeNumber * 4 + 17;
|
|
1363
|
+
_modules = function(moduleCount) {
|
|
1364
|
+
const modules = new Array(moduleCount);
|
|
1365
|
+
for (let row = 0; row < moduleCount; row += 1) {
|
|
1366
|
+
modules[row] = new Array(moduleCount);
|
|
1367
|
+
for (let col = 0; col < moduleCount; col += 1) modules[row][col] = null;
|
|
1368
|
+
}
|
|
1369
|
+
return modules;
|
|
1370
|
+
}(_moduleCount);
|
|
1371
|
+
setupPositionProbePattern(0, 0);
|
|
1372
|
+
setupPositionProbePattern(_moduleCount - 7, 0);
|
|
1373
|
+
setupPositionProbePattern(0, _moduleCount - 7);
|
|
1374
|
+
setupPositionAdjustPattern();
|
|
1375
|
+
setupTimingPattern();
|
|
1376
|
+
setupTypeInfo(test, maskPattern);
|
|
1377
|
+
if (_typeNumber >= 7) setupTypeNumber(test);
|
|
1378
|
+
if (_dataCache == null) _dataCache = createData(_typeNumber, _errorCorrectionLevel, _dataList);
|
|
1379
|
+
mapData(_dataCache, maskPattern);
|
|
1380
|
+
};
|
|
1381
|
+
const setupPositionProbePattern = function(row, col) {
|
|
1382
|
+
for (let r = -1; r <= 7; r += 1) {
|
|
1383
|
+
if (row + r <= -1 || _moduleCount <= row + r) continue;
|
|
1384
|
+
for (let c = -1; c <= 7; c += 1) {
|
|
1385
|
+
if (col + c <= -1 || _moduleCount <= col + c) continue;
|
|
1386
|
+
if (0 <= r && r <= 6 && (c == 0 || c == 6) || 0 <= c && c <= 6 && (r == 0 || r == 6) || 2 <= r && r <= 4 && 2 <= c && c <= 4) _modules[row + r][col + c] = true;
|
|
1387
|
+
else _modules[row + r][col + c] = false;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
};
|
|
1391
|
+
const getBestMaskPattern = function() {
|
|
1392
|
+
let minLostPoint = 0;
|
|
1393
|
+
let pattern = 0;
|
|
1394
|
+
for (let i = 0; i < 8; i += 1) {
|
|
1395
|
+
makeImpl(true, i);
|
|
1396
|
+
const lostPoint = QRUtil.getLostPoint(_this);
|
|
1397
|
+
if (i == 0 || minLostPoint > lostPoint) {
|
|
1398
|
+
minLostPoint = lostPoint;
|
|
1399
|
+
pattern = i;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
return pattern;
|
|
1403
|
+
};
|
|
1404
|
+
const setupTimingPattern = function() {
|
|
1405
|
+
for (let r = 8; r < _moduleCount - 8; r += 1) {
|
|
1406
|
+
if (_modules[r][6] != null) continue;
|
|
1407
|
+
_modules[r][6] = r % 2 == 0;
|
|
1408
|
+
}
|
|
1409
|
+
for (let c = 8; c < _moduleCount - 8; c += 1) {
|
|
1410
|
+
if (_modules[6][c] != null) continue;
|
|
1411
|
+
_modules[6][c] = c % 2 == 0;
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
const setupPositionAdjustPattern = function() {
|
|
1415
|
+
const pos = QRUtil.getPatternPosition(_typeNumber);
|
|
1416
|
+
for (let i = 0; i < pos.length; i += 1) for (let j = 0; j < pos.length; j += 1) {
|
|
1417
|
+
const row = pos[i];
|
|
1418
|
+
const col = pos[j];
|
|
1419
|
+
if (_modules[row][col] != null) continue;
|
|
1420
|
+
for (let r = -2; r <= 2; r += 1) for (let c = -2; c <= 2; c += 1) if (r == -2 || r == 2 || c == -2 || c == 2 || r == 0 && c == 0) _modules[row + r][col + c] = true;
|
|
1421
|
+
else _modules[row + r][col + c] = false;
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
const setupTypeNumber = function(test) {
|
|
1425
|
+
const bits = QRUtil.getBCHTypeNumber(_typeNumber);
|
|
1426
|
+
for (let i = 0; i < 18; i += 1) {
|
|
1427
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1428
|
+
_modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
|
|
1429
|
+
}
|
|
1430
|
+
for (let i = 0; i < 18; i += 1) {
|
|
1431
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1432
|
+
_modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
const setupTypeInfo = function(test, maskPattern) {
|
|
1436
|
+
const data = _errorCorrectionLevel << 3 | maskPattern;
|
|
1437
|
+
const bits = QRUtil.getBCHTypeInfo(data);
|
|
1438
|
+
for (let i = 0; i < 15; i += 1) {
|
|
1439
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1440
|
+
if (i < 6) _modules[i][8] = mod;
|
|
1441
|
+
else if (i < 8) _modules[i + 1][8] = mod;
|
|
1442
|
+
else _modules[_moduleCount - 15 + i][8] = mod;
|
|
1443
|
+
}
|
|
1444
|
+
for (let i = 0; i < 15; i += 1) {
|
|
1445
|
+
const mod = !test && (bits >> i & 1) == 1;
|
|
1446
|
+
if (i < 8) _modules[8][_moduleCount - i - 1] = mod;
|
|
1447
|
+
else if (i < 9) _modules[8][15 - i - 1 + 1] = mod;
|
|
1448
|
+
else _modules[8][15 - i - 1] = mod;
|
|
1449
|
+
}
|
|
1450
|
+
_modules[_moduleCount - 8][8] = !test;
|
|
1451
|
+
};
|
|
1452
|
+
const mapData = function(data, maskPattern) {
|
|
1453
|
+
let inc = -1;
|
|
1454
|
+
let row = _moduleCount - 1;
|
|
1455
|
+
let bitIndex = 7;
|
|
1456
|
+
let byteIndex = 0;
|
|
1457
|
+
const maskFunc = QRUtil.getMaskFunction(maskPattern);
|
|
1458
|
+
for (let col = _moduleCount - 1; col > 0; col -= 2) {
|
|
1459
|
+
if (col == 6) col -= 1;
|
|
1460
|
+
while (true) {
|
|
1461
|
+
for (let c = 0; c < 2; c += 1) if (_modules[row][col - c] == null) {
|
|
1462
|
+
let dark = false;
|
|
1463
|
+
if (byteIndex < data.length) dark = (data[byteIndex] >>> bitIndex & 1) == 1;
|
|
1464
|
+
if (maskFunc(row, col - c)) dark = !dark;
|
|
1465
|
+
_modules[row][col - c] = dark;
|
|
1466
|
+
bitIndex -= 1;
|
|
1467
|
+
if (bitIndex == -1) {
|
|
1468
|
+
byteIndex += 1;
|
|
1469
|
+
bitIndex = 7;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
row += inc;
|
|
1473
|
+
if (row < 0 || _moduleCount <= row) {
|
|
1474
|
+
row -= inc;
|
|
1475
|
+
inc = -inc;
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
const createBytes = function(buffer, rsBlocks) {
|
|
1482
|
+
let offset = 0;
|
|
1483
|
+
let maxDcCount = 0;
|
|
1484
|
+
let maxEcCount = 0;
|
|
1485
|
+
const dcdata = new Array(rsBlocks.length);
|
|
1486
|
+
const ecdata = new Array(rsBlocks.length);
|
|
1487
|
+
for (let r = 0; r < rsBlocks.length; r += 1) {
|
|
1488
|
+
const dcCount = rsBlocks[r].dataCount;
|
|
1489
|
+
const ecCount = rsBlocks[r].totalCount - dcCount;
|
|
1490
|
+
maxDcCount = Math.max(maxDcCount, dcCount);
|
|
1491
|
+
maxEcCount = Math.max(maxEcCount, ecCount);
|
|
1492
|
+
dcdata[r] = new Array(dcCount);
|
|
1493
|
+
for (let i = 0; i < dcdata[r].length; i += 1) dcdata[r][i] = 255 & buffer.getBuffer()[i + offset];
|
|
1494
|
+
offset += dcCount;
|
|
1495
|
+
const rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
|
|
1496
|
+
const modPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1).mod(rsPoly);
|
|
1497
|
+
ecdata[r] = new Array(rsPoly.getLength() - 1);
|
|
1498
|
+
for (let i = 0; i < ecdata[r].length; i += 1) {
|
|
1499
|
+
const modIndex = i + modPoly.getLength() - ecdata[r].length;
|
|
1500
|
+
ecdata[r][i] = modIndex >= 0 ? modPoly.getAt(modIndex) : 0;
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
let totalCodeCount = 0;
|
|
1504
|
+
for (let i = 0; i < rsBlocks.length; i += 1) totalCodeCount += rsBlocks[i].totalCount;
|
|
1505
|
+
const data = new Array(totalCodeCount);
|
|
1506
|
+
let index = 0;
|
|
1507
|
+
for (let i = 0; i < maxDcCount; i += 1) for (let r = 0; r < rsBlocks.length; r += 1) if (i < dcdata[r].length) {
|
|
1508
|
+
data[index] = dcdata[r][i];
|
|
1509
|
+
index += 1;
|
|
1510
|
+
}
|
|
1511
|
+
for (let i = 0; i < maxEcCount; i += 1) for (let r = 0; r < rsBlocks.length; r += 1) if (i < ecdata[r].length) {
|
|
1512
|
+
data[index] = ecdata[r][i];
|
|
1513
|
+
index += 1;
|
|
1514
|
+
}
|
|
1515
|
+
return data;
|
|
1516
|
+
};
|
|
1517
|
+
const createData = function(typeNumber, errorCorrectionLevel, dataList) {
|
|
1518
|
+
const rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectionLevel);
|
|
1519
|
+
const buffer = qrBitBuffer();
|
|
1520
|
+
for (let i = 0; i < dataList.length; i += 1) {
|
|
1521
|
+
const data = dataList[i];
|
|
1522
|
+
buffer.put(data.getMode(), 4);
|
|
1523
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber));
|
|
1524
|
+
data.write(buffer);
|
|
1525
|
+
}
|
|
1526
|
+
let totalDataCount = 0;
|
|
1527
|
+
for (let i = 0; i < rsBlocks.length; i += 1) totalDataCount += rsBlocks[i].dataCount;
|
|
1528
|
+
if (buffer.getLengthInBits() > totalDataCount * 8) throw "code length overflow. (" + buffer.getLengthInBits() + ">" + totalDataCount * 8 + ")";
|
|
1529
|
+
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) buffer.put(0, 4);
|
|
1530
|
+
while (buffer.getLengthInBits() % 8 != 0) buffer.putBit(false);
|
|
1531
|
+
while (true) {
|
|
1532
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) break;
|
|
1533
|
+
buffer.put(PAD0, 8);
|
|
1534
|
+
if (buffer.getLengthInBits() >= totalDataCount * 8) break;
|
|
1535
|
+
buffer.put(PAD1, 8);
|
|
1536
|
+
}
|
|
1537
|
+
return createBytes(buffer, rsBlocks);
|
|
1538
|
+
};
|
|
1539
|
+
_this.addData = function(data, mode) {
|
|
1540
|
+
mode = mode || "Byte";
|
|
1541
|
+
let newData = null;
|
|
1542
|
+
switch (mode) {
|
|
1543
|
+
case "Numeric":
|
|
1544
|
+
newData = qrNumber(data);
|
|
1545
|
+
break;
|
|
1546
|
+
case "Alphanumeric":
|
|
1547
|
+
newData = qrAlphaNum(data);
|
|
1548
|
+
break;
|
|
1549
|
+
case "Byte":
|
|
1550
|
+
newData = qr8BitByte(data);
|
|
1551
|
+
break;
|
|
1552
|
+
case "Kanji":
|
|
1553
|
+
newData = qrKanji(data);
|
|
1554
|
+
break;
|
|
1555
|
+
default: throw "mode:" + mode;
|
|
1556
|
+
}
|
|
1557
|
+
_dataList.push(newData);
|
|
1558
|
+
_dataCache = null;
|
|
1559
|
+
};
|
|
1560
|
+
_this.isDark = function(row, col) {
|
|
1561
|
+
if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) throw row + "," + col;
|
|
1562
|
+
return _modules[row][col];
|
|
1563
|
+
};
|
|
1564
|
+
_this.getModuleCount = function() {
|
|
1565
|
+
return _moduleCount;
|
|
1566
|
+
};
|
|
1567
|
+
_this.make = function() {
|
|
1568
|
+
if (_typeNumber < 1) {
|
|
1569
|
+
let typeNumber = 1;
|
|
1570
|
+
for (; typeNumber < 40; typeNumber++) {
|
|
1571
|
+
const rsBlocks = QRRSBlock.getRSBlocks(typeNumber, _errorCorrectionLevel);
|
|
1572
|
+
const buffer = qrBitBuffer();
|
|
1573
|
+
for (let i = 0; i < _dataList.length; i++) {
|
|
1574
|
+
const data = _dataList[i];
|
|
1575
|
+
buffer.put(data.getMode(), 4);
|
|
1576
|
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber));
|
|
1577
|
+
data.write(buffer);
|
|
1578
|
+
}
|
|
1579
|
+
let totalDataCount = 0;
|
|
1580
|
+
for (let i = 0; i < rsBlocks.length; i++) totalDataCount += rsBlocks[i].dataCount;
|
|
1581
|
+
if (buffer.getLengthInBits() <= totalDataCount * 8) break;
|
|
1582
|
+
}
|
|
1583
|
+
_typeNumber = typeNumber;
|
|
1584
|
+
}
|
|
1585
|
+
makeImpl(false, getBestMaskPattern());
|
|
1586
|
+
};
|
|
1587
|
+
_this.createTableTag = function(cellSize, margin) {
|
|
1588
|
+
cellSize = cellSize || 2;
|
|
1589
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1590
|
+
let qrHtml = "";
|
|
1591
|
+
qrHtml += "<table style=\"";
|
|
1592
|
+
qrHtml += " border-width: 0px; border-style: none;";
|
|
1593
|
+
qrHtml += " border-collapse: collapse;";
|
|
1594
|
+
qrHtml += " padding: 0px; margin: " + margin + "px;";
|
|
1595
|
+
qrHtml += "\">";
|
|
1596
|
+
qrHtml += "<tbody>";
|
|
1597
|
+
for (let r = 0; r < _this.getModuleCount(); r += 1) {
|
|
1598
|
+
qrHtml += "<tr>";
|
|
1599
|
+
for (let c = 0; c < _this.getModuleCount(); c += 1) {
|
|
1600
|
+
qrHtml += "<td style=\"";
|
|
1601
|
+
qrHtml += " border-width: 0px; border-style: none;";
|
|
1602
|
+
qrHtml += " border-collapse: collapse;";
|
|
1603
|
+
qrHtml += " padding: 0px; margin: 0px;";
|
|
1604
|
+
qrHtml += " width: " + cellSize + "px;";
|
|
1605
|
+
qrHtml += " height: " + cellSize + "px;";
|
|
1606
|
+
qrHtml += " background-color: ";
|
|
1607
|
+
qrHtml += _this.isDark(r, c) ? "#000000" : "#ffffff";
|
|
1608
|
+
qrHtml += ";";
|
|
1609
|
+
qrHtml += "\"/>";
|
|
1610
|
+
}
|
|
1611
|
+
qrHtml += "</tr>";
|
|
1612
|
+
}
|
|
1613
|
+
qrHtml += "</tbody>";
|
|
1614
|
+
qrHtml += "</table>";
|
|
1615
|
+
return qrHtml;
|
|
1616
|
+
};
|
|
1617
|
+
_this.createSvgTag = function(cellSize, margin, alt, title) {
|
|
1618
|
+
let opts = {};
|
|
1619
|
+
if (typeof arguments[0] == "object") {
|
|
1620
|
+
opts = arguments[0];
|
|
1621
|
+
cellSize = opts.cellSize;
|
|
1622
|
+
margin = opts.margin;
|
|
1623
|
+
alt = opts.alt;
|
|
1624
|
+
title = opts.title;
|
|
1625
|
+
}
|
|
1626
|
+
cellSize = cellSize || 2;
|
|
1627
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1628
|
+
alt = typeof alt === "string" ? { text: alt } : alt || {};
|
|
1629
|
+
alt.text = alt.text || null;
|
|
1630
|
+
alt.id = alt.text ? alt.id || "qrcode-description" : null;
|
|
1631
|
+
title = typeof title === "string" ? { text: title } : title || {};
|
|
1632
|
+
title.text = title.text || null;
|
|
1633
|
+
title.id = title.text ? title.id || "qrcode-title" : null;
|
|
1634
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1635
|
+
let c, mc, r, mr, qrSvg = "", rect;
|
|
1636
|
+
rect = "l" + cellSize + ",0 0," + cellSize + " -" + cellSize + ",0 0,-" + cellSize + "z ";
|
|
1637
|
+
qrSvg += "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\"";
|
|
1638
|
+
qrSvg += !opts.scalable ? " width=\"" + size + "px\" height=\"" + size + "px\"" : "";
|
|
1639
|
+
qrSvg += " viewBox=\"0 0 " + size + " " + size + "\" ";
|
|
1640
|
+
qrSvg += " preserveAspectRatio=\"xMinYMin meet\"";
|
|
1641
|
+
qrSvg += title.text || alt.text ? " role=\"img\" aria-labelledby=\"" + escapeXml([title.id, alt.id].join(" ").trim()) + "\"" : "";
|
|
1642
|
+
qrSvg += ">";
|
|
1643
|
+
qrSvg += title.text ? "<title id=\"" + escapeXml(title.id) + "\">" + escapeXml(title.text) + "</title>" : "";
|
|
1644
|
+
qrSvg += alt.text ? "<description id=\"" + escapeXml(alt.id) + "\">" + escapeXml(alt.text) + "</description>" : "";
|
|
1645
|
+
qrSvg += "<rect width=\"100%\" height=\"100%\" fill=\"white\" cx=\"0\" cy=\"0\"/>";
|
|
1646
|
+
qrSvg += "<path d=\"";
|
|
1647
|
+
for (r = 0; r < _this.getModuleCount(); r += 1) {
|
|
1648
|
+
mr = r * cellSize + margin;
|
|
1649
|
+
for (c = 0; c < _this.getModuleCount(); c += 1) if (_this.isDark(r, c)) {
|
|
1650
|
+
mc = c * cellSize + margin;
|
|
1651
|
+
qrSvg += "M" + mc + "," + mr + rect;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
qrSvg += "\" stroke=\"transparent\" fill=\"black\"/>";
|
|
1655
|
+
qrSvg += "</svg>";
|
|
1656
|
+
return qrSvg;
|
|
1657
|
+
};
|
|
1658
|
+
_this.createDataURL = function(cellSize, margin) {
|
|
1659
|
+
cellSize = cellSize || 2;
|
|
1660
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1661
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1662
|
+
const min = margin;
|
|
1663
|
+
const max = size - margin;
|
|
1664
|
+
return createDataURL(size, size, function(x, y) {
|
|
1665
|
+
if (min <= x && x < max && min <= y && y < max) {
|
|
1666
|
+
const c = Math.floor((x - min) / cellSize);
|
|
1667
|
+
const r = Math.floor((y - min) / cellSize);
|
|
1668
|
+
return _this.isDark(r, c) ? 0 : 1;
|
|
1669
|
+
} else return 1;
|
|
1670
|
+
});
|
|
1671
|
+
};
|
|
1672
|
+
_this.createImgTag = function(cellSize, margin, alt) {
|
|
1673
|
+
cellSize = cellSize || 2;
|
|
1674
|
+
margin = typeof margin == "undefined" ? cellSize * 4 : margin;
|
|
1675
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1676
|
+
let img = "";
|
|
1677
|
+
img += "<img";
|
|
1678
|
+
img += " src=\"";
|
|
1679
|
+
img += _this.createDataURL(cellSize, margin);
|
|
1680
|
+
img += "\"";
|
|
1681
|
+
img += " width=\"";
|
|
1682
|
+
img += size;
|
|
1683
|
+
img += "\"";
|
|
1684
|
+
img += " height=\"";
|
|
1685
|
+
img += size;
|
|
1686
|
+
img += "\"";
|
|
1687
|
+
if (alt) {
|
|
1688
|
+
img += " alt=\"";
|
|
1689
|
+
img += escapeXml(alt);
|
|
1690
|
+
img += "\"";
|
|
1691
|
+
}
|
|
1692
|
+
img += "/>";
|
|
1693
|
+
return img;
|
|
1694
|
+
};
|
|
1695
|
+
const escapeXml = function(s) {
|
|
1696
|
+
let escaped = "";
|
|
1697
|
+
for (let i = 0; i < s.length; i += 1) {
|
|
1698
|
+
const c = s.charAt(i);
|
|
1699
|
+
switch (c) {
|
|
1700
|
+
case "<":
|
|
1701
|
+
escaped += "<";
|
|
1702
|
+
break;
|
|
1703
|
+
case ">":
|
|
1704
|
+
escaped += ">";
|
|
1705
|
+
break;
|
|
1706
|
+
case "&":
|
|
1707
|
+
escaped += "&";
|
|
1708
|
+
break;
|
|
1709
|
+
case "\"":
|
|
1710
|
+
escaped += """;
|
|
1711
|
+
break;
|
|
1712
|
+
default:
|
|
1713
|
+
escaped += c;
|
|
1714
|
+
break;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
return escaped;
|
|
1718
|
+
};
|
|
1719
|
+
const _createHalfASCII = function(margin) {
|
|
1720
|
+
const cellSize = 1;
|
|
1721
|
+
margin = typeof margin == "undefined" ? cellSize * 2 : margin;
|
|
1722
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1723
|
+
const min = margin;
|
|
1724
|
+
const max = size - margin;
|
|
1725
|
+
let y, x, r1, r2, p;
|
|
1726
|
+
const blocks = {
|
|
1727
|
+
"██": "█",
|
|
1728
|
+
"█ ": "▀",
|
|
1729
|
+
" █": "▄",
|
|
1730
|
+
" ": " "
|
|
1731
|
+
};
|
|
1732
|
+
const blocksLastLineNoMargin = {
|
|
1733
|
+
"██": "▀",
|
|
1734
|
+
"█ ": "▀",
|
|
1735
|
+
" █": " ",
|
|
1736
|
+
" ": " "
|
|
1737
|
+
};
|
|
1738
|
+
let ascii = "";
|
|
1739
|
+
for (y = 0; y < size; y += 2) {
|
|
1740
|
+
r1 = Math.floor((y - min) / cellSize);
|
|
1741
|
+
r2 = Math.floor((y + 1 - min) / cellSize);
|
|
1742
|
+
for (x = 0; x < size; x += 1) {
|
|
1743
|
+
p = "█";
|
|
1744
|
+
if (min <= x && x < max && min <= y && y < max && _this.isDark(r1, Math.floor((x - min) / cellSize))) p = " ";
|
|
1745
|
+
if (min <= x && x < max && min <= y + 1 && y + 1 < max && _this.isDark(r2, Math.floor((x - min) / cellSize))) p += " ";
|
|
1746
|
+
else p += "█";
|
|
1747
|
+
ascii += margin < 1 && y + 1 >= max ? blocksLastLineNoMargin[p] : blocks[p];
|
|
1748
|
+
}
|
|
1749
|
+
ascii += "\n";
|
|
1750
|
+
}
|
|
1751
|
+
if (size % 2 && margin > 0) return ascii.substring(0, ascii.length - size - 1) + Array(size + 1).join("▀");
|
|
1752
|
+
return ascii.substring(0, ascii.length - 1);
|
|
1753
|
+
};
|
|
1754
|
+
_this.createASCII = function(cellSize, margin) {
|
|
1755
|
+
cellSize = cellSize || 1;
|
|
1756
|
+
if (cellSize < 2) return _createHalfASCII(margin);
|
|
1757
|
+
cellSize -= 1;
|
|
1758
|
+
margin = typeof margin == "undefined" ? cellSize * 2 : margin;
|
|
1759
|
+
const size = _this.getModuleCount() * cellSize + margin * 2;
|
|
1760
|
+
const min = margin;
|
|
1761
|
+
const max = size - margin;
|
|
1762
|
+
let y, x, r, p;
|
|
1763
|
+
const white = Array(cellSize + 1).join("██");
|
|
1764
|
+
const black = Array(cellSize + 1).join(" ");
|
|
1765
|
+
let ascii = "";
|
|
1766
|
+
let line = "";
|
|
1767
|
+
for (y = 0; y < size; y += 1) {
|
|
1768
|
+
r = Math.floor((y - min) / cellSize);
|
|
1769
|
+
line = "";
|
|
1770
|
+
for (x = 0; x < size; x += 1) {
|
|
1771
|
+
p = 1;
|
|
1772
|
+
if (min <= x && x < max && min <= y && y < max && _this.isDark(r, Math.floor((x - min) / cellSize))) p = 0;
|
|
1773
|
+
line += p ? white : black;
|
|
1774
|
+
}
|
|
1775
|
+
for (r = 0; r < cellSize; r += 1) ascii += line + "\n";
|
|
1776
|
+
}
|
|
1777
|
+
return ascii.substring(0, ascii.length - 1);
|
|
1778
|
+
};
|
|
1779
|
+
_this.renderTo2dContext = function(context, cellSize) {
|
|
1780
|
+
cellSize = cellSize || 2;
|
|
1781
|
+
const length = _this.getModuleCount();
|
|
1782
|
+
for (let row = 0; row < length; row++) for (let col = 0; col < length; col++) {
|
|
1783
|
+
context.fillStyle = _this.isDark(row, col) ? "black" : "white";
|
|
1784
|
+
context.fillRect(col * cellSize, row * cellSize, cellSize, cellSize);
|
|
1785
|
+
}
|
|
1786
|
+
};
|
|
1787
|
+
return _this;
|
|
1788
|
+
};
|
|
1789
|
+
qrcode.stringToBytes = function(s) {
|
|
1790
|
+
const bytes = [];
|
|
1791
|
+
for (let i = 0; i < s.length; i += 1) {
|
|
1792
|
+
const c = s.charCodeAt(i);
|
|
1793
|
+
bytes.push(c & 255);
|
|
1794
|
+
}
|
|
1795
|
+
return bytes;
|
|
1796
|
+
};
|
|
1797
|
+
/**
|
|
1798
|
+
* @param unicodeData base64 string of byte array.
|
|
1799
|
+
* [16bit Unicode],[16bit Bytes], ...
|
|
1800
|
+
* @param numChars
|
|
1801
|
+
*/
|
|
1802
|
+
qrcode.createStringToBytes = function(unicodeData, numChars) {
|
|
1803
|
+
const unicodeMap = function() {
|
|
1804
|
+
const bin = base64DecodeInputStream(unicodeData);
|
|
1805
|
+
const read = function() {
|
|
1806
|
+
const b = bin.read();
|
|
1807
|
+
if (b == -1) throw "eof";
|
|
1808
|
+
return b;
|
|
1809
|
+
};
|
|
1810
|
+
let count = 0;
|
|
1811
|
+
const unicodeMap = {};
|
|
1812
|
+
while (true) {
|
|
1813
|
+
const b0 = bin.read();
|
|
1814
|
+
if (b0 == -1) break;
|
|
1815
|
+
const b1 = read();
|
|
1816
|
+
const b2 = read();
|
|
1817
|
+
const b3 = read();
|
|
1818
|
+
const k = String.fromCharCode(b0 << 8 | b1);
|
|
1819
|
+
unicodeMap[k] = b2 << 8 | b3;
|
|
1820
|
+
count += 1;
|
|
1821
|
+
}
|
|
1822
|
+
if (count != numChars) throw count + " != " + numChars;
|
|
1823
|
+
return unicodeMap;
|
|
1824
|
+
}();
|
|
1825
|
+
const unknownChar = "?".charCodeAt(0);
|
|
1826
|
+
return function(s) {
|
|
1827
|
+
const bytes = [];
|
|
1828
|
+
for (let i = 0; i < s.length; i += 1) {
|
|
1829
|
+
const c = s.charCodeAt(i);
|
|
1830
|
+
if (c < 128) bytes.push(c);
|
|
1831
|
+
else {
|
|
1832
|
+
const b = unicodeMap[s.charAt(i)];
|
|
1833
|
+
if (typeof b == "number") if ((b & 255) == b) bytes.push(b);
|
|
1834
|
+
else {
|
|
1835
|
+
bytes.push(b >>> 8);
|
|
1836
|
+
bytes.push(b & 255);
|
|
1837
|
+
}
|
|
1838
|
+
else bytes.push(unknownChar);
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
return bytes;
|
|
1842
|
+
};
|
|
1843
|
+
};
|
|
1844
|
+
const QRMode = {
|
|
1845
|
+
MODE_NUMBER: 1,
|
|
1846
|
+
MODE_ALPHA_NUM: 2,
|
|
1847
|
+
MODE_8BIT_BYTE: 4,
|
|
1848
|
+
MODE_KANJI: 8
|
|
1849
|
+
};
|
|
1850
|
+
const QRErrorCorrectionLevel = {
|
|
1851
|
+
L: 1,
|
|
1852
|
+
M: 0,
|
|
1853
|
+
Q: 3,
|
|
1854
|
+
H: 2
|
|
1855
|
+
};
|
|
1856
|
+
const QRMaskPattern = {
|
|
1857
|
+
PATTERN000: 0,
|
|
1858
|
+
PATTERN001: 1,
|
|
1859
|
+
PATTERN010: 2,
|
|
1860
|
+
PATTERN011: 3,
|
|
1861
|
+
PATTERN100: 4,
|
|
1862
|
+
PATTERN101: 5,
|
|
1863
|
+
PATTERN110: 6,
|
|
1864
|
+
PATTERN111: 7
|
|
1865
|
+
};
|
|
1866
|
+
const QRUtil = function() {
|
|
1867
|
+
const PATTERN_POSITION_TABLE = [
|
|
1868
|
+
[],
|
|
1869
|
+
[6, 18],
|
|
1870
|
+
[6, 22],
|
|
1871
|
+
[6, 26],
|
|
1872
|
+
[6, 30],
|
|
1873
|
+
[6, 34],
|
|
1874
|
+
[
|
|
1875
|
+
6,
|
|
1876
|
+
22,
|
|
1877
|
+
38
|
|
1878
|
+
],
|
|
1879
|
+
[
|
|
1880
|
+
6,
|
|
1881
|
+
24,
|
|
1882
|
+
42
|
|
1883
|
+
],
|
|
1884
|
+
[
|
|
1885
|
+
6,
|
|
1886
|
+
26,
|
|
1887
|
+
46
|
|
1888
|
+
],
|
|
1889
|
+
[
|
|
1890
|
+
6,
|
|
1891
|
+
28,
|
|
1892
|
+
50
|
|
1893
|
+
],
|
|
1894
|
+
[
|
|
1895
|
+
6,
|
|
1896
|
+
30,
|
|
1897
|
+
54
|
|
1898
|
+
],
|
|
1899
|
+
[
|
|
1900
|
+
6,
|
|
1901
|
+
32,
|
|
1902
|
+
58
|
|
1903
|
+
],
|
|
1904
|
+
[
|
|
1905
|
+
6,
|
|
1906
|
+
34,
|
|
1907
|
+
62
|
|
1908
|
+
],
|
|
1909
|
+
[
|
|
1910
|
+
6,
|
|
1911
|
+
26,
|
|
1912
|
+
46,
|
|
1913
|
+
66
|
|
1914
|
+
],
|
|
1915
|
+
[
|
|
1916
|
+
6,
|
|
1917
|
+
26,
|
|
1918
|
+
48,
|
|
1919
|
+
70
|
|
1920
|
+
],
|
|
1921
|
+
[
|
|
1922
|
+
6,
|
|
1923
|
+
26,
|
|
1924
|
+
50,
|
|
1925
|
+
74
|
|
1926
|
+
],
|
|
1927
|
+
[
|
|
1928
|
+
6,
|
|
1929
|
+
30,
|
|
1930
|
+
54,
|
|
1931
|
+
78
|
|
1932
|
+
],
|
|
1933
|
+
[
|
|
1934
|
+
6,
|
|
1935
|
+
30,
|
|
1936
|
+
56,
|
|
1937
|
+
82
|
|
1938
|
+
],
|
|
1939
|
+
[
|
|
1940
|
+
6,
|
|
1941
|
+
30,
|
|
1942
|
+
58,
|
|
1943
|
+
86
|
|
1944
|
+
],
|
|
1945
|
+
[
|
|
1946
|
+
6,
|
|
1947
|
+
34,
|
|
1948
|
+
62,
|
|
1949
|
+
90
|
|
1950
|
+
],
|
|
1951
|
+
[
|
|
1952
|
+
6,
|
|
1953
|
+
28,
|
|
1954
|
+
50,
|
|
1955
|
+
72,
|
|
1956
|
+
94
|
|
1957
|
+
],
|
|
1958
|
+
[
|
|
1959
|
+
6,
|
|
1960
|
+
26,
|
|
1961
|
+
50,
|
|
1962
|
+
74,
|
|
1963
|
+
98
|
|
1964
|
+
],
|
|
1965
|
+
[
|
|
1966
|
+
6,
|
|
1967
|
+
30,
|
|
1968
|
+
54,
|
|
1969
|
+
78,
|
|
1970
|
+
102
|
|
1971
|
+
],
|
|
1972
|
+
[
|
|
1973
|
+
6,
|
|
1974
|
+
28,
|
|
1975
|
+
54,
|
|
1976
|
+
80,
|
|
1977
|
+
106
|
|
1978
|
+
],
|
|
1979
|
+
[
|
|
1980
|
+
6,
|
|
1981
|
+
32,
|
|
1982
|
+
58,
|
|
1983
|
+
84,
|
|
1984
|
+
110
|
|
1985
|
+
],
|
|
1986
|
+
[
|
|
1987
|
+
6,
|
|
1988
|
+
30,
|
|
1989
|
+
58,
|
|
1990
|
+
86,
|
|
1991
|
+
114
|
|
1992
|
+
],
|
|
1993
|
+
[
|
|
1994
|
+
6,
|
|
1995
|
+
34,
|
|
1996
|
+
62,
|
|
1997
|
+
90,
|
|
1998
|
+
118
|
|
1999
|
+
],
|
|
2000
|
+
[
|
|
2001
|
+
6,
|
|
2002
|
+
26,
|
|
2003
|
+
50,
|
|
2004
|
+
74,
|
|
2005
|
+
98,
|
|
2006
|
+
122
|
|
2007
|
+
],
|
|
2008
|
+
[
|
|
2009
|
+
6,
|
|
2010
|
+
30,
|
|
2011
|
+
54,
|
|
2012
|
+
78,
|
|
2013
|
+
102,
|
|
2014
|
+
126
|
|
2015
|
+
],
|
|
2016
|
+
[
|
|
2017
|
+
6,
|
|
2018
|
+
26,
|
|
2019
|
+
52,
|
|
2020
|
+
78,
|
|
2021
|
+
104,
|
|
2022
|
+
130
|
|
2023
|
+
],
|
|
2024
|
+
[
|
|
2025
|
+
6,
|
|
2026
|
+
30,
|
|
2027
|
+
56,
|
|
2028
|
+
82,
|
|
2029
|
+
108,
|
|
2030
|
+
134
|
|
2031
|
+
],
|
|
2032
|
+
[
|
|
2033
|
+
6,
|
|
2034
|
+
34,
|
|
2035
|
+
60,
|
|
2036
|
+
86,
|
|
2037
|
+
112,
|
|
2038
|
+
138
|
|
2039
|
+
],
|
|
2040
|
+
[
|
|
2041
|
+
6,
|
|
2042
|
+
30,
|
|
2043
|
+
58,
|
|
2044
|
+
86,
|
|
2045
|
+
114,
|
|
2046
|
+
142
|
|
2047
|
+
],
|
|
2048
|
+
[
|
|
2049
|
+
6,
|
|
2050
|
+
34,
|
|
2051
|
+
62,
|
|
2052
|
+
90,
|
|
2053
|
+
118,
|
|
2054
|
+
146
|
|
2055
|
+
],
|
|
2056
|
+
[
|
|
2057
|
+
6,
|
|
2058
|
+
30,
|
|
2059
|
+
54,
|
|
2060
|
+
78,
|
|
2061
|
+
102,
|
|
2062
|
+
126,
|
|
2063
|
+
150
|
|
2064
|
+
],
|
|
2065
|
+
[
|
|
2066
|
+
6,
|
|
2067
|
+
24,
|
|
2068
|
+
50,
|
|
2069
|
+
76,
|
|
2070
|
+
102,
|
|
2071
|
+
128,
|
|
2072
|
+
154
|
|
2073
|
+
],
|
|
2074
|
+
[
|
|
2075
|
+
6,
|
|
2076
|
+
28,
|
|
2077
|
+
54,
|
|
2078
|
+
80,
|
|
2079
|
+
106,
|
|
2080
|
+
132,
|
|
2081
|
+
158
|
|
2082
|
+
],
|
|
2083
|
+
[
|
|
2084
|
+
6,
|
|
2085
|
+
32,
|
|
2086
|
+
58,
|
|
2087
|
+
84,
|
|
2088
|
+
110,
|
|
2089
|
+
136,
|
|
2090
|
+
162
|
|
2091
|
+
],
|
|
2092
|
+
[
|
|
2093
|
+
6,
|
|
2094
|
+
26,
|
|
2095
|
+
54,
|
|
2096
|
+
82,
|
|
2097
|
+
110,
|
|
2098
|
+
138,
|
|
2099
|
+
166
|
|
2100
|
+
],
|
|
2101
|
+
[
|
|
2102
|
+
6,
|
|
2103
|
+
30,
|
|
2104
|
+
58,
|
|
2105
|
+
86,
|
|
2106
|
+
114,
|
|
2107
|
+
142,
|
|
2108
|
+
170
|
|
2109
|
+
]
|
|
2110
|
+
];
|
|
2111
|
+
const G15 = 1335;
|
|
2112
|
+
const G18 = 7973;
|
|
2113
|
+
const G15_MASK = 21522;
|
|
2114
|
+
const _this = {};
|
|
2115
|
+
const getBCHDigit = function(data) {
|
|
2116
|
+
let digit = 0;
|
|
2117
|
+
while (data != 0) {
|
|
2118
|
+
digit += 1;
|
|
2119
|
+
data >>>= 1;
|
|
2120
|
+
}
|
|
2121
|
+
return digit;
|
|
2122
|
+
};
|
|
2123
|
+
_this.getBCHTypeInfo = function(data) {
|
|
2124
|
+
let d = data << 10;
|
|
2125
|
+
while (getBCHDigit(d) - getBCHDigit(G15) >= 0) d ^= G15 << getBCHDigit(d) - getBCHDigit(G15);
|
|
2126
|
+
return (data << 10 | d) ^ G15_MASK;
|
|
2127
|
+
};
|
|
2128
|
+
_this.getBCHTypeNumber = function(data) {
|
|
2129
|
+
let d = data << 12;
|
|
2130
|
+
while (getBCHDigit(d) - getBCHDigit(G18) >= 0) d ^= G18 << getBCHDigit(d) - getBCHDigit(G18);
|
|
2131
|
+
return data << 12 | d;
|
|
2132
|
+
};
|
|
2133
|
+
_this.getPatternPosition = function(typeNumber) {
|
|
2134
|
+
return PATTERN_POSITION_TABLE[typeNumber - 1];
|
|
2135
|
+
};
|
|
2136
|
+
_this.getMaskFunction = function(maskPattern) {
|
|
2137
|
+
switch (maskPattern) {
|
|
2138
|
+
case QRMaskPattern.PATTERN000: return function(i, j) {
|
|
2139
|
+
return (i + j) % 2 == 0;
|
|
2140
|
+
};
|
|
2141
|
+
case QRMaskPattern.PATTERN001: return function(i, j) {
|
|
2142
|
+
return i % 2 == 0;
|
|
2143
|
+
};
|
|
2144
|
+
case QRMaskPattern.PATTERN010: return function(i, j) {
|
|
2145
|
+
return j % 3 == 0;
|
|
2146
|
+
};
|
|
2147
|
+
case QRMaskPattern.PATTERN011: return function(i, j) {
|
|
2148
|
+
return (i + j) % 3 == 0;
|
|
2149
|
+
};
|
|
2150
|
+
case QRMaskPattern.PATTERN100: return function(i, j) {
|
|
2151
|
+
return (Math.floor(i / 2) + Math.floor(j / 3)) % 2 == 0;
|
|
2152
|
+
};
|
|
2153
|
+
case QRMaskPattern.PATTERN101: return function(i, j) {
|
|
2154
|
+
return i * j % 2 + i * j % 3 == 0;
|
|
2155
|
+
};
|
|
2156
|
+
case QRMaskPattern.PATTERN110: return function(i, j) {
|
|
2157
|
+
return (i * j % 2 + i * j % 3) % 2 == 0;
|
|
2158
|
+
};
|
|
2159
|
+
case QRMaskPattern.PATTERN111: return function(i, j) {
|
|
2160
|
+
return (i * j % 3 + (i + j) % 2) % 2 == 0;
|
|
2161
|
+
};
|
|
2162
|
+
default: throw "bad maskPattern:" + maskPattern;
|
|
2163
|
+
}
|
|
2164
|
+
};
|
|
2165
|
+
_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
|
|
2166
|
+
let a = qrPolynomial([1], 0);
|
|
2167
|
+
for (let i = 0; i < errorCorrectLength; i += 1) a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0));
|
|
2168
|
+
return a;
|
|
2169
|
+
};
|
|
2170
|
+
_this.getLengthInBits = function(mode, type) {
|
|
2171
|
+
if (1 <= type && type < 10) switch (mode) {
|
|
2172
|
+
case QRMode.MODE_NUMBER: return 10;
|
|
2173
|
+
case QRMode.MODE_ALPHA_NUM: return 9;
|
|
2174
|
+
case QRMode.MODE_8BIT_BYTE: return 8;
|
|
2175
|
+
case QRMode.MODE_KANJI: return 8;
|
|
2176
|
+
default: throw "mode:" + mode;
|
|
2177
|
+
}
|
|
2178
|
+
else if (type < 27) switch (mode) {
|
|
2179
|
+
case QRMode.MODE_NUMBER: return 12;
|
|
2180
|
+
case QRMode.MODE_ALPHA_NUM: return 11;
|
|
2181
|
+
case QRMode.MODE_8BIT_BYTE: return 16;
|
|
2182
|
+
case QRMode.MODE_KANJI: return 10;
|
|
2183
|
+
default: throw "mode:" + mode;
|
|
2184
|
+
}
|
|
2185
|
+
else if (type < 41) switch (mode) {
|
|
2186
|
+
case QRMode.MODE_NUMBER: return 14;
|
|
2187
|
+
case QRMode.MODE_ALPHA_NUM: return 13;
|
|
2188
|
+
case QRMode.MODE_8BIT_BYTE: return 16;
|
|
2189
|
+
case QRMode.MODE_KANJI: return 12;
|
|
2190
|
+
default: throw "mode:" + mode;
|
|
2191
|
+
}
|
|
2192
|
+
else throw "type:" + type;
|
|
2193
|
+
};
|
|
2194
|
+
_this.getLostPoint = function(qrcode) {
|
|
2195
|
+
const moduleCount = qrcode.getModuleCount();
|
|
2196
|
+
let lostPoint = 0;
|
|
2197
|
+
for (let row = 0; row < moduleCount; row += 1) for (let col = 0; col < moduleCount; col += 1) {
|
|
2198
|
+
let sameCount = 0;
|
|
2199
|
+
const dark = qrcode.isDark(row, col);
|
|
2200
|
+
for (let r = -1; r <= 1; r += 1) {
|
|
2201
|
+
if (row + r < 0 || moduleCount <= row + r) continue;
|
|
2202
|
+
for (let c = -1; c <= 1; c += 1) {
|
|
2203
|
+
if (col + c < 0 || moduleCount <= col + c) continue;
|
|
2204
|
+
if (r == 0 && c == 0) continue;
|
|
2205
|
+
if (dark == qrcode.isDark(row + r, col + c)) sameCount += 1;
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
if (sameCount > 5) lostPoint += 3 + sameCount - 5;
|
|
2209
|
+
}
|
|
2210
|
+
for (let row = 0; row < moduleCount - 1; row += 1) for (let col = 0; col < moduleCount - 1; col += 1) {
|
|
2211
|
+
let count = 0;
|
|
2212
|
+
if (qrcode.isDark(row, col)) count += 1;
|
|
2213
|
+
if (qrcode.isDark(row + 1, col)) count += 1;
|
|
2214
|
+
if (qrcode.isDark(row, col + 1)) count += 1;
|
|
2215
|
+
if (qrcode.isDark(row + 1, col + 1)) count += 1;
|
|
2216
|
+
if (count == 0 || count == 4) lostPoint += 3;
|
|
2217
|
+
}
|
|
2218
|
+
for (let row = 0; row < moduleCount; row += 1) for (let col = 0; col < moduleCount - 6; col += 1) if (qrcode.isDark(row, col) && !qrcode.isDark(row, col + 1) && qrcode.isDark(row, col + 2) && qrcode.isDark(row, col + 3) && qrcode.isDark(row, col + 4) && !qrcode.isDark(row, col + 5) && qrcode.isDark(row, col + 6)) lostPoint += 40;
|
|
2219
|
+
for (let col = 0; col < moduleCount; col += 1) for (let row = 0; row < moduleCount - 6; row += 1) if (qrcode.isDark(row, col) && !qrcode.isDark(row + 1, col) && qrcode.isDark(row + 2, col) && qrcode.isDark(row + 3, col) && qrcode.isDark(row + 4, col) && !qrcode.isDark(row + 5, col) && qrcode.isDark(row + 6, col)) lostPoint += 40;
|
|
2220
|
+
let darkCount = 0;
|
|
2221
|
+
for (let col = 0; col < moduleCount; col += 1) for (let row = 0; row < moduleCount; row += 1) if (qrcode.isDark(row, col)) darkCount += 1;
|
|
2222
|
+
const ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
|
|
2223
|
+
lostPoint += ratio * 10;
|
|
2224
|
+
return lostPoint;
|
|
2225
|
+
};
|
|
2226
|
+
return _this;
|
|
2227
|
+
}();
|
|
2228
|
+
const QRMath = function() {
|
|
2229
|
+
const EXP_TABLE = new Array(256);
|
|
2230
|
+
const LOG_TABLE = new Array(256);
|
|
2231
|
+
for (let i = 0; i < 8; i += 1) EXP_TABLE[i] = 1 << i;
|
|
2232
|
+
for (let i = 8; i < 256; i += 1) EXP_TABLE[i] = EXP_TABLE[i - 4] ^ EXP_TABLE[i - 5] ^ EXP_TABLE[i - 6] ^ EXP_TABLE[i - 8];
|
|
2233
|
+
for (let i = 0; i < 255; i += 1) LOG_TABLE[EXP_TABLE[i]] = i;
|
|
2234
|
+
const _this = {};
|
|
2235
|
+
_this.glog = function(n) {
|
|
2236
|
+
if (n < 1) throw "glog(" + n + ")";
|
|
2237
|
+
return LOG_TABLE[n];
|
|
2238
|
+
};
|
|
2239
|
+
_this.gexp = function(n) {
|
|
2240
|
+
while (n < 0) n += 255;
|
|
2241
|
+
while (n >= 256) n -= 255;
|
|
2242
|
+
return EXP_TABLE[n];
|
|
2243
|
+
};
|
|
2244
|
+
return _this;
|
|
2245
|
+
}();
|
|
2246
|
+
const qrPolynomial = function(num, shift) {
|
|
2247
|
+
if (typeof num.length == "undefined") throw num.length + "/" + shift;
|
|
2248
|
+
const _num = function() {
|
|
2249
|
+
let offset = 0;
|
|
2250
|
+
while (offset < num.length && num[offset] == 0) offset += 1;
|
|
2251
|
+
const _num = new Array(num.length - offset + shift);
|
|
2252
|
+
for (let i = 0; i < num.length - offset; i += 1) _num[i] = num[i + offset];
|
|
2253
|
+
return _num;
|
|
2254
|
+
}();
|
|
2255
|
+
const _this = {};
|
|
2256
|
+
_this.getAt = function(index) {
|
|
2257
|
+
return _num[index];
|
|
2258
|
+
};
|
|
2259
|
+
_this.getLength = function() {
|
|
2260
|
+
return _num.length;
|
|
2261
|
+
};
|
|
2262
|
+
_this.multiply = function(e) {
|
|
2263
|
+
const num = new Array(_this.getLength() + e.getLength() - 1);
|
|
2264
|
+
for (let i = 0; i < _this.getLength(); i += 1) for (let j = 0; j < e.getLength(); j += 1) num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i)) + QRMath.glog(e.getAt(j)));
|
|
2265
|
+
return qrPolynomial(num, 0);
|
|
2266
|
+
};
|
|
2267
|
+
_this.mod = function(e) {
|
|
2268
|
+
if (_this.getLength() - e.getLength() < 0) return _this;
|
|
2269
|
+
const ratio = QRMath.glog(_this.getAt(0)) - QRMath.glog(e.getAt(0));
|
|
2270
|
+
const num = new Array(_this.getLength());
|
|
2271
|
+
for (let i = 0; i < _this.getLength(); i += 1) num[i] = _this.getAt(i);
|
|
2272
|
+
for (let i = 0; i < e.getLength(); i += 1) num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i)) + ratio);
|
|
2273
|
+
return qrPolynomial(num, 0).mod(e);
|
|
2274
|
+
};
|
|
2275
|
+
return _this;
|
|
2276
|
+
};
|
|
2277
|
+
const QRRSBlock = function() {
|
|
2278
|
+
const RS_BLOCK_TABLE = [
|
|
2279
|
+
[
|
|
2280
|
+
1,
|
|
2281
|
+
26,
|
|
2282
|
+
19
|
|
2283
|
+
],
|
|
2284
|
+
[
|
|
2285
|
+
1,
|
|
2286
|
+
26,
|
|
2287
|
+
16
|
|
2288
|
+
],
|
|
2289
|
+
[
|
|
2290
|
+
1,
|
|
2291
|
+
26,
|
|
2292
|
+
13
|
|
2293
|
+
],
|
|
2294
|
+
[
|
|
2295
|
+
1,
|
|
2296
|
+
26,
|
|
2297
|
+
9
|
|
2298
|
+
],
|
|
2299
|
+
[
|
|
2300
|
+
1,
|
|
2301
|
+
44,
|
|
2302
|
+
34
|
|
2303
|
+
],
|
|
2304
|
+
[
|
|
2305
|
+
1,
|
|
2306
|
+
44,
|
|
2307
|
+
28
|
|
2308
|
+
],
|
|
2309
|
+
[
|
|
2310
|
+
1,
|
|
2311
|
+
44,
|
|
2312
|
+
22
|
|
2313
|
+
],
|
|
2314
|
+
[
|
|
2315
|
+
1,
|
|
2316
|
+
44,
|
|
2317
|
+
16
|
|
2318
|
+
],
|
|
2319
|
+
[
|
|
2320
|
+
1,
|
|
2321
|
+
70,
|
|
2322
|
+
55
|
|
2323
|
+
],
|
|
2324
|
+
[
|
|
2325
|
+
1,
|
|
2326
|
+
70,
|
|
2327
|
+
44
|
|
2328
|
+
],
|
|
2329
|
+
[
|
|
2330
|
+
2,
|
|
2331
|
+
35,
|
|
2332
|
+
17
|
|
2333
|
+
],
|
|
2334
|
+
[
|
|
2335
|
+
2,
|
|
2336
|
+
35,
|
|
2337
|
+
13
|
|
2338
|
+
],
|
|
2339
|
+
[
|
|
2340
|
+
1,
|
|
2341
|
+
100,
|
|
2342
|
+
80
|
|
2343
|
+
],
|
|
2344
|
+
[
|
|
2345
|
+
2,
|
|
2346
|
+
50,
|
|
2347
|
+
32
|
|
2348
|
+
],
|
|
2349
|
+
[
|
|
2350
|
+
2,
|
|
2351
|
+
50,
|
|
2352
|
+
24
|
|
2353
|
+
],
|
|
2354
|
+
[
|
|
2355
|
+
4,
|
|
2356
|
+
25,
|
|
2357
|
+
9
|
|
2358
|
+
],
|
|
2359
|
+
[
|
|
2360
|
+
1,
|
|
2361
|
+
134,
|
|
2362
|
+
108
|
|
2363
|
+
],
|
|
2364
|
+
[
|
|
2365
|
+
2,
|
|
2366
|
+
67,
|
|
2367
|
+
43
|
|
2368
|
+
],
|
|
2369
|
+
[
|
|
2370
|
+
2,
|
|
2371
|
+
33,
|
|
2372
|
+
15,
|
|
2373
|
+
2,
|
|
2374
|
+
34,
|
|
2375
|
+
16
|
|
2376
|
+
],
|
|
2377
|
+
[
|
|
2378
|
+
2,
|
|
2379
|
+
33,
|
|
2380
|
+
11,
|
|
2381
|
+
2,
|
|
2382
|
+
34,
|
|
2383
|
+
12
|
|
2384
|
+
],
|
|
2385
|
+
[
|
|
2386
|
+
2,
|
|
2387
|
+
86,
|
|
2388
|
+
68
|
|
2389
|
+
],
|
|
2390
|
+
[
|
|
2391
|
+
4,
|
|
2392
|
+
43,
|
|
2393
|
+
27
|
|
2394
|
+
],
|
|
2395
|
+
[
|
|
2396
|
+
4,
|
|
2397
|
+
43,
|
|
2398
|
+
19
|
|
2399
|
+
],
|
|
2400
|
+
[
|
|
2401
|
+
4,
|
|
2402
|
+
43,
|
|
2403
|
+
15
|
|
2404
|
+
],
|
|
2405
|
+
[
|
|
2406
|
+
2,
|
|
2407
|
+
98,
|
|
2408
|
+
78
|
|
2409
|
+
],
|
|
2410
|
+
[
|
|
2411
|
+
4,
|
|
2412
|
+
49,
|
|
2413
|
+
31
|
|
2414
|
+
],
|
|
2415
|
+
[
|
|
2416
|
+
2,
|
|
2417
|
+
32,
|
|
2418
|
+
14,
|
|
2419
|
+
4,
|
|
2420
|
+
33,
|
|
2421
|
+
15
|
|
2422
|
+
],
|
|
2423
|
+
[
|
|
2424
|
+
4,
|
|
2425
|
+
39,
|
|
2426
|
+
13,
|
|
2427
|
+
1,
|
|
2428
|
+
40,
|
|
2429
|
+
14
|
|
2430
|
+
],
|
|
2431
|
+
[
|
|
2432
|
+
2,
|
|
2433
|
+
121,
|
|
2434
|
+
97
|
|
2435
|
+
],
|
|
2436
|
+
[
|
|
2437
|
+
2,
|
|
2438
|
+
60,
|
|
2439
|
+
38,
|
|
2440
|
+
2,
|
|
2441
|
+
61,
|
|
2442
|
+
39
|
|
2443
|
+
],
|
|
2444
|
+
[
|
|
2445
|
+
4,
|
|
2446
|
+
40,
|
|
2447
|
+
18,
|
|
2448
|
+
2,
|
|
2449
|
+
41,
|
|
2450
|
+
19
|
|
2451
|
+
],
|
|
2452
|
+
[
|
|
2453
|
+
4,
|
|
2454
|
+
40,
|
|
2455
|
+
14,
|
|
2456
|
+
2,
|
|
2457
|
+
41,
|
|
2458
|
+
15
|
|
2459
|
+
],
|
|
2460
|
+
[
|
|
2461
|
+
2,
|
|
2462
|
+
146,
|
|
2463
|
+
116
|
|
2464
|
+
],
|
|
2465
|
+
[
|
|
2466
|
+
3,
|
|
2467
|
+
58,
|
|
2468
|
+
36,
|
|
2469
|
+
2,
|
|
2470
|
+
59,
|
|
2471
|
+
37
|
|
2472
|
+
],
|
|
2473
|
+
[
|
|
2474
|
+
4,
|
|
2475
|
+
36,
|
|
2476
|
+
16,
|
|
2477
|
+
4,
|
|
2478
|
+
37,
|
|
2479
|
+
17
|
|
2480
|
+
],
|
|
2481
|
+
[
|
|
2482
|
+
4,
|
|
2483
|
+
36,
|
|
2484
|
+
12,
|
|
2485
|
+
4,
|
|
2486
|
+
37,
|
|
2487
|
+
13
|
|
2488
|
+
],
|
|
2489
|
+
[
|
|
2490
|
+
2,
|
|
2491
|
+
86,
|
|
2492
|
+
68,
|
|
2493
|
+
2,
|
|
2494
|
+
87,
|
|
2495
|
+
69
|
|
2496
|
+
],
|
|
2497
|
+
[
|
|
2498
|
+
4,
|
|
2499
|
+
69,
|
|
2500
|
+
43,
|
|
2501
|
+
1,
|
|
2502
|
+
70,
|
|
2503
|
+
44
|
|
2504
|
+
],
|
|
2505
|
+
[
|
|
2506
|
+
6,
|
|
2507
|
+
43,
|
|
2508
|
+
19,
|
|
2509
|
+
2,
|
|
2510
|
+
44,
|
|
2511
|
+
20
|
|
2512
|
+
],
|
|
2513
|
+
[
|
|
2514
|
+
6,
|
|
2515
|
+
43,
|
|
2516
|
+
15,
|
|
2517
|
+
2,
|
|
2518
|
+
44,
|
|
2519
|
+
16
|
|
2520
|
+
],
|
|
2521
|
+
[
|
|
2522
|
+
4,
|
|
2523
|
+
101,
|
|
2524
|
+
81
|
|
2525
|
+
],
|
|
2526
|
+
[
|
|
2527
|
+
1,
|
|
2528
|
+
80,
|
|
2529
|
+
50,
|
|
2530
|
+
4,
|
|
2531
|
+
81,
|
|
2532
|
+
51
|
|
2533
|
+
],
|
|
2534
|
+
[
|
|
2535
|
+
4,
|
|
2536
|
+
50,
|
|
2537
|
+
22,
|
|
2538
|
+
4,
|
|
2539
|
+
51,
|
|
2540
|
+
23
|
|
2541
|
+
],
|
|
2542
|
+
[
|
|
2543
|
+
3,
|
|
2544
|
+
36,
|
|
2545
|
+
12,
|
|
2546
|
+
8,
|
|
2547
|
+
37,
|
|
2548
|
+
13
|
|
2549
|
+
],
|
|
2550
|
+
[
|
|
2551
|
+
2,
|
|
2552
|
+
116,
|
|
2553
|
+
92,
|
|
2554
|
+
2,
|
|
2555
|
+
117,
|
|
2556
|
+
93
|
|
2557
|
+
],
|
|
2558
|
+
[
|
|
2559
|
+
6,
|
|
2560
|
+
58,
|
|
2561
|
+
36,
|
|
2562
|
+
2,
|
|
2563
|
+
59,
|
|
2564
|
+
37
|
|
2565
|
+
],
|
|
2566
|
+
[
|
|
2567
|
+
4,
|
|
2568
|
+
46,
|
|
2569
|
+
20,
|
|
2570
|
+
6,
|
|
2571
|
+
47,
|
|
2572
|
+
21
|
|
2573
|
+
],
|
|
2574
|
+
[
|
|
2575
|
+
7,
|
|
2576
|
+
42,
|
|
2577
|
+
14,
|
|
2578
|
+
4,
|
|
2579
|
+
43,
|
|
2580
|
+
15
|
|
2581
|
+
],
|
|
2582
|
+
[
|
|
2583
|
+
4,
|
|
2584
|
+
133,
|
|
2585
|
+
107
|
|
2586
|
+
],
|
|
2587
|
+
[
|
|
2588
|
+
8,
|
|
2589
|
+
59,
|
|
2590
|
+
37,
|
|
2591
|
+
1,
|
|
2592
|
+
60,
|
|
2593
|
+
38
|
|
2594
|
+
],
|
|
2595
|
+
[
|
|
2596
|
+
8,
|
|
2597
|
+
44,
|
|
2598
|
+
20,
|
|
2599
|
+
4,
|
|
2600
|
+
45,
|
|
2601
|
+
21
|
|
2602
|
+
],
|
|
2603
|
+
[
|
|
2604
|
+
12,
|
|
2605
|
+
33,
|
|
2606
|
+
11,
|
|
2607
|
+
4,
|
|
2608
|
+
34,
|
|
2609
|
+
12
|
|
2610
|
+
],
|
|
2611
|
+
[
|
|
2612
|
+
3,
|
|
2613
|
+
145,
|
|
2614
|
+
115,
|
|
2615
|
+
1,
|
|
2616
|
+
146,
|
|
2617
|
+
116
|
|
2618
|
+
],
|
|
2619
|
+
[
|
|
2620
|
+
4,
|
|
2621
|
+
64,
|
|
2622
|
+
40,
|
|
2623
|
+
5,
|
|
2624
|
+
65,
|
|
2625
|
+
41
|
|
2626
|
+
],
|
|
2627
|
+
[
|
|
2628
|
+
11,
|
|
2629
|
+
36,
|
|
2630
|
+
16,
|
|
2631
|
+
5,
|
|
2632
|
+
37,
|
|
2633
|
+
17
|
|
2634
|
+
],
|
|
2635
|
+
[
|
|
2636
|
+
11,
|
|
2637
|
+
36,
|
|
2638
|
+
12,
|
|
2639
|
+
5,
|
|
2640
|
+
37,
|
|
2641
|
+
13
|
|
2642
|
+
],
|
|
2643
|
+
[
|
|
2644
|
+
5,
|
|
2645
|
+
109,
|
|
2646
|
+
87,
|
|
2647
|
+
1,
|
|
2648
|
+
110,
|
|
2649
|
+
88
|
|
2650
|
+
],
|
|
2651
|
+
[
|
|
2652
|
+
5,
|
|
2653
|
+
65,
|
|
2654
|
+
41,
|
|
2655
|
+
5,
|
|
2656
|
+
66,
|
|
2657
|
+
42
|
|
2658
|
+
],
|
|
2659
|
+
[
|
|
2660
|
+
5,
|
|
2661
|
+
54,
|
|
2662
|
+
24,
|
|
2663
|
+
7,
|
|
2664
|
+
55,
|
|
2665
|
+
25
|
|
2666
|
+
],
|
|
2667
|
+
[
|
|
2668
|
+
11,
|
|
2669
|
+
36,
|
|
2670
|
+
12,
|
|
2671
|
+
7,
|
|
2672
|
+
37,
|
|
2673
|
+
13
|
|
2674
|
+
],
|
|
2675
|
+
[
|
|
2676
|
+
5,
|
|
2677
|
+
122,
|
|
2678
|
+
98,
|
|
2679
|
+
1,
|
|
2680
|
+
123,
|
|
2681
|
+
99
|
|
2682
|
+
],
|
|
2683
|
+
[
|
|
2684
|
+
7,
|
|
2685
|
+
73,
|
|
2686
|
+
45,
|
|
2687
|
+
3,
|
|
2688
|
+
74,
|
|
2689
|
+
46
|
|
2690
|
+
],
|
|
2691
|
+
[
|
|
2692
|
+
15,
|
|
2693
|
+
43,
|
|
2694
|
+
19,
|
|
2695
|
+
2,
|
|
2696
|
+
44,
|
|
2697
|
+
20
|
|
2698
|
+
],
|
|
2699
|
+
[
|
|
2700
|
+
3,
|
|
2701
|
+
45,
|
|
2702
|
+
15,
|
|
2703
|
+
13,
|
|
2704
|
+
46,
|
|
2705
|
+
16
|
|
2706
|
+
],
|
|
2707
|
+
[
|
|
2708
|
+
1,
|
|
2709
|
+
135,
|
|
2710
|
+
107,
|
|
2711
|
+
5,
|
|
2712
|
+
136,
|
|
2713
|
+
108
|
|
2714
|
+
],
|
|
2715
|
+
[
|
|
2716
|
+
10,
|
|
2717
|
+
74,
|
|
2718
|
+
46,
|
|
2719
|
+
1,
|
|
2720
|
+
75,
|
|
2721
|
+
47
|
|
2722
|
+
],
|
|
2723
|
+
[
|
|
2724
|
+
1,
|
|
2725
|
+
50,
|
|
2726
|
+
22,
|
|
2727
|
+
15,
|
|
2728
|
+
51,
|
|
2729
|
+
23
|
|
2730
|
+
],
|
|
2731
|
+
[
|
|
2732
|
+
2,
|
|
2733
|
+
42,
|
|
2734
|
+
14,
|
|
2735
|
+
17,
|
|
2736
|
+
43,
|
|
2737
|
+
15
|
|
2738
|
+
],
|
|
2739
|
+
[
|
|
2740
|
+
5,
|
|
2741
|
+
150,
|
|
2742
|
+
120,
|
|
2743
|
+
1,
|
|
2744
|
+
151,
|
|
2745
|
+
121
|
|
2746
|
+
],
|
|
2747
|
+
[
|
|
2748
|
+
9,
|
|
2749
|
+
69,
|
|
2750
|
+
43,
|
|
2751
|
+
4,
|
|
2752
|
+
70,
|
|
2753
|
+
44
|
|
2754
|
+
],
|
|
2755
|
+
[
|
|
2756
|
+
17,
|
|
2757
|
+
50,
|
|
2758
|
+
22,
|
|
2759
|
+
1,
|
|
2760
|
+
51,
|
|
2761
|
+
23
|
|
2762
|
+
],
|
|
2763
|
+
[
|
|
2764
|
+
2,
|
|
2765
|
+
42,
|
|
2766
|
+
14,
|
|
2767
|
+
19,
|
|
2768
|
+
43,
|
|
2769
|
+
15
|
|
2770
|
+
],
|
|
2771
|
+
[
|
|
2772
|
+
3,
|
|
2773
|
+
141,
|
|
2774
|
+
113,
|
|
2775
|
+
4,
|
|
2776
|
+
142,
|
|
2777
|
+
114
|
|
2778
|
+
],
|
|
2779
|
+
[
|
|
2780
|
+
3,
|
|
2781
|
+
70,
|
|
2782
|
+
44,
|
|
2783
|
+
11,
|
|
2784
|
+
71,
|
|
2785
|
+
45
|
|
2786
|
+
],
|
|
2787
|
+
[
|
|
2788
|
+
17,
|
|
2789
|
+
47,
|
|
2790
|
+
21,
|
|
2791
|
+
4,
|
|
2792
|
+
48,
|
|
2793
|
+
22
|
|
2794
|
+
],
|
|
2795
|
+
[
|
|
2796
|
+
9,
|
|
2797
|
+
39,
|
|
2798
|
+
13,
|
|
2799
|
+
16,
|
|
2800
|
+
40,
|
|
2801
|
+
14
|
|
2802
|
+
],
|
|
2803
|
+
[
|
|
2804
|
+
3,
|
|
2805
|
+
135,
|
|
2806
|
+
107,
|
|
2807
|
+
5,
|
|
2808
|
+
136,
|
|
2809
|
+
108
|
|
2810
|
+
],
|
|
2811
|
+
[
|
|
2812
|
+
3,
|
|
2813
|
+
67,
|
|
2814
|
+
41,
|
|
2815
|
+
13,
|
|
2816
|
+
68,
|
|
2817
|
+
42
|
|
2818
|
+
],
|
|
2819
|
+
[
|
|
2820
|
+
15,
|
|
2821
|
+
54,
|
|
2822
|
+
24,
|
|
2823
|
+
5,
|
|
2824
|
+
55,
|
|
2825
|
+
25
|
|
2826
|
+
],
|
|
2827
|
+
[
|
|
2828
|
+
15,
|
|
2829
|
+
43,
|
|
2830
|
+
15,
|
|
2831
|
+
10,
|
|
2832
|
+
44,
|
|
2833
|
+
16
|
|
2834
|
+
],
|
|
2835
|
+
[
|
|
2836
|
+
4,
|
|
2837
|
+
144,
|
|
2838
|
+
116,
|
|
2839
|
+
4,
|
|
2840
|
+
145,
|
|
2841
|
+
117
|
|
2842
|
+
],
|
|
2843
|
+
[
|
|
2844
|
+
17,
|
|
2845
|
+
68,
|
|
2846
|
+
42
|
|
2847
|
+
],
|
|
2848
|
+
[
|
|
2849
|
+
17,
|
|
2850
|
+
50,
|
|
2851
|
+
22,
|
|
2852
|
+
6,
|
|
2853
|
+
51,
|
|
2854
|
+
23
|
|
2855
|
+
],
|
|
2856
|
+
[
|
|
2857
|
+
19,
|
|
2858
|
+
46,
|
|
2859
|
+
16,
|
|
2860
|
+
6,
|
|
2861
|
+
47,
|
|
2862
|
+
17
|
|
2863
|
+
],
|
|
2864
|
+
[
|
|
2865
|
+
2,
|
|
2866
|
+
139,
|
|
2867
|
+
111,
|
|
2868
|
+
7,
|
|
2869
|
+
140,
|
|
2870
|
+
112
|
|
2871
|
+
],
|
|
2872
|
+
[
|
|
2873
|
+
17,
|
|
2874
|
+
74,
|
|
2875
|
+
46
|
|
2876
|
+
],
|
|
2877
|
+
[
|
|
2878
|
+
7,
|
|
2879
|
+
54,
|
|
2880
|
+
24,
|
|
2881
|
+
16,
|
|
2882
|
+
55,
|
|
2883
|
+
25
|
|
2884
|
+
],
|
|
2885
|
+
[
|
|
2886
|
+
34,
|
|
2887
|
+
37,
|
|
2888
|
+
13
|
|
2889
|
+
],
|
|
2890
|
+
[
|
|
2891
|
+
4,
|
|
2892
|
+
151,
|
|
2893
|
+
121,
|
|
2894
|
+
5,
|
|
2895
|
+
152,
|
|
2896
|
+
122
|
|
2897
|
+
],
|
|
2898
|
+
[
|
|
2899
|
+
4,
|
|
2900
|
+
75,
|
|
2901
|
+
47,
|
|
2902
|
+
14,
|
|
2903
|
+
76,
|
|
2904
|
+
48
|
|
2905
|
+
],
|
|
2906
|
+
[
|
|
2907
|
+
11,
|
|
2908
|
+
54,
|
|
2909
|
+
24,
|
|
2910
|
+
14,
|
|
2911
|
+
55,
|
|
2912
|
+
25
|
|
2913
|
+
],
|
|
2914
|
+
[
|
|
2915
|
+
16,
|
|
2916
|
+
45,
|
|
2917
|
+
15,
|
|
2918
|
+
14,
|
|
2919
|
+
46,
|
|
2920
|
+
16
|
|
2921
|
+
],
|
|
2922
|
+
[
|
|
2923
|
+
6,
|
|
2924
|
+
147,
|
|
2925
|
+
117,
|
|
2926
|
+
4,
|
|
2927
|
+
148,
|
|
2928
|
+
118
|
|
2929
|
+
],
|
|
2930
|
+
[
|
|
2931
|
+
6,
|
|
2932
|
+
73,
|
|
2933
|
+
45,
|
|
2934
|
+
14,
|
|
2935
|
+
74,
|
|
2936
|
+
46
|
|
2937
|
+
],
|
|
2938
|
+
[
|
|
2939
|
+
11,
|
|
2940
|
+
54,
|
|
2941
|
+
24,
|
|
2942
|
+
16,
|
|
2943
|
+
55,
|
|
2944
|
+
25
|
|
2945
|
+
],
|
|
2946
|
+
[
|
|
2947
|
+
30,
|
|
2948
|
+
46,
|
|
2949
|
+
16,
|
|
2950
|
+
2,
|
|
2951
|
+
47,
|
|
2952
|
+
17
|
|
2953
|
+
],
|
|
2954
|
+
[
|
|
2955
|
+
8,
|
|
2956
|
+
132,
|
|
2957
|
+
106,
|
|
2958
|
+
4,
|
|
2959
|
+
133,
|
|
2960
|
+
107
|
|
2961
|
+
],
|
|
2962
|
+
[
|
|
2963
|
+
8,
|
|
2964
|
+
75,
|
|
2965
|
+
47,
|
|
2966
|
+
13,
|
|
2967
|
+
76,
|
|
2968
|
+
48
|
|
2969
|
+
],
|
|
2970
|
+
[
|
|
2971
|
+
7,
|
|
2972
|
+
54,
|
|
2973
|
+
24,
|
|
2974
|
+
22,
|
|
2975
|
+
55,
|
|
2976
|
+
25
|
|
2977
|
+
],
|
|
2978
|
+
[
|
|
2979
|
+
22,
|
|
2980
|
+
45,
|
|
2981
|
+
15,
|
|
2982
|
+
13,
|
|
2983
|
+
46,
|
|
2984
|
+
16
|
|
2985
|
+
],
|
|
2986
|
+
[
|
|
2987
|
+
10,
|
|
2988
|
+
142,
|
|
2989
|
+
114,
|
|
2990
|
+
2,
|
|
2991
|
+
143,
|
|
2992
|
+
115
|
|
2993
|
+
],
|
|
2994
|
+
[
|
|
2995
|
+
19,
|
|
2996
|
+
74,
|
|
2997
|
+
46,
|
|
2998
|
+
4,
|
|
2999
|
+
75,
|
|
3000
|
+
47
|
|
3001
|
+
],
|
|
3002
|
+
[
|
|
3003
|
+
28,
|
|
3004
|
+
50,
|
|
3005
|
+
22,
|
|
3006
|
+
6,
|
|
3007
|
+
51,
|
|
3008
|
+
23
|
|
3009
|
+
],
|
|
3010
|
+
[
|
|
3011
|
+
33,
|
|
3012
|
+
46,
|
|
3013
|
+
16,
|
|
3014
|
+
4,
|
|
3015
|
+
47,
|
|
3016
|
+
17
|
|
3017
|
+
],
|
|
3018
|
+
[
|
|
3019
|
+
8,
|
|
3020
|
+
152,
|
|
3021
|
+
122,
|
|
3022
|
+
4,
|
|
3023
|
+
153,
|
|
3024
|
+
123
|
|
3025
|
+
],
|
|
3026
|
+
[
|
|
3027
|
+
22,
|
|
3028
|
+
73,
|
|
3029
|
+
45,
|
|
3030
|
+
3,
|
|
3031
|
+
74,
|
|
3032
|
+
46
|
|
3033
|
+
],
|
|
3034
|
+
[
|
|
3035
|
+
8,
|
|
3036
|
+
53,
|
|
3037
|
+
23,
|
|
3038
|
+
26,
|
|
3039
|
+
54,
|
|
3040
|
+
24
|
|
3041
|
+
],
|
|
3042
|
+
[
|
|
3043
|
+
12,
|
|
3044
|
+
45,
|
|
3045
|
+
15,
|
|
3046
|
+
28,
|
|
3047
|
+
46,
|
|
3048
|
+
16
|
|
3049
|
+
],
|
|
3050
|
+
[
|
|
3051
|
+
3,
|
|
3052
|
+
147,
|
|
3053
|
+
117,
|
|
3054
|
+
10,
|
|
3055
|
+
148,
|
|
3056
|
+
118
|
|
3057
|
+
],
|
|
3058
|
+
[
|
|
3059
|
+
3,
|
|
3060
|
+
73,
|
|
3061
|
+
45,
|
|
3062
|
+
23,
|
|
3063
|
+
74,
|
|
3064
|
+
46
|
|
3065
|
+
],
|
|
3066
|
+
[
|
|
3067
|
+
4,
|
|
3068
|
+
54,
|
|
3069
|
+
24,
|
|
3070
|
+
31,
|
|
3071
|
+
55,
|
|
3072
|
+
25
|
|
3073
|
+
],
|
|
3074
|
+
[
|
|
3075
|
+
11,
|
|
3076
|
+
45,
|
|
3077
|
+
15,
|
|
3078
|
+
31,
|
|
3079
|
+
46,
|
|
3080
|
+
16
|
|
3081
|
+
],
|
|
3082
|
+
[
|
|
3083
|
+
7,
|
|
3084
|
+
146,
|
|
3085
|
+
116,
|
|
3086
|
+
7,
|
|
3087
|
+
147,
|
|
3088
|
+
117
|
|
3089
|
+
],
|
|
3090
|
+
[
|
|
3091
|
+
21,
|
|
3092
|
+
73,
|
|
3093
|
+
45,
|
|
3094
|
+
7,
|
|
3095
|
+
74,
|
|
3096
|
+
46
|
|
3097
|
+
],
|
|
3098
|
+
[
|
|
3099
|
+
1,
|
|
3100
|
+
53,
|
|
3101
|
+
23,
|
|
3102
|
+
37,
|
|
3103
|
+
54,
|
|
3104
|
+
24
|
|
3105
|
+
],
|
|
3106
|
+
[
|
|
3107
|
+
19,
|
|
3108
|
+
45,
|
|
3109
|
+
15,
|
|
3110
|
+
26,
|
|
3111
|
+
46,
|
|
3112
|
+
16
|
|
3113
|
+
],
|
|
3114
|
+
[
|
|
3115
|
+
5,
|
|
3116
|
+
145,
|
|
3117
|
+
115,
|
|
3118
|
+
10,
|
|
3119
|
+
146,
|
|
3120
|
+
116
|
|
3121
|
+
],
|
|
3122
|
+
[
|
|
3123
|
+
19,
|
|
3124
|
+
75,
|
|
3125
|
+
47,
|
|
3126
|
+
10,
|
|
3127
|
+
76,
|
|
3128
|
+
48
|
|
3129
|
+
],
|
|
3130
|
+
[
|
|
3131
|
+
15,
|
|
3132
|
+
54,
|
|
3133
|
+
24,
|
|
3134
|
+
25,
|
|
3135
|
+
55,
|
|
3136
|
+
25
|
|
3137
|
+
],
|
|
3138
|
+
[
|
|
3139
|
+
23,
|
|
3140
|
+
45,
|
|
3141
|
+
15,
|
|
3142
|
+
25,
|
|
3143
|
+
46,
|
|
3144
|
+
16
|
|
3145
|
+
],
|
|
3146
|
+
[
|
|
3147
|
+
13,
|
|
3148
|
+
145,
|
|
3149
|
+
115,
|
|
3150
|
+
3,
|
|
3151
|
+
146,
|
|
3152
|
+
116
|
|
3153
|
+
],
|
|
3154
|
+
[
|
|
3155
|
+
2,
|
|
3156
|
+
74,
|
|
3157
|
+
46,
|
|
3158
|
+
29,
|
|
3159
|
+
75,
|
|
3160
|
+
47
|
|
3161
|
+
],
|
|
3162
|
+
[
|
|
3163
|
+
42,
|
|
3164
|
+
54,
|
|
3165
|
+
24,
|
|
3166
|
+
1,
|
|
3167
|
+
55,
|
|
3168
|
+
25
|
|
3169
|
+
],
|
|
3170
|
+
[
|
|
3171
|
+
23,
|
|
3172
|
+
45,
|
|
3173
|
+
15,
|
|
3174
|
+
28,
|
|
3175
|
+
46,
|
|
3176
|
+
16
|
|
3177
|
+
],
|
|
3178
|
+
[
|
|
3179
|
+
17,
|
|
3180
|
+
145,
|
|
3181
|
+
115
|
|
3182
|
+
],
|
|
3183
|
+
[
|
|
3184
|
+
10,
|
|
3185
|
+
74,
|
|
3186
|
+
46,
|
|
3187
|
+
23,
|
|
3188
|
+
75,
|
|
3189
|
+
47
|
|
3190
|
+
],
|
|
3191
|
+
[
|
|
3192
|
+
10,
|
|
3193
|
+
54,
|
|
3194
|
+
24,
|
|
3195
|
+
35,
|
|
3196
|
+
55,
|
|
3197
|
+
25
|
|
3198
|
+
],
|
|
3199
|
+
[
|
|
3200
|
+
19,
|
|
3201
|
+
45,
|
|
3202
|
+
15,
|
|
3203
|
+
35,
|
|
3204
|
+
46,
|
|
3205
|
+
16
|
|
3206
|
+
],
|
|
3207
|
+
[
|
|
3208
|
+
17,
|
|
3209
|
+
145,
|
|
3210
|
+
115,
|
|
3211
|
+
1,
|
|
3212
|
+
146,
|
|
3213
|
+
116
|
|
3214
|
+
],
|
|
3215
|
+
[
|
|
3216
|
+
14,
|
|
3217
|
+
74,
|
|
3218
|
+
46,
|
|
3219
|
+
21,
|
|
3220
|
+
75,
|
|
3221
|
+
47
|
|
3222
|
+
],
|
|
3223
|
+
[
|
|
3224
|
+
29,
|
|
3225
|
+
54,
|
|
3226
|
+
24,
|
|
3227
|
+
19,
|
|
3228
|
+
55,
|
|
3229
|
+
25
|
|
3230
|
+
],
|
|
3231
|
+
[
|
|
3232
|
+
11,
|
|
3233
|
+
45,
|
|
3234
|
+
15,
|
|
3235
|
+
46,
|
|
3236
|
+
46,
|
|
3237
|
+
16
|
|
3238
|
+
],
|
|
3239
|
+
[
|
|
3240
|
+
13,
|
|
3241
|
+
145,
|
|
3242
|
+
115,
|
|
3243
|
+
6,
|
|
3244
|
+
146,
|
|
3245
|
+
116
|
|
3246
|
+
],
|
|
3247
|
+
[
|
|
3248
|
+
14,
|
|
3249
|
+
74,
|
|
3250
|
+
46,
|
|
3251
|
+
23,
|
|
3252
|
+
75,
|
|
3253
|
+
47
|
|
3254
|
+
],
|
|
3255
|
+
[
|
|
3256
|
+
44,
|
|
3257
|
+
54,
|
|
3258
|
+
24,
|
|
3259
|
+
7,
|
|
3260
|
+
55,
|
|
3261
|
+
25
|
|
3262
|
+
],
|
|
3263
|
+
[
|
|
3264
|
+
59,
|
|
3265
|
+
46,
|
|
3266
|
+
16,
|
|
3267
|
+
1,
|
|
3268
|
+
47,
|
|
3269
|
+
17
|
|
3270
|
+
],
|
|
3271
|
+
[
|
|
3272
|
+
12,
|
|
3273
|
+
151,
|
|
3274
|
+
121,
|
|
3275
|
+
7,
|
|
3276
|
+
152,
|
|
3277
|
+
122
|
|
3278
|
+
],
|
|
3279
|
+
[
|
|
3280
|
+
12,
|
|
3281
|
+
75,
|
|
3282
|
+
47,
|
|
3283
|
+
26,
|
|
3284
|
+
76,
|
|
3285
|
+
48
|
|
3286
|
+
],
|
|
3287
|
+
[
|
|
3288
|
+
39,
|
|
3289
|
+
54,
|
|
3290
|
+
24,
|
|
3291
|
+
14,
|
|
3292
|
+
55,
|
|
3293
|
+
25
|
|
3294
|
+
],
|
|
3295
|
+
[
|
|
3296
|
+
22,
|
|
3297
|
+
45,
|
|
3298
|
+
15,
|
|
3299
|
+
41,
|
|
3300
|
+
46,
|
|
3301
|
+
16
|
|
3302
|
+
],
|
|
3303
|
+
[
|
|
3304
|
+
6,
|
|
3305
|
+
151,
|
|
3306
|
+
121,
|
|
3307
|
+
14,
|
|
3308
|
+
152,
|
|
3309
|
+
122
|
|
3310
|
+
],
|
|
3311
|
+
[
|
|
3312
|
+
6,
|
|
3313
|
+
75,
|
|
3314
|
+
47,
|
|
3315
|
+
34,
|
|
3316
|
+
76,
|
|
3317
|
+
48
|
|
3318
|
+
],
|
|
3319
|
+
[
|
|
3320
|
+
46,
|
|
3321
|
+
54,
|
|
3322
|
+
24,
|
|
3323
|
+
10,
|
|
3324
|
+
55,
|
|
3325
|
+
25
|
|
3326
|
+
],
|
|
3327
|
+
[
|
|
3328
|
+
2,
|
|
3329
|
+
45,
|
|
3330
|
+
15,
|
|
3331
|
+
64,
|
|
3332
|
+
46,
|
|
3333
|
+
16
|
|
3334
|
+
],
|
|
3335
|
+
[
|
|
3336
|
+
17,
|
|
3337
|
+
152,
|
|
3338
|
+
122,
|
|
3339
|
+
4,
|
|
3340
|
+
153,
|
|
3341
|
+
123
|
|
3342
|
+
],
|
|
3343
|
+
[
|
|
3344
|
+
29,
|
|
3345
|
+
74,
|
|
3346
|
+
46,
|
|
3347
|
+
14,
|
|
3348
|
+
75,
|
|
3349
|
+
47
|
|
3350
|
+
],
|
|
3351
|
+
[
|
|
3352
|
+
49,
|
|
3353
|
+
54,
|
|
3354
|
+
24,
|
|
3355
|
+
10,
|
|
3356
|
+
55,
|
|
3357
|
+
25
|
|
3358
|
+
],
|
|
3359
|
+
[
|
|
3360
|
+
24,
|
|
3361
|
+
45,
|
|
3362
|
+
15,
|
|
3363
|
+
46,
|
|
3364
|
+
46,
|
|
3365
|
+
16
|
|
3366
|
+
],
|
|
3367
|
+
[
|
|
3368
|
+
4,
|
|
3369
|
+
152,
|
|
3370
|
+
122,
|
|
3371
|
+
18,
|
|
3372
|
+
153,
|
|
3373
|
+
123
|
|
3374
|
+
],
|
|
3375
|
+
[
|
|
3376
|
+
13,
|
|
3377
|
+
74,
|
|
3378
|
+
46,
|
|
3379
|
+
32,
|
|
3380
|
+
75,
|
|
3381
|
+
47
|
|
3382
|
+
],
|
|
3383
|
+
[
|
|
3384
|
+
48,
|
|
3385
|
+
54,
|
|
3386
|
+
24,
|
|
3387
|
+
14,
|
|
3388
|
+
55,
|
|
3389
|
+
25
|
|
3390
|
+
],
|
|
3391
|
+
[
|
|
3392
|
+
42,
|
|
3393
|
+
45,
|
|
3394
|
+
15,
|
|
3395
|
+
32,
|
|
3396
|
+
46,
|
|
3397
|
+
16
|
|
3398
|
+
],
|
|
3399
|
+
[
|
|
3400
|
+
20,
|
|
3401
|
+
147,
|
|
3402
|
+
117,
|
|
3403
|
+
4,
|
|
3404
|
+
148,
|
|
3405
|
+
118
|
|
3406
|
+
],
|
|
3407
|
+
[
|
|
3408
|
+
40,
|
|
3409
|
+
75,
|
|
3410
|
+
47,
|
|
3411
|
+
7,
|
|
3412
|
+
76,
|
|
3413
|
+
48
|
|
3414
|
+
],
|
|
3415
|
+
[
|
|
3416
|
+
43,
|
|
3417
|
+
54,
|
|
3418
|
+
24,
|
|
3419
|
+
22,
|
|
3420
|
+
55,
|
|
3421
|
+
25
|
|
3422
|
+
],
|
|
3423
|
+
[
|
|
3424
|
+
10,
|
|
3425
|
+
45,
|
|
3426
|
+
15,
|
|
3427
|
+
67,
|
|
3428
|
+
46,
|
|
3429
|
+
16
|
|
3430
|
+
],
|
|
3431
|
+
[
|
|
3432
|
+
19,
|
|
3433
|
+
148,
|
|
3434
|
+
118,
|
|
3435
|
+
6,
|
|
3436
|
+
149,
|
|
3437
|
+
119
|
|
3438
|
+
],
|
|
3439
|
+
[
|
|
3440
|
+
18,
|
|
3441
|
+
75,
|
|
3442
|
+
47,
|
|
3443
|
+
31,
|
|
3444
|
+
76,
|
|
3445
|
+
48
|
|
3446
|
+
],
|
|
3447
|
+
[
|
|
3448
|
+
34,
|
|
3449
|
+
54,
|
|
3450
|
+
24,
|
|
3451
|
+
34,
|
|
3452
|
+
55,
|
|
3453
|
+
25
|
|
3454
|
+
],
|
|
3455
|
+
[
|
|
3456
|
+
20,
|
|
3457
|
+
45,
|
|
3458
|
+
15,
|
|
3459
|
+
61,
|
|
3460
|
+
46,
|
|
3461
|
+
16
|
|
3462
|
+
]
|
|
3463
|
+
];
|
|
3464
|
+
const qrRSBlock = function(totalCount, dataCount) {
|
|
3465
|
+
const _this = {};
|
|
3466
|
+
_this.totalCount = totalCount;
|
|
3467
|
+
_this.dataCount = dataCount;
|
|
3468
|
+
return _this;
|
|
3469
|
+
};
|
|
3470
|
+
const _this = {};
|
|
3471
|
+
const getRsBlockTable = function(typeNumber, errorCorrectionLevel) {
|
|
3472
|
+
switch (errorCorrectionLevel) {
|
|
3473
|
+
case QRErrorCorrectionLevel.L: return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
|
|
3474
|
+
case QRErrorCorrectionLevel.M: return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
|
|
3475
|
+
case QRErrorCorrectionLevel.Q: return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
|
|
3476
|
+
case QRErrorCorrectionLevel.H: return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
|
|
3477
|
+
default: return;
|
|
3478
|
+
}
|
|
3479
|
+
};
|
|
3480
|
+
_this.getRSBlocks = function(typeNumber, errorCorrectionLevel) {
|
|
3481
|
+
const rsBlock = getRsBlockTable(typeNumber, errorCorrectionLevel);
|
|
3482
|
+
if (typeof rsBlock == "undefined") throw "bad rs block @ typeNumber:" + typeNumber + "/errorCorrectionLevel:" + errorCorrectionLevel;
|
|
3483
|
+
const length = rsBlock.length / 3;
|
|
3484
|
+
const list = [];
|
|
3485
|
+
for (let i = 0; i < length; i += 1) {
|
|
3486
|
+
const count = rsBlock[i * 3 + 0];
|
|
3487
|
+
const totalCount = rsBlock[i * 3 + 1];
|
|
3488
|
+
const dataCount = rsBlock[i * 3 + 2];
|
|
3489
|
+
for (let j = 0; j < count; j += 1) list.push(qrRSBlock(totalCount, dataCount));
|
|
3490
|
+
}
|
|
3491
|
+
return list;
|
|
3492
|
+
};
|
|
3493
|
+
return _this;
|
|
3494
|
+
}();
|
|
3495
|
+
const qrBitBuffer = function() {
|
|
3496
|
+
const _buffer = [];
|
|
3497
|
+
let _length = 0;
|
|
3498
|
+
const _this = {};
|
|
3499
|
+
_this.getBuffer = function() {
|
|
3500
|
+
return _buffer;
|
|
3501
|
+
};
|
|
3502
|
+
_this.getAt = function(index) {
|
|
3503
|
+
const bufIndex = Math.floor(index / 8);
|
|
3504
|
+
return (_buffer[bufIndex] >>> 7 - index % 8 & 1) == 1;
|
|
3505
|
+
};
|
|
3506
|
+
_this.put = function(num, length) {
|
|
3507
|
+
for (let i = 0; i < length; i += 1) _this.putBit((num >>> length - i - 1 & 1) == 1);
|
|
3508
|
+
};
|
|
3509
|
+
_this.getLengthInBits = function() {
|
|
3510
|
+
return _length;
|
|
3511
|
+
};
|
|
3512
|
+
_this.putBit = function(bit) {
|
|
3513
|
+
const bufIndex = Math.floor(_length / 8);
|
|
3514
|
+
if (_buffer.length <= bufIndex) _buffer.push(0);
|
|
3515
|
+
if (bit) _buffer[bufIndex] |= 128 >>> _length % 8;
|
|
3516
|
+
_length += 1;
|
|
3517
|
+
};
|
|
3518
|
+
return _this;
|
|
3519
|
+
};
|
|
3520
|
+
const qrNumber = function(data) {
|
|
3521
|
+
const _mode = QRMode.MODE_NUMBER;
|
|
3522
|
+
const _data = data;
|
|
3523
|
+
const _this = {};
|
|
3524
|
+
_this.getMode = function() {
|
|
3525
|
+
return _mode;
|
|
3526
|
+
};
|
|
3527
|
+
_this.getLength = function(buffer) {
|
|
3528
|
+
return _data.length;
|
|
3529
|
+
};
|
|
3530
|
+
_this.write = function(buffer) {
|
|
3531
|
+
const data = _data;
|
|
3532
|
+
let i = 0;
|
|
3533
|
+
while (i + 2 < data.length) {
|
|
3534
|
+
buffer.put(strToNum(data.substring(i, i + 3)), 10);
|
|
3535
|
+
i += 3;
|
|
3536
|
+
}
|
|
3537
|
+
if (i < data.length) {
|
|
3538
|
+
if (data.length - i == 1) buffer.put(strToNum(data.substring(i, i + 1)), 4);
|
|
3539
|
+
else if (data.length - i == 2) buffer.put(strToNum(data.substring(i, i + 2)), 7);
|
|
3540
|
+
}
|
|
3541
|
+
};
|
|
3542
|
+
const strToNum = function(s) {
|
|
3543
|
+
let num = 0;
|
|
3544
|
+
for (let i = 0; i < s.length; i += 1) num = num * 10 + chatToNum(s.charAt(i));
|
|
3545
|
+
return num;
|
|
3546
|
+
};
|
|
3547
|
+
const chatToNum = function(c) {
|
|
3548
|
+
if ("0" <= c && c <= "9") return c.charCodeAt(0) - "0".charCodeAt(0);
|
|
3549
|
+
throw "illegal char :" + c;
|
|
3550
|
+
};
|
|
3551
|
+
return _this;
|
|
3552
|
+
};
|
|
3553
|
+
const qrAlphaNum = function(data) {
|
|
3554
|
+
const _mode = QRMode.MODE_ALPHA_NUM;
|
|
3555
|
+
const _data = data;
|
|
3556
|
+
const _this = {};
|
|
3557
|
+
_this.getMode = function() {
|
|
3558
|
+
return _mode;
|
|
3559
|
+
};
|
|
3560
|
+
_this.getLength = function(buffer) {
|
|
3561
|
+
return _data.length;
|
|
3562
|
+
};
|
|
3563
|
+
_this.write = function(buffer) {
|
|
3564
|
+
const s = _data;
|
|
3565
|
+
let i = 0;
|
|
3566
|
+
while (i + 1 < s.length) {
|
|
3567
|
+
buffer.put(getCode(s.charAt(i)) * 45 + getCode(s.charAt(i + 1)), 11);
|
|
3568
|
+
i += 2;
|
|
3569
|
+
}
|
|
3570
|
+
if (i < s.length) buffer.put(getCode(s.charAt(i)), 6);
|
|
3571
|
+
};
|
|
3572
|
+
const getCode = function(c) {
|
|
3573
|
+
if ("0" <= c && c <= "9") return c.charCodeAt(0) - "0".charCodeAt(0);
|
|
3574
|
+
else if ("A" <= c && c <= "Z") return c.charCodeAt(0) - "A".charCodeAt(0) + 10;
|
|
3575
|
+
else switch (c) {
|
|
3576
|
+
case " ": return 36;
|
|
3577
|
+
case "$": return 37;
|
|
3578
|
+
case "%": return 38;
|
|
3579
|
+
case "*": return 39;
|
|
3580
|
+
case "+": return 40;
|
|
3581
|
+
case "-": return 41;
|
|
3582
|
+
case ".": return 42;
|
|
3583
|
+
case "/": return 43;
|
|
3584
|
+
case ":": return 44;
|
|
3585
|
+
default: throw "illegal char :" + c;
|
|
3586
|
+
}
|
|
3587
|
+
};
|
|
3588
|
+
return _this;
|
|
3589
|
+
};
|
|
3590
|
+
const qr8BitByte = function(data) {
|
|
3591
|
+
const _mode = QRMode.MODE_8BIT_BYTE;
|
|
3592
|
+
const _bytes = qrcode.stringToBytes(data);
|
|
3593
|
+
const _this = {};
|
|
3594
|
+
_this.getMode = function() {
|
|
3595
|
+
return _mode;
|
|
3596
|
+
};
|
|
3597
|
+
_this.getLength = function(buffer) {
|
|
3598
|
+
return _bytes.length;
|
|
3599
|
+
};
|
|
3600
|
+
_this.write = function(buffer) {
|
|
3601
|
+
for (let i = 0; i < _bytes.length; i += 1) buffer.put(_bytes[i], 8);
|
|
3602
|
+
};
|
|
3603
|
+
return _this;
|
|
3604
|
+
};
|
|
3605
|
+
const qrKanji = function(data) {
|
|
3606
|
+
const _mode = QRMode.MODE_KANJI;
|
|
3607
|
+
const stringToBytes = qrcode.stringToBytes;
|
|
3608
|
+
(function(c, code) {
|
|
3609
|
+
const test = stringToBytes(c);
|
|
3610
|
+
if (test.length != 2 || (test[0] << 8 | test[1]) != code) throw "sjis not supported.";
|
|
3611
|
+
})("友", 38726);
|
|
3612
|
+
const _bytes = stringToBytes(data);
|
|
3613
|
+
const _this = {};
|
|
3614
|
+
_this.getMode = function() {
|
|
3615
|
+
return _mode;
|
|
3616
|
+
};
|
|
3617
|
+
_this.getLength = function(buffer) {
|
|
3618
|
+
return ~~(_bytes.length / 2);
|
|
3619
|
+
};
|
|
3620
|
+
_this.write = function(buffer) {
|
|
3621
|
+
const data = _bytes;
|
|
3622
|
+
let i = 0;
|
|
3623
|
+
while (i + 1 < data.length) {
|
|
3624
|
+
let c = (255 & data[i]) << 8 | 255 & data[i + 1];
|
|
3625
|
+
if (33088 <= c && c <= 40956) c -= 33088;
|
|
3626
|
+
else if (57408 <= c && c <= 60351) c -= 49472;
|
|
3627
|
+
else throw "illegal char at " + (i + 1) + "/" + c;
|
|
3628
|
+
c = (c >>> 8 & 255) * 192 + (c & 255);
|
|
3629
|
+
buffer.put(c, 13);
|
|
3630
|
+
i += 2;
|
|
3631
|
+
}
|
|
3632
|
+
if (i < data.length) throw "illegal char at " + (i + 1);
|
|
3633
|
+
};
|
|
3634
|
+
return _this;
|
|
3635
|
+
};
|
|
3636
|
+
const byteArrayOutputStream = function() {
|
|
3637
|
+
const _bytes = [];
|
|
3638
|
+
const _this = {};
|
|
3639
|
+
_this.writeByte = function(b) {
|
|
3640
|
+
_bytes.push(b & 255);
|
|
3641
|
+
};
|
|
3642
|
+
_this.writeShort = function(i) {
|
|
3643
|
+
_this.writeByte(i);
|
|
3644
|
+
_this.writeByte(i >>> 8);
|
|
3645
|
+
};
|
|
3646
|
+
_this.writeBytes = function(b, off, len) {
|
|
3647
|
+
off = off || 0;
|
|
3648
|
+
len = len || b.length;
|
|
3649
|
+
for (let i = 0; i < len; i += 1) _this.writeByte(b[i + off]);
|
|
3650
|
+
};
|
|
3651
|
+
_this.writeString = function(s) {
|
|
3652
|
+
for (let i = 0; i < s.length; i += 1) _this.writeByte(s.charCodeAt(i));
|
|
3653
|
+
};
|
|
3654
|
+
_this.toByteArray = function() {
|
|
3655
|
+
return _bytes;
|
|
3656
|
+
};
|
|
3657
|
+
_this.toString = function() {
|
|
3658
|
+
let s = "";
|
|
3659
|
+
s += "[";
|
|
3660
|
+
for (let i = 0; i < _bytes.length; i += 1) {
|
|
3661
|
+
if (i > 0) s += ",";
|
|
3662
|
+
s += _bytes[i];
|
|
3663
|
+
}
|
|
3664
|
+
s += "]";
|
|
3665
|
+
return s;
|
|
3666
|
+
};
|
|
3667
|
+
return _this;
|
|
3668
|
+
};
|
|
3669
|
+
const base64EncodeOutputStream = function() {
|
|
3670
|
+
let _buffer = 0;
|
|
3671
|
+
let _buflen = 0;
|
|
3672
|
+
let _length = 0;
|
|
3673
|
+
let _base64 = "";
|
|
3674
|
+
const _this = {};
|
|
3675
|
+
const writeEncoded = function(b) {
|
|
3676
|
+
_base64 += String.fromCharCode(encode(b & 63));
|
|
3677
|
+
};
|
|
3678
|
+
const encode = function(n) {
|
|
3679
|
+
if (n < 0) throw "n:" + n;
|
|
3680
|
+
else if (n < 26) return 65 + n;
|
|
3681
|
+
else if (n < 52) return 97 + (n - 26);
|
|
3682
|
+
else if (n < 62) return 48 + (n - 52);
|
|
3683
|
+
else if (n == 62) return 43;
|
|
3684
|
+
else if (n == 63) return 47;
|
|
3685
|
+
else throw "n:" + n;
|
|
3686
|
+
};
|
|
3687
|
+
_this.writeByte = function(n) {
|
|
3688
|
+
_buffer = _buffer << 8 | n & 255;
|
|
3689
|
+
_buflen += 8;
|
|
3690
|
+
_length += 1;
|
|
3691
|
+
while (_buflen >= 6) {
|
|
3692
|
+
writeEncoded(_buffer >>> _buflen - 6);
|
|
3693
|
+
_buflen -= 6;
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
_this.flush = function() {
|
|
3697
|
+
if (_buflen > 0) {
|
|
3698
|
+
writeEncoded(_buffer << 6 - _buflen);
|
|
3699
|
+
_buffer = 0;
|
|
3700
|
+
_buflen = 0;
|
|
3701
|
+
}
|
|
3702
|
+
if (_length % 3 != 0) {
|
|
3703
|
+
const padlen = 3 - _length % 3;
|
|
3704
|
+
for (let i = 0; i < padlen; i += 1) _base64 += "=";
|
|
3705
|
+
}
|
|
3706
|
+
};
|
|
3707
|
+
_this.toString = function() {
|
|
3708
|
+
return _base64;
|
|
3709
|
+
};
|
|
3710
|
+
return _this;
|
|
3711
|
+
};
|
|
3712
|
+
const base64DecodeInputStream = function(str) {
|
|
3713
|
+
const _str = str;
|
|
3714
|
+
let _pos = 0;
|
|
3715
|
+
let _buffer = 0;
|
|
3716
|
+
let _buflen = 0;
|
|
3717
|
+
const _this = {};
|
|
3718
|
+
_this.read = function() {
|
|
3719
|
+
while (_buflen < 8) {
|
|
3720
|
+
if (_pos >= _str.length) {
|
|
3721
|
+
if (_buflen == 0) return -1;
|
|
3722
|
+
throw "unexpected end of file./" + _buflen;
|
|
3723
|
+
}
|
|
3724
|
+
const c = _str.charAt(_pos);
|
|
3725
|
+
_pos += 1;
|
|
3726
|
+
if (c == "=") {
|
|
3727
|
+
_buflen = 0;
|
|
3728
|
+
return -1;
|
|
3729
|
+
} else if (c.match(/^\s$/)) continue;
|
|
3730
|
+
_buffer = _buffer << 6 | decode(c.charCodeAt(0));
|
|
3731
|
+
_buflen += 6;
|
|
3732
|
+
}
|
|
3733
|
+
const n = _buffer >>> _buflen - 8 & 255;
|
|
3734
|
+
_buflen -= 8;
|
|
3735
|
+
return n;
|
|
3736
|
+
};
|
|
3737
|
+
const decode = function(c) {
|
|
3738
|
+
if (65 <= c && c <= 90) return c - 65;
|
|
3739
|
+
else if (97 <= c && c <= 122) return c - 97 + 26;
|
|
3740
|
+
else if (48 <= c && c <= 57) return c - 48 + 52;
|
|
3741
|
+
else if (c == 43) return 62;
|
|
3742
|
+
else if (c == 47) return 63;
|
|
3743
|
+
else throw "c:" + c;
|
|
3744
|
+
};
|
|
3745
|
+
return _this;
|
|
3746
|
+
};
|
|
3747
|
+
const gifImage = function(width, height) {
|
|
3748
|
+
const _width = width;
|
|
3749
|
+
const _height = height;
|
|
3750
|
+
const _data = new Array(width * height);
|
|
3751
|
+
const _this = {};
|
|
3752
|
+
_this.setPixel = function(x, y, pixel) {
|
|
3753
|
+
_data[y * _width + x] = pixel;
|
|
3754
|
+
};
|
|
3755
|
+
_this.write = function(out) {
|
|
3756
|
+
out.writeString("GIF87a");
|
|
3757
|
+
out.writeShort(_width);
|
|
3758
|
+
out.writeShort(_height);
|
|
3759
|
+
out.writeByte(128);
|
|
3760
|
+
out.writeByte(0);
|
|
3761
|
+
out.writeByte(0);
|
|
3762
|
+
out.writeByte(0);
|
|
3763
|
+
out.writeByte(0);
|
|
3764
|
+
out.writeByte(0);
|
|
3765
|
+
out.writeByte(255);
|
|
3766
|
+
out.writeByte(255);
|
|
3767
|
+
out.writeByte(255);
|
|
3768
|
+
out.writeString(",");
|
|
3769
|
+
out.writeShort(0);
|
|
3770
|
+
out.writeShort(0);
|
|
3771
|
+
out.writeShort(_width);
|
|
3772
|
+
out.writeShort(_height);
|
|
3773
|
+
out.writeByte(0);
|
|
3774
|
+
const lzwMinCodeSize = 2;
|
|
3775
|
+
const raster = getLZWRaster(lzwMinCodeSize);
|
|
3776
|
+
out.writeByte(lzwMinCodeSize);
|
|
3777
|
+
let offset = 0;
|
|
3778
|
+
while (raster.length - offset > 255) {
|
|
3779
|
+
out.writeByte(255);
|
|
3780
|
+
out.writeBytes(raster, offset, 255);
|
|
3781
|
+
offset += 255;
|
|
3782
|
+
}
|
|
3783
|
+
out.writeByte(raster.length - offset);
|
|
3784
|
+
out.writeBytes(raster, offset, raster.length - offset);
|
|
3785
|
+
out.writeByte(0);
|
|
3786
|
+
out.writeString(";");
|
|
3787
|
+
};
|
|
3788
|
+
const bitOutputStream = function(out) {
|
|
3789
|
+
const _out = out;
|
|
3790
|
+
let _bitLength = 0;
|
|
3791
|
+
let _bitBuffer = 0;
|
|
3792
|
+
const _this = {};
|
|
3793
|
+
_this.write = function(data, length) {
|
|
3794
|
+
if (data >>> length != 0) throw "length over";
|
|
3795
|
+
while (_bitLength + length >= 8) {
|
|
3796
|
+
_out.writeByte(255 & (data << _bitLength | _bitBuffer));
|
|
3797
|
+
length -= 8 - _bitLength;
|
|
3798
|
+
data >>>= 8 - _bitLength;
|
|
3799
|
+
_bitBuffer = 0;
|
|
3800
|
+
_bitLength = 0;
|
|
3801
|
+
}
|
|
3802
|
+
_bitBuffer = data << _bitLength | _bitBuffer;
|
|
3803
|
+
_bitLength = _bitLength + length;
|
|
3804
|
+
};
|
|
3805
|
+
_this.flush = function() {
|
|
3806
|
+
if (_bitLength > 0) _out.writeByte(_bitBuffer);
|
|
3807
|
+
};
|
|
3808
|
+
return _this;
|
|
3809
|
+
};
|
|
3810
|
+
const getLZWRaster = function(lzwMinCodeSize) {
|
|
3811
|
+
const clearCode = 1 << lzwMinCodeSize;
|
|
3812
|
+
const endCode = (1 << lzwMinCodeSize) + 1;
|
|
3813
|
+
let bitLength = lzwMinCodeSize + 1;
|
|
3814
|
+
const table = lzwTable();
|
|
3815
|
+
for (let i = 0; i < clearCode; i += 1) table.add(String.fromCharCode(i));
|
|
3816
|
+
table.add(String.fromCharCode(clearCode));
|
|
3817
|
+
table.add(String.fromCharCode(endCode));
|
|
3818
|
+
const byteOut = byteArrayOutputStream();
|
|
3819
|
+
const bitOut = bitOutputStream(byteOut);
|
|
3820
|
+
bitOut.write(clearCode, bitLength);
|
|
3821
|
+
let dataIndex = 0;
|
|
3822
|
+
let s = String.fromCharCode(_data[dataIndex]);
|
|
3823
|
+
dataIndex += 1;
|
|
3824
|
+
while (dataIndex < _data.length) {
|
|
3825
|
+
const c = String.fromCharCode(_data[dataIndex]);
|
|
3826
|
+
dataIndex += 1;
|
|
3827
|
+
if (table.contains(s + c)) s = s + c;
|
|
3828
|
+
else {
|
|
3829
|
+
bitOut.write(table.indexOf(s), bitLength);
|
|
3830
|
+
if (table.size() < 4095) {
|
|
3831
|
+
if (table.size() == 1 << bitLength) bitLength += 1;
|
|
3832
|
+
table.add(s + c);
|
|
3833
|
+
}
|
|
3834
|
+
s = c;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
bitOut.write(table.indexOf(s), bitLength);
|
|
3838
|
+
bitOut.write(endCode, bitLength);
|
|
3839
|
+
bitOut.flush();
|
|
3840
|
+
return byteOut.toByteArray();
|
|
3841
|
+
};
|
|
3842
|
+
const lzwTable = function() {
|
|
3843
|
+
const _map = {};
|
|
3844
|
+
let _size = 0;
|
|
3845
|
+
const _this = {};
|
|
3846
|
+
_this.add = function(key) {
|
|
3847
|
+
if (_this.contains(key)) throw "dup key:" + key;
|
|
3848
|
+
_map[key] = _size;
|
|
3849
|
+
_size += 1;
|
|
3850
|
+
};
|
|
3851
|
+
_this.size = function() {
|
|
3852
|
+
return _size;
|
|
3853
|
+
};
|
|
3854
|
+
_this.indexOf = function(key) {
|
|
3855
|
+
return _map[key];
|
|
3856
|
+
};
|
|
3857
|
+
_this.contains = function(key) {
|
|
3858
|
+
return typeof _map[key] != "undefined";
|
|
3859
|
+
};
|
|
3860
|
+
return _this;
|
|
3861
|
+
};
|
|
3862
|
+
return _this;
|
|
3863
|
+
};
|
|
3864
|
+
const createDataURL = function(width, height, getPixel) {
|
|
3865
|
+
const gif = gifImage(width, height);
|
|
3866
|
+
for (let y = 0; y < height; y += 1) for (let x = 0; x < width; x += 1) gif.setPixel(x, y, getPixel(x, y));
|
|
3867
|
+
const b = byteArrayOutputStream();
|
|
3868
|
+
gif.write(b);
|
|
3869
|
+
const base64 = base64EncodeOutputStream();
|
|
3870
|
+
const bytes = b.toByteArray();
|
|
3871
|
+
for (let i = 0; i < bytes.length; i += 1) base64.writeByte(bytes[i]);
|
|
3872
|
+
base64.flush();
|
|
3873
|
+
return "data:image/gif;base64," + base64;
|
|
3874
|
+
};
|
|
3875
|
+
qrcode.stringToBytes;
|
|
3876
|
+
//#endregion
|
|
3877
|
+
//#region src/client/arkme-billing.ts
|
|
3878
|
+
function createBillingClientRequestId(source = globalThis.crypto) {
|
|
3879
|
+
if (typeof source?.randomUUID === "function") return source.randomUUID().toLowerCase();
|
|
3880
|
+
if (typeof source?.getRandomValues !== "function") throw new Error("当前环境无法生成安全的支付请求标识");
|
|
3881
|
+
const bytes = source.getRandomValues(/* @__PURE__ */ new Uint8Array(16));
|
|
3882
|
+
bytes[6] = (bytes[6] ?? 0) & 15 | 64;
|
|
3883
|
+
bytes[8] = (bytes[8] ?? 0) & 63 | 128;
|
|
3884
|
+
const hex = Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("");
|
|
3885
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
3886
|
+
}
|
|
3887
|
+
function selectDefaultBillingProduct(products) {
|
|
3888
|
+
return products.find((product) => product.enabled);
|
|
3889
|
+
}
|
|
3890
|
+
function billingPaymentAvailable(product, paymentMethod) {
|
|
3891
|
+
return product?.enabled === true && product.paymentMethods.some((method) => method.id === paymentMethod);
|
|
3892
|
+
}
|
|
3893
|
+
function activateBillingPaymentAction(action, openURL) {
|
|
3894
|
+
if (action?.type !== "open_url") return false;
|
|
3895
|
+
openURL(action.url);
|
|
3896
|
+
return true;
|
|
3897
|
+
}
|
|
3898
|
+
function billingErrorCode(error) {
|
|
3899
|
+
if (typeof error !== "object" || error === null) return "";
|
|
3900
|
+
const body = "body" in error ? error.body : void 0;
|
|
3901
|
+
if (typeof body === "object" && body !== null && "code" in body && typeof body.code === "string") return body.code;
|
|
3902
|
+
return "code" in error && typeof error.code === "string" ? error.code : "";
|
|
3903
|
+
}
|
|
3904
|
+
async function waitForBillingRetry(millis) {
|
|
3905
|
+
await new Promise((resolve) => setTimeout(resolve, millis));
|
|
3906
|
+
}
|
|
3907
|
+
async function createBillingOrderWithProcessingRetry(attempt, createOrder, wait = waitForBillingRetry) {
|
|
3908
|
+
for (let attemptIndex = 0; attemptIndex < 3; attemptIndex += 1) try {
|
|
3909
|
+
return await createOrder(attempt);
|
|
3910
|
+
} catch (error) {
|
|
3911
|
+
if (billingErrorCode(error) !== "order_processing" || attemptIndex === 2) throw error;
|
|
3912
|
+
await wait(500);
|
|
3913
|
+
}
|
|
3914
|
+
throw new Error("支付订单创建状态无效");
|
|
3915
|
+
}
|
|
3916
|
+
function checkoutAttempt(previous, productId, paymentMethod, createId) {
|
|
3917
|
+
if (previous?.productId === productId && previous.paymentMethod === paymentMethod) return previous;
|
|
3918
|
+
return {
|
|
3919
|
+
productId,
|
|
3920
|
+
paymentMethod,
|
|
3921
|
+
clientRequestId: createId()
|
|
3922
|
+
};
|
|
3923
|
+
}
|
|
3924
|
+
function checkoutAttemptAfterOrder(attempt, status) {
|
|
3925
|
+
return [
|
|
3926
|
+
"paid",
|
|
3927
|
+
"expired",
|
|
3928
|
+
"closed",
|
|
3929
|
+
"failed"
|
|
3930
|
+
].includes(status) ? void 0 : attempt;
|
|
3931
|
+
}
|
|
3932
|
+
function billingOrderScreen(order, nowMillis) {
|
|
3933
|
+
return (order.status === "pending" || order.status === "closed") && nowMillis >= order.expiresAtMillis ? "expired" : order.status;
|
|
3934
|
+
}
|
|
3935
|
+
function billingQrDataUrl(content) {
|
|
3936
|
+
const normalized = content?.trim() ?? "";
|
|
3937
|
+
if (normalized === "") return "";
|
|
3938
|
+
const qr = qrcode(0, "M");
|
|
3939
|
+
qr.addData(normalized);
|
|
3940
|
+
qr.make();
|
|
3941
|
+
return qr.createDataURL(6, 12);
|
|
3942
|
+
}
|
|
3943
|
+
const TERMINAL_ORDER_STATUSES = /* @__PURE__ */ new Set([
|
|
3944
|
+
"paid",
|
|
3945
|
+
"expired",
|
|
3946
|
+
"closed",
|
|
3947
|
+
"failed"
|
|
3948
|
+
]);
|
|
3949
|
+
const POLL_RETRY_BACKOFF_MILLIS = [
|
|
3950
|
+
1e3,
|
|
3951
|
+
2e3,
|
|
3952
|
+
4e3,
|
|
3953
|
+
8e3,
|
|
3954
|
+
1e4
|
|
3955
|
+
];
|
|
3956
|
+
function billingPollErrorRetryable(error) {
|
|
3957
|
+
if (typeof error !== "object" || error === null || !("body" in error)) return true;
|
|
3958
|
+
const body = error.body;
|
|
3959
|
+
return !(typeof body === "object" && body !== null && "retryable" in body && body.retryable === false);
|
|
3960
|
+
}
|
|
3961
|
+
var ArkmeBillingOrderPoller = class {
|
|
3962
|
+
options;
|
|
3963
|
+
timer;
|
|
3964
|
+
generation = 0;
|
|
3965
|
+
refresh;
|
|
3966
|
+
intervalMillis;
|
|
3967
|
+
now;
|
|
3968
|
+
constructor(options) {
|
|
3969
|
+
this.options = options;
|
|
3970
|
+
this.intervalMillis = options.intervalMillis ?? 2e3;
|
|
3971
|
+
this.now = options.now ?? Date.now;
|
|
3972
|
+
}
|
|
3973
|
+
start(orderId, expiresAtMillis, requestedIntervalMillis, initialStatus = "pending") {
|
|
3974
|
+
this.stop();
|
|
3975
|
+
const generation = this.generation;
|
|
3976
|
+
const intervalMillis = requestedIntervalMillis !== void 0 && Number.isFinite(requestedIntervalMillis) && requestedIntervalMillis > 0 ? Math.min(1e4, Math.max(500, Math.trunc(requestedIntervalMillis))) : this.intervalMillis;
|
|
3977
|
+
let currentStatus = initialStatus;
|
|
3978
|
+
if (TERMINAL_ORDER_STATUSES.has(currentStatus)) return;
|
|
3979
|
+
let inFlight = false;
|
|
3980
|
+
let refreshQueued = false;
|
|
3981
|
+
let retryAttempt = 0;
|
|
3982
|
+
const schedule = (delayMillis = intervalMillis) => {
|
|
3983
|
+
if (generation !== this.generation) return;
|
|
3984
|
+
if (currentStatus === "pending") {
|
|
3985
|
+
const remaining = expiresAtMillis - this.now();
|
|
3986
|
+
if (remaining <= 0) {
|
|
3987
|
+
this.options.onExpired?.();
|
|
3988
|
+
this.stop();
|
|
3989
|
+
return;
|
|
3990
|
+
}
|
|
3991
|
+
this.timer = setTimeout(() => {
|
|
3992
|
+
poll();
|
|
3993
|
+
}, Math.min(delayMillis, remaining));
|
|
3994
|
+
} else this.timer = setTimeout(() => {
|
|
3995
|
+
poll();
|
|
3996
|
+
}, delayMillis);
|
|
3997
|
+
};
|
|
3998
|
+
const poll = async () => {
|
|
3999
|
+
if (generation !== this.generation) return;
|
|
4000
|
+
if (inFlight) {
|
|
4001
|
+
refreshQueued = true;
|
|
4002
|
+
return;
|
|
4003
|
+
}
|
|
4004
|
+
if (currentStatus === "pending" && this.now() >= expiresAtMillis) {
|
|
4005
|
+
this.options.onExpired?.();
|
|
4006
|
+
this.stop();
|
|
4007
|
+
return;
|
|
4008
|
+
}
|
|
4009
|
+
inFlight = true;
|
|
4010
|
+
let nextDelayMillis = intervalMillis;
|
|
4011
|
+
try {
|
|
4012
|
+
const order = await this.options.readStatus(orderId);
|
|
4013
|
+
if (generation !== this.generation) return;
|
|
4014
|
+
retryAttempt = 0;
|
|
4015
|
+
this.options.onUpdate(order);
|
|
4016
|
+
currentStatus = billingOrderScreen({
|
|
4017
|
+
...order,
|
|
4018
|
+
expiresAtMillis
|
|
4019
|
+
}, this.now());
|
|
4020
|
+
if (TERMINAL_ORDER_STATUSES.has(currentStatus)) this.stop();
|
|
4021
|
+
} catch (error) {
|
|
4022
|
+
if (generation !== this.generation) return;
|
|
4023
|
+
if (!billingPollErrorRetryable(error)) {
|
|
4024
|
+
this.options.onError(error, { willRetry: false });
|
|
4025
|
+
this.stop();
|
|
4026
|
+
return;
|
|
4027
|
+
}
|
|
4028
|
+
nextDelayMillis = POLL_RETRY_BACKOFF_MILLIS[Math.min(retryAttempt, POLL_RETRY_BACKOFF_MILLIS.length - 1)] ?? 1e4;
|
|
4029
|
+
retryAttempt += 1;
|
|
4030
|
+
this.options.onError(error, {
|
|
4031
|
+
willRetry: true,
|
|
4032
|
+
retryInMillis: nextDelayMillis
|
|
4033
|
+
});
|
|
4034
|
+
} finally {
|
|
4035
|
+
inFlight = false;
|
|
4036
|
+
}
|
|
4037
|
+
if (generation !== this.generation) return;
|
|
4038
|
+
if (refreshQueued) {
|
|
4039
|
+
refreshQueued = false;
|
|
4040
|
+
poll();
|
|
4041
|
+
return;
|
|
4042
|
+
}
|
|
4043
|
+
schedule(nextDelayMillis);
|
|
4044
|
+
};
|
|
4045
|
+
this.refresh = () => {
|
|
4046
|
+
if (generation !== this.generation) return;
|
|
4047
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
4048
|
+
this.timer = void 0;
|
|
4049
|
+
if (inFlight) {
|
|
4050
|
+
refreshQueued = true;
|
|
4051
|
+
return;
|
|
4052
|
+
}
|
|
4053
|
+
poll();
|
|
4054
|
+
};
|
|
4055
|
+
schedule();
|
|
4056
|
+
}
|
|
4057
|
+
refreshNow() {
|
|
4058
|
+
this.refresh?.();
|
|
4059
|
+
}
|
|
4060
|
+
stop() {
|
|
4061
|
+
this.generation += 1;
|
|
4062
|
+
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
4063
|
+
this.timer = void 0;
|
|
4064
|
+
this.refresh = void 0;
|
|
4065
|
+
}
|
|
4066
|
+
};
|
|
4067
|
+
//#endregion
|
|
4068
|
+
//#region src/client/ArkmeBillingSettings.tsx
|
|
4069
|
+
function formatArkmeBillingPrice(priceMinor, _currency) {
|
|
4070
|
+
return `¥${(priceMinor / 100).toFixed(2)}`;
|
|
4071
|
+
}
|
|
4072
|
+
function formatArkmeNanoCny(value) {
|
|
4073
|
+
if (!/^(?:0|[1-9]\d*)$/.test(value)) return "—";
|
|
4074
|
+
const roundedCents = (BigInt(value) + 5000000n) / 10000000n;
|
|
4075
|
+
const whole = roundedCents / 100n;
|
|
4076
|
+
const fraction = (roundedCents % 100n).toString().padStart(2, "0");
|
|
4077
|
+
return `¥${whole.toLocaleString("zh-CN")}.${fraction}`;
|
|
4078
|
+
}
|
|
4079
|
+
function errorMessage(error) {
|
|
4080
|
+
return error instanceof Error && error.message.trim() !== "" ? error.message : String(error);
|
|
4081
|
+
}
|
|
4082
|
+
function ArkmeBalanceSettingsRowView(props) {
|
|
4083
|
+
const description = props.quotaState.kind === "ready" ? formatArkmeNanoCny(props.quotaState.quota.availableNanoCny) : props.quotaState.kind === "loading" ? "正在加载余额…" : "余额读取失败,点击重试";
|
|
4084
|
+
const reservedNanoCny = props.quotaState.kind === "ready" ? props.quotaState.quota.reservedNanoCny : void 0;
|
|
4085
|
+
const showReserved = reservedNanoCny !== void 0 && /^(?:0|[1-9]\d*)$/.test(reservedNanoCny) && BigInt(reservedNanoCny) > 0n;
|
|
4086
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4087
|
+
className: `arkme-redesign-setting-row arkme-redesign-balance-row${showReserved ? "" : " is-without-reserved"}`,
|
|
4088
|
+
children: [
|
|
4089
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4090
|
+
type: "button",
|
|
4091
|
+
className: "arkme-redesign-balance-main",
|
|
4092
|
+
onClick: props.onOpen,
|
|
4093
|
+
children: [
|
|
4094
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "AI 余额" }),
|
|
4095
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: description }),
|
|
4096
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4097
|
+
className: "arkme-redesign-balance-usage",
|
|
4098
|
+
children: "可用于在 DSH 会话中通过 Arkme 调用 AI 模型"
|
|
4099
|
+
})
|
|
4100
|
+
]
|
|
4101
|
+
}),
|
|
4102
|
+
showReserved && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4103
|
+
className: "arkme-redesign-reserved-balance",
|
|
4104
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4105
|
+
className: "arkme-redesign-reserved-title",
|
|
4106
|
+
children: [
|
|
4107
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "预占余额" }),
|
|
4108
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4109
|
+
className: "arkme-redesign-reserved-help",
|
|
4110
|
+
tabIndex: 0,
|
|
4111
|
+
"aria-label": "预占余额说明",
|
|
4112
|
+
"aria-describedby": "arkme-reserved-balance-tooltip",
|
|
4113
|
+
children: "?"
|
|
4114
|
+
}),
|
|
4115
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4116
|
+
id: "arkme-reserved-balance-tooltip",
|
|
4117
|
+
role: "tooltip",
|
|
4118
|
+
children: "当前运行的任务预先占用的余额,任务完成后将返还剩余余额。"
|
|
4119
|
+
})
|
|
4120
|
+
]
|
|
4121
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: formatArkmeNanoCny(reservedNanoCny) })]
|
|
4122
|
+
}),
|
|
4123
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4124
|
+
type: "button",
|
|
4125
|
+
className: "arkme-redesign-update-button arkme-redesign-recharge-trigger",
|
|
4126
|
+
onClick: props.onOpen,
|
|
4127
|
+
children: "充值"
|
|
4128
|
+
}),
|
|
4129
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4130
|
+
className: "arkme-redesign-trailing-slot",
|
|
4131
|
+
"aria-hidden": true
|
|
4132
|
+
})
|
|
4133
|
+
]
|
|
4134
|
+
});
|
|
4135
|
+
}
|
|
4136
|
+
function AlipayIcon() {
|
|
4137
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
4138
|
+
"aria-hidden": "true",
|
|
4139
|
+
focusable: "false",
|
|
4140
|
+
className: "arkme-billing-platform-icon is-alipay",
|
|
4141
|
+
viewBox: "0 0 1024 1024",
|
|
4142
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
4143
|
+
d: "M1024.0512 701.0304V196.864A196.9664 196.9664 0 0 0 827.136 0H196.864A196.9664 196.9664 0 0 0 0 196.864v630.272A196.9152 196.9152 0 0 0 196.864 1024h630.272a197.12 197.12 0 0 0 193.8432-162.0992c-52.224-22.6304-278.528-120.32-396.4416-176.64-89.7024 108.6976-183.7056 173.9264-325.3248 173.9264s-236.1856-87.2448-224.8192-194.048c7.4752-70.0416 55.552-184.576 264.2944-164.9664 110.08 10.3424 160.4096 30.8736 250.1632 60.5184 23.1936-42.5984 42.496-89.4464 57.1392-139.264H248.064v-39.424h196.9152V311.1424H204.8V267.776h240.128V165.632s2.1504-15.9744 19.8144-15.9744h98.4576V267.776h256v43.4176h-256V381.952h208.8448a805.9904 805.9904 0 0 1-84.8384 212.6848c60.672 22.016 336.7936 106.3936 336.7936 106.3936zM283.5456 791.6032c-149.6576 0-173.312-94.464-165.376-133.9392 7.8336-39.3216 51.2-90.624 134.4-90.624 95.5904 0 181.248 24.4736 284.0576 74.5472-72.192 94.0032-160.9216 150.016-253.0816 150.016z",
|
|
4144
|
+
fill: "#009FE8"
|
|
4145
|
+
})
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
function WechatIcon() {
|
|
4149
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
4150
|
+
"aria-hidden": "true",
|
|
4151
|
+
focusable: "false",
|
|
4152
|
+
className: "arkme-billing-platform-icon is-wechat",
|
|
4153
|
+
viewBox: "0 0 24 24",
|
|
4154
|
+
fill: "currentColor",
|
|
4155
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M9.55 4C4.82 4 1 7.12 1 10.96c0 2.17 1.23 4.1 3.15 5.38l-.8 2.45 2.89-1.43c1.03.36 2.14.56 3.31.56.25 0 .5-.01.74-.03a6.38 6.38 0 0 1-.28-1.86c0-3.7 3.25-6.73 7.43-7.05C16.39 6.07 13.32 4 9.55 4Zm-2.9 5.08a1.1 1.1 0 1 1 0-2.2 1.1 1.1 0 0 1 0 2.2Zm5.8 0a1.1 1.1 0 1 1 0-2.2 1.1 1.1 0 0 1 0 2.2Z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M23 16.02c0-3.3-3.25-5.98-7.26-5.98s-7.25 2.68-7.25 5.98S11.73 22 15.74 22c.99 0 1.94-.17 2.79-.47l2.45 1.2-.67-2.07C21.95 19.56 23 17.9 23 16.02Zm-9.7-1.48a.94.94 0 1 1 0-1.88.94.94 0 0 1 0 1.88Zm4.9 0a.94.94 0 1 1 0-1.88.94.94 0 0 1 0 1.88Z" })]
|
|
4156
|
+
});
|
|
4157
|
+
}
|
|
4158
|
+
function ArkmeRechargeDialogView(props) {
|
|
4159
|
+
const enabledProducts = props.productsState.kind === "ready" ? props.productsState.products.filter((product) => product.enabled) : [];
|
|
4160
|
+
const selected = enabledProducts.find((product) => product.productId === props.selectedProductId);
|
|
4161
|
+
const creating = props.creatingMethod !== void 0;
|
|
4162
|
+
const method = (paymentMethod) => selected?.paymentMethods.find((item) => item.id === paymentMethod);
|
|
4163
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4164
|
+
className: "arkme-billing-backdrop",
|
|
4165
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4166
|
+
role: "dialog",
|
|
4167
|
+
"aria-modal": "true",
|
|
4168
|
+
"aria-label": "余额充值",
|
|
4169
|
+
className: "arkme-billing-recharge-dialog",
|
|
4170
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
4171
|
+
className: "arkme-billing-dialog-header",
|
|
4172
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", { children: "余额充值" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "充值后可在 DSH 会话中通过 Arkme 调用 AI 模型" })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4173
|
+
type: "button",
|
|
4174
|
+
"aria-label": "关闭充值弹窗",
|
|
4175
|
+
onClick: props.onClose,
|
|
4176
|
+
children: "×"
|
|
4177
|
+
})]
|
|
4178
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4179
|
+
className: "arkme-billing-dialog-body",
|
|
4180
|
+
children: [
|
|
4181
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4182
|
+
className: "arkme-billing-dialog-balance",
|
|
4183
|
+
children: [
|
|
4184
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "当前余额" }),
|
|
4185
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: props.quotaState.kind === "ready" ? formatArkmeNanoCny(props.quotaState.quota.availableNanoCny) : props.quotaState.kind === "loading" ? "正在加载…" : "读取失败" }),
|
|
4186
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4187
|
+
type: "button",
|
|
4188
|
+
onClick: props.onRefreshQuota,
|
|
4189
|
+
children: "刷新"
|
|
4190
|
+
})
|
|
4191
|
+
]
|
|
4192
|
+
}),
|
|
4193
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4194
|
+
className: "arkme-billing-products-section",
|
|
4195
|
+
children: [
|
|
4196
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "充值套餐" }),
|
|
4197
|
+
props.productsState.kind === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4198
|
+
className: "arkme-billing-state",
|
|
4199
|
+
"aria-busy": "true",
|
|
4200
|
+
children: "正在加载充值套餐…"
|
|
4201
|
+
}),
|
|
4202
|
+
props.productsState.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4203
|
+
className: "arkme-billing-error",
|
|
4204
|
+
role: "alert",
|
|
4205
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: props.productsState.message }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4206
|
+
type: "button",
|
|
4207
|
+
onClick: props.onRetryProducts,
|
|
4208
|
+
children: "重试"
|
|
4209
|
+
})]
|
|
4210
|
+
}),
|
|
4211
|
+
props.productsState.kind === "ready" && enabledProducts.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4212
|
+
className: "arkme-billing-state",
|
|
4213
|
+
children: "暂无可购买套餐"
|
|
4214
|
+
}),
|
|
4215
|
+
enabledProducts.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4216
|
+
role: "radiogroup",
|
|
4217
|
+
"aria-label": "充值套餐",
|
|
4218
|
+
style: {
|
|
4219
|
+
display: "grid",
|
|
4220
|
+
gridTemplateColumns: "repeat(auto-fit, minmax(128px, 1fr))",
|
|
4221
|
+
gap: 10
|
|
4222
|
+
},
|
|
4223
|
+
className: "arkme-billing-product-grid",
|
|
4224
|
+
children: enabledProducts.map((product) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4225
|
+
type: "button",
|
|
4226
|
+
role: "radio",
|
|
4227
|
+
"aria-checked": product.productId === props.selectedProductId,
|
|
4228
|
+
className: product.productId === props.selectedProductId ? "is-selected" : "",
|
|
4229
|
+
title: product.title,
|
|
4230
|
+
disabled: creating,
|
|
4231
|
+
onClick: () => props.onSelectProduct(product.productId),
|
|
4232
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: product.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: formatArkmeBillingPrice(product.priceMinor, product.currency) })]
|
|
4233
|
+
}, product.productId))
|
|
4234
|
+
})
|
|
4235
|
+
]
|
|
4236
|
+
}),
|
|
4237
|
+
selected !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4238
|
+
className: "arkme-billing-payment-section",
|
|
4239
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: "支付方式" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4240
|
+
className: "arkme-billing-payment-actions",
|
|
4241
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4242
|
+
type: "button",
|
|
4243
|
+
disabled: creating || method("alipay_pc_web") === void 0,
|
|
4244
|
+
onClick: () => props.onPurchase("alipay_pc_web"),
|
|
4245
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlipayIcon, {}), props.creatingMethod === "alipay_pc_web" ? "正在创建订单…" : "支付宝网页支付"]
|
|
4246
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4247
|
+
type: "button",
|
|
4248
|
+
disabled: creating || method("wechat_native") === void 0,
|
|
4249
|
+
onClick: () => props.onPurchase("wechat_native"),
|
|
4250
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(WechatIcon, {}), props.creatingMethod === "wechat_native" ? "正在创建订单…" : "微信扫码支付"]
|
|
4251
|
+
})]
|
|
4252
|
+
})]
|
|
4253
|
+
}),
|
|
4254
|
+
props.purchaseError !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4255
|
+
className: "arkme-billing-error is-purchase",
|
|
4256
|
+
role: "alert",
|
|
4257
|
+
children: props.purchaseError
|
|
4258
|
+
})
|
|
4259
|
+
]
|
|
4260
|
+
})]
|
|
4261
|
+
})
|
|
4262
|
+
});
|
|
4263
|
+
}
|
|
4264
|
+
function countdownText(remainingMillis) {
|
|
4265
|
+
const seconds = Math.max(0, Math.ceil(remainingMillis / 1e3));
|
|
4266
|
+
return `${Math.floor(seconds / 60).toString().padStart(2, "0")}:${(seconds % 60).toString().padStart(2, "0")}`;
|
|
4267
|
+
}
|
|
4268
|
+
function ArkmePaymentDialog(props) {
|
|
4269
|
+
const dialogRef = (0, react.useRef)(null);
|
|
4270
|
+
const screen = billingOrderScreen(props.order, props.nowMillis);
|
|
4271
|
+
const paymentAction = screen === "pending" ? props.order.paymentAction : void 0;
|
|
4272
|
+
const qrDataUrl = paymentAction?.type === "display_qr" ? billingQrDataUrl(paymentAction.qrContent) : "";
|
|
4273
|
+
const openUrl = paymentAction?.type === "open_url" ? paymentAction.url : "";
|
|
4274
|
+
const paidAmount = formatArkmeBillingPrice(props.order.amountMinor, props.order.currency);
|
|
4275
|
+
const statusProblem = props.statusError !== "" && (screen === "pending" || screen === "crediting");
|
|
4276
|
+
const title = statusProblem ? "正在确认支付" : screen === "crediting" ? "支付已确认" : screen === "pending" ? "完成支付" : screen === "paid" ? "支付成功" : screen === "expired" ? "支付未完成" : "订单状态";
|
|
4277
|
+
(0, react.useEffect)(() => {
|
|
4278
|
+
dialogRef.current?.focus();
|
|
4279
|
+
}, []);
|
|
4280
|
+
const onKeyDown = (event) => {
|
|
4281
|
+
if (event.key === "Escape") {
|
|
4282
|
+
event.preventDefault();
|
|
4283
|
+
props.onClose();
|
|
4284
|
+
return;
|
|
4285
|
+
}
|
|
4286
|
+
if (event.key !== "Tab") return;
|
|
4287
|
+
const focusable = dialogRef.current?.querySelectorAll("button:not([disabled]), [href], [tabindex]:not([tabindex=\"-1\"])");
|
|
4288
|
+
if (focusable === void 0 || focusable.length === 0) return;
|
|
4289
|
+
const first = focusable[0];
|
|
4290
|
+
const last = focusable[focusable.length - 1];
|
|
4291
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
4292
|
+
event.preventDefault();
|
|
4293
|
+
last.focus();
|
|
4294
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
4295
|
+
event.preventDefault();
|
|
4296
|
+
first.focus();
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4299
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4300
|
+
className: "arkme-billing-backdrop",
|
|
4301
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4302
|
+
ref: dialogRef,
|
|
4303
|
+
role: "dialog",
|
|
4304
|
+
"aria-modal": "true",
|
|
4305
|
+
"aria-labelledby": "arkme-payment-dialog-title",
|
|
4306
|
+
tabIndex: -1,
|
|
4307
|
+
className: "arkme-billing-payment-dialog",
|
|
4308
|
+
onKeyDown,
|
|
4309
|
+
children: [
|
|
4310
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("header", {
|
|
4311
|
+
className: "arkme-billing-dialog-header",
|
|
4312
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
4313
|
+
id: "arkme-payment-dialog-title",
|
|
4314
|
+
children: title
|
|
4315
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: ["订单号:", props.order.orderId] })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4316
|
+
type: "button",
|
|
4317
|
+
"aria-label": "关闭支付弹窗",
|
|
4318
|
+
onClick: props.onClose,
|
|
4319
|
+
children: "×"
|
|
4320
|
+
})]
|
|
4321
|
+
}),
|
|
4322
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4323
|
+
className: `arkme-billing-payment-content is-${screen}`,
|
|
4324
|
+
children: [
|
|
4325
|
+
statusProblem && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4326
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "暂时无法查询订单状态" }),
|
|
4327
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: props.statusRetryable ? "系统会自动退避重试;请勿重复付款。" : "请勿重复付款;请点击立即重试。" }),
|
|
4328
|
+
props.statusRetryable && props.statusRetryInMillis !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
4329
|
+
"下次重试约 ",
|
|
4330
|
+
Math.max(1, Math.ceil(props.statusRetryInMillis / 1e3)),
|
|
4331
|
+
" 秒后"
|
|
4332
|
+
] })
|
|
4333
|
+
] }),
|
|
4334
|
+
!statusProblem && screen === "pending" && openUrl !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4335
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(AlipayIcon, {}),
|
|
4336
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "支付宝收银台已在浏览器中打开" }),
|
|
4337
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "完成支付后返回 Arkme,页面会自动确认结果。" })
|
|
4338
|
+
] }),
|
|
4339
|
+
!statusProblem && screen === "pending" && qrDataUrl !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
4340
|
+
src: qrDataUrl,
|
|
4341
|
+
alt: "微信支付二维码"
|
|
4342
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "使用微信扫码支付" })] }),
|
|
4343
|
+
!statusProblem && screen === "pending" && qrDataUrl === "" && openUrl === "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4344
|
+
className: "arkme-billing-error",
|
|
4345
|
+
role: "alert",
|
|
4346
|
+
children: "支付操作暂不可用"
|
|
4347
|
+
}),
|
|
4348
|
+
!statusProblem && screen === "pending" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [
|
|
4349
|
+
"支付金额 ",
|
|
4350
|
+
formatArkmeBillingPrice(props.order.amountMinor, props.order.currency),
|
|
4351
|
+
" · 剩余 ",
|
|
4352
|
+
countdownText(props.order.expiresAtMillis - props.nowMillis)
|
|
4353
|
+
] }),
|
|
4354
|
+
!statusProblem && screen === "crediting" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "支付已确认,无需重复支付" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "余额到账中,到账后会自动刷新当前余额。" })] }),
|
|
4355
|
+
screen === "paid" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4356
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("strong", { children: [paidAmount, " 已到账"] }),
|
|
4357
|
+
props.quotaState.kind === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: "已到账,正在刷新当前余额" }),
|
|
4358
|
+
props.quotaState.kind === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: ["当前余额已刷新:", formatArkmeNanoCny(props.quotaState.quota.availableNanoCny)] }),
|
|
4359
|
+
props.quotaState.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4360
|
+
className: "arkme-billing-error",
|
|
4361
|
+
role: "alert",
|
|
4362
|
+
children: ["余额暂未刷新:", props.quotaState.message]
|
|
4363
|
+
})
|
|
4364
|
+
] }),
|
|
4365
|
+
screen === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "支付凭据已过期,请重新生成。" }),
|
|
4366
|
+
screen === "closed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "订单已关闭。" }),
|
|
4367
|
+
screen === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "订单支付失败。" }),
|
|
4368
|
+
statusProblem && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4369
|
+
className: "arkme-billing-error",
|
|
4370
|
+
role: "alert",
|
|
4371
|
+
children: props.statusError
|
|
4372
|
+
})
|
|
4373
|
+
]
|
|
4374
|
+
}),
|
|
4375
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("footer", {
|
|
4376
|
+
className: "arkme-billing-dialog-actions",
|
|
4377
|
+
children: [
|
|
4378
|
+
screen === "expired" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4379
|
+
type: "button",
|
|
4380
|
+
className: "arkme-billing-primary-action",
|
|
4381
|
+
onClick: props.onRegenerate,
|
|
4382
|
+
children: "重新生成"
|
|
4383
|
+
}),
|
|
4384
|
+
screen === "paid" && props.quotaState.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4385
|
+
type: "button",
|
|
4386
|
+
onClick: props.onRefreshQuota,
|
|
4387
|
+
children: "刷新余额"
|
|
4388
|
+
}),
|
|
4389
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4390
|
+
type: "button",
|
|
4391
|
+
onClick: props.onClose,
|
|
4392
|
+
children: screen === "paid" ? "完成" : screen === "pending" || screen === "crediting" ? "稍后查看" : "关闭"
|
|
4393
|
+
}),
|
|
4394
|
+
!statusProblem && screen === "pending" && openUrl !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4395
|
+
type: "button",
|
|
4396
|
+
className: "arkme-billing-primary-action",
|
|
4397
|
+
onClick: () => props.onOpenPaymentUrl(openUrl),
|
|
4398
|
+
children: "重新打开支付页面"
|
|
4399
|
+
}),
|
|
4400
|
+
props.statusError !== "" && (screen === "pending" || screen === "crediting") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4401
|
+
type: "button",
|
|
4402
|
+
className: "arkme-billing-primary-action",
|
|
4403
|
+
onClick: props.onRetryStatus,
|
|
4404
|
+
children: "立即重试"
|
|
4405
|
+
})
|
|
4406
|
+
]
|
|
4407
|
+
})
|
|
4408
|
+
]
|
|
4409
|
+
})
|
|
4410
|
+
});
|
|
4411
|
+
}
|
|
4412
|
+
function ArkmeBillingSettings({ active = true, renderTrigger } = {}) {
|
|
4413
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
4414
|
+
const [quotaState, setQuotaState] = (0, react.useState)({ kind: "loading" });
|
|
4415
|
+
const [productsState, setProductsState] = (0, react.useState)({ kind: "loading" });
|
|
4416
|
+
const [selectedProductId, setSelectedProductId] = (0, react.useState)();
|
|
4417
|
+
const [creatingMethod, setCreatingMethod] = (0, react.useState)();
|
|
4418
|
+
const [purchaseError, setPurchaseError] = (0, react.useState)("");
|
|
4419
|
+
const [order, setOrder] = (0, react.useState)();
|
|
4420
|
+
const [orderStatusError, setOrderStatusError] = (0, react.useState)("");
|
|
4421
|
+
const [orderStatusRetryable, setOrderStatusRetryable] = (0, react.useState)(false);
|
|
4422
|
+
const [orderStatusRetryInMillis, setOrderStatusRetryInMillis] = (0, react.useState)();
|
|
4423
|
+
const [nowMillis, setNowMillis] = (0, react.useState)(Date.now());
|
|
4424
|
+
const pollerRef = (0, react.useRef)();
|
|
4425
|
+
const checkoutAttemptRef = (0, react.useRef)();
|
|
4426
|
+
const selectedProductIdRef = (0, react.useRef)();
|
|
4427
|
+
const dialogRoot = (0, react.useRef)(null);
|
|
4428
|
+
const quotaLoadGenerationRef = (0, react.useRef)(0);
|
|
4429
|
+
const quotaRequestRef = (0, react.useRef)();
|
|
4430
|
+
const openPaymentUrl = (0, react.useCallback)((url) => {
|
|
4431
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
4432
|
+
}, []);
|
|
4433
|
+
const loadQuota = (0, react.useCallback)(async () => {
|
|
4434
|
+
const generation = ++quotaLoadGenerationRef.current;
|
|
4435
|
+
quotaRequestRef.current?.abort();
|
|
4436
|
+
const request = new AbortController();
|
|
4437
|
+
quotaRequestRef.current = request;
|
|
4438
|
+
setQuotaState({ kind: "loading" });
|
|
4439
|
+
try {
|
|
4440
|
+
const quota = await callArkme("billing.quota", void 0, request.signal);
|
|
4441
|
+
if (generation === quotaLoadGenerationRef.current) setQuotaState({
|
|
4442
|
+
kind: "ready",
|
|
4443
|
+
quota
|
|
4444
|
+
});
|
|
4445
|
+
} catch (error) {
|
|
4446
|
+
if (generation === quotaLoadGenerationRef.current) setQuotaState({
|
|
4447
|
+
kind: "error",
|
|
4448
|
+
message: errorMessage(error)
|
|
4449
|
+
});
|
|
4450
|
+
}
|
|
4451
|
+
}, []);
|
|
4452
|
+
const loadProducts = (0, react.useCallback)(async () => {
|
|
4453
|
+
setProductsState({ kind: "loading" });
|
|
4454
|
+
try {
|
|
4455
|
+
const result = await callArkme("billing.products");
|
|
4456
|
+
setProductsState({
|
|
4457
|
+
kind: "ready",
|
|
4458
|
+
products: result.items
|
|
4459
|
+
});
|
|
4460
|
+
const next = result.items.find((product) => product.enabled && product.productId === selectedProductIdRef.current) ?? selectDefaultBillingProduct(result.items);
|
|
4461
|
+
selectedProductIdRef.current = next?.productId;
|
|
4462
|
+
setSelectedProductId(next?.productId);
|
|
4463
|
+
if (next?.productId !== checkoutAttemptRef.current?.productId) checkoutAttemptRef.current = void 0;
|
|
4464
|
+
} catch (error) {
|
|
4465
|
+
setProductsState({
|
|
4466
|
+
kind: "error",
|
|
4467
|
+
message: errorMessage(error)
|
|
4468
|
+
});
|
|
4469
|
+
}
|
|
4470
|
+
}, []);
|
|
4471
|
+
(0, react.useEffect)(() => {
|
|
4472
|
+
if (active) loadQuota();
|
|
4473
|
+
}, [active, loadQuota]);
|
|
4474
|
+
(0, react.useEffect)(() => () => {
|
|
4475
|
+
quotaLoadGenerationRef.current += 1;
|
|
4476
|
+
quotaRequestRef.current?.abort();
|
|
4477
|
+
pollerRef.current?.stop();
|
|
4478
|
+
}, []);
|
|
4479
|
+
(0, react.useEffect)(() => {
|
|
4480
|
+
if (open) loadProducts();
|
|
4481
|
+
}, [loadProducts, open]);
|
|
4482
|
+
(0, react.useEffect)(() => {
|
|
4483
|
+
if (order === void 0) return;
|
|
4484
|
+
setNowMillis(Date.now());
|
|
4485
|
+
const timer = setInterval(() => {
|
|
4486
|
+
setNowMillis(Date.now());
|
|
4487
|
+
}, 1e3);
|
|
4488
|
+
return () => clearInterval(timer);
|
|
4489
|
+
}, [order?.orderId]);
|
|
4490
|
+
(0, react.useEffect)(() => {
|
|
4491
|
+
if (order === void 0 || [
|
|
4492
|
+
"paid",
|
|
4493
|
+
"expired",
|
|
4494
|
+
"closed",
|
|
4495
|
+
"failed"
|
|
4496
|
+
].includes(order.status)) return;
|
|
4497
|
+
const refresh = () => {
|
|
4498
|
+
pollerRef.current?.refreshNow();
|
|
4499
|
+
};
|
|
4500
|
+
const refreshWhenVisible = () => {
|
|
4501
|
+
if (document.visibilityState === "visible") refresh();
|
|
4502
|
+
};
|
|
4503
|
+
window.addEventListener("focus", refresh);
|
|
4504
|
+
document.addEventListener("visibilitychange", refreshWhenVisible);
|
|
4505
|
+
return () => {
|
|
4506
|
+
window.removeEventListener("focus", refresh);
|
|
4507
|
+
document.removeEventListener("visibilitychange", refreshWhenVisible);
|
|
4508
|
+
};
|
|
4509
|
+
}, [order?.orderId, order?.status]);
|
|
4510
|
+
const startPolling = (0, react.useCallback)((nextOrder) => {
|
|
4511
|
+
pollerRef.current?.stop();
|
|
4512
|
+
const poller = new ArkmeBillingOrderPoller({
|
|
4513
|
+
readStatus: async (orderId) => await callArkme("billing.order.status", { orderId }),
|
|
4514
|
+
onUpdate: (latest) => {
|
|
4515
|
+
setOrder(latest);
|
|
4516
|
+
setOrderStatusError("");
|
|
4517
|
+
setOrderStatusRetryable(false);
|
|
4518
|
+
setOrderStatusRetryInMillis(void 0);
|
|
4519
|
+
checkoutAttemptRef.current = checkoutAttemptAfterOrder(checkoutAttemptRef.current, latest.status);
|
|
4520
|
+
if (latest.status === "paid") loadQuota();
|
|
4521
|
+
},
|
|
4522
|
+
onError: (error, retry) => {
|
|
4523
|
+
setOrderStatusError(errorMessage(error));
|
|
4524
|
+
setOrderStatusRetryable(retry.willRetry);
|
|
4525
|
+
setOrderStatusRetryInMillis(retry.retryInMillis);
|
|
4526
|
+
},
|
|
4527
|
+
onExpired: () => {
|
|
4528
|
+
checkoutAttemptRef.current = checkoutAttemptAfterOrder(checkoutAttemptRef.current, "expired");
|
|
4529
|
+
setNowMillis(nextOrder.expiresAtMillis);
|
|
4530
|
+
}
|
|
4531
|
+
});
|
|
4532
|
+
pollerRef.current = poller;
|
|
4533
|
+
poller.start(nextOrder.orderId, nextOrder.expiresAtMillis, nextOrder.pollIntervalMillis, nextOrder.status);
|
|
4534
|
+
}, [loadQuota]);
|
|
4535
|
+
const createPayment = (0, react.useCallback)(async (paymentMethod, forceNew = false) => {
|
|
4536
|
+
if (productsState.kind !== "ready") return;
|
|
4537
|
+
const product = productsState.products.find((item) => item.enabled && item.productId === selectedProductIdRef.current);
|
|
4538
|
+
if (product === void 0 || !billingPaymentAvailable(product, paymentMethod)) return;
|
|
4539
|
+
const attempt = checkoutAttempt(forceNew ? void 0 : checkoutAttemptRef.current, product.productId, paymentMethod, createBillingClientRequestId);
|
|
4540
|
+
checkoutAttemptRef.current = attempt;
|
|
4541
|
+
setCreatingMethod(paymentMethod);
|
|
4542
|
+
setPurchaseError("");
|
|
4543
|
+
setOrderStatusError("");
|
|
4544
|
+
setOrderStatusRetryable(false);
|
|
4545
|
+
setOrderStatusRetryInMillis(void 0);
|
|
4546
|
+
try {
|
|
4547
|
+
const nextOrder = await createBillingOrderWithProcessingRetry(attempt, async (input) => await callArkme("billing.order.create", { ...input }));
|
|
4548
|
+
checkoutAttemptRef.current = checkoutAttemptAfterOrder(attempt, nextOrder.status);
|
|
4549
|
+
setNowMillis(Date.now());
|
|
4550
|
+
activateBillingPaymentAction(nextOrder.paymentAction, openPaymentUrl);
|
|
4551
|
+
setOrder(nextOrder);
|
|
4552
|
+
if (nextOrder.status === "paid") loadQuota();
|
|
4553
|
+
else startPolling(nextOrder);
|
|
4554
|
+
} catch (error) {
|
|
4555
|
+
setPurchaseError(errorMessage(error));
|
|
4556
|
+
} finally {
|
|
4557
|
+
setCreatingMethod(void 0);
|
|
4558
|
+
}
|
|
4559
|
+
}, [
|
|
4560
|
+
loadQuota,
|
|
4561
|
+
openPaymentUrl,
|
|
4562
|
+
productsState,
|
|
4563
|
+
startPolling
|
|
4564
|
+
]);
|
|
4565
|
+
const selectProduct = (productId) => {
|
|
4566
|
+
if (productId === selectedProductIdRef.current) return;
|
|
4567
|
+
selectedProductIdRef.current = productId;
|
|
4568
|
+
checkoutAttemptRef.current = void 0;
|
|
4569
|
+
setSelectedProductId(productId);
|
|
4570
|
+
setPurchaseError("");
|
|
4571
|
+
};
|
|
4572
|
+
const closeRecharge = () => {
|
|
4573
|
+
if (order === void 0) setOpen(false);
|
|
4574
|
+
};
|
|
4575
|
+
const closePayment = () => {
|
|
4576
|
+
pollerRef.current?.stop();
|
|
4577
|
+
setOrder(void 0);
|
|
4578
|
+
setOrderStatusError("");
|
|
4579
|
+
setOrderStatusRetryable(false);
|
|
4580
|
+
setOrderStatusRetryInMillis(void 0);
|
|
4581
|
+
setOpen(false);
|
|
4582
|
+
};
|
|
4583
|
+
const retryOrderStatus = () => {
|
|
4584
|
+
if (order === void 0) return;
|
|
4585
|
+
setOrderStatusError("");
|
|
4586
|
+
setOrderStatusRetryInMillis(void 0);
|
|
4587
|
+
if (orderStatusRetryable) pollerRef.current?.refreshNow();
|
|
4588
|
+
else {
|
|
4589
|
+
startPolling(order);
|
|
4590
|
+
pollerRef.current?.refreshNow();
|
|
4591
|
+
}
|
|
4592
|
+
};
|
|
4593
|
+
const regenerateOrder = () => {
|
|
4594
|
+
if (order === void 0) return;
|
|
4595
|
+
const paymentMethod = order.paymentMethod;
|
|
4596
|
+
pollerRef.current?.stop();
|
|
4597
|
+
setOrder(void 0);
|
|
4598
|
+
createPayment(paymentMethod, true);
|
|
4599
|
+
};
|
|
4600
|
+
const triggerProps = {
|
|
4601
|
+
quotaState,
|
|
4602
|
+
onOpen: () => {
|
|
4603
|
+
setOpen(true);
|
|
4604
|
+
loadQuota();
|
|
4605
|
+
},
|
|
4606
|
+
onRefresh: () => {
|
|
4607
|
+
loadQuota();
|
|
4608
|
+
}
|
|
4609
|
+
};
|
|
4610
|
+
(0, react.useEffect)(() => {
|
|
4611
|
+
const dialog = dialogRoot.current;
|
|
4612
|
+
if (!dialog) return;
|
|
4613
|
+
const previous = document.activeElement;
|
|
4614
|
+
const controls = () => [...dialog.querySelectorAll("button:not(:disabled), [href], input:not(:disabled), [tabindex=\"0\"]")];
|
|
4615
|
+
controls()[0]?.focus();
|
|
4616
|
+
const keydown = (event) => {
|
|
4617
|
+
if (event.key === "Escape") {
|
|
4618
|
+
event.preventDefault();
|
|
4619
|
+
event.stopPropagation();
|
|
4620
|
+
if (order) closePayment();
|
|
4621
|
+
else closeRecharge();
|
|
4622
|
+
} else if (event.key === "Tab") {
|
|
4623
|
+
const items = controls(), first = items[0], last = items.at(-1);
|
|
4624
|
+
if (event.shiftKey && document.activeElement === first) {
|
|
4625
|
+
event.preventDefault();
|
|
4626
|
+
last?.focus();
|
|
4627
|
+
} else if (!event.shiftKey && document.activeElement === last) {
|
|
4628
|
+
event.preventDefault();
|
|
4629
|
+
first?.focus();
|
|
4630
|
+
}
|
|
4631
|
+
}
|
|
4632
|
+
};
|
|
4633
|
+
dialog.addEventListener("keydown", keydown);
|
|
4634
|
+
return () => {
|
|
4635
|
+
dialog.removeEventListener("keydown", keydown);
|
|
4636
|
+
if (previous?.isConnected) previous.focus();
|
|
4637
|
+
};
|
|
4638
|
+
}, [open, order?.orderId]);
|
|
4639
|
+
const dialogs = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4640
|
+
ref: dialogRoot,
|
|
4641
|
+
children: [open && order === void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeRechargeDialogView, {
|
|
4642
|
+
quotaState,
|
|
4643
|
+
productsState,
|
|
4644
|
+
selectedProductId,
|
|
4645
|
+
creatingMethod,
|
|
4646
|
+
purchaseError,
|
|
4647
|
+
onClose: closeRecharge,
|
|
4648
|
+
onRefreshQuota: () => {
|
|
4649
|
+
loadQuota();
|
|
4650
|
+
},
|
|
4651
|
+
onRetryProducts: () => {
|
|
4652
|
+
loadProducts();
|
|
4653
|
+
},
|
|
4654
|
+
onSelectProduct: selectProduct,
|
|
4655
|
+
onPurchase: (paymentMethod) => {
|
|
4656
|
+
createPayment(paymentMethod);
|
|
4657
|
+
}
|
|
4658
|
+
}), order !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmePaymentDialog, {
|
|
4659
|
+
order,
|
|
4660
|
+
nowMillis,
|
|
4661
|
+
statusError: orderStatusError,
|
|
4662
|
+
statusRetryable: orderStatusRetryable,
|
|
4663
|
+
...orderStatusRetryInMillis === void 0 ? {} : { statusRetryInMillis: orderStatusRetryInMillis },
|
|
4664
|
+
quotaState,
|
|
4665
|
+
onClose: closePayment,
|
|
4666
|
+
onRetryStatus: retryOrderStatus,
|
|
4667
|
+
onRegenerate: regenerateOrder,
|
|
4668
|
+
onRefreshQuota: () => {
|
|
4669
|
+
loadQuota();
|
|
4670
|
+
},
|
|
4671
|
+
onOpenPaymentUrl: openPaymentUrl
|
|
4672
|
+
})]
|
|
4673
|
+
});
|
|
4674
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4675
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: arkme_billing_default }),
|
|
4676
|
+
renderTrigger ? renderTrigger(triggerProps) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeBalanceSettingsRowView, { ...triggerProps }),
|
|
4677
|
+
(open || order !== void 0) && (renderTrigger && typeof document !== "undefined" ? (0, react_dom.createPortal)(dialogs, document.body) : dialogs)
|
|
4678
|
+
] });
|
|
4679
|
+
}
|
|
4680
|
+
//#endregion
|
|
4681
|
+
//#region src/client/arkme-model-select.css?inline
|
|
4682
|
+
var arkme_model_select_default = ".arkme-model-select {\n min-width: 0;\n font-family: inherit;\n position: relative;\n}\n\n.arkme-model-select button {\n font: inherit;\n cursor: pointer;\n}\n\n.arkme-model-select button:disabled {\n opacity: .5;\n cursor: default;\n}\n\n.arkme-model-select button:focus-visible {\n outline-offset: -2px;\n outline: 2px solid #3578e5;\n}\n\n.arkme-model-trigger {\n max-width: min(360px, 45vw);\n height: 28px;\n color: var(--dsw-alias-label-secondary, #626772);\n background: none;\n border: 0;\n border-radius: 14px;\n align-items: center;\n gap: 5px;\n padding: 0 8px;\n display: flex;\n font-size: 13px !important;\n}\n\n.arkme-model-trigger > span:first-child {\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow: hidden;\n}\n\n.arkme-model-trigger small {\n flex-shrink: 0;\n}\n\n.arkme-model-menu {\n z-index: 30;\n box-sizing: border-box;\n border: 1px solid var(--dsw-alias-border-inverted, #e8e8eb);\n background: var(--dsw-specific-menu, #fff);\n width: max-content;\n min-width: min(280px, 100vw - 32px);\n max-width: min(420px, 100vw - 32px);\n max-height: min(400px, 100vh - 96px);\n color: var(--dsw-alias-label-primary, #20232c);\n border-radius: 12px;\n padding: 5px;\n position: absolute;\n bottom: calc(100% + 8px);\n right: 0;\n overflow-y: auto;\n box-shadow: 0 8px 30px #0002;\n}\n\n.arkme-model-group-heading {\n color: var(--dsw-alias-label-tertiary, #8a8e96);\n justify-content: space-between;\n align-items: center;\n gap: 18px;\n padding: 7px 9px 4px;\n font-size: 12px;\n display: flex;\n}\n\n.arkme-model-balance-actions {\n flex-shrink: 0;\n gap: 8px;\n display: flex;\n}\n\n.arkme-model-balance-actions button {\n color: #3478e5;\n white-space: nowrap;\n background: none;\n border: 0;\n padding: 2px 0;\n}\n\n.arkme-model-balance-actions button:hover {\n text-decoration: underline;\n}\n\n.arkme-model-option {\n width: 100%;\n min-height: 38px;\n color: inherit;\n text-align: left;\n background: none;\n border: 0;\n border-radius: 8px;\n justify-content: space-between;\n align-items: center;\n gap: 18px;\n padding: 8px 10px;\n display: flex;\n font-size: 14px !important;\n}\n\n.arkme-model-option:hover, .arkme-model-trigger:hover {\n background: var(--dsw-alias-interactive-bg-hover, #f3f4f6);\n}\n\n.arkme-model-option > span:first-child {\n overflow-wrap: anywhere;\n min-width: 0;\n}\n\n.arkme-model-option small {\n color: var(--dsw-alias-label-tertiary, #8a8e96);\n font-size: 12px;\n display: block;\n}\n\n.arkme-model-status, .arkme-model-error {\n padding: 8px 10px;\n font-size: 12px;\n}\n\n.arkme-model-error {\n color: var(--dsw-alias-state-error-primary, #b54841);\n}\n\n.arkme-model-error button {\n color: inherit;\n background: none;\n border: 0;\n text-decoration: underline;\n}\n";
|
|
4683
|
+
//#endregion
|
|
4684
|
+
//#region src/client/ArkmeModelSelect.tsx
|
|
4685
|
+
function ArkmeModelSelect({ directory, locked, available }) {
|
|
4686
|
+
const state = (0, react.useSyncExternalStore)(directory.store.subscribe, directory.store.getSnapshot);
|
|
4687
|
+
const [pane, setPane] = (0, react.useState)(null);
|
|
4688
|
+
const root = (0, react.useRef)(null);
|
|
4689
|
+
const trigger = (0, react.useRef)(null);
|
|
4690
|
+
const id = (0, react.useId)();
|
|
4691
|
+
const model = state.groups.find((item) => item.id === state.current?.provider)?.models.find((item) => item.id === state.current?.model);
|
|
4692
|
+
const reasoning = model?.reasoning;
|
|
4693
|
+
const effort = state.current?.reasoningEffort ?? reasoning?.defaultEffort;
|
|
4694
|
+
const effortLabel = reasoning?.efforts.find((item) => item.id === effort)?.name ?? effort ?? "默认";
|
|
4695
|
+
const busy = locked || state.status === "selecting";
|
|
4696
|
+
const reload = () => {
|
|
4697
|
+
directory.load().catch(() => {});
|
|
4698
|
+
};
|
|
4699
|
+
const close = (focus = false) => {
|
|
4700
|
+
setPane(null);
|
|
4701
|
+
if (focus) trigger.current?.focus();
|
|
4702
|
+
};
|
|
4703
|
+
(0, react.useEffect)(() => {
|
|
4704
|
+
if (available) reload();
|
|
4705
|
+
}, [available, directory]);
|
|
4706
|
+
(0, react.useEffect)(() => {
|
|
4707
|
+
if (pane === null) return;
|
|
4708
|
+
const outside = (event) => {
|
|
4709
|
+
if (!root.current?.contains(event.target)) close();
|
|
4710
|
+
};
|
|
4711
|
+
document.addEventListener("pointerdown", outside);
|
|
4712
|
+
root.current?.querySelector("[role=\"menuitemradio\"][aria-checked=\"true\"]")?.focus();
|
|
4713
|
+
return () => document.removeEventListener("pointerdown", outside);
|
|
4714
|
+
}, [pane]);
|
|
4715
|
+
if (!available) return null;
|
|
4716
|
+
const choose = (selection) => {
|
|
4717
|
+
if (busy) return;
|
|
4718
|
+
directory.select(selection).then(() => close(true), () => {});
|
|
4719
|
+
};
|
|
4720
|
+
const keyboard = (event) => {
|
|
4721
|
+
if (pane === null) return;
|
|
4722
|
+
if (event.key === "Escape") {
|
|
4723
|
+
event.preventDefault();
|
|
4724
|
+
event.stopPropagation();
|
|
4725
|
+
close(true);
|
|
4726
|
+
return;
|
|
4727
|
+
}
|
|
4728
|
+
if (![
|
|
4729
|
+
"ArrowDown",
|
|
4730
|
+
"ArrowUp",
|
|
4731
|
+
"Home",
|
|
4732
|
+
"End"
|
|
4733
|
+
].includes(event.key)) return;
|
|
4734
|
+
const items = [...root.current?.querySelectorAll("[role=\"menu\"] button:not(:disabled)") ?? []];
|
|
4735
|
+
if (items.length === 0) return;
|
|
4736
|
+
event.preventDefault();
|
|
4737
|
+
const current = items.indexOf(document.activeElement);
|
|
4738
|
+
items[event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : (current + (event.key === "ArrowDown" ? 1 : -1) + items.length) % items.length]?.focus();
|
|
4739
|
+
};
|
|
4740
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeBillingSettings, {
|
|
4741
|
+
active: pane === "model" && state.groups.some((item) => item.id === "arkme-managed"),
|
|
4742
|
+
renderTrigger: ({ quotaState, onOpen, onRefresh }) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4743
|
+
className: "arkme-model-select",
|
|
4744
|
+
ref: root,
|
|
4745
|
+
onKeyDown: keyboard,
|
|
4746
|
+
onBlur: (event) => {
|
|
4747
|
+
if (event.relatedTarget instanceof Node && !root.current?.contains(event.relatedTarget)) close();
|
|
4748
|
+
},
|
|
4749
|
+
children: [
|
|
4750
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: arkme_model_select_default }),
|
|
4751
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4752
|
+
ref: trigger,
|
|
4753
|
+
type: "button",
|
|
4754
|
+
className: "arkme-model-trigger",
|
|
4755
|
+
disabled: locked,
|
|
4756
|
+
"aria-label": `选择模型:${model?.name ?? "选择模型"}`,
|
|
4757
|
+
"aria-haspopup": "menu",
|
|
4758
|
+
"aria-expanded": pane !== null,
|
|
4759
|
+
"aria-controls": pane === null ? void 0 : id,
|
|
4760
|
+
onClick: () => {
|
|
4761
|
+
if (pane !== null) close();
|
|
4762
|
+
else {
|
|
4763
|
+
setPane("model");
|
|
4764
|
+
reload();
|
|
4765
|
+
}
|
|
4766
|
+
},
|
|
4767
|
+
children: [
|
|
4768
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: model?.name ?? "选择模型" }),
|
|
4769
|
+
reasoning && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [" · ", effortLabel] }),
|
|
4770
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4771
|
+
"aria-hidden": true,
|
|
4772
|
+
children: "⌄"
|
|
4773
|
+
})
|
|
4774
|
+
]
|
|
4775
|
+
}),
|
|
4776
|
+
pane !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4777
|
+
id,
|
|
4778
|
+
className: "arkme-model-menu",
|
|
4779
|
+
role: "menu",
|
|
4780
|
+
"aria-label": "模型选择",
|
|
4781
|
+
"aria-busy": state.status === "loading" || busy,
|
|
4782
|
+
children: [pane === "effort" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4783
|
+
type: "button",
|
|
4784
|
+
role: "menuitem",
|
|
4785
|
+
className: "arkme-model-option",
|
|
4786
|
+
onClick: () => setPane("model"),
|
|
4787
|
+
children: "‹ 返回模型列表"
|
|
4788
|
+
}), reasoning && [{
|
|
4789
|
+
id: void 0,
|
|
4790
|
+
name: "默认",
|
|
4791
|
+
description: void 0
|
|
4792
|
+
}, ...reasoning.efforts].map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4793
|
+
type: "button",
|
|
4794
|
+
role: "menuitemradio",
|
|
4795
|
+
className: "arkme-model-option",
|
|
4796
|
+
"aria-checked": item.id === (state.current?.reasoningEffort ?? void 0),
|
|
4797
|
+
disabled: busy,
|
|
4798
|
+
onClick: () => {
|
|
4799
|
+
if (state.current) choose({
|
|
4800
|
+
provider: state.current.provider,
|
|
4801
|
+
model: state.current.model,
|
|
4802
|
+
...item.id === void 0 ? {} : { reasoningEffort: item.id }
|
|
4803
|
+
});
|
|
4804
|
+
},
|
|
4805
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [item.name, item.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: item.description })] }), item.id === state.current?.reasoningEffort && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4806
|
+
"aria-hidden": true,
|
|
4807
|
+
children: "✓"
|
|
4808
|
+
})]
|
|
4809
|
+
}, item.id ?? "default"))] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4810
|
+
reasoning && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4811
|
+
type: "button",
|
|
4812
|
+
role: "menuitem",
|
|
4813
|
+
className: "arkme-model-option",
|
|
4814
|
+
disabled: busy,
|
|
4815
|
+
onClick: () => setPane("effort"),
|
|
4816
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: "思考强度" }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("small", { children: [effortLabel, " ›"] })]
|
|
4817
|
+
}),
|
|
4818
|
+
state.status === "loading" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4819
|
+
className: "arkme-model-status",
|
|
4820
|
+
role: "status",
|
|
4821
|
+
children: "正在加载模型…"
|
|
4822
|
+
}),
|
|
4823
|
+
state.failures.map((failure) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4824
|
+
className: "arkme-model-error",
|
|
4825
|
+
role: "alert",
|
|
4826
|
+
children: [
|
|
4827
|
+
failure.name,
|
|
4828
|
+
":",
|
|
4829
|
+
failure.message
|
|
4830
|
+
]
|
|
4831
|
+
}, failure.id)),
|
|
4832
|
+
state.groups.map((provider) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4833
|
+
role: "group",
|
|
4834
|
+
"aria-labelledby": `${id}-${provider.id}`,
|
|
4835
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4836
|
+
className: "arkme-model-group-heading",
|
|
4837
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4838
|
+
id: `${id}-${provider.id}`,
|
|
4839
|
+
children: [provider.id === "arkme-managed" ? "Arkme" : provider.name, provider.id === "arkme-managed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4840
|
+
"aria-live": "polite",
|
|
4841
|
+
children: [" · ", quotaState.kind === "ready" ? formatArkmeNanoCny(quotaState.quota.availableNanoCny) : quotaState.kind === "loading" ? "余额加载中…" : "余额读取失败"]
|
|
4842
|
+
})]
|
|
4843
|
+
}), provider.id === "arkme-managed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4844
|
+
className: "arkme-model-balance-actions",
|
|
4845
|
+
children: [quotaState.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4846
|
+
type: "button",
|
|
4847
|
+
onClick: onRefresh,
|
|
4848
|
+
children: "重试"
|
|
4849
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4850
|
+
type: "button",
|
|
4851
|
+
onClick: () => {
|
|
4852
|
+
close(true);
|
|
4853
|
+
onOpen();
|
|
4854
|
+
},
|
|
4855
|
+
children: "去充值"
|
|
4856
|
+
})]
|
|
4857
|
+
})]
|
|
4858
|
+
}), provider.models.map((item) => {
|
|
4859
|
+
const selected = state.current?.provider === provider.id && state.current.model === item.id;
|
|
4860
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4861
|
+
type: "button",
|
|
4862
|
+
role: "menuitemradio",
|
|
4863
|
+
className: "arkme-model-option",
|
|
4864
|
+
"aria-checked": selected,
|
|
4865
|
+
disabled: busy,
|
|
4866
|
+
onClick: () => selected ? close(true) : choose({
|
|
4867
|
+
provider: provider.id,
|
|
4868
|
+
model: item.id
|
|
4869
|
+
}),
|
|
4870
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [item.name, item.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: item.description })] }), selected && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4871
|
+
"aria-hidden": true,
|
|
4872
|
+
children: "✓"
|
|
4873
|
+
})]
|
|
4874
|
+
}, item.id);
|
|
4875
|
+
})]
|
|
4876
|
+
}, provider.id)),
|
|
4877
|
+
state.status === "ready" && state.groups.every((item) => item.models.length === 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4878
|
+
className: "arkme-model-status",
|
|
4879
|
+
children: "暂无可用模型"
|
|
4880
|
+
})
|
|
4881
|
+
] }), (state.error || state.failures.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4882
|
+
className: "arkme-model-error",
|
|
4883
|
+
role: "alert",
|
|
4884
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: state.error }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4885
|
+
type: "button",
|
|
4886
|
+
disabled: busy,
|
|
4887
|
+
onClick: reload,
|
|
4888
|
+
children: "重新加载"
|
|
4889
|
+
})]
|
|
4890
|
+
})]
|
|
4891
|
+
})
|
|
4892
|
+
]
|
|
4893
|
+
})
|
|
4894
|
+
});
|
|
4895
|
+
}
|
|
4896
|
+
//#endregion
|
|
4897
|
+
//#region src/client/harness-model-client.tsx
|
|
4898
|
+
function ModelSeat({ sessionId, ...props }) {
|
|
4899
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ArkmeModelSelect, { ...props }, sessionId);
|
|
4900
|
+
}
|
|
4901
|
+
const inject = ["slots", "sessions"];
|
|
4902
|
+
function apply(ctx) {
|
|
4903
|
+
ctx.inject(["modelDirectories"], (scope) => {
|
|
4904
|
+
const models = scope.get("modelDirectories");
|
|
4905
|
+
const sessions = scope.sessions;
|
|
4906
|
+
if (typeof models?.directoryFor !== "function" || typeof sessions.subagentAddress !== "function") return;
|
|
4907
|
+
scope.slots.inject("conversation.input.model", () => scope.slots.register({
|
|
4908
|
+
name: "conversation.input.model",
|
|
4909
|
+
priority: -100,
|
|
4910
|
+
inject: (sessionId) => ({
|
|
4911
|
+
directory: models.directoryFor(sessionId),
|
|
4912
|
+
available: sessions.subagentAddress(sessionId) === void 0
|
|
4913
|
+
})
|
|
4914
|
+
}, ModelSeat));
|
|
4915
|
+
});
|
|
4916
|
+
}
|
|
4917
|
+
//#endregion
|
|
4918
|
+
exports.apply = apply;
|
|
4919
|
+
exports.inject = inject;
|
|
4920
|
+
return module.exports;
|
|
4921
|
+
}
|
|
4922
|
+
});
|