@bunnyland/ui-web 0.2.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.
Files changed (57) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +211 -0
  3. package/assets/bunnyland-api.js +345 -0
  4. package/assets/bunnyland-play.js +1223 -0
  5. package/assets/bunnyland-ui.css +1634 -0
  6. package/assets/bunnyland-ui.js +795 -0
  7. package/dist/admin-widgets.d.ts +31 -0
  8. package/dist/admin-widgets.d.ts.map +1 -0
  9. package/dist/admin-widgets.js +100 -0
  10. package/dist/admin-widgets.js.map +1 -0
  11. package/dist/api.d.ts +37 -0
  12. package/dist/api.d.ts.map +1 -0
  13. package/dist/api.js +125 -0
  14. package/dist/api.js.map +1 -0
  15. package/dist/index.d.ts +7 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +7 -0
  18. package/dist/play.d.ts +265 -0
  19. package/dist/play.d.ts.map +1 -0
  20. package/dist/play.js +806 -0
  21. package/dist/play.js.map +1 -0
  22. package/dist/player-widgets.d.ts +11 -0
  23. package/dist/player-widgets.d.ts.map +1 -0
  24. package/dist/player-widgets.js +21 -0
  25. package/dist/player-widgets.js.map +1 -0
  26. package/dist/preact/auth.d.ts +37 -0
  27. package/dist/preact/auth.d.ts.map +1 -0
  28. package/dist/preact/components.d.ts +58 -0
  29. package/dist/preact/components.d.ts.map +1 -0
  30. package/dist/preact/theme.d.ts +16 -0
  31. package/dist/preact/theme.d.ts.map +1 -0
  32. package/dist/preact.d.ts +4 -0
  33. package/dist/preact.d.ts.map +1 -0
  34. package/dist/preact.js +488 -0
  35. package/dist/preact.js.map +1 -0
  36. package/dist/theme.d.ts +31 -0
  37. package/dist/theme.d.ts.map +1 -0
  38. package/dist/theme.js +165 -0
  39. package/dist/theme.js.map +1 -0
  40. package/dist/widgets.d.ts +7 -0
  41. package/dist/widgets.d.ts.map +1 -0
  42. package/dist/widgets.js +31 -0
  43. package/dist/widgets.js.map +1 -0
  44. package/docs/admin/custom-web-themes.md +112 -0
  45. package/docs/developer/design-language.md +116 -0
  46. package/package.json +101 -0
  47. package/src/admin-widgets.ts +189 -0
  48. package/src/api.ts +193 -0
  49. package/src/index.ts +6 -0
  50. package/src/play.ts +1151 -0
  51. package/src/player-widgets.ts +29 -0
  52. package/src/preact/auth.tsx +338 -0
  53. package/src/preact/components.tsx +249 -0
  54. package/src/preact/theme.tsx +72 -0
  55. package/src/preact.ts +3 -0
  56. package/src/theme.ts +225 -0
  57. package/src/widgets.ts +41 -0
