" + (n ? i : X(i, !0)) + "\n" : "" + (n ? i : X(i, !0)) + "\n";
+ }
+ blockquote({ tokens: e }) {
+ return `+${this.parser.parse(e)}+`; + } + html({ text: e }) { + return e; + } + def(e) { + return ""; + } + heading({ tokens: e, depth: t }) { + return `
${this.parser.parseInline(e)}
+`; + } + table(e) { + let t = "", n = ""; + for (let t = 0; t < e.header.length; t++) n += this.tablecell(e.header[t]); + t += this.tablerow({ text: n }); + let r = ""; + for (let t = 0; t < e.rows.length; t++) { + let i = e.rows[t]; + n = ""; + for (let e = 0; e < i.length; e++) n += this.tablecell(i[e]); + r += this.tablerow({ text: n }); + } + return r &&= `${r}`, "${X(e, !0)}`;
+ }
+ br(e) {
+ return "An error occurred:
" + X(n.message + "", !0) + ""; + return t ? Promise.resolve(e) : e; + } + if (t) return Promise.reject(n); + throw n; + }; + } +}(); +function $(e, t) { + return Bn.parse(e, t); +} +$.options = $.setOptions = function(e) { + return Bn.setOptions(e), $.defaults = Bn.defaults, _t($.defaults), $; +}, $.getDefaults = gt, $.defaults = B; +function Vn(...e) { + return Bn.use(...e), $.defaults = Bn.defaults, _t($.defaults), $; +} +$.use = Vn, $.walkTokens = function(e, t) { + return Bn.walkTokens(e, t); +}, $.parseInline = Bn.parseInline, $.Parser = Q, $.parser = Q.parse, $.Renderer = Ln, $.TextRenderer = Rn, $.Lexer = Z, $.lexer = Z.lex, $.Tokenizer = In, $.Hooks = zn, $.parse = $, $.options, $.setOptions, $.walkTokens, $.parseInline, Q.parse, Z.lex; +//#endregion +//#region resources/js/composables/useAiChat.ts +function Hn(e, t) { + let r = _(null), i = _([]), a = _([]), o = _(!1), s = _(!1), c = _(!1), l = _(null), u = n(() => r.value !== null); + function d() { + r.value = null, i.value = [], a.value = [], o.value = !1, s.value = !1, c.value = !1, l.value = null; + } + function f() { + r.value = null, i.value = [], l.value = null; + } + async function p() { + o.value = !0; + try { + let { data: t } = await e.get(E.conversations); + a.value = t.conversations; + } catch {} finally { + o.value = !1; + } + } + async function m(n) { + s.value = !0, l.value = null; + try { + let { data: t } = await e.get(E.conversation(n)); + r.value = t.conversation.id, i.value = t.messages; + } catch (e) { + let n = Un(e, "Unable to load this conversation."); + l.value = n, t("error", n); + } finally { + s.value = !1; + } + } + async function h(t) { + let n = t.trim(); + if (!n || c.value) return; + l.value = null, c.value = !0; + let a = { + id: -Date.now(), + role: "user", + content: n, + created_at: (/* @__PURE__ */ new Date()).toISOString() + }; + i.value.push(a); + try { + let { data: t } = await e.post(E.chat, { + conversation_id: r.value, + message: n + }); + r.value = t.conversation.id, i.value = i.value.map((e) => e.id === a.id ? { + ...a, + id: a.id + } : e), i.value.push(t.message), p(); + } catch (e) { + i.value = i.value.filter((e) => e.id !== a.id), l.value = Un(e, "The assistant could not respond. Please try again."); + } finally { + c.value = !1; + } + } + async function g(n, r) { + let i = r.trim(); + if (i) try { + await e.patch(E.conversation(n), { title: i }); + let t = a.value.find((e) => e.id === n); + t && (t.title = i); + } catch (e) { + t("error", Un(e, "Unable to rename this conversation.")); + } + } + async function v(n) { + try { + await e.delete(E.conversation(n)), a.value = a.value.filter((e) => e.id !== n), r.value === n && f(); + } catch (e) { + t("error", Un(e, "Unable to delete this conversation.")); + } + } + return { + currentConversationId: r, + messages: i, + conversations: a, + isLoadingConversations: o, + isLoadingConversation: s, + isSending: c, + lastError: l, + hasActiveConversation: u, + reset: d, + newConversation: f, + refreshConversations: p, + loadConversation: m, + sendMessage: h, + renameConversation: g, + deleteConversation: v + }; +} +function Un(e, t) { + if (typeof e == "object" && e && "response" in e) { + let t = e.response; + if (typeof t?.data?.message == "string") return t.data.message; + } + return e instanceof Error && e.message ? e.message : t; +} +//#endregion +//#region resources/js/components/AiChatOverlay.vue?vue&type=script&setup=true&lang.ts +var Wn = { + key: 1, + class: "fixed inset-y-0 right-0 z-50 flex w-full bg-surface shadow-2xl sm:w-[480px] lg:w-[640px]", + "aria-label": "AI Assistant" +}, Gn = { class: "hidden w-52 shrink-0 border-r border-line-default bg-surface-secondary sm:flex sm:flex-col" }, Kn = { class: "flex-1 overflow-y-auto p-2" }, qn = { + key: 0, + class: "p-2 text-xs text-muted" +}, Jn = { + key: 1, + class: "p-2 text-xs text-muted" +}, Yn = { + key: 2, + class: "space-y-1" +}, Xn = ["onClick"], Zn = { class: "hidden shrink-0 group-hover:flex" }, Qn = ["onClick"], $n = ["onClick"], er = { class: "flex min-w-0 flex-1 flex-col" }, tr = { class: "flex h-14 items-center justify-between border-b border-line-default px-3" }, nr = { class: "flex items-center gap-2" }, rr = { class: "flex items-center gap-1" }, ir = { + key: 0, + class: "border-b border-line-default bg-surface-secondary p-2 sm:hidden" +}, ar = { class: "max-h-48 overflow-y-auto" }, or = ["onClick"], sr = { + key: 0, + class: "p-2 text-xs text-muted" +}, cr = { + key: 0, + class: "py-12 text-center text-sm text-muted" +}, lr = { + key: 1, + class: "mx-auto mt-16 max-w-xs text-center text-sm text-muted" +}, ur = { + key: 0, + class: "max-w-[85%] whitespace-pre-wrap break-words rounded-lg bg-primary-500 px-4 py-2 text-sm text-white" +}, dr = ["innerHTML"], fr = { + key: 2, + class: "inline-flex rounded-lg bg-surface-tertiary px-4 py-2 text-sm italic text-muted" +}, pr = { + key: 3, + class: "rounded bg-alert-error-bg p-3 text-xs text-alert-error-text" +}, mr = ["disabled"], hr = ["disabled"], gr = /* @__PURE__ */ l({ + __name: "AiChatOverlay", + props: /*@__PURE__*/ d({ + client: { type: [Function, Object] }, + enabled: { type: Boolean }, + notify: { type: Function } + }, { + open: { + type: Boolean, + default: !1 + }, + openModifiers: {} + }), + emits: ["update:open"], + setup(n) { + let s = n, l = S(n, "open"), u = Hn(s.client, s.notify), d = _(""), m = _(null), g = _(!1), C = _(null), w = _(""); + te(l, (e) => { + e && s.enabled && u.refreshConversations(); + }), te(() => s.enabled, (e) => { + e || (l.value = !1, u.reset()); + }), te(() => u.messages.value.length, async () => { + await f(), m.value && (m.value.scrollTop = m.value.scrollHeight); + }); + function ee() { + l.value = !1, g.value = !1; + } + async function ne(e) { + await u.loadConversation(e.id), g.value = !1; + } + async function E() { + let e = d.value; + d.value = "", await u.sendMessage(e); + } + function oe(e, t) { + t.stopPropagation(), C.value = e.id, w.value = e.title ?? ""; + } + async function se() { + C.value !== null && await u.renameConversation(C.value, w.value), C.value = null, w.value = ""; + } + async function ce(e, t) { + t.stopPropagation(), window.confirm(`Delete “${e.title ?? "Untitled conversation"}”?`) && await u.deleteConversation(e.id); + } + function D() { + u.newConversation(), g.value = !1; + } + function O(e) { + e.key === "Enter" && !e.shiftKey && (e.preventDefault(), E()); + } + function le(e) { + return ht.sanitize($.parse(e ?? "", { async: !1 })); + } + return (s, f) => { + let _ = y("BaseIcon"); + return h(), r(t, { to: "body" }, [l.value ? (h(), a("button", { + key: 0, + type: "button", + class: "fixed inset-0 z-40 cursor-default bg-black/20", + "aria-label": "Close AI Assistant", + onClick: ee + })) : i("", !0), l.value ? (h(), a("aside", Wn, [o("section", Gn, [o("div", { class: "border-b border-line-default p-3" }, [o("button", { + type: "button", + class: "w-full rounded-md bg-btn-primary px-3 py-2 text-xs font-medium text-white hover:bg-btn-primary-hover", + onClick: D + }, " + New conversation ")]), o("div", Kn, [x(u).isLoadingConversations.value && !x(u).conversations.value.length ? (h(), a("p", qn, "Loading history…")) : x(u).conversations.value.length ? (h(), a("ul", Yn, [(h(!0), a(e, null, v(x(u).conversations.value, (e) => (h(), a("li", { key: e.id }, [C.value === e.id ? (h(), a("form", { + key: 0, + class: "flex gap-1 p-1", + onSubmit: ae(se, ["prevent"]) + }, [re(o("input", { + "onUpdate:modelValue": f[0] ||= (e) => w.value = e, + class: "min-w-0 flex-1 rounded border border-line-default bg-surface px-2 py-1 text-xs text-body", + "aria-label": "Conversation title", + autofocus: "", + onKeydown: f[1] ||= ie((e) => C.value = null, ["esc"]) + }, null, 544), [[T, w.value]]), f[4] ||= o("button", { + type: "submit", + class: "rounded px-1 text-xs text-primary-500 hover:bg-hover", + "aria-label": "Save conversation title" + }, "Save", -1)], 32)) : (h(), a("div", { + key: 1, + class: p(["group flex items-center gap-1 rounded text-sm hover:bg-hover", x(u).currentConversationId.value === e.id ? "bg-hover-strong font-semibold text-heading" : "text-body"]) + }, [o("button", { + type: "button", + class: "min-w-0 flex-1 truncate px-2 py-2 text-left", + onClick: (t) => ne(e) + }, b(e.title || "Untitled conversation"), 9, Xn), o("span", Zn, [o("button", { + type: "button", + class: "px-1 text-muted hover:text-heading", + "aria-label": "Rename conversation", + onClick: (t) => oe(e, t) + }, "✎", 8, Qn), o("button", { + type: "button", + class: "px-1 text-muted hover:text-alert-error-text", + "aria-label": "Delete conversation", + onClick: (t) => ce(e, t) + }, "×", 8, $n)])], 2))]))), 128))])) : (h(), a("p", Jn, "No conversations yet."))])]), o("section", er, [ + o("header", tr, [o("div", nr, [ + o("button", { + type: "button", + class: "rounded p-1 text-muted hover:bg-hover hover:text-heading sm:hidden", + "aria-label": "Conversation history", + onClick: f[2] ||= (e) => g.value = !g.value + }, [c(_, { + name: "Bars3Icon", + class: "h-5 w-5" + })]), + c(_, { + name: "SparklesIcon", + class: "h-5 w-5 text-primary-500" + }), + f[5] ||= o("h2", { class: "text-sm font-semibold text-heading" }, "AI Assistant", -1) + ]), o("div", rr, [o("button", { + type: "button", + class: "rounded p-2 text-muted hover:bg-hover hover:text-heading", + "aria-label": "New conversation", + title: "New conversation", + onClick: D + }, [c(_, { + name: "PlusIcon", + class: "h-4 w-4" + })]), o("button", { + type: "button", + class: "rounded p-2 text-muted hover:bg-hover hover:text-heading", + "aria-label": "Close AI Assistant", + onClick: ee + }, [c(_, { + name: "XMarkIcon", + class: "h-5 w-5" + })])])]), + g.value ? (h(), a("div", ir, [o("button", { + type: "button", + class: "mb-2 w-full rounded-md bg-btn-primary px-3 py-2 text-xs font-medium text-white", + onClick: D + }, "+ New conversation"), o("div", ar, [(h(!0), a(e, null, v(x(u).conversations.value, (e) => (h(), a("button", { + key: e.id, + type: "button", + class: "block w-full truncate rounded px-2 py-2 text-left text-sm text-body hover:bg-hover", + onClick: (t) => ne(e) + }, b(e.title || "Untitled conversation"), 9, or))), 128)), x(u).conversations.value.length ? i("", !0) : (h(), a("p", sr, "No conversations yet."))])])) : i("", !0), + o("main", { + ref_key: "messagesEl", + ref: m, + class: "flex-1 space-y-3 overflow-y-auto p-4", + "aria-live": "polite" + }, [ + x(u).isLoadingConversation.value ? (h(), a("div", cr, "Loading conversation…")) : x(u).messages.value.length ? i("", !0) : (h(), a("div", lr, [c(_, { + name: "SparklesIcon", + class: "mx-auto mb-3 h-10 w-10 text-subtle" + }), f[6] ||= o("p", null, "Ask for help with invoices, customers, or your business.", -1)])), + (h(!0), a(e, null, v(x(u).messages.value, (e) => (h(), a("article", { + key: e.id, + class: p(["flex", e.role === "user" ? "justify-end" : "justify-start"]) + }, [e.role === "user" ? (h(), a("p", ur, b(e.content), 1)) : (h(), a("div", { + key: 1, + class: "max-w-[85%] break-words rounded-lg bg-surface-tertiary px-4 py-2 text-sm text-body [&_a]:text-primary-500 [&_a]:underline [&_ol]:mt-3 [&_ol]:list-decimal [&_ol]:pl-5 [&_p+p]:mt-3 [&_pre]:mt-3 [&_pre]:overflow-x-auto [&_pre]:rounded [&_pre]:bg-surface [&_pre]:p-3 [&_ul]:mt-3 [&_ul]:list-disc [&_ul]:pl-5", + innerHTML: le(e.content) + }, null, 8, dr))], 2))), 128)), + x(u).isSending.value ? (h(), a("div", fr, "Thinking…")) : i("", !0), + x(u).lastError.value ? (h(), a("p", pr, b(x(u).lastError.value), 1)) : i("", !0) + ], 512), + o("form", { + class: "flex items-end gap-2 border-t border-line-default p-3", + onSubmit: ae(E, ["prevent"]) + }, [re(o("textarea", { + "onUpdate:modelValue": f[3] ||= (e) => d.value = e, + rows: "2", + class: "min-h-11 flex-1 resize-none rounded-md border border-line-default bg-surface px-3 py-2 text-sm text-body outline-none focus:ring-1 focus:ring-primary-500", + placeholder: "Ask about your business…", + disabled: !n.enabled || x(u).isSending.value, + onKeydown: O + }, null, 40, mr), [[T, d.value]]), o("button", { + type: "submit", + class: "rounded-md bg-btn-primary px-3 py-2 text-sm font-medium text-white hover:bg-btn-primary-hover disabled:cursor-not-allowed disabled:opacity-50", + disabled: !n.enabled || x(u).isSending.value || !d.value.trim() + }, b(x(u).isSending.value ? "Sending…" : "Send"), 9, hr)], 32) + ])])) : i("", !0)]); + }; + } +}), _r = /* @__PURE__ */ l({ + __name: "AiHeaderAction", + emits: ["open"], + setup(e) { + return (e, t) => { + let n = y("BaseIcon"); + return h(), a("li", null, [o("button", { + type: "button", + class: "inline-flex items-center gap-1 rounded-md px-2 py-1 text-sm text-body hover:bg-hover", + title: "AI Assistant", + "aria-label": "Open AI Assistant", + onClick: t[0] ||= (t) => e.$emit("open") + }, [c(n, { + name: "SparklesIcon", + class: "h-5 w-5 text-primary-500" + }), t[1] ||= o("span", { class: "hidden lg:inline" }, "AI", -1)])]); + }; + } +}), vr = ["disabled"], yr = { + class: "w-full max-w-xl overflow-hidden rounded-lg bg-surface shadow-2xl", + role: "dialog", + "aria-modal": "true", + "aria-labelledby": "ai-generate-title" +}, br = { class: "flex items-center justify-between border-b border-line-default px-5 py-4" }, xr = { class: "flex items-center gap-2" }, Sr = ["disabled"], Cr = ["disabled"], wr = { + key: 0, + class: "flex cursor-pointer items-start gap-2 text-sm text-body" +}, Tr = { + key: 1, + class: "rounded bg-alert-error-bg p-3 text-sm text-alert-error-text" +}, Er = { + key: 2, + class: "rounded-md border border-line-default bg-surface-secondary p-3" +}, Dr = { class: "max-h-48 overflow-y-auto whitespace-pre-wrap break-words text-sm text-body" }, Or = { class: "flex flex-wrap justify-end gap-2 border-t border-line-default p-4" }, kr = ["disabled"], Ar = ["disabled"], jr = ["disabled"], Mr = ["disabled"], Nr = ["disabled"], Pr = /* @__PURE__ */ l({ + __name: "AiTextAction", + props: { + client: { type: [Function, Object] }, + context: {}, + enabled: { type: Boolean }, + notify: { type: Function } + }, + setup(l) { + let u = l, d = _(!1), f = _(""), p = _(!1), m = _(""), g = _(!1), v = _(""), x = n(() => m.value.trim().length > 0); + te(d, (e) => { + e || (f.value = "", p.value = !1, m.value = "", v.value = ""); + }); + async function S() { + if (!(!f.value.trim() || g.value)) { + g.value = !0, v.value = "", m.value = ""; + try { + let { data: e } = await u.client.post(E.generate, { + prompt: f.value.trim(), + context: p.value ? u.context.getHtml() : void 0 + }); + e.text ? m.value = e.text : v.value = e.message ?? e.error ?? "The assistant could not generate text."; + } catch (e) { + v.value = Un(e, "The assistant could not generate text."); + } finally { + g.value = !1; + } + } + } + function w() { + u.context.insertContent(m.value), u.notify("success", "AI text inserted."), d.value = !1; + } + function ee() { + u.context.replaceContent(m.value), u.notify("success", "Editor content replaced with AI text."), d.value = !1; + } + return (n, u) => { + let _ = y("BaseIcon"); + return h(), a(e, null, [o("button", { + type: "button", + class: "inline-flex items-center gap-1 rounded px-2 py-1 text-xs text-body hover:bg-hover disabled:cursor-not-allowed disabled:opacity-50", + disabled: !l.enabled, + title: "Generate with AI", + onClick: u[0] ||= (e) => d.value = !0 + }, [c(_, { + name: "SparklesIcon", + class: "h-4 w-4 text-primary-500" + }), u[6] ||= s(" AI write ", -1)], 8, vr), (h(), r(t, { to: "body" }, [d.value ? (h(), a("div", { + key: 0, + class: "fixed inset-0 z-50 grid place-items-center bg-black/30 p-4", + role: "presentation", + onMousedown: u[5] ||= ae((e) => d.value = !1, ["self"]) + }, [o("section", yr, [ + o("header", br, [o("div", xr, [c(_, { + name: "SparklesIcon", + class: "h-5 w-5 text-primary-500" + }), u[7] ||= o("h2", { + id: "ai-generate-title", + class: "font-semibold text-heading" + }, "Generate text", -1)]), o("button", { + type: "button", + class: "rounded p-1 text-muted hover:bg-hover hover:text-heading", + "aria-label": "Close", + disabled: g.value, + onClick: u[1] ||= (e) => d.value = !1 + }, [c(_, { + name: "XMarkIcon", + class: "h-5 w-5" + })], 8, Sr)]), + o("form", { + class: "space-y-4 p-5", + onSubmit: ae(S, ["prevent"]) + }, [ + u[11] ||= o("label", { + class: "block text-sm font-medium text-heading", + for: "ai-prompt" + }, "What would you like to write?", -1), + re(o("textarea", { + id: "ai-prompt", + "onUpdate:modelValue": u[2] ||= (e) => f.value = e, + rows: "3", + class: "w-full rounded-md border border-line-default bg-surface px-3 py-2 text-sm text-body outline-none focus:ring-1 focus:ring-primary-500", + placeholder: "For example: Write a polite payment reminder", + disabled: g.value, + autofocus: "" + }, null, 8, Cr), [[T, f.value]]), + l.context.getHtml() ? (h(), a("label", wr, [ + re(o("input", { + "onUpdate:modelValue": u[3] ||= (e) => p.value = e, + type: "checkbox", + class: "mt-1" + }, null, 512), [[C, p.value]]), + u[8] ||= s(), + u[9] ||= o("span", null, [ + s("Use the current editor content as context"), + o("br"), + o("span", { class: "text-xs text-muted" }, "The content helps the assistant match the document’s tone and details.") + ], -1) + ])) : i("", !0), + v.value ? (h(), a("p", Tr, b(v.value), 1)) : i("", !0), + m.value ? (h(), a("section", Er, [u[10] ||= o("p", { class: "mb-2 text-xs font-medium text-muted" }, "Preview", -1), o("p", Dr, b(m.value), 1)])) : i("", !0) + ], 32), + o("footer", Or, [ + o("button", { + type: "button", + class: "rounded px-3 py-2 text-sm text-body hover:bg-hover", + disabled: g.value, + onClick: u[4] ||= (e) => d.value = !1 + }, "Cancel", 8, kr), + x.value ? (h(), a("button", { + key: 0, + type: "button", + class: "rounded border border-line-default px-3 py-2 text-sm text-body hover:bg-hover", + disabled: g.value, + onClick: ee + }, "Replace", 8, Ar)) : i("", !0), + x.value ? (h(), a("button", { + key: 1, + type: "button", + class: "rounded border border-line-default px-3 py-2 text-sm text-body hover:bg-hover", + disabled: g.value, + onClick: S + }, "Regenerate", 8, jr)) : i("", !0), + x.value ? (h(), a("button", { + key: 2, + type: "button", + class: "rounded bg-btn-primary px-3 py-2 text-sm font-medium text-white hover:bg-btn-primary-hover", + disabled: g.value, + onClick: w + }, "Insert", 8, Mr)) : (h(), a("button", { + key: 3, + type: "button", + class: "rounded bg-btn-primary px-3 py-2 text-sm font-medium text-white hover:bg-btn-primary-hover disabled:cursor-not-allowed disabled:opacity-50", + disabled: g.value || !f.value.trim(), + onClick: S + }, b(g.value ? "Generating…" : "Generate"), 9, Nr)) + ]) + ])], 32)) : i("", !0)]))], 64); + }; + } +}), Fr = { + key: 0, + class: "p-6 text-sm text-muted" +}, Ir = { + key: 1, + class: "space-y-3 p-6" +}, Lr = { class: "rounded bg-alert-error-bg p-3 text-sm text-alert-error-text" }, Rr = { + key: 0, + class: "border-b border-line-default pb-6" +}, zr = { class: "flex cursor-pointer items-start gap-3" }, Br = ["checked", "disabled"], Vr = { + key: 0, + class: "mt-4 rounded bg-alert-success-bg p-3 text-sm text-alert-success-text" +}, Hr = { + key: 1, + class: "space-y-6" +}, Ur = { class: "flex cursor-pointer items-start gap-3" }, Wr = ["checked"], Gr = { + key: 0, + class: "space-y-5" +}, Kr = { class: "block text-sm font-medium text-heading" }, qr = ["value"], Jr = { + key: 0, + class: "-mt-3 text-xs text-muted" +}, Yr = ["href"], Xr = { class: "block text-sm font-medium text-heading" }, Zr = { class: "mt-1 flex gap-2" }, Qr = ["type", "placeholder"], $r = [ + "value", + "placeholder", + "onInput" +], ei = ["value", "onChange"], ti = ["value"], ni = { class: "border-t border-line-default pt-5" }, ri = { class: "mt-5 space-y-5" }, ii = { class: "flex cursor-pointer gap-3" }, ai = ["checked"], oi = { + key: 0, + class: "mt-3 block text-sm text-body" +}, si = { class: "flex cursor-pointer gap-3" }, ci = ["checked"], li = { + key: 0, + class: "mt-3 block text-sm text-body" +}, ui = { id: "ai-models" }, di = ["value"], fi = { + key: 2, + class: "flex flex-wrap items-center gap-3 border-t border-line-default pt-5" +}, pi = ["disabled"], mi = ["disabled"], hi = /* @__PURE__ */ l({ + __name: "AiConfigurationPage", + props: { + client: { type: [Function, Object] }, + scope: {}, + notify: { type: Function } + }, + setup(t) { + let l = t, u = { + ai_enabled: "NO", + ai_driver: "openrouter", + ai_api_key: "", + ai_base_url: "", + ai_chat_enabled: "NO", + ai_chat_model: "anthropic/claude-sonnet-4.6", + ai_text_generation_enabled: "NO", + ai_text_generation_model: "anthropic/claude-haiku-4.5" + }, d = g({ + ...u, + use_custom_ai_config: "NO" + }), f = _([]), p = _(!0), x = _(!1), S = _(!1), C = _(!1), ie = _(""), oe = n(() => l.scope === "admin" ? E.globalConfig : E.companyConfig), se = n(() => l.scope === "admin" ? E.globalTest : E.companyTest), ce = n(() => d.use_custom_ai_config === "YES"), D = n(() => d.ai_enabled === "YES"), O = n(() => f.value.find((e) => e.value === d.ai_driver)), le = n(() => O.value?.suggested_models ?? []), ue = n(() => O.value?.config_fields ?? []), de = n(() => /[•*]/.test(d.ai_api_key)), fe = n(() => l.scope === "admin" || ce.value); + te(() => d.ai_driver, (e) => { + let t = f.value.find((t) => t.value === e); + t?.default_base_url && !d.ai_base_url && (d.ai_base_url = t.default_base_url); + }), m(() => { + pe(); + }); + async function pe() { + p.value = !0, ie.value = ""; + try { + let [{ data: e }, { data: t }] = await Promise.all([l.client.get(E.drivers), l.client.get(oe.value)]); + f.value = e.ai_drivers, Object.assign(d, u, t); + } catch (e) { + ie.value = Un(e, "Unable to load AI configuration."), l.notify("error", ie.value); + } finally { + p.value = !1; + } + } + function me() { + return { ...d }; + } + function k() { + if (!D.value) return null; + if (!d.ai_driver) return "Choose an AI provider."; + if (!d.ai_api_key && !de.value) return "Enter an API key."; + if (d.ai_base_url) try { + new URL(d.ai_base_url); + } catch { + return "Enter a valid base URL."; + } + return d.ai_chat_enabled === "YES" && !d.ai_chat_model.trim() ? "Choose a chat model." : d.ai_text_generation_enabled === "YES" && !d.ai_text_generation_model.trim() ? "Choose a text generation model." : null; + } + async function A() { + let e = k(); + if (e) { + l.notify("error", e); + return; + } + x.value = !0; + try { + await l.client.post(oe.value, me()), l.notify("success", "AI settings saved."); + } catch (e) { + l.notify("error", Un(e, "Unable to save AI settings.")); + } finally { + x.value = !1; + } + } + async function he(e) { + let t = e.target.checked; + if (d.use_custom_ai_config = t ? "YES" : "NO", !t) { + x.value = !0; + try { + await l.client.post(oe.value, { use_custom_ai_config: "NO" }), l.notify("success", "This company now uses the global AI configuration."); + } catch (e) { + d.use_custom_ai_config = "YES", l.notify("error", Un(e, "Unable to update the company configuration.")); + } finally { + x.value = !1; + } + } + } + async function ge() { + if (D.value) { + S.value = !0; + try { + let { data: e } = await l.client.post(se.value, { + ai_driver: d.ai_driver, + ai_api_key: de.value ? void 0 : d.ai_api_key, + ai_base_url: d.ai_base_url + }); + e.success ? l.notify("success", e.message ?? "Connection successful.") : l.notify("error", e.message ?? e.error ?? "The connection test failed."); + } catch (e) { + l.notify("error", Un(e, "The connection test failed.")); + } finally { + S.value = !1; + } + } + } + function _e(e, t) { + d[e] = t.target.checked ? "YES" : "NO"; + } + function ve(e, t) { + d[`ai_${e}`] = t.target.value; + } + return (n, l) => { + let u = y("BasePageHeader"), m = y("BaseCard"), g = y("BasePage"); + return h(), r(g, null, { + default: ne(() => [c(u, { title: t.scope === "admin" ? "AI Assistant" : "AI Assistant configuration" }, null, 8, ["title"]), c(m, { class: "max-w-3xl" }, { + default: ne(() => [p.value ? (h(), a("div", Fr, "Loading AI configuration…")) : ie.value ? (h(), a("div", Ir, [o("p", Lr, b(ie.value), 1), o("button", { + type: "button", + class: "rounded bg-btn-primary px-3 py-2 text-sm text-white", + onClick: pe + }, "Try again")])) : (h(), a("form", { + key: 2, + class: "space-y-6 p-6", + onSubmit: ae(A, ["prevent"]) + }, [ + t.scope === "company" ? (h(), a("section", Rr, [o("label", zr, [o("input", { + type: "checkbox", + checked: ce.value, + disabled: x.value, + onChange: he + }, null, 40, Br), l[8] ||= o("span", null, [o("span", { class: "block text-sm font-medium text-heading" }, "Use a company-specific AI configuration"), o("span", { class: "mt-1 block text-xs text-muted" }, "Override the global provider and models for this company.")], -1)]), ce.value ? i("", !0) : (h(), a("p", Vr, "This company is using the global AI configuration."))])) : i("", !0), + fe.value ? (h(), a("div", Hr, [o("section", null, [o("label", Ur, [o("input", { + type: "checkbox", + checked: D.value, + onChange: l[0] ||= (e) => _e("ai_enabled", e) + }, null, 40, Wr), l[9] ||= o("span", null, [o("span", { class: "block text-sm font-medium text-heading" }, "Enable AI Assistant"), o("span", { class: "mt-1 block text-xs text-muted" }, "Allow this provider to power chat and editor text generation.")], -1)])]), D.value ? (h(), a("div", Gr, [ + o("label", Kr, [l[10] ||= s("Provider ", -1), re(o("select", { + "onUpdate:modelValue": l[1] ||= (e) => d.ai_driver = e, + class: "mt-1 w-full rounded-md border border-line-default bg-surface px-3 py-2 text-body" + }, [(h(!0), a(e, null, v(f.value, (e) => (h(), a("option", { + key: e.value, + value: e.value + }, b(e.label), 9, qr))), 128))], 512), [[ee, d.ai_driver]])]), + O.value?.website ? (h(), a("p", Jr, [ + l[11] ||= s("Get an API key from ", -1), + o("a", { + class: "text-primary-500 underline", + href: O.value.website, + target: "_blank", + rel: "noopener noreferrer" + }, b(O.value.label), 9, Yr), + l[12] ||= s(".", -1) + ])) : i("", !0), + o("label", Xr, [ + l[13] ||= s("API key ", -1), + o("span", Zr, [re(o("input", { + "onUpdate:modelValue": l[2] ||= (e) => d.ai_api_key = e, + type: C.value ? "text" : "password", + autocomplete: "new-password", + class: "min-w-0 flex-1 rounded-md border border-line-default bg-surface px-3 py-2 text-body", + placeholder: de.value ? "Stored securely — enter a new key to replace it" : "" + }, null, 8, Qr), [[w, d.ai_api_key]]), o("button", { + type: "button", + class: "rounded border border-line-default px-3 text-xs text-body hover:bg-hover", + onClick: l[3] ||= (e) => C.value = !C.value + }, b(C.value ? "Hide" : "Show"), 1)]), + l[14] ||= o("span", { class: "mt-1 block text-xs font-normal text-muted" }, "Keys are stored securely. Leave the masked value unchanged to keep the current key.", -1) + ]), + (h(!0), a(e, null, v(ue.value, (t) => (h(), a("label", { + key: t.key, + class: "block text-sm font-medium text-heading" + }, [s(b(t.label) + " ", 1), t.type === "text" ? (h(), a("input", { + key: 0, + value: d[`ai_${t.key}`] ?? "", + placeholder: t.default, + class: "mt-1 w-full rounded-md border border-line-default bg-surface px-3 py-2 text-body", + onInput: (e) => ve(t.key, e) + }, null, 40, $r)) : (h(), a("select", { + key: 1, + value: d[`ai_${t.key}`] ?? "", + class: "mt-1 w-full rounded-md border border-line-default bg-surface px-3 py-2 text-body", + onChange: (e) => ve(t.key, e) + }, [(h(!0), a(e, null, v(t.options, (e) => (h(), a("option", { + key: e.value, + value: e.value + }, b(e.label), 9, ti))), 128))], 40, ei))]))), 128)), + o("section", ni, [ + l[19] ||= o("h2", { class: "text-sm font-semibold text-heading" }, "AI capabilities", -1), + l[20] ||= o("p", { class: "mt-1 text-xs text-muted" }, "Enable only the AI tools your team needs, and choose a model for each one.", -1), + o("div", ri, [o("div", null, [o("label", ii, [o("input", { + type: "checkbox", + checked: d.ai_chat_enabled === "YES", + onChange: l[4] ||= (e) => _e("ai_chat_enabled", e) + }, null, 40, ai), l[15] ||= o("span", { class: "text-sm font-medium text-heading" }, [s("Assistant chat"), o("span", { class: "mt-1 block text-xs font-normal text-muted" }, "Show the conversational AI drawer in company pages.")], -1)]), d.ai_chat_enabled === "YES" ? (h(), a("label", oi, [l[16] ||= s("Chat model", -1), re(o("input", { + "onUpdate:modelValue": l[5] ||= (e) => d.ai_chat_model = e, + list: "ai-models", + class: "mt-1 w-full rounded-md border border-line-default bg-surface px-3 py-2" + }, null, 512), [[T, d.ai_chat_model]])])) : i("", !0)]), o("div", null, [o("label", si, [o("input", { + type: "checkbox", + checked: d.ai_text_generation_enabled === "YES", + onChange: l[6] ||= (e) => _e("ai_text_generation_enabled", e) + }, null, 40, ci), l[17] ||= o("span", { class: "text-sm font-medium text-heading" }, [s("Editor text generation"), o("span", { class: "mt-1 block text-xs font-normal text-muted" }, "Add AI writing tools to rich-text editors.")], -1)]), d.ai_text_generation_enabled === "YES" ? (h(), a("label", li, [l[18] ||= s("Text generation model", -1), re(o("input", { + "onUpdate:modelValue": l[7] ||= (e) => d.ai_text_generation_model = e, + list: "ai-models", + class: "mt-1 w-full rounded-md border border-line-default bg-surface px-3 py-2" + }, null, 512), [[T, d.ai_text_generation_model]])])) : i("", !0)])]), + o("datalist", ui, [(h(!0), a(e, null, v(le.value, (e) => (h(), a("option", { + key: e.value, + value: e.value + }, b(e.label), 9, di))), 128))]) + ]) + ])) : i("", !0)])) : i("", !0), + fe.value ? (h(), a("footer", fi, [o("button", { + type: "submit", + class: "rounded bg-btn-primary px-4 py-2 text-sm font-medium text-white hover:bg-btn-primary-hover disabled:opacity-50", + disabled: x.value + }, b(x.value ? "Saving…" : "Save settings"), 9, pi), D.value ? (h(), a("button", { + key: 0, + type: "button", + class: "rounded border border-line-default px-4 py-2 text-sm text-body hover:bg-hover disabled:opacity-50", + disabled: x.value || S.value, + onClick: ge + }, b(S.value ? "Testing…" : "Test connection"), 9, mi)) : i("", !0)])) : i("", !0) + ], 32))]), + _: 1 + })]), + _: 1 + }); + }; + } +}); +//#endregion +//#region resources/js/init.ts +window.InvoiceShelf.booting((e, t, n) => { + let r = _(!1), i = _(!1), a = _(!1), o = _(!1), s = _(!1), c = _(0), d = (e, t) => { + n.notify(e, t); + }, f = async (e = !1) => { + try { + let t = e ? E.adminCapabilities : E.capabilities, { data: s } = await n.client.get(t); + r.value = !!s.chat, i.value = !!s.text_generation, a.value = !!s.can_manage_company, o.value = !!s.can_manage_global; + } catch { + r.value = !1, i.value = !1, a.value = !1, o.value = !1; + } + }; + n.addMessages({ en: { ai_assistant: { + title: "AI Assistant", + chat: { + new_conversation: "New conversation", + empty: "No conversations yet.", + thinking: "Thinking…" + }, + settings: { + saved: "AI settings saved.", + connection_success: "Connection successful." + } + } } }), n.on("bootstrap:completed", ({ adminMode: e }) => { + f(e); + }), n.on("company:changing", () => { + s.value = !1, c.value += 1, r.value = !1, i.value = !1; + }), n.on("company:changed", ({ companyId: e }) => { + f(e === null); + }), n.registerHeaderAction({ + id: "ai-assistant.header", + priority: 20, + visible: () => r.value, + component: l({ setup: () => () => u(_r, { onOpen: () => { + s.value = !0; + } }) }) + }), n.registerCompanyLayoutOverlay({ + id: "ai-assistant.overlay", + component: l({ setup: () => () => u(gr, { + key: c.value, + open: s.value, + enabled: r.value, + client: n.client, + notify: d, + "onUpdate:open": (e) => { + s.value = e; + } + }) }) + }), n.registerRichEditorToolbarAction({ + id: "ai-assistant.editor", + visible: () => i.value, + component: l({ + props: { context: { + type: Object, + required: !0 + } }, + setup: (e) => () => u(Pr, { + context: e.context, + enabled: i.value, + client: n.client, + notify: d + }) + }) + }), gi(n, "admin", o, d), gi(n, "company", a, d); +}); +function gi(e, t, n, r) { + let i = { + id: `ai-assistant.${t}-settings`, + title: "AI Assistant", + icon: "SparklesIcon", + path: "ai-assistant", + priority: 80, + visible: () => n.value, + component: l({ setup: () => () => u(hi, { + client: e.client, + scope: t, + notify: r + }) }) + }; + t === "admin" ? e.registerAdminSettingsPage(i) : e.registerCompanySettingsPage(i); +} +//#endregion diff --git a/dist/style.css b/dist/style.css new file mode 100644 index 0000000..a3d801b --- /dev/null +++ b/dist/style.css @@ -0,0 +1,3 @@ +/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.static{position:static}.inset-0{inset:0}.inset-y-0{inset-block:0}.right-0{right:0}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-inline:auto}.-mt-3{margin-top:calc(var(--spacing,.25rem) * -3)}.mt-1{margin-top:var(--spacing,.25rem)}.mt-3{margin-top:calc(var(--spacing,.25rem) * 3)}.mt-4{margin-top:calc(var(--spacing,.25rem) * 4)}.mt-5{margin-top:calc(var(--spacing,.25rem) * 5)}.mt-16{margin-top:calc(var(--spacing,.25rem) * 16)}.mb-2{margin-bottom:calc(var(--spacing,.25rem) * 2)}.mb-3{margin-bottom:calc(var(--spacing,.25rem) * 3)}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing,.25rem) * 4)}.h-5{height:calc(var(--spacing,.25rem) * 5)}.h-10{height:calc(var(--spacing,.25rem) * 10)}.h-14{height:calc(var(--spacing,.25rem) * 14)}.max-h-48{max-height:calc(var(--spacing,.25rem) * 48)}.min-h-11{min-height:calc(var(--spacing,.25rem) * 11)}.w-4{width:calc(var(--spacing,.25rem) * 4)}.w-5{width:calc(var(--spacing,.25rem) * 5)}.w-10{width:calc(var(--spacing,.25rem) * 10)}.w-52{width:calc(var(--spacing,.25rem) * 52)}.w-full{width:100%}.max-w-3xl{max-width:var(--container-3xl,48rem)}.max-w-\[85\%\]{max-width:85%}.max-w-xl{max-width:var(--container-xl,36rem)}.max-w-xs{max-width:var(--container-xs,20rem)}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:var(--spacing,.25rem)}.gap-2{gap:calc(var(--spacing,.25rem) * 2)}.gap-3{gap:calc(var(--spacing,.25rem) * 3)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing,.25rem) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing,.25rem) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 5) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing,.25rem) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing,.25rem) * 6) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg,.5rem)}.rounded-md{border-radius:var(--radius-md,.375rem)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-line-default{border-color:var(--color-line-default)}.bg-alert-error-bg{background-color:var(--color-alert-error-bg)}.bg-alert-success-bg{background-color:var(--color-alert-success-bg)}.bg-black\/20{background-color:#0003}@supports (color:color-mix(in lab, red, red)){.bg-black\/20{background-color:color-mix(in oklab, var(--color-black,#000) 20%, transparent)}}.bg-black\/30{background-color:#0000004d}@supports (color:color-mix(in lab, red, red)){.bg-black\/30{background-color:color-mix(in oklab, var(--color-black,#000) 30%, transparent)}}.bg-btn-primary{background-color:var(--color-btn-primary)}.bg-hover-strong{background-color:var(--color-hover-strong)}.bg-primary-500{background-color:var(--color-primary-500)}.bg-surface{background-color:var(--color-surface)}.bg-surface-secondary{background-color:var(--color-surface-secondary)}.bg-surface-tertiary{background-color:var(--color-surface-tertiary)}.p-1{padding:var(--spacing,.25rem)}.p-2{padding:calc(var(--spacing,.25rem) * 2)}.p-3{padding:calc(var(--spacing,.25rem) * 3)}.p-4{padding:calc(var(--spacing,.25rem) * 4)}.p-5{padding:calc(var(--spacing,.25rem) * 5)}.p-6{padding:calc(var(--spacing,.25rem) * 6)}.px-1{padding-inline:var(--spacing,.25rem)}.px-2{padding-inline:calc(var(--spacing,.25rem) * 2)}.px-3{padding-inline:calc(var(--spacing,.25rem) * 3)}.px-4{padding-inline:calc(var(--spacing,.25rem) * 4)}.px-5{padding-inline:calc(var(--spacing,.25rem) * 5)}.py-1{padding-block:var(--spacing,.25rem)}.py-2{padding-block:calc(var(--spacing,.25rem) * 2)}.py-4{padding-block:calc(var(--spacing,.25rem) * 4)}.py-12{padding-block:calc(var(--spacing,.25rem) * 12)}.pt-5{padding-top:calc(var(--spacing,.25rem) * 5)}.pb-6{padding-bottom:calc(var(--spacing,.25rem) * 6)}.text-center{text-align:center}.text-left{text-align:left}.text-sm{font-size:var(--text-sm,.875rem);line-height:var(--tw-leading,var(--text-sm--line-height,calc(1.25 / .875)))}.text-xs{font-size:var(--text-xs,.75rem);line-height:var(--tw-leading,var(--text-xs--line-height,calc(1 / .75)))}.font-medium{--tw-font-weight:var(--font-weight-medium,500);font-weight:var(--font-weight-medium,500)}.font-normal{--tw-font-weight:var(--font-weight-normal,400);font-weight:var(--font-weight-normal,400)}.font-semibold{--tw-font-weight:var(--font-weight-semibold,600);font-weight:var(--font-weight-semibold,600)}.break-words{overflow-wrap:break-word}.whitespace-pre-wrap{white-space:pre-wrap}.text-alert-error-text{color:var(--color-alert-error-text)}.text-alert-success-text{color:var(--color-alert-success-text)}.text-body{color:var(--color-body)}.text-heading{color:var(--color-heading)}.text-muted{color:var(--color-muted)}.text-primary-500{color:var(--color-primary-500)}.text-subtle{color:var(--color-subtle)}.text-white{color:var(--color-white,#fff)}.italic{font-style:italic}.underline{text-decoration-line:underline}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline-none{--tw-outline-style:none;outline-style:none}@media (hover:hover){.group-hover\:flex:is(:where(.group):hover *){display:flex}.hover\:bg-btn-primary-hover:hover{background-color:var(--color-btn-primary-hover)}.hover\:bg-hover:hover{background-color:var(--color-hover)}.hover\:text-alert-error-text:hover{color:var(--color-alert-error-text)}.hover\:text-heading:hover{color:var(--color-heading)}}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-primary-500:focus{--tw-ring-color:var(--color-primary-500)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\:flex{display:flex}.sm\:hidden{display:none}.sm\:w-\[480px\]{width:480px}.sm\:flex-col{flex-direction:column}}@media (width>=64rem){.lg\:inline{display:inline}.lg\:w-\[640px\]{width:640px}}.\[\&_a\]\:text-primary-500 a{color:var(--color-primary-500)}.\[\&_a\]\:underline a{text-decoration-line:underline}.\[\&_ol\]\:mt-3 ol{margin-top:calc(var(--spacing,.25rem) * 3)}.\[\&_ol\]\:list-decimal ol{list-style-type:decimal}.\[\&_ol\]\:pl-5 ol{padding-left:calc(var(--spacing,.25rem) * 5)}.\[\&_p\+p\]\:mt-3 p+p,.\[\&_pre\]\:mt-3 pre{margin-top:calc(var(--spacing,.25rem) * 3)}.\[\&_pre\]\:overflow-x-auto pre{overflow-x:auto}.\[\&_pre\]\:rounded pre{border-radius:.25rem}.\[\&_pre\]\:bg-surface pre{background-color:var(--color-surface)}.\[\&_pre\]\:p-3 pre{padding:calc(var(--spacing,.25rem) * 3)}.\[\&_ul\]\:mt-3 ul{margin-top:calc(var(--spacing,.25rem) * 3)}.\[\&_ul\]\:list-disc ul{list-style-type:disc}.\[\&_ul\]\:pl-5 ul{padding-left:calc(var(--spacing,.25rem) * 5)}}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"
{{ loadError }}