@storvia/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs.js +431 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.mts +234 -0
- package/dist/index.esm.js +431 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.umd.js +432 -0
- package/dist/index.umd.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/settings-sheet.ts","../src/modules/chat.ts","../src/modules/characters.ts","../src/modules/relationships.ts","../src/modules/inventory.ts","../src/modules/world.ts","../src/modules/player.ts","../src/modules/custom.ts","../src/StorviaSDK.ts"],"sourcesContent":["export { StorviaSDK } from './StorviaSDK'\nexport { StorviaError } from './errors'\nexport type {\n StorviaErrorCode,\n ChatSendOptions,\n ChatSaveOptions,\n ChatGenerateOptions,\n ChatResponse,\n CharacterPersona,\n CharacterDetail,\n Relationship,\n InventoryItemData,\n PlayerPersona,\n} from './types'\n","import type { StorviaErrorCode } from './types'\n\nexport class StorviaError extends Error {\n code: StorviaErrorCode\n\n constructor(code: StorviaErrorCode, message: string) {\n super(message)\n this.name = 'StorviaError'\n this.code = code\n }\n}\n","import { StorviaError } from './errors'\nimport type { StorviaErrorCode } from './types'\n\nexport class HttpClient {\n private baseUrl: string\n private token: string\n private debug: boolean\n\n constructor(baseUrl: string, token: string, debug = false) {\n this.baseUrl = baseUrl\n this.token = token\n this.debug = debug\n }\n\n setToken(token: string) {\n this.token = token\n }\n\n setDebug(enabled: boolean) {\n this.debug = enabled\n }\n\n private buildHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.token}`,\n }\n }\n\n private log(method: string, path: string, body?: unknown, response?: unknown, error?: unknown) {\n if (!this.debug) return\n const tag = '%c[Storvia SDK]'\n const style = 'color: #6366f1; font-weight: bold'\n if (error) {\n console.groupCollapsed(`${tag} %c${method} ${path} %c✗`, style, 'color: #e0e0e0', 'color: #ef4444')\n if (body) console.log('Request:', body)\n console.error('Error:', error)\n console.groupEnd()\n } else {\n console.groupCollapsed(`${tag} %c${method} ${path} %c✓`, style, 'color: #e0e0e0', 'color: #22c55e')\n if (body) console.log('Request:', body)\n if (response !== undefined) console.log('Response:', response)\n console.groupEnd()\n }\n }\n\n async get<T>(path: string, params?: Record<string, string>): Promise<T> {\n const url = new URL(`${this.baseUrl}${path}`)\n if (params) {\n Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v))\n }\n\n try {\n const res = await fetch(url.toString(), {\n method: 'GET',\n headers: this.buildHeaders(),\n })\n const data = await this.handleResponse<T>(res)\n this.log('GET', path, params, data)\n return data\n } catch (e) {\n this.log('GET', path, params, undefined, e)\n throw e\n }\n }\n\n async post<T>(path: string, body?: unknown): Promise<T> {\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: this.buildHeaders(),\n body: body ? JSON.stringify(body) : undefined,\n })\n const data = await this.handleResponse<T>(res)\n this.log('POST', path, body, data)\n return data\n } catch (e) {\n this.log('POST', path, body, undefined, e)\n throw e\n }\n }\n\n async patch<T>(path: string, body?: unknown): Promise<T> {\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'PATCH',\n headers: this.buildHeaders(),\n body: body ? JSON.stringify(body) : undefined,\n })\n const data = await this.handleResponse<T>(res)\n this.log('PATCH', path, body, data)\n return data\n } catch (e) {\n this.log('PATCH', path, body, undefined, e)\n throw e\n }\n }\n\n async delete<T>(path: string): Promise<T> {\n try {\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'DELETE',\n headers: this.buildHeaders(),\n })\n const data = await this.handleResponse<T>(res)\n this.log('DELETE', path, undefined, data)\n return data\n } catch (e) {\n this.log('DELETE', path, undefined, undefined, e)\n throw e\n }\n }\n\n async stream(\n path: string,\n body: unknown,\n onChunk: (text: string) => void,\n onDone: (id: string) => void\n ): Promise<void> {\n this.log('STREAM', path, body)\n\n const res = await fetch(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: this.buildHeaders(),\n body: JSON.stringify(body),\n })\n\n if (!res.ok) {\n const err = await res.json().catch(() => ({}))\n const error = new StorviaError(\n (err.code as StorviaErrorCode) || 'INTERNAL_ERROR',\n err.message || `HTTP ${res.status}`\n )\n this.log('STREAM', path, body, undefined, error)\n throw error\n }\n\n const reader = res.body?.getReader()\n if (!reader) {\n throw new StorviaError('INTERNAL_ERROR', 'No response body for stream')\n }\n\n const decoder = new TextDecoder()\n let messageId = ''\n let accumulated = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n const lines = chunk.split('\\n')\n\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue\n const data = line.slice(6)\n\n if (data === '[DONE]') {\n if (this.debug) {\n this.log('STREAM', path + ' [done]', undefined, { id: messageId, contentLength: accumulated.length })\n }\n onDone(messageId)\n return\n }\n\n try {\n const parsed = JSON.parse(data)\n if (parsed.type === 'done' && parsed.id) {\n messageId = parsed.id\n } else if (parsed.content) {\n accumulated += parsed.content\n onChunk(parsed.content)\n }\n } catch {\n // non-JSON data line, ignore\n }\n }\n }\n\n if (messageId) onDone(messageId)\n }\n\n private async handleResponse<T>(res: Response): Promise<T> {\n if (!res.ok) {\n let err: { code?: string; message?: string } = {}\n try {\n err = await res.json()\n } catch {\n // ignore\n }\n\n const codeMap: Record<number, StorviaErrorCode> = {\n 401: 'UNAUTHORIZED',\n 402: 'INSUFFICIENT_BALANCE',\n 404: 'GAME_NOT_FOUND',\n 429: 'RATE_LIMITED',\n }\n\n const code =\n (err.code as StorviaErrorCode) || codeMap[res.status] || 'INTERNAL_ERROR'\n throw new StorviaError(code, err.message || `HTTP ${res.status}`)\n }\n\n const json = await res.json()\n // 解包 { success, data } 格式\n if (json && typeof json === 'object' && 'data' in json) {\n return json.data as T\n }\n return json as T\n }\n}\n","interface ModelOption {\n id: string\n name: string\n provider: string\n}\n\nexport interface GameSettings {\n temperature: number\n reply_length: string\n reply_language: string\n player_note: string\n}\n\ntype SyncState = 'idle' | 'loading' | 'success' | 'error'\n\ninterface SettingsCallbacks {\n onModelChange: (id: string) => void\n onSettingsChange: (settings: Partial<GameSettings>) => Promise<void>\n onSync: () => Promise<void>\n}\n\nconst LANGUAGES = [\n { value: 'zh-CN', label: '简体中文' },\n { value: 'zh-TW', label: '繁體中文' },\n { value: 'en', label: 'English' },\n { value: 'ja', label: '日本語' },\n { value: 'ko', label: '한국어' },\n]\n\nconst LENGTH_OPTIONS = [\n { value: 'low', label: '简短' },\n { value: 'medium', label: '适中' },\n { value: 'high', label: '详细' },\n]\n\nconst STYLES = `\n :host {\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 2147483647;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n }\n\n .fab {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n background: #6366f1;\n color: #fff;\n border: none;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n box-shadow: 0 4px 12px rgba(0,0,0,0.3);\n transition: transform 0.2s, box-shadow 0.2s;\n }\n\n .fab:hover {\n transform: scale(1.08);\n box-shadow: 0 6px 16px rgba(0,0,0,0.4);\n }\n\n .fab svg {\n width: 22px;\n height: 22px;\n }\n\n .panel {\n position: absolute;\n bottom: 60px;\n right: 0;\n width: 320px;\n max-height: 80vh;\n overflow-y: auto;\n background: #1e1e1e;\n border: 1px solid #333;\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(0,0,0,0.6);\n display: none;\n }\n\n .panel.open {\n display: block;\n }\n\n .panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 16px 8px;\n }\n\n .panel-title {\n font-size: 16px;\n font-weight: 600;\n color: #fff;\n }\n\n .close-btn {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n border: none;\n background: transparent;\n color: #888;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 18px;\n transition: background 0.15s;\n }\n\n .close-btn:hover {\n background: #2a2a2a;\n color: #fff;\n }\n\n .panel-body {\n padding: 4px 16px 16px;\n }\n\n .section {\n margin-bottom: 16px;\n }\n\n .section:last-child {\n margin-bottom: 0;\n }\n\n .section-label {\n font-size: 13px;\n font-weight: 500;\n color: #c0c0c0;\n margin-bottom: 6px;\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n\n .section-label .value {\n color: #6366f1;\n font-weight: 600;\n }\n\n .section-desc {\n font-size: 11px;\n color: #666;\n margin-bottom: 8px;\n line-height: 1.4;\n }\n\n .divider {\n height: 1px;\n background: #2a2a2a;\n margin: 12px 0;\n }\n\n /* Model options */\n .model-list {\n display: flex;\n flex-direction: column;\n gap: 2px;\n }\n\n .model-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px;\n border-radius: 8px;\n cursor: pointer;\n border: none;\n background: transparent;\n color: #e0e0e0;\n width: 100%;\n font-size: 13px;\n text-align: left;\n transition: background 0.15s;\n }\n\n .model-option:hover {\n background: #2a2a2a;\n }\n\n .model-option.active {\n background: #6366f1;\n color: #fff;\n }\n\n .model-option .dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: #555;\n flex-shrink: 0;\n }\n\n .model-option.active .dot {\n background: #fff;\n }\n\n /* Slider */\n .slider-wrap {\n position: relative;\n padding: 0 2px;\n }\n\n .slider {\n -webkit-appearance: none;\n appearance: none;\n width: 100%;\n height: 4px;\n border-radius: 2px;\n background: #333;\n outline: none;\n }\n\n .slider::-webkit-slider-thumb {\n -webkit-appearance: none;\n appearance: none;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: #6366f1;\n cursor: pointer;\n border: 2px solid #1e1e1e;\n box-shadow: 0 2px 4px rgba(0,0,0,0.3);\n }\n\n .slider::-moz-range-thumb {\n width: 20px;\n height: 20px;\n border-radius: 50%;\n background: #6366f1;\n cursor: pointer;\n border: 2px solid #1e1e1e;\n box-shadow: 0 2px 4px rgba(0,0,0,0.3);\n }\n\n .slider-labels {\n display: flex;\n justify-content: space-between;\n font-size: 11px;\n color: #666;\n margin-top: 4px;\n }\n\n /* Segment control (reply length) */\n .segment {\n display: flex;\n background: #2a2a2a;\n border-radius: 10px;\n padding: 3px;\n gap: 2px;\n }\n\n .segment-btn {\n flex: 1;\n padding: 8px 0;\n border: none;\n border-radius: 8px;\n background: transparent;\n color: #999;\n font-size: 13px;\n cursor: pointer;\n transition: all 0.2s;\n }\n\n .segment-btn.active {\n background: #6366f1;\n color: #fff;\n font-weight: 500;\n }\n\n .segment-btn:not(.active):hover {\n color: #ddd;\n }\n\n /* Select */\n .select-wrap {\n position: relative;\n }\n\n .select {\n width: 100%;\n padding: 10px 32px 10px 12px;\n border-radius: 10px;\n border: 1px solid #333;\n background: #2a2a2a;\n color: #e0e0e0;\n font-size: 13px;\n appearance: none;\n -webkit-appearance: none;\n cursor: pointer;\n outline: none;\n transition: border-color 0.15s;\n }\n\n .select:focus {\n border-color: #6366f1;\n }\n\n .select-arrow {\n position: absolute;\n right: 12px;\n top: 50%;\n transform: translateY(-50%);\n pointer-events: none;\n color: #666;\n font-size: 12px;\n }\n\n /* Textarea (note) */\n .note-wrap {\n position: relative;\n }\n\n .note-input {\n width: 100%;\n padding: 10px 12px;\n border-radius: 10px;\n border: 1px solid #333;\n background: #2a2a2a;\n color: #e0e0e0;\n font-size: 13px;\n font-family: inherit;\n resize: none;\n height: 64px;\n outline: none;\n transition: border-color 0.15s;\n box-sizing: border-box;\n }\n\n .note-input::placeholder {\n color: #555;\n }\n\n .note-input:focus {\n border-color: #6366f1;\n }\n\n .note-counter {\n text-align: right;\n font-size: 11px;\n color: #555;\n margin-top: 4px;\n }\n\n /* Sync section */\n .sync-desc {\n font-size: 11px;\n color: #666;\n margin-bottom: 8px;\n line-height: 1.4;\n }\n\n .sync-btn {\n width: 100%;\n padding: 10px 0;\n border-radius: 10px;\n border: 1px solid #333;\n background: #2a2a2a;\n color: #e0e0e0;\n font-size: 13px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n transition: background 0.15s;\n }\n\n .sync-btn:hover:not(:disabled) {\n background: #333;\n }\n\n .sync-btn:disabled {\n cursor: default;\n opacity: 0.6;\n }\n\n .sync-btn svg {\n width: 16px;\n height: 16px;\n }\n\n .sync-btn.success { color: #4ade80; border-color: #4ade80; }\n .sync-btn.error { color: #f87171; border-color: #f87171; }\n\n /* Save button */\n .save-row {\n display: flex;\n justify-content: flex-end;\n margin-top: 16px;\n }\n\n .save-btn {\n padding: 8px 24px;\n border-radius: 10px;\n border: none;\n background: #6366f1;\n color: #fff;\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.15s;\n }\n\n .save-btn:hover:not(:disabled) {\n background: #5558e6;\n }\n\n .save-btn:disabled {\n opacity: 0.6;\n cursor: default;\n }\n\n .save-btn.saved {\n background: #4ade80;\n }\n\n /* FAB red dot */\n .fab-dot {\n position: absolute;\n top: 2px;\n right: 2px;\n width: 10px;\n height: 10px;\n border-radius: 50%;\n background: #f87171;\n border: 2px solid #1e1e1e;\n display: none;\n }\n\n .fab-dot.visible {\n display: block;\n }\n\n .fab-wrap {\n position: relative;\n display: inline-block;\n }\n\n /* Sync update hint */\n .sync-hint {\n font-size: 11px;\n color: #f59e0b;\n margin-bottom: 6px;\n line-height: 1.4;\n display: none;\n }\n\n .sync-hint.visible {\n display: block;\n }\n\n @keyframes spin {\n to { transform: rotate(360deg); }\n }\n .spin { animation: spin 0.8s linear infinite; }\n`\n\nconst ICON_GEAR = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"3\"/><path d=\"M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z\"/></svg>`\nconst ICON_SYNC = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"23 4 23 10 17 10\"/><polyline points=\"1 20 1 14 7 14\"/><path d=\"M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15\"/></svg>`\nconst ICON_SYNC_SPIN = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"spin\"><path d=\"M21 12a9 9 0 1 1-6.219-8.56\"/></svg>`\nconst ICON_CHECK = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><polyline points=\"20 6 9 17 4 12\"/></svg>`\nconst ICON_ERROR = `<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/></svg>`\n\nexport class SettingsSheet {\n private shadowRoot: ShadowRoot\n private host: HTMLElement\n private callbacks: SettingsCallbacks\n\n // Model state\n private selectedModelId = ''\n private models: ModelOption[] = []\n private modelPanel: HTMLElement | null = null\n\n // Settings state (local copy, saved on \"保存\")\n private settings: GameSettings = {\n temperature: 1.0,\n reply_length: 'medium',\n reply_language: 'zh-CN',\n player_note: '',\n }\n\n // World update state\n private hasWorldUpdate = false\n private fabDot: HTMLElement | null = null\n private syncHint: HTMLElement | null = null\n\n // Display mode state\n private displayMode: 'phone' | 'desktop' = 'phone'\n private displayModeBtns: HTMLButtonElement[] = []\n\n // UI refs\n private tempValueEl: HTMLElement | null = null\n private tempSlider: HTMLInputElement | null = null\n private segmentBtns: HTMLButtonElement[] = []\n private langSelect: HTMLSelectElement | null = null\n private noteInput: HTMLTextAreaElement | null = null\n private noteCounter: HTMLElement | null = null\n private saveBtn: HTMLButtonElement | null = null\n private syncBtn: HTMLButtonElement | null = null\n private syncState: SyncState = 'idle'\n private panel: HTMLElement | null = null\n\n constructor(callbacks: SettingsCallbacks) {\n this.callbacks = callbacks\n this.host = document.createElement('div')\n this.host.id = 'storvia-settings'\n this.shadowRoot = this.host.attachShadow({ mode: 'closed' })\n }\n\n mount(models: ModelOption[], defaultModelId: string, settings: GameSettings, hasWorldUpdate = false) {\n this.models = models\n this.selectedModelId = defaultModelId\n this.settings = { ...settings }\n this.hasWorldUpdate = hasWorldUpdate\n try {\n const saved = localStorage.getItem('storvia_display_mode')\n if (saved === 'phone' || saved === 'desktop') this.displayMode = saved\n } catch { /* ignore */ }\n document.body.appendChild(this.host)\n this.render()\n }\n\n unmount() {\n this.host.remove()\n }\n\n getSelectedModelId(): string {\n return this.selectedModelId\n }\n\n getSettings(): GameSettings {\n return { ...this.settings }\n }\n\n private render() {\n const style = document.createElement('style')\n style.textContent = STYLES\n\n const container = document.createElement('div')\n\n // FAB with red dot\n const fabWrap = document.createElement('div')\n fabWrap.className = 'fab-wrap'\n const fab = document.createElement('button')\n fab.className = 'fab'\n fab.innerHTML = ICON_GEAR\n fab.title = '游戏设置'\n const fabDot = document.createElement('div')\n fabDot.className = `fab-dot${this.hasWorldUpdate ? ' visible' : ''}`\n this.fabDot = fabDot\n fabWrap.appendChild(fab)\n fabWrap.appendChild(fabDot)\n\n // Panel\n const panel = document.createElement('div')\n panel.className = 'panel'\n this.panel = panel\n\n // Header\n const header = document.createElement('div')\n header.className = 'panel-header'\n const title = document.createElement('div')\n title.className = 'panel-title'\n title.textContent = '游戏设置'\n const closeBtn = document.createElement('button')\n closeBtn.className = 'close-btn'\n closeBtn.innerHTML = '✕'\n closeBtn.addEventListener('click', () => panel.classList.remove('open'))\n header.appendChild(title)\n header.appendChild(closeBtn)\n panel.appendChild(header)\n\n // Body\n const body = document.createElement('div')\n body.className = 'panel-body'\n\n // ── AI Model ──\n if (this.models.length > 0) {\n body.appendChild(this.buildModelSection())\n body.appendChild(this.makeDivider())\n }\n\n // ── Temperature ──\n body.appendChild(this.buildTemperatureSection())\n body.appendChild(this.makeDivider())\n\n // ── Reply Length ──\n body.appendChild(this.buildReplyLengthSection())\n body.appendChild(this.makeDivider())\n\n // ── Reply Language ──\n body.appendChild(this.buildReplyLanguageSection())\n body.appendChild(this.makeDivider())\n\n // ── Note ──\n body.appendChild(this.buildNoteSection())\n body.appendChild(this.makeDivider())\n\n // ── Display Mode ──\n body.appendChild(this.buildDisplayModeSection())\n body.appendChild(this.makeDivider())\n\n // ── Sync ──\n body.appendChild(this.buildSyncSection())\n\n // ── Save Button ──\n const saveRow = document.createElement('div')\n saveRow.className = 'save-row'\n const saveBtn = document.createElement('button')\n saveBtn.className = 'save-btn'\n saveBtn.textContent = '保存'\n this.saveBtn = saveBtn\n saveBtn.addEventListener('click', () => this.handleSave())\n saveRow.appendChild(saveBtn)\n body.appendChild(saveRow)\n\n panel.appendChild(body)\n\n // Toggle\n fab.addEventListener('click', () => panel.classList.toggle('open'))\n document.addEventListener('click', (e) => {\n if (!this.host.contains(e.target as Node)) panel.classList.remove('open')\n })\n\n container.appendChild(panel)\n container.appendChild(fabWrap)\n this.shadowRoot.appendChild(style)\n this.shadowRoot.appendChild(container)\n }\n\n // ── Build sections ──\n\n private buildModelSection(): HTMLElement {\n const section = document.createElement('div')\n section.className = 'section'\n\n const label = document.createElement('div')\n label.className = 'section-label'\n label.textContent = 'AI 模型'\n section.appendChild(label)\n\n const list = document.createElement('div')\n list.className = 'model-list'\n this.modelPanel = list\n\n this.models.forEach((model) => {\n const btn = document.createElement('button')\n btn.className = `model-option${model.id === this.selectedModelId ? ' active' : ''}`\n btn.innerHTML = `<span class=\"dot\"></span><span>${model.name}</span>`\n btn.addEventListener('click', () => {\n this.selectedModelId = model.id\n this.callbacks.onModelChange(model.id)\n try { localStorage.setItem('storvia_model_id', model.id) } catch { /* ignore */ }\n this.updateModelActive()\n })\n list.appendChild(btn)\n })\n\n section.appendChild(list)\n return section\n }\n\n private buildTemperatureSection(): HTMLElement {\n const section = document.createElement('div')\n section.className = 'section'\n\n const label = document.createElement('div')\n label.className = 'section-label'\n const labelText = document.createElement('span')\n labelText.textContent = '温度'\n const valueText = document.createElement('span')\n valueText.className = 'value'\n valueText.textContent = this.settings.temperature.toFixed(1)\n this.tempValueEl = valueText\n label.appendChild(labelText)\n label.appendChild(valueText)\n section.appendChild(label)\n\n const desc = document.createElement('div')\n desc.className = 'section-desc'\n desc.textContent = '更低的温度会让 AI 回复更稳定、理智;更高的温度会让 AI 的回复更具多样性和随机性'\n section.appendChild(desc)\n\n const wrap = document.createElement('div')\n wrap.className = 'slider-wrap'\n const slider = document.createElement('input')\n slider.type = 'range'\n slider.className = 'slider'\n slider.min = '0'\n slider.max = '2'\n slider.step = '0.1'\n slider.value = String(this.settings.temperature)\n this.tempSlider = slider\n slider.addEventListener('input', () => {\n const v = parseFloat(slider.value)\n this.settings.temperature = v\n if (this.tempValueEl) this.tempValueEl.textContent = v.toFixed(1)\n })\n wrap.appendChild(slider)\n\n const labels = document.createElement('div')\n labels.className = 'slider-labels'\n labels.innerHTML = '<span>精确</span><span>创意</span>'\n wrap.appendChild(labels)\n\n section.appendChild(wrap)\n return section\n }\n\n private buildReplyLengthSection(): HTMLElement {\n const section = document.createElement('div')\n section.className = 'section'\n\n const label = document.createElement('div')\n label.className = 'section-label'\n label.textContent = '回复长度'\n section.appendChild(label)\n\n const segment = document.createElement('div')\n segment.className = 'segment'\n this.segmentBtns = []\n\n LENGTH_OPTIONS.forEach((opt) => {\n const btn = document.createElement('button')\n btn.className = `segment-btn${opt.value === this.settings.reply_length ? ' active' : ''}`\n btn.textContent = opt.label\n btn.addEventListener('click', () => {\n this.settings.reply_length = opt.value\n this.segmentBtns.forEach((b, i) => {\n b.classList.toggle('active', LENGTH_OPTIONS[i].value === opt.value)\n })\n })\n this.segmentBtns.push(btn)\n segment.appendChild(btn)\n })\n\n section.appendChild(segment)\n return section\n }\n\n private buildReplyLanguageSection(): HTMLElement {\n const section = document.createElement('div')\n section.className = 'section'\n\n const label = document.createElement('div')\n label.className = 'section-label'\n label.textContent = '回复语言'\n section.appendChild(label)\n\n const wrap = document.createElement('div')\n wrap.className = 'select-wrap'\n const select = document.createElement('select')\n select.className = 'select'\n this.langSelect = select\n\n LANGUAGES.forEach((lang) => {\n const option = document.createElement('option')\n option.value = lang.value\n option.textContent = lang.label\n if (lang.value === this.settings.reply_language) option.selected = true\n select.appendChild(option)\n })\n\n select.addEventListener('change', () => {\n this.settings.reply_language = select.value\n })\n\n const arrow = document.createElement('span')\n arrow.className = 'select-arrow'\n arrow.textContent = '▾'\n wrap.appendChild(select)\n wrap.appendChild(arrow)\n\n section.appendChild(wrap)\n return section\n }\n\n private buildNoteSection(): HTMLElement {\n const section = document.createElement('div')\n section.className = 'section'\n\n const label = document.createElement('div')\n label.className = 'section-label'\n const labelText = document.createElement('span')\n labelText.textContent = '备注'\n const counter = document.createElement('span')\n counter.textContent = `${this.settings.player_note.length}/50`\n this.noteCounter = counter\n label.appendChild(labelText)\n label.appendChild(counter)\n section.appendChild(label)\n\n const desc = document.createElement('div')\n desc.className = 'section-desc'\n desc.textContent = '写下你对这个游戏的备注,会显示在存档列表'\n section.appendChild(desc)\n\n const wrap = document.createElement('div')\n wrap.className = 'note-wrap'\n const textarea = document.createElement('textarea')\n textarea.className = 'note-input'\n textarea.placeholder = '备注'\n textarea.maxLength = 50\n textarea.value = this.settings.player_note\n this.noteInput = textarea\n\n textarea.addEventListener('input', () => {\n this.settings.player_note = textarea.value\n if (this.noteCounter) {\n this.noteCounter.textContent = `${textarea.value.length}/50`\n }\n })\n\n wrap.appendChild(textarea)\n section.appendChild(wrap)\n return section\n }\n\n private buildDisplayModeSection(): HTMLElement {\n const section = document.createElement('div')\n section.className = 'section'\n\n const label = document.createElement('div')\n label.className = 'section-label'\n label.textContent = '显示模式'\n section.appendChild(label)\n\n const desc = document.createElement('div')\n desc.className = 'section-desc'\n desc.textContent = '电脑模式会自动横屏显示'\n section.appendChild(desc)\n\n const segment = document.createElement('div')\n segment.className = 'segment'\n this.displayModeBtns = []\n\n const DISPLAY_OPTIONS = [\n { value: 'phone' as const, label: '手机' },\n { value: 'desktop' as const, label: '电脑' },\n ]\n\n DISPLAY_OPTIONS.forEach((opt) => {\n const btn = document.createElement('button')\n btn.className = `segment-btn${opt.value === this.displayMode ? ' active' : ''}`\n btn.textContent = opt.label\n btn.addEventListener('click', () => {\n this.displayMode = opt.value\n try { localStorage.setItem('storvia_display_mode', opt.value) } catch { /* ignore */ }\n this.displayModeBtns.forEach((b, i) => {\n b.classList.toggle('active', DISPLAY_OPTIONS[i].value === opt.value)\n })\n // 通知父页面旋转\n try {\n window.parent.postMessage({\n type: 'storvia-orientation',\n mode: opt.value === 'desktop' ? 'landscape' : 'portrait',\n }, '*')\n } catch { /* ignore */ }\n })\n this.displayModeBtns.push(btn)\n segment.appendChild(btn)\n })\n\n section.appendChild(segment)\n return section\n }\n\n private buildSyncSection(): HTMLElement {\n const section = document.createElement('div')\n section.className = 'section'\n\n const label = document.createElement('div')\n label.className = 'section-label'\n label.textContent = '同步游戏设定'\n section.appendChild(label)\n\n // Update hint (only visible when hasWorldUpdate)\n const hint = document.createElement('div')\n hint.className = `sync-hint${this.hasWorldUpdate ? ' visible' : ''}`\n hint.textContent = '⚠ 作者已更新游戏内容,建议点击同步'\n this.syncHint = hint\n section.appendChild(hint)\n\n const desc = document.createElement('div')\n desc.className = 'sync-desc'\n desc.textContent = '如果游戏作者更新了内容,点击此按钮可将最新的游戏设定和开幕小剧场同步到当前对话'\n section.appendChild(desc)\n\n const syncBtn = document.createElement('button')\n syncBtn.className = 'sync-btn'\n this.syncBtn = syncBtn\n this.renderSyncBtn()\n\n syncBtn.addEventListener('click', async () => {\n if (this.syncState === 'loading') return\n this.setSyncState('loading')\n try {\n await this.callbacks.onSync()\n this.setSyncState('success')\n // Clear update indicators\n this.hasWorldUpdate = false\n if (this.fabDot) this.fabDot.classList.remove('visible')\n if (this.syncHint) this.syncHint.classList.remove('visible')\n setTimeout(() => this.setSyncState('idle'), 3000)\n } catch {\n this.setSyncState('error')\n setTimeout(() => this.setSyncState('idle'), 4000)\n }\n })\n\n section.appendChild(syncBtn)\n return section\n }\n\n // ── Helpers ──\n\n private makeDivider(): HTMLElement {\n const d = document.createElement('div')\n d.className = 'divider'\n return d\n }\n\n private updateModelActive() {\n if (!this.modelPanel) return\n this.modelPanel.querySelectorAll('.model-option').forEach((btn, i) => {\n btn.classList.toggle('active', this.models[i].id === this.selectedModelId)\n })\n }\n\n private setSyncState(state: SyncState) {\n this.syncState = state\n this.renderSyncBtn()\n }\n\n private renderSyncBtn() {\n if (!this.syncBtn) return\n const s = this.syncState\n const icons: Record<SyncState, string> = {\n idle: ICON_SYNC,\n loading: ICON_SYNC_SPIN,\n success: ICON_CHECK,\n error: ICON_ERROR,\n }\n const labels: Record<SyncState, string> = {\n idle: '同步最新设定',\n loading: '同步中...',\n success: '已同步',\n error: '同步失败',\n }\n this.syncBtn.className = `sync-btn${s === 'success' || s === 'error' ? ` ${s}` : ''}`\n this.syncBtn.disabled = s === 'loading'\n this.syncBtn.innerHTML = `${icons[s]}<span>${labels[s]}</span>`\n }\n\n private async handleSave() {\n if (!this.saveBtn || this.saveBtn.disabled) return\n this.saveBtn.disabled = true\n this.saveBtn.textContent = '保存中...'\n try {\n await this.callbacks.onSettingsChange({\n temperature: this.settings.temperature,\n reply_length: this.settings.reply_length,\n reply_language: this.settings.reply_language,\n player_note: this.settings.player_note,\n })\n this.saveBtn.textContent = '已保存'\n this.saveBtn.classList.add('saved')\n setTimeout(() => {\n if (this.saveBtn) {\n this.saveBtn.textContent = '保存'\n this.saveBtn.classList.remove('saved')\n this.saveBtn.disabled = false\n }\n }, 2000)\n } catch {\n this.saveBtn.textContent = '保存失败'\n setTimeout(() => {\n if (this.saveBtn) {\n this.saveBtn.textContent = '保存'\n this.saveBtn.disabled = false\n }\n }, 2000)\n }\n }\n}\n","import type { HttpClient } from '../http'\nimport type { ChatSendOptions, ChatSaveOptions, ChatGenerateOptions, ChatResponse } from '../types'\n\nexport class ChatModule {\n private previewConfig?: Record<string, unknown>\n\n constructor(\n private http: HttpClient,\n private getGameId: () => string,\n private getConversationId: () => string,\n private getModelId: () => string,\n private isPreview: boolean = false,\n previewConfig?: Record<string, unknown>,\n ) {\n this.previewConfig = previewConfig\n }\n\n /** 更新预览配置(由父页面 postMessage 触发) */\n updatePreviewConfig(config?: Record<string, unknown>): void {\n this.previewConfig = config\n }\n\n /**\n * 发送消息并获取 AI 回复(user 消息 + AI 回复一体化)\n * 适用于 1v1 对话场景\n */\n async send(options: ChatSendOptions): Promise<ChatResponse | void> {\n if (this.isPreview) {\n return this.sendPreview(options)\n }\n\n const body = {\n game_id: this.getGameId(),\n conversation_id: this.getConversationId(),\n model_id: this.getModelId(),\n message: options.message,\n topic: options.topic,\n context: options.context,\n stream: options.stream ?? false,\n }\n\n if (options.stream) {\n await this.http.stream(\n '/chat',\n body,\n options.onChunk ?? (() => {}),\n options.onDone ?? (() => {})\n )\n return\n }\n\n return this.http.post<ChatResponse>('/chat', body)\n }\n\n /**\n * 单独保存一条消息(不调 AI,不扣费)\n * 适用于群聊中先保存用户消息,再分别请求各 NPC 回复\n */\n async save(options: ChatSaveOptions): Promise<{ id: string }> {\n return this.http.post<{ id: string }>('/messages/save', {\n conversation_id: this.getConversationId(),\n role: options.role,\n content: options.content,\n topic: options.topic,\n })\n }\n\n /**\n * 基于 topic 历史 + context 请求 AI 生成回复(无需当前轮用户输入)\n * 适用于群聊 NPC 回复、NPC 自动发言、弹幕生成等\n */\n async generate(options: ChatGenerateOptions): Promise<ChatResponse | void> {\n const body = {\n conversation_id: this.getConversationId(),\n model_id: this.getModelId(),\n topic: options.topic,\n context: options.context,\n stream: options.stream ?? false,\n save: options.save ?? true,\n }\n\n if (options.stream) {\n await this.http.stream(\n '/generate',\n body,\n options.onChunk ?? (() => {}),\n options.onDone ?? (() => {})\n )\n return\n }\n\n return this.http.post<ChatResponse>('/generate', body)\n }\n\n /** 预览模式:发到 /preview 端点 */\n private async sendPreview(options: ChatSendOptions): Promise<ChatResponse | void> {\n const body = {\n model_id: this.getModelId(),\n message: options.message,\n topic: options.topic,\n context: options.context,\n prompt_config: this.previewConfig || {},\n history_messages: [],\n }\n\n if (options.stream) {\n await this.http.stream(\n '/preview',\n body,\n options.onChunk ?? (() => {}),\n options.onDone ?? (() => {})\n )\n return\n }\n\n let content = ''\n await this.http.stream(\n '/preview',\n body,\n (chunk) => { content += chunk },\n () => {}\n )\n return { id: '', content }\n }\n}\n","import type { HttpClient } from '../http'\nimport type { CharacterPersona, CharacterDetail } from '../types'\n\n/** 内部存储含 description(用于 add 传给后端),但不对外暴露 */\ninterface CharacterInternal {\n name: string\n description?: string\n avatar?: string\n}\n\nclass CharacterStateModule {\n private mem: Record<string, Record<string, string | number>> = {}\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {}\n\n async get(characterName: string): Promise<Record<string, string | number>> {\n if (this.isPreview) return { ...(this.mem[characterName] || {}) }\n return this.http.get(`/conversations/${this.getConversationId()}/characters/${encodeURIComponent(characterName)}/state`)\n }\n\n async update(characterName: string, values: Record<string, string | number>): Promise<void> {\n if (this.isPreview) {\n this.mem[characterName] = { ...(this.mem[characterName] || {}), ...values }\n return\n }\n await this.http.patch(`/conversations/${this.getConversationId()}/characters/${encodeURIComponent(characterName)}/state`, values)\n }\n}\n\nexport class CharactersModule {\n state: CharacterStateModule\n private mem: CharacterInternal[] = []\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {\n this.state = new CharacterStateModule(http, getConversationId, isPreview)\n }\n\n async list(): Promise<CharacterPersona[]> {\n if (this.isPreview) return this.mem.map(c => ({ name: c.name, avatar: c.avatar }))\n return this.http.get(`/conversations/${this.getConversationId()}/characters`)\n }\n\n async get(name: string): Promise<CharacterPersona> {\n if (this.isPreview) {\n const c = this.mem.find(c => c.name === name)\n return { name: c?.name || name, avatar: c?.avatar }\n }\n return this.http.get(`/conversations/${this.getConversationId()}/characters/${encodeURIComponent(name)}`)\n }\n\n async add(character: { name: string; description?: string; avatar?: string }): Promise<void> {\n if (this.isPreview) { this.mem.push(character); return }\n await this.http.post(`/conversations/${this.getConversationId()}/characters`, character)\n }\n\n async update(name: string, data: Partial<Omit<CharacterPersona, 'name'>>): Promise<void> {\n if (this.isPreview) { const c = this.mem.find(c => c.name === name); if (c) Object.assign(c, data); return }\n await this.http.patch(`/conversations/${this.getConversationId()}/characters/${encodeURIComponent(name)}`, data)\n }\n\n async remove(name: string): Promise<void> {\n if (this.isPreview) { this.mem = this.mem.filter(c => c.name !== name); return }\n await this.http.delete(`/conversations/${this.getConversationId()}/characters/${encodeURIComponent(name)}`)\n }\n\n async detail(name: string): Promise<CharacterDetail> {\n if (this.isPreview) {\n const c = this.mem.find(c => c.name === name)\n return { name, avatar: c?.avatar, state: await this.state.get(name) }\n }\n return this.http.get(`/conversations/${this.getConversationId()}/characters/${encodeURIComponent(name)}/detail`)\n }\n}\n","import type { HttpClient } from '../http'\nimport type { Relationship } from '../types'\n\nexport class RelationshipsModule {\n private mem: Relationship[] = []\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {}\n\n async list(): Promise<Relationship[]> {\n if (this.isPreview) return [...this.mem]\n return this.http.get(`/conversations/${this.getConversationId()}/relationships`)\n }\n\n async get(from: string, to: string): Promise<Relationship> {\n if (this.isPreview) {\n const r = this.mem.find(r => r.from === from && r.to === to)\n if (r) return { ...r }\n throw new Error('Relationship not found')\n }\n return this.http.get(`/conversations/${this.getConversationId()}/relationships/${encodeURIComponent(from)}/${encodeURIComponent(to)}`)\n }\n\n async add(relationship: Relationship): Promise<void> {\n if (this.isPreview) { this.mem.push({ ...relationship }); return }\n await this.http.post(`/conversations/${this.getConversationId()}/relationships`, relationship)\n }\n\n async update(from: string, to: string, data: Record<string, string | number>): Promise<void> {\n if (this.isPreview) {\n const r = this.mem.find(r => r.from === from && r.to === to)\n if (r) Object.assign(r, data)\n else this.mem.push({ from, to, ...data })\n return\n }\n await this.http.patch(`/conversations/${this.getConversationId()}/relationships/${encodeURIComponent(from)}/${encodeURIComponent(to)}`, data)\n }\n\n async remove(from: string, to: string): Promise<void> {\n if (this.isPreview) { this.mem = this.mem.filter(r => !(r.from === from && r.to === to)); return }\n await this.http.delete(`/conversations/${this.getConversationId()}/relationships/${encodeURIComponent(from)}/${encodeURIComponent(to)}`)\n }\n}\n","import type { HttpClient } from '../http'\nimport type { InventoryItemData } from '../types'\n\nexport class InventoryModule {\n private mem: InventoryItemData[] = []\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {}\n\n async list(group?: string): Promise<InventoryItemData[]> {\n if (this.isPreview) return group ? this.mem.filter(i => i.group === group) : [...this.mem]\n const params = group ? { group } : undefined\n return this.http.get(`/conversations/${this.getConversationId()}/inventory`, params)\n }\n\n async add(item: InventoryItemData): Promise<void> {\n if (this.isPreview) { this.mem.push({ ...item }); return }\n await this.http.post(`/conversations/${this.getConversationId()}/inventory`, item)\n }\n\n async update(name: string, data: Partial<Omit<InventoryItemData, 'name'>>): Promise<void> {\n if (this.isPreview) {\n const i = this.mem.find(i => i.name === name)\n if (i) Object.assign(i, data)\n return\n }\n await this.http.patch(`/conversations/${this.getConversationId()}/inventory/${encodeURIComponent(name)}`, data)\n }\n\n async remove(name: string): Promise<void> {\n if (this.isPreview) { this.mem = this.mem.filter(i => i.name !== name); return }\n await this.http.delete(`/conversations/${this.getConversationId()}/inventory/${encodeURIComponent(name)}`)\n }\n}\n","import type { HttpClient } from '../http'\n\nexport class WorldModule {\n private mem: Record<string, unknown> = {}\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {}\n\n async get(): Promise<Record<string, unknown>> {\n if (this.isPreview) return { ...this.mem }\n return this.http.get(`/conversations/${this.getConversationId()}/world`)\n }\n\n async update(data: Record<string, unknown>): Promise<void> {\n if (this.isPreview) { Object.assign(this.mem, data); return }\n await this.http.patch(`/conversations/${this.getConversationId()}/world`, data)\n }\n}\n","import type { HttpClient } from '../http'\nimport type { PlayerPersona } from '../types'\n\nclass PlayerStateModule {\n private mem: Record<string, Record<string, string | number>> = {}\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {}\n\n async get(): Promise<Record<string, Record<string, string | number>>>\n async get(group: string): Promise<Record<string, string | number>>\n async get(group?: string): Promise<Record<string, string | number> | Record<string, Record<string, string | number>>> {\n if (this.isPreview) {\n if (group) return { ...(this.mem[group] || {}) }\n return { ...this.mem }\n }\n const params = group ? { group } : undefined\n return this.http.get(`/conversations/${this.getConversationId()}/player/state`, params)\n }\n\n async update(group: string, values: Record<string, string | number>): Promise<void> {\n if (this.isPreview) {\n this.mem[group] = { ...(this.mem[group] || {}), ...values }\n return\n }\n await this.http.patch(`/conversations/${this.getConversationId()}/player/state`, { group, values })\n }\n}\n\nexport class PlayerModule {\n state: PlayerStateModule\n private memPersona: PlayerPersona = { name: '' }\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {\n this.state = new PlayerStateModule(http, getConversationId, isPreview)\n }\n\n async get(): Promise<PlayerPersona> {\n if (this.isPreview) return { ...this.memPersona }\n return this.http.get(`/conversations/${this.getConversationId()}/player`)\n }\n}\n","import type { HttpClient } from '../http'\n\nexport class CustomModule {\n private mem: Record<string, unknown> = {}\n\n constructor(\n private http: HttpClient,\n private getConversationId: () => string,\n private isPreview: boolean = false\n ) {}\n\n async get(): Promise<Record<string, unknown>>\n async get(key: string): Promise<unknown>\n async get(key?: string): Promise<unknown> {\n if (this.isPreview) {\n if (key !== undefined) return this.mem[key]\n return { ...this.mem }\n }\n const data = await this.http.get<Record<string, unknown>>(\n `/conversations/${this.getConversationId()}/custom`\n )\n if (key !== undefined) return data[key]\n return data\n }\n\n async set(data: Record<string, unknown>): Promise<void> {\n if (this.isPreview) {\n // 深合并:分组对象合并子字段\n for (const [key, value] of Object.entries(data)) {\n if (typeof value === 'object' && value !== null && !Array.isArray(value)\n && typeof this.mem[key] === 'object' && this.mem[key] !== null) {\n this.mem[key] = { ...(this.mem[key] as Record<string, unknown>), ...value }\n } else {\n this.mem[key] = value\n }\n }\n return\n }\n await this.http.patch(`/conversations/${this.getConversationId()}/custom`, data)\n }\n\n async delete(key: string): Promise<void> {\n if (this.isPreview) { delete this.mem[key]; return }\n await this.http.delete(`/conversations/${this.getConversationId()}/custom/${encodeURIComponent(key)}`)\n }\n\n async setGroupField(groupKey: string, fieldKey: string, value: unknown): Promise<void> {\n if (this.isPreview) {\n if (!this.mem[groupKey] || typeof this.mem[groupKey] !== 'object') {\n this.mem[groupKey] = {}\n }\n (this.mem[groupKey] as Record<string, unknown>)[fieldKey] = value\n return\n }\n await this.http.patch(\n `/conversations/${this.getConversationId()}/custom/${encodeURIComponent(groupKey)}/${encodeURIComponent(fieldKey)}`,\n { value }\n )\n }\n\n async deleteGroupField(groupKey: string, fieldKey: string): Promise<void> {\n if (this.isPreview) {\n if (typeof this.mem[groupKey] === 'object' && this.mem[groupKey] !== null) {\n const group = this.mem[groupKey] as Record<string, unknown>\n delete group[fieldKey]\n if (Object.keys(group).length === 0) delete this.mem[groupKey]\n }\n return\n }\n await this.http.delete(\n `/conversations/${this.getConversationId()}/custom/${encodeURIComponent(groupKey)}/${encodeURIComponent(fieldKey)}`\n )\n }\n}\n","import { HttpClient } from './http'\nimport { StorviaError } from './errors'\nimport { SettingsSheet } from './settings-sheet'\nimport type { GameSettings } from './settings-sheet'\nimport { ChatModule } from './modules/chat'\nimport { CharactersModule } from './modules/characters'\nimport { RelationshipsModule } from './modules/relationships'\nimport { InventoryModule } from './modules/inventory'\nimport { WorldModule } from './modules/world'\nimport { PlayerModule } from './modules/player'\nimport { CustomModule } from './modules/custom'\n\nconst API_PATH = '/functions/v1/world-engine/v1'\n\ninterface StorviaEnv {\n apiBase?: string\n gameId?: string\n accessToken?: string\n /** 预览模式标志:跳过 auth,chat 发到 /preview,其他模块内存模式 */\n preview?: boolean\n /** 预览模式下的 prompt 配置 */\n previewConfig?: {\n game_prompt?: string | null\n game_config?: Record<string, unknown>\n game_state?: Record<string, unknown>\n persona_settings?: Record<string, unknown>\n reply_length?: string\n reply_language?: string\n }\n /** 预览模式下由父页面传入的模型列表 */\n models?: { id: string; name: string; provider: string }[]\n /** 预览模式下由父页面传入的选中模型 ID */\n selectedModelId?: string\n /** 开启 debug 模式:打印所有请求/响应日志 */\n debug?: boolean\n}\n\nfunction getEnv(): StorviaEnv {\n return ((window as unknown as Record<string, StorviaEnv>).__STORVIA__) || {}\n}\n\n/**\n * 等待父页面通过 postMessage 注入 __STORVIA__ 配置\n * 支持两种消息格式:\n * 1. 正式环境 (World.vue): { type: 'storvia-init', apiBase, gameId, accessToken }\n * 2. 预览环境 (CreatePreviewModal): { type: 'storvia-preview-init', config: { apiBase, gameId, accessToken, preview, previewConfig } }\n * 如果 __STORVIA__ 已存在则立即返回\n */\nfunction waitForEnv(timeoutMs = 5000): Promise<StorviaEnv> {\n const existing = getEnv()\n if (existing.apiBase) return Promise.resolve(existing)\n\n return new Promise<StorviaEnv>((resolve, reject) => {\n const timer = setTimeout(() => {\n window.removeEventListener('message', handler)\n reject(new StorviaError('NOT_INITIALIZED', 'Timed out waiting for __STORVIA__ config from parent frame.'))\n }, timeoutMs)\n\n function handler(event: MessageEvent) {\n let env: StorviaEnv | null = null\n\n if (event.data?.type === 'storvia-init' && event.data.apiBase) {\n // 正式环境:World.vue 发送的扁平格式\n env = {\n apiBase: event.data.apiBase,\n gameId: event.data.gameId,\n accessToken: event.data.accessToken,\n }\n } else if (event.data?.type === 'storvia-preview-init' && event.data.config) {\n // 预览环境:CreatePreviewModal 发送的嵌套格式\n env = event.data.config\n }\n\n if (env && env.apiBase) {\n clearTimeout(timer)\n window.removeEventListener('message', handler)\n const w = window as unknown as Record<string, StorviaEnv>\n w.__STORVIA__ = env\n resolve(env)\n }\n }\n\n window.addEventListener('message', handler)\n })\n}\n\nexport class StorviaSDK {\n private http!: HttpClient\n private settingsSheet!: SettingsSheet\n private baseUrl = ''\n private gameId = ''\n private conversationId = ''\n private modelId = ''\n private accessToken = ''\n private initialized = false\n private isPreview = false\n private previewConfig: StorviaEnv['previewConfig'] = undefined\n\n chat!: ChatModule\n characters!: CharactersModule\n relationships!: RelationshipsModule\n inventory!: InventoryModule\n world!: WorldModule\n player!: PlayerModule\n custom!: CustomModule\n\n async init(): Promise<void> {\n // 尝试获取环境配置;如果不存在则等待父页面 postMessage 注入(预览场景)\n const env = await waitForEnv()\n\n const apiBase = env.apiBase\n if (!apiBase) {\n throw new StorviaError(\n 'NOT_INITIALIZED',\n 'window.__STORVIA__.apiBase is not set. The platform must inject it before SDK init.'\n )\n }\n this.baseUrl = `${apiBase}${API_PATH}`\n this.isPreview = !!env.preview\n this.previewConfig = env.previewConfig\n\n if (!env.gameId) {\n throw new StorviaError(\n 'GAME_NOT_FOUND',\n 'window.__STORVIA__.gameId is not set. The platform must inject it before SDK init.'\n )\n }\n this.gameId = env.gameId\n\n // 获取用户 access token(由主站通过 postMessage 注入到 __STORVIA__)\n if (!env.accessToken) {\n throw new StorviaError(\n 'UNAUTHORIZED',\n 'window.__STORVIA__.accessToken is not set. The platform must inject the user token before SDK init.'\n )\n }\n this.accessToken = env.accessToken\n\n this.http = new HttpClient(this.baseUrl, this.accessToken, !!env.debug)\n\n if (this.isPreview) {\n // ===== 预览模式:跳过 auth,使用占位 conversationId =====\n this.conversationId = '00000000-0000-0000-0000-000000000000'\n\n // 模型列表和选中模型由父页面传入\n const models = env.models || []\n this.modelId = env.selectedModelId || models[0]?.id || ''\n\n // 挂载设置面板(预览也需要模型选择和设置入口)\n this.settingsSheet = new SettingsSheet({\n onModelChange: (id) => { this.modelId = id },\n onSettingsChange: () => Promise.resolve(),\n onSync: () => Promise.resolve(),\n })\n if (models.length > 0) {\n this.settingsSheet.mount(models, this.modelId, {\n temperature: 1.0,\n reply_length: 'medium',\n reply_language: 'zh-CN',\n player_note: '',\n }, false)\n }\n\n const getGameId = () => this.gameId\n const getConversationId = () => this.conversationId\n const getModelId = () => this.modelId\n this.chat = new ChatModule(this.http, getGameId, getConversationId, getModelId, true, this.previewConfig as Record<string, unknown> | undefined)\n this.characters = new CharactersModule(this.http, getConversationId, true)\n this.relationships = new RelationshipsModule(this.http, getConversationId, true)\n this.inventory = new InventoryModule(this.http, getConversationId, true)\n this.world = new WorldModule(this.http, getConversationId, true)\n this.player = new PlayerModule(this.http, getConversationId, true)\n this.custom = new CustomModule(this.http, getConversationId, true)\n\n this.initialized = true\n\n // 监听父页面的配置更新(模型选择、prompt 变更等)\n this.listenForPreviewMessages()\n return\n }\n\n // ===== 正常模式 =====\n const authResult = await this.authenticate()\n this.conversationId = authResult.conversationId\n\n const models = authResult.models || []\n const savedModelId = this.getSavedModelId()\n this.modelId =\n savedModelId && models.some((m: { id: string }) => m.id === savedModelId)\n ? savedModelId\n : models[0]?.id || ''\n\n const settings: GameSettings = authResult.settings || {\n temperature: 1.0,\n reply_length: 'medium',\n reply_language: 'zh-CN',\n player_note: '',\n }\n\n this.settingsSheet = new SettingsSheet({\n onModelChange: (id) => { this.modelId = id },\n onSettingsChange: (s) => this.saveSettings(s),\n onSync: () => this.syncSettings(),\n })\n this.settingsSheet.mount(models, this.modelId, settings, authResult.hasWorldUpdate ?? false)\n\n const getGameId = () => this.gameId\n const getConversationId = () => this.conversationId\n const getModelId = () => this.modelId\n this.chat = new ChatModule(this.http, getGameId, getConversationId, getModelId)\n this.characters = new CharactersModule(this.http, getConversationId)\n this.relationships = new RelationshipsModule(this.http, getConversationId)\n this.inventory = new InventoryModule(this.http, getConversationId)\n this.world = new WorldModule(this.http, getConversationId)\n this.player = new PlayerModule(this.http, getConversationId)\n this.custom = new CustomModule(this.http, getConversationId)\n\n this.initialized = true\n }\n\n /** 监听父页面 postMessage(预览模式) */\n private listenForPreviewMessages(): void {\n window.addEventListener('message', (event: MessageEvent) => {\n if (!event.data || event.data.type !== 'storvia-preview') return\n\n if (event.data.action === 'set-model') {\n this.modelId = event.data.modelId || ''\n }\n\n if (event.data.action === 'update-config') {\n this.previewConfig = event.data.previewConfig\n // 更新 ChatModule 的 previewConfig\n if (this.chat) {\n this.chat.updatePreviewConfig(this.previewConfig as Record<string, unknown> | undefined)\n }\n }\n })\n }\n\n /** 保存设置到后端 */\n private async saveSettings(settings: Partial<GameSettings>): Promise<void> {\n await this.http.patch('/settings', {\n conversation_id: this.conversationId,\n ...settings,\n })\n }\n\n /** 同步最新游戏设定到当前存档 */\n async syncSettings(): Promise<void> {\n await this.http.post('/sync', { conversation_id: this.conversationId })\n }\n\n private async authenticate(): Promise<{\n conversationId: string\n models: { id: string; name: string; provider: string }[]\n settings?: GameSettings\n hasWorldUpdate?: boolean\n }> {\n const res = await fetch(`${this.baseUrl}/auth`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.accessToken}`,\n },\n body: JSON.stringify({ game_id: this.gameId }),\n })\n\n if (!res.ok) {\n if (res.status === 401) throw new StorviaError('UNAUTHORIZED', 'User not authenticated')\n if (res.status === 404) throw new StorviaError('GAME_NOT_FOUND', 'Game not found or not published')\n throw new StorviaError('INTERNAL_ERROR', `Auth failed: HTTP ${res.status}`)\n }\n\n const json = await res.json()\n return json.data ?? json\n }\n\n private getSavedModelId(): string {\n try {\n return localStorage.getItem('storvia_model_id') || ''\n } catch {\n return ''\n }\n }\n}\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,kBAAAE,EAAA,eAAAC,IAAA,eAAAC,EAAAJ,GCEO,IAAMK,EAAN,cAA2B,KAAM,CAGtC,YAAYC,EAAwBC,EAAiB,CACnD,MAAMA,CAAO,EACb,KAAK,KAAO,eACZ,KAAK,KAAOD,CACd,CACF,ECPO,IAAME,EAAN,KAAiB,CAKtB,YAAYC,EAAiBC,EAAeC,EAAQ,GAAO,CACzD,KAAK,QAAUF,EACf,KAAK,MAAQC,EACb,KAAK,MAAQC,CACf,CAEA,SAASD,EAAe,CACtB,KAAK,MAAQA,CACf,CAEA,SAASE,EAAkB,CACzB,KAAK,MAAQA,CACf,CAEQ,cAAuC,CAC7C,MAAO,CACL,eAAgB,mBAChB,cAAe,UAAU,KAAK,KAAK,EACrC,CACF,CAEQ,IAAIC,EAAgBC,EAAcC,EAAgBC,EAAoBC,EAAiB,CAC7F,GAAI,CAAC,KAAK,MAAO,OACjB,IAAMC,EAAM,kBACNC,EAAQ,oCACVF,GACF,QAAQ,eAAe,GAAGC,CAAG,MAAML,CAAM,IAAIC,CAAI,YAAQK,EAAO,iBAAkB,gBAAgB,EAC9FJ,GAAM,QAAQ,IAAI,WAAYA,CAAI,EACtC,QAAQ,MAAM,SAAUE,CAAK,EAC7B,QAAQ,SAAS,IAEjB,QAAQ,eAAe,GAAGC,CAAG,MAAML,CAAM,IAAIC,CAAI,YAAQK,EAAO,iBAAkB,gBAAgB,EAC9FJ,GAAM,QAAQ,IAAI,WAAYA,CAAI,EAClCC,IAAa,QAAW,QAAQ,IAAI,YAAaA,CAAQ,EAC7D,QAAQ,SAAS,EAErB,CAEA,MAAM,IAAOF,EAAcM,EAA6C,CACtE,IAAMC,EAAM,IAAI,IAAI,GAAG,KAAK,OAAO,GAAGP,CAAI,EAAE,EACxCM,GACF,OAAO,QAAQA,CAAM,EAAE,QAAQ,CAAC,CAACE,EAAGC,CAAC,IAAMF,EAAI,aAAa,IAAIC,EAAGC,CAAC,CAAC,EAGvE,GAAI,CACF,IAAMC,EAAM,MAAM,MAAMH,EAAI,SAAS,EAAG,CACtC,OAAQ,MACR,QAAS,KAAK,aAAa,CAC7B,CAAC,EACKI,EAAO,MAAM,KAAK,eAAkBD,CAAG,EAC7C,YAAK,IAAI,MAAOV,EAAMM,EAAQK,CAAI,EAC3BA,CACT,OAASC,EAAG,CACV,WAAK,IAAI,MAAOZ,EAAMM,EAAQ,OAAWM,CAAC,EACpCA,CACR,CACF,CAEA,MAAM,KAAQZ,EAAcC,EAA4B,CACtD,GAAI,CACF,IAAMS,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAGV,CAAI,GAAI,CAChD,OAAQ,OACR,QAAS,KAAK,aAAa,EAC3B,KAAMC,EAAO,KAAK,UAAUA,CAAI,EAAI,MACtC,CAAC,EACKU,EAAO,MAAM,KAAK,eAAkBD,CAAG,EAC7C,YAAK,IAAI,OAAQV,EAAMC,EAAMU,CAAI,EAC1BA,CACT,OAASC,EAAG,CACV,WAAK,IAAI,OAAQZ,EAAMC,EAAM,OAAWW,CAAC,EACnCA,CACR,CACF,CAEA,MAAM,MAASZ,EAAcC,EAA4B,CACvD,GAAI,CACF,IAAMS,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAGV,CAAI,GAAI,CAChD,OAAQ,QACR,QAAS,KAAK,aAAa,EAC3B,KAAMC,EAAO,KAAK,UAAUA,CAAI,EAAI,MACtC,CAAC,EACKU,EAAO,MAAM,KAAK,eAAkBD,CAAG,EAC7C,YAAK,IAAI,QAASV,EAAMC,EAAMU,CAAI,EAC3BA,CACT,OAASC,EAAG,CACV,WAAK,IAAI,QAASZ,EAAMC,EAAM,OAAWW,CAAC,EACpCA,CACR,CACF,CAEA,MAAM,OAAUZ,EAA0B,CACxC,GAAI,CACF,IAAMU,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAGV,CAAI,GAAI,CAChD,OAAQ,SACR,QAAS,KAAK,aAAa,CAC7B,CAAC,EACKW,EAAO,MAAM,KAAK,eAAkBD,CAAG,EAC7C,YAAK,IAAI,SAAUV,EAAM,OAAWW,CAAI,EACjCA,CACT,OAASC,EAAG,CACV,WAAK,IAAI,SAAUZ,EAAM,OAAW,OAAWY,CAAC,EAC1CA,CACR,CACF,CAEA,MAAM,OACJZ,EACAC,EACAY,EACAC,EACe,CACf,KAAK,IAAI,SAAUd,EAAMC,CAAI,EAE7B,IAAMS,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,GAAGV,CAAI,GAAI,CAChD,OAAQ,OACR,QAAS,KAAK,aAAa,EAC3B,KAAM,KAAK,UAAUC,CAAI,CAC3B,CAAC,EAED,GAAI,CAACS,EAAI,GAAI,CACX,IAAMK,EAAM,MAAML,EAAI,KAAK,EAAE,MAAM,KAAO,CAAC,EAAE,EACvCP,EAAQ,IAAIa,EACfD,EAAI,MAA6B,iBAClCA,EAAI,SAAW,QAAQL,EAAI,MAAM,EACnC,EACA,WAAK,IAAI,SAAUV,EAAMC,EAAM,OAAWE,CAAK,EACzCA,CACR,CAEA,IAAMc,EAASP,EAAI,MAAM,UAAU,EACnC,GAAI,CAACO,EACH,MAAM,IAAID,EAAa,iBAAkB,6BAA6B,EAGxE,IAAME,EAAU,IAAI,YAChBC,EAAY,GACZC,EAAc,GAElB,OAAa,CACX,GAAM,CAAE,KAAAC,EAAM,MAAAC,CAAM,EAAI,MAAML,EAAO,KAAK,EAC1C,GAAII,EAAM,MAGV,IAAME,EADQL,EAAQ,OAAOI,EAAO,CAAE,OAAQ,EAAK,CAAC,EAChC,MAAM;AAAA,CAAI,EAE9B,QAAWE,KAAQD,EAAO,CACxB,GAAI,CAACC,EAAK,WAAW,QAAQ,EAAG,SAChC,IAAMb,EAAOa,EAAK,MAAM,CAAC,EAEzB,GAAIb,IAAS,SAAU,CACjB,KAAK,OACP,KAAK,IAAI,SAAUX,EAAO,UAAW,OAAW,CAAE,GAAImB,EAAW,cAAeC,EAAY,MAAO,CAAC,EAEtGN,EAAOK,CAAS,EAChB,MACF,CAEA,GAAI,CACF,IAAMM,EAAS,KAAK,MAAMd,CAAI,EAC1Bc,EAAO,OAAS,QAAUA,EAAO,GACnCN,EAAYM,EAAO,GACVA,EAAO,UAChBL,GAAeK,EAAO,QACtBZ,EAAQY,EAAO,OAAO,EAE1B,MAAQ,CAER,CACF,CACF,CAEIN,GAAWL,EAAOK,CAAS,CACjC,CAEA,MAAc,eAAkBT,EAA2B,CACzD,GAAI,CAACA,EAAI,GAAI,CACX,IAAIK,EAA2C,CAAC,EAChD,GAAI,CACFA,EAAM,MAAML,EAAI,KAAK,CACvB,MAAQ,CAER,CAEA,IAAMgB,EAA4C,CAChD,IAAK,eACL,IAAK,uBACL,IAAK,iBACL,IAAK,cACP,EAEMC,EACHZ,EAAI,MAA6BW,EAAQhB,EAAI,MAAM,GAAK,iBAC3D,MAAM,IAAIM,EAAaW,EAAMZ,EAAI,SAAW,QAAQL,EAAI,MAAM,EAAE,CAClE,CAEA,IAAMkB,EAAO,MAAMlB,EAAI,KAAK,EAE5B,OAAIkB,GAAQ,OAAOA,GAAS,UAAY,SAAUA,EACzCA,EAAK,KAEPA,CACT,CACF,EC7LA,IAAMC,EAAY,CAChB,CAAE,MAAO,QAAS,MAAO,0BAAO,EAChC,CAAE,MAAO,QAAS,MAAO,0BAAO,EAChC,CAAE,MAAO,KAAM,MAAO,SAAU,EAChC,CAAE,MAAO,KAAM,MAAO,oBAAM,EAC5B,CAAE,MAAO,KAAM,MAAO,oBAAM,CAC9B,EAEMC,EAAiB,CACrB,CAAE,MAAO,MAAO,MAAO,cAAK,EAC5B,CAAE,MAAO,SAAU,MAAO,cAAK,EAC/B,CAAE,MAAO,OAAQ,MAAO,cAAK,CAC/B,EAEMC,EAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8aTC,EAAY,qxBACZC,EAAY,4RACZC,EAAiB,wLACjBC,EAAa,uKACbC,EAAa,oPAENC,EAAN,KAAoB,CAuCzB,YAAYC,EAA8B,CAjC1C,KAAQ,gBAAkB,GAC1B,KAAQ,OAAwB,CAAC,EACjC,KAAQ,WAAiC,KAGzC,KAAQ,SAAyB,CAC/B,YAAa,EACb,aAAc,SACd,eAAgB,QAChB,YAAa,EACf,EAGA,KAAQ,eAAiB,GACzB,KAAQ,OAA6B,KACrC,KAAQ,SAA+B,KAGvC,KAAQ,YAAmC,QAC3C,KAAQ,gBAAuC,CAAC,EAGhD,KAAQ,YAAkC,KAC1C,KAAQ,WAAsC,KAC9C,KAAQ,YAAmC,CAAC,EAC5C,KAAQ,WAAuC,KAC/C,KAAQ,UAAwC,KAChD,KAAQ,YAAkC,KAC1C,KAAQ,QAAoC,KAC5C,KAAQ,QAAoC,KAC5C,KAAQ,UAAuB,OAC/B,KAAQ,MAA4B,KAGlC,KAAK,UAAYA,EACjB,KAAK,KAAO,SAAS,cAAc,KAAK,EACxC,KAAK,KAAK,GAAK,mBACf,KAAK,WAAa,KAAK,KAAK,aAAa,CAAE,KAAM,QAAS,CAAC,CAC7D,CAEA,MAAMC,EAAuBC,EAAwBC,EAAwBC,EAAiB,GAAO,CACnG,KAAK,OAASH,EACd,KAAK,gBAAkBC,EACvB,KAAK,SAAW,CAAE,GAAGC,CAAS,EAC9B,KAAK,eAAiBC,EACtB,GAAI,CACF,IAAMC,EAAQ,aAAa,QAAQ,sBAAsB,GACrDA,IAAU,SAAWA,IAAU,aAAW,KAAK,YAAcA,EACnE,MAAQ,CAAe,CACvB,SAAS,KAAK,YAAY,KAAK,IAAI,EACnC,KAAK,OAAO,CACd,CAEA,SAAU,CACR,KAAK,KAAK,OAAO,CACnB,CAEA,oBAA6B,CAC3B,OAAO,KAAK,eACd,CAEA,aAA4B,CAC1B,MAAO,CAAE,GAAG,KAAK,QAAS,CAC5B,CAEQ,QAAS,CACf,IAAMC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,YAAcb,EAEpB,IAAMc,EAAY,SAAS,cAAc,KAAK,EAGxCC,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,WACpB,IAAMC,EAAM,SAAS,cAAc,QAAQ,EAC3CA,EAAI,UAAY,MAChBA,EAAI,UAAYf,EAChBe,EAAI,MAAQ,2BACZ,IAAMC,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,UAAU,KAAK,eAAiB,WAAa,EAAE,GAClE,KAAK,OAASA,EACdF,EAAQ,YAAYC,CAAG,EACvBD,EAAQ,YAAYE,CAAM,EAG1B,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,QAClB,KAAK,MAAQA,EAGb,IAAMC,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,eACnB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,cAClBA,EAAM,YAAc,2BACpB,IAAMC,EAAW,SAAS,cAAc,QAAQ,EAChDA,EAAS,UAAY,YACrBA,EAAS,UAAY,SACrBA,EAAS,iBAAiB,QAAS,IAAMH,EAAM,UAAU,OAAO,MAAM,CAAC,EACvEC,EAAO,YAAYC,CAAK,EACxBD,EAAO,YAAYE,CAAQ,EAC3BH,EAAM,YAAYC,CAAM,EAGxB,IAAMG,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,aAGb,KAAK,OAAO,OAAS,IACvBA,EAAK,YAAY,KAAK,kBAAkB,CAAC,EACzCA,EAAK,YAAY,KAAK,YAAY,CAAC,GAIrCA,EAAK,YAAY,KAAK,wBAAwB,CAAC,EAC/CA,EAAK,YAAY,KAAK,YAAY,CAAC,EAGnCA,EAAK,YAAY,KAAK,wBAAwB,CAAC,EAC/CA,EAAK,YAAY,KAAK,YAAY,CAAC,EAGnCA,EAAK,YAAY,KAAK,0BAA0B,CAAC,EACjDA,EAAK,YAAY,KAAK,YAAY,CAAC,EAGnCA,EAAK,YAAY,KAAK,iBAAiB,CAAC,EACxCA,EAAK,YAAY,KAAK,YAAY,CAAC,EAGnCA,EAAK,YAAY,KAAK,wBAAwB,CAAC,EAC/CA,EAAK,YAAY,KAAK,YAAY,CAAC,EAGnCA,EAAK,YAAY,KAAK,iBAAiB,CAAC,EAGxC,IAAMC,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,WACpB,IAAMC,EAAU,SAAS,cAAc,QAAQ,EAC/CA,EAAQ,UAAY,WACpBA,EAAQ,YAAc,eACtB,KAAK,QAAUA,EACfA,EAAQ,iBAAiB,QAAS,IAAM,KAAK,WAAW,CAAC,EACzDD,EAAQ,YAAYC,CAAO,EAC3BF,EAAK,YAAYC,CAAO,EAExBL,EAAM,YAAYI,CAAI,EAGtBN,EAAI,iBAAiB,QAAS,IAAME,EAAM,UAAU,OAAO,MAAM,CAAC,EAClE,SAAS,iBAAiB,QAAUO,GAAM,CACnC,KAAK,KAAK,SAASA,EAAE,MAAc,GAAGP,EAAM,UAAU,OAAO,MAAM,CAC1E,CAAC,EAEDJ,EAAU,YAAYI,CAAK,EAC3BJ,EAAU,YAAYC,CAAO,EAC7B,KAAK,WAAW,YAAYF,CAAK,EACjC,KAAK,WAAW,YAAYC,CAAS,CACvC,CAIQ,mBAAiC,CACvC,IAAMY,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UAEpB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,gBAClBA,EAAM,YAAc,kBACpBD,EAAQ,YAAYC,CAAK,EAEzB,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzC,OAAAA,EAAK,UAAY,aACjB,KAAK,WAAaA,EAElB,KAAK,OAAO,QAASC,GAAU,CAC7B,IAAMC,EAAM,SAAS,cAAc,QAAQ,EAC3CA,EAAI,UAAY,eAAeD,EAAM,KAAO,KAAK,gBAAkB,UAAY,EAAE,GACjFC,EAAI,UAAY,kCAAkCD,EAAM,IAAI,UAC5DC,EAAI,iBAAiB,QAAS,IAAM,CAClC,KAAK,gBAAkBD,EAAM,GAC7B,KAAK,UAAU,cAAcA,EAAM,EAAE,EACrC,GAAI,CAAE,aAAa,QAAQ,mBAAoBA,EAAM,EAAE,CAAE,MAAQ,CAAe,CAChF,KAAK,kBAAkB,CACzB,CAAC,EACDD,EAAK,YAAYE,CAAG,CACtB,CAAC,EAEDJ,EAAQ,YAAYE,CAAI,EACjBF,CACT,CAEQ,yBAAuC,CAC7C,IAAMA,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UAEpB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,gBAClB,IAAMI,EAAY,SAAS,cAAc,MAAM,EAC/CA,EAAU,YAAc,eACxB,IAAMC,EAAY,SAAS,cAAc,MAAM,EAC/CA,EAAU,UAAY,QACtBA,EAAU,YAAc,KAAK,SAAS,YAAY,QAAQ,CAAC,EAC3D,KAAK,YAAcA,EACnBL,EAAM,YAAYI,CAAS,EAC3BJ,EAAM,YAAYK,CAAS,EAC3BN,EAAQ,YAAYC,CAAK,EAEzB,IAAMM,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,eACjBA,EAAK,YAAc,6NACnBP,EAAQ,YAAYO,CAAI,EAExB,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,cACjB,IAAMC,EAAS,SAAS,cAAc,OAAO,EAC7CA,EAAO,KAAO,QACdA,EAAO,UAAY,SACnBA,EAAO,IAAM,IACbA,EAAO,IAAM,IACbA,EAAO,KAAO,MACdA,EAAO,MAAQ,OAAO,KAAK,SAAS,WAAW,EAC/C,KAAK,WAAaA,EAClBA,EAAO,iBAAiB,QAAS,IAAM,CACrC,IAAMC,EAAI,WAAWD,EAAO,KAAK,EACjC,KAAK,SAAS,YAAcC,EACxB,KAAK,cAAa,KAAK,YAAY,YAAcA,EAAE,QAAQ,CAAC,EAClE,CAAC,EACDF,EAAK,YAAYC,CAAM,EAEvB,IAAME,EAAS,SAAS,cAAc,KAAK,EAC3C,OAAAA,EAAO,UAAY,gBACnBA,EAAO,UAAY,qDACnBH,EAAK,YAAYG,CAAM,EAEvBX,EAAQ,YAAYQ,CAAI,EACjBR,CACT,CAEQ,yBAAuC,CAC7C,IAAMA,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UAEpB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,gBAClBA,EAAM,YAAc,2BACpBD,EAAQ,YAAYC,CAAK,EAEzB,IAAMW,EAAU,SAAS,cAAc,KAAK,EAC5C,OAAAA,EAAQ,UAAY,UACpB,KAAK,YAAc,CAAC,EAEpBvC,EAAe,QAASwC,GAAQ,CAC9B,IAAMT,EAAM,SAAS,cAAc,QAAQ,EAC3CA,EAAI,UAAY,cAAcS,EAAI,QAAU,KAAK,SAAS,aAAe,UAAY,EAAE,GACvFT,EAAI,YAAcS,EAAI,MACtBT,EAAI,iBAAiB,QAAS,IAAM,CAClC,KAAK,SAAS,aAAeS,EAAI,MACjC,KAAK,YAAY,QAAQ,CAACC,EAAGC,IAAM,CACjCD,EAAE,UAAU,OAAO,SAAUzC,EAAe0C,CAAC,EAAE,QAAUF,EAAI,KAAK,CACpE,CAAC,CACH,CAAC,EACD,KAAK,YAAY,KAAKT,CAAG,EACzBQ,EAAQ,YAAYR,CAAG,CACzB,CAAC,EAEDJ,EAAQ,YAAYY,CAAO,EACpBZ,CACT,CAEQ,2BAAyC,CAC/C,IAAMA,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UAEpB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,gBAClBA,EAAM,YAAc,2BACpBD,EAAQ,YAAYC,CAAK,EAEzB,IAAMO,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,cACjB,IAAMQ,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,UAAY,SACnB,KAAK,WAAaA,EAElB5C,EAAU,QAAS6C,GAAS,CAC1B,IAAMC,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,MAAQD,EAAK,MACpBC,EAAO,YAAcD,EAAK,MACtBA,EAAK,QAAU,KAAK,SAAS,iBAAgBC,EAAO,SAAW,IACnEF,EAAO,YAAYE,CAAM,CAC3B,CAAC,EAEDF,EAAO,iBAAiB,SAAU,IAAM,CACtC,KAAK,SAAS,eAAiBA,EAAO,KACxC,CAAC,EAED,IAAMG,EAAQ,SAAS,cAAc,MAAM,EAC3C,OAAAA,EAAM,UAAY,eAClBA,EAAM,YAAc,SACpBX,EAAK,YAAYQ,CAAM,EACvBR,EAAK,YAAYW,CAAK,EAEtBnB,EAAQ,YAAYQ,CAAI,EACjBR,CACT,CAEQ,kBAAgC,CACtC,IAAMA,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UAEpB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,gBAClB,IAAMI,EAAY,SAAS,cAAc,MAAM,EAC/CA,EAAU,YAAc,eACxB,IAAMe,EAAU,SAAS,cAAc,MAAM,EAC7CA,EAAQ,YAAc,GAAG,KAAK,SAAS,YAAY,MAAM,MACzD,KAAK,YAAcA,EACnBnB,EAAM,YAAYI,CAAS,EAC3BJ,EAAM,YAAYmB,CAAO,EACzBpB,EAAQ,YAAYC,CAAK,EAEzB,IAAMM,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,eACjBA,EAAK,YAAc,2HACnBP,EAAQ,YAAYO,CAAI,EAExB,IAAMC,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,YACjB,IAAMa,EAAW,SAAS,cAAc,UAAU,EAClD,OAAAA,EAAS,UAAY,aACrBA,EAAS,YAAc,eACvBA,EAAS,UAAY,GACrBA,EAAS,MAAQ,KAAK,SAAS,YAC/B,KAAK,UAAYA,EAEjBA,EAAS,iBAAiB,QAAS,IAAM,CACvC,KAAK,SAAS,YAAcA,EAAS,MACjC,KAAK,cACP,KAAK,YAAY,YAAc,GAAGA,EAAS,MAAM,MAAM,MAE3D,CAAC,EAEDb,EAAK,YAAYa,CAAQ,EACzBrB,EAAQ,YAAYQ,CAAI,EACjBR,CACT,CAEQ,yBAAuC,CAC7C,IAAMA,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UAEpB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,gBAClBA,EAAM,YAAc,2BACpBD,EAAQ,YAAYC,CAAK,EAEzB,IAAMM,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,eACjBA,EAAK,YAAc,qEACnBP,EAAQ,YAAYO,CAAI,EAExB,IAAMK,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UACpB,KAAK,gBAAkB,CAAC,EAExB,IAAMU,EAAkB,CACtB,CAAE,MAAO,QAAkB,MAAO,cAAK,EACvC,CAAE,MAAO,UAAoB,MAAO,cAAK,CAC3C,EAEA,OAAAA,EAAgB,QAAST,GAAQ,CAC/B,IAAMT,EAAM,SAAS,cAAc,QAAQ,EAC3CA,EAAI,UAAY,cAAcS,EAAI,QAAU,KAAK,YAAc,UAAY,EAAE,GAC7ET,EAAI,YAAcS,EAAI,MACtBT,EAAI,iBAAiB,QAAS,IAAM,CAClC,KAAK,YAAcS,EAAI,MACvB,GAAI,CAAE,aAAa,QAAQ,uBAAwBA,EAAI,KAAK,CAAE,MAAQ,CAAe,CACrF,KAAK,gBAAgB,QAAQ,CAACC,EAAGC,IAAM,CACrCD,EAAE,UAAU,OAAO,SAAUQ,EAAgBP,CAAC,EAAE,QAAUF,EAAI,KAAK,CACrE,CAAC,EAED,GAAI,CACF,OAAO,OAAO,YAAY,CACxB,KAAM,sBACN,KAAMA,EAAI,QAAU,UAAY,YAAc,UAChD,EAAG,GAAG,CACR,MAAQ,CAAe,CACzB,CAAC,EACD,KAAK,gBAAgB,KAAKT,CAAG,EAC7BQ,EAAQ,YAAYR,CAAG,CACzB,CAAC,EAEDJ,EAAQ,YAAYY,CAAO,EACpBZ,CACT,CAEQ,kBAAgC,CACtC,IAAMA,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,UAEpB,IAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,gBAClBA,EAAM,YAAc,uCACpBD,EAAQ,YAAYC,CAAK,EAGzB,IAAMsB,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,YAAY,KAAK,eAAiB,WAAa,EAAE,GAClEA,EAAK,YAAc,0GACnB,KAAK,SAAWA,EAChBvB,EAAQ,YAAYuB,CAAI,EAExB,IAAMhB,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,YACjBA,EAAK,YAAc,6OACnBP,EAAQ,YAAYO,CAAI,EAExB,IAAMiB,EAAU,SAAS,cAAc,QAAQ,EAC/C,OAAAA,EAAQ,UAAY,WACpB,KAAK,QAAUA,EACf,KAAK,cAAc,EAEnBA,EAAQ,iBAAiB,QAAS,SAAY,CAC5C,GAAI,KAAK,YAAc,UACvB,MAAK,aAAa,SAAS,EAC3B,GAAI,CACF,MAAM,KAAK,UAAU,OAAO,EAC5B,KAAK,aAAa,SAAS,EAE3B,KAAK,eAAiB,GAClB,KAAK,QAAQ,KAAK,OAAO,UAAU,OAAO,SAAS,EACnD,KAAK,UAAU,KAAK,SAAS,UAAU,OAAO,SAAS,EAC3D,WAAW,IAAM,KAAK,aAAa,MAAM,EAAG,GAAI,CAClD,MAAQ,CACN,KAAK,aAAa,OAAO,EACzB,WAAW,IAAM,KAAK,aAAa,MAAM,EAAG,GAAI,CAClD,EACF,CAAC,EAEDxB,EAAQ,YAAYwB,CAAO,EACpBxB,CACT,CAIQ,aAA2B,CACjC,IAAMyB,EAAI,SAAS,cAAc,KAAK,EACtC,OAAAA,EAAE,UAAY,UACPA,CACT,CAEQ,mBAAoB,CACrB,KAAK,YACV,KAAK,WAAW,iBAAiB,eAAe,EAAE,QAAQ,CAACrB,EAAKW,IAAM,CACpEX,EAAI,UAAU,OAAO,SAAU,KAAK,OAAOW,CAAC,EAAE,KAAO,KAAK,eAAe,CAC3E,CAAC,CACH,CAEQ,aAAaW,EAAkB,CACrC,KAAK,UAAYA,EACjB,KAAK,cAAc,CACrB,CAEQ,eAAgB,CACtB,GAAI,CAAC,KAAK,QAAS,OACnB,IAAMC,EAAI,KAAK,UACTC,EAAmC,CACvC,KAAMpD,EACN,QAASC,EACT,QAASC,EACT,MAAOC,CACT,EACMgC,EAAoC,CACxC,KAAM,uCACN,QAAS,wBACT,QAAS,qBACT,MAAO,0BACT,EACA,KAAK,QAAQ,UAAY,WAAWgB,IAAM,WAAaA,IAAM,QAAU,IAAIA,CAAC,GAAK,EAAE,GACnF,KAAK,QAAQ,SAAWA,IAAM,UAC9B,KAAK,QAAQ,UAAY,GAAGC,EAAMD,CAAC,CAAC,SAAShB,EAAOgB,CAAC,CAAC,SACxD,CAEA,MAAc,YAAa,CACzB,GAAI,GAAC,KAAK,SAAW,KAAK,QAAQ,UAClC,MAAK,QAAQ,SAAW,GACxB,KAAK,QAAQ,YAAc,wBAC3B,GAAI,CACF,MAAM,KAAK,UAAU,iBAAiB,CACpC,YAAa,KAAK,SAAS,YAC3B,aAAc,KAAK,SAAS,aAC5B,eAAgB,KAAK,SAAS,eAC9B,YAAa,KAAK,SAAS,WAC7B,CAAC,EACD,KAAK,QAAQ,YAAc,qBAC3B,KAAK,QAAQ,UAAU,IAAI,OAAO,EAClC,WAAW,IAAM,CACX,KAAK,UACP,KAAK,QAAQ,YAAc,eAC3B,KAAK,QAAQ,UAAU,OAAO,OAAO,EACrC,KAAK,QAAQ,SAAW,GAE5B,EAAG,GAAI,CACT,MAAQ,CACN,KAAK,QAAQ,YAAc,2BAC3B,WAAW,IAAM,CACX,KAAK,UACP,KAAK,QAAQ,YAAc,eAC3B,KAAK,QAAQ,SAAW,GAE5B,EAAG,GAAI,CACT,EACF,CACF,EC79BO,IAAME,EAAN,KAAiB,CAGtB,YACUC,EACAC,EACAC,EACAC,EACAC,EAAqB,GAC7BC,EACA,CANQ,UAAAL,EACA,eAAAC,EACA,uBAAAC,EACA,gBAAAC,EACA,eAAAC,EAGR,KAAK,cAAgBC,CACvB,CAGA,oBAAoBC,EAAwC,CAC1D,KAAK,cAAgBA,CACvB,CAMA,MAAM,KAAKC,EAAwD,CACjE,GAAI,KAAK,UACP,OAAO,KAAK,YAAYA,CAAO,EAGjC,IAAMC,EAAO,CACX,QAAS,KAAK,UAAU,EACxB,gBAAiB,KAAK,kBAAkB,EACxC,SAAU,KAAK,WAAW,EAC1B,QAASD,EAAQ,QACjB,MAAOA,EAAQ,MACf,QAASA,EAAQ,QACjB,OAAQA,EAAQ,QAAU,EAC5B,EAEA,GAAIA,EAAQ,OAAQ,CAClB,MAAM,KAAK,KAAK,OACd,QACAC,EACAD,EAAQ,UAAY,IAAM,CAAC,GAC3BA,EAAQ,SAAW,IAAM,CAAC,EAC5B,EACA,MACF,CAEA,OAAO,KAAK,KAAK,KAAmB,QAASC,CAAI,CACnD,CAMA,MAAM,KAAKD,EAAmD,CAC5D,OAAO,KAAK,KAAK,KAAqB,iBAAkB,CACtD,gBAAiB,KAAK,kBAAkB,EACxC,KAAMA,EAAQ,KACd,QAASA,EAAQ,QACjB,MAAOA,EAAQ,KACjB,CAAC,CACH,CAMA,MAAM,SAASA,EAA4D,CACzE,IAAMC,EAAO,CACX,gBAAiB,KAAK,kBAAkB,EACxC,SAAU,KAAK,WAAW,EAC1B,MAAOD,EAAQ,MACf,QAASA,EAAQ,QACjB,OAAQA,EAAQ,QAAU,GAC1B,KAAMA,EAAQ,MAAQ,EACxB,EAEA,GAAIA,EAAQ,OAAQ,CAClB,MAAM,KAAK,KAAK,OACd,YACAC,EACAD,EAAQ,UAAY,IAAM,CAAC,GAC3BA,EAAQ,SAAW,IAAM,CAAC,EAC5B,EACA,MACF,CAEA,OAAO,KAAK,KAAK,KAAmB,YAAaC,CAAI,CACvD,CAGA,MAAc,YAAYD,EAAwD,CAChF,IAAMC,EAAO,CACX,SAAU,KAAK,WAAW,EAC1B,QAASD,EAAQ,QACjB,MAAOA,EAAQ,MACf,QAASA,EAAQ,QACjB,cAAe,KAAK,eAAiB,CAAC,EACtC,iBAAkB,CAAC,CACrB,EAEA,GAAIA,EAAQ,OAAQ,CAClB,MAAM,KAAK,KAAK,OACd,WACAC,EACAD,EAAQ,UAAY,IAAM,CAAC,GAC3BA,EAAQ,SAAW,IAAM,CAAC,EAC5B,EACA,MACF,CAEA,IAAIE,EAAU,GACd,aAAM,KAAK,KAAK,OACd,WACAD,EACCE,GAAU,CAAED,GAAWC,CAAM,EAC9B,IAAM,CAAC,CACT,EACO,CAAE,GAAI,GAAI,QAAAD,CAAQ,CAC3B,CACF,EClHA,IAAME,EAAN,KAA2B,CAGzB,YACUC,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,IAAuD,CAAC,CAM7D,CAEH,MAAM,IAAIC,EAAiE,CACzE,OAAI,KAAK,UAAkB,CAAE,GAAI,KAAK,IAAIA,CAAa,GAAK,CAAC,CAAG,EACzD,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,eAAe,mBAAmBA,CAAa,CAAC,QAAQ,CACzH,CAEA,MAAM,OAAOA,EAAuBC,EAAwD,CAC1F,GAAI,KAAK,UAAW,CAClB,KAAK,IAAID,CAAa,EAAI,CAAE,GAAI,KAAK,IAAIA,CAAa,GAAK,CAAC,EAAI,GAAGC,CAAO,EAC1E,MACF,CACA,MAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,kBAAkB,CAAC,eAAe,mBAAmBD,CAAa,CAAC,SAAUC,CAAM,CAClI,CACF,EAEaC,EAAN,KAAuB,CAI5B,YACUL,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,IAA2B,CAAC,EAOlC,KAAK,MAAQ,IAAIH,EAAqBC,EAAMC,EAAmBC,CAAS,CAC1E,CAEA,MAAM,MAAoC,CACxC,OAAI,KAAK,UAAkB,KAAK,IAAI,IAAII,IAAM,CAAE,KAAMA,EAAE,KAAM,OAAQA,EAAE,MAAO,EAAE,EAC1E,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,aAAa,CAC9E,CAEA,MAAM,IAAIC,EAAyC,CACjD,GAAI,KAAK,UAAW,CAClB,IAAMD,EAAI,KAAK,IAAI,KAAKA,GAAKA,EAAE,OAASC,CAAI,EAC5C,MAAO,CAAE,KAAMD,GAAG,MAAQC,EAAM,OAAQD,GAAG,MAAO,CACpD,CACA,OAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,eAAe,mBAAmBC,CAAI,CAAC,EAAE,CAC1G,CAEA,MAAM,IAAIC,EAAmF,CAC3F,GAAI,KAAK,UAAW,CAAE,KAAK,IAAI,KAAKA,CAAS,EAAG,MAAO,CACvD,MAAM,KAAK,KAAK,KAAK,kBAAkB,KAAK,kBAAkB,CAAC,cAAeA,CAAS,CACzF,CAEA,MAAM,OAAOD,EAAcE,EAA8D,CACvF,GAAI,KAAK,UAAW,CAAE,IAAMH,EAAI,KAAK,IAAI,KAAKA,GAAKA,EAAE,OAASC,CAAI,EAAOD,GAAG,OAAO,OAAOA,EAAGG,CAAI,EAAG,MAAO,CAC3G,MAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,kBAAkB,CAAC,eAAe,mBAAmBF,CAAI,CAAC,GAAIE,CAAI,CACjH,CAEA,MAAM,OAAOF,EAA6B,CACxC,GAAI,KAAK,UAAW,CAAE,KAAK,IAAM,KAAK,IAAI,OAAOD,GAAKA,EAAE,OAASC,CAAI,EAAG,MAAO,CAC/E,MAAM,KAAK,KAAK,OAAO,kBAAkB,KAAK,kBAAkB,CAAC,eAAe,mBAAmBA,CAAI,CAAC,EAAE,CAC5G,CAEA,MAAM,OAAOA,EAAwC,CACnD,GAAI,KAAK,UAAW,CAClB,IAAMD,EAAI,KAAK,IAAI,KAAKA,GAAKA,EAAE,OAASC,CAAI,EAC5C,MAAO,CAAE,KAAAA,EAAM,OAAQD,GAAG,OAAQ,MAAO,MAAM,KAAK,MAAM,IAAIC,CAAI,CAAE,CACtE,CACA,OAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,eAAe,mBAAmBA,CAAI,CAAC,SAAS,CACjH,CACF,EC7EO,IAAMG,EAAN,KAA0B,CAG/B,YACUC,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,IAAsB,CAAC,CAM5B,CAEH,MAAM,MAAgC,CACpC,OAAI,KAAK,UAAkB,CAAC,GAAG,KAAK,GAAG,EAChC,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,gBAAgB,CACjF,CAEA,MAAM,IAAIC,EAAcC,EAAmC,CACzD,GAAI,KAAK,UAAW,CAClB,IAAMC,EAAI,KAAK,IAAI,KAAKA,GAAKA,EAAE,OAASF,GAAQE,EAAE,KAAOD,CAAE,EAC3D,GAAIC,EAAG,MAAO,CAAE,GAAGA,CAAE,EACrB,MAAM,IAAI,MAAM,wBAAwB,CAC1C,CACA,OAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,kBAAkB,mBAAmBF,CAAI,CAAC,IAAI,mBAAmBC,CAAE,CAAC,EAAE,CACvI,CAEA,MAAM,IAAIE,EAA2C,CACnD,GAAI,KAAK,UAAW,CAAE,KAAK,IAAI,KAAK,CAAE,GAAGA,CAAa,CAAC,EAAG,MAAO,CACjE,MAAM,KAAK,KAAK,KAAK,kBAAkB,KAAK,kBAAkB,CAAC,iBAAkBA,CAAY,CAC/F,CAEA,MAAM,OAAOH,EAAcC,EAAYG,EAAsD,CAC3F,GAAI,KAAK,UAAW,CAClB,IAAMF,EAAI,KAAK,IAAI,KAAKA,GAAKA,EAAE,OAASF,GAAQE,EAAE,KAAOD,CAAE,EACvDC,EAAG,OAAO,OAAOA,EAAGE,CAAI,EACvB,KAAK,IAAI,KAAK,CAAE,KAAAJ,EAAM,GAAAC,EAAI,GAAGG,CAAK,CAAC,EACxC,MACF,CACA,MAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,kBAAkB,CAAC,kBAAkB,mBAAmBJ,CAAI,CAAC,IAAI,mBAAmBC,CAAE,CAAC,GAAIG,CAAI,CAC9I,CAEA,MAAM,OAAOJ,EAAcC,EAA2B,CACpD,GAAI,KAAK,UAAW,CAAE,KAAK,IAAM,KAAK,IAAI,OAAOC,GAAK,EAAEA,EAAE,OAASF,GAAQE,EAAE,KAAOD,EAAG,EAAG,MAAO,CACjG,MAAM,KAAK,KAAK,OAAO,kBAAkB,KAAK,kBAAkB,CAAC,kBAAkB,mBAAmBD,CAAI,CAAC,IAAI,mBAAmBC,CAAE,CAAC,EAAE,CACzI,CACF,EC1CO,IAAMI,EAAN,KAAsB,CAG3B,YACUC,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,IAA2B,CAAC,CAMjC,CAEH,MAAM,KAAKC,EAA8C,CACvD,GAAI,KAAK,UAAW,OAAOA,EAAQ,KAAK,IAAI,OAAOC,GAAKA,EAAE,QAAUD,CAAK,EAAI,CAAC,GAAG,KAAK,GAAG,EACzF,IAAME,EAASF,EAAQ,CAAE,MAAAA,CAAM,EAAI,OACnC,OAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,aAAcE,CAAM,CACrF,CAEA,MAAM,IAAIC,EAAwC,CAChD,GAAI,KAAK,UAAW,CAAE,KAAK,IAAI,KAAK,CAAE,GAAGA,CAAK,CAAC,EAAG,MAAO,CACzD,MAAM,KAAK,KAAK,KAAK,kBAAkB,KAAK,kBAAkB,CAAC,aAAcA,CAAI,CACnF,CAEA,MAAM,OAAOC,EAAcC,EAA+D,CACxF,GAAI,KAAK,UAAW,CAClB,IAAMJ,EAAI,KAAK,IAAI,KAAKA,GAAKA,EAAE,OAASG,CAAI,EACxCH,GAAG,OAAO,OAAOA,EAAGI,CAAI,EAC5B,MACF,CACA,MAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,kBAAkB,CAAC,cAAc,mBAAmBD,CAAI,CAAC,GAAIC,CAAI,CAChH,CAEA,MAAM,OAAOD,EAA6B,CACxC,GAAI,KAAK,UAAW,CAAE,KAAK,IAAM,KAAK,IAAI,OAAOH,GAAKA,EAAE,OAASG,CAAI,EAAG,MAAO,CAC/E,MAAM,KAAK,KAAK,OAAO,kBAAkB,KAAK,kBAAkB,CAAC,cAAc,mBAAmBA,CAAI,CAAC,EAAE,CAC3G,CACF,EClCO,IAAME,EAAN,KAAkB,CAGvB,YACUC,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,IAA+B,CAAC,CAMrC,CAEH,MAAM,KAAwC,CAC5C,OAAI,KAAK,UAAkB,CAAE,GAAG,KAAK,GAAI,EAClC,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,QAAQ,CACzE,CAEA,MAAM,OAAOC,EAA8C,CACzD,GAAI,KAAK,UAAW,CAAE,OAAO,OAAO,KAAK,IAAKA,CAAI,EAAG,MAAO,CAC5D,MAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,kBAAkB,CAAC,SAAUA,CAAI,CAChF,CACF,ECjBA,IAAMC,EAAN,KAAwB,CAGtB,YACUC,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,IAAuD,CAAC,CAM7D,CAIH,MAAM,IAAIC,EAA4G,CACpH,GAAI,KAAK,UACP,OAAIA,EAAc,CAAE,GAAI,KAAK,IAAIA,CAAK,GAAK,CAAC,CAAG,EACxC,CAAE,GAAG,KAAK,GAAI,EAEvB,IAAMC,EAASD,EAAQ,CAAE,MAAAA,CAAM,EAAI,OACnC,OAAO,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,gBAAiBC,CAAM,CACxF,CAEA,MAAM,OAAOD,EAAeE,EAAwD,CAClF,GAAI,KAAK,UAAW,CAClB,KAAK,IAAIF,CAAK,EAAI,CAAE,GAAI,KAAK,IAAIA,CAAK,GAAK,CAAC,EAAI,GAAGE,CAAO,EAC1D,MACF,CACA,MAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,kBAAkB,CAAC,gBAAiB,CAAE,MAAAF,EAAO,OAAAE,CAAO,CAAC,CACpG,CACF,EAEaC,EAAN,KAAmB,CAIxB,YACUN,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,WAA4B,CAAE,KAAM,EAAG,EAO7C,KAAK,MAAQ,IAAIH,EAAkBC,EAAMC,EAAmBC,CAAS,CACvE,CAEA,MAAM,KAA8B,CAClC,OAAI,KAAK,UAAkB,CAAE,GAAG,KAAK,UAAW,EACzC,KAAK,KAAK,IAAI,kBAAkB,KAAK,kBAAkB,CAAC,SAAS,CAC1E,CACF,EC9CO,IAAMK,EAAN,KAAmB,CAGxB,YACUC,EACAC,EACAC,EAAqB,GAC7B,CAHQ,UAAAF,EACA,uBAAAC,EACA,eAAAC,EALV,KAAQ,IAA+B,CAAC,CAMrC,CAIH,MAAM,IAAIC,EAAgC,CACxC,GAAI,KAAK,UACP,OAAIA,IAAQ,OAAkB,KAAK,IAAIA,CAAG,EACnC,CAAE,GAAG,KAAK,GAAI,EAEvB,IAAMC,EAAO,MAAM,KAAK,KAAK,IAC3B,kBAAkB,KAAK,kBAAkB,CAAC,SAC5C,EACA,OAAID,IAAQ,OAAkBC,EAAKD,CAAG,EAC/BC,CACT,CAEA,MAAM,IAAIA,EAA8C,CACtD,GAAI,KAAK,UAAW,CAElB,OAAW,CAACD,EAAKE,CAAK,IAAK,OAAO,QAAQD,CAAI,EACxC,OAAOC,GAAU,UAAYA,IAAU,MAAQ,CAAC,MAAM,QAAQA,CAAK,GAClE,OAAO,KAAK,IAAIF,CAAG,GAAM,UAAY,KAAK,IAAIA,CAAG,IAAM,KAC1D,KAAK,IAAIA,CAAG,EAAI,CAAE,GAAI,KAAK,IAAIA,CAAG,EAA+B,GAAGE,CAAM,EAE1E,KAAK,IAAIF,CAAG,EAAIE,EAGpB,MACF,CACA,MAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,kBAAkB,CAAC,UAAWD,CAAI,CACjF,CAEA,MAAM,OAAOD,EAA4B,CACvC,GAAI,KAAK,UAAW,CAAE,OAAO,KAAK,IAAIA,CAAG,EAAG,MAAO,CACnD,MAAM,KAAK,KAAK,OAAO,kBAAkB,KAAK,kBAAkB,CAAC,WAAW,mBAAmBA,CAAG,CAAC,EAAE,CACvG,CAEA,MAAM,cAAcG,EAAkBC,EAAkBF,EAA+B,CACrF,GAAI,KAAK,UAAW,EACd,CAAC,KAAK,IAAIC,CAAQ,GAAK,OAAO,KAAK,IAAIA,CAAQ,GAAM,YACvD,KAAK,IAAIA,CAAQ,EAAI,CAAC,GAEvB,KAAK,IAAIA,CAAQ,EAA8BC,CAAQ,EAAIF,EAC5D,MACF,CACA,MAAM,KAAK,KAAK,MACd,kBAAkB,KAAK,kBAAkB,CAAC,WAAW,mBAAmBC,CAAQ,CAAC,IAAI,mBAAmBC,CAAQ,CAAC,GACjH,CAAE,MAAAF,CAAM,CACV,CACF,CAEA,MAAM,iBAAiBC,EAAkBC,EAAiC,CACxE,GAAI,KAAK,UAAW,CAClB,GAAI,OAAO,KAAK,IAAID,CAAQ,GAAM,UAAY,KAAK,IAAIA,CAAQ,IAAM,KAAM,CACzE,IAAME,EAAQ,KAAK,IAAIF,CAAQ,EAC/B,OAAOE,EAAMD,CAAQ,EACjB,OAAO,KAAKC,CAAK,EAAE,SAAW,GAAG,OAAO,KAAK,IAAIF,CAAQ,CAC/D,CACA,MACF,CACA,MAAM,KAAK,KAAK,OACd,kBAAkB,KAAK,kBAAkB,CAAC,WAAW,mBAAmBA,CAAQ,CAAC,IAAI,mBAAmBC,CAAQ,CAAC,EACnH,CACF,CACF,EC7DA,IAAME,EAAW,gCAyBjB,SAASC,GAAqB,CAC5B,OAAS,OAAiD,aAAgB,CAAC,CAC7E,CASA,SAASC,EAAWC,EAAY,IAA2B,CACzD,IAAMC,EAAWH,EAAO,EACxB,OAAIG,EAAS,QAAgB,QAAQ,QAAQA,CAAQ,EAE9C,IAAI,QAAoB,CAACC,EAASC,IAAW,CAClD,IAAMC,EAAQ,WAAW,IAAM,CAC7B,OAAO,oBAAoB,UAAWC,CAAO,EAC7CF,EAAO,IAAIG,EAAa,kBAAmB,6DAA6D,CAAC,CAC3G,EAAGN,CAAS,EAEZ,SAASK,EAAQE,EAAqB,CACpC,IAAIC,EAAyB,KAc7B,GAZID,EAAM,MAAM,OAAS,gBAAkBA,EAAM,KAAK,QAEpDC,EAAM,CACJ,QAASD,EAAM,KAAK,QACpB,OAAQA,EAAM,KAAK,OACnB,YAAaA,EAAM,KAAK,WAC1B,EACSA,EAAM,MAAM,OAAS,wBAA0BA,EAAM,KAAK,SAEnEC,EAAMD,EAAM,KAAK,QAGfC,GAAOA,EAAI,QAAS,CACtB,aAAaJ,CAAK,EAClB,OAAO,oBAAoB,UAAWC,CAAO,EAC7C,IAAMI,EAAI,OACVA,EAAE,YAAcD,EAChBN,EAAQM,CAAG,CACb,CACF,CAEA,OAAO,iBAAiB,UAAWH,CAAO,CAC5C,CAAC,CACH,CAEO,IAAMK,EAAN,KAAiB,CAAjB,cAGL,KAAQ,QAAU,GAClB,KAAQ,OAAS,GACjB,KAAQ,eAAiB,GACzB,KAAQ,QAAU,GAClB,KAAQ,YAAc,GACtB,KAAQ,YAAc,GACtB,KAAQ,UAAY,GACpB,KAAQ,cAA6C,OAUrD,MAAM,MAAsB,CAE1B,IAAMF,EAAM,MAAMT,EAAW,EAEvBY,EAAUH,EAAI,QACpB,GAAI,CAACG,EACH,MAAM,IAAIL,EACR,kBACA,qFACF,EAMF,GAJA,KAAK,QAAU,GAAGK,CAAO,GAAGd,CAAQ,GACpC,KAAK,UAAY,CAAC,CAACW,EAAI,QACvB,KAAK,cAAgBA,EAAI,cAErB,CAACA,EAAI,OACP,MAAM,IAAIF,EACR,iBACA,oFACF,EAKF,GAHA,KAAK,OAASE,EAAI,OAGd,CAACA,EAAI,YACP,MAAM,IAAIF,EACR,eACA,qGACF,EAMF,GAJA,KAAK,YAAcE,EAAI,YAEvB,KAAK,KAAO,IAAII,EAAW,KAAK,QAAS,KAAK,YAAa,CAAC,CAACJ,EAAI,KAAK,EAElE,KAAK,UAAW,CAElB,KAAK,eAAiB,uCAGtB,IAAMK,EAASL,EAAI,QAAU,CAAC,EAC9B,KAAK,QAAUA,EAAI,iBAAmBK,EAAO,CAAC,GAAG,IAAM,GAGvD,KAAK,cAAgB,IAAIC,EAAc,CACrC,cAAgBC,GAAO,CAAE,KAAK,QAAUA,CAAG,EAC3C,iBAAkB,IAAM,QAAQ,QAAQ,EACxC,OAAQ,IAAM,QAAQ,QAAQ,CAChC,CAAC,EACGF,EAAO,OAAS,GAClB,KAAK,cAAc,MAAMA,EAAQ,KAAK,QAAS,CAC7C,YAAa,EACb,aAAc,SACd,eAAgB,QAChB,YAAa,EACf,EAAG,EAAK,EAGV,IAAMG,EAAY,IAAM,KAAK,OACvBC,EAAoB,IAAM,KAAK,eAC/BC,EAAa,IAAM,KAAK,QAC9B,KAAK,KAAO,IAAIC,EAAW,KAAK,KAAMH,EAAWC,EAAmBC,EAAY,GAAM,KAAK,aAAoD,EAC/I,KAAK,WAAa,IAAIE,EAAiB,KAAK,KAAMH,EAAmB,EAAI,EACzE,KAAK,cAAgB,IAAII,EAAoB,KAAK,KAAMJ,EAAmB,EAAI,EAC/E,KAAK,UAAY,IAAIK,EAAgB,KAAK,KAAML,EAAmB,EAAI,EACvE,KAAK,MAAQ,IAAIM,EAAY,KAAK,KAAMN,EAAmB,EAAI,EAC/D,KAAK,OAAS,IAAIO,EAAa,KAAK,KAAMP,EAAmB,EAAI,EACjE,KAAK,OAAS,IAAIQ,EAAa,KAAK,KAAMR,EAAmB,EAAI,EAEjE,KAAK,YAAc,GAGnB,KAAK,yBAAyB,EAC9B,MACF,CAGA,IAAMS,EAAa,MAAM,KAAK,aAAa,EAC3C,KAAK,eAAiBA,EAAW,eAEjC,IAAMb,EAASa,EAAW,QAAU,CAAC,EAC/BC,EAAe,KAAK,gBAAgB,EAC1C,KAAK,QACHA,GAAgBd,EAAO,KAAMe,GAAsBA,EAAE,KAAOD,CAAY,EACpEA,EACAd,EAAO,CAAC,GAAG,IAAM,GAEvB,IAAMgB,EAAyBH,EAAW,UAAY,CACpD,YAAa,EACb,aAAc,SACd,eAAgB,QAChB,YAAa,EACf,EAEA,KAAK,cAAgB,IAAIZ,EAAc,CACrC,cAAgBC,GAAO,CAAE,KAAK,QAAUA,CAAG,EAC3C,iBAAmBe,GAAM,KAAK,aAAaA,CAAC,EAC5C,OAAQ,IAAM,KAAK,aAAa,CAClC,CAAC,EACD,KAAK,cAAc,MAAMjB,EAAQ,KAAK,QAASgB,EAAUH,EAAW,gBAAkB,EAAK,EAE3F,IAAMV,EAAY,IAAM,KAAK,OACvBC,EAAoB,IAAM,KAAK,eAC/BC,EAAa,IAAM,KAAK,QAC9B,KAAK,KAAO,IAAIC,EAAW,KAAK,KAAMH,EAAWC,EAAmBC,CAAU,EAC9E,KAAK,WAAa,IAAIE,EAAiB,KAAK,KAAMH,CAAiB,EACnE,KAAK,cAAgB,IAAII,EAAoB,KAAK,KAAMJ,CAAiB,EACzE,KAAK,UAAY,IAAIK,EAAgB,KAAK,KAAML,CAAiB,EACjE,KAAK,MAAQ,IAAIM,EAAY,KAAK,KAAMN,CAAiB,EACzD,KAAK,OAAS,IAAIO,EAAa,KAAK,KAAMP,CAAiB,EAC3D,KAAK,OAAS,IAAIQ,EAAa,KAAK,KAAMR,CAAiB,EAE3D,KAAK,YAAc,EACrB,CAGQ,0BAAiC,CACvC,OAAO,iBAAiB,UAAYV,GAAwB,CACtD,CAACA,EAAM,MAAQA,EAAM,KAAK,OAAS,oBAEnCA,EAAM,KAAK,SAAW,cACxB,KAAK,QAAUA,EAAM,KAAK,SAAW,IAGnCA,EAAM,KAAK,SAAW,kBACxB,KAAK,cAAgBA,EAAM,KAAK,cAE5B,KAAK,MACP,KAAK,KAAK,oBAAoB,KAAK,aAAoD,GAG7F,CAAC,CACH,CAGA,MAAc,aAAasB,EAAgD,CACzE,MAAM,KAAK,KAAK,MAAM,YAAa,CACjC,gBAAiB,KAAK,eACtB,GAAGA,CACL,CAAC,CACH,CAGA,MAAM,cAA8B,CAClC,MAAM,KAAK,KAAK,KAAK,QAAS,CAAE,gBAAiB,KAAK,cAAe,CAAC,CACxE,CAEA,MAAc,cAKX,CACD,IAAME,EAAM,MAAM,MAAM,GAAG,KAAK,OAAO,QAAS,CAC9C,OAAQ,OACR,QAAS,CACP,eAAgB,mBAChB,cAAe,UAAU,KAAK,WAAW,EAC3C,EACA,KAAM,KAAK,UAAU,CAAE,QAAS,KAAK,MAAO,CAAC,CAC/C,CAAC,EAED,GAAI,CAACA,EAAI,GACP,MAAIA,EAAI,SAAW,IAAW,IAAIzB,EAAa,eAAgB,wBAAwB,EACnFyB,EAAI,SAAW,IAAW,IAAIzB,EAAa,iBAAkB,iCAAiC,EAC5F,IAAIA,EAAa,iBAAkB,qBAAqByB,EAAI,MAAM,EAAE,EAG5E,IAAMC,EAAO,MAAMD,EAAI,KAAK,EAC5B,OAAOC,EAAK,MAAQA,CACtB,CAEQ,iBAA0B,CAChC,GAAI,CACF,OAAO,aAAa,QAAQ,kBAAkB,GAAK,EACrD,MAAQ,CACN,MAAO,EACT,CACF,CACF","names":["index_exports","__export","StorviaError","StorviaSDK","__toCommonJS","StorviaError","code","message","HttpClient","baseUrl","token","debug","enabled","method","path","body","response","error","tag","style","params","url","k","v","res","data","e","onChunk","onDone","err","StorviaError","reader","decoder","messageId","accumulated","done","value","lines","line","parsed","codeMap","code","json","LANGUAGES","LENGTH_OPTIONS","STYLES","ICON_GEAR","ICON_SYNC","ICON_SYNC_SPIN","ICON_CHECK","ICON_ERROR","SettingsSheet","callbacks","models","defaultModelId","settings","hasWorldUpdate","saved","style","container","fabWrap","fab","fabDot","panel","header","title","closeBtn","body","saveRow","saveBtn","e","section","label","list","model","btn","labelText","valueText","desc","wrap","slider","v","labels","segment","opt","b","i","select","lang","option","arrow","counter","textarea","DISPLAY_OPTIONS","hint","syncBtn","d","state","s","icons","ChatModule","http","getGameId","getConversationId","getModelId","isPreview","previewConfig","config","options","body","content","chunk","CharacterStateModule","http","getConversationId","isPreview","characterName","values","CharactersModule","c","name","character","data","RelationshipsModule","http","getConversationId","isPreview","from","to","r","relationship","data","InventoryModule","http","getConversationId","isPreview","group","i","params","item","name","data","WorldModule","http","getConversationId","isPreview","data","PlayerStateModule","http","getConversationId","isPreview","group","params","values","PlayerModule","CustomModule","http","getConversationId","isPreview","key","data","value","groupKey","fieldKey","group","API_PATH","getEnv","waitForEnv","timeoutMs","existing","resolve","reject","timer","handler","StorviaError","event","env","w","StorviaSDK","apiBase","HttpClient","models","SettingsSheet","id","getGameId","getConversationId","getModelId","ChatModule","CharactersModule","RelationshipsModule","InventoryModule","WorldModule","PlayerModule","CustomModule","authResult","savedModelId","m","settings","s","res","json"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
declare class HttpClient {
|
|
2
|
+
private baseUrl;
|
|
3
|
+
private token;
|
|
4
|
+
private debug;
|
|
5
|
+
constructor(baseUrl: string, token: string, debug?: boolean);
|
|
6
|
+
setToken(token: string): void;
|
|
7
|
+
setDebug(enabled: boolean): void;
|
|
8
|
+
private buildHeaders;
|
|
9
|
+
private log;
|
|
10
|
+
get<T>(path: string, params?: Record<string, string>): Promise<T>;
|
|
11
|
+
post<T>(path: string, body?: unknown): Promise<T>;
|
|
12
|
+
patch<T>(path: string, body?: unknown): Promise<T>;
|
|
13
|
+
delete<T>(path: string): Promise<T>;
|
|
14
|
+
stream(path: string, body: unknown, onChunk: (text: string) => void, onDone: (id: string) => void): Promise<void>;
|
|
15
|
+
private handleResponse;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type StorviaErrorCode = 'UNAUTHORIZED' | 'INSUFFICIENT_BALANCE' | 'RATE_LIMITED' | 'MODEL_NOT_FOUND' | 'GAME_NOT_FOUND' | 'INTERNAL_ERROR' | 'NOT_INITIALIZED' | 'NETWORK_ERROR';
|
|
19
|
+
interface ChatSendOptions {
|
|
20
|
+
message: string;
|
|
21
|
+
topic?: string;
|
|
22
|
+
context?: string;
|
|
23
|
+
stream?: boolean;
|
|
24
|
+
onChunk?: (text: string) => void;
|
|
25
|
+
onDone?: (id: string) => void;
|
|
26
|
+
}
|
|
27
|
+
interface ChatSaveOptions {
|
|
28
|
+
role: 'user' | 'assistant';
|
|
29
|
+
content: string;
|
|
30
|
+
topic?: string;
|
|
31
|
+
}
|
|
32
|
+
interface ChatGenerateOptions {
|
|
33
|
+
topic?: string;
|
|
34
|
+
context?: string;
|
|
35
|
+
stream?: boolean;
|
|
36
|
+
save?: boolean;
|
|
37
|
+
onChunk?: (text: string) => void;
|
|
38
|
+
onDone?: (id: string) => void;
|
|
39
|
+
}
|
|
40
|
+
interface ChatResponse {
|
|
41
|
+
id: string;
|
|
42
|
+
content: string;
|
|
43
|
+
}
|
|
44
|
+
interface CharacterPersona {
|
|
45
|
+
name: string;
|
|
46
|
+
avatar?: string;
|
|
47
|
+
}
|
|
48
|
+
interface CharacterDetail {
|
|
49
|
+
name: string;
|
|
50
|
+
avatar?: string;
|
|
51
|
+
state: Record<string, string | number>;
|
|
52
|
+
}
|
|
53
|
+
interface Relationship {
|
|
54
|
+
from: string;
|
|
55
|
+
to: string;
|
|
56
|
+
[key: string]: string | number;
|
|
57
|
+
}
|
|
58
|
+
interface InventoryItemData {
|
|
59
|
+
name: string;
|
|
60
|
+
count: number;
|
|
61
|
+
desc?: string;
|
|
62
|
+
group: string;
|
|
63
|
+
iconUrl?: string;
|
|
64
|
+
}
|
|
65
|
+
interface PlayerPersona {
|
|
66
|
+
name: string;
|
|
67
|
+
gender?: string;
|
|
68
|
+
description?: string;
|
|
69
|
+
avatar?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
declare class ChatModule {
|
|
73
|
+
private http;
|
|
74
|
+
private getGameId;
|
|
75
|
+
private getConversationId;
|
|
76
|
+
private getModelId;
|
|
77
|
+
private isPreview;
|
|
78
|
+
private previewConfig?;
|
|
79
|
+
constructor(http: HttpClient, getGameId: () => string, getConversationId: () => string, getModelId: () => string, isPreview?: boolean, previewConfig?: Record<string, unknown>);
|
|
80
|
+
/** 更新预览配置(由父页面 postMessage 触发) */
|
|
81
|
+
updatePreviewConfig(config?: Record<string, unknown>): void;
|
|
82
|
+
/**
|
|
83
|
+
* 发送消息并获取 AI 回复(user 消息 + AI 回复一体化)
|
|
84
|
+
* 适用于 1v1 对话场景
|
|
85
|
+
*/
|
|
86
|
+
send(options: ChatSendOptions): Promise<ChatResponse | void>;
|
|
87
|
+
/**
|
|
88
|
+
* 单独保存一条消息(不调 AI,不扣费)
|
|
89
|
+
* 适用于群聊中先保存用户消息,再分别请求各 NPC 回复
|
|
90
|
+
*/
|
|
91
|
+
save(options: ChatSaveOptions): Promise<{
|
|
92
|
+
id: string;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* 基于 topic 历史 + context 请求 AI 生成回复(无需当前轮用户输入)
|
|
96
|
+
* 适用于群聊 NPC 回复、NPC 自动发言、弹幕生成等
|
|
97
|
+
*/
|
|
98
|
+
generate(options: ChatGenerateOptions): Promise<ChatResponse | void>;
|
|
99
|
+
/** 预览模式:发到 /preview 端点 */
|
|
100
|
+
private sendPreview;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
declare class CharacterStateModule {
|
|
104
|
+
private http;
|
|
105
|
+
private getConversationId;
|
|
106
|
+
private isPreview;
|
|
107
|
+
private mem;
|
|
108
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
109
|
+
get(characterName: string): Promise<Record<string, string | number>>;
|
|
110
|
+
update(characterName: string, values: Record<string, string | number>): Promise<void>;
|
|
111
|
+
}
|
|
112
|
+
declare class CharactersModule {
|
|
113
|
+
private http;
|
|
114
|
+
private getConversationId;
|
|
115
|
+
private isPreview;
|
|
116
|
+
state: CharacterStateModule;
|
|
117
|
+
private mem;
|
|
118
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
119
|
+
list(): Promise<CharacterPersona[]>;
|
|
120
|
+
get(name: string): Promise<CharacterPersona>;
|
|
121
|
+
add(character: {
|
|
122
|
+
name: string;
|
|
123
|
+
description?: string;
|
|
124
|
+
avatar?: string;
|
|
125
|
+
}): Promise<void>;
|
|
126
|
+
update(name: string, data: Partial<Omit<CharacterPersona, 'name'>>): Promise<void>;
|
|
127
|
+
remove(name: string): Promise<void>;
|
|
128
|
+
detail(name: string): Promise<CharacterDetail>;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
declare class RelationshipsModule {
|
|
132
|
+
private http;
|
|
133
|
+
private getConversationId;
|
|
134
|
+
private isPreview;
|
|
135
|
+
private mem;
|
|
136
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
137
|
+
list(): Promise<Relationship[]>;
|
|
138
|
+
get(from: string, to: string): Promise<Relationship>;
|
|
139
|
+
add(relationship: Relationship): Promise<void>;
|
|
140
|
+
update(from: string, to: string, data: Record<string, string | number>): Promise<void>;
|
|
141
|
+
remove(from: string, to: string): Promise<void>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
declare class InventoryModule {
|
|
145
|
+
private http;
|
|
146
|
+
private getConversationId;
|
|
147
|
+
private isPreview;
|
|
148
|
+
private mem;
|
|
149
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
150
|
+
list(group?: string): Promise<InventoryItemData[]>;
|
|
151
|
+
add(item: InventoryItemData): Promise<void>;
|
|
152
|
+
update(name: string, data: Partial<Omit<InventoryItemData, 'name'>>): Promise<void>;
|
|
153
|
+
remove(name: string): Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
declare class WorldModule {
|
|
157
|
+
private http;
|
|
158
|
+
private getConversationId;
|
|
159
|
+
private isPreview;
|
|
160
|
+
private mem;
|
|
161
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
162
|
+
get(): Promise<Record<string, unknown>>;
|
|
163
|
+
update(data: Record<string, unknown>): Promise<void>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
declare class PlayerStateModule {
|
|
167
|
+
private http;
|
|
168
|
+
private getConversationId;
|
|
169
|
+
private isPreview;
|
|
170
|
+
private mem;
|
|
171
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
172
|
+
get(): Promise<Record<string, Record<string, string | number>>>;
|
|
173
|
+
get(group: string): Promise<Record<string, string | number>>;
|
|
174
|
+
update(group: string, values: Record<string, string | number>): Promise<void>;
|
|
175
|
+
}
|
|
176
|
+
declare class PlayerModule {
|
|
177
|
+
private http;
|
|
178
|
+
private getConversationId;
|
|
179
|
+
private isPreview;
|
|
180
|
+
state: PlayerStateModule;
|
|
181
|
+
private memPersona;
|
|
182
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
183
|
+
get(): Promise<PlayerPersona>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
declare class CustomModule {
|
|
187
|
+
private http;
|
|
188
|
+
private getConversationId;
|
|
189
|
+
private isPreview;
|
|
190
|
+
private mem;
|
|
191
|
+
constructor(http: HttpClient, getConversationId: () => string, isPreview?: boolean);
|
|
192
|
+
get(): Promise<Record<string, unknown>>;
|
|
193
|
+
get(key: string): Promise<unknown>;
|
|
194
|
+
set(data: Record<string, unknown>): Promise<void>;
|
|
195
|
+
delete(key: string): Promise<void>;
|
|
196
|
+
setGroupField(groupKey: string, fieldKey: string, value: unknown): Promise<void>;
|
|
197
|
+
deleteGroupField(groupKey: string, fieldKey: string): Promise<void>;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
declare class StorviaSDK {
|
|
201
|
+
private http;
|
|
202
|
+
private settingsSheet;
|
|
203
|
+
private baseUrl;
|
|
204
|
+
private gameId;
|
|
205
|
+
private conversationId;
|
|
206
|
+
private modelId;
|
|
207
|
+
private accessToken;
|
|
208
|
+
private initialized;
|
|
209
|
+
private isPreview;
|
|
210
|
+
private previewConfig;
|
|
211
|
+
chat: ChatModule;
|
|
212
|
+
characters: CharactersModule;
|
|
213
|
+
relationships: RelationshipsModule;
|
|
214
|
+
inventory: InventoryModule;
|
|
215
|
+
world: WorldModule;
|
|
216
|
+
player: PlayerModule;
|
|
217
|
+
custom: CustomModule;
|
|
218
|
+
init(): Promise<void>;
|
|
219
|
+
/** 监听父页面 postMessage(预览模式) */
|
|
220
|
+
private listenForPreviewMessages;
|
|
221
|
+
/** 保存设置到后端 */
|
|
222
|
+
private saveSettings;
|
|
223
|
+
/** 同步最新游戏设定到当前存档 */
|
|
224
|
+
syncSettings(): Promise<void>;
|
|
225
|
+
private authenticate;
|
|
226
|
+
private getSavedModelId;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
declare class StorviaError extends Error {
|
|
230
|
+
code: StorviaErrorCode;
|
|
231
|
+
constructor(code: StorviaErrorCode, message: string);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export { type CharacterDetail, type CharacterPersona, type ChatGenerateOptions, type ChatResponse, type ChatSaveOptions, type ChatSendOptions, type InventoryItemData, type PlayerPersona, type Relationship, StorviaError, type StorviaErrorCode, StorviaSDK };
|