@@ -0,0 +1,31 @@
1
+ export interface SearchDropdownOption {
2
+ value: string;
3
+ label?: string;
4
+ }
5
+ export declare function tagEditorHtml(tags: unknown, options?: {
6
+ hiddenClass?: string;
7
+ hiddenAttributes?: string;
8
+ disabled?: boolean;
9
+ addLabel?: string;
10
+ placeholder?: string;
11
+ }): string;
12
+ export declare function readTagEditorTags(editor: Element, options?: {
13
+ hiddenSelector?: string;
14
+ }): string[];
15
+ export declare function renderTagEditorTags(editor: Element, tags: unknown): void;
16
+ export declare function bindTagEditor(editor: Element, options?: {
17
+ hiddenSelector?: string;
18
+ onChange?: (tags: string[]) => void;
19
+ }): {
20
+ readTags: () => string[];
21
+ update: (tags: string[]) => void;
22
+ };
23
+ export declare function bindSearchDropdown(root: Element, { options, value, onChange, emptyLabel, }: {
24
+ options: Array<string | SearchDropdownOption>;
25
+ value?: string;
26
+ onChange?: ((value: string, item: SearchDropdownOption | null) => void) | null;
27
+ emptyLabel?: string;
28
+ }): {
29
+ setValue: (value: string, notify?: boolean) => void;
30
+ };
31
+ //# sourceMappingURL=admin-widgets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-widgets.d.ts","sourceRoot":"","sources":["../src/admin-widgets.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,GAAE;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAmBd;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,MAAM,EAAE,CAOtG;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAIxE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,GAAE;IACtD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAChC,GAAG;IAAE,QAAQ,EAAE,MAAM,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,IAAI,CAAA;CAAE,CAgCtE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,EAChD,OAAO,EACP,KAAU,EACV,QAAe,EACf,UAAyB,GAC1B,EAAE;IACD,OAAO,EAAE,KAAK,CAAC,MAAM,GAAG,oBAAoB,CAAC,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,IAAI,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG;IAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,CAiF1D"}
@@ -0,0 +1,100 @@
1
+ import { escapeHtml as e, normalizeTags as t } from "./widgets.js";
2
+ //#region src/admin-widgets.ts
3
+ function n(n, r = {}) {
4
+ let i = t(n), a = r.hiddenClass || "component-tags-value", o = r.hiddenAttributes || "", c = r.disabled ? " disabled" : "", l = r.addLabel || "Add Tag", u = r.placeholder || "add tag...";
5
+ return `
6
+ <div class="tag-editor">
7
+ <input class="${e(a)}" ${o} type="hidden" value="${e(JSON.stringify(i))}">
8
+ <div class="tag-list">
9
+ ${s(i, c)}
10
+ </div>
11
+ <div class="tag-entry">
12
+ <input class="tag-input" type="text" placeholder="${e(u)}" spellcheck="false"${c}>
13
+ <button type="button" data-add-tag${c}>${e(l)}</button>
14
+ </div>
15
+ </div>
16
+ `;
17
+ }
18
+ function r(e, n = {}) {
19
+ let r = e.querySelector(n.hiddenSelector || ".component-tags-value");
20
+ try {
21
+ return t(JSON.parse(r?.value || "[]"));
22
+ } catch {
23
+ return [];
24
+ }
25
+ }
26
+ function i(e, n) {
27
+ let r = e.querySelector(".tag-list");
28
+ r && (r.innerHTML = s(t(n), ""));
29
+ }
30
+ function a(e, n = {}) {
31
+ let a = e.querySelector(".tag-input"), o = e.querySelector("[data-add-tag]"), s = e.querySelector(n.hiddenSelector || ".component-tags-value"), c = n.onChange || (() => {}), l = (n) => {
32
+ let r = t(n);
33
+ s && (s.value = JSON.stringify(r)), i(e, r), c(r);
34
+ }, u = () => {
35
+ let t = a?.value.trim();
36
+ if (!t) return;
37
+ let i = r(e, n);
38
+ i.includes(t) || i.push(t), a && (a.value = ""), l(i), a?.focus();
39
+ };
40
+ return o?.addEventListener("click", u), a?.addEventListener("keydown", (e) => {
41
+ e.key === "Enter" && (e.preventDefault(), u());
42
+ }), e.addEventListener("click", (t) => {
43
+ let i = t.target?.closest("[data-remove-tag]");
44
+ i && l(r(e, n).filter((e) => e !== i.dataset.removeTag));
45
+ }), {
46
+ readTags: () => r(e, n),
47
+ update: l
48
+ };
49
+ }
50
+ function o(t, { options: n, value: r = "", onChange: i = null, emptyLabel: a = "No matches" }) {
51
+ let o = t.querySelector(".search-dropdown-input"), s = t.querySelector(".search-dropdown-value"), c = t.querySelector(".search-dropdown-menu");
52
+ if (!o || !s || !c) throw Error("Search dropdown is missing required elements");
53
+ let l = n.map((e) => typeof e == "string" ? {
54
+ value: e,
55
+ label: e
56
+ } : {
57
+ value: e.value,
58
+ label: e.label || e.value
59
+ }), u = 0, d = () => {
60
+ let e = o.value.trim().toLowerCase();
61
+ return e ? l.filter((t) => String(t.label).toLowerCase().includes(e) || t.value.toLowerCase().includes(e)) : l;
62
+ }, f = (e, t = !0) => {
63
+ let n = l.find((t) => t.value === e) || null;
64
+ s.value = n?.value || "", o.value = n?.label || "", t && i && i(s.value, n);
65
+ }, p = () => {
66
+ let t = d();
67
+ u = Math.max(0, Math.min(u, t.length - 1)), c.innerHTML = t.length ? t.map((t, n) => `
68
+ <div class="search-dropdown-option ${n === u ? "active" : ""}" data-value="${e(t.value)}">
69
+ ${e(t.label)}
70
+ </div>
71
+ `).join("") : `<div class="search-dropdown-empty">${e(a)}</div>`, c.classList.remove("hidden"), o.setAttribute("aria-expanded", "true");
72
+ }, m = () => {
73
+ let e = d()[u];
74
+ e && (f(e.value), c.classList.add("hidden"), o.setAttribute("aria-expanded", "false"));
75
+ };
76
+ return o.addEventListener("input", () => {
77
+ u = 0, p();
78
+ }), o.addEventListener("focus", p), o.addEventListener("keydown", (e) => {
79
+ e.key === "ArrowDown" ? (e.preventDefault(), u += 1, p()) : e.key === "ArrowUp" ? (e.preventDefault(), --u, p()) : e.key === "Enter" ? (e.preventDefault(), m()) : e.key === "Escape" && (c.classList.add("hidden"), o.setAttribute("aria-expanded", "false"), o.blur());
80
+ }), o.addEventListener("blur", () => {
81
+ setTimeout(() => {
82
+ c.classList.add("hidden"), o.setAttribute("aria-expanded", "false");
83
+ }, 150);
84
+ }), c.addEventListener("mousedown", (e) => {
85
+ let t = e.target?.closest(".search-dropdown-option");
86
+ t && (e.preventDefault(), f(t.dataset.value || ""), c.classList.add("hidden"), o.setAttribute("aria-expanded", "false"));
87
+ }), f(r, !1), { setValue: f };
88
+ }
89
+ function s(t, n) {
90
+ return t.length ? t.map((t) => `
91
+ <span class="tag-pill">
92
+ <span>${e(t)}</span>
93
+ <button type="button" data-remove-tag="${e(t)}" aria-label="Remove tag ${e(t)}"${n}>x</button>
94
+ </span>
95
+ `).join("") : "<span class=\"tiny\">No tags.</span>";
96
+ }
97
+ //#endregion
98
+ export { o as bindSearchDropdown, a as bindTagEditor, r as readTagEditorTags, i as renderTagEditorTags, n as tagEditorHtml };
99
+
100
+ //# sourceMappingURL=admin-widgets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-widgets.js","names":[],"sources":["../src/admin-widgets.ts"],"sourcesContent":["import { escapeHtml, normalizeTags } from './widgets';\n\nexport interface SearchDropdownOption {\n value: string;\n label?: string;\n}\n\nexport function tagEditorHtml(tags: unknown, options: {\n hiddenClass?: string;\n hiddenAttributes?: string;\n disabled?: boolean;\n addLabel?: string;\n placeholder?: string;\n} = {}): string {\n const values = normalizeTags(tags);\n const hiddenClass = options.hiddenClass || 'component-tags-value';\n const hiddenAttributes = options.hiddenAttributes || '';\n const disabled = options.disabled ? ' disabled' : '';\n const addLabel = options.addLabel || 'Add Tag';\n const placeholder = options.placeholder || 'add tag...';\n return `\n <div class=\"tag-editor\">\n <input class=\"${escapeHtml(hiddenClass)}\" ${hiddenAttributes} type=\"hidden\" value=\"${escapeHtml(JSON.stringify(values))}\">\n <div class=\"tag-list\">\n ${renderTagPills(values, disabled)}\n </div>\n <div class=\"tag-entry\">\n <input class=\"tag-input\" type=\"text\" placeholder=\"${escapeHtml(placeholder)}\" spellcheck=\"false\"${disabled}>\n <button type=\"button\" data-add-tag${disabled}>${escapeHtml(addLabel)}</button>\n </div>\n </div>\n `;\n}\n\nexport function readTagEditorTags(editor: Element, options: { hiddenSelector?: string } = {}): string[] {\n const hidden = editor.querySelector<HTMLInputElement>(options.hiddenSelector || '.component-tags-value');\n try {\n return normalizeTags(JSON.parse(hidden?.value || '[]'));\n } catch (_err) {\n return [];\n }\n}\n\nexport function renderTagEditorTags(editor: Element, tags: unknown): void {\n const list = editor.querySelector('.tag-list');\n if (!list) return;\n list.innerHTML = renderTagPills(normalizeTags(tags), '');\n}\n\nexport function bindTagEditor(editor: Element, options: {\n hiddenSelector?: string;\n onChange?: (tags: string[]) => void;\n} = {}): { readTags: () => string[]; update: (tags: string[]) => void } {\n const input = editor.querySelector<HTMLInputElement>('.tag-input');\n const add = editor.querySelector('[data-add-tag]');\n const hidden = editor.querySelector<HTMLInputElement>(options.hiddenSelector || '.component-tags-value');\n const onChange = options.onChange || (() => {});\n const update = (tags: string[]): void => {\n const values = normalizeTags(tags);\n if (hidden) hidden.value = JSON.stringify(values);\n renderTagEditorTags(editor, values);\n onChange(values);\n };\n const addTag = (): void => {\n const tag = input?.value.trim();\n if (!tag) return;\n const tags = readTagEditorTags(editor, options);\n if (!tags.includes(tag)) tags.push(tag);\n if (input) input.value = '';\n update(tags);\n input?.focus();\n };\n add?.addEventListener('click', addTag);\n input?.addEventListener('keydown', event => {\n if (event.key !== 'Enter') return;\n event.preventDefault();\n addTag();\n });\n editor.addEventListener('click', event => {\n const button = (event.target as Element | null)?.closest<HTMLElement>('[data-remove-tag]');\n if (!button) return;\n update(readTagEditorTags(editor, options).filter(tag => tag !== button.dataset.removeTag));\n });\n return { readTags: () => readTagEditorTags(editor, options), update };\n}\n\nexport function bindSearchDropdown(root: Element, {\n options,\n value = '',\n onChange = null,\n emptyLabel = 'No matches',\n}: {\n options: Array<string | SearchDropdownOption>;\n value?: string;\n onChange?: ((value: string, item: SearchDropdownOption | null) => void) | null;\n emptyLabel?: string;\n}): { setValue: (value: string, notify?: boolean) => void } {\n const input = root.querySelector<HTMLInputElement>('.search-dropdown-input');\n const hidden = root.querySelector<HTMLInputElement>('.search-dropdown-value');\n const menu = root.querySelector<HTMLElement>('.search-dropdown-menu');\n if (!input || !hidden || !menu) throw new Error('Search dropdown is missing required elements');\n const items = options.map(option => typeof option === 'string'\n ? { value: option, label: option }\n : { value: option.value, label: option.label || option.value });\n let active = 0;\n const filteredItems = (): SearchDropdownOption[] => {\n const q = input.value.trim().toLowerCase();\n if (!q) return items;\n return items.filter(item =>\n String(item.label).toLowerCase().includes(q) ||\n item.value.toLowerCase().includes(q));\n };\n const setValue = (nextValue: string, notify = true): void => {\n const item = items.find(option => option.value === nextValue) || null;\n hidden.value = item?.value || '';\n input.value = item?.label || '';\n if (notify && onChange) onChange(hidden.value, item);\n };\n const renderMenu = (): void => {\n const filtered = filteredItems();\n active = Math.max(0, Math.min(active, filtered.length - 1));\n menu.innerHTML = filtered.length\n ? filtered.map((item, index) => `\n <div class=\"search-dropdown-option ${index === active ? 'active' : ''}\" data-value=\"${escapeHtml(item.value)}\">\n ${escapeHtml(item.label)}\n </div>\n `).join('')\n : `<div class=\"search-dropdown-empty\">${escapeHtml(emptyLabel)}</div>`;\n menu.classList.remove('hidden');\n input.setAttribute('aria-expanded', 'true');\n };\n const chooseActive = (): void => {\n const item = filteredItems()[active];\n if (!item) return;\n setValue(item.value);\n menu.classList.add('hidden');\n input.setAttribute('aria-expanded', 'false');\n };\n input.addEventListener('input', () => {\n active = 0;\n renderMenu();\n });\n input.addEventListener('focus', renderMenu);\n input.addEventListener('keydown', event => {\n if (event.key === 'ArrowDown') {\n event.preventDefault();\n active += 1;\n renderMenu();\n } else if (event.key === 'ArrowUp') {\n event.preventDefault();\n active -= 1;\n renderMenu();\n } else if (event.key === 'Enter') {\n event.preventDefault();\n chooseActive();\n } else if (event.key === 'Escape') {\n menu.classList.add('hidden');\n input.setAttribute('aria-expanded', 'false');\n input.blur();\n }\n });\n input.addEventListener('blur', () => {\n setTimeout(() => {\n menu.classList.add('hidden');\n input.setAttribute('aria-expanded', 'false');\n }, 150);\n });\n menu.addEventListener('mousedown', event => {\n const option = (event.target as Element | null)?.closest<HTMLElement>('.search-dropdown-option');\n if (!option) return;\n event.preventDefault();\n setValue(option.dataset.value || '');\n menu.classList.add('hidden');\n input.setAttribute('aria-expanded', 'false');\n });\n setValue(value, false);\n return { setValue };\n}\n\nfunction renderTagPills(values: string[], disabled: string): string {\n return values.length\n ? values.map(tag => `\n <span class=\"tag-pill\">\n <span>${escapeHtml(tag)}</span>\n <button type=\"button\" data-remove-tag=\"${escapeHtml(tag)}\" aria-label=\"Remove tag ${escapeHtml(tag)}\"${disabled}>x</button>\n </span>\n `).join('')\n : '<span class=\"tiny\">No tags.</span>';\n}\n"],"mappings":"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"}
package/dist/api.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ export interface AdminAuth {
2
+ authorization?: string;
3
+ clientId?: string;
4
+ }
5
+ export interface ControlClaimLike {
6
+ claimId?: string;
7
+ claimSecret?: string;
8
+ clientId?: string;
9
+ }
10
+ export declare class ApiError extends Error {
11
+ readonly status: number;
12
+ constructor(message: string, status: number);
13
+ }
14
+ export declare function normalizeBase(url: string): string;
15
+ export declare function assertSameOriginBase(base: string, pageLocation?: Location): string;
16
+ export declare function serverFromUrl(search?: string): string;
17
+ export declare function setServerInUrl(base: string, href?: string): void;
18
+ export declare function jsonHeaders(authHeader?: string): Record<string, string>;
19
+ export declare function adminHeaders(auth?: AdminAuth, contentType?: string): Record<string, string>;
20
+ export declare function claimHeaders(control?: ControlClaimLike | null): Record<string, string>;
21
+ export declare function mergePlayerHeaders(headers?: HeadersInit): Headers;
22
+ export declare function parseJsonResponse(res: Response): Promise<unknown>;
23
+ export declare function sendJson(base: string, path: string, init?: RequestInit): Promise<unknown>;
24
+ export declare function login(base: string, username: string, password: string): Promise<unknown>;
25
+ export declare function authMe(base: string): Promise<unknown>;
26
+ export declare function rotateAuth(base: string): Promise<unknown>;
27
+ export declare function logout(base: string): Promise<unknown>;
28
+ export declare function setPlayerAuth(authHeader?: string): void;
29
+ export declare function getPlayerAuth(): string;
30
+ export declare function subscribePlayerAuth(listener: () => void): () => void;
31
+ export declare function setClientId(clientId: string): void;
32
+ export declare function getClientId(): string;
33
+ export declare function sendAdmin(base: string, path: string, auth?: AdminAuth, init?: RequestInit): Promise<unknown>;
34
+ export declare function socketUrl(base: string, path?: string, _authHeader?: string): string;
35
+ export declare function mediaUrl(base: string, url: string): string;
36
+ export declare function requestSceneImage(base: string, characterId: string, control?: ControlClaimLike | null): Promise<unknown>;
37
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
package/dist/api.js ADDED
@@ -0,0 +1,125 @@
1
+ //#region src/api.ts
2
+ var e = class extends Error {
3
+ status;
4
+ constructor(e, t) {
5
+ super(e), this.name = "ApiError", this.status = t;
6
+ }
7
+ }, t = "", n = /* @__PURE__ */ new Set(), r = typeof globalThis.crypto?.randomUUID == "function" ? globalThis.crypto.randomUUID() : `web-${Date.now()}-${Math.random().toString(16).slice(2)}`;
8
+ function i(e) {
9
+ return String(e || "").trim().replace(/\/$/, "");
10
+ }
11
+ function a(e, t = globalThis.location) {
12
+ let n = i(e);
13
+ if (!t) return n;
14
+ if (new URL(n || "/", t.href).origin !== t.origin) throw Error("Bunnyland browser connections must use the page origin");
15
+ return n;
16
+ }
17
+ function o(e = globalThis.location?.search || "") {
18
+ let t = new URLSearchParams(e).get("server") || "";
19
+ return t ? a(t) : "";
20
+ }
21
+ function s(e, t = globalThis.location?.href || "") {
22
+ if (!t || !globalThis.history) return;
23
+ let n = new URL(t), r = a(e);
24
+ r ? n.searchParams.set("server", r) : n.searchParams.delete("server"), history.replaceState(null, "", n);
25
+ }
26
+ function c(e = "") {
27
+ return {
28
+ "Content-Type": "application/json",
29
+ "X-Bunnyland-Client-Id": r,
30
+ ...e.startsWith("Bearer ") ? { Authorization: e } : {}
31
+ };
32
+ }
33
+ function l(e = {}, t = "") {
34
+ let n = { "X-Bunnyland-Client-Id": e.clientId || r };
35
+ return t && (n["Content-Type"] = t), e.authorization?.startsWith("Bearer ") && (n.Authorization = e.authorization), n;
36
+ }
37
+ function u(e = null) {
38
+ return {
39
+ ...c(t),
40
+ "X-Bunnyland-Client-Id": e?.clientId || r,
41
+ ...e?.claimSecret ? { "X-Bunnyland-Claim-Secret": e.claimSecret } : {}
42
+ };
43
+ }
44
+ function d(e = {}) {
45
+ let n = new Headers(e);
46
+ return n.has("X-Bunnyland-Client-Id") || n.set("X-Bunnyland-Client-Id", r), t.startsWith("Bearer ") && n.set("Authorization", t), n;
47
+ }
48
+ async function f(t) {
49
+ let n = await t.json().catch(() => ({}));
50
+ if (!t.ok) throw new e(typeof n == "object" && n && "detail" in n ? String(n.detail) : `HTTP ${t.status}`, t.status);
51
+ return n;
52
+ }
53
+ async function p(e, t, n = {}) {
54
+ let r = d(n.headers || {});
55
+ return n.body && !r.has("Content-Type") && r.set("Content-Type", "application/json"), f(await fetch(`${a(e)}${t}`, {
56
+ ...n,
57
+ headers: r,
58
+ credentials: "include"
59
+ }));
60
+ }
61
+ async function m(e, t, n) {
62
+ return a(e), p(e, "/auth/session", {
63
+ method: "POST",
64
+ body: JSON.stringify({
65
+ username: t,
66
+ password: n,
67
+ delivery: "cookie"
68
+ })
69
+ });
70
+ }
71
+ async function h(e) {
72
+ return p(e, "/auth/session");
73
+ }
74
+ async function g(e) {
75
+ return p(e, "/auth/session", { method: "PATCH" });
76
+ }
77
+ async function _(e) {
78
+ return p(e, "/auth/session", { method: "DELETE" });
79
+ }
80
+ function v(e = "") {
81
+ let r = e.startsWith("Bearer ") ? e : "";
82
+ if (r !== t) {
83
+ t = r;
84
+ for (let e of n) e();
85
+ }
86
+ }
87
+ function y() {
88
+ return t;
89
+ }
90
+ function b(e) {
91
+ return n.add(e), () => n.delete(e);
92
+ }
93
+ function x(e) {
94
+ let t = e.trim();
95
+ t && (r = t);
96
+ }
97
+ function S() {
98
+ return r;
99
+ }
100
+ async function C(e, t, n = {}, r = {}) {
101
+ return f(await fetch(`${a(e)}${t}`, {
102
+ ...r,
103
+ headers: l(n),
104
+ credentials: "include"
105
+ }));
106
+ }
107
+ function w(e, t = "/admin/world/stream", n = "") {
108
+ let r = a(e);
109
+ return (globalThis.location ? new URL(`${r}${t}`, globalThis.location.href).href : `${r}${t}`).replace(/^http/, "ws");
110
+ }
111
+ function T(e, t) {
112
+ return t ? t.startsWith("data:") ? t : /^https?:/.test(t) ? (a(t), t) : `${a(e)}${t}` : "";
113
+ }
114
+ async function E(e, t, n = null) {
115
+ if (!n?.claimId) throw Error("A character claim is required");
116
+ return p(e, `/play/claims/${encodeURIComponent(n.claimId)}/jobs`, {
117
+ method: "POST",
118
+ headers: u(n),
119
+ body: JSON.stringify({ kind: "scene_image" })
120
+ });
121
+ }
122
+ //#endregion
123
+ export { e as ApiError, l as adminHeaders, a as assertSameOriginBase, h as authMe, u as claimHeaders, S as getClientId, y as getPlayerAuth, c as jsonHeaders, m as login, _ as logout, T as mediaUrl, d as mergePlayerHeaders, i as normalizeBase, f as parseJsonResponse, E as requestSceneImage, g as rotateAuth, C as sendAdmin, p as sendJson, o as serverFromUrl, x as setClientId, v as setPlayerAuth, s as setServerInUrl, w as socketUrl, b as subscribePlayerAuth };
124
+
125
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","names":[],"sources":["../src/api.ts"],"sourcesContent":["export interface AdminAuth {\n authorization?: string;\n clientId?: string;\n}\n\nexport interface ControlClaimLike {\n claimId?: string;\n claimSecret?: string;\n clientId?: string;\n}\n\nexport class ApiError extends Error {\n readonly status: number;\n\n constructor(message: string, status: number) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n }\n}\n\nlet playerAuthHeader = '';\nconst playerAuthListeners = new Set<() => void>();\nlet browserClientId = typeof globalThis.crypto?.randomUUID === 'function'\n ? globalThis.crypto.randomUUID()\n : `web-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n\nexport function normalizeBase(url: string): string {\n return String(url || '').trim().replace(/\\/$/, '');\n}\n\nexport function assertSameOriginBase(\n base: string,\n pageLocation = globalThis.location,\n): string {\n const normalized = normalizeBase(base);\n if (!pageLocation) return normalized;\n const resolved = new URL(normalized || '/', pageLocation.href);\n if (resolved.origin !== pageLocation.origin) {\n throw new Error('Bunnyland browser connections must use the page origin');\n }\n return normalized;\n}\n\nexport function serverFromUrl(search = globalThis.location?.search || ''): string {\n const configured = new URLSearchParams(search).get('server') || '';\n return configured ? assertSameOriginBase(configured) : '';\n}\n\nexport function setServerInUrl(base: string, href = globalThis.location?.href || ''): void {\n if (!href || !globalThis.history) return;\n const url = new URL(href);\n const normalized = assertSameOriginBase(base);\n if (normalized) url.searchParams.set('server', normalized);\n else url.searchParams.delete('server');\n history.replaceState(null, '', url);\n}\n\nexport function jsonHeaders(authHeader = ''): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n 'X-Bunnyland-Client-Id': browserClientId,\n ...(authHeader.startsWith('Bearer ') ? { Authorization: authHeader } : {}),\n };\n}\n\nexport function adminHeaders(auth: AdminAuth = {}, contentType = ''): Record<string, string> {\n const headers: Record<string, string> = {\n 'X-Bunnyland-Client-Id': auth.clientId || browserClientId,\n };\n if (contentType) headers['Content-Type'] = contentType;\n if (auth.authorization?.startsWith('Bearer ')) headers.Authorization = auth.authorization;\n return headers;\n}\n\nexport function claimHeaders(control: ControlClaimLike | null = null): Record<string, string> {\n return {\n ...jsonHeaders(playerAuthHeader),\n 'X-Bunnyland-Client-Id': control?.clientId || browserClientId,\n ...(control?.claimSecret ? { 'X-Bunnyland-Claim-Secret': control.claimSecret } : {}),\n };\n}\n\nexport function mergePlayerHeaders(headers: HeadersInit = {}): Headers {\n const merged = new Headers(headers);\n if (!merged.has('X-Bunnyland-Client-Id')) {\n merged.set('X-Bunnyland-Client-Id', browserClientId);\n }\n if (playerAuthHeader.startsWith('Bearer ')) {\n merged.set('Authorization', playerAuthHeader);\n }\n return merged;\n}\n\nexport async function parseJsonResponse(res: Response): Promise<unknown> {\n const data = await res.json().catch(() => ({}));\n if (!res.ok) {\n const message = typeof data === 'object' && data && 'detail' in data ? String(data.detail) : `HTTP ${res.status}`;\n throw new ApiError(message, res.status);\n }\n return data;\n}\n\nexport async function sendJson(base: string, path: string, init: RequestInit = {}): Promise<unknown> {\n const headers = mergePlayerHeaders(init.headers || {});\n if (init.body && !headers.has('Content-Type')) headers.set('Content-Type', 'application/json');\n return parseJsonResponse(await fetch(`${assertSameOriginBase(base)}${path}`, {\n ...init,\n headers,\n credentials: 'include',\n }));\n}\n\nexport async function login(base: string, username: string, password: string): Promise<unknown> {\n assertSameOriginBase(base);\n return sendJson(base, '/auth/session', {\n method: 'POST',\n body: JSON.stringify({ username, password, delivery: 'cookie' }),\n });\n}\n\nexport async function authMe(base: string): Promise<unknown> {\n return sendJson(base, '/auth/session');\n}\n\nexport async function rotateAuth(base: string): Promise<unknown> {\n return sendJson(base, '/auth/session', { method: 'PATCH' });\n}\n\nexport async function logout(base: string): Promise<unknown> {\n return sendJson(base, '/auth/session', { method: 'DELETE' });\n}\n\nexport function setPlayerAuth(authHeader = ''): void {\n const next = authHeader.startsWith('Bearer ') ? authHeader : '';\n if (next === playerAuthHeader) return;\n playerAuthHeader = next;\n for (const listener of playerAuthListeners) listener();\n}\n\nexport function getPlayerAuth(): string {\n return playerAuthHeader;\n}\n\nexport function subscribePlayerAuth(listener: () => void): () => void {\n playerAuthListeners.add(listener);\n return () => playerAuthListeners.delete(listener);\n}\n\nexport function setClientId(clientId: string): void {\n const normalized = clientId.trim();\n if (normalized) browserClientId = normalized;\n}\n\nexport function getClientId(): string {\n return browserClientId;\n}\n\nexport async function sendAdmin(base: string, path: string, auth: AdminAuth = {}, init: RequestInit = {}): Promise<unknown> {\n return parseJsonResponse(await fetch(`${assertSameOriginBase(base)}${path}`, {\n ...init,\n headers: adminHeaders(auth),\n credentials: 'include',\n }));\n}\n\nexport function socketUrl(base: string, path = '/admin/world/stream', _authHeader = ''): string {\n const normalized = assertSameOriginBase(base);\n const httpUrl = globalThis.location\n ? new URL(`${normalized}${path}`, globalThis.location.href).href\n : `${normalized}${path}`;\n return httpUrl.replace(/^http/, 'ws');\n}\n\nexport function mediaUrl(base: string, url: string): string {\n if (!url) return '';\n if (url.startsWith('data:')) return url;\n if (/^https?:/.test(url)) {\n assertSameOriginBase(url);\n return url;\n }\n return `${assertSameOriginBase(base)}${url}`;\n}\n\nexport async function requestSceneImage(base: string, characterId: string, control: ControlClaimLike | null = null): Promise<unknown> {\n void characterId;\n if (!control?.claimId) throw new Error('A character claim is required');\n return sendJson(base, `/play/claims/${encodeURIComponent(control.claimId)}/jobs`, {\n method: 'POST',\n headers: claimHeaders(control),\n body: JSON.stringify({ kind: 'scene_image' }),\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export * from './api';
2
+ export * from './play';
3
+ export * from './theme';
4
+ export * from './widgets';
5
+ export * from './player-widgets';
6
+ export * from './admin-widgets';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import { ApiError as e, adminHeaders as t, assertSameOriginBase as n, authMe as r, claimHeaders as i, getClientId as a, getPlayerAuth as o, jsonHeaders as s, login as c, logout as l, mediaUrl as u, mergePlayerHeaders as d, normalizeBase as f, parseJsonResponse as p, requestSceneImage as m, rotateAuth as h, sendAdmin as g, sendJson as _, serverFromUrl as v, setClientId as y, setPlayerAuth as b, setServerInUrl as x, socketUrl as S, subscribePlayerAuth as C } from "./api.js";
2
+ import { cloneJson as w, escapeHtml as T, normalizeTags as E, storageGet as D, storageRemove as O, storageSet as k } from "./widgets.js";
3
+ import { IMAGE_AFFORDANCE as A, KIND_ICON as j, actionArguments as M, actionAvailable as N, actionCommandType as P, actionCost as F, actionFields as I, actionIcon as L, actionLane as R, actionTitle as z, actionTool as B, actionUnavailableReason as V, allTargets as H, cancelQueuedCommand as U, characterHref as W, claimCharacter as G, clearClaimControl as K, controlFromResponse as q, createPlayerLiveUpdates as J, drainNarratedEvents as Y, fetchCharacterProjection as X, fetchCharacterRecentEvents as Z, fetchCharacters as Q, fetchClaimProjection as $, fetchQueuedCommands as ee, fetchRecentEvents as te, filterActions as ne, iconPreference as re, imageCompletionFromMessage as ie, imageCompletions as ae, imageFailureFromMessage as oe, imageRequestMessage as se, inventoryEntries as ce, isClaimNotFoundError as le, latestImageCompletion as ue, latestImageFailure as de, openPlayerUpdates as fe, orderActionsByAvailability as pe, parseCharacterList as me, parseCharacterProjection as he, parseQueuedCommands as ge, persistentClientId as _e, portraitStatusMessage as ve, queuedCommandCost as ye, queuedCommandDetail as be, queuedCommandLabel as xe, queuedCommandName as Se, queuedCountdownSeconds as Ce, randomClientId as we, setIconPreference as Te, storeClaimControl as Ee, storedClaimControl as De, submitCommand as Oe } from "./play.js";
4
+ import { COLOR_SCHEME_CLASS_PREFIX as ke, COLOR_SCHEME_KEY as Ae, DEFAULT_THEME as je, DEFAULT_THEME_OPTIONS as Me, THEME_CHANGE_EVENT as Ne, THEME_CLASS_PREFIX as Pe, THEME_KEY as Fe, THEME_OPTIONS as Ie, bindColorSchemeSelect as Le, bindThemeSelect as Re, colorSchemeOptions as ze, currentColorScheme as Be, currentTheme as Ve, initTheme as He, normalizeTheme as Ue, registerThemeOption as We, registerThemeOptions as Ge, setColorScheme as Ke, setTheme as qe, themeFromSearch as Je, themeOptions as Ye } from "./theme.js";
5
+ import { mergeGalleryItems as Xe, renderGalleryItems as Ze } from "./player-widgets.js";
6
+ import { bindSearchDropdown as Qe, bindTagEditor as $e, readTagEditorTags as et, renderTagEditorTags as tt, tagEditorHtml as nt } from "./admin-widgets.js";
7
+ export { e as ApiError, ke as COLOR_SCHEME_CLASS_PREFIX, Ae as COLOR_SCHEME_KEY, je as DEFAULT_THEME, Me as DEFAULT_THEME_OPTIONS, A as IMAGE_AFFORDANCE, j as KIND_ICON, Ne as THEME_CHANGE_EVENT, Pe as THEME_CLASS_PREFIX, Fe as THEME_KEY, Ie as THEME_OPTIONS, M as actionArguments, N as actionAvailable, P as actionCommandType, F as actionCost, I as actionFields, L as actionIcon, R as actionLane, z as actionTitle, B as actionTool, V as actionUnavailableReason, t as adminHeaders, H as allTargets, n as assertSameOriginBase, r as authMe, Le as bindColorSchemeSelect, Qe as bindSearchDropdown, $e as bindTagEditor, Re as bindThemeSelect, U as cancelQueuedCommand, W as characterHref, G as claimCharacter, i as claimHeaders, K as clearClaimControl, w as cloneJson, ze as colorSchemeOptions, q as controlFromResponse, J as createPlayerLiveUpdates, Be as currentColorScheme, Ve as currentTheme, Y as drainNarratedEvents, T as escapeHtml, X as fetchCharacterProjection, Z as fetchCharacterRecentEvents, Q as fetchCharacters, $ as fetchClaimProjection, ee as fetchQueuedCommands, te as fetchRecentEvents, ne as filterActions, a as getClientId, o as getPlayerAuth, re as iconPreference, ie as imageCompletionFromMessage, ae as imageCompletions, oe as imageFailureFromMessage, se as imageRequestMessage, He as initTheme, ce as inventoryEntries, le as isClaimNotFoundError, s as jsonHeaders, ue as latestImageCompletion, de as latestImageFailure, c as login, l as logout, u as mediaUrl, Xe as mergeGalleryItems, d as mergePlayerHeaders, f as normalizeBase, E as normalizeTags, Ue as normalizeTheme, fe as openPlayerUpdates, pe as orderActionsByAvailability, me as parseCharacterList, he as parseCharacterProjection, p as parseJsonResponse, ge as parseQueuedCommands, _e as persistentClientId, ve as portraitStatusMessage, ye as queuedCommandCost, be as queuedCommandDetail, xe as queuedCommandLabel, Se as queuedCommandName, Ce as queuedCountdownSeconds, we as randomClientId, et as readTagEditorTags, We as registerThemeOption, Ge as registerThemeOptions, Ze as renderGalleryItems, tt as renderTagEditorTags, m as requestSceneImage, h as rotateAuth, g as sendAdmin, _ as sendJson, v as serverFromUrl, y as setClientId, Ke as setColorScheme, Te as setIconPreference, b as setPlayerAuth, x as setServerInUrl, qe as setTheme, S as socketUrl, D as storageGet, O as storageRemove, k as storageSet, Ee as storeClaimControl, De as storedClaimControl, Oe as submitCommand, C as subscribePlayerAuth, nt as tagEditorHtml, Je as themeFromSearch, Ye as themeOptions };
package/dist/play.d.ts ADDED
@@ -0,0 +1,265 @@
1
+ import { type ControlClaimLike } from './api';
2
+ export declare const IMAGE_AFFORDANCE: {
3
+ REQUEST_EMOJI: string;
4
+ ACK_EMOJI: string;
5
+ DELIVER_EMOJI: string;
6
+ FAIL_EMOJI: string;
7
+ REQUEST_LABEL: string;
8
+ };
9
+ export declare const KIND_ICON: Record<string, string>;
10
+ export interface CharacterSummary {
11
+ id: string;
12
+ name: string;
13
+ kind: string;
14
+ suspended: boolean;
15
+ }
16
+ export interface ControlClaim {
17
+ characterId: string;
18
+ controllerId: string;
19
+ generation: number;
20
+ claimId: string;
21
+ claimSecret: string;
22
+ clientId: string;
23
+ active?: boolean;
24
+ }
25
+ export interface ActionArgument {
26
+ key: string;
27
+ title?: string;
28
+ kind?: string;
29
+ required?: boolean;
30
+ target_group?: string;
31
+ }
32
+ export interface ActionView {
33
+ command_type?: string;
34
+ tool_name?: string;
35
+ title?: string;
36
+ lane?: string;
37
+ available?: boolean;
38
+ unavailable_reason?: string;
39
+ cost?: {
40
+ action?: number;
41
+ focus?: number;
42
+ };
43
+ arguments?: ActionArgument[];
44
+ icon?: string;
45
+ }
46
+ export interface TargetOption {
47
+ value: string;
48
+ label: string;
49
+ kind: string;
50
+ icon: string;
51
+ }
52
+ export interface ProjectionItem {
53
+ id: string;
54
+ label?: string;
55
+ name?: string;
56
+ kind?: string;
57
+ sprite?: Record<string, unknown>;
58
+ }
59
+ export interface CharacterProjection {
60
+ characterId: string;
61
+ characterName: string;
62
+ worldEpoch: number;
63
+ room: {
64
+ id: string;
65
+ title: string;
66
+ biome: string;
67
+ exits: {
68
+ id: string;
69
+ direction: string;
70
+ label: string;
71
+ locked: boolean;
72
+ }[];
73
+ entities: unknown[];
74
+ };
75
+ inventory: ProjectionItem[];
76
+ points: Record<string, number>;
77
+ controller: {
78
+ controller_id?: string;
79
+ generation?: number;
80
+ } | null;
81
+ portrait?: Record<string, unknown>;
82
+ sheet?: Record<string, unknown>;
83
+ targetGroups: Record<string, TargetOption[]>;
84
+ actions: ActionView[];
85
+ }
86
+ export interface QueuedProjection {
87
+ characterId: string;
88
+ worldEpoch: number;
89
+ generatedAtUnix?: number | null;
90
+ nextTickAtUnix: number | null;
91
+ tickSeconds?: number | null;
92
+ commands: QueuedCommand[];
93
+ }
94
+ export interface ClaimProjectionBundle {
95
+ character: CharacterProjection | null;
96
+ queued: QueuedProjection | null;
97
+ }
98
+ export interface QueuedCommand {
99
+ command_id?: string;
100
+ command_type?: string;
101
+ lane?: string;
102
+ payload?: Record<string, unknown>;
103
+ cost?: {
104
+ action?: number;
105
+ focus?: number;
106
+ };
107
+ }
108
+ export interface ActivityLine {
109
+ text: string;
110
+ kind: 'event' | 'system' | 'rejection';
111
+ icon?: string;
112
+ }
113
+ export interface ClaimOptions {
114
+ fallbackController?: string;
115
+ timeoutSeconds?: number;
116
+ label?: string;
117
+ clientIdKey?: string;
118
+ clientIdPrefix?: string;
119
+ }
120
+ export type PlayerLiveState = 'connecting' | 'live' | 'fallback' | 'closed';
121
+ export interface PlayerUpdateFrame {
122
+ type: 'ready' | 'event' | 'invalidate' | 'resync' | 'heartbeat';
123
+ data: Record<string, unknown>;
124
+ world_id?: string;
125
+ protocol_version?: number;
126
+ projection_version?: number;
127
+ world_epoch?: number;
128
+ stream_sequence?: number;
129
+ event_id?: string | null;
130
+ causal_command_id?: string | null;
131
+ }
132
+ export interface PlayerUpdatesSocket {
133
+ close(code?: number): void;
134
+ }
135
+ interface WebSocketLike extends PlayerUpdatesSocket {
136
+ onopen: ((event?: unknown) => void) | null;
137
+ onmessage: ((event: {
138
+ data?: unknown;
139
+ }) => void) | null;
140
+ onclose: ((event?: unknown) => void) | null;
141
+ onerror: ((event?: unknown) => void) | null;
142
+ send(data: string): void;
143
+ }
144
+ export interface OpenPlayerUpdatesOptions {
145
+ base: string;
146
+ characterId: string;
147
+ control?: ControlClaimLike | null;
148
+ onFrame: (frame: PlayerUpdateFrame) => void;
149
+ onOpen?: () => void;
150
+ onClose?: () => void;
151
+ onError?: () => void;
152
+ onAnyFrame?: () => void;
153
+ webSocketFactory?: (url: string) => WebSocketLike;
154
+ }
155
+ export interface PlayerLiveUpdates {
156
+ close(): void;
157
+ getState(): PlayerLiveState;
158
+ }
159
+ export interface CreatePlayerLiveUpdatesOptions {
160
+ base: string;
161
+ characterId: string;
162
+ control?: ControlClaimLike | null;
163
+ refresh: () => void | Promise<void>;
164
+ onFrame?: (frame: PlayerUpdateFrame) => void;
165
+ onState?: (state: PlayerLiveState) => void;
166
+ webSocketFactory?: (url: string) => WebSocketLike;
167
+ random?: () => number;
168
+ }
169
+ export declare function openPlayerUpdates(options: OpenPlayerUpdatesOptions): PlayerUpdatesSocket | null;
170
+ export declare function createPlayerLiveUpdates(options: CreatePlayerLiveUpdatesOptions): PlayerLiveUpdates;
171
+ export declare function randomClientId(prefix?: string): string;
172
+ export declare function persistentClientId(key?: string, prefix?: string): string;
173
+ export declare function storedClaimControl(key: string, characterId: string): ControlClaim | null;
174
+ export declare function storeClaimControl(key: string, control: ControlClaim): void;
175
+ export declare function clearClaimControl(key: string, characterId: string): void;
176
+ export declare function isClaimNotFoundError(error: unknown): boolean;
177
+ export declare function fetchClaimProjection(base: string, characterId: string, control?: ControlClaim | null): Promise<ClaimProjectionBundle>;
178
+ export declare function fetchCharacters(base: string): Promise<CharacterSummary[]>;
179
+ export declare function claimCharacter(base: string, characterId: string, storageKey: string, options?: ClaimOptions): Promise<ControlClaim>;
180
+ export declare function fetchCharacterProjection(base: string, characterId: string, control?: ControlClaim | null): Promise<CharacterProjection>;
181
+ export declare function fetchQueuedCommands(base: string, characterId: string, control?: ControlClaim | null): Promise<QueuedProjection>;
182
+ export declare function submitCommand(base: string, payload: unknown, control?: ControlClaim | null): Promise<unknown>;
183
+ export declare function cancelQueuedCommand(base: string, characterId: string, commandId: string, control: ControlClaim): Promise<unknown>;
184
+ export declare function fetchRecentEvents(base: string): Promise<unknown[]>;
185
+ export declare function fetchCharacterRecentEvents(base: string, characterId: string, control?: ControlClaimLike | null): Promise<unknown[]>;
186
+ export declare function parseCharacterList(data: unknown): {
187
+ epoch: number;
188
+ characters: CharacterSummary[];
189
+ };
190
+ export declare function parseCharacterProjection(data: unknown): CharacterProjection | null;
191
+ export declare function parseQueuedCommands(data: unknown): QueuedProjection | null;
192
+ export declare function controlFromResponse(data: unknown, fallbackCharacterId?: string, { active }?: {
193
+ active?: boolean;
194
+ }): ControlClaim | null;
195
+ export declare function actionTitle(action: ActionView | QueuedCommand): string;
196
+ export declare function actionIcon(action: ActionView): string;
197
+ export declare function actionTool(action: ActionView): string;
198
+ export declare function actionCommandType(action: ActionView | QueuedCommand): string;
199
+ export declare function actionCost(action: ActionView | QueuedCommand): {
200
+ action: number;
201
+ focus: number;
202
+ };
203
+ export declare function actionLane(action: ActionView | QueuedCommand): string;
204
+ export declare function actionArguments(action: ActionView): ActionArgument[];
205
+ export declare function actionAvailable(action: ActionView): boolean;
206
+ export declare function actionUnavailableReason(action: ActionView): string;
207
+ export declare function orderActionsByAvailability(actions: ActionView[]): ActionView[];
208
+ export declare function filterActions(actions: ActionView[], query?: string): ActionView[];
209
+ export declare function actionFields(action: ActionView, projection: CharacterProjection): {
210
+ key: string;
211
+ label: string;
212
+ kind: string;
213
+ required: boolean;
214
+ candidates: TargetOption[] | null;
215
+ }[];
216
+ export declare function allTargets(projection: CharacterProjection | null): TargetOption[];
217
+ export declare function inventoryEntries(projection: CharacterProjection | null): TargetOption[];
218
+ export declare function iconPreference(key: string, defaultValue?: boolean): boolean;
219
+ export declare function setIconPreference(key: string, value: boolean): void;
220
+ export declare function queuedCountdownSeconds(queueProjection: QueuedProjection | null): number | null;
221
+ export declare function queuedCommandCost(command: QueuedCommand): string;
222
+ export declare function queuedCommandDetail(command: QueuedCommand): string;
223
+ export declare function queuedCommandName(command: QueuedCommand, actions?: ActionView[]): string;
224
+ export declare function queuedCommandLabel(command: QueuedCommand, actions?: ActionView[]): string;
225
+ export declare function imageRequestMessage(result: unknown): string;
226
+ export declare function imageCompletionFromMessage(message: unknown, base?: string): {
227
+ url: string;
228
+ alphaUrl: string;
229
+ purpose: string;
230
+ epoch: number;
231
+ entityId: string;
232
+ } | null;
233
+ export declare function imageCompletions(messages: unknown[], base?: string, purpose?: string): {
234
+ url: string;
235
+ alphaUrl: string;
236
+ purpose: string;
237
+ epoch: number;
238
+ entityId: string;
239
+ }[];
240
+ export declare function latestImageCompletion(messages: unknown[], baseOrOptions?: string | {
241
+ base?: string;
242
+ purpose?: string;
243
+ }, purpose?: string): ReturnType<typeof imageCompletionFromMessage>;
244
+ export declare function imageFailureFromMessage(message: unknown): {
245
+ entityId: string;
246
+ purpose: string;
247
+ reason: string;
248
+ epoch: number;
249
+ } | null;
250
+ export declare function latestImageFailure(messages: unknown[], purposeOrOptions?: string | {
251
+ purpose?: string;
252
+ }): ReturnType<typeof imageFailureFromMessage>;
253
+ export declare function characterHref(apiBase: string, characterId: string, view?: 'chat' | 'sheet', page?: string): string;
254
+ export declare function portraitStatusMessage(projection: CharacterProjection | null, requestState?: string): string;
255
+ export declare function drainNarratedEvents(messages: unknown[], { seenIds, playerId, roomOf, nameFor, }?: {
256
+ seenIds?: Set<string>;
257
+ playerId?: string;
258
+ roomOf?: (id: string) => string | null;
259
+ nameFor?: (id: string) => string | null;
260
+ }): {
261
+ lines: ActivityLine[];
262
+ seenIds: Set<string>;
263
+ };
264
+ export {};
265
+ //# sourceMappingURL=play.d.ts.map